Complete guide: Choosing an industrial controller
I2C is a two-wire synchronous bus: SDA carries the data and SCL the clock, and a master talks to slaves that each have a 7-bit address. On an Industrial Shields Arduino based PLC, you connect the devices to the SDA and SCL terminals of the PLC (Arduino pins 20 and 21 on the M-Duino 21+), add pull-up resistors, and use the Wire library: beginTransmission(), write() and endTransmission() to write, requestFrom() and read() to read. I2C is made for short distances, inside the cabinet.
All the Arduino based controllers of Industrial Shields, M-Duino and Ardbox, have an I2C port, and so do the ESP32 PLC and the Raspberry PLC. Many devices have an I2C connection: analog to digital converters, real-time clocks, EEPROM memories, displays, accelerometers and compasses. This guide uses an M-Duino 21+. The Wire code is the same on the other Arduino based models: only the SDA and SCL terminals change.
How does the I2C protocol work?
Philips created I2C (Inter-Integrated Circuit) in 1982 to connect chips inside its products. Today the specification is published by NXP (UM10204). The master generates the clock and starts every transfer. Slaves never start a transfer and do not talk to each other.
- Start. The master pulls SDA low while SCL is high.
- Address and direction. 7 address bits and 1 bit for read or write.
- ACK. The slave with that address pulls SDA low for one clock to say "I am here".
- Data. One or more bytes, each one followed by an ACK. When the master reads, it answers the last byte with NACK.
- Stop. The master releases SDA while SCL is high. With a repeated start instead of a stop, the master keeps the bus and starts a new transfer.

| Mode | Speed | Wire.setClock() on the AVR |
|---|---|---|
| Standard mode | 100 kbit/s | 100000 |
| Fast mode | 400 kbit/s | 400000 |
| Fast mode plus | 1 Mbit/s | Not on all processors |
| High speed mode | 3.4 Mbit/s | Not on all processors |
The 7-bit address gives 128 combinations. The specification reserves 0x00 to 0x07 and 0x78 to 0x7F, so devices use 0x08 to 0x77. Each device on the bus needs its own address. Many devices let you change the last bits with address pins.
Advantages and limits of I2C

| Advantages | Limits |
|---|---|
| Two wires (plus ground) for many devices | Short distance: the bus capacitance limits the cable length |
| Each byte is acknowledged (ACK), so the master knows if a device answers | No CRC: the ACK says that a byte arrived, not that its content is right |
| Addressing on the bus, without one select line per device | Half duplex and medium speed |
| Supported by most microcontrollers and sensors | A device that holds SDA low can block the whole bus |
Wiring and pull-up resistors
Connect SDA to SDA and SCL to SCL on every device, and connect the grounds. SDA and SCL are open-drain lines: the devices only pull them down, and a pull-up resistor to Vcc brings them up.

- Pull-ups.
Wire.begin()turns on the internal pull-ups of the microcontroller, but they are weak: the edges rise slowly, and the bus only works at low speed and with short cables. For a reliable bus, add external pull-ups of 1 kΩ to 4.7 kΩ. Industrial Shields has an I2C pull-up resistor module for DIN rail. - Logic level. The microcontrollers of the M-Duino and the Ardbox work at 5 V. A 3.3 V device needs a level shifter.
- Distance. Keep the I2C wiring short and inside the cabinet. For longer distances use an I2C bus extender, or a field bus such as RS-485 with Modbus.
- Addresses in use. On models with I/O expanders on the I2C bus, such as the Raspberry PLC, some addresses are already taken. The datasheet of the Raspberry PLC lists them.
Where are SDA and SCL on the PLC?
Each PLC has the SDA and SCL terminals on its communication connector. Check the user guide of your model. On the M-Duino 21+, SCL is Arduino pin 21 and SDA is Arduino pin 20. On the Ardbox HF+, SDA is pin 2 and SCL is pin 3.

The Wire library
The Arduino IDE includes the Wire library (Wire.h), which controls the I2C hardware of the microcontroller. With the Industrial Shields boards installed, you find its examples in File > Examples > Wire.

