Air202 GSM - sending temperature, humidity, battery voltage and rssi to the Cayenne IoT cloud and relay control from the phone application

About This Project

The script is designed for Air202 on the S6 board and the module version with the solar battery charging system (diagram on the attached photo). For the version with CN3065 chip, we can connect the solar panel, which in combination with the module susceptibility will significantly extend its working time.

What’s Connected

Instruction

The scripts below are based on source files. All source files are found here.

A description of the installation of scripts and basic software can be found here Moduł GSM Air202 - opis instalacji oprogramowania - elektroda.pl

Preparation of scripts for installation

1. From the Luat_2G_RDA_8955/script_LuaTask/lib/ directory, open the link.lua file in the text editor and enter the access data for your network in the following function.

function setAPN (apn, user, pwd)
apnname, username, password = “internet”, “internet”, “internet”
end

2. Open the mqttTask.lua file in the text editor and enter the configuration data from your Cayenne IoT panel.

c_username = “ce263200-1682-11e9-a08c-c5a286f8c00d”
c_password = “b25e852397da440628babd9bf4198fec6870da91”
c_clientid = “327c5f30-28a4-11e9-8cb9-732fc93af22b”

3. In the file mqttTask.lua you can set the time (ms) to send the following data to the Cayenne IoT.

send_temp = 30000
send_hum = 32000
send_batt = 50000
send_rssi = 42000

4. In the file mqttOutMsg.lua you can choose which data you want to send to Cayenne IoT. By default, the script sends battery voltage and rssi. Temperature and humidity will be sent after removing “–” from “-- pubQos0Test()” and “-- pubQos1Test()”. If you do not use BME280, do not delete “–” because the script will report an error.

function init()
– Publish temperature
– pubQos0Test()
– Publish humidity
– pubQos1Test()
– Publish battery voltage
pubQos2Test()
– Publish rssi
pubQos3Test()
end

5. Download the basic software DEFAULT_2.0.0_Luat_V0033_Air202 into the module.
6. Upload all files from the Luat_2G_RDA_8955/script_LuaTask/lib/ directory to the module.
7. Upload the changed files main.lua, mqttInMsg.lua, mqttOutMsg.lua and mqttTask.lua to the module.
8. The connection of the BME280 and the relay with Air202 is shown in the attached photos.
9. Restart the module and go to the configuration settings in the Cayenne IoT panel to add the switch on channel 5 and widgets.

Scripts

main.lua

-- PROJECT and VERSION variables must be defined at this location
-- PROJECT: ascii string type, can be defined casually, as long as it is not used,
-- VERSION: ascii string type, if you use the Luat IoT cloud platform firmware upgrade function, you must follow the definition of "X.X.X", X means 1 digit; otherwise you can define it casually
PROJECT = "MQTT"
VERSION = "2.0.0"

-- Load the log function module and set the log output level
-- If you close the log that is output from the log module interface, the level is set to log.LOG_SILENT
require "log"
LOG_LEVEL = log.LOGLEVEL_TRACE
--[[
If you use the UART output log, open the code "--log.openTrace(true,1,115200)" in this line, and modify the parameters of this interface according to your needs.
If you want to completely close the output log in the script (including calling the log module interface and the log output from the Lua standard print interface), execute log.openTrace (false, the second parameter is the same as the second parameter that calls the openTrace interface to open the log). E.g:
1, did not call sys.opntrace configuration log output port or the last time is to log.openTrace (true, nil, 921600) configuration log output port, at this time to close the output log, directly call log.openTrace (false)
2, the last time is to call log.openTrace (true, 1, 115200) to configure the log output port, at this time to close the output log, directly call log.openTrace (false, 1)
]]
-- log.openTrace(true,1,115200)

require "sys"

require "net"
-- Query the GSM signal strength every 1 minute
-- Query base station information every 1 minute
net.startQueryAll(60000, 60000)

-- Load console debugging function module (here code is uart1, baud rate 115200)
-- This function module is not required, depending on the project requirements, whether to load or not
-- Note when using: uart used by the console should not conflict with uart used by other functions.
-- For instructions, refer to "console function instructions.docx" under demo/console
-- require "console"
-- console.setup(1, 115200)

