Fab Academy 2019 - Lucas Lim
14. Networking and Communications

Assignment :


Week 14 learning journey :

So far we had been building boards with a single processor, however this week is on how we can work with multiple processors or integrating multiple boards (or projects) together and have them talking to each other.

I am exploring I2C communication.


Implementation of I²C Protocol

I2C (Inter-Integrated Circuit), sometimes also pronounced as "I-squared-C" or "I-two-C" or "I-I-C" (written as IIC), combines the best features of SPI and Serial UARTs.

I2C is a serial communication protocol, so data is transferred bit by bit along a single wire (the SDA line). But unlike Serial UART Ports which are asynchronous (meaning devices need to agree on a data rate beforehand), I2C is synchronous; synchronized to the sampling of bits by a clock signal shared between the master and the slave. In I2C protocol, the master controls the clock speed.

I2C only uses two wires to transmit data between devices. The common I2C bus speeds are 100 kbit/s which is the standard mode and the 400 kbit/s Fast mode. It allow connection up to 128 devices using 7-bit addressing or up to 1024 devices using 10-bit addressing. 10-bit address space is rarely used.


Illustration extracted from Circuit Basic


In I2C bus, both the SCL and SDA lines are refer to "open drain" drivers, meaning that they can pull the corresponding signal line low, but cannot drive it high. Thus, there can be no bus contention where one device is trying to drive the line high while another tries to pull it low, eliminating the potential for damage to the drivers or excessive power dissipation in the system.
Reference : Circuit Basic : I2C


For the line to be able to go high you must provide pull-up resistor(s) to the 5V supply. You only need at least one pull-up resistor for the whole I2C bus and may not require for each I2C board/device. If the resistor is missing, the SCL and SDA lines will always be low - nearly 0 volts - and the I2C bus will not work. A pair of 4.7k ohm Resistors are typically used.
Reference : JLabsTech : What is I2C? (Simplified)




SDA (Serial Data) – The line for the master and slave to send and receive data. I2C is a serial communication protocol, so data is transferred bit by bit along a single wire (the SDA line).

SCL (Serial Clock) – The line that carries the clock signal. The clock signal is always generated by the current bus master. I2C is synchronous, so the output of data bits is synchronized to the sampling of bits by a clock signal shared between the master and the slave(s).
Reference : Circuit Basic : I2C


Each node connected on the I2C bus has its own individual I2C address.

1. To initiate a transaction (or start of a data transfer). The Master node pull the SDA line from high voltage level to a low voltage level before the SCL line switches from high to low. All the slaves as such will start to listen to the SDA line.

2. The master then send a 7-bit address of the slave it wants to communicate with, along with the read/write bit. The single read/write bit specifying whether the master is sending data to the slave (low voltage level) or requesting data from it (high voltage level).

3. The addressed slave node (which the Master wish to communicate with) will replies with an ACK bit to the MASTER by pulling the SDA line low for one bit.

4. After the master detects the ACK bit from the slave, it will start the transaction and send the data frame(s).

5. The Slave after receive of each data frame will send a ACK bit back tot he MASTER.

6. Once the MASTER finished with the transaction, it will send a STOP condition to the slave by switching SCL high before switching SDA high.

Reference : Circuit Basic : I2C



I2C communication between Master and multiple Slaves and Software Serial between Master and laptop

I refered to Neil's I2C board designs and had made some modifications to Neil's designs.


Neil's I2C Master (Bridge)

Neils's I2C Slave (Node)

What will I design?
I will be designing and making three boards. One Master and two Slaves (namely Slave 1 and Slave 2).

How do I connect the boards?
The Master board will be connected to the host PC/laptop via a FTDI (usb/serial) adaptor. This allows me to send command to Master, Slave 1 or Slave 2 to test the networking addressing capability of I2C.

The Master, Slave 1 and Slave 2 nodes are physically connected by I2C ribbon cable, using the same two bus pin (SDA and SCL).

I am using ATTiny45 which is pin 5 (PB0) for SDA pin and pin7 (PB2) for SCL pin.

Microcontroller SDA SCL
ATMega328P 20 21
ATtiny45 5 (PB0) 7 (PB2)
ATtiny44A 7 (PA6) 9 (PA4)


