Default scripts NB-IoT
- Overview
- S0
- Modbus
- M-Bus
- W M-Bus
Here you can find default NB-IoT LUA scripts for IoT Converter
-- Copyright 2021 ACRIOS Systems s.r.o.
-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function wordToBuffer(word)
    -- little endian
    local buff = ""
    buff = buff ..  string.char(word%256) ..  string.char((word/256)%256) ..  string.char(((word/256)/256)%256) ..  string.char((((word/256)/256)/256)%256) 
    return buff
end
-- get and format S0 inputs
function getS0Data()
    s01 = api.S0readCounter(1) 
    print("S0-1: "..tostring(s01))
    s02 = api.S0readCounter(2) 
    print("S0-2: "..tostring(s02))
    s03 = api.S0readCounter(3)
    print("S0-3: "..tostring(s03))
    s04 = api.S0readCounter(4) 
    print("S0-4: "..tostring(s04))
    -- read old values
    s01_l = api.getVar(16)
    s02_l = api.getVar(17)
    s03_l = api.getVar(18)
    s04_l = api.getVar(19)
    s01_ll = api.getVar(20)
    s02_ll = api.getVar(21)
    s03_ll = api.getVar(22)
    s04_ll = api.getVar(23)
    -- update old values
    api.setVar(16, s01)
    api.setVar(17, s02)
    api.setVar(18, s03)
    api.setVar(19, s04)
    api.setVar(20, s01_l)
    api.setVar(21, s02_l)
    api.setVar(22, s03_l)
    api.setVar(23, s04_l)
    -- get battery voltage
    v = api.getBatteryVoltage()
    -- assemble the frame
    buf = string.char(5) -- device class
    buf = buf ..  string.char(v%256) ..  string.char((v/256)%256)
    buf = buf .. wordToBuffer(s01) .. wordToBuffer(s01_l) .. wordToBuffer(s01_ll)
    buf = buf .. wordToBuffer(s02) .. wordToBuffer(s02_l) .. wordToBuffer(s02_ll)
    buf = buf .. wordToBuffer(s03) .. wordToBuffer(s03_l) .. wordToBuffer(s03_ll)
    buf = buf .. wordToBuffer(s04) .. wordToBuffer(s04_l) .. wordToBuffer(s04_ll)
    -- print the frame
    print("Frame in hex: <devClass, voltage, S0_1, S0_1_last, ...>")
    api.dumpArray(buf)
    return buf
end
-- CONFIGURATION --
protocol = "UDP"
APN = "nb.m2mc"
PLMNID = "23003"
band = "20"
ip = "192.168.0.20"
port = 4242
receiveTimeout = 50
periodMinutes = 60
--
function onWake () 
    
    print("onWake(), periodic wake up")
    buf = getS0Data()
    print("Sending to NB-IoT: " .. ip .. ":" .. tostring(port) .. " (" .. protocol .. ")")
    res = api.nbSend(ip, port, buf, receiveTimeout, protocol)
    if res == 0 then
        print("Done, sent to NB-IoT")
    else
        print("Error " .. tostring(res) .. ", failed to send to NB-IoT!")
    end
    api.wakeUpIn(0,0,periodMinutes,0)
end
function onThreshold ()
    print("onThreshold(), reason S0: " .. tostring(src))
    buf = getS0Data()
    print("Sending to NB-IoT: " .. ip .. ":" .. tostring(port) .. " (" .. protocol .. ")")
    res = api.nbSend(ip, port, buf, receiveTimeout, protocol)
    if res == 0 then
        print("Done, sent to NB-IoT")
    else
        print("Error " .. tostring(res) .. ", failed to send to NB-IoT!")
    end
end 
function onStartup()
    print("onStartup(), S0 persistent emulation...")
    --set to threshold
    api.S0setThreshold(1, 0)
    api.S0setThreshold(2, 0)
    api.S0setThreshold(3, 0)
    api.S0setThreshold(4, 0)
    s01 = api.getVar(20)
    s02 = api.getVar(21)
    s03 = api.getVar(22)
    s04 = api.getVar(23)
    api.S0initializeCounter(1, s01)
    api.S0initializeCounter(2, s02)
    api.S0initializeCounter(3, s03)
    api.S0initializeCounter(4, s04)
  api.nbAT("AT*MCGDEFCONT=\"IP\",\"" .. APN .."\"", 5000, 1)
  api.nbAT("AT+COPS=1,2,\"".. PLMNID .."\"", 5000, 1)
  api.nbAT("AT+CBAND=" .. band, 5000, 1)
