tfirmata is a Tcl implementation of Arduino Firmata 2.5.
Firmata is a communications protocol for direct control and reading back of Arduino hardware from another system, such as a PC. Software running on a host, to control Arduino hardware, can be written in any language that the host system supports, including high level scripting languages such as Tcl.
The StandardFirmata project, which is included with the Arduino IDE, implements the Firmata protocol. An Arduino can be programmed with StandardFirmata, and then controlled from scripts running on a PC, for a quick and easy method of controlling hardware. See the Firmata page in the Tcl'ers Wiki for more information and references to examples.
tfirmata is a complete implementation of Firmata, including board capability querying. As a result tfirmata should work with all boards supported by Firmata.
The tfirmata library provides a number of commands and subcommands to support control of Arduino I/O using Tcl. Available commands and subcommands are described below. As a brief introduction, here is a complete example script showing how tfirmata can be used:
package require tfirmata
set bd [tfirmata::open /dev/ttyUSB0]
$bd mode 13 out
while {1} {
$bd dset 13 1
tfirmata::sleep 250
$bd dset 13 0
tfirmata::sleep 250
}
The tfirmata::open command opens a connection to an Arduino, and
returns a reference command. The reference command is then used with the
mode subcommand to configure pin 13, which is connected to an LED
on the Arduino board, to an output. The LED is then flashed, 250ms on and
250ms off, using tfirmata::sleep to provide the delays.
tfirmata provides the following commands:
tfirmata::chan chantfirmata::open porttfirmata::sleep millisecondstfirmata::handles
The chan command uses a pre-opened Tcl channel for communicating
with the Arduino. The open command opens that channel explicitely
and takes an argument specifying the serial port that the Arduino is connected
on. chan and open also return a reference command
for calling subcommands.
The sleep command delays a script for the number of milliseconds
specified. sleep is the recommended way of delaying a script,
because it passes control to the Tcl event loop, allowing tfirmata to continue
to service open connections.
The handles command returns a list of all currently open
Firmata communication channels.
See above for an example of open and sleep being
used.
tfirmata subcommands are as follows:
mode pin ?...? mode ?...?dget pinNum ?..?dset pinNum value ?...?dset25 pinNum value ?...?aget pinNum ?..?aset pinNum value ?...?dstream portNum ?port ...? state ?...?astream analogChannel ?port ...? state ?...?dcommand commandacommand commandservolimits pin ?...? limits ?...?amapping pin ?...?twiconfig ?microseconds?twiget ?-repeat? address ?control ...? size ?-command command?
twiget -stop addresstwiset address byte ?...?period millisecondsstate pin ?...?firmwareerrorscloseerrcommand commandstepconfig num delay interface stepsPerRevolution pin1 pin2 ?pin3 pin4?stepoff numstep num steps speed ?acceleration? ?deceleration?stepwait ?mask? ?milliseconds?eattach num pin1 pin2equery ?num?ereset numereport flagecommand commandedetach numeget numserconfig num baud ?pinRX pinTX?sercommand commandserwrite num dataserread num ?stop maxRead?serclose numserflush numserlisten numowconfig pin ?power?owsearch pin ?-alarms? ?-command command?owtrans pin ?-reset? ?-skip? ?-address addr? ?-write list?
?-read num? ?-tid tid? ?-delay milliseconds? ?-command command?owcallback commandtaskdatacreatetask num taskdatadeletetask numdelaytask millisecondsscheduletask num millisecondsresettasks
The mode subcommand is used to configure the mode of operation
of Arduino pins. The mode can be any of in, out,
analog, pwm, or servo (to put TWI
capable pins in TWI mode, use twiconfig). The mode
subcommand may be used to set the mode of a single pin, or set the modes of
several pins. On Firmata 2.5 additional known modes are shift,
onewire, stepper, encoder,
serial, and input_pullup.
$bd mode 13 out $bd mode 4 5 6 7 in 9 pwm 10 11 12 13 out
The dget subcommand gets the digital value of input pins,
retrieving values from a local cache. Before the dget
subcommand is used, the dstream subcommand should be called to
set up the Arduino, so that when a digital input changes it's new value is
sent for the local tfirmata cache. dget may be used to get the
digital value of one pin, or multiple pins. When getting the value of multiple
pins, values are returned as a list.
puts [$bd dget 13] puts [$bd dget 4 5 6 7]
The dset subcommand sets the value of digital output pins.
dset may be used to set the value of a single pin, or to set the
value of multiple pins. The alternate form dset25 requires
Firmata version 2.5 and uses a different protocol on the serial port.
$bd dset 13 1 $bd dset 10 0 11 0 12 1 $bd dset25 10 0 11 0 12 1
The aget subcommand gets the analog value of analog input
channels, retrieving values from a local cache. Before the aget
subcommand is used, the astream subcommand should be called to
set up the Arduino, so that it reports analog values at regular intervals for
storing in the local tfirmata cache. The interval between updates can be set
with the period subcommand. aget may be used to get
the analog value of one pin, or multiple pins. When requesting the value of
multiple pins, values are returned as a list.
puts [$bd aget 0] puts [$bd aget 0 1 2 3]
The aset subcommand sets the value of pwm or servo configured
output pins. aset may be used to set the value of a single pin,
or to set the value of multiple pins.
$bd aset 11 1 $bd aset 9 128 10 0 11 255
The dstream subcommand configures an Arduino to stream digital
port values from Arduino to host when a port value changes. Firmata considers 8
pins to make up a port so, for example, enabling streaming on port 0 will
result in a port update being sent to the host if any of pins 0 to 7 change
value. dstream can be used to turn streaming on or off. In addition, streaming
can be configured for a single port or multiple ports.
$bd dstream 0 on $bd dstream 0 1 on 2 off
The astream subcommand configures an Arduino to stream analog
channel values from Arduino to tfirmata at regular intervals set by the period
subcommand. astream can be used to turn streaming on or off. In addition,
streaming can be configured for a single analog channel or for multiple analog
channels.
$bd astream 0 on $bd astream 1 2 3 on 4 5 off
The dcommand subcommand is used to set code that's run when
tfirmata receives an update message after a port, that has been configured
with dstream for streaming, has changed value. The port number
and port value that is part of an update message can be substituted into a
code block by using '%P' and '%V'.
$bd dcommand {puts "digital port update"}
$bd dcommand {puts "digital port update, port: %P, value: %V"}
The acommand subcommand is used to set code that's run when
tfirmata receives an update message for an analog channel that's been
configured by astream for streaming. The analog channel and
analog value that is part of an update message can be substituted into a code
block by using '%C' and '%V'.
$bd acommand {puts "analog channel update"}
$bd acommand {puts "analog channel update, channel: %C, value: %V"}
The servolimits subcommand sets the lower and upper limits for
servo outputs in microseconds. Limits are provided as a two element list.
Limits can be set for a single pin or for multiple pins.
$bd servolimits 12 {850 2250}
$bd servolimits 10 11 {900 2100} 12 {850 2250}
The amapping subcommand returns pin numbers that analog channel
numbers map to. Mappings for a single analog channel or multiple analog
channels can be retreived.
puts [$bd amapping 0] puts [$bd amapping 0 1 2]
The twiconfig subcommand configures an Arduino's TWI capable pins
to TWI mode. In addition, a delay in microseconds may be specified for
reads. The delay is inserted between a TWI message that requests data from a
TWI device and a TWI message that reads out the data. Most TWI devices
will not require a delay, and if not specified, the delay will default to 0us.
$bd twiconfig $bd twiconfig 5000
The twiget subcommand can be used to retreive data from a TWI
device, and also used to stop repeated TWI reads. For reading from a TWI
device, the address of the TWI device, any command bytes to send to the device
before reading, and the number of bytes to read, are supplied as arguments.
The subcommand will block if a -command argument isn't provided.
If it is provided, the subcommand will not block, and once data has been
retrieved, code specified by -command is run. Data bytes read
from the TWI device may be substituted into a code block with
'%D'. If -repeat is specified, the read message to
the TWI device is repeated at regular intervals, set by the
period subcommand. To stop repeated reads, -stop is
specified along with device address.
puts [$bd twiget 0x50 0 1]
$bd twiget 0x50 1 -command {puts "Data: %D"}
$bd twiget -repeat 0x50 1 -command {myTwiCallback %D}
$bd twiget -stop 0x50
The twiset subcommand is used to initiate a TWI write message.
The subcommand takes the address of a target TWI device, followed by the bytes
to send to it.
$bd twiset 0x50 0 0 0x55
The period subcommand sets the number of milliseconds between
analog samples and repeated TWI read messages.
$bd period 500
The state subcommand returns the current configuration state of
Arduino pins. The state is returned as a dictionary, where dictionary
keys are the requested pin numbers, and values are a list of state values.
Configuration state can be retrieved for a single pin, multiple pins, or
all pins.
puts [$bd state 13] puts [$bd state 10 11 12 13] puts [$bd state all]
The firmware subcommand returns the name and version of the
firmware programmed into an Arduino.
puts [$bd firmware]
The errors subcommand returns a count of the number of errors
detected when tfirmata parses messages received from an Arduino.
puts [$bd errors]
The close subcommand closes a connection to an Arduino.
$bd close
The errcommand subcommand adds a Tcl command to be called
when an error on the connection to the Arduino is detected. That command's
single argument is the connection reference.
proc errproc {bd} {
puts stderr "I/O error on $bd"
$bd close
exit
}
$bd errcommand eproc
The stepconfig subcommand configures a stepper motor with
given motor number (between 0 and 5), delay (1 or 2 microseconds),
interface (2 or 4 wires, number of steps for one revolution, and
corresponding pin numbers.
# 4-wire stepper on pins 9..11 $bd stepconfig 0 1 4 2050 10 12 9 11
The stepoff subcommand switches the pins of the stepper
motor given its motor number (between 0 and 5) to zero.
# turn stepper 0 off $bd stepoff 0
The step subcommand controls the stepper motor given its motor
number to turn a specified number of steps with given speed and optional
acceleration and deceleration.
$bd step 0 4100 150 120 120
The stepwait subcommand waits for zero or more stepper motors
to reach their programmed number of steps or for a maximum amount of time.
The motors 0..5 are expressed as bits in a bit mask. The subcommand returns
the bits for the motors which are at position or zero on timeout.
# wait for stepper 1 up to 10 seconds
if {[$bd stepwait 1 10000] == 0} {
# force stop
$bd step 0 0 0
}
The eattach subcommand attaches an encoder given encoder
number (between 0 and 4) on the given two pins. For best results, interrupt
capable input pins should be used.
$bd eattach 0 2 3
The equery subcommand dispatches an encoder query of a
specific or all encoders. The encoder value is reported in the callback
function.
$bd equery 0
The eresets subcommand sets the value of the given encoder
back to zero.
$bd ereset 0
The ereport subcommand turns encoder event reports on or off.
$bd ereport on
The ecommand subcommand sets a callback command which gets
called on encoder events. Substitutions performed are '%C'
for the encoder number, and '%V' for the encoder value.
$bd ecommand {puts "encoder %C is on position %V"}
The edetach subcommand releases the encoder pins and closes
the encoder.
$bd edetach 0
The eget subcommand returns the last known encoder value
without further communication with the Arduino
set value [$bd eget 0]
The serconfig subcommand configures a hardware or software
serial port given port number (0..4 are HW ports, 8..11 are SW ports)
with a baud rate and optional receiver and transmitter pin numbers
for SW ports.
# RX on pin 2, TX on pin 4 $bd serconfig 8 115200 2 4
The sercommand subcommand adds a receive callback for
serial ports. Substitutions performed are '%C'
for the number of the serial port, and '%V' for the
received data from that port.
$bd sercommand {puts "port %C received '%V'"}
The serwrite subcommand sends data on the given serial port.
$bd serwrite 8 "Hello world!\r\n"
The serclose subcommand releases a serial port given number.
$bd serclose 8
The serflush subcommand flushes transmit data on HW serial ports
or empties the receive buffer on SW serial ports.
$bd serflush 8
The serlisten subcommand arranges for receiver start of
a SW serial port given its number. According to the Arduino documentation
only one SW serial port can be listening at any one time.
$bd serlisten 8
The owconfig subcommand configures the given pin for
a 1-Wire bus which by default is externally powered unless the power
argument is one for parasitic power.
$bd owconfig 4
The owsearch subcommand performs a normal or alarm
search for 1-Wire devices on the bus identified by a pin number.
An alarm search is performed when the option "-alarms" is present.
A Tcl command (option "-command") is invoked for all detected
1-Wire devices on the bus. Substitutions on the command are
'%C' for the pin, '%S' for alarms
(0 or 1), and '%A' for the 1-Wire address.
Onewire adresses are expressed in the format used by Linux,
i.e. two hex digits for the device identification, followed
by a dot, followed by 14 hex digits with unique id and CRC.
proc print_ow {pin alarm address} {
puts "pin=$pin alarm=$alarm address=$address"
}
$bd owsearch 4 -command {print_ow %C %S %A}
The owtrans subcommand performs a transaction
on the 1-Wire bus identified by a pin number. The option
'-reset' issues a bus reset at the begin of the transaction.
The option '-skip' allows to skip the address phase. The
option '-address' specifies the 1-Wire address of the
target device. The option '-write' specifies a list
with bytes to be written to the device, the option '-read'
the number of bytes to read from the device. The option
'-tid' allows to specify a transaction identifier. If omitted,
it is chosen automatically. The option '-delay' can be used
in combination with the scheduler to delay the next scheduled
message by the specified number of milliseconds. When
the transaction is complete the Tcl command specified with the
'-command' option is invoked with the substitutions
'%C' for the pin number, '%V'
for the data bytes read, and '%T' for the
transaction identifier.
proc read_ow {pin data} {
lassign $data lo hi
puts "lo=$lo hi=$hi"
}
# read a DS18B20
$bd owtrans 4 -reset -address 28.xxxxxxxxxxxxxx -write 0xbe
-read 9 -command {read_ow %C %V}
The owcallback subcommand is used to set a
callback procedure which catches all unmatched 1-wire
read transactions without a specific callback.
The taskdata subcommand is used to create
a buffer to gather Firmata commands for creating a scheduler task
using the createtask subcommand.
It creates a virtual connection similar to tfirmata::open
and captures the output of subcommands into that virtual
connection.
The createtask subcommand creates a scheduler
task given a task number (0..127) and fills the task's message
buffer with the given taskdata virtual channel.
That virtual channel is then closed automatically. The task
itself is created in suspended state.
The deletetask subcommand deletes a task given
the task number (0..127).
The delaytask subcommand is used in the context of
a task message buffer (see taskdata) in order to
delay the containing task for a specified amount of milliseconds.
The scheduletask subcommand is used to activate
a task given the task number (0..127) and the activation time
relative to now in milliseconds.
The resettasks subcommand can be used to delete
all currently defined tasks with a single command.
More than one Arduino can be controlled at a time. The following example is a variation on the flashing LED script above, but this time LEDs on two boards are flashed, one the inverse of the other.
package require tfirmata
set bd1 [tfirmata::open /dev/ttyUSB0]
set bd2 [tfirmata::open /dev/ttyUSB1]
$bd1 mode 13 out
$bd2 mode 13 out
while {1} {
$bd1 dset 13 1
$bd2 dset 13 0
tfirmata::sleep 250
$bd1 dset 13 0
$bd2 dset 13 1
tfirmata::sleep 250
}