''[JM] 23 Nov 2019'' - Uli Ender presented "Using Tcl for simple hardware-interfaces" back in the [12th European Tcl/Tk Users Meeting 2014] [bitbangImage] The key element from his presentation was to hack commonly available PC accessories in such a way that they can be converted into I/O interfaces for electronic projects (this look very valuable as a tool to be used to teach electronics) Nowadays the Arduino is commonly available and relatively low-cost, so I made this minor adaptation to use Ender's code to use Arduino instead of the proposed hack to a mouse he presented back then. What you need: 1. Arduino UNO or similar (loaded with Standard [Firmata]) 2. PC with a Tcl interpreter (preferable [undroidwish], which already includes the tfirmata library) 3. The LCD with I2C "backpack" module This code is not yet processing the data from the slave, i.e: * ACK is being ignored in this demo * you cannot use it (yet) to read an [I2C] temperature sensor for example, as you can only write at this point From the 13 digital pins on Arduino UNO, I used (hard coded): digital pin 2 for SCL digital pin 3 for SDA Also, the Arduino is powering the LCD modules with +5V and GND ---- See Also: Example #2 from the [Firmata] page in this wiki ---- The LCD with I2C "backpack" module is pre-wired like this: ===none + 1 2 3 | + + + | | | | +-------+-------+ | | | | 2 | +-+---+--+---+ | | 16| A0 A1 A2 | | | +-------+V P0+--------+4 RS 3+-----+ | | | | | P1+--------+5 RW | 15| | | | +------+SDA P2+--------+6 EN | | | | | | PCF8574 P3| | LCD | 14| | | | +------+SCL | DB4| | | P4+--------+11 | | | DB5| | 13| P5+--------+12 | +------+INT | DB6| | | P6+--------+13 | | | DB7| | | P7+--------+14 | | | DB3| | | 8 | +--+10 | +--+---------+ DB2| | | +--+9 | v DB1| | +--+8 | DB0| | +--+7 1 | +-------+-------+ | v === and the Tcl code: ======tcl package require tfirmata set bd [tfirmata::open COM4] ### i2C.demo 1 global rechteck rechteck2 iks scl sda sda2 abstand dauer ser set scl 180 set sda 30 set sda2 30 set abstand 4 set dauer 100 proc init {} { global rechteck rechteck2 iks scl sda abstand dauer set iks 80 destroy .i2c toplevel .i2c wm geometry .i2c 1020x510+0+0 # wm geometry .i2c 785x380+0+0 destroy .i2c.c1 canvas .i2c.c1 pack .i2c.c1 -expand yes -fill both ### scl H if {$scl == 180} {set rechteck [.i2c.c1 create rectangle 0 190 50 380 -fill black]} ### scl L if {$scl == 250} {set rechteck [.i2c.c1 create rectangle 0 190 50 380 -fill white]} ### sda H if {$sda == 30} {set rechteck2 [.i2c.c1 create rectangle 0 0 50 188 -fill black]} if {$sda == 100} {set rechteck2 [.i2c.c1 create rectangle 0 0 50 180 -fill white]} if 0 { button .i2c.c1.b2 -text "a u t o" button .i2c.c1.b3 -text "s t e p" place .i2c.c1.b2 -x 735 -y 0 -height 190 -width 52 place .i2c.c1.b3 -x 735 -y 190 -height 190 -width 52 } .i2c.c1 create text 65 30 -text H .i2c.c1 create text 65 100 -text L .i2c.c1 create text 65 180 -text H .i2c.c1 create text 65 250 -text L .i2c.c1 create text 80 120 -text SDA .i2c.c1 create text 80 270 -text SCL bind .i2c.c1 { global sda2 rechteck2 set sda2 100 .i2c.c1 itemconfigure $rechteck2 -fill blue update } bind .i2c.c1 { global sda2 rechteck2 set sda2 30 .i2c.c1 itemconfigure $rechteck2 -fill yellow update } seitwaerts seitwaerts seitwaerts seitwaerts } proc seitwaerts {} { global iks sda scl abstand dauer set iks2 $iks incr iks $abstand .i2c.c1 create line $iks2 $sda $iks $sda .i2c.c1 create line $iks2 $scl $iks $scl after $dauer update } ################################################################### proc sdaH {} { global bd global iks sda rechteck2 ser .i2c.c1 itemconfigure $rechteck2 -fill black $bd mode 3 in $bd dset 3 1 } proc sdaHH {} { global iks sda .i2c.c1 create line $iks $sda $iks 30 set sda 30 } proc sdaL {} { global bd global iks sda rechteck2 ser .i2c.c1 create line $iks $sda $iks 100 set sda 100 .i2c.c1 itemconfigure $rechteck2 -fill white $bd mode 3 out $bd dset 3 0 } proc sclH {} { global bd global iks scl rechteck ser .i2c.c1 create line $iks $scl $iks 180 set scl 180 .i2c.c1 itemconfigure $rechteck -fill black $bd mode 2 in $bd dset 2 1 } proc sclL {} { global bd global iks scl rechteck ser .i2c.c1 create line $iks $scl $iks 250 set scl 250 .i2c.c1 itemconfigure $rechteck -fill white $bd mode 2 out $bd dset 2 0 update } ################################################################### proc start {} { global iks .i2c.c1 create text $iks 315 -anchor nw -text " Start" seitwaerts sdaL .i2c.c1 create text $iks 130 -text S seitwaerts } proc stop {} { global iks .i2c.c1 create text $iks 315 -anchor nw -text " Stop" s 0 seitwaerts seitwaerts seitwaerts seitwaerts sdaH sdaHH .i2c.c1 create text $iks 130 -text P seitwaerts seitwaerts seitwaerts seitwaerts } proc s {w} { global iks sda2 x ser seitwaerts sclL seitwaerts if {$w == "1" || $w == "A"} { sdaH } #after 100 tfirmata::sleep 100 update if {$w == "A" && $sda2 == 30} {set w "N"} if {$w == "1" && $sda2 == 100} {set w "0"} if {$w == "0" || $w == "a" || $w == "A"} { sdaL } if {$w == "1" || $w == "N"} { sdaHH } seitwaerts sclH .i2c.c1 create text $iks 130 -text $w seitwaerts } proc adresse {a} { set b [format %c $a] binary scan $b B8 var set n 0 while {$n < 8} { set bit [string index $var $n] if {$bit == 0} { s 0 } if {$bit == 1} { s 1 } incr n } } proc chipadresse {a} { global iks .i2c.c1 create text $iks 300 -anchor nw -text " sende Chipadresse $a" adresse $a s A } proc sende {a} { global iks .i2c.c1 create text $iks 300 -anchor nw -text " sende $a" adresse $a s A } init stop proc enviar {addr args} { start chipadresse 78 foreach byte $args { sende $byte puts ">>> $byte" } stop } proc sendData {byte} { set byte [string range $byte end-1 end] puts "byte: $byte" foreach {n1 n2} [split $byte ""] break scan ${n1}5 %x part1 scan ${n1}1 %x part2 scan ${n2}5 %x part3 scan ${n2}1 %x part4 enviar 78 $part1 $part2 $part3 $part4 } enviar 78 52 48 enviar 78 52 48 52 48 36 32 36 32 enviar 78 196 192 enviar 78 4 0 20 16 enviar 78 4 0 196 192 enviar 78 4 0 100 96 # enviar 78 69 65 21 17 foreach letter [split "Tcl/Tk & Arduino" ""] { puts "Sending letter: $letter ===" binary scan $letter H2 valor sendData $valor } if 0 { start chipadresse 78 sende 4 sende 0 sende 20 sende 16 stop start chipadresse 78 sende 69 sende 65 sende 21 sende 17 stop } $bd close ======