end
-- Copyright 2021 ACRIOS Systems s.r.o.
-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-----------------------
--- CONFIGURATION -----
-----------------------
----- NB-IoT ----------
APN = "nb.m2mc"
PLMNID = "23003"
protocol = "UDP"	-- UDP or TCP
ip = "192.168.0.20"
port = 4242
receiveTimeout = 500 -- the maximum execution time in milliseconds
----- Modbus -----------
baudrate = 9600		-- baudrate: up to 921600 baud
parity = 0	        -- communication parity: 0 for none, 1 for odd and 2 for even parity
stopBits = 1		-- number of stop bits: 1 or 2
dataBits = 8		-- number of data bits: 7 or 8
rxTimeout = 500     -- slave device receive timeout in ms
------ Timing ---------
-- device wakeup interval
periodHours = 2
periodMinutes = 30
-----------------------
-----------------------
-- CONFIGURATION END --
-----------------------
function onWake ()
    -- set link parameters - e.g. 9600 baud, 8N1
    api.rs485Setup(baudrate,parity,stopBits,dataBits)
    api.rs485State(1)
    -- EXAMPLE Modbus request:
    -- check our applicaton notes or https://simplymodbus.ca/FC03.htm
    -- req = ""
    -- SLAVE ADDRESS (0xAA)| FUNCTION CODE (0x04)|
    -- MSB REG ADDR (0x00) | LSB REG ADDR (0x08) | 
    -- MSB REG COUNT (0x00) | LSB REG COUNT (0x01)
    -- req = pack.pack('<b6', 0xAA, 0x04, 0x00, 0x08, 0x00, 0x01)
    -- crc =  api.modbusCrc(req)
    -- req = req .. crc -- add CRC, request is complete and ready for sending
    if req == nil then
        print("Please provide configuration!")
        ans = "NO CONFIGURATION PROVIDED!"
    else
        print("To RS485: ")
        api.dumpArray(req)
        api.rs485Send(req)
        ans,length=api.rs485Receive(rxTimeout) 
        print("From RS485: ")
        api.dumpArray(ans)
        api.delayms(500)
        api.rs485State(0)
    end
    
    if #ans < 1 then
        buf = "NO DATA RECEIVED"
    else
        buf = ans
    end
    print("To NBIOT: ")
    api.dumpArray(buf)
    print("Sending to NB-IoT")
    api.nbSend(ip, port, buf, receiveTimeout, protocol)
    
    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 NB-IoT default Modbus script with APN settings")
    result = api.nbAT("AT*MCGDEFCONT=\"IP\",\"" .. APN .."\"", 5000, 1)
    print("APN result: " .. result)
    result = api.nbAT("AT+COPS=1,2,\"" .. PLMNID .."\"", 5000, 1)
    print("PLMNID result: " .. result)
end
-- Copyright 2021 ACRIOS Systems s.r.o.
-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-----------------------
--- CONFIGURATION -----
-----------------------
----- NB-IoT ----------
APN = "nb.m2mc"
PLMNID = "23003"
protocol = "UDP"	-- UDP or TCP
ip = "192.168.0.20"
port = 4242
receiveTimeout = 500 -- the maximum execution time in milliseconds
----- M-BUS -----------
baudrate = 2400		-- baudrate: up to 921600 baud
parity = 2			-- communication parity: 0 for none, 1 for odd and 2 for even parity
stopBits = 1		-- number of stop bits: 1 or 2
dataBits = 8		-- number of data bits: 7 or 8
------ Timing ---------
-- device wakeup interval
periodHours = 2
periodMinutes = 30
-----------------------
-----------------------
-- CONFIGURATION END --
-----------------------
function onWake ()
    -- set link parameters - 2400 baud, 8E1
    api.mbusSetup(baudrate,parity,stopBits,dataBits)
    api.mbusState(1)
    api.delayms(2000)
    -- CREATE MBUS QUERY --
    -- UD2
    --b=pack.pack('<b5', 0x10, 0x5B, 0xFE, 0x59, 0x16)
    b=pack.pack('<b5', 0x10, 0x7B, 0xFE, (0x7B+0xFE)%256, 0x16)
    status,c,a,ci,ans = api.mbusTransaction(b,3000,1)
    api.mbusState(0)
    print("From MBus Calorimeter: ")
    api.dumpArray(ans)
    
    if #ans < 1 then
        buf = "NO DATA RECEIVED"
    else
        buf = ans
    end
    print("To NBIOT: ")
    api.dumpArray(buf)
    print("Sending to NB-IoT")
    api.nbSend(ip, port, buf, receiveTimeout, protocol)
    
    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 NB-IoT default script with APN settings")
    result = api.nbAT("AT*MCGDEFCONT=\"IP\",\"" .. APN .."\"", 5000, 1)
    print("APN result: " .. result)
    result = api.nbAT("AT+COPS=1,2,\"" .. PLMNID .."\"", 5000, 1)
    print("PLMNID result: " .. result)
end
-- Copyright 2021 ACRIOS Systems s.r.o.
-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---- CONFIGURATION ----
  APN = "nb.m2mc"
  PLMNID = "23003"
  periodDays = 1
  periodHours = 0
  periodMinutes = 0
  proto = "UDP"
  ip = "192.168.0.20"
  port = 4242