What will I test?
User send data from host PC/laptop to MASTER
On the host PC/laptop, when the user press a '0', which refer to the MASTER node, the red LED on the MASTER Will blink three times. If user pressed '1', which refer to 'Slave 1', the led on slave 1 will blink. When user pressed '2' which refer to 'Slave 2', the led on slave 2 will blink.

The MASTER also send the message (data) across to either Salve 1 and Slave 2. Telling Slave 1/2 on how many times to blink the LED light.



Schematics and Board Design

I used a pull-up resistor of 10k ohm for the I2C bus (on the SDA and SCL lines) on the Master board. Only one sets of pull-up resistors is required for the whole I2C bus. Note: If your boards are far away from each other, you might reduce the resistor value.


Components required for stuffing my boards

Components require for the Master board :
1 x ATTiny45
1 x ISP pin header (2x3)
1 x 4 pos pin header (2x2)(for I2C)
3 x Resistor 10kΩ
1 x Resistor 1KΩ
1 x LED Red
1 x Capacitor 1uF
1 x FTDI header (1x6)


Components require for two Slaves boards :
2 x ATTiny45
2 x ISP pin header (2x3)
2 x 4 pos pin header (2x2)(for I2C)
2 x Resistor 10kΩ
2 x Resistor 1KΩ
2 x LED Red
2 x Capacitor 1uF


Components require for I2C ribbon :
1 x ribbon cable
3 x 4 Pos. receptacle connectors
Note : Preferably to use ATTiny85 if your Fab Lab has stock, as the Tiny85 provide more Program Memory Space.


Milling and stuffing my boards

In EAGLE, I rotated the boards to be in verical orientation, so I can print two boards together in one 2"x3" FR1 copper-clad board. Soldering three boards at one go took some time and effort. Laster I used a glue gun and apply glue to all the vertical as well as horizontal connector to make sure it is firmly secured in position.

    


My completed boards



Programming : Software Serial and I2C

1) Burn the bootloader :
After finished milling the three boards, burn the bootloader using Arduino IDE. Ensure that the clock speed is set to run at 'Internal 8Mhz', if you set to Internal 1Mhz, you will see 'funny' text on the Serial Monitor.

Upload a simple blink LED Arduino sketch to see if the LEDs are working fine.

2) The 'TinyWire' library
As I am using the 'ATTiny' microntroller, I downloaded TinyWireM.h library (for Master) and TinyWireS.h library (for slave)

If you are using the ATMega328P microntroller, you can using the built-in 'Wire' Arduino library.

3) Coding : Initilaizing I2C
Addressing : There is no need to specify the address for the single Master in the I2C bus arrangement. If the board is initialised as a slave, then you would need to specify an address, 0 to 127 (7-bit address), as its parameter to uniquely identifying it on the I2C bus. (Simon Monk, 2019, pp157)

Master sending data :
The 'TinyWireM.beginTransmission(slave address);' function enable the Master to start sending data to the designated slave address which is on the I2C bus.

The TinyWireM.send(data) buffer up bytes to send, this function can be called multiple times

TinyWireM.endTransmission() function actually send the bytes in the buffer

4) Upload the code to the Master :
Note : I added in a few more global variable and this quickly filled up the 'dynamic memory' space.





My Code for the MASTER :

/*
  Title: attiny_i2c_master.ino
  Author: Lucas Lim 
  Date Created: 29/04/2019
  Last Modified: 29/04/2019
  
  Purpose: MASTER receive 'instruction' from PC via serial port (Tx, Rx)
           MASTER reply acknoledge message back to PC
           If instruction is to Slave 1/2, MASTER re-route to addressed 
              Slave 1/2 via I2C bus 
           Instruction :
             '0' MASTER will Blink LED
             '1' Slave 1 will blink LED
             '2' Slave 2 will blink LED
  NOTE! - It is important to use pullups on the SDA & SCL lines on the I2C bas!             
*/

#include < SoftwareSerial.h>
#include < TinyWireM.h>

#define slave1 (1)
#define slave2 (2)

const int ledPin = 1;   //PB1 - Arduino Pin No. 1
const int tx = 3;       //PB3 - Arduino Pin No. 3
const int rx = 4;       //PB4 - Arduino Pin No. 4
int times = 3;          //Number of times that LED will blink

SoftwareSerial mySerial(rx, tx);

void setup() {
  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
  mySerial.println("< Serial Communication is ready>");

  // initialize the led pin as an output.
  pinMode(ledPin, OUTPUT);

  // join I2C bas as MASTER
  TinyWireM.begin();
}