-- Load hardware watchdog function module
-- According to your own hardware configuration: 1, whether to load this function module; 2, configure the Luat module to reset the microcontroller pins and feed the dog pins
-- The Air201 development board sold by Hezhou has a hardware watchdog, so this function module must be loaded when using the official Air201 development board.
require "wdt"
wdt.setup(pio.P0_30, pio.P0_31)

-- Set GPIO 29 as an output
-- Set GPIO 29 low
pio.pin.setdir(pio.OUTPUT, pio.P0_29)
pio.pin.setval(0, pio.P0_29)

-- Load network indicator function module
-- According to your own project needs and hardware configuration: 1, whether to load this function module; 2, configuration indicator pin
-- The indicator pins on the Air800 and Air801 development boards sold by Hezhou are pio.P0_28, and the indicator pins on other development boards are pio.P1_1
--require "netLed"
--netLed.setup(true,pio.P1_1)
-- In the network indicator function module, the blinking rule of the indicator light under various working conditions is configured by default. Refer to the default value of ledBlinkTime configuration in netLed.lua.
-- If the default value does not meet the requirements, call netLed.updateBlinkTime here to configure the blinking duration.

-- Load error log management function module [It is strongly recommended to open this function]
-- The following 2 lines of code, just a simple demonstration of how to use the errDump function, see the errDump api for details
-- require "errDump"
-- errDump.request("udp://ota.airm2m.com:9072")

-- Load remote upgrade function module [strongly recommended to open this function]
-- The following 3 lines of code, just a simple demonstration of how to use the update function, refer to the update api and demo/update for details.
-- PRODUCT_KEY = "v32xEAKsGTIEQxtqgwCldp5aPlcnPs3K"
-- require "update"
-- update.request()

-- Load MQTT function test module
require "mqttTask"
-- Start system framework
sys.init(0, 0)
sys.run()

mqttInMsg.lua

-- Module function: MQTT client data receiving processing
-- @author openLuat
-- @module mqtt.mqttInMsg
-- @license MIT
-- @copyright openLuat
-- @release 2018.03.28

module(...,package.seeall)

-- MQTT client data receiving processing
-- @param mqttClient, MQTT client object
-- @return processing returns true, processing error returns false
-- @usage mqttInMsg.proc(mqttClient)
function proc(mqttClient)
    local result,data
    while true do
        result,data = mqttClient:receive(2000)
        -- Received data
        if result then
            log.info("mqttInMsg.proc",data.topic,string.toHex(data.payload))
                 
            -- TODO: Process data.payload according to your own requirements
            if string.sub(data.topic, 89,89) == "5" then
                str_rec = string.sub(data.payload, 1,15)
                str_sta = string.sub(data.payload, 17,17)
                mqttOutMsg.pubQos4Test()
            end
            
            -- If there is data waiting to be sent in mqttOutMsg, exit this loop immediately
            if mqttOutMsg.waitForSend() then return true end
        else
            break
        end
    end
	
    return result or data=="timeout"
end

mqttOutMsg.lua

-- Module function: MQTT client data transmission processing
-- @author openLuat
-- @module mqtt.mqttOutMsg
-- @license MIT
-- @copyright openLuat
-- @release 2018.03.28

--[[Temperature and humidity are read on the basis of the data sheet provided by Bosch for the Bme280 environmental sensor. 
The sensor calibration code is based on algorithms provided by Bosch.]]

module(...,package.seeall)

-- message queue for data transmission
local msgQueue = {}

local function insertMsg(topic,payload,qos,user)
    table.insert(msgQueue,{t=topic,p=payload,q=qos,user=user})
end

local function pubQos0TestCb(result)
    log.info("mqttOutMsg.pubQos0TestCb",result)
    if result then sys.timerStart(pubQos0Test,mqttTask.send_temp) end
end

function pubQos0Test()
    insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/1","temp,c=" .. string.sub(readTemHum(),6,10),1,{cb=pubQos0TestCb})    
end

local function pubQos1TestCb(result)
    log.info("mqttOutMsg.pubQos1TestCb",result)
    if result then sys.timerStart(pubQos1Test,mqttTask.send_hum) end
end

function pubQos1Test()
    insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/2","rel_hum,p=" .. string.sub(readTemHum(),20,24),1,{cb=pubQos1TestCb})
end

local function pubQos2TestCb(result)
    log.info("mqttOutMsg.pubQos2TestCb",result)
    if result then sys.timerStart(pubQos2Test,mqttTask.send_batt) end
