diff --git a/src/driver/nbiot.be b/src/driver/nbiot.be index 000e6a9df474dd66f129d1f47b8eeb0bbad17864..a7b350299f015cb6ac2fb3bdb537209b8e746f00 100644 --- a/src/driver/nbiot.be +++ b/src/driver/nbiot.be @@ -10,8 +10,9 @@ class NBIoTRequestType static var MQTT_PUB = 2 static var MQTT_DISC = 3 static var COAP = 4 - static var NTP = 5 - static var PSM_ENABLE = 6 + static var HTTP = 5 + static var NTP = 6 + static var PSM_ENABLE = 7 end class NBIoTRequest @@ -68,18 +69,32 @@ end class NBIoTCOAPRequest : NBIoTRequest var host var port - var path - var query + var paths + var method + var queries var topic var payload - def init(host, port, path, method, query, payload, callback) + def init(host, port, paths, method, queries, payload, callback) super(self).init(NBIoTRequestType.COAP, callback) self.host = host self.port = port - self.path = path + self.paths = paths + self.method = method + self.queries = queries + self.payload = payload + end +end + +class NBIoTHTTPRequest : NBIoTRequest + var url + var method + var payload + + def init(url, method, payload, callback) + super(self).init(NBIoTRequestType.HTTP, callback) + self.url = url self.method = method - self.query = query self.payload = payload end end @@ -104,8 +119,56 @@ class NBIoTCommand end end +class NBIoTReadDriver + var ser + var buf + var flush + var printed + + def fetch_and_flush() + self.flush = true + self.printed = false + return self.buf + end + + def fast_loop() + if self.flush + self.buf = '' + self.flush = false + end + + var bytes_available = self.ser.available() + + if (size(self.buf) + bytes_available) >= 2048 + self.buf = self.buf[(size(self.buf) + bytes_available) - 2048 ..] + end + + if bytes_available != 0 + self.buf += self.ser.read().asstring() + end + + # todo remove + if string.find(self.buf, 'xcx') >= 0 && !self.printed + tasmota.log('------time stop------', 2) # todo remove + # tasmota.log('buf size: ' + str(size(self.buf)), 2) + # tasmota.log(self.buf, 2) + self.printed = true + end + end + + def init(ser) + self.ser = ser + self.buf = '' + self.flush = false + self.printed = false + + tasmota.add_fast_loop(/-> self.fast_loop()) # todo deactivate when psm enabled + end +end + class NBIoTProcedure var ser + var read_drv var request var response var debug @@ -114,8 +177,9 @@ class NBIoTProcedure var rsp_awaiting var cmd_in_process - def init(ser, request) + def init(ser, read_drv, request) self.ser = ser + self.read_drv = read_drv self.request = request self.response = nil self.debug = false @@ -134,20 +198,27 @@ class NBIoTProcedure end def send_cmd() - self.ser.write(bytes().fromstring(self.cmd_in_process.cmd)) - self.rsp_awaiting = true + if self.cmd_in_process.cmd != nil + self.ser.write(bytes().fromstring(self.cmd_in_process.cmd)) - tasmota.log(string.format('NBT: Sending command \'%s\'', string.replace(self.cmd_in_process.cmd, '\r\n', '')), 2) + tasmota.log(string.format('NBT: Sending command \'%s\'', string.replace(self.cmd_in_process.cmd, '\r\n', '')), 2) + end + + self.rsp_awaiting = true end def read_rsp_contains_expected_rsp(return_rsp) if self.rsp_awaiting - var rsp = self.ser.read().asstring() + var rsp = self.read_drv.fetch_and_flush() if self.debug print(rsp) end + if string.find(rsp, 'OK') >= 0 + tasmota.log('------time start------', 2) # todo remove + end + if string.find(rsp, self.cmd_in_process.rsp) >= 0 self.rsp_awaiting = false @@ -204,8 +275,8 @@ end class NBIoTDebugProcedure : NBIoTProcedure var cmd_debug - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) self.debug = true self.cmd_debug = NBIoTCommand(request.cmd + '\r\n', request.rsp, request.retries) @@ -225,8 +296,8 @@ class NBIoTResetProcedure : NBIoTProcedure var cmd_reset var cmd_at - def init(ser) - super(self).init(ser) + def init(ser, read_drv) + super(self).init(ser, read_drv) self.cmd_reset = NBIoTCommand('AT+QRST=1\r\n', 'RDY', 10) self.cmd_at = NBIoTCommand('AT\r\n', 'OK') @@ -252,8 +323,8 @@ class NBIoTEnablePSMProcedure : NBIoTProcedure var cmd_enable_psm var cmd_enable_sleep - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) self.cmd_register = NBIoTCommand('AT+CEREG=5\r\n', 'OK', 10) self.cmd_enable_psm = NBIoTCommand('AT+CPSMS=1,,,\"01011111\",\"00000001\"\r\n', 'OK', 10) @@ -282,8 +353,8 @@ class NBIoTDisablePSMProcedure : NBIoTProcedure var cmd_disable_psm var cmd_disable_sleep - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv) + super(self).init(ser, read_drv) self.cmd_register = NBIoTCommand('AT+CEREG=1\r\n', 'OK') self.cmd_disable_psm = NBIoTCommand('AT+CPSMS=0\r\n', 'OK') @@ -309,16 +380,14 @@ class NBIoTMQTTConnectProcedure : NBIoTProcedure var cmd_open var cmd_conn - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) var cmd = 'AT+QMTOPEN=0,\"%s\",%s\r\n' - self.cmd_open = NBIoTCommand(string.format(cmd, self.request.host, str(self.request.port)), '+QMTOPEN: 0,0', 15) + self.cmd_open = NBIoTCommand(string.format(cmd, request.host, str(request.port)), '+QMTOPEN: 0,0', 15) cmd = 'AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n' - self.cmd_conn = NBIoTCommand(string.format(cmd, self.request.client, self.request.username, self.request.password), - '+QMTCONN: 0,0,0', - 15) + self.cmd_conn = NBIoTCommand(string.format(cmd, request.client, request.username, request.password), '+QMTCONN: 0,0,0', 15) end def execute() @@ -340,13 +409,13 @@ class NBIoTMQTTPublishProcedure : NBIoTProcedure var cmd_conn var cmd_pub - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) self.cmd_conn = NBIoTCommand('AT+QMTCONN?\r\n', '+QMTCONN: 0,3', 10) var cmd = 'AT+QMTPUB=0,0,0,0,\"%s\",\"%s\"\r\n' - self.cmd_pub = NBIoTCommand(string.format(cmd, self.request.topic, self.request.payload), '+QMTPUB: 0,0,0', 10) + self.cmd_pub = NBIoTCommand(string.format(cmd, request.topic, request.payload), '+QMTPUB: 0,0,0', 10) end def execute() @@ -367,8 +436,8 @@ end class NBIoTMQTTDisconnectProcedure : NBIoTProcedure var cmd_close - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) self.cmd_close = NBIoTCommand('AT+QMTCLOSE=0\r\n', '+QMTCLOSE: 0,0', 10) end @@ -386,8 +455,8 @@ end class NBIoTNTPProcedure : NBIoTProcedure var cmd_ntp - def init(ser, request) - super(self).init(ser, request) + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) self.cmd_ntp = NBIoTCommand('AT+QNTP=1,\"0.at.pool.ntp.org\"\r\n', '+QNTP: 0', 10) end @@ -412,7 +481,7 @@ class NBIoTNTPProcedure : NBIoTProcedure self.cmd_in_process = self.cmd_ntp end - var rsp = self.fetch_read_rsp_if_contains_expected_rsp_or_send('+QNTP: 0', 'AT+QNTP=1,\"0.at.pool.ntp.org\"\r\n') + var rsp = self.fetch_read_rsp_if_contains_expected_rsp_or_send() if rsp != nil self.response = self.extract_timestamp(rsp) @@ -423,6 +492,124 @@ class NBIoTNTPProcedure : NBIoTProcedure end end +class NBIoTCOAPGetProcedure : NBIoTProcedure + var cmd_create + var cmd_url + var cmd_send + var cmd_del + + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) + + self.cmd_create = NBIoTCommand('AT+QCOAPCREATE=1,0\r\n', 'OK', 10) + + var cmd = string.format('AT+QCOAPOPTION=%i', request.paths.size() + request.queries.size()) + + for path : request.paths + cmd += string.format(',11,\"%s\"', path) + end + + for query : request.queries + cmd += string.format(',11,\"%s\"', query) + end + + cmd += '\r\n' + + self.cmd_url = NBIoTCommand(cmd, 'OK', 10) + + cmd = 'AT+QCOAPSEND=0,1,\"%s\",%s,0\r\n' + self.cmd_send = NBIoTCommand(string.format(cmd, request.host, str(request.port)), '+QCOAPSEND: 0', 1) + self.cmd_del = NBIoTCommand('AT+QCOAPDEL\r\n', 'OK', 10) + end + + def execute() + if self.cmd_in_process == nil + self.cmd_in_process = self.cmd_create + end + + if self.cmd_in_process == self.cmd_create && self.read_rsp_contains_expected_rsp_or_send() + self.cmd_in_process = self.cmd_url + elif self.cmd_in_process == self.cmd_url && self.read_rsp_contains_expected_rsp_or_send() + self.cmd_in_process = self.cmd_send + + if !self.done + self.read_rsp_contains_expected_rsp_or_send() + self.ser.flush() + end + + self.done = true + end + + self.aborted = self.retries_exceeded() + end +end + +class NBIoTHTTPGetProcedure : NBIoTProcedure + var cmd_url + var cmd_input + var cmd_get + var cmd_wait + var cmd_read + var payload_size + + def init(ser, read_drv, request) + super(self).init(ser, read_drv, request) + + var url = request.url + + if size(url) > 0 && url[size(url)-1] != '/' + url += '/' + end + + self.cmd_url = NBIoTCommand(string.format('AT+QHTTPURL=%i,15\r\n', size(url)), '>', 30) + self.cmd_input = NBIoTCommand(string.format('%s\r\n', url), 'OK', 30) + self.cmd_get = NBIoTCommand('AT+QHTTPGET=15\r\n', 'OK', 50) + self.cmd_wait = NBIoTCommand(nil, '+QHTTPGET: 0,', 100) + self.cmd_read = NBIoTCommand('AT+QHTTPREAD=256\r\n', '+QHTTPREAD:', 100) + + self.payload_size = 0 + self.response = '' + end + + def execute() + if self.cmd_in_process == nil + self.cmd_in_process = self.cmd_url + end + + if self.cmd_in_process == self.cmd_url && self.read_rsp_contains_expected_rsp_or_send() + self.cmd_in_process = self.cmd_input + elif self.cmd_in_process == self.cmd_input && self.read_rsp_contains_expected_rsp_or_send() + self.cmd_in_process = self.cmd_get + elif self.cmd_in_process == self.cmd_get && self.read_rsp_contains_expected_rsp_or_send() + self.cmd_in_process = self.cmd_wait + elif self.cmd_in_process == self.cmd_wait + var rsp = self.fetch_read_rsp_if_contains_expected_rsp_or_send() + + if rsp != nil + var params = string.split(rsp, ',') + var payload_size = params[params.size()-1] + + self.payload_size = int(string.replace(payload_size, '\r\n', '')) + 256 + self.cmd_in_process = self.cmd_read + end + elif self.cmd_in_process == self.cmd_read + var rsp = self.fetch_read_rsp_if_contains_expected_rsp_or_send() + + if rsp != nil + self.response += rsp # todo filter + # print('rsp ' + rsp) + self.done = size(self.response) > self.payload_size + + if self.done + # print(self.response) + end + end + end + + self.aborted = self.retries_exceeded() + end +end + # ------------------------------------------------------- # # Driver State # # ------------------------------------------------------- # @@ -434,6 +621,22 @@ class NBIoTDriverState static var DISABLE_PSM = 3 static var READY = 4 static var BUSY = 5 + + static def tostring(state) + if state == NBIoTDriverState.INIT + return 'INIT' + elif state == NBIoTDriverState.PSM + return 'PSM' + elif state == NBIoTDriverState.RESET + return 'RESET' + elif state == NBIoTDriverState.DISABLE_PSM + return 'DISABLE_PSM' + elif state == NBIoTDriverState.READY + return 'READY' + elif state == NBIoTDriverState.BUSY + return 'BUSY' + end + end end # ------------------------------------------------------- # @@ -443,6 +646,7 @@ end class NBIoTDriver var ser var psm_eint + var read_drv var state var request_queue var procedure @@ -452,6 +656,8 @@ class NBIoTDriver self.ser = serial(rx, tx, 115200, serial.SERIAL_8N1) self.psm_eint = psm_eint + self.read_drv = NBIoTReadDriver(self.ser) + self.state = NBIoTDriverState.INIT self.request_queue = [] self.procedure = nil @@ -497,7 +703,10 @@ class NBIoTDriver end def next_state(state) - tasmota.log(string.format('NBT: Transitioning from state %i to %i', self.state, state), 2) + var current_state = NBIoTDriverState.tostring(self.state) + var next_state = NBIoTDriverState.tostring(state) + + tasmota.log(string.format('NBT: Transitioning from state %s to %s', current_state, next_state), 2) self.state = state end @@ -520,7 +729,7 @@ class NBIoTDriver tasmota.set_timer(150, /-> self.set_psm_pin_high()) end - def every_second() + def every_250ms() if self.state == NBIoTDriverState.PSM if self.psm_disabled self.next_state(NBIoTDriverState.INIT) @@ -532,7 +741,7 @@ class NBIoTDriver tasmota.set_timer(150, /-> self.wake_up()) elif self.state == NBIoTDriverState.RESET if self.procedure == nil - self.start_procedure(NBIoTResetProcedure(self.ser)) + self.start_procedure(NBIoTResetProcedure(self.ser, self.read_drv)) end self.procedure.execute() @@ -550,7 +759,7 @@ class NBIoTDriver end elif self.state == NBIoTDriverState.DISABLE_PSM if self.procedure == nil - self.start_procedure(NBIoTDisablePSMProcedure(self.ser)) + self.start_procedure(NBIoTDisablePSMProcedure(self.ser, self.read_drv)) end self.procedure.execute() @@ -568,17 +777,21 @@ class NBIoTDriver var procedure = nil if request.request_type == NBIoTRequestType.DEBUG - procedure = NBIoTDebugProcedure(self.ser, request) + procedure = NBIoTDebugProcedure(self.ser, self.read_drv, request) elif request.request_type == NBIoTRequestType.MQTT_CONN - procedure = NBIoTMQTTConnectProcedure(self.ser, request) + procedure = NBIoTMQTTConnectProcedure(self.ser, self.read_drv, request) elif request.request_type == NBIoTRequestType.MQTT_PUB - procedure = NBIoTMQTTPublishProcedure(self.ser, request) + procedure = NBIoTMQTTPublishProcedure(self.ser, self.read_drv, request) elif request.request_type == NBIoTRequestType.MQTT_DISC - procedure = NBIoTMQTTDisconnectProcedure(self.ser, request) + procedure = NBIoTMQTTDisconnectProcedure(self.ser, self.read_drv, request) elif request.request_type == NBIoTRequestType.NTP - procedure = NBIoTNTPProcedure(self.ser, request) + procedure = NBIoTNTPProcedure(self.ser, self.read_drv, request) + elif request.request_type == NBIoTRequestType.COAP && request.method == 'GET' + procedure = NBIoTCOAPGetProcedure(self.ser, self.read_drv, request) + elif request.request_type == NBIoTRequestType.HTTP && request.method == 'GET' + procedure = NBIoTHTTPGetProcedure(self.ser, self.read_drv, request) elif request.request_type == NBIoTRequestType.PSM_ENABLE - procedure = NBIoTEnablePSMProcedure(self.ser, request) + procedure = NBIoTEnablePSMProcedure(self.ser, self.read_drv, request) else tasmota.log(string.format('NBT: Request with type %i not supported, discarding request', request.request_type), 2) end @@ -675,21 +888,27 @@ nbiot.init = def (m) return self._driver.queue_request(request) end - def coap_get(host, port, path, query, callback) - if type(path) != type([]) || type(query) != type([]) + def http_get(url, callback) + var request = NBIoTHTTPRequest(url, 'GET', nil, callback) + + return self._driver.queue_request(request) + end + + def coap_get(host, port, paths, queries, callback) + if type(paths) != type([]) || type(queries) != type([]) return false else - var request = NBIoTCOAPRequest(host, port, path, 'GET', query, nil, callback) + var request = NBIoTCOAPRequest(host, port, paths, 'GET', queries, nil, callback) return self._driver.queue_request(request) end end - def coap_post(host, port, path, payload, callback) - if type(path) != type([]) + def coap_post(host, port, paths, payload, callback) + if type(paths) != type([]) return false else - var request = NBIoTCOAPRequest(host, port, path, 'POST', [], payload, callback) + var request = NBIoTCOAPRequest(host, port, paths, 'POST', [], payload, callback) return self._driver.queue_request(request) end