Sayeed97 / UNO-ESP8266-I2C

This repository contains all the files required for establishing and communicating data transfer between Arduino UNO and ESP8266.
GNU General Public License v3.0
0 stars 0 forks source link

I2C: Prototype I2C communication between UNO and ESP8266 #2

Open Sayeed97 opened 3 months ago

Sayeed97 commented 3 months ago

Write code that can successfully perform I2C communication between Arduino UNO and ESP8266.

Sayeed97 commented 2 months ago

Issue: Wire.h library has no trace of which device it is talking to other than its I2C address.

Arduino Memory Reference: https://wiki-content.arduino.cc/en/Tutorial/Foundations/Memory

New library features:

  1. When each device starts it will scan its environment for all the devices to create a map.
  2. Transmit data by device name and its unique ID this makes the transmission more robust, which means the I2C address is now mapped using the device name and unique ID provided at the time of instantiating this new I2C API.
  3. The name of the device and unique ID will be stored as a complex data structure mapped to the I2C device.
  4. Additionally the library provides the master to white list and blacklist certain I2C addresses.
Sayeed97 commented 2 months ago

In the I2C (Inter-Integrated Circuit) protocol, it is indeed possible to have multiple master devices on the same bus. However, this arrangement requires careful coordination and adherence to specific rules to prevent conflicts and ensure proper communication.

Here are some key points regarding multiple I2C master devices:

  1. Bus Arbitration: When multiple masters share the same I2C bus, they must follow a protocol for bus arbitration. This means that if two or more masters attempt to communicate simultaneously, they need a mechanism to determine which one gets control of the bus. The I2C protocol uses a combination of start/stop conditions and clock stretching to handle this.

  2. Start and Stop Conditions:

    • Start Condition: A master initiates communication by sending a start condition (S) on the bus. This indicates that it wants to communicate with a slave device.
    • Stop Condition: After completing its transaction, the master sends a stop condition (P) to release the bus.
  3. Addressing:

    • Each I2C device (master or slave) has a unique 7-bit or 10-bit address.
    • When a master wants to communicate with a specific slave, it sends the slave's address followed by a read/write bit.
    • If multiple masters are present, they must coordinate to avoid addressing conflicts.
  4. Clock Synchronization:

    • All masters on the bus share the same clock signal (SCL).
    • If one master holds the clock line low (clock stretching), other masters must wait until it releases the line.
    • Clock synchronization ensures that data is transmitted correctly.
  5. Priority and Design Considerations:

    • Masters can have different priorities based on their roles (e.g., real-time control vs. peripheral management).
    • Design considerations include ensuring that masters do not interfere with critical operations or cause delays.
  6. Multi-Master Scenarios:

    • Equal Priority: In some cases, multiple masters have equal priority. They must cooperate to avoid collisions.
    • Hierarchical: One master may control other sub-masters, each responsible for specific tasks.
    • Bus Switching: Some systems use bus switches to isolate segments of the I2C bus.
Sayeed97 commented 2 months ago

Both the ESP8266 (NodeMCU) and the Arduino UNO need to run the Wire.h code for successful I2C communication. Let me explain why:

  1. Master-Slave Relationship:

    • In an I2C setup, one device acts as the master, and the other is the slave.
    • The master initiates communication by sending requests or data to the slave.
    • The slave responds to the master's requests.
  2. Communication Flow:

    • When the master wants to send data to the slave, it uses the Wire.beginTransmission() function to specify the slave's address and writes data using Wire.write().
    • The slave receives this data when the master calls Wire.endTransmission().
    • Conversely, when the slave wants to send data to the master, it uses Wire.requestFrom() to request data from the master.
  3. Both Devices Must Participate:

    • If only one device runs the Wire.h code, communication won't occur.
    • Both devices must actively participate in the I2C communication process.
    • The master sends commands or data, and the slave responds accordingly.
  4. Addressing:

    • Each I2C device has a unique address.
    • The master specifies the address of the slave it wants to communicate with.
    • In your case, the UNO (master) communicates with the ESP8266 (slave) using their respective addresses.
Sayeed97 commented 2 months ago

In the I2C (Inter-Integrated Circuit) protocol, it is indeed possible for multiple devices to communicate on the same I2C bus. However, there are some important considerations to keep in mind:

  1. Shared Bus Architecture:

    • I2C allows for multiple slave devices to be connected to a single master device.
    • The maximum number of slaves that can be connected to the bus depends on the addressing scheme used by the devices.
    • In the standard 7-bit addressing scheme, up to 128 slave devices can be connected to a single I2C bus².
  2. Unique Addresses:

    • Every I2C device on the bus must have a unique address.
    • If you're using only one I2C device, you might not have realized this requirement because most drivers use predefined default addresses.
    • However, when using multiple copies of the same I2C device, address conflicts become unavoidable.
    • There are three main ways to deal with this:

      • Alternate Address: Some devices allow you to set an alternate address. If possible, use this approach.
      • I2C Channel Multiplexer: If alternate addresses are not possible, consider using an I2C channel multiplexer. This device allows you to switch between different I2C channels, effectively isolating devices with the same address.
      • Alternate I2C Ports: As a last resort, you can use different I2C ports (if available) on your microcontroller.
  3. Hardware Solutions:

    • When working with multiple I2C devices, you can use hardware solutions like the TCA9548A I2C multiplexer.
    • The TCA9548A allows you to select one of its eight channels, effectively enabling communication with different devices on the same bus.
    • Other I2C multiplexers may also serve this purpose.
  4. Example Scenario:

    • Imagine you've found the perfect I2C sensor, but it has a fixed I2C address.
    • By using an I2C multiplexer, you can connect multiple sensors with the same address to your microcontroller, allowing them to communicate on the same bus.