end

function pubQos2Test()
    insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/3","batt,v=" .. misc.getVbatt(),1,{cb=pubQos2TestCb})    
end

local function pubQos3TestCb(result)
    log.info("mqttOutMsg.pubQos3TestCb",result)
    if result then sys.timerStart(pubQos3Test,mqttTask.send_rssi) end    
end

function pubQos3Test()
    insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/4","rssi,dbm=" .. net.getRssi(),1,{cb=pubQos3TestCb})            
end

local function pubQos4TestCb(result)
    log.info("mqttOutMsg.pubQos4TestCb",result)   
end

-- Set the switch
function pubQos4Test()
    if mqttInMsg.str_sta == "1" then
        print("switch on")
        -- Set GPIO 29 high 
        pio.pin.sethigh(pio.P0_29)    
        insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/5","1",1,{cb=pubQos4TestCb})
        insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/response", "ok," .. string.sub(mqttInMsg.str_rec, 1,15),1,{cb=pubQos4TestCb})
    elseif mqttInMsg.str_sta == "0" then
        print("switch off")
        -- Set GPIO 29 low 
        pio.pin.setlow(pio.P0_29)       
        insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/5","0",1,{cb=pubQos4TestCb})
        insertMsg("v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/response", "ok," .. string.sub(mqttInMsg.str_rec, 1,15),1,{cb=pubQos4TestCb})     
    end              
end

-- Initialize "MQTT client data transmission"
-- @return none
-- @usage mqttOutMsg.init()
function init() 
    -- Publish temperature
    --pubQos0Test()
     
    -- Publish humidity
    --pubQos1Test() 
    
    -- Publish battery voltage
    pubQos2Test()
     
    -- Publish rssi
    pubQos3Test() 
end

-- To initialize "MQTT client data transmission"
-- @return none
-- @usage mqttOutMsg.unInit()
function unInit()
    sys.timerStop(pubQos0Test)
    sys.timerStop(pubQos1Test)
    while #msgQueue>0 do
        local outMsg = table.remove(msgQueue,1)
        if outMsg.user and outMsg.user.cb then outMsg.user.cb(false,outMsg.user.para) end
    end
end

-- Does the MQTT client have data waiting to be sent?
-- @return returns true if data is waiting to be sent, otherwise returns false
-- @usage mqttOutMsg.waitForSend()
function waitForSend()
    return #msgQueue > 0
end

-- MQTT client data transmission processing
-- @param mqttClient, MQTT client object
-- @return processing returns true, processing error returns false
-- @usage mqttOutMsg.proc(mqttClient)
function proc(mqttClient)
    while #msgQueue>0 do
        local outMsg = table.remove(msgQueue,1)
        local result = mqttClient:publish(outMsg.t,outMsg.p,outMsg.q)
        if outMsg.user and outMsg.user.cb then outMsg.user.cb(result,outMsg.user.para) end
        if not result then return end
    end
    return true
end

