findyIoT module

The generic LTE node of the Findy Lab ecosystem: an ESP32-WROOM-32U with a Quectel EG915R1 modem. It has no sensor front-end of its own - the application in last.py decides what is measured and reported; the framework provides the modem, the parameter database, the HiGPS protocol and the over-the-air update.

Overview

  • ESP32-WROOM-32U (MicroPython v1.25), deep-sleep duty cycle

  • Quectel EG915R1 LTE modem on UART1, 115200 baud (findylab.modem.eg915)

  • Status LED on GPIO 2 - lit while the firmware is awake

  • Serving-cell diagnostics (bearer, RSSI, RSRQ, cell ID, MCC/MNC, SINR, LAC) refreshed before every report (parameters 230-238)

  • Zip-based, resumable OTA update with boot-time commit and a recovery image (Over-the-air update)

  • Round-boundary wakeups: the sleep timer is aligned to multiples of the measurement period (findylab.core.fw.seconds_to_next_wakeup())

Pin map

Function

GPIO

Notes

Modem power rail (GSM On/Off)

12

P_Power_GSM - held through deep sleep

Modem PWRKEY (GSM PWR Key)

32

P_PWRKey - power-on pulse, up to three attempts

Modem UART TX (ESP32 side)

25

P_RXGSM - Rx GSM net, modem RX

Modem UART RX (ESP32 side)

26

P_TxGSM - Tx GSM net, modem TX

Modem RESET_N (PON_TRIG)

27

P_RESET_GSM - via an NPN inverter; defined but not driven, reset() power-cycles the rail instead

Modem DTR

33

P_DTR - slow-clock (sleep) control

Status LED

2

Driven high when firmware_core is imported

Peripheral power

25

PperipherialPower - constant inherited from Logger Pro, not used by this module’s firmware. It shares GPIO 25 with the modem UART line: check the schematic before driving it.

Note

Deep sleep on this module is a plain machine.deepsleep() with no wakeup pin armed - only the timer (or a hardware reset) wakes it. findylab.core.fw.prepareSleep() therefore takes a single argument, prepareSleep(t), unlike Logger Pro’s prepareSleep(t, trig).

Duty cycle

The reference start() in the module’s last.py:

  1. Wake up (timer at a round boundary of parameter 421, or reset).

  2. wakeCount = 522 + 1. If it reached the ratio 422/421, or the wakeup was an external alarm (machine.wake_reason() == 2): report(alarm, datalist) - power the modem, register, refresh parameters 230-238, send datalist and execute any queued server commands. Otherwise just store the new count in 522.

  3. After a successful connection: 522 = 0, 443 = server-synchronized timestamp.

  4. prepareSleep(t) with t from seconds_to_next_wakeup() - modem off (AT+QPOWD and the power rail), modem pins released as inputs, deep sleep.

Parameters

The module registers the following IDs in databaseParams (see System IDs for the entry format):

IDs

Purpose

200

IMEI

421 / 422

Wakeup period / report period (s)

522 / 443

Wakeups since the last report / timestamp of the last report

230-238

Network status, written by getGSMInfo()

51, 52, 53, 54, 55

OTA update (Over-the-air update)

221 / 222 / 312

Restart / factory reset / send current settings

429

resetCount1() - on this module resets and re-registers the modem (there are no pulse counters)

Default report:

datalist = ['421','443','230','231','232','234','235','236','237','238']

Files on the device

boot.py, firmware_core.mpy, last.py (or last.mpy) and base.py - see Getting started. There is no ulp_main.bin on this module.

Application skeleton

import machine, time, utime
import firmware_core as core
from firmware_core import fw, database, modem, protocol, device, setError

databaseParams = {
    "200": ["R", 1, 0, 0, "0", "R"],          # IMEI
    "421": ["W", 0, 0, 0, "900", "R"],        # wakeup period (s)
    "422": ["W", 0, 0, 0, "900", "R"],        # report period (s)
    "522": ["R", 0, 0, 0, "0", "R"],
    "443": ["R", 0, 0, 0, "0", "R"],
    # ... OTA (51-55), commands (221/222/312/429), network (230-238)
}
datalist = ['421', '443', '230', '231']

db = database(databaseParams)      # created here, published back so
core.db = db                       # firmware_core methods see it as `db`

def start(self):
    wakeCount = int(core.db.read('522')) + 1
    every = float(core.db.read('422')) / float(core.db.read('421'))
    if wakeCount >= every:
        self.report(False, datalist)
    else:
        core.db.write('522', wakeCount)
    if core.connectionSuccess:
        core.db.write('522', '0')
        core.db.write('443', device.getTimestamp())
    t = self.seconds_to_next_wakeup(int(core.db.read('421')))
    self.prepareSleep(t)

fw.start = start