Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper way to write to registers #39

Open
utdrmac opened this issue May 9, 2024 · 8 comments
Open

Proper way to write to registers #39

utdrmac opened this issue May 9, 2024 · 8 comments

Comments

@utdrmac
Copy link

utdrmac commented May 9, 2024

What's the correct way to write to a register? I'm not seeing much documentation on this. I just want to write to the registers holding the clock on my inverter to correct the time drift (and probably DST in Nov). I'm thinking this be just a one-shot type script that I can call on a cron job, say every Sunday, to set the clock. I'm not understanding how after I set 'write = true', then where do I specify which regs and what values to write? Thanks.

@HotNoob
Copy link
Owner

HotNoob commented May 9, 2024

yes, i should "write" some documentation for writing....

it's still a bit a of a work in progress. currently only single register values are writeable. ( 2 bytes )

so, because of the ambiguous nature of modbus,
i have a safety feature that requires the holding register to be accurate; i plan on making a per-variable bypass \w manual verification later.

with that enabled, the writing is done via mqtt. when write is enabled, the script subscribes to
{base topic}/write/{variable name}

with the eg4_v58 protocol, the holding registry map is i think a bit incomplete.
the verification check is done here:

so you can bypass that if it doesn't pass, and at the very minimum you'll have to set the value range for the clock.

oh and the writable column will need to have "W" in it. i left that blank because i was unsure which registers are writable, but i think eg4 just went with the everything in the holding register is writable approach.

@HotNoob
Copy link
Owner

HotNoob commented May 9, 2024

oh, i did clean up the eg4_v58 holding register a bit, i think the current version can pass the 90% check.
https://github.com/HotNoob/PythonProtocolGateway/blob/main/protocols/eg4_v58.holding_registry_map.csv

@utdrmac
Copy link
Author

utdrmac commented Jul 4, 2024

In reading the code when write is enabled, it looks like there is a subscription for each parameter? Is there some way to filter that, so that instead of 100+ subscriptions, there's only a handful? In my case, I just want to routinely update the time as the EG4 drifts by almost a full minute over the course of a week.

@HotNoob
Copy link
Owner

HotNoob commented Jul 4, 2024

yes, the variable_screen / mask txt files can just remove the variable all together.

alternatively, editing the protocol csv itself and changing the writable columns to WD or R for readonly.
more info on csv format: https://github.com/HotNoob/PythonProtocolGateway/wiki/Creating-and-Editing-Protocols-%E2%80%90-JSON-%E2%80%90-CSV

when editing the csv, it's best to create a custom protocol so it doesnt get overwritten when updating.
details on making a custom protocol: https://github.com/HotNoob/PythonProtocolGateway/wiki/Protocols#custom--editing-protocols


note to self.
i think in the future, an override csv feature would be a better solution.

@aviateur17
Copy link

@HotNoob, need some help with the writes.

  1. Seems I had to change line 127 in modbus_base.py
def validate_registry(self, registry_type : Registry_Type = Registry_Type.INPUT) -> float:

I think that should be Registry_Type.HOLDING and not Registry_Type.INPUT. The Holding registers are the writable ones.

  1. Then my "score" was 80% which was below the 90% threshold. What is that "score" based upon?
    so I had to lower the score_percent on line 88 of modbus_base.py. Once I can figure out what the score is based upon I can modify my holding register map to increase the score

  2. Then I was seeing an exception based on line 309 and 310 of modbus_base.py:

File "/home/jlehman/PythonProtocolGateway/classes/transports/modbus_base.py", line 121, in write_data
    self.write_variable(entry, value, Registry_Type.HOLDING)
  File "/home/jlehman/PythonProtocolGateway/classes/transports/modbus_base.py", line 339, in write_variable
    raise ValueError("Invalid value in register. unsafe to write") #i need to figure out a better error handler for theese. 
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Invalid value in register. unsafe to write
if not self.protocolSettings.validate_registry_entry(entry, current_value):
            raise ValueError("Invalid value in register. unsafe to write") #i need to figure out a better error handler for theese. 

Somehow the validate_registry_entry was returning 0 and not 1 and I have no idea why. So I went into that function and just put return 1 right after the definition and voila, I was able to write to the register!

So seems like I'm bypassing some of the safeties - the score and then the validate_registry_entry. If you have any tips on how I can debug this please let me know.

@aviateur17
Copy link

Okay, another question.
Let's say I want to write the time to the inverter. Just testing this out, I'm writing to time_seconds. So I can see the MQTT entry for time_seconds and then I can see the value written to the inverter upon next query/read. Then upon successive passes the MQTT entry is ignored unless the value is changed, which is fine and what I'd expect. However, the issue is when I stop the program and restart the program, upon starting, sees those old, stale, write MQTT entries and then writes them to the inverter upon first pass after startup. I'd rather have those values not written unless they update while the program is running. Is there a value I can write to the MQTT entry (maybe here's where the validation comes in) to prevent it from writing upon startup or perhaps we can ignore on startup until the value refreshes in the MQTT server? Hope all that makes sense.

@HotNoob
Copy link
Owner

HotNoob commented Sep 24, 2024

  1. i don't think that would actually have any effect on anything :S
    the default parameter is replaced with holding register

score_percent = self.validate_registry(Registry_Type.HOLDING)

although it does look like the code could be cleaned up a bit.

the validation process is just comparing the current holding register values with the .csv / map. checking to see that the values are within the documented ranges. ( values column )

https://github.com/HotNoob/PythonProtocolGateway/blob/main/protocols/eg4/eg4_v58.holding_registry_map.csv

part of the problem is that i don't have ranges for all of the values, so 80% is pretty good.

your probably safe to adjust this line of code:

for a long term solution, i can try to find some time to clean up values column a bit, and toss in some vauge interpretations where it's not documented.

if you could run once with this:
under the modbus portion.

analyze_protocol = true

you can stop it after you see "=== START INPUT REGISTER ==="

that should save a copy of your registers into theese files:

input_registry.json
holding_registry.json

then i or you could use them to figure out what the missing value ranges should be.

  1. this would be releated to 2. another "layer" or "safety check"
    to write, the minimum is that a valid value range is defined.

so... tl;dr, need to complete / update the values column in protocols/eg4/eg4_v58.holding_registry_map.csv


hmm, the script doesn't store anything after it's restarted. maybe the mqtt broker your using is resending something.
once the script get's the /write, it should write those changes to the inverter's registers... and then the inverter would decide what todo after that.

@aviateur17
Copy link

Okay, another question. Let's say I want to write the time to the inverter. Just testing this out, I'm writing to time_seconds. So I can see the MQTT entry for time_seconds and then I can see the value written to the inverter upon next query/read. Then upon successive passes the MQTT entry is ignored unless the value is changed, which is fine and what I'd expect. However, the issue is when I stop the program and restart the program, upon starting, sees those old, stale, write MQTT entries and then writes them to the inverter upon first pass after startup. I'd rather have those values not written unless they update while the program is running. Is there a value I can write to the MQTT entry (maybe here's where the validation comes in) to prevent it from writing upon startup or perhaps we can ignore on startup until the value refreshes in the MQTT server? Hope all that makes sense.

I had retain=True on my side when writing to the write/time_seconds topic. Changing retain=false "fixes" the issue for me. When restarting the program it no longer writes to that register upon first pass as the topic is now gone. Just in case others have this same issue.

if you could run once with this:
under the modbus portion.
analyze_protocol = true
you can stop it after you see "=== START INPUT REGISTER ==="

I will try this. Appreciate your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants