debevv / nanoMODBUS

A compact MODBUS RTU/TCP C library for embedded/microcontrollers
MIT License
271 stars 55 forks source link

modbus RTU server on esp32 #59

Closed mudgalp closed 6 days ago

mudgalp commented 3 weeks ago

I'm trying to use this library in an ESP32 project in platformIO IDE but don't understand how to do it. I read the value of Temperature & humidity from a sensor and want to pass it to Two Input registers, so if any RTU client requests the device will respond. Here is my basic code :

#include <Arduino.h>
#include "nanomodbus.h"
#include <SensirionI2cSht3x.h>
#include <Wire.h>

SensirionI2cSht3x sensor;

static char errorMessage[64];
static int16_t error;

#define LED_BUILTIN 2

#define COILS_ADDR_MAX 100
#define REGS_ADDR_MAX 32

#define RTU_SERVER_ADDRESS 1

#define ESP32_U1_TX 18 // MAX485_RX -->
#define ESP32_U1_RX 19 // MAX485_TX -->

const byte potPins[2] = {34, 35};
const byte buttonPins[2] = {16, 17};
const byte ledPins[4] = {12, 14, 27, 26};
const byte dePin = 5;

float Temp = 0.0;
float Humi = 0.0;

// A single nmbs_bitfield variable can keep 2000 coils
nmbs_bitfield server_coils = {0};
uint16_t server_registers[REGS_ADDR_MAX] = {0};

int32_t read_serial(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
  Serial.setTimeout(byte_timeout_ms);
  return Serial1.readBytes(buf, count);
}

int32_t write_serial(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
  Serial.setTimeout(byte_timeout_ms);
  return Serial1.write(buf, count);
}

void onError()
{
  // Set the led ON on error
  while (true)
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

nmbs_error handle_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void *arg)
{
  if (address + quantity > COILS_ADDR_MAX + 1)
    return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

  // Read our coils values into coils_out
  for (int i = 0; i < quantity; i++)
  {
    bool value = nmbs_bitfield_read(server_coils, address + i);
    nmbs_bitfield_write(coils_out, i, value);
  }

  return NMBS_ERROR_NONE;
}

nmbs_error handle_write_multiple_coils(uint16_t address, uint16_t quantity, const nmbs_bitfield coils, uint8_t unit_id, void *arg)
{
  if (address + quantity > COILS_ADDR_MAX + 1)
    return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

  // Write coils values to our server_coils
  for (int i = 0; i < quantity; i++)
  {
    nmbs_bitfield_write(server_coils, address + i, nmbs_bitfield_read(coils, i));
  }

  return NMBS_ERROR_NONE;
}

nmbs_error handler_read_holding_registers(uint16_t address, uint16_t quantity, uint16_t *registers_out, uint8_t unit_id, void *arg)
{
  if (address + quantity > REGS_ADDR_MAX + 1)
    return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

  // Read our registers values into registers_out
  for (int i = 0; i < quantity; i++)
    registers_out[i] = server_registers[address + i];

  return NMBS_ERROR_NONE;
}

nmbs_error handle_write_multiple_registers(uint16_t address, uint16_t quantity, const uint16_t *registers, uint8_t unit_id, void *arg)
{
  if (address + quantity > REGS_ADDR_MAX + 1)
    return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

  // Write registers values to our server_registers
  for (int i = 0; i < quantity; i++)
    server_registers[address + i] = registers[i];

  return NMBS_ERROR_NONE;
}

void SensorRead(float *T, float *H)
{
  error = sensor.measureSingleShot(REPEATABILITY_MEDIUM, false, *T, *H);
  if (error != NO_ERROR)
  {
    Serial.print("Error trying to execute measureSingleShot(): ");
    errorToString(error, errorMessage, sizeof errorMessage);
    Serial.println(errorMessage);
    return;
  }
}