Sayeed97 commented 2 months ago

The two pins which you need for the I2C communication are the following:

SDA (Serial Data): Connection between master and slave to send and receive data. SCL (Serial Clock): Shares the clock signal between the master and the slave, where the master always controls the clock signal. I2C overview

The Serial Data line and the Serial Clock line are pulled up with resistors. Therefore when there is no transmission of data on the bus, the SDA and SCL are in the HIGH state. Why a resistor is needed, see subsection “Physical layer”. Typical voltages are +5V and +3.3V and devices can communicate at 100 kHz or 400 kHz. All I2C devices are connected to the bus either with open collector or open drain pins to pull the line LOW. The communication between master and slave occurs by toggling the lines by pulling LOW and releasing HIGH. Also bits are clocked on falling clock edges.

There may be four potential modes of operation for a given bus device, although most devices only use a single role and its two modes:

master transmit – master node is sending data to a slave, master receive – master node is receiving data from a slave, slave transmit – slave node is sending data to the master, slave receive – slave node is receiving data from the master.

Reference: https://diyi0t.com/i2c-tutorial-for-arduino-and-esp8266/

charantejaabbavathini commented 2 months ago

Arduino Being Slave Code

///Arduino Sketch for Arduino Uno (Slave I2C Device)
#include <Wire.h>

void setup() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent); /* register receive event */
 Serial.begin(9600);           /* start serial for debug */
}

void loop() {
 delay(100);
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
 while (0 <Wire.available()) {
    char c = Wire.read();      /* receive byte as a character */
    Serial.print(c);           /* print the character */
  }
 Serial.println();             /* to newline */
}

ESP8266 Being Master Code

//Arduino Sketch for NodeMCU (Master I2C Device)
#include <Wire.h>

#define SDA 4
#define SCL 5

void setup() {
 Serial.begin(9600); /* begin serial for debug */
 Wire.begin(SDA, SCL); /* join i2c bus with SDA=D1 (GPIO 5) and SCL=D2 (GPIO 4) of NodeMCU */
}

void loop() {
 Wire.beginTransmission(8); /* begin with device address 8 */
 Wire.write("Hello Arduino");  /* sends hello string */
 Wire.endTransmission();    /* stop transmitting */

 delay(1000);
}

Original Working Code Reference: https://github.com/microdigisoft/ESP8266-I2C-communication-Using-Arduino/blob/main/i2c_master___salve.ino

charantejaabbavathini commented 2 months ago

Customized the above code to make the Master first send "Hello\n", to which the Slave performs a String match to reply it with its DEVICE_NAME.

UNO Slave Code:

///Arduino Sketch for Arduino Uno (Slave I2C Device)
#include <Wire.h>
#include <string.h>

#define DEVICE_NAME "UNO"

void setup() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent); /* register receive event */
 Serial.begin(9600);           /* start serial for debug */
}

void loop() {
 delay(100);
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
  String recvInfo;
 while (0 <Wire.available()) {
    char c = Wire.read();      /* receive byte as a character */
    if(c == '\n')
      break;
    recvInfo += c;
    //Serial.print(c);           /* print the character */
  }
  Serial.print(recvInfo);
  if(recvInfo == "Hello")
    Wire.onRequest(requestEvent);
 Serial.println();             /* to newline */
}

// function that executes whenever data is requested from master
void requestEvent() {
 Wire.write(DEVICE_NAME);  /*send string on request */
}

ESP8266 Master Code:

//Arduino Sketch for NodeMCU (Master I2C Device)
#include <Wire.h>

#define SDA 4
#define SCL 5

void setup() {
 Serial.begin(9600); /* begin serial for debug */
 Wire.begin(SDA, SCL); /* join i2c bus with SDA=D1 (GPIO 5) and SCL=D2 (GPIO 4) of NodeMCU */
}

void loop() {
  Wire.beginTransmission(8); /* begin with device address 8 */
  Wire.write("Hello\n");  /* sends hello string */
  Wire.endTransmission();    /* stop transmitting */

  Wire.requestFrom(8, 3);
  Serial.print("Device Name: ");
  while(Wire.available()){
    char c = Wire.read();
    Serial.print(c);
  }
  Serial.println();
  delay(1000);
}
charantejaabbavathini commented 2 months ago

TODO: Switch UNO as the Master and ESP8266 as the Slave; Build and Test.