Friday, 27 July 2018

RS-485 Modbus IoT Gateway using ESP8266 NodeMCU ESP-12E Part 1 of 3

Clockwise from top: USB 3.0 powered hub, RS-485 USB dongle, Arduino RS-485 to TTL module and NodeMCU ESP-12E ESP8266
Many remote devices, especially for industrial use come with the RS-485 serial interface and use the Modbus protocol. These industrial remote devices are often easily converted to IoT devices using for example, an IoT Gateway that also has RS-485 interfaces.

The Schneider PM1200 3-phase Power Meter has an RS-485 interface and would make a great IoT device.

Schneider PM1200 Power Meter


Fuji FRENIC 15KW Inverter

The ESP8266 is incredibly tempting as such  an IoT gateway. It is low-cost, low-power and the WiFi module allows for a much safer non-galvanic, isolated data connection. Similar devices like the monster 10-30KW Inverters used in escalators, cranes, etc can be monitored safely from the cloud this way.

As usual I used a low-cost (RM3.38) Arduino RS-485 to serial TTL module. Now this is a 5V device and since the ESP8266 is strictly a 3.3V device, we should really use a logic level converter, but at a pinch it should work at 3.3V, especially if I powered  it from the ESP-12E.

I also used a modified version of the Trialcommand ESP8255 Master Modbus RTU (RS232) sketch. This in turn use the SoftwareSerial library, which essentially determined the wiring diagram.

RS-485 to 5V TTL module 

MOSFET-based Logic Level Converter

  The wiring diagram (sans level converter) is:

       ESP12-E    RS-485 PCB
            3V3
            GND
            D5 ------- *RE
            D6 ------- DE
            D7 ------- RO
            D8 ------- DI

NodeMCU ESP-12E Pinout

The wiring really makes sense when you look at the MAX485E IC datasheet:

Notice the MAX485E is a half-duplex device
I made a little ribbon cable with Moles 0.1" pitch Molex connectors:

Cable assembly. Right, top: data & control, right bottom: rs-485 power. Left: ESP-12E
When assembled the RS-485 module looked like this:


Download the libraries and copy them into your Arduino IDE libraries folder.

$ls -lt /root/Arduino/libraries
total 16
drwxr-xr-x 4 root  root  4096 Apr 26 20:44 Time-master
-rw-r--r-- 1 root  root    87 Feb 27 07:20 readme.txt
drwxr-xr-x 2 heong users 4096 Mar 15  2017 ModBusMaster232
drwxr-xr-x 3 heong users 4096 Mar 15  2017 SoftwareSerial

Since the ESP8266-Modbus-RTU-Master library uses full duplex TTL RS-232 I put in a tiny patch to convert it to half duplex required by the MAX485E. The file is ./libraries/ModBusMaster232/ModbusMaster232^Cpp and the change is in boldface at line 735:

#define not_RE 14 // D5. Enable receiver, active low
#define DE 12 // D6  Enable Transmitter, active high

  u16CRC = 0xFFFF;
  for (i = 0; i < u8ModbusADUSize; i++)
  {
  //Function  crc16  for ESP8266   - PDAControl
    u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]);
  }
  u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC);
  u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC);
  u8ModbusADU[u8ModbusADUSize] = 0;

  // transmit request
  digitalWrite(not_RE, HIGH); // cmheong Set max485 to transmit
  digitalWrite(DE, HIGH);
  for (i = 0; i < u8ModbusADUSize; i++)
  {
                swSer.print(char(u8ModbusADU[i]));
                delay(2); // cmheong 2018-07-26
  }
  u16CRC = 0xFFFF;
  for (i = 0; i < u8ModbusADUSize; i++)
  {
  //Function  crc16  for ESP8266   - PDAControl
    u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]);
  }
  u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC);
  u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC);
  u8ModbusADU[u8ModbusADUSize] = 0;

  // transmit request
  digitalWrite(not_RE, HIGH); // cmheong Set max485 to transmit
  digitalWrite(DE, HIGH);
  for (i = 0; i < u8ModbusADUSize; i++)
  {
                swSer.print(char(u8ModbusADU[i]));
                delay(2); // cmheong 2018-07-26
  }


I modified the Trialcommand sketch (patches in boldface):

#include <ModbusMaster232.h> 
#include <SoftwareSerial.h>  // Modbus RTU pins   D7(13),D8(15)   RX,TX
// MAX485 half duplex control lines
#define not_RE 14 // D5. Enable receiver, active low
#define DE 12 // D6  Enable Transmitter, active high

// Instantiate ModbusMaster object as slave ID 1
  ModbusMaster232 node(1);

void setup()
{
  pinMode(not_RE, OUTPUT);
  pinMode(DE, OUTPUT);
  // default to transmit mode to reduce noise
  digitalWrite(not_RE, HIGH); // disable receiver
  digitalWrite(DE, HIGH); // enable transmitter
  
  Serial.begin(9600);
  delay(100);  
  node.begin(9600);  // Modbus RTU
  delay(100);
  Serial.println("Connected "); 
  Serial.println("Modbus RTU Master Online");
  
}


void loop()
{
//Website http://trialcommand.com
///////// Holding Register [0]  A [9]   = 10 Holding Registers Escritura
///////// Holding Register [0] A [9] = 10 Holding Registers Writing

int Mdelay = 500; // from 5 

node.readDiscreteInputs(0, 1); 
Serial.print("[0] ");
Serial.print(node.getResponseBuffer(0));
node.clearResponseBuffer();
delay(Mdelay);
  
node.writeSingleRegister(0x1001, 0xff); 
delay(Mdelay);
  
}

There you have it: the ESP8266 NodeMCU ESP-12E will now be able to request digital input from the modbus device as well as output to it. Having obtained the data it will now need to relay it to the Internet via its webpage, which will be shown in Part 2.

Happy Trails.

No comments:

Post a Comment