read failed: Illegal data address
#1
Hello,

I'm trying to make a JSON mapper for the Schneider APC UPS ove Modbus TCP but I'm having some problems.
No values get present, and the error is: read failed: Illegal data address

This is what I have mapped up so far:
Code:
{
 "manufacturer": "Schneider",
 "description": "APC UPS",
 "mapping": [
 { "name": "On Line", "bus_datatype": "bool", "type": "register", "address": 3, "writable": 0, "value_bitmask": 0x8, "bus_address": "6/0/0" },
 { "name": "On battery", "bus_datatype": "bool", "type": "register", "address": 3, "writable": 0, "value_bitmask": 0x10, "bus_address": "6/0/1" },
{ "name": "Overload", "bus_datatype": "bool", "type": "register", "address": 3, "writable": 0, "value_bitmask": 0x20, "bus_address": "6/0/2" },
{ "name": "Low battery", "bus_datatype": "bool", "type": "register", "address": 3, "writable": 0, "value_bitmask": 0x40, "bus_address": "6/0/3" },
{ "name": "Replace battery", "bus_datatype": "bool", "type": "register", "address": 3, "writable": 0, "value_bitmask": 0x80, "bus_address": "6/0/4" },
{ "name": "% battery state of charge", "bus_datatype": "float16", "type": "register", "address": 5, "writable": 0, "bus_address": "6/0/5" },
{ "name": "Runtime remaining", "bus_datatype": "float16", "type": "register", "address": 6, "writable": 0, "bus_address": "6/0/6" },
{ "name": "Battery voltage", "bus_datatype": "float16", "type": "register", "address": 7, "writable": 0, "bus_address": "6/0/7" },
{ "name": "UPS internal temperature", "bus_datatype": "float16", "type": "register", "address": 8, "writable": 0, "bus_address": "6/0/8" },
{ "name": "Amps drawn by load", "bus_datatype": "float16", "type": "register", "address": 9, "writable": 0, "bus_address": "6/0/9" },
{ "name": "Quantity of bad batt packs", "bus_datatype": "float16", "type": "register", "address": 10, "writable": 0, "bus_address": "6/0/10" },
{ "name": "Quantity of batt packs", "bus_datatype": "float16", "type": "register", "address": 11, "writable": 0, "bus_address": "6/0/11" },
{ "name": "% power drawn by load", "bus_datatype": "float16", "type": "register", "address": 12, "writable": 0, "bus_address": "6/0/12" },
{ "name": "Nominal power output", "bus_datatype": "float16", "type": "register", "address": 13, "writable": 0, "bus_address": "6/0/13" },
{ "name": "Actual output voltage", "bus_datatype": "float16", "type": "register", "address": 14, "writable": 0, "bus_address": "6/0/14" },
{ "name": "Maximum input", "bus_datatype": "float16", "type": "register", "address": 15, "writable": 0, "bus_address": "6/0/15" },
{ "name": "Minimum input", "bus_datatype": "float16", "type": "register", "address": 16, "writable": 0, "bus_address": "6/0/16" },
{ "name": "Input voltage", "bus_datatype": "float16", "type": "register", "address": 17, "writable": 0, "bus_address": "6/0/17" },
{ "name": "Input frequency", "bus_datatype": "float16", "type": "register", "address": 18, "writable": 0, "bus_address": "6/0/18" },
{ "name": "Low battery duration", "bus_datatype": "float16", "type": "register", "address": 31, "writable": 0, "bus_address": "6/0/19" }


 ]
}

This is the documentation regarding Modbus protocol for the UPS:
http://download.schneider-electric.com/f..._R1_EN.pdf

Is it something with hexadecimal/decimal adresses? or with my bitmasking?
The documentations states UINT16 on datatype, must I add this to the mapper?
Reply
#2
Does RTU read test work for these register addresses? Also there's no point in using float16, you will get precision loss when converting from uint16 to float16 for larger values.
Reply
#3
Actually I found my mistake, I was looking at an old documentation when mappin. Things have changed alot with the one linked in OP. Tried the new registers in OP documentation and it seem to work.

So I should just delete bus_datatype column?

How about dividing a value with 256 or 64? is there a mapper object for that?
Reply
#4
Set multiplier to 1/value, 0.015625 for 64 and 0.00390625 for 256
Reply
#5
Thanks!

I have problems reading and writing to bool-registers that in the documentation have stated "Length # register" = 2.
Does this have anything to do with why I'm not able to read out from theese?

