diff --git a/examples/arduino/README.md b/examples/arduino/README.md new file mode 100644 index 0000000..5e02ea7 --- /dev/null +++ b/examples/arduino/README.md @@ -0,0 +1,4 @@ +## Arduino examples + +This folder contains nanoMODBUS examples for Arduino. +To build and load a sketch with the Arduino IDE, copy `nanomodbus.c` and `nanomodbus.h` inside its folder. \ No newline at end of file diff --git a/examples/arduino/client-rtu/client-rtu.ino b/examples/arduino/client-rtu/client-rtu.ino new file mode 100644 index 0000000..b113baf --- /dev/null +++ b/examples/arduino/client-rtu/client-rtu.ino @@ -0,0 +1,90 @@ +/* + This example client application connects via RTU to a server and sends some requests to it. +*/ + +#include "nanomodbus.h" + +// The server address +#define RTU_SERVER_ADDRESS 1 + + +int32_t read_serial(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) { + Serial.setTimeout(byte_timeout_ms); + return Serial.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 Serial.write(buf, count); +} + + +void onError() { + // Make the LED blink on error + while (true) { + digitalWrite(LED_BUILTIN, HIGH); + delay(1000); + digitalWrite(LED_BUILTIN, LOW); + delay(1000); + } +} + + +void setup() { + pinMode (LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + Serial.begin(9600); + while (!Serial); +} + + +void loop() { + nmbs_platform_conf platform_conf; + platform_conf.transport = NMBS_TRANSPORT_RTU; + platform_conf.read = read_serial; + platform_conf.write = write_serial; + + nmbs_t nmbs; + nmbs_error err = nmbs_client_create(&nmbs, &platform_conf); + if (err != NMBS_ERROR_NONE) + onError(); + + nmbs_set_read_timeout(&nmbs, 1000); + nmbs_set_byte_timeout(&nmbs, 100); + + nmbs_set_destination_rtu_address(&nmbs, RTU_SERVER_ADDRESS); + + // Write 2 coils from address 64 + nmbs_bitfield coils; + nmbs_bitfield_write(coils, 0, 1); + nmbs_bitfield_write(coils, 1, 1); + err = nmbs_write_multiple_coils(&nmbs, 64, 2, coils); + if (err != NMBS_ERROR_NONE) + onError(); + + // Read 3 coils from address 64 + nmbs_bitfield_reset(coils); // Reset whole bitfield to zero + err = nmbs_read_coils(&nmbs, 64, 3, coils); + if (err != NMBS_ERROR_NONE) + onError(); + + // Write 2 holding registers at address 26 + uint16_t w_regs[2] = {123, 124}; + err = nmbs_write_multiple_registers(&nmbs, 26, 2, w_regs); + if (err != NMBS_ERROR_NONE) + onError(); + + // Read 2 holding registers from address 26 + uint16_t r_regs[2]; + err = nmbs_read_holding_registers(&nmbs, 26, 2, r_regs); + if (err != NMBS_ERROR_NONE) + onError(); + + // Turn off the led on success + digitalWrite(LED_BUILTIN, LOW); + + // No need to destroy the nmbs instance, bye bye + exit(0); +} diff --git a/examples/arduino/server-rtu/server-rtu.ino b/examples/arduino/server-rtu/server-rtu.ino new file mode 100644 index 0000000..ea2cc9c --- /dev/null +++ b/examples/arduino/server-rtu/server-rtu.ino @@ -0,0 +1,144 @@ +/* + This example application sets up an RTU server and polls from modbus requests + + This server supports the following function codes: + FC 01 (0x01) Read Coils + FC 03 (0x03) Read Holding Registers + FC 15 (0x0F) Write Multiple Coils + FC 16 (0x10) Write Multiple registers +*/ + +#include "nanomodbus.h" + +// The data model of this sever will support coils addresses 0 to 100 and registers addresses from 0 to 32 +#define COILS_ADDR_MAX 100 +#define REGS_ADDR_MAX 32 + +// Our RTU address +#define RTU_SERVER_ADDRESS 1 + +// 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 Serial.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 Serial.write(buf, count); +} + + +void onError() { + // Make the LED blink on error + while (true) { + digitalWrite(LED_BUILTIN, HIGH); + delay(1000); + digitalWrite(LED_BUILTIN, LOW); + delay(1000); + } +} + + +nmbs_error handle_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out) { + 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) { + 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) { + 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) { + 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 setup() { + pinMode (LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + Serial.begin(9600); + while (!Serial); +} + + +void loop() { + nmbs_platform_conf platform_conf = {0}; + platform_conf.transport = NMBS_TRANSPORT_RTU; + platform_conf.read = read_serial; + platform_conf.write = write_serial; + platform_conf.arg = NULL; + + // These functions are defined in server.h + 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); + + 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; + } + + // Turn off the led at the end + digitalWrite(LED_BUILTIN, LOW); + + // No need to destroy the nmbs instance, bye bye + exit(0); +}