The data type is used for communication via Modbus. The Modbus programming requires specifying the starting location in the array and the number of elements for the read/write commands.
Currently I have a couple of sets of positions and lengths. I would like to predetermine where in the array the start element is, without typing the actual number. For example, to read from panel 1 I must specify P=5 and L=5. But if I insert an item in the first group, then I have to find everywhere in the code where P=5 and change it to P=6 and change P=10 to P=11. I would like to predetermine the location of Panel_1_Supply_Temp relative to the start of the array. In the above example it would 5. Changing to
The best solution would for it to be determined at compile time (preprocessor directive?). Obviously the array structure will not change once downloaded so there is no need to repetitively compute the location. It could be stored in a global variable and computed once in setup(); again since the location will not change once downloaded that would be a waste of valuable RAM.
I had to do something similar back in the eighties. I was using plain old C then and couldn't figure out how to do it. One of my colleagues suggested casting a null pointer to a pointer to the type of struct I was trying to get offsets into and making dirty use of the fact that NULL is 0.
I did and it worked. I would hope that C++ offers you a better alternative. If not, here is your example in all it's ugliness:
wildbill:
I had to do something similar back in the eighties. I was using plain old C then and couldn't figure out how to do it. One of my colleagues suggested casting a null pointer to a pointer to the type of struct I was trying to get offsets into and making dirty use of the fact that NULL is 0.
I did and it worked. I would hope that C++ offers you a better alternative. If not, here is your example in all it's ugliness:
void setup()
{
Serial.begin(115200); // start serial for output
void ptr=NULL;
long a=&(((modbus_registers_t)ptr)->x.Panel_1_Supply_Temp);
Serial.println(a);
}
void loop()
{
}
Shows 10 on my Uno meaning bytes into the struct.
How does that come up with 10? I understand the 10=10b ytes, but not sure I follow the dereferencing.
I was thinking something similar would be the runtime solution. Actually I was thinking it would take more math. Find the address of the start of the array, find the address of the element, subtract and then divide. Looks like the last step is true enough.
ptr is null and is of void* type. Then I cast it to be a pointer to modbus_registers_t*. Then I use that to get the address of the Panel_1_Supply_Temp inside the struct, which I called x because the compiler insisted that it have a name.
Because null is zero, the address of the (actually non existent) Panel_1_Supply_Temp element of the struct is its offset from the start of the struct (and union).
It's a filthy hack, for which you can blame my old friend Larry
It occurs to me that the void* pointer is an unnecessary complication. This does the same job:
void setup()
{
Serial.begin(115200); // start serial for output
modbus_registers_t*ptr=NULL;
long a=&(ptr->x.Panel_1_Supply_Temp);
Serial.println(a);
}
wildbill:
It occurs to me that the void* pointer is an unnecessary complication. This does the same job:
void setup()
{
Serial.begin(115200); // start serial for output
modbus_registers_t*ptr=NULL;
long a=&(ptr->x.Panel_1_Supply_Temp);
Serial.println(a);
}
I understand that a bit better. Not sure why you had to define the struct in the union as x. I tend to program in layers. So the full union-struct works with the Modbus programming I have. Now, as several times before, I have added elements and need to adjust the programming in several sketches when I do. Doing this programatically allows me to just recompile the other devices and download without actually changing any code.
You would need more (irrelevant) details about modbus telegrams. Just know they need to know where Panel_1_Supply_Temp is in the array, and that value is used as the position in the Modbus communication telegram. If I add an itms or multiple items before Panel_1_Supply_Temp, then the position in the array moves and I have 6+ sketches to manually update. gfvalvo found a instruction that does the exact item (only return in bytes instead of index, but I somewhat expected that and can just divide by the size of uint16_t to convert.
wildbill and gfvalvo. The solutions are great, but they fall into the category of "determined at runtime". Is there anything that can be done preprocessor such that we can save on RAM (ie., global variable) and/or clock cycles (computed each function call)?
adwsystems:
wildbill and gfvalvo. The solutions are great, but they fall into the category of "determined at runtime". Is there anything that can be done preprocessor such that we can save on RAM (ie., global variable) and/or clock cycles (computed each function call)?
What makes you say that? 'offsetof' is a pre-processor macro, same as sizeof.
I assume the struct inside the union needs a name (like wildbill's solution) in order to use the offset of macro. So I added one. Now none of the sketches compile.
The union is instantiated modbus_registers_t modbus_register and modbus_register.Control_Type (or any member of the struct) does not exist.
Pool_Heater_Panel_V4:673:37: error: 'union modbus_registers_t' has no member named 'Light_Level'
Serial.println(modbus_register.Light_Level.West);
^
exit status 1
'union modbus_registers_t' has no member named 'Control_Type'
Am I missing something or is this rewrite going to be ugly?
Pool_Heater_Panel_V4:673:37: error: 'union modbus_registers_t' has no member named 'Light_Level'
Serial.println(modbus_register.Light_Level.West);
^
exit status 1
'union modbus_registers_t' has no member named 'Control_Type'