Network and communications


design, build, and connect wired or wireless node(s) with network or bus addresses
  • Explained the programming process/es you used.
  • Explained any problems you encountered and how you fixed them.
  • Included original design files and source code.
Group assignment:
  • Send a message between two projects
  • Document your work to the group work page and reflect on your individual page what you learned

Group Assignment

This assignment was made with the help of ChatGPT and edited by me.
For this assignment I want to use the I2C communication protocol. It is designed to be a simple and efficient method for inter-chip communication, particularly between integrated circuits on the same circuit board or within a close proximity. It allows multiple devices to be connected to a common bus, enabling them to exchange data and control signals.

I2C Communication Protocol

Overview

I2C (Inter-Integrated Circuit) is a widely used bus interface connection protocol for short-distance serial communication. It was originally developed by Philips Semiconductor in 1982. The protocol utilizes two bi-directional open-drain lines, namely Serial Data (SDA) and Serial Clock (SCL), which are pulled high with resistors.

Modes and Data Transfer

I2C operates in two modes: Master mode and Slave mode. In Master mode, the master device initiates communication and controls the data transfer. Slave devices, on the other hand, respond to the commands and data received from the master. Each data bit transmitted on the SDA line is synchronized by clock pulses on the SCL line.

Start and Stop Conditions

According to the I2C protocol, data on the SDA line can only change when the SCL line is low. To ensure a high level on the lines, pull-up resistors are required. The data is transmitted in packets consisting of 9 bits, including a Start bit, 8 bits for Slave Address, and an Acknowledge (ACK) bit.

Addressing and Packet Format

Addressing is performed by the master, which sends the address of the desired slave device. The addressed slave compares its own address with the received address and responds with an ACK if it matches. The communication is carried out in the form of packets, with the data transmitted over the SDA line and the 9th bit reserved for ACK/NACK.

Features and Advantages

I2C features include half-duplex communication, synchronous communication in the form of frames or blocks, support for multi-master configurations, clock stretching for synchronization, and arbitration to handle multi-master bus systems. It is a cost-efficient protocol with improved error handling capabilities due to the use of ACK/NACK.

Advantages
  • Can be configured in multi-master mode
  • Reduced complexity with only two bi-directional lines
  • Cost-effective
Limitations
  • Slower speed compared to other protocols
  • Half-duplex communication is used

Comparison with other communication protocols:

Protocol Pros Cons
SPI (Serial Peripheral Interface)
  • Faster data transfer rate compared to I2C
  • Full-duplex communication
  • No device addressing required
  • Requires more pins and additional signal lines
  • Typically limited to short distances
  • Not suitable for multi-master configurations
UART (Universal Asynchronous Receiver-Transmitter)
  • Simple and widely supported protocol
  • Can operate over long distances
  • Typically used for point-to-point communication
  • Limited speed compared to I2C and SPI
  • Requires separate data lines for each device
  • Not suitable for multi-master configurations
CAN (Controller Area Network)
  • Designed for robust and reliable communication
  • Supports long-distance communication
  • Supports multi-master configurations
  • Higher complexity and cost
  • Slower data transfer rate
  • Not as widely supported in general-purpose consumer electronics
USB (Universal Serial Bus)
  • High-speed data transfer and versatile protocol
  • Supports hot-plugging and a wide range of devices
  • Provides power supply to connected devices
  • More complex to implement
  • Requires host controllers and device enumeration
  • Not suitable for direct chip-to-chip communication
Ethernet
  • High-speed and widely supported protocol for network communication
  • Supports long-distance communication
  • Suitable for connecting devices in distributed systems
  • Higher cost and complexity
  • Requires network infrastructure and IP addressing
  • Not typically used for direct chip-to-chip communication

I chose to implement the I2C (Inter-Integrated Circuit) protocol for this project due to its simplicity and ease of implementation. With its straightforward bus interface connection and minimal wiring requirements, I2C allows for quick and hassle-free integration of the master and slave devices. The use of only two bi-directional lines, SDA (Serial Data) and SCL (Serial Clock), simplifies the hardware setup and reduces the complexity of the communication system. Additionally, the availability of libraries and well-documented resources for I2C programming further facilitates the development process.

For this assignment, I want to set up an Arduino Nano as the I2C master and two XIAO SAMD21's as the I2C slaves. This way, the Arduino Nano can request data from each of the slave devices.

Step 1: Hardware Connection

Components:

  • Arduino Nano
  • 2x XIAO SAMD21
  • Jumper wires
  • Breadboard
  • USB-C cable
  • USB mini cable

Connection:

  • Connect the 3.3V pin of the Arduino Nano to the 3.3V pins of both the XIAO ESP32 and XIAO SAMD21. (Power them)
  • Connect the GND pin of the Arduino Nano to the GND pins of both XIAO SAMD21s.
  • Connect the A4 pin (SDA) of the Arduino Nano to the SDA pins of both XIAO SAMD21s.
  • Connect the A5 pin (SCL) of the Arduino Nano to the SCL pins of both XIAO SAMD21s.

Step 2: Coding the Slaves

First XIAO SAMD21 Code:
							
#include 

	#define SLAVE_ADDRESS 0x31
	
	void setup() {
		Wire.begin(SLAVE_ADDRESS); // Join I2C bus as a slave with address 0x31
		Wire.onRequest(requestEvent); // Attach event handler
	}
	
	void loop() {
		delay(100);
	}
	
	void requestEvent() {
		char *data = "SAMD21_1";
		Wire.write(data); // Respond with "SAMD21_1"
	}
					

This code demonstrates an Arduino sketch that sets up an Arduino board as an I2C slave device and responds to requests from the I2C master. Let's go through it step by step:

  1. #include <Wire.h>: This line includes the Wire library, which provides functions for I2C communication.
  2. #define SLAVE_ADDRESS 0x31: This line defines a constant value SLAVE_ADDRESS with the hexadecimal value 0x31. It represents the I2C address of the Arduino board acting as the slave device.
  3. void setup(): This function is called once during the setup phase of the Arduino. It is used to initialize and configure the board.
  4. Wire.begin(SLAVE_ADDRESS): This line initializes the Wire library and joins the I2C bus as a slave device with the specified SLAVE_ADDRESS.
  5. Wire.onRequest(requestEvent): This line attaches an event handler function requestEvent to the Wire library. The requestEvent function will be automatically called whenever the I2C master requests data from the slave device.
  6. void requestEvent(): This function is the event handler that is called when the I2C master requests data from the slave device.
  7. char *data = "SAMD21_1": This line declares a character pointer variable data and assigns it the address of the string "SAMD21_1". This string will be sent as a response to the I2C master's request.
  8. Wire.write(data): This line uses the Wire library to send the data string as a response to the I2C master's request. The Wire.write() function is responsible for transmitting the data over the I2C bus.
Second XIAO SAMD21 Code:
						
#include 

	#define SLAVE_ADDRESS 0x32
	
	void setup() {
		Wire.begin(SLAVE_ADDRESS); // Join I2C bus as a slave with address 0x32
		Wire.onRequest(requestEvent); // Attach event handler
	}
	
	void loop() {
		delay(100);
	}
	
	void requestEvent() {
		char *data = "SAMD21_2";
		Wire.write(data); // Respond with "SAMD21_2"
	}
				

This code does essentially the same as the first one, but it defines another address for the slave device and makes it send a different response to the I2C's master request indicating it's the second Slave.

Step 3: Coding the Arduino Nano (Master)
					
