Software I2C slave for Arduino Uno & TFT shield (+code)

  • I created a software I2C-slave implementation for an Arduino Uno with a TFT-shield.
    The TFT occupies all pins of the Arduino Uno.
    The Uno's default designated (hardware) I2C pins can not be used, because they connect to the TFT's pins that control RESET & CS. If the hardware I2C would be used, the TFT would behave irratic.
    I do not use the TFT's SD card, so i can use 2 of those pins for the (software) I²C bus.
    Arduino pins used: 11 = SCL, 12 = SDA.
    This software I2C bus operates at a +5V level.
    Therefore connect the TFT I2C-slave at the high level side of the Logic Level Converter.

    I made a demo sketch for the slave, and a demo sketch for any other Arduino that acts as the master. The demo is simple: It just sends 5 float values to the slave, which displays it on the TFT.
    Codesize is small and fast. It should not be hard to adjust the code to your own needs. The part of the code you edit, is normal Arduino code.

    Here's the I2C-slave TFT in action on my MySensors node, displaying the measurements:
    alt text

    Here is the sketch of the I2C-slave TFT:

    // TFT
    #include <Adafruit_GFX.h>
    #include <UTFTGLUE.h>
    UTFTGLUE tft(0x9341,A2,A1,A3,A4,A0);
    #if !defined(SmallFont)
    extern uint8_t SmallFont[];
    #define BLACK   0x0000
    #define WHITE   0xFFFF
    // I2C
    #define I2C_SLAVE_ADDRESS 73
    #define SCL_BIT PB3
    #define SDA_BIT PB4
    #define I2C_DDR DDRB
    #define I2C_OUT PORTB
    #define I2C_IN  PINB
    extern "C" {
      volatile uint8_t byteN;
      volatile uint8_t I2C_data;
      volatile union u_floatarray {
        uint8_t b[20];
        float f[5];
      } I2C_values;
      void InComingData(void) {
        I2C_values.b[byteN] = I2C_data;
        if (byteN % 4 == 0) {
          uint8_t v = (byteN >> 2) - 1;
          uint8_t r = v * 8;
          tft.setCursor(80, r);
      void OutGoingData(void) {
        // nothing
    void setup() {
      if (tft.height() > tft.width()) tft.setRotation(1);
      tft.setTextColor(BLACK,WHITE);   //black on white, instead of transparent chars..
      tft.setCursor(0, 0);
      tft.println("   Humidity:");
      tft.println("   UV-index:");
      tft.println("Light Level:");
      tft.println("   Pressure:");
      TIMSK0 = 0;
      __asm__ __volatile__ (
        "cli" "\n"
        "cbi %[I2CDDR], %[SCLBIT]" "\n"
        "cbi %[I2COUT], %[SCLBIT]" "\n"
        "cbi %[I2CDDR], %[SDABIT]" "\n"
        "cbi %[I2COUT], %[SDABIT]" "\n"
       :: [I2CDDR] "I" (_SFR_IO_ADDR(I2C_DDR)),
          [I2COUT] "I" (_SFR_IO_ADDR(I2C_OUT)),
          [SCLBIT] "I" (SCL_BIT),
          [SDABIT] "I" (SDA_BIT)
    void loop() {
      __asm__ __volatile__ (
      "HandleTransaction:" "\n"
         "rcall I2C_activity" "\n"
         "brtc HandleTransaction" "\n"
      "StartCondition:" "\n"
         "ldi R16, 0" "\n"
         "sts byteN, R16" "\n"
         "rcall wait_SCL_low" "\n"
         "rcall slave_readByte" "\n"
         "brts StartCondition" "\n"
         "brhs StopCondition" "\n"
         "mov R19, R18" "\n"
         "lsr R19" "\n"
         "cpi R19, %[SLAVE_ADDR]" "\n"
         "brne StopCondition" "\n"
         "rcall slave_writeACK" "\n"
         "andi R18, 0b00000001" "\n"
         "brne MasterRead" "\n"
      "MasterWrite:" "\n"
         "rcall slave_readByte" "\n"
         "brts StartCondition" "\n"
         "brhs StopCondition" "\n"
         "rcall slave_writeACK" "\n"
         "sbi %[I2CDDR], %[SCLBIT]" "\n"
         "sts I2C_data, R18" "\n"
         "call InComingData" "\n"
         "cbi %[I2CDDR], %[SCLBIT]" "\n"
         "rjmp MasterWrite" "\n"
      "MasterRead:" "\n"
         "sbi %[I2CDDR], %[SCLBIT]" "\n"
         "call OutGoingData" "\n"
         "lds R18, I2C_data" "\n"
         "cbi %[I2CDDR], %[SCLBIT]" "\n"
         "rcall slave_writeByte" "\n"
         "brts StartCondition" "\n"
         "brhs StopCondition" "\n"
         "rcall slave_readACK" "\n"
         "breq MasterRead" "\n"
      "StopCondition:" "\n"
         "rjmp DoneTransaction" "\n"
      "I2C_activity:" "\n"
         "in R16, %[I2CIN]" "\n"
         "andi R16, (1<<%[SCLBIT] | 1<<%[SDABIT])" "\n"
      "ac1:" "in R17, %[I2CIN]" "\n"
         "andi R17, (1<<%[SCLBIT] | 1<<%[SDABIT])" "\n"
         "cp R16, R17" "\n"
         "breq ac1" "\n"
         "clh" "\n"
         "clt" "\n"
         "sbrs R16, %[SCLBIT]" "\n"
         "rjmp ac2" "\n"
         "sbrs R17, %[SCLBIT]" "\n"
         "rjmp ac2" "\n"
         "sbrs R17, %[SDABIT]" "\n"
         "set" "\n"
         "sbrc R17, %[SDABIT]" "\n"
         "seh" "\n"
      "ac2:" "ret" "\n"
      "slave_readByte:" "\n"
         "ldi R18, 0b00000001" "\n"
      "rb1:" "rcall wait_SCL_high" "\n"
         "in R19, %[I2CIN]" "\n"
         "rcall I2C_activity" "\n"
         "brts rb2" "\n"
         "brhs rb2" "\n"
         "sec" "\n"
         "sbrs R19, %[SDABIT]" "\n"
         "clc" "\n"
         "rol R18" "\n"
         "brcc rb1" "\n"
         "clz" "\n"
         "clh" "\n"
      "rb2:" "ret" "\n"
      "slave_writeByte:" "\n"
         "ldi R19, 8" "\n"
      "wb1:" "lsl R18" "\n"
        "brcs wb2" "\n"
        "sbi %[I2CDDR], %[SDABIT]" "\n"
      "wb2:" "brcc wb3" "\n"
        "cbi %[I2CDDR], %[SDABIT]" "\n"
      "wb3:" "rcall wait_SCL_high" "\n"
        "rcall I2C_activity" "\n"
        "brts wb4" "\n"
        "brhs wb4" "\n"
        "dec R19" "\n"
        "brne wb1" "\n"
      "wb4:" "cbi %[I2CDDR], %[SDABIT]" "\n"
        "ret" "\n"
      "skipPulse:" "\n"
        "rcall wait_SCL_high" "\n"
      "wait_SCL_low:" "\n"
        "sbic %[I2CIN], %[SCLBIT]" "\n"
        "rjmp wait_SCL_low" "\n"
        "ret" "\n"
      "wait_SCL_high:" "\n"
        "sbis %[I2CIN], %[SCLBIT]" "\n"
        "rjmp wait_SCL_high" "\n"
        "ret" "\n"
      "slave_writeACK:" "\n"
        "sbi %[I2CDDR], %[SDABIT]" "\n"
        "rcall skipPulse" "\n"
        "cbi %[I2CDDR], %[SDABIT]" "\n"
        "ret" "\n"
      "slave_readACK:" "\n"
        "rcall wait_SCL_high" "\n"
        "sez" "\n"
        "sbic %[I2CIN], %[SDABIT]" "\n"
        "clz" "\n"
        "rcall wait_SCL_low" "\n"
        "ret" "\n"
      "DoneTransaction:" "\n"
      :: [I2CIN] "I" (_SFR_IO_ADDR(I2C_IN)),
         [I2COUT] "I" (_SFR_IO_ADDR(I2C_OUT)),
         [I2CDDR] "I" (_SFR_IO_ADDR(I2C_DDR)),
         [SCLBIT] "I" (SCL_BIT),
         [SDABIT] "I" (SDA_BIT),
         "e" (InComingData),
         "e" (OutGoingData),
         [I2C_data] "label" (I2C_data),
         [byteN] "label" (byteN)

    Here is a demo I2C-master sketch that sends the 5 floats:

    #include <Wire.h>
    #define SLAVE_ADDRESS 73
    union u_floatarray {
      uint8_t b[20];
      float f[5];
    } I2C_values;
    void setup() {
    // test values: temperature, humidity, UV-index, light level, pressure
      I2C_values.f[0] = 20.3;
      I2C_values.f[1] = 48.8;
      I2C_values.f[2] = 2.4;
      I2C_values.f[3] = 345.0;
      I2C_values.f[4] = 1019.7;
      pinMode(LED_BUILTIN, OUTPUT);
    void loop() {
      digitalWrite(LED_BUILTIN, HIGH);
      for (uint8_t n=0; n<20; n++) if (Wire.write(I2C_values.b[n]) != 1) break;
      digitalWrite(LED_BUILTIN, LOW);

  • Mod

    FYI, there are also some arduino boards with 2 additional analog pins if you need more.
    BTW, nice project 👍

  • Thanks gohan..
    This forum is a great source of inspiration. Lots of examples of people and their own projects, and/or problems they encounter(ed).
    I'm fairly new to MySensors & Arduino, but i have a lot of experience with Atmel AVR. At this time, i'm more reading than writing: absorbing info first. Hopefully i can help out others too sometimes.. it's cool to share knowledge.

    I already have 10 Arduino Yúns & 2 Raspberries at my disposal (they belong to friends i'm helping out).
    It would be fun to find a sensible application to combine a MySensors radio network (or perhaps multiple networks), and the IOT-capabilities of the Yún(s) & Raspberries.
    But that's another project..

Suggested Topics