Modbus RTU Low and High Register Information
-
I'm trying to get some info from a modbus RTU device and some of the data is sent at the same register but one is a low register the other value at the high register.
71 H Occupied Cooling Setpoint
71 L Occupied Heating Setpoint
72 H Un-Occupied Cooling Setpoint
72 L Un-Occupied Heating SetpointWhat datatype do I use to read and or command the individual values? Above is an example of 4 points that I need to view and command. The first two are both at register 71 but Occupied Cooling Setpoint is under the H or High and Occupied Heating Setpoint is under the L or Low register. Im looking to map each point value and be able to command as needed.
-
Hi Jason,
Can you post the manual for that device or a link? That sounds wonky!
I am guessing you meant the low byte and the high byte of the register?
There is no support for that directly in Modbus4J, but you could make it work in Mango by using a 16bit data type (probably 2 byte signed or unsigned integer and some point links (bit masks like, 71&0xFF && (71>>8)&0xFF). We can go into a solution there, but I'm really curious, from how you were using the word register in multiple senses, if there isn't more in the manual to shed light on why a company would elect to use Modbus but not elect to respect the specification of 16bits being the word size.
-
Here is the manual for the device. ![alt text](image url)
-
Madness!
Okay, well, here'd be my first guess:
- Create a Modbus data source, Create a data point for register 71 reading a 2 byte unsigned integer, you configuration may look like this:
{ "dataSources":[ { "xid":"DS_cba83af5-38f8-4830-a07a-daeda155bc5b", "name":"wonky-byte-packing", "enabled":false, "type":"MODBUS_IP", "alarmLevels":{ "POINT_WRITE_EXCEPTION":"URGENT", "POLL_ABORTED":"URGENT", "DATA_SOURCE_EXCEPTION":"URGENT", "POINT_READ_EXCEPTION":"URGENT" }, "purgeType":"YEARS", "updatePeriodType":"SECONDS", "transportType":"TCP", "encapsulated":false, "host":"localhost", "port":502, "contiguousBatches":false, "createSlaveMonitorPoints":false, "discardDataDelay":0, "ioLogFileSizeMBytes":1.0, "logIO":false, "maxHistoricalIOLogs":1, "maxReadBitCount":2000, "maxReadRegisterCount":125, "maxWriteRegisterCount":120, "multipleWritesOnly":false, "quantize":false, "retries":2, "timeout":500, "updatePeriods":20, "editPermission":"", "purgeOverride":false, "purgePeriod":1 } ], "dataPoints":[ { "xid":"DP_00c2c077-a245-4768-aed8-774bb0f7f982", "name":"OccupiedTemps", "enabled":false, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"AVERAGE", "purgeType":"YEARS", "pointLocator":{ "range":"HOLDING_REGISTER", "modbusDataType":"TWO_BYTE_INT_UNSIGNED", "writeType":"SETTABLE", "additive":0.0, "bit":0, "charset":"ASCII", "multiplier":1.0, "offset":71, "registerCount":0, "slaveId":1, "slaveMonitor":false }, "eventDetectors":[ ], "plotType":"SPLINE", "rollup":"NONE", "unit":"", "chartColour":"", "chartRenderer":{ "type":"IMAGE", "timePeriodType":"DAYS", "numberOfPeriods":1 }, "dataSourceXid":"DS_cba83af5-38f8-4830-a07a-daeda155bc5b", "defaultCacheSize":1, "deviceName":"wonky-byte-packing", "discardExtremeValues":false, "discardHighLimit":1.7976931348623157E308, "discardLowLimit":-1.7976931348623157E308, "intervalLoggingPeriod":1, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "preventSetExtremeValues":false, "purgeOverride":false, "purgePeriod":1, "readPermission":"", "setExtremeHighLimit":1.7976931348623157E308, "setExtremeLowLimit":-1.7976931348623157E308, "setPermission":"", "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"", "renderedUnit":"", "format":"0.00" }, "tolerance":0.0 } ] }
- Next we'll need a scripting data source and four points, "Set Occupied Heating Setpoint", "Set Occupied Cooling Setpoint", "Occupied Heating Setpoint" and "Occupied Cooling Setpoint". I'll just use their acronyms as variable names. They're all settable numeric. The script body may look like (the modbus point from step 1 will be in the context, let's say variable name "occupiedSetpoints",
if( sohs.value != -1 ) { //Someone is trying to set the heating setpoint if( socs.value != -1) { occupiedSetpoints.set( socs.value<<8 | sohs.value ); //we may need to set both sohs.set( -1 ); //Clear it so we don't keep setting too much socs.set( -1 ); } else { occupiedSetpoints.set( ocs.value<<8 | sohs.value ); sohs.set( -1 ); } } else if( socs.value != -1 ) { occupiedSetpoints.set( socs.value<<8 | ohs.value ); socs.set( -1 ); }
That whole data source's configuration might look like this:
{ "dataSources":[ { "xid":"DS_760b73e5-5444-4a8e-9258-48648b093b04", "name":"HandleWonkiness", "enabled":false, "type":"SCRIPTING", "alarmLevels":{ "SCRIPT_ERROR":"URGENT", "DATA_TYPE_ERROR":"URGENT", "POLL_ABORTED":"URGENT", "LOG_ERROR":"URGENT" }, "purgeType":"YEARS", "context":[ { "varName":"occupiedSetpoints", "dataPointXid":"DP_00c2c077-a245-4768-aed8-774bb0f7f982" } ], "logLevel":"NONE", "cronPattern":"0\/1 * * * * ?", "executionDelaySeconds":0, "historicalSetting":false, "script":"if( sohs.value != -1 ) { \/\/Someone is trying to set the heating setpoint\r\n if( socs.value != -1) {\r\n occupiedSetpoints.set( socs.value<<8 | sohs.value ); \/\/we may need to set both\r\n sohs.set( -1 ); \/\/Clear it so we don't keep setting too much\r\n socs.set( -1 );\r\n } else {\r\n occupiedSetpoints.set( ocs.value<<8 | sohs.value );\r\n sohs.set( -1 );\r\n }\r\n} else if( socs.value != -1 ) {\r\n occupiedSetpoints.set( socs.value<<8 | ohs.value );\r\n socs.set( -1 );\r\n}", "scriptPermissions":{ "customPermissions":"", "dataPointReadPermissions":"superadmin", "dataPointSetPermissions":"superadmin", "dataSourcePermissions":"superadmin" }, "editPermission":"", "purgeOverride":false, "purgePeriod":1 } ], "dataPoints":[ { "xid":"DP_1dd39f06-c252-4bf2-bd75-27108ad1812c", "name":"Set Occupied Heating Setpoint", "enabled":true, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"AVERAGE", "purgeType":"YEARS", "pointLocator":{ "dataType":"NUMERIC", "settable":true, "varName":"sohs" }, "eventDetectors":[ ], "plotType":"SPLINE", "rollup":"NONE", "unit":"", "templateXid":"Numeric_Default", "chartColour":"", "chartRenderer":{ "type":"IMAGE", "timePeriodType":"DAYS", "numberOfPeriods":1 }, "dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04", "defaultCacheSize":1, "deviceName":"HandleWonkiness", "discardExtremeValues":false, "discardHighLimit":1.7976931348623157E308, "discardLowLimit":-1.7976931348623157E308, "intervalLoggingPeriod":1, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "preventSetExtremeValues":false, "purgeOverride":false, "purgePeriod":1, "readPermission":"", "setExtremeHighLimit":1.7976931348623157E308, "setExtremeLowLimit":-1.7976931348623157E308, "setPermission":"", "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"", "renderedUnit":"", "format":"0.00" }, "tolerance":0.0 }, { "xid":"DP_b57761e9-ce04-41d2-9ea8-8018eab7ec4e", "name":"Set Occupied Cooling Setpoint", "enabled":true, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"AVERAGE", "purgeType":"YEARS", "pointLocator":{ "dataType":"NUMERIC", "settable":true, "varName":"socs" }, "eventDetectors":[ ], "plotType":"SPLINE", "rollup":"NONE", "unit":"", "templateXid":"Numeric_Default", "chartColour":"", "chartRenderer":{ "type":"IMAGE", "timePeriodType":"DAYS", "numberOfPeriods":1 }, "dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04", "defaultCacheSize":1, "deviceName":"HandleWonkiness", "discardExtremeValues":false, "discardHighLimit":1.7976931348623157E308, "discardLowLimit":-1.7976931348623157E308, "intervalLoggingPeriod":1, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "preventSetExtremeValues":false, "purgeOverride":false, "purgePeriod":1, "readPermission":"", "setExtremeHighLimit":1.7976931348623157E308, "setExtremeLowLimit":-1.7976931348623157E308, "setPermission":"", "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"", "renderedUnit":"", "format":"0.00" }, "tolerance":0.0 }, { "xid":"DP_654eebab-d687-4083-8f09-80a160d9e375", "name":"Occupied Heating Setpoint", "enabled":true, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"AVERAGE", "purgeType":"YEARS", "pointLocator":{ "dataType":"NUMERIC", "settable":true, "varName":"ohs" }, "eventDetectors":[ ], "plotType":"SPLINE", "rollup":"NONE", "unit":"", "templateXid":"Numeric_Default", "chartColour":"", "chartRenderer":{ "type":"IMAGE", "timePeriodType":"DAYS", "numberOfPeriods":1 }, "dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04", "defaultCacheSize":1, "deviceName":"HandleWonkiness", "discardExtremeValues":false, "discardHighLimit":1.7976931348623157E308, "discardLowLimit":-1.7976931348623157E308, "intervalLoggingPeriod":1, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "preventSetExtremeValues":false, "purgeOverride":false, "purgePeriod":1, "readPermission":"", "setExtremeHighLimit":1.7976931348623157E308, "setExtremeLowLimit":-1.7976931348623157E308, "setPermission":"", "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"", "renderedUnit":"", "format":"0.00" }, "tolerance":0.0 }, { "xid":"DP_879c5c66-784c-4d5e-960e-96cda59bb260", "name":"Occupied Cooling Setpoint", "enabled":true, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"AVERAGE", "purgeType":"YEARS", "pointLocator":{ "dataType":"NUMERIC", "settable":true, "varName":"ocs" }, "eventDetectors":[ ], "plotType":"SPLINE", "rollup":"NONE", "unit":"", "templateXid":"Numeric_Default", "chartColour":"", "chartRenderer":{ "type":"IMAGE", "timePeriodType":"DAYS", "numberOfPeriods":1 }, "dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04", "defaultCacheSize":1, "deviceName":"HandleWonkiness", "discardExtremeValues":false, "discardHighLimit":1.7976931348623157E308, "discardLowLimit":-1.7976931348623157E308, "intervalLoggingPeriod":1, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "preventSetExtremeValues":false, "purgeOverride":false, "purgePeriod":1, "readPermission":"", "setExtremeHighLimit":1.7976931348623157E308, "setExtremeLowLimit":-1.7976931348623157E308, "setPermission":"", "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"", "renderedUnit":"", "format":"0.00" }, "tolerance":0.0 } ] }
Note that you could use a different value than -1, but most people don't set their A/C values anywhere near freezing so you're probably okay there.
- And finally we'll need to have some point links from the modbus point to our two points for each of the current setpoints. That means in the point link source point for both links and "Occpied Heating Setpoint" is the target of one (The other is Occupied Cooling Setpoint).
One link will have the body,
return source.value & 0xFF;
the other will be
return source.value>>8 & 0xFF;
Those may have a configuration like:
{ "pointLinks":[ { "xid":"PL_eacbf9a9-d919-49f4-88d1-ed59cb19c243", "sourcePointId":"DP_00c2c077-a245-4768-aed8-774bb0f7f982", "targetPointId":"DP_879c5c66-784c-4d5e-960e-96cda59bb260", "event":"CHANGE", "logLevel":"NONE", "disabled":false, "script":"return source.value>>8 & 0xFF;", "scriptPermissions":{ "customPermissions":"", "dataPointReadPermissions":"superadmin", "dataPointSetPermissions":"superadmin", "dataSourcePermissions":"superadmin" }, "writeAnnotation":false }, { "xid":"PL_94a9ac52-4770-4b90-b3a3-a294d60b2d03", "sourcePointId":"DP_00c2c077-a245-4768-aed8-774bb0f7f982", "targetPointId":"DP_654eebab-d687-4083-8f09-80a160d9e375", "event":"CHANGE", "logLevel":"NONE", "disabled":false, "script":"return source.value & 0xFF;", "scriptPermissions":{ "customPermissions":"", "dataPointReadPermissions":"superadmin", "dataPointSetPermissions":"superadmin", "dataSourcePermissions":"superadmin" }, "writeAnnotation":false } ] }