forked from tbnobody/OpenDTU
-
Notifications
You must be signed in to change notification settings - Fork 0
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. At Modbus ID 243 the server serves a Sunspec meter that provides aggregated AC power and AC yield values of all registered inverters. The OpenDTU Modbus sources were imspired by : https://github.com/ArekKubacki/OpenDTU. See tbnobody#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. Signed-off-by: Bobby Noelte <[email protected]>
- Loading branch information
Showing
16 changed files
with
1,119 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,79 @@ | ||
// 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; | ||
int16_t val_i16; | ||
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; | ||
// fixed point converted to u16 | ||
uint16_t fixed_point_u16; | ||
// uint64 converted to hex string | ||
char u64_hex_str[sizeof(uint64_t) * 8 + 1]; | ||
// 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 | ||
uint16_t addFloat32(const float_t &val, const size_t reg_offset); | ||
|
||
// Add float as 32 bit decimal fixed point to Modbus register | ||
uint16_t addFloatAsDecimalFixedPoint32(const float_t &val, const float &precision, const size_t reg_offset); | ||
|
||
// Add float as 16 bit decimal fixed point to Modbus register | ||
uint16_t addFloatAsDecimalFixedPoint16(const float_t &val, const float &precision); | ||
|
||
// Add string to Modbus register | ||
uint16_t addString(const char * const str, const size_t length, const size_t reg_offset); | ||
|
||
// Add string to Modbus register | ||
uint16_t addString(const String &str, const size_t reg_offset); | ||
|
||
// Add uint16 to Modbus register | ||
uint16_t addUInt16(const uint16_t val); | ||
|
||
// Add uint32 to Modbus register | ||
uint16_t addUInt32(const uint32_t val, const size_t reg_offset); | ||
|
||
// Add uint64 to Modbus register | ||
uint16_t addUInt64(const uint64_t val, const size_t reg_offset); | ||
|
||
// Convert uint64 to hex string and add to Modbus register | ||
uint16_t addUInt64AsHexString(const uint64_t val, const size_t reg_offset); | ||
|
||
// Convert IP address to string and add to Modbus register | ||
uint16_t addIPAddressAsString(const IPAddress val, const size_t reg_offset); | ||
}; | ||
|
||
ModbusMessage DTUPro(ModbusMessage request); | ||
ModbusMessage OpenDTUTotal(ModbusMessage request); | ||
ModbusMessage OpenDTUMeter(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,123 @@ | ||
// 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; | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addFloat32(const float_t &val, const size_t reg_offset) { | ||
// Use union to convert from float to uint32 | ||
value.val_float = val; | ||
|
||
return addUInt32(value.val_u32, reg_offset); | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addFloatAsDecimalFixedPoint32(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; | ||
} | ||
|
||
return addUInt32(conv.fixed_point_u32, reg_offset); | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addFloatAsDecimalFixedPoint16(const float_t &val, const float &precision) { | ||
// 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 uint16 | ||
value.val_i16 = round(val * std::pow(10, precision)); | ||
|
||
add(value.val_u16); | ||
return value.val_u16; | ||
} | ||
|
||
uint16_t 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); | ||
return value.val_u16; | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addString(const String &str, const size_t reg_offset) { | ||
return addString(str.c_str(), str.length(), reg_offset); | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addUInt16(const uint16_t val) { | ||
add(val); | ||
return val; | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addUInt32(const uint32_t val, const size_t reg_offset) { | ||
if (reg_offset <= 1) { | ||
value.val_u16 = val >> (16 * (1 - reg_offset)); | ||
} else { | ||
value.val_u16 = 0; | ||
} | ||
add(value.val_u16); | ||
return value.val_u16; | ||
} | ||
|
||
uint16_t ModbusDTUMessage::addUInt64(const uint64_t val, const size_t reg_offset) { | ||
if (reg_offset <= 3) { | ||
value.val_u16 = val >> (16 * (3 - reg_offset)); | ||
} else { | ||
value.val_u16 = 0; | ||
} | ||
add(value.val_u16); | ||
return value.val_u16; | ||
} | ||
|
||
uint16_t 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) & 0xFFFFFFFFUL)), | ||
((uint32_t)(val & 0xFFFFFFFFUL))); | ||
// mark conversion | ||
value.val_u64 = val; | ||
} | ||
|
||
return addString(&conv.u64_hex_str[0], sizeof(conv.u64_hex_str), reg_offset); | ||
} | ||
|
||
uint16_t 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; | ||
} | ||
|
||
return addString(&conv.ip_str[0], sizeof(conv.ip_str), reg_offset); | ||
} | ||
|
||
// Create server(s) | ||
ModbusServerTCPasync ModbusTCPServer; |
Oops, something went wrong.