diff --git a/src/driver/nbiot.be b/src/driver/nbiot.be
index a510d3e8495c26358b423daa1ba8f16bdf412447..e78726bcc5849b81ca3028b512ee2a0792a5215e 100644
--- a/src/driver/nbiot.be
+++ b/src/driver/nbiot.be
@@ -1,48 +1,56 @@
import string
import gpio
+# ------------------------------------------------------- #
+# Requests #
+# ------------------------------------------------------- #
class NBIoTRequestType
+ static var DEBUG = 0
static var MQTT = 1
static var COAP = 2
static var NTP = 3
static var PSM = 4
- static var TEST = 69
end
class NBIoTRequest
- var type
+ var request_type
+ var callback
+
+ def init(request_type, callback)
+ self.request_type = request_type
+ self.callback = callback
+ end
end
-class NBIoTTestRequest : NBIoTRequest
- var msg
+class NBIoTDebugRequest : NBIoTRequest
var cmd
+ var rsp
+ var retries
- def init(msg, cmd)
- self.type = NBIoTRequestType.TEST
- self.msg = msg
+ def init(cmd, rsp, retries)
+ super(self).init(NBIoTRequestType.DEBUG)
self.cmd = cmd
+ self.rsp = rsp
+ self.retries = retries
end
end
class NBIoTMQTTRequest : NBIoTRequest
- var type
var host
var port
var username
var password
var topic
var payload
- var callback
def init(host, port, username, password, topic, payload, callback)
- self.type = NBIoTRequestType.MQTT
+ super(self).init(NBIoTRequestType.MQTT, callback)
self.host = host
self.port = port
self.username = username
self.password = password
self.topic = topic
self.payload = payload
- self.callback = callback
end
end
@@ -53,40 +61,39 @@ class NBIoTCOAPRequest : NBIoTRequest
var query
var topic
var payload
- var callback
def init(host, port, path, method, query, payload, callback)
- self.type = NBIoTRequestType.COAP
+ super(self).init(NBIoTRequestType.COAP, callback)
self.host = host
self.port = port
self.path = path
self.method = method
self.query = query
self.payload = payload
- self.callback = callback
end
end
class NBIoTNTPRequest : NBIoTRequest
- var callback
+
def init(callback)
- self.type = NBIoTRequestType.NTP
- self.callback = callback
+ super(self).init(NBIoTRequestType.NTP, callback)
end
end
class NBIoTPSMRequest : NBIoTRequest
var value
- var callback
def init(value, callback)
- self.type = NBIoTRequestType.PSM
+ super(self).init(NBIoTRequestType.PSM, callback)
self.value = value
- self.callback = callback
end
end
+# ------------------------------------------------------- #
+# Connections #
+# ------------------------------------------------------- #
+
class NBIoTMQTTConnection
var host
var port
@@ -111,121 +118,70 @@ class NBIoTMQTTConnection
end
end
-class NBIoTDriverState
- static var IDLE = 0
- static var RESET = 1
- static var READY = 2
- static var NTP_SYNC = 3
- static var MQTT_OPEN = 4
- static var MQTT_CONNECT = 5
- static var MQTT_PUBLISH = 6
- static var MQTT_CHECK_CONN = 7
- static var MQTT_CLOSE = 8
- static var COAP_OPEN = 9
- static var COAP_SET_OPTIONS = 10
- static var COAP_SEND = 11
- static var COAP_RECEIVE = 12
- static var COAP_CLOSE = 13
- static var PSM_DISABLE = 14
- static var PSM_INIT = 15
- static var PSM_CFG = 16
- static var PSM_ENABLE = 17
- static var TEST = 69
-end
+# ------------------------------------------------------- #
+# Procedures #
+# ------------------------------------------------------- #
-class NBIoTDriver
- static var MAX_RETRIES = 10
-
- var ser
- var state
- var rsp_awaiting
- var payload_awaiting
+class NBIoTCommand
+ var cmd
+ var rsp
var retries
- var request_queue
- var request
- var mqtt_connection
- var psm_eint
+ var done
- def init(rx, tx, psm_eint)
- self.state = NBIoTDriverState.PSM_DISABLE
- self.rsp_awaiting = false
- self.retries = self.MAX_RETRIES
- self.request_queue = []
- self.request = nil
- self.mqtt_connection = nil
- self.psm_eint = psm_eint
-
- self.ser = serial(rx, tx, 115200, serial.SERIAL_8N1)
- end
-
- def next_state(state)
- self.state = state
- self.retries = self.MAX_RETRIES
- end
-
- def queue_request(request)
- self.request_queue.push(request)
- end
-
- def finish_request(payload)
- if self.request.callback != nil
- self.request.callback(payload)
- end
-
- self.request = nil
- end
-
- def set_system_time(rsp)
- var rsp_args = string.split(rsp, '\"')
- var timestamp = nil
-
- for rsp_arg : rsp_args
- timestamp = tasmota.strptime(rsp_arg, "%y/%m/%d,%H:%M:%S")
-
- if timestamp != nil
- break
- end
- end
-
- tasmota.cmd('time ' + str(timestamp['epoch'] + 3600)) # UTC + 1
+ def init(cmd, rsp, retries)
+ self.cmd = cmd
+ self.rsp = rsp
+ self.retries = retries
+ self.done = false
end
+end
- def build_mqtt_open_cmd()
- var cmd = 'AT+QMTOPEN=0,\"%s\",%s\r\n'
-
- return string.format(cmd, self.request.host, str(self.request.port))
+class NBIoTProcedure
+ var ser
+ var request
+ var debug
+ var done
+ var aborted
+ var rsp_awaiting
+ var cmd_in_process
+
+ def init(ser, request)
+ self.ser = ser
+ self.request = request
+ self.debug = false
+ self.done = false
+ self.aborted = false
+ self.rsp_awaiting = false
+ self.cmd_in_process = nil
end
- def build_mqtt_conn_cmd()
- var client_id = 'nb-iot' # todo: change
- var cmd = 'AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n'
-
- return string.format(cmd, client_id, self.request.username, self.request.password)
+ def is_done()
+ return self.done
end
- def build_mqtt_pub_cmd()
- var cmd = 'AT+QMTPUB=0,0,0,0,\"%s\",\"%s\"\r\n'
-
- return string.format(cmd, self.request.topic, self.request.payload)
+ def is_aborted()
+ return self.aborted
end
- def send_cmd(cmd)
- self.ser.write(bytes().fromstring(cmd))
+ def send_cmd()
+ self.ser.write(bytes().fromstring(self.cmd_in_process.cmd))
self.rsp_awaiting = true
- tasmota.log(string.format('NBT: Sending command \'%s\'', string.replace(cmd, '\r\n', '')), 2)
+ tasmota.log(string.format('NBT: Sending command \'%s\'', string.replace(self.cmd_in_process.cmd, '\r\n', '')), 2)
end
- def rsp_contains_msg(msg, return_rsp)
+ def read_rsp_contains_expected_rsp(return_rsp)
if self.rsp_awaiting
var rsp = self.ser.read().asstring()
- print(rsp)
+ if self.debug
+ print(rsp)
+ end
- if string.find(rsp, msg) >= 0
+ if string.find(rsp, self.cmd_in_process.rsp) >= 0
self.rsp_awaiting = false
- tasmota.log(string.format('NBT: Received \'%s\'', msg), 2)
+ tasmota.log(string.format('NBT: Received \'%s\'', self.cmd_in_process.rsp), 2)
if return_rsp
return rsp
@@ -233,8 +189,8 @@ class NBIoTDriver
return true
else
- if self.retries > 0
- self.retries -= 1
+ if self.cmd_in_process.retries > 0
+ self.cmd_in_process.retries -= 1
end
end
end
@@ -246,158 +202,169 @@ class NBIoTDriver
end
end
- def fetch_rsp_if_contains_msg_or_send(msg, cmd)
- var rsp = self.rsp_contains_msg(msg, true)
+ def fetch_read_rsp_if_contains_expected_rsp_or_send()
+ var rsp = self.read_rsp_contains_expected_rsp(true)
if rsp != nil
return rsp
else
- self.send_cmd(cmd)
+ self.send_cmd()
return nil
end
end
- def rsp_contains_msg_or_send(msg, cmd)
- if self.rsp_contains_msg(msg, false)
+ def read_rsp_contains_expected_rsp_or_send()
+ if self.read_rsp_contains_expected_rsp(false)
return true
else
- self.send_cmd(cmd)
+ self.send_cmd()
return false
end
end
+ def retries_exceeded()
+ return self.cmd_in_process.retries < 1
+ end
+
+ def execute() end
+end
+
+class NBIoTDebugProcedure : NBIoTProcedure
+ var cmd_debug
+
+ def init(ser, request)
+ super(self).init(ser)
+
+ self.debug = true
+ self.cmd_debug = NBIoTCommand(request.cmd + '\r\n', request.rsp, request.retries)
+ end
+
+ def execute()
+ if self.cmd_in_process == nil
+ self.cmd_in_process = self.cmd_debug
+ end
+
+ self.done = self.read_rsp_contains_expected_rsp_or_send()
+ self.aborted = self.retries_exceeded()
+ end
+end
+
+class NBIoTResetProcedure : NBIoTProcedure
+ var cmd_reset
+
+ def init(ser)
+ super(self).init(ser)
+
+ self.cmd_reset = NBIoTCommand('AT+QRST=1\r\n', 'RDY', 0)
+ end
+
+ def execute()
+ if self.cmd_in_process == nil
+ self.cmd_in_process = self.cmd_reset
+ end
+
+ self.done = self.read_rsp_contains_expected_rsp_or_send()
+ end
+end
+
+# ------------------------------------------------------- #
+# Driver State #
+# ------------------------------------------------------- #
+
+class NBIoTDriverState
+ static var IDLE = 0
+ static var RESET = 1
+ static var READY = 2
+ static var BUSY = 3
+end
+
+# ------------------------------------------------------- #
+# Driver #
+# ------------------------------------------------------- #
+
+class NBIoTDriver
+ var ser
+ var psm_eint
+ var state
+ var request_queue
+ var procedure
+
+ def init(rx, tx, psm_eint)
+ self.ser = serial(rx, tx, 115200, serial.SERIAL_8N1)
+ self.psm_eint = psm_eint
+
+ self.state = NBIoTDriverState.RESET
+ self.request_queue = []
+ self.procedure = nil
+ end
+
+ def queue_request(request)
+ self.request_queue.push(request)
+ end
+
+ def init_procedure(procedure)
+ self.procedure = procedure
+ end
+
+ def finish_procedure(done)
+ if done && self.procedure.request != nil && self.procedure.request.callback != nil
+ self.procedure.request.callback()
+ end
+
+ self.procedure = nil
+ end
+
+ def next_state(state)
+ tasmota.log(string.format('NBT: Transitioning from state %i to %i', self.state, state), 2)
+
+ self.state = state
+ end
+
def every_second()
if self.state == NBIoTDriverState.IDLE
return
- # ---- retries exceeded ---- #
- elif self.retries < 1 && self.request != nil
- self.request = nil
- self.mqtt_connection = nil
- self.next_state(NBIoTDriverState.RESET)
-
- tasmota.log('NBT: Maximum number of retries exceeded, skipping request', 2)
- # ---- disable power saving mode ---- #
- elif self.state == NBIoTDriverState.PSM_DISABLE
- gpio.digital_write(self.psm_eint, 1)
-
- if self.rsp_contains_msg_or_send('OK', 'AT+QSCLK=0\r\n')
- self.next_state(NBIoTDriverState.RESET)
- end
- # ---- reset module ---- #
elif self.state == NBIoTDriverState.RESET
- if self.rsp_contains_msg_or_send('RDY', 'AT+QRST=1\r\n')
+ if self.procedure == nil
+ self.init_procedure(NBIoTResetProcedure(self.ser))
+ end
+
+ self.procedure.execute()
+
+ if self.procedure.is_done()
self.next_state(NBIoTDriverState.READY)
end
- # ---- ready for request ---- #
elif self.state == NBIoTDriverState.READY
if self.request_queue.size() > 0
- self.request = self.request_queue[0]
+ var request = self.request_queue[0]
self.request_queue.remove(0)
- if self.request.type == NBIoTRequestType.NTP
- self.next_state(NBIoTDriverState.NTP_SYNC)
- elif self.request.type == NBIoTRequestType.MQTT
- var mqtt_connection = NBIoTMQTTConnection(self.request.host,
- self.request.port,
- self.request.username,
- self.request.password)
-
- if self.mqtt_connection != nil && self.mqtt_connection.equals(mqtt_connection)
- self.next_state(NBIoTDriverState.MQTT_CHECK_CONN)
- elif self.mqtt_connection != nil
- self.mqtt_connection = mqtt_connection
- self.next_state(NBIoTDriverState.MQTT_CLOSE)
- else
- self.mqtt_connection = mqtt_connection
- self.next_state(NBIoTDriverState.MQTT_OPEN)
- end
- elif self.request.type == NBIoTRequestType.TEST
- self.next_state(NBIoTDriverState.TEST)
- elif self.request.type == NBIoTRequestType.PSM
- if self.request.value
- self.next_state(NBIoTDriverState.PSM_INIT)
- else
- self.next_state(NBIoTDriverState.PSM_DISABLE)
- end
- else
- self.finish_request()
+ var procedure = nil
+
+ if request.request_type == NBIoTRequestType.DEBUG
+ procedure = NBIoTDebugProcedure(self.ser, request)
+ elif
+ tasmota.log(string.format('NBT: Request with type %i not supported, discarding request', request.request_type), 2)
end
- end
- elif self.state == NBIoTDriverState.TEST
- if self.rsp_contains_msg_or_send(self.request.msg, self.request.cmd + '\r\n')
- self.next_state(NBIoTDriverState.READY)
- end
- # ---- fetch time from ntp server ---- #
- elif self.state == NBIoTDriverState.NTP_SYNC
- var rsp = self.fetch_rsp_if_contains_msg_or_send('+QNTP: 0', 'AT+QNTP=1,\"0.at.pool.ntp.org\"\r\n')
- if rsp != nil
- self.set_system_time(rsp)
- self.next_state(NBIoTDriverState.READY)
- end
- elif self.state == NBIoTDriverState.TEST
- if self.rsp_contains_msg_or_send(self.request.msg, self.request.cmd + '\r\n')
- self.next_state(NBIoTDriverState.READY)
- end
- # ---- punlish mqtt message ---- #
- elif self.state == NBIoTDriverState.MQTT_CHECK_CONN
- if self.retries == 1
- self.mqtt_connection = nil
-
- self.next_state(NBIoTDriverState.MQTT_CLOSE)
- elif self.rsp_contains_msg_or_send('+QMTCONN: 0,3', 'AT+QMTCONN?\r\n')
- self.next_state(NBIoTDriverState.MQTT_PUBLISH)
- end
- elif self.state == NBIoTDriverState.MQTT_OPEN
- if self.rsp_contains_msg_or_send('+QMTOPEN: 0,0', self.build_mqtt_open_cmd())
- self.next_state(NBIoTDriverState.MQTT_CONNECT)
- end
- elif self.state == NBIoTDriverState.MQTT_CONNECT
- if self.rsp_contains_msg_or_send('+QMTCONN: 0,0,0', self.build_mqtt_conn_cmd())
- self.next_state(NBIoTDriverState.MQTT_PUBLISH)
+ if procedure != nil
+ self.init_procedure(procedure)
+ self.next_state(NBIoTDriverState.BUSY)
+ end
end
- elif self.state == NBIoTDriverState.MQTT_PUBLISH
- if self.rsp_contains_msg_or_send('+QMTPUB: 0,0,0', self.build_mqtt_pub_cmd())
- self.finish_request()
+ elif self.state == NBIoTDriverState.BUSY
+ if self.procedure.is_aborted() || self.procedure.is_done()
+ if self.procedure.is_aborted()
+ var cmd = string.replace(self.procedure.cmd_in_process.cmd, '\r\n', '')
- self.next_state(NBIoTDriverState.READY)
- end
- elif self.state == NBIoTDriverState.MQTT_CLOSE
- if self.rsp_contains_msg_or_send('+QMTCLOSE: 0,0', 'AT+QMTCLOSE=0\r\n')
- if self.request != nil
- self.next_state(NBIoTDriverState.MQTT_OPEN)
- else
- self.next_state(NBIoTDriverState.READY)
+ tasmota.log(string.format('NBT: Exceeded retries at command %s, discarding request', cmd, 2)
end
- end
- # ---- send coap request ---- #
- elif self.state == NBIoTDriverState.COAP_SET_OPTIONS
- if self.rsp_contains_msg_or_send('OK', 'AT+QCOAPOPTION=1,11,\"test\"\r\n')
- self.next_state(NBIoTDriverState.COAP_SEND)
- end
- elif self.state == NBIoTDriverState.COAP_SEND
- if self.rsp_contains_msg_or_send('OK', 'AT+QCOAPSEND=1,0,\"37.120.174.40\",5683,0\r\n')
- self.next_state(NBIoTDriverState.COAP_RECEIVE)
- end
- elif self.state == NBIoTDriverState.COAP_RECEIVE
- var msg = self.ser.read().asstring()
- print(msg)
- # ---- enable power saving mode ---- #
- elif self.state == NBIoTDriverState.PSM_INIT
- if self.rsp_contains_msg_or_send('OK', 'AT+CEREG=5\r\n')
- self.next_state(NBIoTDriverState.PSM_CFG)
- end
- elif self.state == NBIoTDriverState.PSM_CFG
- if self.rsp_contains_msg_or_send('OK', 'AT+CPSMS=1,,,\"11100010\",\"00000001\"\r\n')
- self.next_state(NBIoTDriverState.PSM_ENABLE)
- end
- elif self.state == NBIoTDriverState.PSM_ENABLE
- gpio.digital_write(self.psm_eint, 0)
- if self.rsp_contains_msg_or_send('OK', 'AT+QSCLK=1\r\n')
- self.next_state(NBIoTDriverState.IDLE)
+ self.finish_procedure(self.procedure.is_done())
+ self.next_state(NBIoTDriverState.READY)
+ else
+ self.procedure.execute()
end
else
tasmota.log('NBT: Invalid driver state, stopping driver', 2)
@@ -406,6 +373,10 @@ class NBIoTDriver
end
end
+# ------------------------------------------------------- #
+# nbiot module #
+# ------------------------------------------------------- #
+
var nbiot = module('nbiot')
nbiot.init = def (m)
@@ -466,8 +437,8 @@ nbiot.init = def (m)
end
end
- def test(msg, cmd)
- var request = NBIoTTestRequest(msg, cmd)
+ def debug_send(cmd, rsp, retries)
+ var request = NBIoTDebugRequest(cmd, rsp, retries)
self._driver.queue_request(request)
end