void setup()
{
  analogReadResolution(10);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(potPins[0], INPUT);
  pinMode(potPins[1], INPUT);
  pinMode(buttonPins[0], INPUT_PULLUP);
  pinMode(buttonPins[1], INPUT_PULLUP);
  pinMode(ledPins[0], OUTPUT);
  pinMode(ledPins[1], OUTPUT);
  pinMode(ledPins[2], OUTPUT);
  pinMode(ledPins[3], OUTPUT);
  // pinMode(dePin, OUTPUT);
  // digitalWrite(dePin, LOW);

  Serial.begin(115200);
  while (!Serial)
  {
    delay(100);
  }
  Wire.begin();
  sensor.begin(Wire, SHT30_I2C_ADDR_44);

  sensor.stopMeasurement();
  delay(1);
  sensor.softReset();
  delay(100);
  uint16_t aStatusRegister = 0u;
  error = sensor.readStatusRegister(aStatusRegister);
  if (error != NO_ERROR)
  {
    Serial.print("Error trying to execute readStatusRegister(): ");
    errorToString(error, errorMessage, sizeof errorMessage);
    Serial.println(errorMessage);
    return;
  }
  Serial.print("aStatusRegister: ");
  Serial.print(aStatusRegister);
  Serial.println();
  Serial1.begin(9600, SERIAL_8N1, ESP32_U1_RX, ESP32_U1_TX);
  while (!Serial1)
  {
    delay(100);
  }
}

void loop()
{
  nmbs_platform_conf platform_conf;
  platform_conf.transport = NMBS_TRANSPORT_RTU;
  platform_conf.read = read_serial;
  platform_conf.write = write_serial;
  platform_conf.arg = NULL;

  nmbs_callbacks callbacks = {0};
  callbacks.read_coils = handle_read_coils; 
  callbacks.write_multiple_coils = handle_write_multiple_coils;
  callbacks.read_holding_registers = handler_read_holding_registers;
  callbacks.write_multiple_registers = handle_write_multiple_registers;

  // Create the modbus server
  nmbs_t nmbs;
  nmbs_error err = nmbs_server_create(&nmbs, RTU_SERVER_ADDRESS, &platform_conf, &callbacks);
  if (err != NMBS_ERROR_NONE)
  {
    onError();
  }

  nmbs_set_read_timeout(&nmbs, 1000);
  nmbs_set_byte_timeout(&nmbs, 100);

  bool led = false;

  while (true)
  {
    err = nmbs_server_poll(&nmbs);

    // This will probably never happen, since we don't return < 0 in our platform funcs
    if (err == NMBS_ERROR_TRANSPORT)
      break;

    SensorRead(&Temp, &Humi);
    Serial.print("Temperature: " + String(Temp));
    Serial.println(" | Humidity: " + String(Humi));

    /*
    Some function to put the &Temp, &Humi data in to two Input Registers
    */

    digitalWrite(LED_BUILTIN, led);
    led = !led;
  }

  // No need to destroy the nmbs instance, terminate the program
  exit(0);
}
mudgalp commented 3 weeks ago

Anyone here to help???

pseudotronics commented 1 week ago

I mean all you need to do is put the data into the register.

server_registers[TEMP_ADDRESS] = ((uint16_t*)(&Temp))[1];
server_registers[TEMP_ADDRESS+1] = ((uint16_t*)(&Temp))[0];
mudgalp commented 6 days ago

I mean all you need to do is put the data into the register.

server_registers[TEMP_ADDRESS] = ((uint16_t*)(&Temp))[1];
server_registers[TEMP_ADDRESS+1] = ((uint16_t*)(&Temp))[0];

Thank you for your response, but I already figured out how to do it.

mudgalp commented 6 days ago

Now if anyone working with this library and Requires some help, I can help him.

moreover, I have modified this library to use MAX485CSA ic in your project, which does not have an auto transmission feature. This IC requires a DE/RE pin to be pulled up and down at some specific point in the transmission. I have added some more lines of code to this library.