#include 

	void setup() {
		Wire.begin(); // Join I2C bus as master
		Serial.begin(9600); // Start serial communication at 9600 baud
	}
	
	void loop() {
		Wire.requestFrom(0x31, 8); // Request 8 bytes from the slave with address 0x31
		while(Wire.available()) {
		char c = Wire.read();
		Serial.print(c);
		}
		Serial.println();
		
		Wire.requestFrom(0x32, 8); // Request 8 bytes from the slave with address 0x32
		while(Wire.available()) {
		char c = Wire.read();
		Serial.print(c);
		}
		Serial.println();
		
		delay(1000);
	}
							
					
				

  1. #include <Wire.h>: This line includes the Wire library, which provides functions for I2C communication.
  2. void setup(): This function is called once during the setup phase of the Arduino. It is used to initialize and configure the board.
    • Wire.begin();: This line initializes the Wire library and joins the I2C bus as a master device.
    • Serial.begin(9600);: This line starts the serial communication with a baud rate of 9600, allowing communication with a computer via the Serial Monitor.
  3. Wire.requestFrom(0x31, 8);: This line sends a request to the slave device with the address 0x31, asking it to send 8 bytes of data.
  4. while (Wire.available()) { ... }: This loop is executed as long as there is data available to be read from the slave device.
    • char c = Wire.read();: This line reads one byte at a time from the slave device and assigns it to the variable c.
    • Serial.print(c);: This line prints the received byte to the Serial Monitor.
  5. Serial.println();: This line adds a newline character to the Serial Monitor, separating the data received from each slave device.
  6. Similar steps are repeated for the second slave device with the address 0x32.
  7. delay(1000);: This line adds a delay of 1000 milliseconds (1 second) before repeating the loop.

In summary: These codes set different I2C addresses for each of the XIAO SAMD21 boards (0x31 and 0x32) and the Arduino Nano requests data from each of them in turn. You should see the responses "SAMD21_1" and "SAMD21_2" printed in the Arduino Nano's serial monitor.

Upload the code to the first SAMD21
Upload to the second SAMD21 on ym selfmade pcb
Upload the master code to the Nano
Here is the full connection
If you now open the serial monitor on the Arduino Nano, you'll see both SAMD21's sending their message.
End of Group Assignment.

I2C communication between Arduino Nano and SAMD21

For more infor on my selfmade board (SAMD21) click here: Electronics design, Electronics production, Embedded programming

For the second part of the assignment, I will make the Arduino Nano (master) send a message to the XIAO SAMD21 (slave) and also request a message from the SAMD21 in return. This will demonstrate bidirectional communication between the two boards using I2C.

XIAO SAMD21 Code (Slave):

							
#include 

	#define SLAVE_ADDRESS 0x31
	
	char messageFromMaster[16];
	
	void setup() {
		Wire.begin(SLAVE_ADDRESS); // Join I2C bus as a slave with address 0x31
		Wire.onRequest(requestEvent); // Attach event handler for master request
		Wire.onReceive(receiveEvent); // Attach event handler for receiving data
		
		// Initialize message buffer
		strcpy(messageFromMaster, "No Message");
	}
	
	void loop() {
		// Nothing to do here
	}
	
	void requestEvent() {
		// Send a message to the master
		char *data = "Hello Master";
		Wire.write(data);
	}
	
	void receiveEvent(int bytes) {
		// Read the message sent by master
		int i = 0;
		while (Wire.available() && i < 15) {
		messageFromMaster[i++] = (char)Wire.read();
		}
		messageFromMaster[i] = '\0'; // Null terminate the string
	}
	
							
						
  1. char messageFromMaster[16]: This line declares a character array messageFromMaster with a size of 16. It will be used to store the message received from the I2C master.
  2. void setup():
    • Wire.begin(SLAVE_ADDRESS): This line initializes the Wire library and joins the I2C bus as a slave device with the specified SLAVE_ADDRESS.
    • Wire.onRequest(requestEvent): This line attaches an event handler function requestEvent to the Wire library. The requestEvent function will be automatically called whenever the I2C master requests data from the slave device.
    • Wire.onReceive(receiveEvent): This line attaches an event handler function receiveEvent to the Wire library. The receiveEvent function will be automatically called whenever the slave device receives data from the master.
    • strcpy(messageFromMaster, "No Message"): This line initializes the messageFromMaster array with the string "No Message". It sets a default value for the message in case no data is received from the master.
  3. void loop():
  4. void requestEvent(): This function is called when the I2C master requests data from the slave device.
    • char *data = "Hello Master": This line declares a character pointer variable data and assigns it the address of the string "Hello Master". This message will be sent to the master in response to its request.
    • Wire.write(data): This line uses the Wire library to send the data message to the I2C master.
  5. void receiveEvent(int bytes): This function is called when the slave device receives data from the I2C master.
    • int i = 0: This line initializes a counter variable i to zero, which will be used to index the elements of the messageFromMaster array.
    • while (Wire.available() && i < 15): This loop reads the data sent by the master byte by byte until there is no more data available or the array is full.
    • messageFromMaster[i++] = (char)Wire.read(): This line reads a byte of data from the I2C bus using the Wire library's Wire.read() function and assigns it to the next element of the messageFromMaster array. The i++ operation increments the value of i after the assignment.
    • messageFromMaster[i] = '\0': This line adds a null character at the end of the messageFromMaster array to terminate the string and make it a valid C-string.