-- Read the temperature and humidity from bme280
function readTemHum()
    local str_tem
    local function init(data)
        local i2cslaveaddr = 0x76   
        if i2c.setup(2,i2c.SLOW, i2cslaveaddr) ~= i2c.SLOW then
            print("init fail")
          return
        end 
        i2c.write(2, 0xF2, 0x01)
        i2c.write(2, 0xF4, 0x25)
        i2c.write(2, 0xF5, 0x00)    
        -- Temperature 
        dig_T1 = bit.bor(bit.lshift(string.byte(i2c.read(2,0x89,1)),8), string.byte(i2c.read(2,0x88,1)))
        dig_T2 = bit.bor(bit.lshift(string.byte(i2c.read(2,0x8B,1)),8), string.byte(i2c.read(2,0x8A,1)))
        dig_T3 = bit.bor(bit.lshift(string.byte(i2c.read(2,0x8D,1)),8), string.byte(i2c.read(2,0x8C,1))) 
        adc_T = bit.bor(bit.lshift(string.byte(i2c.read(2,0xFA,1)),12), bit.lshift(string.byte(i2c.read(2,0xFB,1)),4), bit.lshift(string.byte(i2c.read(2,0xFC,1)),4))     
        var1 = bit.arshift((bit.arshift(adc_T,3) - bit.lshift(dig_T1,1)) * dig_T2,11)
        var2 = bit.arshift((bit.arshift((bit.arshift(adc_T,4) - dig_T1) * (bit.arshift(adc_T,4) - dig_T1),12)) * dig_T3,14) 
        t_fine = var1 + var2  
        T = bit.arshift(t_fine * 5 + 128,8)    
        -- Humidity
        dig_H1 = string.byte(i2c.read(2,0xA1,1))
        dig_H2 = bit.bor(bit.lshift(string.byte(i2c.read(2,0xE2,1)),8), string.byte(i2c.read(2,0xE1,1)))
        dig_H3 = string.byte(i2c.read(2,0xE3,1))
        dig_H4 = bit.bor(bit.lshift(string.byte(i2c.read(2,0xE4,1)),4), bit.band(string.byte(i2c.read(2,0xE5,1)),0xF))
        dig_H5 = bit.bor(bit.lshift(string.byte(i2c.read(2,0xE6,1)),4), bit.rshift(string.byte(i2c.read(2,0xE5,1)),4))
        dig_H6 = string.byte(i2c.read(2,0xE7,1))
        adc_H = bit.bor(bit.lshift(string.byte(i2c.read(2,0xFD,1)),8), string.byte(i2c.read(2,0xFE,1)))    
        v_x1_u32r = (t_fine - (76800))  
        v_x1_u32r = (bit.arshift(((bit.lshift(adc_H,14) - bit.lshift((dig_H4),20) - ((dig_H5) * v_x1_u32r)) + (16384)),15) * bit.arshift(((bit.arshift((bit.arshift((v_x1_u32r * (dig_H6)),10) * (bit.arshift((v_x1_u32r * (dig_H3)),11) + (32768))),10) + (2097152)) * (dig_H2) + 8192),14)) 
        v_x1_u32r = (v_x1_u32r - bit.arshift((bit.arshift((bit.arshift(v_x1_u32r,15) * bit.arshift(v_x1_u32r,15)),7) * (dig_H1)),4))   
        if v_x1_u32r < 0 then
            v_x1_u32r = 0 
        end  
        if v_x1_u32r > 419430400 then
            v_x1_u32r = 419430400 
        end    
        -- String temperature and humidity
        str_tem = "tem: " .. string.sub(T,1,2) .. "." .. string.sub(T,3,4) .. "*C " .. " hum: " .. string.sub(bit.arshift(bit.arshift(v_x1_u32r,12) * 1000, 10),1,2) .. "." .. string.sub(bit.arshift(bit.arshift(v_x1_u32r,12) * 1000, 10),3,4) .. "%"
        io.write(str_tem, "\n")
    end 
    init()
    i2c.close(2)
    return str_tem
end

mqttTask.lua

-- Module function: MQTT client processing framework
-- @author openLuat
-- @module mqtt.mqttTask
-- @license MIT
-- @copyright openLuat
-- @release 2018.03.28

module(...,package.seeall)

require"misc"
require"mqtt"
require"mqttOutMsg"
require"mqttInMsg"

-- Enter the username, password and client id from the Cayenne configuration page
c_username = "5c37e060-2ea8-11e9-9c33-75e6b356cec4"
c_password = "4ee24ceeae4d5c88babbcf95aa93a5e72b444165"
c_clientid = "7c91e3b0-2ea8-11e9-9c33-75e6b356cec4"

-- Set the time (ms) for publication of temperature, humidity, battery voltage and rssi
send_temp = 30000
send_hum = 32000
send_batt = 50000
send_rssi = 42000

local ready = false

-- Is the MQTT connection active?
-- @return returns true if activated, false returns if inactive
-- @usage mqttTask.isReady()
function isReady()
    return ready
end

