# 3. Arduino Communication
## 3.1 IIC Communication Instruction
### 3.1.1 Getting Started
* **Wiring Instruction**
(1) Connect the 5V, GND, SDA, and SCL pins of the 8-ch Line Follower to the corresponding pins on the Arduino controller. The wiring method is shown in the diagram below:
:::{Note}
* Before powering on, ensure that no metal objects are touching the controller. Otherwise, the exposed pins at the bottom of the board may cause a short circuit and damage the controller.
:::
* **Program Download**
[Source Code](../_static/source_code/Arduino.zip)
(1) Connect the Arduino controller to the computer via USB cable.
(2) Open the program file located in the same directory as this document: [Program\LineFollowerLearn8CH_I2C_Test](Appendix.md)
(3) Select **"Arduino UNO"** as the development board and choose the correct port number.
(4) Then click the button
to upload the program to the Arduino and wait for it to complete.
### 3.1.2 Test Case
* **Project Outcome**
:::{Note}
Before recognition, you need to perform the one-click intelligent calibration for the sensor to ensure proper detection.
:::
(1) Once the 8-ch Line Follower detects the corresponding color of the line-following target, the serial port will print the status, analog values, and threshold values of each sensor. The printed data will appear as follows:
* **Program Brief Analysis**
[Source Code](Appendix.md)
(1) The `Wire.h` library is imported, which contains the I2C communication class.
{lineno-start=1}
```cpp
#include "Wire.h"
```
(2) The macro definitions specify the input/output pins for `UART`, define the number of channels for the 8-ch Line Follower to 8 channels, and specify the sensor's register addresses.
{lineno-start=1}
```cpp
#include "Wire.h"
#define SENSOR_SUM 8 //Number of sensor channels (传感器通道数)
#define I2C_ADDR 0x5D //Sensor I2C address (传感器I2C地址)
#define SensorStateReg (5u) //Address of sensor channel state data register (传感器全通道状态数据寄存器地址)
#define SensorAnalogReg (6u) //Address of channel 1 analog value register (low 8 bits) (传感器通道1模拟值寄存器低8位地址)
#define SensorThresholdReg (22u) //Address of channel 1 threshold register (low 8 bits) (传感器通道1阈值寄存器低8位地址)
```
(3) In the `setup` function, the serial baud rate is initialized to `115200`, and `Wire.begin()` is called to initialize IIC communication.
{lineno-start=11}
```cpp
void setup()
{
Serial.begin(115200); //Initialize serial communication (初始化串口)
Wire.begin(); //Initialize I2C (初始化I2C)
}
```
(4) In the `loop` function, `Wire.beginTransmission()` is used to specify the device address, and `Wire.write()` is used to send the `SensorStateReg` (status register) address to the device. Then, `Wire.requestFrom()` is called to send a request to read 1 byte of data. `Wire.readBytes()` is used to read the data and store it in the `temp` array. A for loop is used to parse the state data for each sensor channel, and the results are printed via the serial port.
{lineno-start=17}
```cpp
void loop()
{
uint8_t temp[SENSOR_SUM*2] = {0};
uint16_t data[SENSOR_SUM] = {0};
Wire.beginTransmission(I2C_ADDR); //Start communication (开始通信)
Wire.write(SensorStateReg); //Send the address of the state data register (发送状态数据寄存器地址)
Wire.endTransmission();
Wire.requestFrom(I2C_ADDR, 1); //Send read request for 1 byte of data (发送读请求,请求1Byte数据)
Wire.readBytes(temp, 1); //Read 1 byte of data into temp (读取1Byte数据,保存到temp)
for(uint8_t len = 0; len < SENSOR_SUM; len++)
{
data[len]= ((temp[0] >> len) & 0x01); //Parse data and extract state value of each channel (解析数据,提取各通道状态值)
Serial.print(" State");Serial.print(len+1);Serial.print(":");Serial.print(data[len]);// Print state of each sensor channel via serial (串口打印传感器各通道状态)
}
Serial.println();
Wire.endTransmission(); //End communication (结束通信)
delay(10);
```
(5) Next, `Wire.write(SensorAnalogReg)` is used to send the analog value register address. Then, `Wire.requestFrom()` is called to request `8 * 2` bytes of data, the data from all 8 channels. `Wire.readBytes()` is used to read the `8 * 2` bytes of data and store them in `temp`. Finally, a for loop is used to parse the analog values for each channel and print the results via the serial port.
{lineno-start=38}
```cpp
// Wire.beginTransmission(I2C_ADDR); //Start communication (开始通信)
Wire.write(SensorAnalogReg); //Send the address of the analog value register (发送模拟值寄存器地址)
Wire.endTransmission();
Wire.requestFrom(I2C_ADDR, SENSOR_SUM*2); //Send read request for 8*2 bytes of data (发送读请求,请求8*2 Byte数据,请求8个通道的全部数据)
Wire.readBytes(temp, SENSOR_SUM*2); //Read 8*2 bytes of data into temp (读取8*2 Byte数据,保存到temp,读取8个通道的全部数据)
Wire.endTransmission(); //End communication (结束通信)
uint8_t count = 0;
for(uint8_t len = 0; len < SENSOR_SUM; len++)
{
data[len]= (temp[count]) | (temp[count+1]<<8); //Parse data by combining low and high 8-bit analog values into 16-bit (解析数据,把各通道模拟值低8位和高8位数据合并为16位数据)
Serial.print(" Analog");Serial.print(len+1);Serial.print(":");Serial.print(data[len]);//Print analog value of each sensor channel via serial (串口打印传感器各通道模拟值)
count += 2;
}
Serial.println();
delay(10);
```
(6) Then, `Wire.beginTransmission()` is used again to specify the device address, and `Wire.write()` sends the threshold register address. `Wire.requestFrom()` is called to request `8 * 2` bytes of data, reading all data from the 8 channels. Finally, `Wire.readBytes()` is used to read the data and store it in `temp`. A for loop is used to parse the threshold data for each sensor channel and print the results via the serial port.
{lineno-start=56}
```cpp
Wire.beginTransmission(I2C_ADDR); //Start communication (开始通信)
Wire.write(SensorThresholdReg); //Send the address of the threshold register (发送阈值寄存器地址)
Wire.endTransmission();
Wire.requestFrom(I2C_ADDR, SENSOR_SUM*2); //Send read request for 8*2 bytes of data (发送读请求,请求8*2 Byte数据,请求8个通道的全部数据)
Wire.readBytes(temp, SENSOR_SUM*2); //Read 8*2 bytes of data into temp (读取8*2 Byte数据,保存到temp,读取8个通道的全部数据)
Wire.endTransmission(); //End communication (结束通信)
count = 0;
for(uint8_t len = 0; len < SENSOR_SUM; len++)
{
data[len]= (temp[count]) | (temp[count+1]<<8); //Parse data by combining low and high 8-bit threshold values into 16-bit (解析数据,把各通道阈值低8位和高8位数据合并为16位数据)
Serial.print(" Thres");Serial.print(len+1);Serial.print(":");Serial.print(data[len]);//Print threshold value of each sensor channel via serial (串口打印传感器各通道阈值)
count += 2;
}
Serial.println();
delay(50);
}
```
## 3.2 UART Communication Instruction
### 3.2.1 Preparation
* **Wiring Instruction**
(1) Connect the 5V, GND, TX, and RX pins of the 8-ch Line Follower to the corresponding pins on the ESP controller. The wiring method is shown in the diagram below:
:::{Note}
Before powering on, ensure that no metal objects are touching the controller. Otherwise, the exposed pins at the bottom of the board may cause a short circuit and damage the controller.
:::
* **Program Download**
[Source Code](../_static/source_code/Arduino.zip)
(1) Connect the ESP32 controller to your computer using a USB cable.
(2) Open the program file located in the same directory as this document: [Program\LineFollowerLearn8CH_I2C_Test](Appendix.md)
(3) Select **"ESP32 Dev Module"** as the development board and choose the correct port number.
(4) Then click the button
to upload the program to the ESP controller and wait for it to complete.
### 3.2.2 Test Case
* **Project Outcome**
:::{Note}
Before recognition, you need to perform the one-click intelligent calibration for the sensor to ensure proper detection.
:::
(1) Once the 8-ch Line Follower detects the corresponding color of the line-following target, the serial port will print the status, analog values, and threshold values of each sensor. The printed data will appear as follows:
* **Program Brief Analysis**
[Source Code]()
(1) Macro definitions are used to specify the `UART` data input and output pins, define the number of channels for the 8-ch sensor as 8, and define the communication serial port as `UART 1`.
{lineno-start=2}
```cpp
// Define UART pins (定义UART引脚)
#define UART_RX 4
#define UART_TX 5
#define SENSOR_SUM 8 //Number of sensor channels (传感器通道数)
HardwareSerial UART(1); //Define the serial port for module communication (定义模块通信串口)
```
(2) In the `setup` function, the serial baud rate is initialized to `115200`, and `UART.setPins()` is used to set the communication pins, followed by `UART.begin()` to initialize the communication serial port. Next, the program uses `UART.write()` to send a mode command, which sets the sensor to manual mode.
{lineno-start=26}
```cpp
void setup()
{
Serial.begin(115200); //Initialize debug serial port (初始化调试串口)
UART.setPins(UART_RX, UART_TX); //Set UART pin configuration (设置通信串口引脚)
UART.begin(115200, SERIAL_8N1); //Initialize communication UART (初始化通信串口)
Serial.println("Start test...");
UART.write(0); // Set to manual mode. 0: manual mode, 1: auto send state, 2: auto send analog, 3: auto send threshold (设置手动模式。0:手动模式,1:自动发送状态数据,2:自动发送模拟值,3:自动发送阈值)
}
```
(3) A `WriteLock` flag is defined to introduce a state lock for controlling the serial read/write status. If the write lock state is `0`, the read-level status command is sent. Otherwise, the received data is saved, parsed, and printed out.
{lineno-start=37}
```cpp
uint8_t WriteLock = 0;
void loop()
{
uint16_t data[SENSOR_SUM] = {0};
uint8_t temp[SENSOR_SUM*2+5] = {0};
if(UART.availableForWrite() && 0 == WriteLock) //Check if writable and if write lock is cleared (检测可写状态和写入锁状态)
{
UART.write(1); // Send command to read state (发送读取状态指令)
WriteLock = 1;
}
if(UART.available() && 1 == WriteLock) //Check if data is available and if write lock is set (检测可读状态和写入锁状态)
{
UART.readBytes(temp, 1); //Read 1 byte of data into temp (读取1Byte数据,保存到temp)
for(uint8_t len = 0; len < SENSOR_SUM; len++)
{
data[len]= ((temp[0] >> len) & 0x01); //Parse data to extract channel states (解析数据,提取各通道状态值)
Serial.printf(" State%d: ", len+1);Serial.print(data[len]); //Print state of each sensor channel via serial (串口打印传感器各通道状态)
}
WriteLock = 0; //Clear write lock to allow next read command (写入锁清零,发送下一个读指令)
}
delay(10);
```
(4) The program checks the writable state and `write lock` status. Once the conditions are met, it sends the command to read the analog values.
{lineno-start=60}
```cpp
if(UART.availableForWrite() && 0 == WriteLock)
{
UART.write(2); // Send command to read analog values (发送读取模拟值指令)
WriteLock = 2;
}
```
(5) The program then checks whether there is readable data in the serial buffer. If available, the data is parsed, and the analog values for each sensor channel are printed.
{lineno-start=65}
```cpp
if(UART.available() && 2 == WriteLock)
{
UART.readBytes(temp, SENSOR_SUM*2+5); //Read 8*2+5 bytes of data into temp (读取8*2+5Byte数据,保存到temp)
if(0x55 == temp[0] && 0xAA == temp[1]) //Validate frame header according to protocol (根据协议验证帧头)
{
if(0x02 == temp[2] && 0x10 == temp[3]) //Validate data length and read command (根据协议验证数据长度和读指令)
{
if(temp[SENSOR_SUM*2+4] == Check_Code(temp)) //Verify checksum (校验码验证)
{
uint8_t count = 4;
for(uint8_t len = 0; len < SENSOR_SUM; len++)
{
data[len]= (temp[count]) | (temp[count+1]<<8); //Combine low and high 8-bit analog values into 16-bit (解析数据,把各通道模拟值低8位和高8位数据合并为16位数据)
Serial.printf(" Analog%d: ", len+1);Serial.print(data[len]); //Print analog value of each sensor channel via serial (串口打印传感器各通道模拟值)
count += 2;
}
}
}
}
WriteLock = 0;
}
delay(10);
```