Version 6 of Uli Ender bitbang I2C with Arduino

Updated 2019-11-25 23:53:15 by Jorge

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

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 <ButtonPress-3> {
        global sda2 rechteck2
        set sda2 100
        .i2c.c1 itemconfigure $rechteck2 -fill blue
        update
}

bind .i2c.c1 <ButtonRelease-3> {
        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