| Function | What it does |
|---|---|
Wire.begin() | Starts I2C as master. Wire.begin(address) starts it as a slave with that address |
Wire.setClock(100000) | Sets the clock: 100000 (standard) or 400000 (fast) |
Wire.beginTransmission(address) | Prepares a write to the slave with that 7-bit address |
Wire.write(data) | Queues one or more bytes. Returns the number of bytes queued |
Wire.endTransmission(stop) | Sends the queued bytes. With false it ends with a repeated start and keeps the bus. Returns 0 if it worked, 1 data too long, 2 NACK on the address, 3 NACK on the data, 4 other error, 5 timeout |
Wire.requestFrom(address, quantity, stop) | Reads a number of bytes from a slave. Returns the bytes received |
Wire.available() and Wire.read() | Number of bytes waiting, and the next byte |
Wire.onReceive(handler) and Wire.onRequest(handler) | In a slave: functions called when the master sends data or asks for data |
Wire.setWireTimeout(us, reset) | Time limit for a transfer, so that a blocked bus does not stop the program |
Complete example: scan the bus and read a register
This sketch makes the PLC the I2C master. At start it scans the bus and prints the address of each device that answers. Then, every second, it reads two bytes from a register of one device: it writes the register number, keeps the bus with a repeated start and reads the answer. Change DEVICE_ADDRESS, FIRST_REGISTER and BYTES_TO_READ with the data of your device.
// I2C master on an Industrial Shields Arduino based PLC (M-Duino, Ardbox)
// 1. Scans the bus and prints the address of every device that answers.
// 2. Reads N bytes from a register of one device (write pointer + repeated start).
#include <Wire.h>
const uint8_t DEVICE_ADDRESS = 0x48; // 7-bit address of your device (see its datasheet)
const uint8_t FIRST_REGISTER = 0x00; // register to read
const uint8_t BYTES_TO_READ = 2;
void scanBus() {
uint8_t found = 0;
for (uint8_t address = 0x08; address <= 0x77; address++) { // 0x00-0x07 and 0x78-0x7F are reserved
Wire.beginTransmission(address);
if (Wire.endTransmission() == 0) { // 0 = the device answered with ACK
Serial.print("Device found at 0x");
Serial.println(address, HEX);
found++;
}
}
if (found == 0) Serial.println("No I2C device found: check SDA, SCL, GND and pull-ups");
}
bool readRegisters(uint8_t address, uint8_t reg, uint8_t *buffer, uint8_t length) {
Wire.beginTransmission(address);
Wire.write(reg); // register pointer
if (Wire.endTransmission(false) != 0) { // false = repeated start, keep the bus
return false; // NACK, bus error or timeout
}
if (Wire.requestFrom(address, length) != length) {
return false;
}
for (uint8_t i = 0; i < length; i++) {
buffer[i] = Wire.read();
}
return true;
}
void setup() {
Serial.begin(115200);
Wire.begin(); // this PLC is the master
Wire.setClock(100000); // standard mode, 100 kHz
Wire.setWireTimeout(25000, true); // 25 ms: do not hang if a device blocks the bus
scanBus();
}
void loop() {
uint8_t data[BYTES_TO_READ];
if (readRegisters(DEVICE_ADDRESS, FIRST_REGISTER, data, BYTES_TO_READ)) {
uint16_t value = ((uint16_t)data[0] << 8) | data[1]; // most devices send the high byte first
Serial.print("Register value: ");
Serial.println(value);
} else {
Serial.println("Read failed");
if (Wire.getWireTimeoutFlag()) {
Wire.clearWireTimeoutFlag();
Serial.println("I2C timeout: the bus was reset");
}
}
delay(1000);
}
- The scan uses an empty write:
endTransmission()returns 0 when a device acknowledges its address. endTransmission(false)leaves the bus with a repeated start, as most register based devices expect between the write of the register and the read.setWireTimeout(25000, true)stops a transfer after 25 ms and resets the I2C hardware, so a blocked device does not freeze the PLC program.- Most devices send the high byte first. Check the byte order and the meaning of the register in the datasheet of your device.
What we tested. The sketch compiles without errors for the M-Duino 21+ with the Industrial Shields AVR boards (9,478 bytes of program memory, 3.6 %). We did not run it with an I2C device for this update.
Common I2C problems
| What you see | Usual cause | What to check |
|---|---|---|
| The scan finds no device | Wiring, power or pull-ups | SDA and SCL not swapped, common ground, device powered, external pull-ups, logic level |
endTransmission() returns 2 | No device answers at that address | The address in the datasheet is 7-bit. Some datasheets give it already shifted with the R/W bit (8-bit) |
| Readings fail from time to time | Long cable, weak pull-ups or electrical noise | Shorter wiring, stronger pull-ups, 100 kHz instead of 400 kHz |
| The program stops | A device holds the bus | Wire.setWireTimeout() and the power of the device |
| Two devices with the same address | Same model on the same bus | The address pins of the device, or an I2C multiplexer |
An I2C device in a PLC project. The PLC Solution Library has a complete example that reads an ADS1015 analog to digital converter over I2C, with differential inputs and automatic gain.
FAQ about I2C on an Arduino based PLC
How many devices can I connect to the I2C bus?
Up to 112 addresses with 7-bit addressing (0x08 to 0x77). In practice the limit is the capacitance of the bus: many devices and long wires slow down the edges.
How long can the I2C cable be?
I2C is designed for connections on a board or inside a cabinet. The length depends on the speed, the pull-ups and the cable capacitance. For connections between cabinets, use a bus extender or a field bus such as RS-485.
Can the PLC work as an I2C slave?
Yes. Start it with Wire.begin(address) and register Wire.onReceive() and Wire.onRequest().
What is the difference between I2C and SPI?
I2C uses two shared lines and addresses. SPI uses four lines, one chip select per device, and is faster and full duplex. Both are for short distances.
Which pull-up value should I use?
4.7 kΩ is a common value at 100 kHz. Lower values, down to about 1 kΩ, give faster edges for longer wiring or 400 kHz, as long as every device can sink the current.
More about PLC communications
- How to use SPI on an ESP32 based PLC
- Basics about RS-485 of an industrial PLC
- Modbus RTU with Arduino and ESP32 based PLCs
- Communications and protocols used in industrial automation
- How to install the Industrial Shields boards in the Arduino IDE
See the M-Duino 21+, the Ardbox Analog and the guide to choose an industrial controller.