JM 23 Nov 2019 - Uli Ender presented "Using Tcl for simple hardware-interfaces" back in the 12th European Tcl/Tk Users Meeting 2014
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:
This code is processing the response from the slave during write operations, i.e If ACK is received, it is shown with and "A" on the specific bit slot. On the contrary "N" is shown.
But this code is not yet able to process read transactions, i.e: you cannot use it (yet) to read an I2C temperature sensor for example, as you can only write at this point.
From the 14 digital pins (0 to 13) 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:
+ 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:
# Saturday 11/23/2019 Adaptation to use Arduino instead of mouse/light sensors # Monday 11/25/2019 Adding ACK bit check from slave # Added gridlines # package require tfirmata set bd [tfirmata::open COM4] set ctr 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 10 $bd dstream 0 on 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 -xscrollcommand {.i2c.x set} scrollbar .i2c.x -ori hori -command {.i2c.c1 xview} pack .i2c.c1 -expand yes -fill both #grid .i2c.c1 -sticky ew pack .i2c.x -expand yes -fill x .i2c.c1 create line 0 30 700 30 -fill red -dash . .i2c.c1 create line 0 100 700 100 -fill red -dash . .i2c.c1 create line 0 180 700 180 -fill red -dash . .i2c.c1 create line 0 250 700 250 -fill red -dash . for {set i 167} {$i <= 700} {incr i 16} { .i2c.c1 create line $i 0 $i 300 -fill red -dash . } ### 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]} .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 <ButtonPress-3> { global sda2 rechteck2 set sda2 100 .i2c.c1 itemconfigure $rechteck2 -fill blue;# SDA pulled low by slave update } bind .i2c.c1 <ButtonRelease-3> { global sda2 rechteck2 set sda2 30 .i2c.c1 itemconfigure $rechteck2 -fill yellow;# SDA release by slave 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 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 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 ctr 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 update incr ctr } proc sclL {} { global bd 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 ctr .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 } # Prepara un bit proc s {w} { global iks sda2 x ser ctr bd rechteck2 seitwaerts ##### sclL ##### seitwaerts if {$w == "1" || $w == "A"} { sdaH } #after 100 tfirmata::sleep 100 set SDA_state1 [$bd state 3] set SDA_state2 "[lindex [lindex $SDA_state1 1] 0]" if {$sda2==100} { puts "RELEASED" set sda2 30 .i2c.c1 itemconfigure $rechteck2 -fill yellow;# SDA release by slave } if {$SDA_state2 == "in" && [$bd dget 3] == 0} { puts "ACK" set sda2 100 .i2c.c1 itemconfigure $rechteck2 -fill blue;# SDA pulled low by slave } update if {$w == "A" && $sda2 == 30} {set w "N"};# se recibe un HIGH (no ACK) if {$w == "1" && $sda2 == 100} {set w "0"};# se recibe o se escribe un sda LOW, slave overrides master if {$w == "0" || $w == "a" || $w == "A"} {sdaL } if {$w == "1" || $w == "N"} {sdaHH };# solo traza el HIGH seitwaerts ##### sclH ##### .i2c.c1 create text $iks 130 -text $w seitwaerts } # Envia un byte proc adresse {a} { global ctr set ctr 1 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 ctr .i2c.c1 create text $iks 300 -anchor nw -text " sende Chip Addr $a" adresse $a s A } proc sende {a} { global iks ctr .i2c.c1 create text $iks 300 -anchor nw -text " sende $a" adresse $a s A } proc enviar {addr args} { start chipadresse $addr foreach byte $args { sende $byte puts "nibble >>> $byte" } stop } proc sendData {byte} { set byte [string range $byte end-1 end] puts "hex: $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 0x4e $part1 $part2 $part3 $part4 } proc sendData2 {byte} { set byte [string range $byte end-1 end] puts "hex: $byte" foreach {n1 n2} [split $byte ""] break enviar 0x4e 0x${n1}5 0x${n1}1 0x${n2}5 0x${n2}1 } init stop if 0 { 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 0x4e 0x34 0x30 if 1 { enviar 0x4e 0x34 0x30 0x34 0x30 0x24 0x20 0x24 0x20 enviar 0x4e 0xc4 0xc0 enviar 0x4e 0x04 0x00 0x14 0x10 enviar 0x4e 0x04 0x00 0xc4 0xc0 enviar 0x4e 0x04 0x00 0x64 0x60 } # enviar 78 69 65 21 17 if 1 { foreach letter [split "Tcl/Tk & Arduino" ""] { puts "Sending letter: $letter ===" binary scan $letter H2 valor sendData2 $valor } } $bd close