Skip to content
Snippets Groups Projects
Commit 2d3f7bf5 authored by Strasser, Andreas's avatar Strasser, Andreas
Browse files

restructured driver implementation

parent d13c8ade
No related branches found
No related tags found
No related merge requests found
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
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
var done
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
def init(cmd, rsp, retries)
self.cmd = cmd
self.rsp = rsp
self.retries = retries
self.done = false
end
tasmota.cmd('time ' + str(timestamp['epoch'] + 3600)) # UTC + 1
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()
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 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)
def retries_exceeded()
return self.cmd_in_process.retries < 1
end
def execute() end
end
class NBIoTDebugProcedure : NBIoTProcedure
var cmd_debug
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)
def init(ser, request)
super(self).init(ser)
if self.rsp_contains_msg_or_send('OK', 'AT+QSCLK=0\r\n')
self.next_state(NBIoTDriverState.RESET)
self.debug = true
self.cmd_debug = NBIoTCommand(request.cmd + '\r\n', request.rsp, request.retries)
end
# ---- reset module ---- #
elif self.state == NBIoTDriverState.RESET
if self.rsp_contains_msg_or_send('RDY', 'AT+QRST=1\r\n')
self.next_state(NBIoTDriverState.READY)
def execute()
if self.cmd_in_process == nil
self.cmd_in_process = self.cmd_debug
end
# ---- ready for request ---- #
elif self.state == NBIoTDriverState.READY
if self.request_queue.size() > 0
self.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)
self.done = self.read_rsp_contains_expected_rsp_or_send()
self.aborted = self.retries_exceeded()
end
else
self.finish_request()
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
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)
def execute()
if self.cmd_in_process == nil
self.cmd_in_process = self.cmd_reset
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)
self.done = self.read_rsp_contains_expected_rsp_or_send()
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)
# ------------------------------------------------------- #
# 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
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)
def init_procedure(procedure)
self.procedure = procedure
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)
def finish_procedure(done)
if done && self.procedure.request != nil && self.procedure.request.callback != nil
self.procedure.request.callback()
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()
self.next_state(NBIoTDriverState.READY)
self.procedure = nil
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)
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
elif self.state == NBIoTDriverState.RESET
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
# ---- 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)
elif self.state == NBIoTDriverState.READY
if self.request_queue.size() > 0
var request = self.request_queue[0]
self.request_queue.remove(0)
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
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)
if procedure != nil
self.init_procedure(procedure)
self.next_state(NBIoTDriverState.BUSY)
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)
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', '')
tasmota.log(string.format('NBT: Exceeded retries at command %s, discarding request', cmd, 2)
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment