emelianov / modbus-esp8266

Most complete Modbus library for Arduino. A library that allows your Arduino board to communicate via Modbus protocol, acting as a master, slave or both. Supports network transport (Modbus TCP) and Serial line/RS-485 (Modbus RTU). Supports Modbus TCP Security for ESP8266/ESP32.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Help understanding how to read coils

jpvg10 opened this issue · comments

Hello. I want the ESP8266 to act as server and respond to Modbus TCP/IP requests. I am able to do this using OpenPLC, now I am trying to replicate it in Arduino.

So far I'm trying a simple example: set the D0 pin (that is GPIO16) to HIGH, and be able to obtain the value of this coil using Modbus TCP/IP. However I'm having a hard time understanding how to do it. The pin is set to high, but when I try to read the value using Modbus (specifically through ScadaBR), I can see the requests being replied, but the value is 0.

#include <ESP8266WiFi.h>
#include <ModbusIP_ESP8266.h>

int LED = 16;
ModbusIP mb;

void setup() {
  Serial.begin(115200);
  WiFi.begin("your_ssid", "your_password");
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  mb.server();
  mb.addCoil(0);
  pinMode(LED, OUTPUT);
}

void loop() {
  //Call once inside loop() - all magic here
  mb.task();
  digitalWrite(LED, HIGH);
  delay(10);
}

I am using offset 0 for the coil because that is what I used in OpenPLC (see here). I also saw the example IP-server-Led uses offset 100. I don't know why, and it's also not clear to me what this line does:

digitalWrite(ledPin, mb.Coil(LED_COIL));

An additional question to this is: As far as I've seen, if I add more delay() in the loop, for example, toggle the pin from HIGH to LOW every 2 seconds, the Modbus requests are also affected and only replied every 2 seconds. Would it be possible to achieve to read the value of the blinking pin?

Thanks in advance for your time and help.

I've been reviewing this and now I understood it!

I went through the IP-server-Led example code again, and I realized it is actually writing to a coil, not reading it. Then it clicked for me: pins and coils are separate things! I was misled on this because of how OpenPLC does it.

I tried the IP-server-Led example and it worked perfectly with ScadaBR: I was able to write a coil and reflect the value on a LED. Then, I modified my code to make ScadaBR read from the coil, and now it worked!

#include <ESP8266WiFi.h>
#include <ModbusIP_ESP8266.h>

int LED = 16;
ModbusIP mb;

void setup() {
  Serial.begin(115200);
  WiFi.begin("your_ssid", "your_password");
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  mb.server();
  mb.addCoil(0);
  pinMode(LED, OUTPUT);

  digitalWrite(LED, HIGH);
  mb.Coil(0, 1);
}

void loop() {
  mb.task();
  delay(10);
}

I have to write the coil (mb.Coil(0, 1)) so that ScadaBR is able to read it, now I'm seeing the value 1 on ScadaBR. The digitalWrite(LED, HIGH) is not needed for ScadaBR, but it's needed to actually turn on the LED on my circuit.

About my question regarding delay() and the blinking LED, indeed the requests are only replied every 2 seconds, but the value reflected on ScadaBR does alternate between 0 and 1 every time.

I'm leaving this comment in case it's helpful for someone in the future. This issue can now be closed.

Indeed more strict notation should be cdigitalWrite(ledPin, mb.Coil(LED_COIL)?HIGH:LOW);

Blinking led might be something alike

uint32_t lastTime = 0;
void loop() {
  uint32_t now = millis();
  mb.task();
  if (now - lastTime >= PERIOD) // this will be true every PERIOD milliseconds
  {
    lastTime = now;
    if (ledState == LOW && mb.Coil(LED_COIL))
    {
      ledState = HIGH;
    }
    else
    {
      ledState = LOW;
    }
    digitalWrite(OUTPIN, ledState);
  }
}

Ahh, this is a nice trick for avoiding delay(). Thank you!

But just to be clear, we need also something like mb.Coil(LED_COIL, ledState) besides digitalWrite(OUTPIN, ledState), right?

But just to be clear, we need also something like mb.Coil(LED_COIL, ledState) besides digitalWrite(OUTPIN, ledState), right?

No. mb.Coil(LED_COIL, ledState) sets could value by server side itself. In this example we a reading value by mb.Coil(LED_COIL)) call. Value of the coil is set according to client request.