-
-
Notifications
You must be signed in to change notification settings - Fork 509
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OpenDTU is extended by a Modbus server. The Modbus server serves TCP at port 502. At Modbus ID 1 the server mimicks the Modbus registers in the original DTUPro. At Modbus ID 125 the server serves a SunSpec compatible pseudo inverter that provides the OpenDTU aggregated data from all registered inverters. The OpenDTU Modbus sources were imspired by : https://github.com/ArekKubacki/OpenDTU. See #582 for the orignal pull request. The Modbus library used for Modbus communication is: https://github.com/eModbus/eModbus. Documentation for the library is here: https://emodbus.github.io/. The library was choosen to achieve a lower memory footprint. fixes #582 Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
- Loading branch information
Showing
15 changed files
with
881 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include <vector> | ||
|
||
#include <TaskSchedulerDeclarations.h> | ||
|
||
// eModbus | ||
#include "ModbusMessage.h" | ||
#include "ModbusServerTCPasync.h" | ||
|
||
class ModbusDTUMessage : public ModbusMessage { | ||
private: | ||
// Value cache, mostly for conversion | ||
union Value { | ||
float val_float; | ||
uint16_t val_u16; | ||
int32_t val_i32; | ||
uint32_t val_u32; | ||
uint64_t val_u64; | ||
uint32_t val_ip; | ||
} value; | ||
|
||
// Conversion cache | ||
union Conversion { | ||
// fixed point converted to u32 | ||
uint32_t fixed_point_u32; | ||
// uint64 converted to hex string | ||
char u64_hex_str[sizeof(uint64_t) * 8 + 1]; | ||
// uint64 converted to 12 decimal digits (6 registers) in big endian | ||
std::array<uint16_t, 6> u64_dec_digits; | ||
// ip address converted to String | ||
char ip_str[12]; | ||
} conv; | ||
|
||
public: | ||
// Default empty message Constructor - optionally takes expected size of MM_data | ||
explicit ModbusDTUMessage(uint16_t dataLen); | ||
|
||
// Special message Constructor - takes a std::vector<uint8_t> | ||
explicit ModbusDTUMessage(std::vector<uint8_t> s); | ||
|
||
// Add float to Modbus register | ||
void addFloat32(const float_t &val, const size_t reg_offset); | ||
|
||
// Add float as decimal fixed point to Modbus register | ||
void addFloat32AsDecimalFixedPoint(const float_t &val, const float &precision, const size_t reg_offset); | ||
|
||
// Add string to Modbus register | ||
void addString(const char * const str, const size_t length, const size_t reg_offset); | ||
|
||
// Add string to Modbus register | ||
void addString(const String &str, const size_t reg_offset); | ||
|
||
// Add uint32 to Modbus register | ||
void addUInt32(const uint32_t val, const size_t reg_offset); | ||
|
||
// Add uint64 to Modbus register | ||
void addUInt64(const uint64_t val, const size_t reg_offset); | ||
|
||
// Convert uint64 to hex string and add to Modbus register | ||
void addUInt64AsHexString(const uint64_t val, const size_t reg_offset); | ||
|
||
// Convert uint64 to 12 decimal digits (big endian) and add to Modbus register | ||
void addUInt64AsDecimalDigits(const uint64_t val, const size_t reg_offset); | ||
|
||
// Convert IP address to string and add to Modbus register | ||
void addIPAddressAsString(const IPAddress val, const size_t reg_offset); | ||
}; | ||
|
||
ModbusMessage OpenDTUTotal(ModbusMessage request); | ||
ModbusMessage DTUPro(ModbusMessage request); | ||
|
||
extern ModbusServerTCPasync ModbusTCPServer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
class ModbusSettingsClass { | ||
public: | ||
ModbusSettingsClass(); | ||
void init(); | ||
|
||
void performConfig(); | ||
|
||
private: | ||
void startTCP(); | ||
|
||
void stopTCP(); | ||
}; | ||
|
||
extern ModbusSettingsClass ModbusSettings; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include <ESPAsyncWebServer.h> | ||
#include <TaskSchedulerDeclarations.h> | ||
|
||
class WebApiModbusClass { | ||
public: | ||
void init(AsyncWebServer& server, Scheduler& scheduler); | ||
|
||
private: | ||
void onModbusStatus(AsyncWebServerRequest* request); | ||
void onModbusAdminGet(AsyncWebServerRequest* request); | ||
void onModbusAdminPost(AsyncWebServerRequest* request); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
/* | ||
* Copyright (C) 2024 Bobby Noelte | ||
*/ | ||
#include <array> | ||
#include <cstring> | ||
#include <string> | ||
|
||
// OpenDTU | ||
#include "ModbusDtu.h" | ||
|
||
|
||
ModbusDTUMessage::ModbusDTUMessage(uint16_t dataLen = 0) : ModbusMessage(dataLen) { | ||
value.val_float = NAN; | ||
} | ||
|
||
ModbusDTUMessage::ModbusDTUMessage(std::vector<uint8_t> s) : ModbusMessage(s) { | ||
value.val_float = NAN; | ||
} | ||
|
||
void ModbusDTUMessage::addFloat32(const float_t &val, const size_t reg_offset) { | ||
// Use union to convert from float to uint32 | ||
value.val_float = val; | ||
|
||
addUInt32(value.val_u32, reg_offset); | ||
} | ||
|
||
void ModbusDTUMessage::addFloat32AsDecimalFixedPoint(const float_t &val, const float &precision, const size_t reg_offset) { | ||
// Check if value is already converted to fixed point | ||
if (value.val_float != val) { | ||
// Multiply by 10^precision to shift the decimal point | ||
// Round the scaled value to the nearest integer | ||
// Use union to convert from fixed point to uint32 | ||
value.val_i32 = round(val * std::pow(10, precision)); | ||
// remember converted value | ||
conv.fixed_point_u32 = value.val_u32; | ||
// mark conversion | ||
value.val_float = val; | ||
} | ||
|
||
addUInt32(conv.fixed_point_u32, reg_offset); | ||
} | ||
|
||
void ModbusDTUMessage::addString(const char * const str, const size_t length, const size_t reg_offset) { | ||
// Check if the position is within the bounds of the string | ||
size_t offset = reg_offset * sizeof(uint16_t); | ||
if (offset + sizeof(uint16_t) <= length) { | ||
// Reinterpret the memory at position 'offset' as uint16_t | ||
std::memcpy(&value.val_u16, str + offset, sizeof(uint16_t)); | ||
} else { | ||
value.val_u16 = 0; | ||
} | ||
|
||
add(value.val_u16); | ||
} | ||
|
||
void ModbusDTUMessage::addString(const String &str, const size_t reg_offset) { | ||
addString(str.c_str(), str.length(), reg_offset); | ||
} | ||
|
||
void ModbusDTUMessage::addUInt32(const uint32_t val, const size_t reg_offset) { | ||
if (reg_offset <= 1) { | ||
add((uint16_t)(val >> 16 * (1 - reg_offset))); | ||
} else { | ||
add((uint16_t)0); | ||
} | ||
} | ||
|
||
void ModbusDTUMessage::addUInt64(const uint64_t val, const size_t reg_offset) { | ||
if (reg_offset <= 3) { | ||
add((uint16_t)(val >> 16 * (3 - reg_offset))); | ||
} else { | ||
add((uint16_t)0); | ||
} | ||
} | ||
|
||
void ModbusDTUMessage::addUInt64AsHexString(const uint64_t val, const size_t reg_offset) { | ||
// Check if value is already converted to hex string | ||
if (val != value.val_u64) { | ||
snprintf(&conv.u64_hex_str[0], sizeof(conv.u64_hex_str), "%0x%08x", | ||
((uint32_t)((val >> 32) & 0xFFFFFFFF)), | ||
((uint32_t)(val & 0xFFFFFFFF))); | ||
// mark conversion | ||
value.val_u64 = val; | ||
} | ||
|
||
addString(&conv.u64_hex_str[0], sizeof(conv.u64_hex_str), reg_offset); | ||
} | ||
|
||
void ModbusDTUMessage::addUInt64AsDecimalDigits(const uint64_t val, const size_t reg_offset) { | ||
if (val != value.val_u64) { | ||
value.val_u64 = val; | ||
// Extract digits from the number | ||
for (int i = 6 - 1; i >= 0; i--) { | ||
conv.u64_dec_digits[i] = value.val_u64 % 10; // Extract the least significant digit | ||
value.val_u64 /= 10; // Remove the least significant digit | ||
conv.u64_dec_digits[i] += (value.val_u64 % 10) << 8; // Extract the least significant digit | ||
value.val_u64 /= 10; // Remove the least significant digit | ||
} | ||
// mark conversion | ||
value.val_u64 = val; | ||
} | ||
|
||
if (reg_offset < 6) { | ||
add(conv.u64_dec_digits[reg_offset]); | ||
} else { | ||
add((uint16_t)0); | ||
} | ||
} | ||
|
||
void ModbusDTUMessage::addIPAddressAsString(const IPAddress val, const size_t reg_offset) { | ||
// Check if value is already converted to hex string | ||
if (val != value.val_ip) { | ||
String str(val.toString()); | ||
std::memcpy(&conv.ip_str, str.c_str(), std::min(sizeof(conv.ip_str), str.length())); | ||
// mark conversion | ||
value.val_ip = val; | ||
} | ||
|
||
addString(&conv.ip_str[0], sizeof(conv.ip_str), reg_offset); | ||
} | ||
|
||
// Create server(s) | ||
ModbusServerTCPasync ModbusTCPServer; |
Oops, something went wrong.