get cellular setup working
This commit is contained in:
parent
a7ff402b77
commit
6507956a83
|
@ -7,7 +7,7 @@ from lib.gps.micropyGPS import MicropyGPS
|
||||||
from lib.gps.position import Position
|
from lib.gps.position import Position
|
||||||
from lib.interval_tracker import interval_tracker
|
from lib.interval_tracker import interval_tracker
|
||||||
from lib.logging import logger, LogLevel
|
from lib.logging import logger, LogLevel
|
||||||
from lib.runtime import timeout, uTimeoutError
|
from lib.runtime import runtime_timeout, uTimeoutError
|
||||||
from lib.ttime import initialize_rtc, unix_timestamp
|
from lib.ttime import initialize_rtc, unix_timestamp
|
||||||
|
|
||||||
PIN_GPS_POWER = 12
|
PIN_GPS_POWER = 12
|
||||||
|
@ -43,7 +43,7 @@ async def get_position():
|
||||||
# TODO: fix altitude always being 0
|
# TODO: fix altitude always being 0
|
||||||
gps_power_pin.value(1) # Always make sure the GPS is on.
|
gps_power_pin.value(1) # Always make sure the GPS is on.
|
||||||
|
|
||||||
@timeout(TIMEOUT_GNSS_LOCATION)
|
@runtime_timeout(TIMEOUT_GNSS_LOCATION)
|
||||||
async def get_loc():
|
async def get_loc():
|
||||||
timestamp = unix_timestamp()
|
timestamp = unix_timestamp()
|
||||||
gnrmc = await read_gps_uart('$GNRMC')
|
gnrmc = await read_gps_uart('$GNRMC')
|
||||||
|
|
|
@ -11,5 +11,5 @@ class LogLevel:
|
||||||
def logger(msg: str, level: LogLevel = LogLevel.info, source: str = None):
|
def logger(msg: str, level: LogLevel = LogLevel.info, source: str = None):
|
||||||
s = ''
|
s = ''
|
||||||
if source:
|
if source:
|
||||||
s = f'[{source}] - '
|
s = f'[{source.upper()}] - '
|
||||||
print(f'{s}{level} -- {time.ticks_ms() / 1000} -- {msg}')
|
print(f'{s}{level} -- {round(time.ticks_ms() / 1000, 2)} -- {msg}')
|
||||||
|
|
|
@ -1,86 +1,97 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import gc
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import machine
|
from machine import UART, Pin
|
||||||
from machine import Pin
|
|
||||||
|
|
||||||
from config import CELLULAR_APN, CELLULAR_STARTUP_TIMEOUT, TRACCAR_HOST
|
from config import CELLULAR_STARTUP_TIMEOUT, CELLULAR_APN
|
||||||
from lib.logging import logger, LogLevel
|
from lib.logging import logger, LogLevel
|
||||||
from lib.runtime import timeout, uTimeoutError, run_with_timeout
|
from lib.runtime import runtime_timeout, uTimeoutError, run_with_timeout
|
||||||
|
|
||||||
PIN_BEE_POWER = 27
|
PIN_BEE_POWER = 27
|
||||||
PIN_BEE_UART_RXD = 35
|
PIN_BEE_UART_RXD = 35
|
||||||
PIN_BEE_UART_TXD = 2
|
PIN_BEE_UART_TXD = 2
|
||||||
|
|
||||||
|
VERBOSE_XBEE_COMM = False
|
||||||
|
DEBUG_XBEE = True
|
||||||
bee_power_pin = Pin(PIN_BEE_POWER, Pin.OUT)
|
bee_power_pin = Pin(PIN_BEE_POWER, Pin.OUT)
|
||||||
bee_power_pin.value(0)
|
bee_power_pin.value(0)
|
||||||
bee_power_pin.value(1)
|
|
||||||
|
|
||||||
|
|
||||||
class ModemErrorResponse(Exception):
|
class ModemError(Exception):
|
||||||
def __init__(self, message="Modem returned an error", code: tuple = None):
|
def __init__(self, message: str, sent_command: bytes, response: list):
|
||||||
self.command = code[0]
|
super().__init__(f'{message}: {sent_command} -> {response}')
|
||||||
self.lines = code[1]
|
|
||||||
super().__init__(f'{message}: {code}')
|
|
||||||
|
async def xb_toggle_power():
|
||||||
|
bee_power_pin.value(0)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
bee_power_pin.value(1)
|
||||||
|
|
||||||
|
|
||||||
class CellularModem:
|
class CellularModem:
|
||||||
_lock = asyncio.Lock()
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.uart = machine.UART(2, baudrate=115200, timeout=4, rx=PIN_BEE_UART_RXD, tx=PIN_BEE_UART_TXD)
|
self.uart = UART(2, baudrate=115200, timeout=4, rx=PIN_BEE_UART_RXD, tx=PIN_BEE_UART_TXD)
|
||||||
self.swriter = asyncio.StreamWriter(self.uart, {})
|
|
||||||
self.sreader = asyncio.StreamReader(self.uart)
|
|
||||||
asyncio.create_task(self.keep_alive())
|
|
||||||
|
|
||||||
async def keep_alive(self):
|
async def xb_read(self, timeout):
|
||||||
while True:
|
@runtime_timeout(timeout)
|
||||||
await asyncio.sleep(40)
|
async def inner():
|
||||||
async with self._lock:
|
if self.uart.any():
|
||||||
res = await self.send_command("AT")
|
return self.uart.read()
|
||||||
if not res:
|
return None
|
||||||
raise OSError("Modem not responding")
|
|
||||||
|
|
||||||
async def send_command(self, command, require_ok: bool = False, expecing_plus: bool = False, read_until: bytes = None, clean_lines: bool = True) -> tuple:
|
try:
|
||||||
lines = []
|
return await inner()
|
||||||
async with self._lock:
|
except uTimeoutError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def xb_purge(self):
|
||||||
while self.uart.any():
|
while self.uart.any():
|
||||||
# Clear any initial garbage
|
self.uart.read()
|
||||||
self.uart.read(1)
|
|
||||||
await self.swriter.awrite(f'{command}\r\n')
|
async def send_and_receive(self, data_to_send: bytes, read_until: any = b'OK\r\n', expected: bytes = None, timeout: int = 1000) -> list:
|
||||||
for i in range(10):
|
if not isinstance(data_to_send, bytes):
|
||||||
raw_line = await self.sreader.readline()
|
raise Exception
|
||||||
print(raw_line)
|
if read_until is not None and not isinstance(read_until, bytes):
|
||||||
if len(raw_line.decode().strip('\r\n')) > 0 and raw_line != f'{command}\r\r\n'.encode():
|
raise Exception
|
||||||
lines.append(raw_line)
|
if data_to_send.endswith(b'\r\n'):
|
||||||
if raw_line == b'ERROR\r\n':
|
raise Exception
|
||||||
raise ModemErrorResponse(code=(command, lines))
|
if read_until is not None and expected is not None:
|
||||||
if len(lines):
|
raise Exception
|
||||||
if expecing_plus:
|
|
||||||
if lines[-1].decode().startswith('+'):
|
data_to_send += b'\r\n'
|
||||||
|
|
||||||
|
if VERBOSE_XBEE_COMM:
|
||||||
|
print(f'@====> {data_to_send}')
|
||||||
|
self.uart.write(data_to_send)
|
||||||
|
|
||||||
|
# Wait and read the response
|
||||||
|
t = time.ticks_ms()
|
||||||
|
data = b''
|
||||||
|
expected_not_found = 0
|
||||||
|
while time.ticks_diff(time.ticks_ms(), t) < timeout:
|
||||||
|
read = await self.xb_read(50)
|
||||||
|
if read and read != data_to_send:
|
||||||
|
if VERBOSE_XBEE_COMM:
|
||||||
|
print(f'<====@ {read}')
|
||||||
|
data += read
|
||||||
|
if read_until is not None and data.endswith(read_until):
|
||||||
break
|
break
|
||||||
elif read_until:
|
elif expected is not None:
|
||||||
if lines[-1] == read_until:
|
if data.endswith(expected):
|
||||||
break
|
break
|
||||||
elif lines[-1] == b'OK\r\n':
|
|
||||||
break
|
|
||||||
if clean_lines:
|
|
||||||
response = tuple(x for x in [x.decode().strip('\r\n') for x in lines] if len(x))
|
|
||||||
else:
|
else:
|
||||||
response = lines
|
expected_not_found += 1
|
||||||
if require_ok:
|
if expected_not_found == 3:
|
||||||
if response[-1] != 'OK':
|
raise ModemError(f'Did not recieve expected {expected}', sent_command=data_to_send, response=data)
|
||||||
raise ModemErrorResponse(code=(command, lines))
|
return [x.decode() for x in data.replace(b'\r\r\n', b'\r\n').split(b'\r\n') if len(x)] if data else []
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
cellular = CellularModem()
|
cell_modem = CellularModem()
|
||||||
|
|
||||||
|
|
||||||
def _parse_csq(csq_value: int):
|
def parse_csq(csq_value: int):
|
||||||
if csq_value == 99:
|
if csq_value == 99:
|
||||||
return 'unknown'
|
return 'unknown (likely disconnected)'
|
||||||
elif csq_value == 31:
|
elif csq_value == 31:
|
||||||
return '51 dBm or greater'
|
return '51 dBm or greater'
|
||||||
elif csq_value == 0:
|
elif csq_value == 0:
|
||||||
|
@ -93,58 +104,13 @@ def _parse_csq(csq_value: int):
|
||||||
return 'Invalid CSQ value'
|
return 'Invalid CSQ value'
|
||||||
|
|
||||||
|
|
||||||
async def cellular_wake_modem():
|
|
||||||
i = 2
|
|
||||||
while i > 0:
|
|
||||||
resp = await cellular.send_command('AT')
|
|
||||||
if len(resp) == 1 and resp[0] == 'OK':
|
|
||||||
i -= 1
|
|
||||||
else:
|
|
||||||
logger('\n'.join(resp), source='CELL')
|
|
||||||
i += 1
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
|
|
||||||
async def cellular_wait_cpsi():
|
|
||||||
while True:
|
|
||||||
cpsi_resp = await cellular.send_command('AT+CPSI?')
|
|
||||||
try:
|
|
||||||
parts = cpsi_resp[0].lstrip('+CPSI: ').split(',')
|
|
||||||
except:
|
|
||||||
print(cpsi_resp)
|
|
||||||
raise
|
|
||||||
if parts[1] == 'Online':
|
|
||||||
return True, parts
|
|
||||||
elif parts[1] == 'Low Power Mode':
|
|
||||||
return False, parts
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
|
|
||||||
async def cellular_wait_creg():
|
|
||||||
# https://community.hologram.io/t/sim7600-in-ovms-board-t-mobile-registration-denied/4616/12
|
|
||||||
async def check():
|
|
||||||
resp = await cellular.send_command('AT+CREG?')
|
|
||||||
if len(resp) == 2 and resp[0].startswith('+CREG: '):
|
|
||||||
return resp[0] == '+CREG: 0,1' or resp[0] == '+CREG: 0,5', resp
|
|
||||||
else:
|
|
||||||
raise Exception(resp)
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while True:
|
|
||||||
ready, code = await check()
|
|
||||||
if ready:
|
|
||||||
return ready, code
|
|
||||||
i += 1
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
|
|
||||||
async def cellular_signal_strength():
|
async def cellular_signal_strength():
|
||||||
@timeout(10)
|
@runtime_timeout(10)
|
||||||
async def inner():
|
async def inner():
|
||||||
resp = await cellular.send_command('AT+CSQ')
|
resp = await cell_modem.send_and_receive(b'AT+CSQ')
|
||||||
if len(resp) == 2 and resp[0].startswith('+CSQ: '):
|
if len(resp) == 2 and resp[0].startswith('+CSQ: '):
|
||||||
s = int(resp[0].lstrip('+CSQ: ').split(',')[0])
|
s = int(resp[0].lstrip('+CSQ: ').split(',')[0])
|
||||||
return _parse_csq(s)
|
return parse_csq(s)
|
||||||
else:
|
else:
|
||||||
raise Exception(resp)
|
raise Exception(resp)
|
||||||
|
|
||||||
|
@ -157,112 +123,98 @@ async def cellular_signal_strength():
|
||||||
|
|
||||||
async def cellular_ip() -> str:
|
async def cellular_ip() -> str:
|
||||||
try:
|
try:
|
||||||
ip_resp = await cellular.send_command('AT+IPADDR')
|
ip_resp = await cell_modem.send_and_receive(b'AT+IPADDR')
|
||||||
if ip_resp[0].startswith('+IPADDR: '):
|
if ip_resp[0].startswith('+IPADDR: '):
|
||||||
return ip_resp[0].strip('+IPADDR: ')
|
return ip_resp[0].strip('+IPADDR: ')
|
||||||
except ModemErrorResponse:
|
except ModemError:
|
||||||
return '0.0.0.0'
|
return '0.0.0.0'
|
||||||
|
|
||||||
|
|
||||||
async def cellular_check_service_ready():
|
@runtime_timeout(CELLULAR_STARTUP_TIMEOUT)
|
||||||
resp = await cellular.send_command('AT+CGATT?')
|
|
||||||
if len(resp) == 2 and resp[0].startswith('+CGATT: '):
|
|
||||||
return resp[0] == '+CGATT: 1', resp[0]
|
|
||||||
else:
|
|
||||||
raise Exception(resp)
|
|
||||||
|
|
||||||
|
|
||||||
async def restart_modem():
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
bee_power_pin.value(0)
|
|
||||||
time.sleep(0.5)
|
|
||||||
bee_power_pin.value(1)
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
|
|
||||||
async def cellular_check_connected():
|
|
||||||
cpsi = await run_with_timeout(func=cellular_wait_cpsi, timeout_sec=1)
|
|
||||||
if cpsi.failure:
|
|
||||||
return False
|
|
||||||
if cpsi.result[1][0] != 'NO SERVICE':
|
|
||||||
return False
|
|
||||||
if (await cellular_ip()) == '0.0.0.0':
|
|
||||||
return False
|
|
||||||
if (await cellular_signal_strength()) == 'disconnected':
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
@timeout(CELLULAR_STARTUP_TIMEOUT)
|
|
||||||
async def start_modem():
|
async def start_modem():
|
||||||
|
await xb_toggle_power()
|
||||||
while True:
|
while True:
|
||||||
failures = -1
|
logger('Setting up modem', source='CELL')
|
||||||
try:
|
failures = 0
|
||||||
if (await run_with_timeout(func=cellular_wake_modem, timeout_sec=5)).failure:
|
|
||||||
logger('Modem wake-up timed out', source='CELL', level=LogLevel.error)
|
|
||||||
failures += 1
|
|
||||||
if failures == 11:
|
|
||||||
machine.reset()
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
|
||||||
except ModemErrorResponse:
|
|
||||||
# Sporadic ERROR responses
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
|
||||||
|
|
||||||
sim_resp = await cellular.send_command('AT+CPIN?')
|
|
||||||
if not sim_resp[0].endswith(' READY'):
|
|
||||||
msg = '\n'.join(sim_resp)
|
|
||||||
logger(f'SIM card not ready: {msg}', source='CELL', level=LogLevel.error)
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
|
||||||
|
|
||||||
cmds = ['ATE0', 'ATI']
|
|
||||||
for cmd in cmds:
|
|
||||||
while True:
|
while True:
|
||||||
init_resp = await cellular.send_command(cmd)
|
at = await cell_modem.send_and_receive(b'AT')
|
||||||
logger('\n'.join((x for x in init_resp if x != 'OK')), source='CELL', level=LogLevel.info)
|
if len(at) and at[-1] == 'OK':
|
||||||
if init_resp[-1] != 'OK':
|
logger('Modem woken up', source='CELL')
|
||||||
await asyncio.sleep(0.1)
|
break
|
||||||
else:
|
else:
|
||||||
break
|
if DEBUG_XBEE:
|
||||||
|
logger(f'Modem start failed: {at}', source='CELL', level=LogLevel.debug)
|
||||||
simcom_model_resp = await cellular.send_command('AT+SIMCOMATI')
|
failures += 1
|
||||||
model = simcom_model_resp[1]
|
if failures == 5:
|
||||||
if not model.startswith('Model:'):
|
return False
|
||||||
raise Exception
|
await asyncio.sleep(0.5)
|
||||||
if 'SIM7600' not in model:
|
|
||||||
raise Exception('Not implemented')
|
|
||||||
|
|
||||||
cpsi = await run_with_timeout(func=cellular_wait_cpsi, timeout_sec=1)
|
|
||||||
if cpsi.failure:
|
|
||||||
logger('AT+CPSI timeout', source='CELL', level=LogLevel.error)
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
|
||||||
if not cpsi.result[0]:
|
|
||||||
logger('Bad AT+CPSI status', source='CELL', level=LogLevel.error)
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
|
||||||
|
|
||||||
creg = await run_with_timeout(func=cellular_wait_creg, timeout_sec=1)
|
|
||||||
if creg.failure:
|
|
||||||
logger('AT+CREG timeout, no signal?', source='CELL', level=LogLevel.warning)
|
|
||||||
elif not creg.result[0]:
|
|
||||||
logger(f'Bad AT+CREG response: {creg.result[1]}', source='CELL', level=LogLevel.error)
|
|
||||||
await restart_modem()
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await cellular.send_command(f'AT+CGDCONT=1,"IP","{CELLULAR_APN}"', require_ok=True)
|
await cell_modem.send_and_receive(b'ATE0', expected=b'OK\r\n', read_until=None)
|
||||||
await cellular.send_command('AT+CSOCKSETPN=1', require_ok=True)
|
except ModemError as e:
|
||||||
await cellular.send_command('AT+CIPMODE=0', require_ok=True)
|
logger(f'Modem start failed: {e}', source='CELL', level=LogLevel.error)
|
||||||
await cellular.send_command('AT+CNMP=38', require_ok=True)
|
return False
|
||||||
await cellular.send_command('AT+NETOPEN', require_ok=True)
|
|
||||||
break
|
ati = await cell_modem.send_and_receive(b'ATI')
|
||||||
except ModemErrorResponse as e:
|
if not isinstance(ati, list) or ati[-1] != 'OK':
|
||||||
print(e)
|
logger(f'Modem start failed: {b"ATI"} -> {ati}', source='CELL', level=LogLevel.error)
|
||||||
await restart_modem()
|
return False
|
||||||
|
for line in ati:
|
||||||
|
if line != 'OK':
|
||||||
|
logger(line, source='CELL')
|
||||||
|
# Assuming model is SIM7600
|
||||||
|
|
||||||
|
cpin = await cell_modem.send_and_receive(b'AT+CPIN?')
|
||||||
|
if not cpin[0].endswith(': READY'):
|
||||||
|
logger('NO SIM CARD', source='CELL', level=LogLevel.error)
|
||||||
|
await asyncio.sleep(30)
|
||||||
|
return False
|
||||||
|
|
||||||
|
signal_strength = await cellular_signal_strength()
|
||||||
|
logger(f'Signal strength: {signal_strength}', source='CELL')
|
||||||
|
|
||||||
|
cpsi = await cell_modem.send_and_receive(b'AT+CPSI?')
|
||||||
|
if 'OK' not in cpsi:
|
||||||
|
logger(f'Modem start failed: {b"AT+CPSI?"} -> {cpsi}', source='CELL', level=LogLevel.error)
|
||||||
|
return False
|
||||||
|
cpsi_parts = cpsi[0].lstrip('+CPSI: ').split(',')
|
||||||
|
if cpsi_parts[0] == 'NO SERVICE':
|
||||||
|
logger('No service', source='CELL', level=LogLevel.warning)
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
return False
|
||||||
|
elif cpsi_parts[1] != 'Online':
|
||||||
|
logger(f'Modem not online: {",".join(cpsi_parts)}', source='CELL', level=LogLevel.error)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger(f'Connected to {cpsi_parts[0]}', source='CELL')
|
||||||
|
|
||||||
|
for cmd in [b'AT+CREG?', b'AT+CGREG?']:
|
||||||
|
r = await cell_modem.send_and_receive(cmd)
|
||||||
|
if len(r):
|
||||||
|
# Page 85, 185
|
||||||
|
if ' 0,2' in r[0] or ' 0,0' in r[0]:
|
||||||
|
logger(f'Modem not attached to network: {r}', source='CELL', level=LogLevel.warning)
|
||||||
|
await asyncio.sleep(30)
|
||||||
|
return False
|
||||||
|
elif ' 0,1' not in r[0] and ' 0,5' not in r[0]:
|
||||||
|
logger(f'Modem start failed: {cmd} -> {r}', source='CELL', level=LogLevel.error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
cgact = await cell_modem.send_and_receive(b'AT+CGACT?')
|
||||||
|
success = False
|
||||||
|
for line in cgact:
|
||||||
|
if '+CGACT: 1,' in line:
|
||||||
|
success = True
|
||||||
break
|
break
|
||||||
|
if not success:
|
||||||
|
logger(f'Modem start failed: {b"AT+CGACT?"} -> {cgact}', source='CELL', level=LogLevel.error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
await cell_modem.send_and_receive(f'AT+CGSOCKCONT=1,"IP","{CELLULAR_APN}"'.encode())
|
||||||
|
await cell_modem.send_and_receive(b'AT+CSOCKSETPN=1', read_until=None, expected=b'OK\r\n')
|
||||||
|
await cell_modem.send_and_receive(b'AT+CIPMODE=0', read_until=None, expected=b'OK\r\n')
|
||||||
|
await cell_modem.send_and_receive(b'AT+NETOPEN', read_until=None, expected=b'OK\r\n')
|
||||||
|
|
||||||
ip_get = await run_with_timeout(func=cellular_ip, timeout_sec=3)
|
ip_get = await run_with_timeout(func=cellular_ip, timeout_sec=3)
|
||||||
if not ip_get.failure:
|
if not ip_get.failure:
|
||||||
|
@ -270,122 +222,19 @@ async def start_modem():
|
||||||
else:
|
else:
|
||||||
logger(f'IP: 0.0.0.0', source='CELL')
|
logger(f'IP: 0.0.0.0', source='CELL')
|
||||||
|
|
||||||
logger(f'Signal strength: {await cellular_signal_strength()}', source='CELL')
|
|
||||||
logger('Modem initialized', source='CELL')
|
|
||||||
|
|
||||||
|
|
||||||
async def cellular_start_http():
|
|
||||||
if TRACCAR_HOST.startswith('http://'):
|
|
||||||
code = 'AT+HTTPINIT'
|
|
||||||
elif TRACCAR_HOST.startswith('https://'):
|
|
||||||
code = 'AT+CCHSTART'
|
|
||||||
else:
|
|
||||||
raise Exception
|
|
||||||
try:
|
|
||||||
if TRACCAR_HOST.startswith('https://'):
|
|
||||||
await cellular.send_command('AT+CSSLCFG="sslversion",0,4')
|
|
||||||
await cellular.send_command('AT+CSSLCFG="authmode",0,0')
|
|
||||||
await cellular.send_command('AT+CSSLCFG="ignorelocaltime",0,1')
|
|
||||||
await cellular.send_command('AT+CSSLCFG="enableSNI",0,1')
|
|
||||||
await cellular.send_command('AT+CCHSET=1')
|
|
||||||
await cellular.send_command('AT+CCHSSLCFG=0,0')
|
|
||||||
await cellular.send_command(code)
|
|
||||||
return True
|
return True
|
||||||
except ModemErrorResponse:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
async def start_modem_task():
|
async def start_modem_task():
|
||||||
logger('Initalizing modem', source='CELL')
|
|
||||||
|
|
||||||
async def inner():
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
if await start_modem():
|
||||||
gc.collect()
|
|
||||||
await start_modem()
|
|
||||||
except uTimeoutError:
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
continue
|
|
||||||
break
|
break
|
||||||
|
if not (await cell_modem.send_and_receive(b'AT+CPOF')):
|
||||||
|
await xb_toggle_power()
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
x = await run_with_timeout(func=inner, timeout_sec=300)
|
await asyncio.sleep(100)
|
||||||
if not x.failure:
|
# TODO: handle reconnection here
|
||||||
break
|
|
||||||
|
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
cellular.uart.write('AT+CIPOPEN=0,"UDP","64.227.105.164",10001,8000')
|
|
||||||
data = 'Hello!!'
|
|
||||||
cellular.uart.write(f'AT+CIPSEND=0,{len(data.encode())},"64.227.105.164",10001')
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
cellular.uart.write(data)
|
|
||||||
await cellular.send_command('AT+CIPCLOSE=0')
|
|
||||||
|
|
||||||
# TODO: loop to reconnect modem
|
|
||||||
|
|
||||||
|
|
||||||
@timeout(30)
|
|
||||||
async def cellular_send_http(url: str, method: str, data: str = None):
|
|
||||||
m = method.upper()
|
|
||||||
if m == 'GET':
|
|
||||||
mode = 0
|
|
||||||
elif m == 'POST':
|
|
||||||
if not data:
|
|
||||||
raise Exception
|
|
||||||
mode = 1
|
|
||||||
else:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
# for i in range(30):
|
|
||||||
# httpinit = await run_with_timeout(func=cellular_start_http, timeout_sec=5)
|
|
||||||
# if httpinit.failure:
|
|
||||||
# # logger('AT+HTTPINIT timeout', source='CELL', level=LogLevel.error)
|
|
||||||
# await restart_modem()
|
|
||||||
# continue
|
|
||||||
# if not httpinit.result:
|
|
||||||
# # logger('AT+HTTPINIT failure', source='CELL', level=LogLevel.error)
|
|
||||||
# await cellular.send_command('AT+CRESET')
|
|
||||||
# # await restart_modem()
|
|
||||||
# # break
|
|
||||||
|
|
||||||
await cellular_start_http()
|
|
||||||
|
|
||||||
if TRACCAR_HOST.startswith('http://'):
|
|
||||||
await cellular.send_command(f'AT+HTTPPARA="URL","{url}"')
|
|
||||||
_, resp = await cellular.send_command(f'AT+HTTPACTION={mode}', expecing_plus=True)
|
|
||||||
_, status_code, data_len = resp.strip('+HTTPACTION: ').split(',')
|
|
||||||
if status_code != '200':
|
|
||||||
logger(f'HTTP {m} failed: {status_code}', source='CELL', level=LogLevel.error)
|
|
||||||
return
|
|
||||||
data = await cellular.send_command(f'AT+HTTPREAD={data_len}', read_until=b'+HTTPREAD:0\r\n', clean_lines=False)
|
|
||||||
elif TRACCAR_HOST.startswith('https://'):
|
|
||||||
# HTTPS does not work.
|
|
||||||
# ModemErrorResponse: Modem returned an error: ('AT+CCHOPEN=0,"postman-echo.com",443,2', [b'ERROR\r\n'])
|
|
||||||
parts = url.replace('https://', '', 1).split('/')
|
|
||||||
host = parts.pop(0)
|
|
||||||
path = '/' + '/'.join(parts)
|
|
||||||
await cellular.send_command(f'AT+CCHOPEN=0,"{host}",443,2')
|
|
||||||
content = f"""GET {path} HTTP/1.1
|
|
||||||
Host: {host}
|
|
||||||
User-Agent: Freematics
|
|
||||||
Proxy-Connection: keep-alive
|
|
||||||
Content-Length: 0"""
|
|
||||||
await cellular.send_command(f'AT+CCHSEND=0,{len(content.encode())}')
|
|
||||||
resp = await cellular.send_command('AT+CCHRECV=0', read_until=b'+CCHRECV: 0,0\r\n')
|
|
||||||
print(resp)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
if data[0] != b'OK\r\n':
|
|
||||||
raise Exception
|
|
||||||
data = list(data)
|
|
||||||
del data[0]
|
|
||||||
del data[0]
|
|
||||||
del data[-1]
|
|
||||||
data_clean = ''.join([x.decode() for x in data]).strip('\r\n')
|
|
||||||
return data_clean
|
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(start_modem_task())
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import network
|
||||||
|
|
||||||
from config import WIFI_SSID, WIFI_PASSWORD, TIMEOUT_WIFI_CONNECT
|
from config import WIFI_SSID, WIFI_PASSWORD, TIMEOUT_WIFI_CONNECT
|
||||||
from lib.logging import logger, LogLevel
|
from lib.logging import logger, LogLevel
|
||||||
from lib.runtime import timeout
|
from lib.runtime import runtime_timeout
|
||||||
|
|
||||||
|
|
||||||
class WifiStatus:
|
class WifiStatus:
|
||||||
|
@ -38,7 +38,7 @@ class WifiMananger:
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
self._wlan.disconnect()
|
self._wlan.disconnect()
|
||||||
|
|
||||||
@timeout(TIMEOUT_WIFI_CONNECT)
|
@runtime_timeout(TIMEOUT_WIFI_CONNECT)
|
||||||
async def connect(self, ignore_missing: bool = False):
|
async def connect(self, ignore_missing: bool = False):
|
||||||
if self._wlan.isconnected() or self.status() == WifiStatus.CONNECTED:
|
if self._wlan.isconnected() or self.status() == WifiStatus.CONNECTED:
|
||||||
raise Exception("Already connected")
|
raise Exception("Already connected")
|
||||||
|
|
|
@ -6,7 +6,7 @@ class uTimeoutError(Exception):
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
def timeout(seconds):
|
def runtime_timeout(seconds):
|
||||||
"""
|
"""
|
||||||
This will not work if your decorated function uses `time.sleep()`!!! Use `asyncio.sleep()` instead.
|
This will not work if your decorated function uses `time.sleep()`!!! Use `asyncio.sleep()` instead.
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +35,7 @@ class RunWithtimeoutResult:
|
||||||
|
|
||||||
|
|
||||||
async def run_with_timeout(func, timeout_sec: int, *args, **kwargs) -> RunWithtimeoutResult:
|
async def run_with_timeout(func, timeout_sec: int, *args, **kwargs) -> RunWithtimeoutResult:
|
||||||
@timeout(timeout_sec)
|
@runtime_timeout(timeout_sec)
|
||||||
async def inner():
|
async def inner():
|
||||||
return await func(*args, **kwargs)
|
return await func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from lib.runtime import reboot
|
from lib.runtime import reboot;reboot()
|
||||||
|
|
||||||
reboot()
|
|
||||||
|
|
Loading…
Reference in New Issue