Arduino Nano Code (Master):

							
#include 

	char messageFromSlave[16];
	
	void setup() {
		Wire.begin(); // Join I2C bus as master
		Serial.begin(9600); // Start serial communication at 9600 baud
	}
	
	void loop() {
		// Send a message to the slave
		Wire.beginTransmission(0x31);
		Wire.write("Hello Slave");
		Wire.endTransmission();
		
		delay(500);
		
		// Request 12 bytes from the slave
		Wire.requestFrom(0x31, 12);
		int i = 0;
		while(Wire.available() && i < 15) {
		messageFromSlave[i++] = (char)Wire.read();
		}
		messageFromSlave[i] = '\0'; // Null terminate the string
		
		// Print message received from the slave
		Serial.println(messageFromSlave);
		
		delay(1000);
	}
									
							
						
  1. void loop():
    • Wire.beginTransmission(0x31): This line begins the transmission to the slave device with the address 0x31.
    • Wire.write("Hello Slave"): This line sends the message "Hello Slave" to the slave device.
    • Wire.endTransmission(): This line ends the transmission to the slave device.
    • delay(500): This line adds a delay of 500 milliseconds (0.5 seconds) to allow time for the slave device to process the message.
    • Wire.requestFrom(0x31, 12): This line requests 12 bytes of data from the slave device with the address 0x31.
    • while (Wire.available() && i < 15) { ... }: This loop reads the data received from the slave byte by byte until there is no more data available or the array is full.
      • messageFromSlave[i++] = (char)Wire.read(): This line reads a byte of data from the I2C bus using the Wire library's Wire.read() function and assigns it to the next element of the messageFromSlave array. The i++ operation increments the value of i after the assignment.
    • messageFromSlave[i] = '\0': This line adds a null character at the end of the messageFromSlave array to terminate the string and make it a valid C-string.
    • Serial.println(messageFromSlave): This line prints the received message from the slave device to the Serial Monitor.
    • delay(1000): This line adds a delay of 1000 milliseconds (1 second) before repeating the loop.

In summary: In this example, the Arduino Nano sends a message "Hello Slave" to the XIAO SAMD21 and requests a message from it. The XIAO SAMD21 receives the message, and when requested, sends back "Hello Master". You should see "Hello Master" printed in the Arduino Nano's serial monitor. This example shows bi-directional communication between the Arduino Nano and the XIAO SAMD21 over I2C.

Upload the code to the "Slave"
Upload the code to the "Master"
Connection overwiew.
Here you can see the "Slave" sending "Hello Master" as requested by the "Master".