-- Start the MQTT client task
sys.taskInit(
    function()
        local retryConnectCnt = 0
        while true do
            if not socket.isReady() then
                retryConnectCnt = 0
                -- Wait for the network environment to be ready, the timeout is 5 minutes
                sys.waitUntil("IP_READY_IND",300000)
            end
            
            if socket.isReady() then
                local imei = misc.getImei()
                -- Create an MQTT client
                local mqttClient = mqtt.client(c_clientid,nil,c_username,c_password)
                -- Block execution of the MQTT CONNECT action until successful
                -- If you use ssl connection, open mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"}), configure according to your needs
                -- mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"})
                if mqttClient:connect("mqtt.mydevices.com",1883,"tcp") then
                    retryConnectCnt = 0
                    ready = true
                    -- Subscription theme
                    if mqttClient:subscribe({                         
                    -- ["v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/1"]=1,
                    -- ["v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/2"]=1,
                    -- ["v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/3"]=1,
                    -- ["v1/" .. mqttTask.c_username .. "/things/" .. mqttTask.c_clientid .. "/data/4"]=1,
                    ["v1/" .. c_username .. "/things/" .. c_clientid .. "/cmd/5"]=1                                                   
                    }) then                    
                        mqttOutMsg.init()
                        -- Loop processing received and sent data
                        while true do
                            if not mqttInMsg.proc(mqttClient) then log.error("mqttTask.mqttInMsg.proc error") break end
                            if not mqttOutMsg.proc(mqttClient) then log.error("mqttTask.mqttOutMsg proc error") break end
                        end
                        mqttOutMsg.unInit()
                    end
                    ready = false
                else
                    retryConnectCnt = retryConnectCnt+1
                end
                -- Disconnect MQTT connection
                mqttClient:disconnect()
                if retryConnectCnt>=5 then link.shut() retryConnectCnt=0 end
                sys.wait(5000)
            else
                -- Enter flight mode, after 20 seconds, exit flight mode
                net.switchFly(true)
                sys.wait(20000)
                net.switchFly(false)
            end
        end
    end
)

Instruction

Air202 - sending temperature, humidity, battery voltage and rssi to the Cayenne IoT cloud and relay control from the phone application.

The script is designed for Air202 on the S6 board and the module version with the solar battery charging system (diagram on the attached photo). For the version with CN3065 chip, we can connect the solar panel, which in combination with the module susceptibility will significantly extend its working time.

Instruction

The scripts below are based on source files. All source files are found here.
https://github.com/openLuat/Luat_2G_RDA_8955

A description of the installation of scripts and basic software can be found here.
https://www.elektroda.pl/rtvforum/viewtopic.php?p=17591692#17591692

Preparation of scripts for installation

1. From the Luat_2G_RDA_8955/script_LuaTask/lib/ directory, open the link.lua file in the text editor and enter the access data for your network in the following function.
function setAPN (apn, user, pwd)
    apnname, username, password = "internet", "internet", "internet"
end
2. Open the mqttTask.lua file in the text editor and enter the configuration data from your Cayenne IoT panel.
c_username = "ce263200-1682-11e9-a08c-c5a286f8c00d"
c_password = "b25e852397da440628babd9bf4198fec6870da91"
c_clientid = "327c5f30-28a4-11e9-8cb9-732fc93af22b"
3. In the file mqttTask.lua you can set the time (ms) to send the following data to the Cayenne IoT.
send_temp = 30000
send_hum = 32000
send_batt = 50000
send_rssi = 42000
4. In the file mqttOutMsg.lua you can choose which data you want to send to Cayenne IoT. By default, the script sends battery voltage and rssi. Temperature and humidity will be sent after removing "--" from "-- pubQos0Test()" and "-- pubQos1Test()". If you do not use BME280, do not delete "--" because the script will report an error.
function init()
    -- Publish temperature
    -- pubQos0Test()
    -- Publish humidity
    -- pubQos1Test()
    -- Publish battery voltage
    pubQos2Test()
    -- Publish rssi
    pubQos3Test()
end
5. Download the basic software DEFAULT_2.0.0_Luat_V0033_Air202 into the module.
6. Upload all files from the Luat_2G_RDA_8955/script_LuaTask/lib/ directory to the module.
7. Upload the changed files main.lua, mqttInMsg.lua, mqttOutMsg.lua and mqttTask.lua to the module.
8. The connection of the BME280 and the relay with Air202 is shown in the attached photos.
9. Restart the module and go to the configuration settings in the Cayenne IoT panel to add the switch on channel 5 and widgets.

Triggers & Alerts

(Did you use the Triggers & Alerts feature?)

Scheduling

(Did you use the Scheduling feature?)

Dashboard Screenshots

Photos of the Project

(Take some pictures of your project functioning in the wild!)

Video

(Upload a YouTube video showcasing your project in action!)

2 Likes

thanks @vanihada for sharing this detailed tutorial with the community.