Read:
{ "name": "Online state", "bus_datatype": "bool","type": "register", "address": 0, "bus_address": "6/0/0", "value_bitmask": "0x0002" },
{ "name": "On battery state", "bus_datatype": "bool","type": "register", "address": 0, "bus_address": "6/0/1", "value_bitmask": "0x0004" },
{ "name": "Bypass state", "bus_datatype": "bool","type": "register", "address": 0, "bus_address": "6/0/2", "value_bitmask": "0x0008" },

Write:
{ "name": "Output into bypass", "bus_datatype": "bool","type": "register", "address": 1536, "bus_address": "6/1/0", "value_bitmask": "0x0010" },
{ "name": "Output out of bypass", "bus_datatype": "bool","type": "register", "address": 1536, "bus_address": "6/1/1", "value_bitmask": "0x0020" },
Reply
#6
To read two registers at once set "datatype" to "uint32". At this moment writing to registers with bitmasks is not supported, you have to calculate final register value via a script.
Reply
#7
Thanks! uint32 worked for reading.

Now to the bigger problem, scripting. I searched the forums and found a similar thread regarding writing a bit, but I'm not sure I understand it? I have edited it to suit me.
Must I first set mode, before I send either of the bool KNX telegrams? This register contains 32 bits, no need  store all the other bits?

But I'm not convinced I have done it right, valuue+512=512+512 and so on?

How will the Bool KNX objects be resetted to 0? Otherwise they will be 1 next time the script runs and perhaps set the UPS in ByPass by error?

Code:
-- 0=set bit 0, 1
-- 1=set bit 1, 2
-- 2=set bit 2, 4
-- 3=set bit 3, 8
-- 4=set bit 4, 16
-- 5=set bit 5, 32
-- 6=set bit 6, 64
-- 7=set bit 7, 128
-- 8=set bit 8, 256
-- 9=set bit 9, 512

mode = grp.getvalue('1/1/1') -- 1-byte unsigned integer
OutputBypass = grp.getvalue('6/1/0') -- boolean
OutputOnline = grp.getvalue('6/1/1') -- boolean
ClearFaults = grp.getvalue('6/1/2') -- boolean

if mode == 4 then
  value = 16
elseif mode == 5 then
  value = 32
elseif mode == 9 then
  value = 512
end

if OutputBypass then
  value = value + 16 -- Bit 4
end

if OutputOnline then
  value = value + 32 -- Bit 5
end

if ClearFaults then
  value = value + 512 -- Bit 9
end

log(value)
Reply
#8
It's much better to use bit functions for setting bits:
Code:
function setbit(value, nr)
  return bit.bor(value, bit.lshift(1, nr))
end

value = 0

value = setbit(value, 0)
value = setbit(value, 1)
value = setbit(value, 9)

log(value) -- result: 515

The only thing I don't understand is why you have 1-byte value and bool values setting the same bits. How many writable bits are there?
Reply
#9
1-byte value comes from the example that you gave in another thread, that I tried to understand. I have no clue to be honest. Smile
In this register there are 32 writable bits, some of them are reserved though.
https://forum.logicmachine.net/showthrea...24#pid6824

Bit 4 (bypass) and bit 5 (out of bypass) are exclusive as they both control the state of the UPS. I can only write true to one of them because it toggles between thoose states, either bypass or online.

Am I in the ball park with this script? Setting bit 4 in 1536 to true.

Code:
require('luamodbus')
mb = luamodbus.tcp()
mb:open('192.168.1.228', 502)
mb:connect()
mb:setslave(11)

function setbit(value, nr)
 return bit.bor(value, bit.lshift(1, nr))
end

value = 1

value = setbit(value, 4)

mb:writeregisters(1536, value)

mb:close()


log(value) -- result: 17
Reply
#10
Correct, but only if you need to have bit 0 set permanently. Otherwise, set value = 0 before any setbit calls
Reply
#11
I tried it, but it doesn't seem, to work.. I have log(value) in the end, and the value shows up in the log.
I tried with both value=0 and 1, no difference.

I get nothing in the error log either.

Where in the code do I actually settrue/false to the register bit? I guess the UPS would want a "true" in BOOL for the bit to react.
Also, do I need to setslave in TCP script as I have done?
Reply
#12
Start with logging what writeregisters returns. If you need to write two registers at once, do it like this:

Code:
res, err = mb:writeregistervalue(1536, value, 'uint32')
log(res, err)

Refer to product manual for slave id. Some TCP devices ignore slave id completely.
Reply
#13
It writes:

Code:
* arg: 1
 * nil
* arg: 2
 * string: Operation timed out
Reply
#14
Check return values of connect call. Also try it without setting slave id.
Reply


Forum Jump: