mal zwei vereinfachte Beispiele:
Die Register habe ich jetzt alle in FC4 Read Input Registers gelegt
2000 ein Analogeingang
2001 ein weiterer Analogeingang
2002 ein Digitaleingang
2003 ein weiterer Digitaleingang
Der Client fragt alle 2 Sekunden die 4 Werte vom Server mit der Adresse 2 ab
/*******************************************************
Modbus Client Example
This is a reduced example based on 0311_modbus_Client_FC (Modbus Master)
This Modbus Client
- reads from Modbus Servers periodically
hardware
- a MAX485-TTL adapter
by noiasca
2023-01-31
*******************************************************/
#include <ModbusMaster.h> // Modbus Master 2.0.0 by Doc Walker - install with Library Manager
// client and servers must know the same registers
enum
{
programVersion, //
HOLDING_REGS_SIZE // leave this one
};
// client specific
constexpr byte modbus_enable_pin = 11; // The GPIO used to control the MAX485 TX pin. Set to 255 if you are not using RS485 or a selfsensing adapter
constexpr uint32_t modbus_baud = 9600; // use slow speeds with SoftSerial
constexpr byte rxPin = 2; // for Softserial
constexpr byte txPin = 3;
uint32_t previousTx = 0;
uint16_t restTx = 25000; // rest time between transmissions - microseconds
ModbusMaster serverA; // instantiate ModbusMaster object - slave - node
// example to mirror data to global variables
int serverA_A0 = 0;
int serverA_D6 = LOW;
void preTransmission()
{
while (micros() - previousTx < restTx)
{
yield(); // wait some time and do background tasks.
}
digitalWrite(modbus_enable_pin, HIGH);
}
void postTransmission()
{
previousTx = micros(); // remember last timestamp
digitalWrite(modbus_enable_pin, LOW);
}
/* *******************************************************
Serial Interface
* **************************************************** */
// if you don't have enough HW Serial (i.e. on an UNO)
// you are forced to use SoftwareSerial or AltSoftSerial
#include <SoftwareSerial.h>
SoftwareSerial mySerial(rxPin, txPin); // RXpin, TXpin
void requestData()
{
static uint32_t previousMillis = 0;
uint32_t currentMillis = millis();
if (currentMillis - previousMillis > 2000) // set the poll interval in ms
{
previousMillis = currentMillis;
uint16_t reg = 2000;
uint16_t noOfValues = 4;
int result = 0;
result = serverA.readInputRegisters(reg, noOfValues); //FC4
if (result == serverA.ku8MBSuccess) // do something with data if read is successful
{
for (uint16_t i = 0; i < noOfValues; i++)
{
Serial.print(F("FC4 Input Register "));
Serial.print(reg + i);
Serial.print('=');
Serial.println(serverA.getResponseBuffer(i));
}
serverA_A0 = serverA.getResponseBuffer(0); // example to mirror data from Server to Client
serverA_D6 = serverA.getResponseBuffer(2);
}
else
{
Serial.print(F("E ServerA no input register ")); Serial.print(reg); Serial.print(F(" result=")); Serial.println(result, HEX);
}
Serial.println();
}
}
void setup()
{
Serial.begin(9600);
Serial.println(F("Modbus Client Example"));
// use Serial (port 0); initialize Modbus communication baud rate
mySerial.begin(modbus_baud);
serverA.begin(2, mySerial); // communicate with Modbus server ID 2 over the given Serial interface
// Init enable pins for modbus master library
pinMode(modbus_enable_pin, OUTPUT);
digitalWrite(modbus_enable_pin, LOW);
// Callbacks allow us to configure the RS485 transceiver correctly
serverA.preTransmission(preTransmission);
serverA.postTransmission(postTransmission);
}
void loop()
{
requestData();
}
Der Code wirf ein Warning aus der CRC Berechnung. Das ist aber in der Library die nicht mir gehört. Sollte sonst weiter nichts stören. Weiters ist im preTransmission / postTransmission eine "Bremse" drinnen, um den Server nicht mit Anfragen zu überhäufen. Da aber nur 1 Paket alle 2 Sekunden gelesen wird, schlägt diese Bremse de facto nicht zu. Aber man kann relativ einfach weitere Pakete ergänzen ohne dass was passiert.
Und das ist ein Server mit Adresse 2:
der je zwei Analogeingänge und Digitaleingänge ausliest:
/*******************************************************
Modbus Server (Modbus Slave)
This is a reduced example of 0218_Modbus_Server_all_FC
FC Modbus Bit Arduino Example address in this sketch
--------------------------------------------------------------------------------------------
FC4 Read Input Registers 16 analogRead 2000..2001
FC4 Read Input Registers 16 digitalRead 2002..2003
structure of this sketch:
- definitions for the Serial interface - either Software or Hardware and if necessary the TX enable pin
- definition of the Modbus library
hardware
- several GPIOs configured as Output for relays (or LEDs) to simulate "Coils"
- a MAX485-TTL adapter
by noiasca
2023-01-31 ok
*******************************************************/
#include <NoiascaModbusServerSimple.h> // Modbus Server Simple library https://werner.rothschopf.net/microcontroller/202112_arduino_modbus_server.htm
/* **************************************************** *
settings, variables, objects ...
* **************************************************** */
constexpr byte modbus_slave_id = 2; // Modbus address of this Modbus Server (slave)
constexpr byte inputRegisterPin[] {A0, A1}; // GPIOs as input Registers (FC4)
constexpr byte digitalPin[] { 6, 12}; // GPIOs for digital pins to be read with (FC4)
constexpr byte modbus_enable_pin = 11; // The GPIO used to control the MAX485 TX pin. Set to 255 if you are not using RS485 or a selfsensing adapter
constexpr byte rxPin = 2; // for Softserial
constexpr byte txPin = 3;
/* *******************************************************
Serial Interface
* **************************************************** */
// if you don't have enough HW Serial (i.e. on an UNO)
// you are forced to use SoftwareSerial or AltSoftSerial
#include <SoftwareSerial.h>
SoftwareSerial mySerial(rxPin, txPin); // RXpin, TXpin
// On a Mega you can simply use
// a Reference to an existing HW Serial:
// HardwareSerial &mySerial = Serial3;
// If you don't need debugging output, you can even use
// a reference to Serial
//HardwareSerial &mySerial = Serial;
constexpr byte modbus_format = SERIAL_8N1; // if you can use Hardware Serial, Modbus prefered value is SERIAL_8N2
constexpr uint32_t modbus_baud = 9600; // use slow speeds with SoftSerial. 19200 is OK
/* **************************************************** *
Modbus
* **************************************************** */
// Both client and server must know the registers keeping the register
// holding registers (occupy memory)
enum
{
// just add or remove registers and your good to go...
// The first register starts at address 0 which is for Function 3 Register 40001
programVersion, //
HOLDING_REGS_SIZE // leave this one
};
unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array
ModbusServer modbusServer(modbus_slave_id, mySerial);
/*
Modbus Client callbacks
*/
void cbPreTransmission()
{
digitalWrite(modbus_enable_pin, HIGH); // handle the DE/RE pins
}
void cbPostTransmission()
{
digitalWrite(modbus_enable_pin, LOW); // handle the DE/RE pins
}
// callback to evaluate if a FC4 Input Register is valid
bool cbIsValidInputRegister(const uint16_t reg)
{
switch (reg)
{
case 2000 ... 2003 : // a range of valid input register
return true;
default :
return false;
}
}
// callback to respond to FC4 read: Client has requested an input register
uint16_t cbInputRegisterViewed(const uint16_t reg)
{
//Serial.print(F("requested input register:")); Serial.println(reg);
switch (reg)
{
case 2000: return analogRead(inputRegisterPin[0]); // you could read a value on demand
case 2001: return analogRead(inputRegisterPin[1]);
case 2002: return digitalRead(digitalPin[0]) == LOW ? 0 : 1;
case 2003: return digitalRead(digitalPin[1]) == LOW ? 0 : 1;
default: return 0xEEEE; // example for error (when user has requested a non existing register
}
}
/* **************************************************** *
Arduino default setup and loop
* **************************************************** */
void setup()
{
holdingRegs[programVersion] = 1;
Serial.begin(9600); // only used for Debugging
Serial.println(F("Modbus Server with Noiasca Modbus Server Simple"));
Serial.flush();
mySerial.begin(modbus_baud); // start the Serial used for Modbus. Remember: Softserial doesn't support modbus_format
//mySerial.begin(modbus_baud, modbus_format); // Hardware Serial supports modbus_format
modbusServer.begin(&mySerial, modbus_baud, modbus_format, modbus_slave_id, HOLDING_REGS_SIZE, holdingRegs);
pinMode(modbus_enable_pin, OUTPUT); // if you need a TX enable pin, set it to Output
modbusServer.setPreTransmission(cbPreTransmission); // if you need a TX enable pin, set a callback to your pin handling
modbusServer.setPostTransmission(cbPostTransmission); // if you need a TX enable pin, set a callback to your pin handling
modbusServer.setInputRegisterViewed(cbInputRegisterViewed); // set callback for: FC4 Read Input Register
modbusServer.setIsValidInputRegister(cbIsValidInputRegister);// the server must know the valid FC4 Read Input Registers
// init local hardware
for (auto & i : digitalPin) pinMode(i, INPUT);
for (auto & i : inputRegisterPin) pinMode(i, INPUT);
}
void loop()
{
modbusServer.update(); // call modbus in loop
// do other things non blocking in loop
}
Beide male verwende ich Soft-Serial an 2/3 sowie den enable Pin an 11.
Mit Hardware getestet. Soll funktionieren.
Der Client gibt auf der Seriellen so etwas aus:
FC4 Input Register 2000=1023
FC4 Input Register 2001=1023
FC4 Input Register 2002=1
FC4 Input Register 2003=1
Antwortet der Server nicht kommt es zu einer Error Meldung:
E ServerA no input register 2000 result=E2