---------------------
 
 
function processConfig(ret, cmd, cfg)
  -- cmd 1 is selected for new filter configuration
  if cmd == 1 then
    print("New filter config:")
    if  cfg == nil or #cfg < 3 then
      print("Purge filter, receive all")
      api.wmbusFilter("purge", cfg)
      filterLength = 0
      api.setVar(1, filterLength)
    else
      api.dumpArray(cfg)
      api.wmbusFilter("populate", cfg)
      filterLength = #cfg/4
      filterIdHasReceived = {}
      for i=1, filterLength do
        filterIdHasReceived[i] = 0
      end
      maxTimeout = filterLength*60000
      api.setVar(1, filterLength)
    end
  end
  -- cmd 2 is used to switch between WMBUS modes
  if cmd == 2 then
    if  cfg == nil or cfg == '' then
      print("Bad WMBUS mode, Specify C1 or T1 or S1")
    else
      print("New WMBUS mode: "..cfg)
      api.wmbusSetup(10, "master", cfg, 1)
    end
  end
end
 
function onWake () 
  filterLength = api.getVar(1)
  print("Filter length: " .. tostring(filterLength))
 
  if filterLength > 0 then
    filterIdHasReceived = {}
    for i=1, filterLength do
      filterIdHasReceived[i] = 0
    end
  else
    print("Error - no filter setup!")
    ret, recved = api.nbSend(ip, port, string.char(255) .. 'NO FILTER, send ID filter to 1, mode to 2', 5000, proto)
    if recved ~= nil then
      if #recved > 1 then
        processConfig(ret, string.byte(string.sub(recved,1,1)), string.sub(recved, 2))
      end
    end
  end
 
  start = api.getTick()
  maxTimeout = filterLength*60000
 
    -- receive WMBUS frames and upload to NBIOT in format [[meter filter ID] [raw wmbus frame]]
  while filterLength > 0 do
    while true do
 
      -- if timeout, report not responding units
      now = api.getTick()
      if now - start > maxTimeout then
        failing = ""
        for i=1, filterLength do
          failing = failing .. string.char(filterIdHasReceived[i])
        end
        print("List of failing Filter IDs:")
        api.dumpArray(failing)
        ret, recved = api.nbSend(ip, port, failing, 5000, proto)
        if recved ~= nil then
          if #recved > 1 then
            processConfig(ret, string.byte(string.sub(recved,1,1)), string.sub(recved, 2))
          end
        end
 
        print("Timeout receiving data from all meters...")
        -- wake up later
        api.wakeUpIn(periodDays, periodHours, periodMinutes, 0)
        return
      end
 
      status, CI, manID, id, ver, devType, ctrlInfo, data, raw, filterId =  api.wmbusReceiveFrame(60000, 1)
      if raw ~= nil then
        print("Received valid wMBUS frame...")
        break
      end
    end
 
    if filterId ~= nil then
      if filterId > 0 then
 
        if filterIdHasReceived[filterId] == 0 then
          print("Upload data from node "..tostring(filterId).." to NBIOT.")
          filterIdHasReceived[filterId] = 1
          api.dumpArray(string.char(filterId) .. raw)
          ret, recved = api.nbSend(ip, port, string.char(filterId) .. raw, 5000, proto)
          if recved ~= nil then
            if #recved > 1 then
              processConfig(ret, string.byte(string.sub(recved,1,1)), string.sub(recved, 2))
            end
          end
 
        else
          print("Data from meter "..tostring(filterId).." already received.")
        end
 
        test = 1
        for i=1, filterLength do
          if filterIdHasReceived[i] == 0 then
            test = 0
            break
          end
        end
 
        if test == 1 then
          print("Received data from all meters, sleeping.......")
          -- wake up later
          api.wakeUpIn(periodDays, periodHours, periodMinutes, 0)
          return
        end
      end
    end
  end
end
 
function onStartup()
 
  print("Starting up WMBUS to NBIOT script, V1.2")
 
  api.nbAT("AT*MCGDEFCONT=\"IP\",\"" .. APN .."\"", 5000, 1)
  api.nbAT("AT+COPS=1,2,\"".. PLMNID .."\"", 5000, 1)
  api.nbAT("AT+CBAND=20", 5000, 1)
 
  -- Initial filter ids:  0x01673213, 0x19854733 ... (in little endian)
  -- COMMENT OUT START
  --filter = ""
  --filter = filter .. string.char(0x13, 0x32, 0x67, 0x01) -- 0x01673213
  --filter = filter .. string.char(0x33, 0x47, 0x85, 0x19) -- 0x19854733
  --filterLength = #filter/4
  --api.wmbusFilter("populate", filter)
  -- COMMENT OUT END
 
  ret, filterLength = api.wmbusFilter("fetch")
  api.setVar(1, filterLength)
 
  -- Start receiving at C1/T1 mode/frequency
  api.wmbusSetup(10, "master","T1", 1)
end