emsesp / EMS-ESP32

ESP32 firmware to read and control EMS and Heatronic compatible equipment such as boilers, thermostats, solar modules, and heat pumps
https://emsesp.github.io/docs
GNU Lesser General Public License v3.0
584 stars 100 forks source link

Feature: Modbus Support #1744

Closed proddy closed 2 months ago

proddy commented 4 months ago

Creating this issue to track issues on the Modbus feature.

@mheyse did all the coding, in this PR https://github.com/emsesp/EMS-ESP32/pull/1618

Code is in this branch: https://github.com/emsesp/EMS-ESP32/tree/feat_modbus

proddy commented 4 months ago

I've added the Web settings.

Also noticed with modbus enabled it takes up 17kb of heap memory, and 13 when disabled. We need to try and bring that back. I haven't looked into all the code yet.

mheyse commented 4 months ago

How exactly do you compare the heap usage? Build the firmware from dev and from feat_modbus and compare System -> System Memory -> Heap? Or is there a better way?

I suspect that the data I allocate in modbus_entity_parameters.cpp is not stored in Flash (as I thought it would) if it takes up 13kB of heap memory even when disabled.

There are other optimizations I can do (e.g. I register 4 modbus workers each for all ems devices, this could be changed to use ANY_SERVER and ANY_FUNCTION_CODE wildcards)

proddy commented 4 months ago

yes, I actually use a python script for memory profiling but you can also use the WebUI or just read the "free mem" from the JSON output of http://ems-esp.local/api/system

The Modbus is a great feature and easily fits on modern ESP32s like the S3, but it will struggle on 4MB variants. We just need to find ways to reduce the amount of heap memory if Modbus isn't been used. When it's enabled it's fine as that is the price to pay for a new module/feature.

I see the object ModbusServerTCPasync instantiated as default but this can't be the main culprit, something else is eating the memory, even when it's disabled.

proddy commented 4 months ago

could you possibly instantiate ModbusServerTCPasync from within Modbus::start() and just use static pointers? This may save on heap memory.

Also check that LOCAL_LOG_LEVEL is not set, unless EMSESP_DEBUG is.

I'm working on a new C++ Factory class so we can try this as a hot-pluggable module. Could you describe which integration points you have with EMS-ESP? I see the start() but you have nothing in the loop() ?

proddy commented 4 months ago

dev has 190KB free heap, featmodbus with Modbus disabled and using the Michael's change to `static Modbus * modbus` is 177KB so we're still eating up 13KB of memory even if not using Modbus. We need to bring this down before accepting into the dev branch. Can you see what you can do @mheyse ?

mheyse commented 4 months ago

Yes. The issue is the static data with the Modbus Parameters. It ends up in the .flash.text section but apparently it's copied to RAM anyways, even after I've made everything const. I guess I have to familiarize myself with the ESP32 memory model to debug this.If all fails I can still try to embed the data as a static binary but that will be pretty ugly.Allocating the Modbus client dynamically in start() doesn't help at all by the way.Am 09.05.2024 16:57 schrieb Proddy @.**>: dev has 190KB free heap, feat_modbus with Modbus disabled and using the Michael's change to static Modbus modbus_ is 177KB so we're still eating up 13KB of memory even if not using Modbus. We need to bring this down before accepting into the dev branch. Can you see what you can do @mheyse ?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 4 months ago

The design pattern is that Modbus should allocate as little to no heap memory if the service is not enabled. So something is still lurking in the headerfiles and eating up the heap during runtime (not static, progmem/flash). I still suspect it's the creation of the ModbusServerTCPasync object in modbus.cpp - see if you can turn that into a pointer and new-it when it needs to be created.

mheyse commented 4 months ago

I did make the ModbusServerTCPasync a pointer and allocate it dynamically in "Modbus::start" using new - no change at all in memory consumption if Modbus is disabled. I'll push the changes anyways.

On the other hand, if (just as a test) I remove all entries from modbus_register_mappings this reduces memory consumption by about 10kB if Modbus is not enabled - so it's this static data that somehow allocates heap memory. I have to figure out how to solve that because it should be possible to have this static data in flash only.

Unfortunately I have a project to finish at work and not a lot of spare time, but I'll look into it as soon as possible.

