Example application with hardware #112
Replies: 6 comments 10 replies
-
Beta Was this translation helpful? Give feedback.
-
Modbus client (esp32)This is where this library comes to play! To compile the firmware, I used Platformio but Arduino IDE will do as well. #include <Arduino.h>
// Include the header for the ModbusClient RTU style
#include "ModbusClientRTU.h"
#include "Logging.h"
// Definitions for this special case
#define RXPIN GPIO_NUM_5
#define TXPIN GPIO_NUM_18
#define REDEPIN GPIO_NUM_19
#define BAUDRATE 19200
#define FIRST_REGISTER 0x0001
#define NUM_VALUES 2
#define READ_INTERVAL 10000
bool data_ready = false;
float values[NUM_VALUES];
uint32_t request_time;
// Create a ModbusRTU client instance
// The RS485 module has no halfduplex, so the second parameter with the DE/RE pin is required!
ModbusClientRTU MB(Serial1, REDEPIN);
// Define an onData handler function to receive the regular responses
// Arguments are received response message and the request's token
void handleData(ModbusMessage response, uint32_t token)
{
// First value is on pos 3, after server ID, function code and length byte
uint16_t offs = 3;
// The device has values all as IEEE754 float32 in two consecutive registers
// Read the requested in a loop
for (uint8_t i = 0; i < NUM_VALUES; ++i) {
offs = response.get(offs, values[i]);
}
// Signal "data is complete"
request_time = token;
data_ready = true;
}
// Define an onError handler function to receive error responses
// Arguments are the error code returned and a user-supplied token to identify the causing request
void handleError(Error error, uint32_t token)
{
// ModbusError wraps the error code and provides a readable error message for it
ModbusError me(error);
LOG_E("Error response: %02X - %s\n", (int)me, (const char *)me);
}
// Setup() - initialization happens here
void setup() {
// Init Serial monitor
Serial.begin(115200);
while (!Serial) {}
Serial.println("__ OK __");
// Set up Serial2 connected to Modbus RTU
Serial1.begin(BAUDRATE, SERIAL_8N1, RXPIN, TXPIN);
// Set up ModbusRTU client.
// - provide onData handler function
MB.onDataHandler(&handleData);
// - provide onError handler function
MB.onErrorHandler(&handleError);
// Set message timeout to 2000ms
MB.setTimeout(2000);
// Start ModbusRTU background task
MB.begin();
}
// loop() - cyclically request the data
void loop() {
static uint32_t next_request = millis();
// Shall we do another request?
if (millis() - next_request > READ_INTERVAL) {
// Yes.
data_ready = false;
// Issue the request
Error err = MB.addRequest(millis(), 3, READ_INPUT_REGISTER, FIRST_REGISTER, NUM_VALUES * 2);
if (err!=SUCCESS) {
ModbusError e(err);
LOG_E("Error creating request: %02X - %s\n", (int)e, (const char *)e);
}
// Save current time to check for next cycle
next_request = millis();
} else {
// No, but we may have another response
if (data_ready) {
// We do. Print out the data
Serial.printf("Requested at %8.3fs:\n", request_time / 1000.0);
for (uint8_t i = 0; i < NUM_VALUES; ++i) {
Serial.printf(" %04X: %8.3f\n", i * 2 + FIRST_REGISTER, values[i]);
}
Serial.printf("----------\n\n");
data_ready = false;
}
}
} [platformio]
default_envs = esp32
[env]
framework = arduino
lib_extra_dirs =
../libraries
monitor_speed = 115200
[common]
build_flags=
-Wall
-DLOG_LEVEL=LOG_LEVEL_DEBUG
[env:esp32]
platform = espressif32
board = esp32dev
build_flags =
${common.build_flags}
;-D DEBUG_ESP_PORT=Serial
monitor_filters = esp32_exception_decoder |
Beta Was this translation helpful? Give feedback.
-
OutputWith the above circuit and code, you should get these results: esp32 Serial monitor:
Logic analyzer images:
|
Beta Was this translation helpful? Give feedback.
-
Wonderful work indeed. Thanks a lot for undergoing that! Do I understand that with the proper termination, everything runs fine without any changes? |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Beta Was this translation helpful? Give feedback.
-
Circuit
This is an example circuit for the ESP32 side. Keep in mind that biasing resistors (the two 680Ohms resistors) need to be added only once on the entire bus. The terminating resistors have to be added on both ends of the bus and only there.
I use a MAX3485 which is similar to the MAX485 but can be powered by 3.3V. Some people have success with running a MAX485 on 3.3V but I like to operate within specification.
The A-line (non-inverting) is pulled up to 3.3V and line B (inverting) is pulled down to ground using 680Ohm resistors. A 120Ohm resistor is added to avoid communication errors on long wires. But even if you use short wires, add the resistor because it leaves the bus on 0.171V on the bus (measured). It should near or above 0.2V. This leaves the
DO
-pin high during idling.Calculation:
3.3V --> 680Ohms --> 2x 120Ohms parallel --> 680Ohms --> 0V results in 2.32mA
Voltage over the two (!) 120Ohms resistors is 0.1392V
Breadboarded version:
on the left, an ESP32, on the right a max3485 with three resistors.
Beta Was this translation helpful? Give feedback.
All reactions