I have been developing and implementing MQTT and Mote functionality into IoT devices - mostly arduino but also on rpi [using twisted matrix]. Now with the ESP8266 I ported [and condensed] the Mote library into the esp8266 module [ESP-03].
Functionality achieved with each is:
esp/200/$M/sub -> tell the esp8266 to subscribe to new topic. payload will be the new topic to subscribe to
esp/200/$R -> soft restart the esp8266
esp/200/$Lua -> send lua console commands to the esp8266. get response console messages from esp/200/@Lua - [still buggy]
esp/200/$L/g2 -> latch a gpio [a binary output]. payload is latch state ie, 1 or 0. Once executed a mqtt message is send resembling esp/200/@L/g2 with the state of the gpio as the payload
esp/200/$P/g2-> pulse a gpio[a binary output]. payload is pulse duration in ms. For each gpio state change [pulse up and down] a mqtt message is send resembling esp/200/@P/g2 with the state of the gpio as the payload.
esp/200/@A/heap ->Heap size reporting [delta = 2000 ; only report changes greater/less than delta value]
esp/200/@S/g0 -> g0 reporting
esp/200/@S/g15 ->g15 reporting
topics resemble: <deviceType>/<deviceAddress>/<msgType>/<msgIndex>
An example is esp/200/@S/g2 -> which is on esp with device id 200 [src address] of type binary/status input at gpio2
Another example is esp/200/$P/g14 -> which is on esp with device id 200 [dest address] of type binary pulse output at gpio14. The payload [ms] will pulse g14 for that period
msgTypes are:
@S - status/ binary input
@A - analog input
@L - response message to gpio latch control received
@P - response message to gpio pulse control received
@Lua - response message to Lua console message received
$L - binary output latch control on gpio
$P - binary output pulse control on gpio
$M/sub - subscribe to new topic
$M/unsub - unsubscribe to topic [unsubscribe functionality not yet in nodeMcu MQTT module. Hopefully soon..
$R - reset the esp
$Lua - send lua messages to the console [Lua file integration coming soon]
$T - time control to send time to the esp [coming soon]
This code is not optomised for RAM [and I ran out of memory pretty quickly], and so had to compile all the files into lc on the esp besides init.lua. And because of the RAM problem I only have 2 inputs gpio 0 and 25 and 2 gpio outputs gpio 2 and 14. Im using an ESP-03. Once optomised all available gpio will be available through a config file.
Here is the code:
init.lua
--print('baudrate='..uart.setup(0,115200,8,0,1))
--setup wifi hotspot
--dofile('wifiStn_smc.lua')
--node.compile('wifiStn_smc.lua')
dofile('wifiStn_smc.lc')
--pause a little - 1 sec
tmr.delay(1000000)
--dofile('glue.lua')
--node.compile('glue.lua')
dofile('glue.lc')
print('INITDONE. heapsize='..node.heap())glue.lua
-- glue.lua
--glue script for mqtt and mote
--gpioIndex= { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12},
--gpioMap= {'g16', 'g5', 'g4', 'g0', 'g2', 'g14', 'g12', 'g13', 'g15', 'g3', 'g1', 'g9', 'g10'},
--local scanPeriod=500 --moved to moteScan.lua
local brokerIp='192.168.2.100'
local brokerPort=1883
--local user=''
--local password=''
local keepAlive=120
local clientId='esp/200'
--local lwTopic='lwt'
--local lwPayload='offline'
--local subListAll = '/#' --everything esp/200/#
--local subM, subR, subLua, subP, subL, subA, subT= '$M/sub','$R','$Lua','$P/+','$L/+','$A/+','$T'
local subR, subLua, subP, subL= '$R','$Lua','$P/+','$L/+'
--local ioHeapValue, ioHeapStoreValue, ioHeapDelta = -1,-1,100 --moved to moteScan.lua
--local ioVddValue, ioVddStoreValue, ioVddDelta = -1,-1,0.1 --moved to moteScan.lua
--local iog0Index, iog0Value, iog0StoreValue, iog0Type = 3,-1,-1,'@S' --moved to moteScan.lua
local iog2Index, iog2Value, iog2StoreValue, iog2Type = 4,-1,-1,'$L' --moved to moteExecute.lua
--local iog3Index, iog3Value, iog3StoreValue, iog3Type = 9,-1,-1,'$L'
m = mqtt.Client(clientId,keepAlive, '','')--user,password)
local function pub(topicPart, pl)
--print('clientId='..clientId)
--print('topicPart='..topicPart)
--print('payload='..pl)
m:publish(clientId..topicPart,pl,0,0, function(conn) print("--> "..clientId..topicPart..'\t\t'..pl) end)
end
local moteScan= require('moteScan')
local function subscribed(con)
print (clientId..' subscribed to topics')
moteScan:scanIo(pub)
end
local function connected(con)
print(clientId..' connected to '..brokerIp..':'..brokerPort)
--m:subscribe(clientId..subListAll,0,subscribed)
m:subscribe(clientId..'/'..subP,0,function()
m:subscribe(clientId..'/'..subL,0,function()
m:subscribe(clientId..'/'..subR,0,function()
m:subscribe(clientId..'/'..subLua,0,subscribed)
end)
end)
end)
end
local motex= require('moteExecute')
local spl= require('spl')
local function execute(conn, topic, data)
if data ~= nil then
print('<-- '..topic .. ":\t"..data )
--decoding the following topics:
-- <destType>/<destAddress>/<msgType>/<pointId>
-- $M - Mqtt pub and sub
-- $R - Reset Device
--args: <msgType>,<pointid>,<payload>
-- $L - Latch Control
-- $R - Pulse Control
-- $A - Analog Control
-- $T - Time Control
local splits = spl.split(topic,'/')
--test for <destype> and <destAddress> match
if splits[1]..'/'..splits[2] == clientId then
--after match execute for types
motex:execute(splits,data,m,pub)
end
end
end
--m:lwt("/lwt", "offline", 0, 0)
m:on("connect", function(con) print ("connected..") end)
m:on("offline", function(con) print ("offline") end)
m:on("message", execute)
tmr.alarm(1,200,1,function()
local ip=wifi.sta.getip()
if ip ==nil then
--print('.')
else
m:connect(brokerIp,brokerPort, 0,connected)
tmr.stop(1)
end
end)moteExecute.lua
-- mote.lua
--inteface definition to mqtt
--gpioIndex= { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12},
--gpioMap= {'g16', 'g5', 'g4', 'g0', 'g2', 'g14', 'g12', 'g13', 'g15', 'g3', 'g1', 'g9', 'g10'},
local mote={}
--output values
local iog2Index, iog2Value, iog2StoreValue, iog2Type = 4,-1,-1,'$L'
local iog14Index, iog14Value, iog14StoreValue, iog14Type = 5,-1,-1,'$L'
function mote:execute(splits,payload,m, pub)
if splits[3]=='$M' then
--sub/unsub to payload
if splits[4] == 'sub' then
m:subscribe(payload,0,function() print ('Additionally subscribed to'..payload) end)
-- elseif splits[4] == 'unsub' then
-- --m:unsubscribe(payload,0,unsubscribed)
-- print('no mqtt:unsubscribe() available yet')
end
elseif splits[3]=='$R' then
--reset the 'program'
print ('$R-RESTARTING...') node.restart()
elseif splits[3]=='$Lua' then
--send to Lua interpreter'
node.output(function(str) pub('/@Lua',str) end, 1) -- re-direct output to function
node.input(payload..'\r\n')
tmr.alarm(6,500,0,function() node.output(nil) end) -- 500ms should do for the return value??
elseif splits[3]=='$L' then
--iog2 - FW pin 4 - write gpio2
if splits[4]=='g2' then
gpio.mode(iog2Index,gpio.OUTPUT)
if payload=='0' or payload== 0 then
gpio.write(iog2Index,gpio.LOW)
--print ('$L GPIO LOW')
pub('/@L/g2',gpio.LOW)
iog2Value,iog2StoreValue = gpio.LOW,gpio.LOW
else
gpio.write(iog2Index,gpio.HIGH)
--print ('$L GPIO HIGH')
pub('/@L/g2',gpio.HIGH)
iog2Value,iog2StoreValue = gpio.HIGH,gpio.HIGH
end
--iog14 - pin 13 - write gpio14
elseif splits[4]=='g14' then
gpio.mode(iog14Index,gpio.OUTPUT)
if payload=='0' or payload== 0 then
gpio.write(iog14Index,gpio.LOW)
pub('/@L/g14',gpio.LOW)
iog14Value,iog14StoreValue = gpio.LOW,gpio.LOW
else
gpio.write(iog14Index,gpio.HIGH)
pub('/@L/g14',gpio.HIGH)
iog14Value,iog14StoreValue = gpio.HIGH,gpio.HIGH
end
end
elseif splits[3]=='$P' then
--iog2 - FW pin 4 - write gpio2
if splits[4]=='g2' then
gpio.mode(iog2Index,gpio.OUTPUT)
if iog2StoreValue>0 then
gpio.write(iog2Index,gpio.LOW)
--print ('$P GPIO LOW')
pub('/@P/g2',gpio.LOW)
tmr.alarm(4,payload,0,function()
gpio.write(iog2Index,gpio.HIGH)
--print ('$P GPIO HIGH')
pub('/@P/g2',gpio.HIGH)
iog2Value,iog2StoreValue = gpio.HIGH,gpio.HIGH
tmr.stop(4)
end)
else
gpio.write(iog2Index,gpio.HIGH)
--print ('$P GPIO HIGH')
pub('/@P/g2',gpio.HIGH)
tmr.alarm(3,payload,0,function()
--tmr.delay(data*1000)
gpio.write(iog2Index,gpio.LOW)
--print ('$P GPIO LOW')
pub('/@P/g2',gpio.LOW)
iog2Value,iog2StoreValue = gpio.LOW,gpio.LOW
tmr.stop(3)
end)
end
elseif splits[4]=='g14' then
gpio.mode(iog14Index,gpio.OUTPUT)
if iog14StoreValue>0 then
gpio.write(iog14Index,gpio.LOW)
--print ('$P GPIO LOW')
pub('/@P/g14',gpio.LOW)
tmr.alarm(4,payload,0,function()
gpio.write(iog14Index,gpio.HIGH)
--print ('$P GPIO HIGH')
pub('/@P/g14',gpio.HIGH)
iog14Value,iog14StoreValue = gpio.HIGH,gpio.HIGH
tmr.stop(4)
end)
else
gpio.write(iog14Index,gpio.HIGH)
--print ('$P GPIO HIGH')
pub('/@P/g14',gpio.HIGH)
tmr.alarm(3,payload,0,function()
--tmr.delay(data*1000)
gpio.write(iog14Index,gpio.LOW)
--print ('$P GPIO LOW')
pub('/@P/g14',gpio.LOW)
iog14Value,iog14StoreValue = gpio.LOW,gpio.LOW
tmr.stop(3)
end)
end
end
end
end
-- ------------------------------------------------------------------------- --
-- Define Mote "module"
-- ~~~~~~~~~~~~~~~~~~~~~~~
return(mote)
-- ------------------------------------------------------------------------- --moteScan.lua
-- mote.lua
--inteface definition to mqtt
--gpioIndex= { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12},
--gpioMap= {'g16', 'g5', 'g4', 'g0', 'g2', 'g14', 'g12', 'g13', 'g15', 'g3', 'g1', 'g9', 'g10'},
local mote={}
local scanPeriod=500
--input values
local ioHeapValue, ioHeapStoreValue, ioHeapDelta = -1,-1,2000
--local ioVddValue, ioVddStoreValue, ioVddDelta = -1,-1,100
local iog0Index, iog0Value, iog0StoreValue, iog0Type = 3,-1,-1,'@S'
local iog15Index, iog15Value, iog15StoreValue, iog15Type = 8,-1,-1,'@S'
local function scan()
--print('scan pubcb='..tostring(pubcb))
--ioHeap
ioHeapValue = node.heap()
if math.abs(ioHeapStoreValue - ioHeapValue) > ioHeapDelta then
ioHeapStoreValue = ioHeapValue
--m:publish(tostring(clientId)..'/@A/heap',ioHeapValue,0,0, function(conn) print("--> "..tostring(clientId)..'/@A/heap:\t'..ioHeapValue) end)
--print('/@A/heap='..ioHeapValue)
mote.pubCb('/@A/heap',ioHeapValue)
end
--ioVdd33
--ioVddValue = node.readvdd33()/1000
--if math.abs(ioVddStoreValue - ioVddValue) > ioVddDelta then
-- ioVddStoreValue = ioVddValue
-- m:publish(clientId..'/@A/vdd33',ioVddValue,0,0, function(conn) print("--> "..clientId..'/@A/vdd33:\t'..ioVddValue) end)
-- --pub('/@A/vdd33',ioVddValue)
--end
--iog0 - FW pin 3 - read gpio0
gpio.mode(iog0Index,gpio.INPUT) iog0Value = gpio.read(iog0Index)
if iog0Value ~= iog0StoreValue then
iog0StoreValue = iog0Value
--m:publish(tostring(clientId)..'/@S/g0',iog0Value,0,0, function(conn) print("--> "..tostring(clientId)..'/@S/g0:\t'..iog0Value) end)
--print('/@S/g0='..iog0Value)
mote.pubCb('/@S/g0',iog0Value)
end
--iog15 - pin 8 - read gpio15
gpio.mode(iog15Index,gpio.INPUT) iog15Value = gpio.read(iog15Index)
if iog15Value ~= iog15StoreValue then
iog15StoreValue = iog15Value
--m:publish(tostring(clientId)..'/@S/g0',iog0Value,0,0, function(conn) print("--> "..tostring(clientId)..'/@S/g0:\t'..iog0Value) end)
--print('/@S/g0='..iog0Value)
mote.pubCb('/@S/g15',iog15Value)
end
--print('mote.scanIo.end')
end
function mote:scanIo(pubCb)
--print('scanIo pubCb='..tostring(pubCb))
--for i,v in pairs(pubCb) do print('i='..i) print('v='..tostring(v)) end
mote.pubCb = pubCb
tmr.alarm(2,scanPeriod,1,scan)--(pubCb))
end
-- ------------------------------------------------------------------------- --
-- Define Mote "module"
-- ~~~~~~~~~~~~~~~~~~~~~~~
return(mote)
-- ------------------------------------------------------------------------- --spl.lua
local spl={}
local function split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
spl.split =split
return(spl)wifiStn_smc.lua
--print(wifi.sta.getip())
--nil
wifi.setmode(wifi.STATION)
wifi.sta.config("SMC","dummypassword")
--tmr.delay(1000000)
tmr.alarm(1,200,1,function()
ip=wifi.sta.getip()
--192.168.0.x for nubbleNet
if ip ==nil then
--print('.')
else
print('Wifi connected! IP='..ip)
tmr.stop(1)
end
end)