Week 11: Communication and Networking
Objectives
Group Assignment
As a group, the goal is simpler but stricter: make two different projects talk to each other. That means agreeing on how we send data, making sure both sides understand it, and actually getting a message to go from one system to another reliably.
To know more, visit Group Assignment .
I2C Communication
IIC or I2C is short form of Inter-Integrated Circuits,It's a type protocol to communicate in between Integrated circuits,Microcontrollers,sensors etc.. just like USB in Computers.there are many communication peripheral available for Microcontrollers.Most of the Microcontrollers have two or more different type of protocols. The advantages of I2C is most of the Devices supports the protocol and very easy to use two wire required for data and clock.
Source
The above figure shows how a simple I2C Network look like.One device act as Master others are called slaves,and bidirectional data transfer are possible with the permission of the master device.The pins that required for this communication protocol are the SDA (Serial Data) and THe SCL (Serial Clock)
SOURCE
I2C Pins
- SDA (Serial Data Line) – Used to transfer data
- SCL (Serial Clock Line) – Used for clock synchronization
- GND – Common ground
I2C Pin Diagram
Master Board – LED Chaser (Week 08)
For the master device, I used my Week 08 PCB (LED Chaser board). This board acts as the controller and sends commands to the slave nodes.
Week 08 Link: View Week 08 Documentation
Master Board Image
Master Code
#include <Wire.h>
int leds[] = {2,3,4,5};
void setup() {
Wire.begin();
for(int i=0;i<4;i++){
pinMode(leds[i], OUTPUT);
}
}
void loop() {
for(int i=0;i<4;i++){
digitalWrite(leds[i], HIGH);
delay(200);
digitalWrite(leds[i], LOW);
}
Wire.beginTransmission(0x08);
Wire.write(1);
Wire.endTransmission();
Wire.beginTransmission(0x09);
Wire.write(1);
Wire.endTransmission();
delay(1000);
}
Master Working Video
Node 1 – Vibrator PCB (Week 10)
For Node 1, I used my Week 10 output device PCB (Vibrator). This board acts as a slave device and responds to commands from the master.
Week 10 Link: View Week 10 Documentation
Node 1 Image

Node 1 Code
#include <Wire.h>
#define SLAVE_ADDRESS 0x08
#define MOTOR_PIN 2
void setup() {
pinMode(MOTOR_PIN, OUTPUT);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
}
void loop() {}
void receiveEvent(int x) {
int cmd = Wire.read();
if(cmd == 1){
digitalWrite(MOTOR_PIN, HIGH);
delay(500);
digitalWrite(MOTOR_PIN, LOW);
}
}
Node 1 Working Video
Node 2 – LED PCB (Week 11)
For this week, I designed and fabricated a new PCB with 4 SMD LEDs. This board acts as another I2C NODE device.
To design a PCB in KiCad, we begin by creating a new project, which generates two main files: one for the schematic (.kicad_sch) and another for the PCB layout (.kicad_pcb). In the Schematic Editor, you add components by pressing A, wire them using W, and include power symbols like +5V or GND.Running the Electrical Rules Checker (ERC) helps catch common mistakes.
Once complete, we transfer the schematic to the PCB Editor using the "Update PCB from Schematic" tool. In the PCB Editor, drew the board outline on the Edge.Cuts layer. Then route the copper traces using the Route Tracks tool. Finally, a Design Rule Check (DRC) helps ensure there are no layout violations. After verifying the design, we can generate Gerber files for fabrication.
I used Gerber2png software to convert my PCB design's Gerber files into PNG images. After the conversion, I obtained separate images for the trace layer and the outline layer. The trace layer showed the copper connections on the board, illustrating how the components were electrically linked. The outline layer displayed the board’s physical boundary, which I had drawn on the Edge.Cuts layer in KiCad.
I then uploaded the trace and outline images into mods CE, setting them up for PCB milling.
Milling the PCB
I wanted to make two PCBs. I made one in Modela MDX-20 milling machine
Rolland Modela MDX-20 milling machine
I used the FR-1 copper-clad board . I installed a 1/64-inch end mill for milling the traces and carefully set the X, Y, and Z origins, making sure the bit just touched the board’s surface. Using mods CE , I loaded my PNG file, selected the "PCB traces (1/64)" process, and generated the toolpath. I sent the job to the machine and monitored it as it milled the traces. Once done, I changed the bit to a 1/32-inch mill for cutting the board outline, reset the Z-axis, and repeated the process with the appropriate settings. After milling, I removed the board, cleaned it with isopropyl alcohol, and inspected it for any imperfections
Assembling the components
I had two PCBs to assemble as I was planning to add two nodes. A board was already built in electronics design week.I used the Interactive HTML BOM to identify and source the required components from FabStash. After receiving the parts, I soldered them onto the PCB. Post-soldering, I inspected the board both visually under a microscope to check for solder bridges, alignment, and cold joints, and electrically using a multimeter to verify continuity and proper connections.
Inspecting the PCBs
Node 2 Image

