arduino-libraries / ArduinoModbus

258 stars 123 forks source link

Question about directly accessing holding register data array via union #124

Open kurt-klingbeil opened 1 year ago

kurt-klingbeil commented 1 year ago

I have developed an application using yaacov ArduinoModbusSlave via RS485 serial That library requires the program allocating the array of data structures and then using a union to link the application memory and the modbus holding register space.

This enables conserving memory by having only a single array in memory. I need to upgrade the application to use TCP rather than RTU and so far I am not clear how to migrate the ethernet support from ESP to Ethernet shield v2 W5500

I have successfully upgraded the RTU version of the Kitchen Sink which was/is missing from the TCP examples. Is there a way to create an array of data structures and have it overlay the memory which is allocated by:

modbusTCPServer.configureHoldingRegisters(0x00, numHoldingRegisters);

I'm guessing this allocates memory somewhere which is normally only accessed via

modbusTCPServer.holdingRegisterWrite(i,2*i+3);

As I write this I am realizing I could probably do what I need by sacrificing the struct and the element naming and just brute force the accesses by organized indexing... something like defining the current structure element names as constants and then i = struct_index * struct_size + struct_element

Anyway, I thought I would ask the question anyway... maybe there is a way to find the address of the HoldingRegister array and union it with the structure array...


Oh... I noticed one little anomaly related to my conversion of KitchenSink RTU to TCP... In both RTU and TCP, toggling the LED to ON takes effect immediately in RTU the LED goes off immediately. In TCP version there is initially no change, and then after a few seconds the LED kinda fades to OFF - a shaky dimming rather than switching OFF

Feel free to check this code and stick it in the TCP examples... I always think example code should be more than the bare minimalist simplest functionality leaving any practical implementation as further mysteries ;-)

/*
  Modbus TCP Server Kitchen Sink

  This sketch creates a Modbus RTU Server and demonstrates
  how to use various Modbus Server APIs.

  Circuit:
   - MKR board
   - MKR ETH shield
     - ISO GND connected to GND of the Modbus RTU server
     - Y connected to A/Y of the Modbus RTU client
     - Z connected to B/Z of the Modbus RTU client
     - Jumper positions
       - FULL set to OFF
       - Z \/\/ Y set to OFF

  created 18 July 2018
  by Sandeep Mistry
*/
#include <SPI.h>
#include <Ethernet.h>

#include <ArduinoRS485.h> // ArduinoModbus depends on the ArduinoRS485 library
#include <ArduinoModbus.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
// The IP address will be dependent on your local network:
byte mac[] = {
//  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
  0xA8, 0x61, 0x0A, 0xAF, 0x18, 0x27
};
IPAddress ip(192, 168, 1, 177);

EthernetServer ethServer(502);

ModbusTCPServer modbusTCPServer;

const int ledPin = LED_BUILTIN;

const int numCoils = 10;
const int numDiscreteInputs = 10;
const int numHoldingRegisters = 10;
const int numInputRegisters = 10;

void setup() {
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(10);  // Most Arduino shields
  //Ethernet.init(5);   // MKR ETH shield
  //Ethernet.init(0);   // Teensy 2.0
  //Ethernet.init(20);  // Teensy++ 2.0
  //Ethernet.init(15);  // ESP8266 with Adafruit Featherwing Ethernet
  //Ethernet.init(33);  // ESP32 with Adafruit Featherwing Ethernet

  Serial.begin(9600);
  while (!Serial);

  Serial.println("Modbus TCP Server Kitchen Sink");
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  delay(3);
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  } else {
   Serial.println("Ethernet cable is now connected.");
  }
  // start the server
  ethServer.begin();

  // start the Modbus TCP server
  if (!modbusTCPServer.begin()) {
    Serial.println("Failed to start Modbus TCP Server!");
    while (1);
  }

  // configure coils at address 0x00
  modbusTCPServer.configureCoils(0x00, numCoils);

  // configure discrete inputs at address 0x00
  modbusTCPServer.configureDiscreteInputs(0x00, numDiscreteInputs);

  // configure holding registers at address 0x00
  modbusTCPServer.configureHoldingRegisters(0x00, numHoldingRegisters);

  // configure input registers at address 0x00
  modbusTCPServer.configureInputRegisters(0x00, numInputRegisters);
}

void loop() {
  // listen for incoming clients
  EthernetClient client = ethServer.available();

  if (client) {
    // a new client connected
    Serial.println("new client");

    // let the Modbus TCP accept the connection 
    modbusTCPServer.accept(client);

    while (client.connected()) {
      // poll for Modbus TCP requests, while client connected
      modbusTCPServer.poll();

      // update the LED
      updateLED();
    }

  // map the coil values to the discrete input values
  for (int i = 0; i < numCoils; i++) {
    int coilValue = modbusTCPServer.coilRead(i);

    modbusTCPServer.discreteInputWrite(i, coilValue);
  }
  for (int i = 0; i < numHoldingRegisters; i++) {
    modbusTCPServer.holdingRegisterWrite(i,2*i+3);
  }
  for (int i = 0; i < numHoldingRegisters; i++) {
    modbusTCPServer.inputRegisterWrite(i,3*i+2);
  }

  // output the value of each analog input pin
  for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
    int sensorReading = analogRead(analogChannel);
    Serial.print(sensorReading); Serial.print(" ");
    modbusTCPServer.inputRegisterWrite(analogChannel,sensorReading);
  }

  // map the holding register values to the input register values
  for (int i = 0; i < numHoldingRegisters; i++) {
    long holdingRegisterValue = modbusTCPServer.holdingRegisterRead(i);

    //modbusTCPServer.inputRegisterWrite(i, holdingRegisterValue);
    Serial.print(holdingRegisterValue); Serial.print(" ");
  }
    Serial.println("client disconnected");
  }

}

void updateLED() {
  // read the current value of the coil
  int coilValue = modbusTCPServer.coilRead(0x00);

  if (coilValue) {
    // coil value set, turn LED on
    digitalWrite(ledPin, HIGH);
  } else {
    // coil value clear, turn LED off
    digitalWrite(ledPin, LOW);
  }
}