Arduino Mini Project ทดสอบการเชื่อมต่อกับ RS485 ด้วย TTL to RS485 ครั้งที่4

Nut Techosakondee
4 min readDec 7, 2022

--

จากการทดลอง 3 ครั้งแรก เราได้ทดลอง modbus ด้วย arduino uno คราวนี้เราลองเปลี่ยนมาใช้ esp32 กันบ้าง เพื่อที่ในอนาคต เราสามารถไปต่อยอดกับระบบ wifi ได้ด้วย โดยเราจะทดสอบ master , slave โดยใช้โค้ดเดิม แต่อาจจะมีการปรับใช้ pin ใหม่ เนื่องจาก esp32 มี pinout ที่ไม่เหมือนกับของ uno

esp32 pinout

ตัว uno มี serial port แค่ 1 port ดังนั้นในโค้ดเก่า เราจึงใช้ software serial ในการสร้าง port serial จาก pinout ขาอื่นๆ แต่ใน esp32 นั้นมี hardware serial มาให้ใช้ถึง 3 port ดังนั้นในการใช้งาน เราจะเปลี่ยนจาก software serial ไปใช้ hardware serial 2 แทน (RX2 , TX2 หรือ pin 16 , 17)

Wiring & Code Master

wiring master mode
Pin ESP32   MAX485
16 (RX) ----> RO
17 (TX) ---->DI
18 ---->RE
5 ---->DE
5v ----> VCC
GND ----> GND

A(MAX485) ----> A(USB)
B(MAX485) ----> B(USB)
#include <ModbusMaster.h>
#define RXD2 16
#define TXD2 17

#define MAX485_DE 5
#define MAX485_RE_NEG 18

uint8_t i = 0;
unsigned long last_time = 0;

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}

void setup()
{
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);

// Init in receive mode
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);

// Modbus communication runs at 115200 baud
Serial.begin(115200);
Serial2.begin(9600,SERIAL_8N1, RXD2, TXD2);

// Modbus slave ID 2
node.begin(2, Serial2);
// Callbacks allow us to configure the RS485 transceiver correctly
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}


void loop()
{
uint8_t result = 0;
uint8_t respons = 0;
uint16_t data[10];

if(millis()- last_time > 1000)
{

i++;
if (i > 100)
{
i = 0;
}

node.writeSingleRegister(0x40003,i);
//node.writeSingleRegister(1,456);

//node.setTransmitBuffer(0x40002, i);
//node.setTransmitBuffer(0x40003, 888);
//respons = node.writeMultipleRegisters(0x40000, 4);

result = node.readHoldingRegisters(0x40000, 10);
if (getResultMsg(&node, result))
{
Serial.println("Read data from Slave");
for (int j = 0; j < 10; j++)
{
data[j] = node.getResponseBuffer(j);
Serial.println(data[j]);
}
}

last_time = millis();
}

}

bool getResultMsg(ModbusMaster *node, uint8_t result)
{
String tmpstr2 = "\r\n";
switch (result)
{
case node->ku8MBSuccess:
return true;
break;
case node->ku8MBIllegalFunction:
tmpstr2 += "Illegal Function";
break;
case node->ku8MBIllegalDataAddress:
tmpstr2 += "Illegal Data Address";
break;
case node->ku8MBIllegalDataValue:
tmpstr2 += "Illegal Data Value";
break;
case node->ku8MBSlaveDeviceFailure:
tmpstr2 += "Slave Device Failure";
break;
case node->ku8MBInvalidSlaveID:
tmpstr2 += "Invalid Slave ID";
break;
case node->ku8MBInvalidFunction:
tmpstr2 += "Invalid Function";
break;
case node->ku8MBResponseTimedOut:
tmpstr2 += "Response Timed Out";
break;
case node->ku8MBInvalidCRC:
tmpstr2 += "Invalid CRC";
break;
default:
tmpstr2 += "Unknown error: " + String(result);
break;
}
Serial.println(tmpstr2);
return false;
}

จาก code ด้านบน จะมีส่วนที่แตกต่าง และปรับปรุงมากกว่าครั้งที่แล้ว ตรงที่ครั้งนี้เปลี่ยนไปใช้ Serial2 แทน softwareserial และมี function getResultMsg เอาไว้ตรวจสอบสถานะของการสื่อสาร

ทดสอบการส่งข้อมูลจาก mster ไปที่ slave โดยใช้ writeSingleRegister ส่งไปค่าเดียว หรือจะใช้ writeMultipleRegisters ส่งไปจำนวนหลายๆค่าก็ได้

node.writeSingleRegister(0x40003,i);

node.setTransmitBuffer(0x40002, i);
node.setTransmitBuffer(0x40003, 888);
respons = node.writeMultipleRegisters(0x40000, 4);

ส่วนของการรับข้อมูล มีการใช้ function getResultMsg มาตรวจสอบ ถ้าสามารถเชื่อมต่อได้ code จะ print ค่าที่อ่านได้จาก buffer ถ้ามี error function จะ return ค่า error ออกมา ทำให้ตรวจสอบได้ว่า error ที่จุดไหน

result = node.readHoldingRegisters(0x40000, 10);
if (getResultMsg(&node, result))
{
Serial.println("Read data from Slave");
for (int j = 0; j < 10; j++)
{
data[j] = node.getResponseBuffer(j);
Serial.println(data[j]);
}
}
ทดสอบด้วย modbus slave เหมือนครังก่อน เพื่อตรวจสอบการสื่อสาร

Slave code for UNO (ปรับปรุง)

#include <ModbusSlave.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10,11);

#define SLAVE_ID 2
#define SERIAL_BAUDRATE 9600


// Comment out the following line if your not using RS485
#define RS485_CTRL_PIN 8
int rdata[10];
int wdata[10];
uint8_t i = 0;
int sensorValue;

#ifdef RS485_CTRL_PIN
Modbus slave(mySerial, SLAVE_ID, RS485_CTRL_PIN);
#else
Modbus slave(mySerial, SLAVE_ID);
#endif

void setup()
{

slave.cbVector[CB_READ_HOLDING_REGISTERS] = readsensor;
slave.cbVector[CB_WRITE_HOLDING_REGISTERS] = writesensor;

Serial.begin(SERIAL_BAUDRATE);
mySerial.begin(SERIAL_BAUDRATE);
slave.begin(SERIAL_BAUDRATE);
}

void loop()
{
slave.poll();
}

uint8_t readsensor(uint8_t fc, uint16_t address, uint16_t length)
{

sensorValue = analogRead(A1);
rdata[0] = sensorValue;
i++;
if (i > 100)
{
i = 0;
}
rdata[1] = i;
slave.writeRegisterToBuffer(0, rdata[0]);
slave.writeRegisterToBuffer(1, rdata[1]);

return STATUS_OK;
}

uint8_t writesensor(uint8_t fc, uint16_t address, uint16_t length)
{

Serial.println(F("Read memory on Master"));

for (uint8_t i = 0; i < length; ++i)
{
wdata[i] = slave.readRegisterFromBuffer(i);
Serial.println(wdata[i]);
}
return STATUS_OK;
}

สมมุติถ้าให้ esp32 เป็น slave ก็สามารถใช้ code เดียวกันได้ โดยเปลี่ยนไปใช้ Serial2 แทน เหมือนกับ code master แต่จะขอไม่ทดสอบเนื่องจากนิยมให้ esp32 เป็น master มากกว่า เพราะจะได้ไปต่อยอดกับระบบ wifi ได้

ทดลองเชื่อมต่อ Master & Slave

Master & Slave wiring

จาก code master และ code slave เราสามารถทดลองสื่อสารด้วย modbus จากการต่อสายดังรูป จากนั้นเปิด serial port เพื่อ monitor ค่าของ esp32 และ program hercules เพื่อ monitor port ของ uno

ทดสอบการสื่อสาร

จะเห็นว่าสามารถรับ-ส่งค่าจาก master -> slave และ slave -> masterได้แล้ว!!

ถ้าหาก code มีอาการ error timeout หรือ Invalid Slave ID ของฝั่ง master ให้ทดลองถอดสาย DE ของฝั่ง slave (uno) ออก เพราะจากการทดลองถอดแล้ว error หาย

--

--