I am writing a device in OpenEMS for a battery system that we are using in our project.
We are reading the status of the battery via Modbus TCP and I am currently implementing the Modbus protocol.
Some of the values are encoded by bits inside a Modbus register. For values where a single bit corresponds to a channel, this is no problem and is easily handled by the BitsWordElement.bit() method which maps directly to the channel.
Some of the status information is encoded across multiple bits, but should really map to a single channel within OpenEMS.
In one specific case, the status is encoded across the bits 0, 1 and 2 (representing numbers 00-07), with the following meaning:
00: Sleep
01: Charge
02: Discharge
03: Idle
etc
The bit >4 are used to encode other status, e.g single bits for status and alarms etc so can be ignored for this question.
I am unsure what is the best way to read these values in OpenEMS. I suppose one approach would be to use bitshifting and bit masking to extract a number from the first 3 bits. Is this the right approach? How would this be implemented within OpenEMS?
First of all, I would create an Enum Status implementing OptionsEnum for decoding the status from the integer like that:
public enum Status implements OptionsEnum {
UNDEFINED(-1, "Undefined"), //
SLEEP(0, "Sleep"), //
...
Then you need a corresponding channel in your component:
STATUS(Doc.of(Status.values()))
Now if you donât need the bits > 4, one could define an ElementToChannelConverter like
value -> value & 0b11100000
(or maybe something else)
If you do need the higher bits, maybe itâs best to define a dummy channel where you put the whole byte (or word) and use an onUpdate-callback on that channel, which âcuts the channel value into piecesâ and maps everything correspondingly.
Now as I wrote this, I think this may be exactly your approach written down in a more detailed way⌠I donât know of any more elegant ways, maybe someone else does?
In the method make sure to always set every Channel, even if the value is null. This makes sure that, e.g. if somebody pulls the cable, the values are reset to null or false, whatever is more suitable in the situation. (I would never set states to ânullâ, but only to false)
public class BatteryFeneconCommercialImplTest {
@Test
public void testHandle2729() {
var sut = new BatteryFeneconCommercialImpl();
var faultStatus = sut.channel(BatteryFeneconCommercial.ChannelId.FAULT_STATUS);
...
sut.handle2729(null);
assertEquals(false, faultStatus.getNextValue().get());
...
sut.handle2729(0xFFFF);
assertEquals(true, faultStatus.getNextValue().get());
...
}
}
@tsicking: the difference between this approach and an ElementToChannelConverter is, that I do not need a separate Channel that maps on the entire Element.
Thank you both very much for your responses. I will try implementing this later on and will report back.
I think the approach presented is probably the most elegant way of doing it. One other idea I had would be to use two separate FC3ReadRegistersTask calls. They both access the same register, but one of them looks at the first 3 bits only using a similar logic to that presented above, and the other then uses BitsWordElement to access the subsequent bits directly.
I suppose this approach would be less efficient as it would require 2 modbus read operations. Is this a sufficient reason not to do it? Or is there another drawback I have not considered?
That would indeed work as well (didnât even think of it). But yes, the major drawback is, that you have multiple read operations. I cannot think of any other drawback.
Why is it important to set the values of the channels to null? I understand that we donât want stale values in the event that we lose connectivity or another error occurs. My question is really, why do we not need this logic for every channel which involves reading from modbus?
I.e for every other channel I donât write any logic to handle the case that the response is null - I am using a simple ElementToChannelConverter or reading bits to get the value.
Is this something that is handled automatically in OpenEMS? Or is there some reason why it is not necessary to set the null values?
I am going to reopen this topic because I am working on a similar issue right now.
I am implementing an OpenEMS device for an ESS which uses one bit of a register for the Grid Connection status - i.e for the bit 0=âGrid Connectedâ, 1=âOff-gridâ.
I want to map this to the SymmetricEss.GRID_MODE.
I have been using the BitsWordElement.bit() method to access the individual bits in my other registers.
bit() only takes a BitConverter as an argument and this only has the options to map 1-to-1 or to invert the bit. bit() doesnât allow for an ElementToChannelConverter to be passed so I am not sure that I can do it using the bit method.
The other possibility is to define an ElementToChannelConverter which operates on the whole register and does the conversion. This is the approach mentioned in the previous message. This will probably work but it is a little more involved that using the direct BitsWordElement method.
ok, I donât think there is a straight forward way right now to solve this. You can of course always map the bit to an intermediate Channel and add a onSetNextValue-Listener that actually sets the GRID_MODE. Itâs not the cleanest approach, but solves the problemâŚ