Uli Ender bitbang I2C with Arduino

Difference between version 0 and 12 - Previous - Next
''[JM] 23 Nov 2019'' - Uli Ender presented "Using Tcl for simple hardware-interfaces" back in 20the [142th (European Tcl)/Tk Users Meeting 2014] 
[bitbangImage2]

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 dataresponse from the slave during write operations, i.e:
 If  * ACK is breceing ignorved, in th is demsho
   * you cawnnot use with (yet) to reand a"A" on [I2C] themperature spensor ciforic example,bit as ylout. caOn only writhe acontrary th"N" is pshoiwnt.
FromBut theis 13 code igis not yet able to process read transactions, Ii.e: you cannot used it (hayet) to read an [I2C] temperature sensor for example, as you can odnly writed): at this point.
From the 14 digital pins 2(0 fto 13) on Arduino SCLUNO, I used (hard coded):
'''digital pin 32 for SDACL'''
Also, the Ar'''duino gistal powering the3 LCD mfodulesr with +5V and GNSDA'''
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# 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
### i2C.demo                1
global rechteck rechteck2 iks scl sda sda2 abstand dauer ser
set scl 180
set sda 30
set sda2 30
set abstand 4set dauer 100
$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.c1canvas .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]}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 <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
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 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 1update
incr ctr
}

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 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
}

updateif {$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 Chipa Addresse $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
}
init
stop

proc enviar {addr args} {
startchipadresse 78$addr
foreach byte $args {
        sende $byte        puts "nibble >>> $byte"
}
stop
}

proc sendData {byte} {
 set byte [string range $byte end-1 end] puts "bythex: $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 780x4e $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 780x4e 690x34 65 21 17 0x30
ifor 1 {
enviachr l0x4etter [split0x34 "Tcl/Tk0x30 &0x34 Arduino"0x30 ""]0x24 {
0x20 0x24 puts "S0x20
endvingar l0x4ett 0xc4 0xc0
enviar: $l0x4etter ==="
0x04 0x00 0x14      bi0x10
enviary scan $l0x4etter H20x04 valor
0x00 0xc4 s0xc0
endData $vialor 0x4e 0x04 0x00 0x64 0x60
}
# enviar 78 69 65 21 17 
if 01 {
sta        fort
eachipadresse 78
slendtter 4
[senplit "Tcl/Tk & Ardeuino" 0""] {
          puts "Sendeing 20
slendtter: 16
s$letop

staert ==="
ch                bipnadressey 78
secande 69
s$lendtter 65
sende H21 valor
          sende 17
sData2 $valopr
        }
}
$bd close
======