void process_incoming_command(char cmd)
// Received user command from host PC via Software Serial
{
  switch (cmd)
  {
    case '0':                     // MASTER to blink led
      blink_led(times);
      break;
    case '1':                    // Slave 1 to blink led
      TinyWireM.beginTransmission(slave1);
      TinyWireM.send(times);
      TinyWireM.endTransmission();
      break;
    case '2':                   // Slave 2 to blink led
      TinyWireM.beginTransmission(slave2);
      TinyWireM.send(times);
      TinyWireM.endTransmission();
      break;
  }
}


void blink_led(int times)
{
    while(times--)
    {
        digitalWrite(ledPin, HIGH);
        delay(500);
        digitalWrite(ledPin, LOW);
        delay(500);
        
        // for debugging
        //mySerial.print("Blink times : "); 
        //mySerial.println(times); 
    }
}

void loop() {
  // don't read unless there is data, Serial.available() > 0
  if(mySerial.available())
  {
    char cmd = mySerial.read();  //User to type '0' for Master, '1' for Slave 1 and '2' for Slave 2"
    mySerial.print("You typed : ");
    mySerial.println(cmd); 
    process_incoming_command(cmd);
  }
  
  delay(50);   //limit how fast the serial check/update
}


My Code for Slave 1 :
Note : The code for Slave 2 is the same as Slave 1 except the following line at the top :
  #define I2C_SLAVE_ADDRESS (2)
which set the I2C address for Slave 2 board

/*
  Title: attiny_i2c_slave1.ino
  Author: Lucas Lim 
  Date Created: 29/04/2019
  Last Modified: 29/04/2019
  
  Purpose: Receive data from MASTER via the I2C bus
           Blink the LED
*/

#include < TinyWireS.h>

#define I2C_SLAVE_ADDRESS (1)

const int ledPin = 4;       //PB4 - Arduino Pin No. 4
//int times = 0;            //Number of times that LED will blink
byte received = 0;          //Received data(on number of times that LED will blink) from MASTER


void setup()
{
    TinyWireS.begin(I2C_SLAVE_ADDRESS);    // join i2c network as slave

}


void blink_led(int times)
{
    while(times--)
    {
        digitalWrite(ledPin, HIGH);
        delay(500);
        digitalWrite(ledPin, LOW);
        delay(500);
    }
}

 
void loop()
{
    
    if (TinyWireS.available()) {
      received = TinyWireS.receive();
      blink_led(received);
    }
    received = 0;
    
         
    // This needs to be here
    TinyWireS_stop_check();
}
 


Video on I2C implementation

Duration : 57 secs
Audio : No




Group Assignment: send a message between two projects

I am the one and only one in the Fab Lab taking the Fab Academy Course in Singapore. So I will use my existing projects to connect them together and send a message across.

So far I been just blinking the LED, now is the time to move on to doing some useful stuff.

On week 11, I had build a board and connect to an ultrasonic sensor.


On Week 12, I had build another board and connected to a LCD display module with a I2C adaptor.


My idea is to connect these two boards/project together and have the reading taken by ultrasonic sensor to pass to the I2C LCD dsiplay module.



Video on demostration of commnuication between networked projects

Duration : secs
Audio : No



Reflection :

I ran into a problem while milling my boards, I didn't realise the tip of the v-bit was worn off. Next time when milling few boards at a go, it is best to select a sharp v-bit. While etching to check the traces to see if it look ok.


The ATtiny45 is a 8-pin microcontroller, with maximum 6 pins I/O pins for use. Using I2C greatly expands its capability and made it a bit more useful. However the memory space is quite little.

There are many I2C libraries online, this took me some time to look for the right one that will work with the ATTiny45.




Files :

- I2C Master Schemetic
- I2C Master Board
- I2C Slave Schemetic
- I2C Slave Board
- Arduino Code for Master board : attiny_i2c_master.ino.ino
- Arduino Code for Slave 1 : attiny_i2c_slave1.ino
- Arduino Code for Slave 2 : attiny_i2c_slave2.ino



References :

- Wikipedia : I²C
- Sparkfun : I²C
- Circuit Basic : I²C
- ATTiny I2C slave
- Simon Monk, 2019, Programming Arduino Next Steps : Going Futhur with sketches, Second Edition, McGraw-Hill Education, USA, pp 153 - 159 on I2C