Node 2 Code
#include <Wire.h>
#define SLAVE_ADDRESS 0x09
#define LED_PIN 3
void setup() {
pinMode(LED_PIN, OUTPUT);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
}
void loop() {}
void receiveEvent(int x) {
int cmd = Wire.read();
if(cmd == 1){
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
}
}
Node 2 Working Video
Final Communication (All Boards)
Master and Node 1 Communication (OLED & Motor)
The master board sends data to Node 1 through I2C communication. Node 1 displays information on the OLED and activates the vibrating motor based on received signals.
Master Code
#include#define SDA_PIN D4 #define SCL_PIN D5 #define MOTOR_PIN D2 volatile int mode = 0; void receiveEvent(int howMany) { while (Wire.available()) { mode = Wire.read(); } } void setup() { pinMode(MOTOR_PIN, OUTPUT); Wire.setSDA(SDA_PIN); Wire.setSCL(SCL_PIN); Wire.begin(8); Wire.onReceive(receiveEvent); } void loop() { if (mode == 0) { digitalWrite(MOTOR_PIN, LOW); } else if (mode == 1) { digitalWrite(MOTOR_PIN, HIGH); delay(100); digitalWrite(MOTOR_PIN, LOW); delay(100); } else if (mode == 2) { digitalWrite(MOTOR_PIN, HIGH); delay(50); digitalWrite(MOTOR_PIN, LOW); delay(50); } else if (mode == 3) { digitalWrite(MOTOR_PIN, HIGH); } }
Node 1 Code
#include// ✅ USE GPIO NUMBERS (NOT D PINS) #define SDA_PIN 4 #define SCL_PIN 5 #define MOTOR_PIN 2 // MOSFET control pin volatile int mode = 0; // I2C receive function void receiveEvent(int howMany) { while (Wire.available()) { mode = Wire.read(); } } void setup() { pinMode(MOTOR_PIN, OUTPUT); analogWrite(MOTOR_PIN, 0); // motor OFF initially // ✅ I2C SLAVE INIT Wire.setSDA(SDA_PIN); Wire.setSCL(SCL_PIN); Wire.begin(8); // slave address = 8 Wire.onReceive(receiveEvent); } void loop() { // Motor vibration control switch (mode) { case 0: // OFF analogWrite(MOTOR_PIN, 0); break; case 1: // LOW vibration analogWrite(MOTOR_PIN, 80); break; case 2: // MEDIUM vibration analogWrite(MOTOR_PIN, 150); break; case 3: // HIGH vibration analogWrite(MOTOR_PIN, 255); break; default: analogWrite(MOTOR_PIN, 0); break; } delay(5); // stability }
Working Video
Final I2C Communication Setup
All boards were connected together using I2C communication with shared SDA and SCL lines. The master successfully controlled both Node 1 and Node 2 simultaneously.
Master Code
#include#include #include #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 32 #define SDA_PIN D4 #define SCL_PIN D5 #define BUTTON_PIN D3 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); int mode = 0; int lastButtonState = HIGH; unsigned long lastDebounceTime = 0; int debounceDelay = 200; void showDisplay() { display.clearDisplay(); display.setCursor(0, 10); display.print("MODE: "); switch (mode) { case 0: display.println("OFF"); break; case 1: display.println("LOW"); break; case 2: display.println("MED"); break; case 3: display.println("HIGH"); break; } display.display(); } void setup() { pinMode(BUTTON_PIN, INPUT_PULLUP); Wire.setSDA(SDA_PIN); Wire.setSCL(SCL_PIN); Wire.begin(); // MASTER if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { while (1); } display.setTextSize(1); display.setTextColor(WHITE); showDisplay(); } void loop() { int reading = digitalRead(BUTTON_PIN); if (reading == LOW && lastButtonState == HIGH && (millis() - lastDebounceTime) > debounceDelay) { lastDebounceTime = millis(); mode++; if (mode > 3) mode = 0; showDisplay(); // Send to Slave 1 Wire.beginTransmission(8); Wire.write(mode); Wire.endTransmission(); // Send to Slave 2 Wire.beginTransmission(9); Wire.write(mode); Wire.endTransmission(); } lastButtonState = reading; }
Node 1 Code
#include#include #include #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 32 #define SDA_PIN D4 #define SCL_PIN D5 #define MOTOR_PIN D2 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); volatile int mode = 0; void receiveEvent(int howMany) { while (Wire.available()) { mode = Wire.read(); } } void setup() { pinMode(MOTOR_PIN, OUTPUT); analogWrite(MOTOR_PIN, 0); Wire.setSDA(SDA_PIN); Wire.setSCL(SCL_PIN); Wire.begin(8); // SLAVE 1 Wire.onReceive(receiveEvent); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { while (1); } display.setTextSize(1); display.setTextColor(WHITE); } void loop() { display.clearDisplay(); display.setCursor(0, 10); display.print("MODE: "); switch (mode) { case 0: display.println("OFF"); analogWrite(MOTOR_PIN, 0); break; case 1: display.println("LOW"); analogWrite(MOTOR_PIN, 80); break; case 2: display.println("MED"); analogWrite(MOTOR_PIN, 150); break; case 3: display.println("HIGH"); analogWrite(MOTOR_PIN, 255); break; } display.display(); delay(50); }
Node 2 Code
#include#define SDA_PIN D4 #define SCL_PIN D5 #define LED1 D0 #define LED2 D1 #define LED3 D2 #define LED4 D3 volatile int mode = 0; void receiveEvent(int howMany) { while (Wire.available()) { mode = Wire.read(); } } void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); Wire.setSDA(SDA_PIN); Wire.setSCL(SCL_PIN); Wire.begin(9); // SLAVE 2 Wire.onReceive(receiveEvent); } void loop() { digitalWrite(LED1, LOW); digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); switch (mode) { case 1: digitalWrite(LED1, HIGH); break; case 2: digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); break; case 3: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, HIGH); break; } delay(10); }
Final Working Video
I2C Communication Test
To verify communication between nodes, an I2C scanner was used.
The scanner detected a device at address 0x08, confirming that Slave 1 is properly connected and communicating with the master.
Further debugging showed that the second slave address 0x09)
Figure: I2C scanner output showing detected device at address 0x08.
Conclusion
In this week, I successfully implemented communication between multiple PCBs using the I2C protocol. I connected one master board and two slave boards, assigned unique addresses, and verified communication. This helped me understand networking between embedded devices.
Hero Shot