We've encountered a customer's request for the device with LoRaWAN communication and low energy consumption, that would be able to read out measuring from Glötzl load cell and send data to the network server. The data are being read out from the strain gauge module using a powered current loop 4-20 mA. In addition, it also reads out the values from the NTC 10k thermistor, where one input is being used as a pull-up.
This application can be implemented using ACR-CV-101L-1A3U17V-D converter, which is specificaly used in this application. The converter is communicating over the LoRaWAN network and has a D battery with a capacity of 17 000 mAh. Nevertheless NB-IoT is also available.
The Gloetzl anchor load cell's code is SKN 250 A 50 AI 4/T with a measuring range of 0-250kN, a power supply of 15-30V DC, an output signal of 4-20mA, and a mean sensitivity of 0.064mA/kN. The built-in NTC thermistor has a range of -25 to +80°C with a temperature coefficient of <0.5%.
Red | Supply + 15…30V |
Brown | Supply - |
Blue | Supply - |
Black | Thermocouple |
Yellow | Thermocouple |
The red and brown cable goes into the power supply (2 pole clamp on the board). The blue cable is connected to “S1”, the black goes into “GND” next to “S1”. The yellow goes into “S2” and is also connected to the “S4”. The hollow can be used to fit 2 cables into one connector.
-- Copyright 2020 ACRIOS Systems s.r.o.
-----------------------
--- CONFIGURATION -----
-----------------------
----- LoRaWAN ----------
ack = 0 -- 1 for acknowledged, 0 for non-acknowledged
port = 100 -- transmit port
receiveTimeout = 10000 -- the maximum execution time in milliseconds
rejoinCount = 120 -- rejoin after sending X messages
----- Sensor -----------
loadCellChannel = 1
thermocoupleChannel = 2
thermocoupleSourceChannel = 4
-- custom HW: 4.7kOhm instead of 2.2kOhm for input 4, input 1: 120R load, 0R series, input 2,3: no load, 2.2kOhm series = standard
-- 22kOhm instead of 10k in step up lower resistance
------ Timing ---------
-- device wakeup interval
periodHours = 1
periodMinutes = 0
-----------------------
-----------------------
-- CONFIGURATION END --
-----------------------
function rawToCentiCelsius(rawIn)
offset = 125
cDegC = {7782,7106,6573,6136,5765,5446,5165,4915,4690,4486,4127,3820,3431,3207,2911,2654,2500,2427,2289,2160,2040,1927,1820,1623,1404,1032,725,465,239,40,-297,-576,-813,-1018,-1199,-1874}
raw = {321,393,463,530,595,658,718,776,832,887,990,1087,1222,1305,1421,1528,1595,1627,1689,1747,1803,1857,1907,2002,2110,2296,2450,2579,2690,2785,2942,3065,3165,3247,3315,3540}
rawIn = rawIn + offset
if rawIn < raw[1] then
return 8000 -- out of range -> very hot
end
for i = 2, #raw do
if rawIn < raw[i] then
lower = raw[i-1]
upper = raw[i]
spread = upper - lower
lowerWeight = (spread - (rawIn - lower))
upperWeight = (spread - (upper - rawIn))
weightedAvg = (lowerWeight*cDegC[i-1] + upperWeight*cDegC[i])/(spread)
return weightedAvg
end
end
return -2000 -- out of range -> very cold
end
function int32ToBufferLsb(int32)
local buff = ""
buff = buff .. string.char(int32%256) .. string.char((int32/256)%256) .. string.char(((int32/256)/256)%256) .. string.char((((int32/256)/256)/256)%256)
return buff
end
function int16ToBufferLsb(int16)
local buff = ""
buff = buff .. string.char(int16%256) .. string.char((int16/256)%256)
return buff
end
function onWake ()
toLoRaWAN = "" -- empty
print("Enable load cell and thermocouple excitation voltage")
api.voltageSourceState(1)
print("Wait more than 6s ~ initialization time of the load cell")
api.delayms(6500)
loadCellmV = api.AnalogReadPin(loadCellChannel)
api.DIOwritePin(loadCellChannel, -2)
api.voltageSourceState(0)
print("Load cell raw value: " .. tostring(loadCellmV) .. " mV")
api.DIOwritePin(thermocoupleSourceChannel, 1)
api.delayms(100)
thermocoupleRaw = api.AnalogReadPin(thermocoupleChannel, 1)
api.DIOwritePin(thermocoupleChannel, -2)
api.DIOwritePin(thermocoupleSourceChannel, -2)
print("Thermocouple raw value: " .. tostring(thermocoupleRaw) .. " counts")
toLoRaWAN = toLoRaWAN .. int16ToBufferLsb(loadCellmV)
toLoRaWAN = toLoRaWAN .. int16ToBufferLsb(thermocoupleRaw)
-- 0.064mA/kN --> F[N] = U[mV] * 130.208333, 0N at 432
fnewton = ((loadCellmV-432)*130208)/1000
if fnewton < 0 then -- if close to 0, we can get negative -> set to 0N
fnewton = 0
end
print("Force is: " .. tostring(fnewton) .. " N")
toLoRaWAN = toLoRaWAN .. int32ToBufferLsb(fnewton)
-- cDegC = 100*((3988/(math.log(r[Ohm]/3000)+13.38255))-273), 4700R vs. NTC
centiDegrees = rawToCentiCelsius(thermocoupleRaw)
print("Sensor temperature is: " .. tostring(centiDegrees) .. " cDegC")
toLoRaWAN = toLoRaWAN .. int32ToBufferLsb(centiDegrees)
msgCounter = api.getVar(1)
if msgCounter > rejoinCount then
api.setVar(1,0)
print("Periodic rejoin!")
api.loraJoin(1)
else
api.setVar(1, msgCounter+1)
end
print("To LoRaWAN: ")
api.dumpArray(toLoRaWAN)
print("Sending to LoRaWAN")
res, rxport, rcvd = api.loraSend(ack, receiveTimeout, toLoRaWAN, port)
print("Done sending")
print("Sleep now, wake in " .. tostring(periodHours) .. "hrs:" .. tostring(periodMinutes) .. "mins.....")
api.wakeUpIn(0,periodHours,periodMinutes,0)
end
function onStartup()
print("Starting up anchor load cell script with periodic rejoin...")
api.setVar(1,0) -- counter for rejoin
end
api.AnalogReadPin() - function reads voltage value on given channel. As you can see above, this function is used to get voltage value and calculate physical values like force and temperature (you can also find equations to calculate those above).
int32ToBufferLsb(int32) - is used to convert integer values to hexadecimal string formated as uint32 (little endian)
int16ToBufferLsb(int16) - is used to convert integer values to hexadecimal string formated as uint16 (little endian)
rawToCentiCelsius(rawIn) - is converting measured voltage value to centi degrees of Celsius
Here is the snippet of a lua script above describing how voltage value “loadCellmV” is used to calculate force in Newtons.
-- 0.064mA/kN --> F[N] = U[mV] * 130.208333, 0N at 432
fnewton = ((loadCellmV-432)*130208)/1000
Compared to snippet above, converting voltage value of thermocouple “thermocoupleRaw”is a bit more complicated.
The only diffrence here is use of funcion rawToCentiCelsius(rawIn). In the script above you can see the function is using arrays cDegC{} and raw{} as a template to get ˚C. The values of cDegC{} are calculated based on function in the snippet bellow. The reason behind this is to reduce the amount of used memory (lua math.log function is not enabled by default).
-- cDegC = 100*((3988/(math.log(r[Ohm]/3000)+13.38255))-273), 4700R vs. NTC
centiDegrees = rawToCentiCelsius(thermocoupleRaw)
For more details about used lua functions, have a look here.
Below is the output of serial line (precisely onwake() function, which is called every time, device wakes up).
<break>
SYS: exiting sleep mode
SYS: button pressed
SYS: --- New request ---
SYS: Battery Voltage: 3670 mV
LORA: setting battery state - 254/254
LUA: Starting onWake() script
~>Enable load cell and thermocouple excitation voltage
~>Wait more than 6s ~ initialization time of the load cell
~>Load cell raw value: 731 mV
~>Thermocouple raw value: 1929 counts
~>Force is: 38932 N
~>Sensor temperature is: 1774 cDegC
~>To LoRaWAN:
⇥ 00 : DB 02 89 07 14 98 00 00 EE 06 00 00
~>Sending to LoRaWAN
LORA: setting port to 100.
LORA: 12 bytes of data will be sent
[LORA]: send data:
⇥ DB 02 89 07 14 98 00 00 EE 06 00 00
[LORA]: McpsConfirm = MCPS_UNCONFIRMED: LORAMAC_EVENT_INFO_STATUS_OK
[LORA]: RX Timeout FAILED!
[LORA]: Receive timeout!
[App] LoRa ACK not received!
~>Done sending
~>Sleep now, wake in 0hrs:3mins.....
SYS: date / time - 2016/01/01 / 00:20:02
SYS: wake up at day / time - 01 / 00:23:02
SYS: entering sleep mode
<break>
As you can see, the payload has 12B. To understand how it's composed have a look at this table:
Description | Load cell raw value [mV] |
Thermocouple raw value [Counts] |
Force [N] |
Temperature [cDegC] |
||||||||
Payload | DB | 02 | 89 | 07 | 14 | 98 | 00 | 00 | EE | 06 | 00 | 00 |
Data | 731 | 1929 | 38932 | 1774 |
From table abowe you can see, data are ordered as little endian.