------ Originalnachricht ------ Von "Proddy" @.> An "emsesp/EMS-ESP32" @.> Cc "mheyse" @.>; "Mention" @.> Datum 10.05.2024 10:16:27 Betreff Re: [emsesp/EMS-ESP32] Feature: Modbus Support (Issue #1744)

The design pattern is that Modbus should allocate as little to no heap memory if the service is not enabled. So something is still lurking in the headerfiles and eating up the heap during runtime (not static, progmem/flash). I still suspect it's the creation of the ModbusServerTCPasync object in modbus.cpp - see if you can turn that into a pointer and new-it when it needs to be created.

— Reply to this email directly, view it on GitHub https://github.com/emsesp/EMS-ESP32/issues/1744#issuecomment-2104158538, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABJD5GWEIQNE6BM4B25OCM3ZBR65XAVCNFSM6AAAAABHIYXM6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMBUGE2TQNJTHA. You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 4 months ago

that's probably it - missed that. The modbus_register_mappings list and tag_to_type map. Is there a way to populate both these in the start() function using static_ptr? Also watch out for that Serial.print() in the start() function - sending Serial on a live EMS-ESP can cause havok since we're spoofing the UART for the EMS bus.

even better is to make it dynamic, like we do with HA MQTT Discovery so the modbus registry's are only created when a valid ems device entity is found.

mheyse commented 4 months ago

FYI, I submitted another PR addressing the memory iasues: https://github.com/emsesp/EMS-ESP32/pull/1770

proddy commented 4 months ago

thanks, I'll take a look and run some more benchmarking. I've been out the last weeks. I still need to fix the Web settings right?

mheyse commented 4 months ago

Yes, I didn't do anything regarding the web settings.Am 28.05.2024 13:45 schrieb Proddy @.***>: thanks, I'll take a look and run some more benchmarking. I've been out the last weeks. I still need to fix the Web settings right?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 4 months ago

I made some small changes to the branch to fix the UI settings.

Your latest changes are good with memory without modbus code its 195 / 107 (free heap / max alloc) with modbus disabled its 192 / 107 with modbus enabled its 187 / 107

So this feature only takes up 3kb extra, which is acceptable.

mheyse commented 4 months ago

Great!

Let's discuss how to integrate the modbus parameter generation into the build process. Currently you need to do the following:

update_modbus_registers.py assigns modbus registers to all entities that do not have a register yet, keeping already assigned registers the same as before. Then it generates the c++ code with the parameters and writes it to stdout. If there are any breaking changes and old regsiter assignments can not be re-used, all parameters must be deleted from modbus_entity_parameters.hpp manually before running dump_entities.sh - this re-assigns registers to all entities (possibly changing registers).

EMS-ESP32 doesn't seem to know the size of string entities, so it's not possible to add this to the dump (is that correct?) - that's why the size of all string entities has to be defined manually in update_modbus_registers.py.

Is this process acceptable or do you have any suggestions to improve it?

(as an alternative you could maintain modbus_entity_parameters.hpp completely by hand, modify it whenever entities change but that is cumbersome)

------ Originalnachricht ------ Von "Proddy" @.> An "emsesp/EMS-ESP32" @.> Cc "mheyse" @.>; "Mention" @.> Datum 04.06.2024 13:49:09 Betreff Re: [emsesp/EMS-ESP32] Feature: Modbus Support (Issue #1744)

I made some small changes to the branch to fix the UI settings.

Your latest changes are good with memory without modbus code its 195 / 107 (free heap / max alloc) with modbus disabled its 192 / 107 with modbus enabled its 187 / 107

So this feature only takes up 3kb extra, which is acceptable.

— Reply to this email directly, view it on GitHub https://github.com/emsesp/EMS-ESP32/issues/1744#issuecomment-2147332661, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABJD5GWIYTUS342PF4CLJTTZFWSTLAVCNFSM6AAAAABHIYXM6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNBXGMZTENRWGE. You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 4 months ago

Is there a way to add the Modbus registers dynamically as soon as they show up, like we do with MQTT Discovery?

mheyse commented 4 months ago

I think it is really important to have a stable register assignment so an entity's Modbus register does not change if other entities are added or removed. So the register offset (and the register count, at least for string entities) needs to be stored somewhere which is currently modbus_entity_parameters.hpp. (BTW, generating this file as part of the build process as I suggested earlier makes no sense as it has to be committed to the repo to ensure stable register assignments so it needs to be regenerated after adding or removing entities)It would also be possible to modify register_device_value() and add a modbus register offset and register count parameter, but maintaining this would be a manual process and quite painful.I can't think of a way to dynamically assign stable registers. It works for MQTT because it uses the entity names which are defined anyways with register_device_value.A clean solution might be to define all entities including their name, unit, data type, size etc. and also the Modbus parameters in a separate data file (e.g. a csv that is maybe compiled into a binary representation and embedded into the firmware image - it would be basically the dump_entities.csv file but it's the authority instead of an export). This file could be maintained manually (e.g. for adding new entities) and with tools (add Modbus parameters for new entities). Entries in this data structure would be referenced by the devices to register entities. But this would be a major development effort.So I guess for now it makes the most sense to document that update_modbus_registers.py needs to run after modifying entities. And maybe improve it so that it automatically dumps entities and generates code.Am 04.06.2024 22:58 schrieb Proddy @.***>: Is there a way to add the Modbus registers dynamically as soon as they show up, like we do with MQTT Discovery?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 3 months ago

Ok, I understand. I'll see if I can run the generation of modbus_entity_parameters.hpp as part of the platformio build target

proddy commented 3 months ago

I looked into automating this today, but it wouldn't compile. Seems running the generation of src/modbus_entity_parameters.hpp is not compatible with the modified class signatures. Can you take a look?

proddy commented 3 months ago

@mheyse any progress on this? It would be good to finally include it 3.7.0

mheyse commented 3 months ago

I was on vacation then sick for quite a while. Hope to have time for it soon.Am 21.06.2024 20:07 schrieb Proddy @.***>: @mheyse any progress on this? It would be good to finally include it 3.7.0

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 3 months ago

Sorry to hear you were unwell. There's no rush - let me know how I can help

mheyse commented 3 months ago

OK, I updated update_modbus_registers.py to reflect the changes I did for memory consuption improvements, forgot to do that.

How exactly do you plan to integrate it into the build process? As the generated modbus_entity_parameters.hpp needs to be committed to the repository after it has been updated with update_modbus_registers.py I don't think it can go into the build process. (I initially wrote that it should be added to the build process, but later stated that does not make sense in my opinion. Sorry for the confusion.)

I think the best way is to add another shell script that automatically dumps the entities and passes them to update_modbus_registers.py similar to dump_entities.sh (e.g. named update_modbus_registers.sh). This script would need to be executed manually whenever entities have been added, and then the updated modbus_entity_parameters.hpp needs to be committed.

------ Originalnachricht ------ Von "Proddy" @.> An "emsesp/EMS-ESP32" @.> Cc "mheyse" @.>; "Mention" @.> Datum 21.06.2024 20:07:51 Betreff Re: [emsesp/EMS-ESP32] Feature: Modbus Support (Issue #1744)

@mheyse https://github.com/mheyse any progress on this? It would be good to finally include it 3.7.0

— Reply to this email directly, view it on GitHub https://github.com/emsesp/EMS-ESP32/issues/1744#issuecomment-2183209395, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABJD5GQKP5NHOTP6JBKTYT3ZIRTXPAVCNFSM6AAAAABHIYXM6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOBTGIYDSMZZGU. You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 3 months ago

yes, I think it needs to be updated manually by running the script and the updated modbus_entity_parameters.hpp file checked in.

Is there some documentation you like to write, which explains what it does - which we can add to the wiki?

proddy commented 3 months ago

@mheyse I checked the code I think it's good to go. Can you refresh your local fork of the dev branch, then Git Merge your local copy of the feat_modbus branch? Then test and if it's good create a PR on dev. That way you'll own the feature and the PR.

mheyse commented 3 months ago

Yes I want to write documentation for devs and end users including generated tables with the modbus registers and data format for all entities. So far I haven't gotten much further than checking out the repo though.Am 29.06.2024 08:57 schrieb Proddy @.***>: yes, I think it needs to be updated manually by running the script and the updated modbus_entity_parameters.hpp file checked in. Is there some documentation you like to write, which explains what it does - which we can add to the wiki?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

mheyse commented 3 months ago

@proddy I merged with the current dev and created a PR against dev.

Please note:

For these files I used the version from dev and added the ENABLE_MODBUS translations:

interface/src/i18n/de/index.ts interface/src/i18n/en/index.ts interface/src/i18n/fr/index.ts interface/src/i18n/it/index.ts interface/src/i18n/nl/index.ts interface/src/i18n/no/index.ts interface/src/i18n/pl/index.ts interface/src/i18n/sk/index.ts interface/src/i18n/sv/index.ts interface/src/i18n/tr/index.ts

For these files I used the version from dev without modification:

interface/package.json interface/src/project/validators.ts interface/yarn.lock mock-api/rest_server.ts src/version.h

------ Originalnachricht ------ Von "Proddy" @.> An "emsesp/EMS-ESP32" @.> Cc "mheyse" @.>; "Mention" @.> Datum 29.06.2024 11:08:54 Betreff Re: [emsesp/EMS-ESP32] Feature: Modbus Support (Issue #1744)

@mheyse https://github.com/mheyse I checked the code I think it's good to go. Can you refresh your local fork of the dev branch, then Git Merge your local copy of the feat_modbus branch? Then test and if it's good create a PR on dev. That way you'll own the feature and the PR.

— Reply to this email directly, view it on GitHub https://github.com/emsesp/EMS-ESP32/issues/1744#issuecomment-2198056955, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABJD5GQCRX3SOJ3MR53AEQDZJZ2SNAVCNFSM6AAAAABHIYXM6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJYGA2TMOJVGU. You are receiving this because you were mentioned.Message ID: @.***>

mheyse commented 3 months ago

I submitted a PR for the documentation and also another PR to EMS-ESP32 with a script that auto generates the Modbus entity parameter documentation.

Please review, from my point of view everything should be ready to be released.

Message ID: @.***>

proddy commented 3 months ago

Thanks for that - we'll review.

proddy commented 3 months ago

merged.

proddy commented 3 months ago

@mheyse - I'm getting compile errors when compiling with -DEMSESP_TEST. It happens here:

#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
#include <modbus_test.h>
#endif

perhaps a namespace clash with Error

mheyse commented 2 months ago

How do I reproduce this? If I run

make clean make ARGS=-DEMSESP_TEST

the build process runs without error here (Ubuntu 22.04) and I can run the emsesp binary.

However, my tests do not seem to work any more, I get "unrecognized path" errors, I need to check why.

------ Originalnachricht ------ Von "Proddy" @.> An "emsesp/EMS-ESP32" @.> Cc "mheyse" @.>; "Mention" @.> Datum 04.07.2024 17:12:26 Betreff Re: [emsesp/EMS-ESP32] Feature: Modbus Support (Issue #1744)

@mheyse https://github.com/mheyse - I'm getting compile errors when compiling with -DEMSESP_TEST. It happens here:

if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)

include

endif

perhaps a namespace clash with Error

— Reply to this email directly, view it on GitHub https://github.com/emsesp/EMS-ESP32/issues/1744#issuecomment-2209207371, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABJD5GSNDLXWSXWHZEZJMETZKVQ5VAVCNFSM6AAAAABHIYXM6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMBZGIYDOMZXGE. You are receiving this because you were mentioned.Message ID: @.***>

proddy commented 2 months ago

set my_build_flags = -DEMSESP_TEST in a pio_local.ini or change it directly in platform.ini and the compile breaks.

We use EMSESP_TEST for creating real ESP32 firmware for testing on ESP32's, not just in standalone mode.

mheyse commented 2 months ago

Ah OK, I thought EMSESP_TEST is only used for testing on PCs. In this case the eModbus libraries are available, too, and || defined(EMSESP_TEST) can simply be removed.

Fix: https://github.com/emsesp/EMS-ESP32/pull/1853

wint100 commented 2 months ago

Is there a standard Modbus register list available?

mheyse commented 2 months ago

I created a PR with the docs here: https://github.com/emsesp/docs/pull/36 It also documents the modbus registers.

wint100 commented 2 months ago

I'm having issues writing to the 'Heating Temperature' using Modbus. I can read back all values, but when trying to write to Holding Register 14 with a uint16 value of 50, return an error on my Modbus Master. I've tried different Modbus Master hardware but both have the same result. Is there anything else I need to do when writing to a Holding Register?

mheyse commented 2 months ago

Please look for error messages in the system log and post them here.What error code do you get?Am 22.07.2024 17:12 schrieb wint100 @.***>: I'm having issues writing to the 'Heating Temperature' using Modbus. I can read back all values, but when trying to write to Holding Register 14 with a uint16 value of 50, return an error on my Modbus Master. I've tried different Modbus Master hardware but both have the same result. Is there anything else I need to do when writing to a Holding Register?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

wint100 commented 2 months ago

It appears to work on the S3 hardware but not on the older hardware. I have 20 of the S3 boards and these work well, but the older E32 boards (ESP32-D0WD-V3) don't seem to allow writing of the Heating Temperature.

Error from older board 2024-07-23 08:42:12.268 E 140: [modbus] Modbus write command failed with error: unrecognized path (Error)

proddy commented 2 months ago

This is in 3.7.0 now, so closing. Any issues/bugs can be reported using the normal channels.