Modem

Quectel EG915 GSM/LTE modem driver.

The eg915 class below is verbatim from the device firmware (firmware_core.py of Logger Pro and the findyIoT module) - same signatures, same bodies - with documentation added. The pin constants and apn mirror the firmware module globals the class reads.

All hardware access happens inside methods; importing this module performs no I/O (machine/utime are mocked for this build).

findylab.modem.apn = 'm2m.tele2.com'

Access Point Name used for the PDP context.

findylab.modem.P_Power_GSM = 17

Modem power rail pin.

findylab.modem.P_PWRKey = 23

Modem PWRKEY pin.

findylab.modem.P_TxGSM = 36

ESP32 pin wired to the modem TX line.

findylab.modem.P_RXGSM = 21

ESP32 pin wired to the modem RX line.

findylab.modem.P_DTR = 5

Modem DTR pin (slow-clock sleep control).

findylab.modem.P_PSM_IN = 35

Modem PSM status input (unused by the driver).

findylab.modem.P_PON_TRIG = 16

Modem PON trigger (unused by this board revision).

class findylab.modem.eg915[source]

Driver for the Quectel EG915 modem on UART1.

Verbatim class from firmware_core.py. The constructor takes no arguments - the UART/control pins come from the module-level pin constants and apn, exactly as on the device.

Note

This board has no dedicated RESET_GSM/PSM pins, so reset() power-cycles the supply rail instead of pulsing a reset line, and there are no RAT-limiting or PSM helpers.

__init__()[source]

Prepares the modem control pins in a safe state.

Reads the pin numbers and APN from the module constants (P_TxGSM, P_RXGSM, P_DTR, P_PWRKey, P_Power_GSM, apn). No UART is created yet - that happens lazily in isOn().

isOn()[source]

Checks whether the modem answers AT commands.

(Re)creates UART1 (falling back to smaller buffers when RAM is low), sends AT and looks for OK. On success also reads the SIM ICCID as a side effect.

Returns:

True if the modem replied OK.

Return type:

bool

turnOn()[source]

Powers the modem on and waits until it accepts AT commands.

Raises the power rail (held through deep sleep), waits 1 s for VBAT to stabilize, wakes the modem from slow-clock mode, then pulses PWRKEY - up to 3 attempts, re-pulsing when the modem stays silent (a cold modem can ignore the first pulse). Stamps the global registrationStart used by registerNetwork().

Returns:

Always True; check isOn() for the actual state.

Return type:

bool

turnOff()[source]

Shuts the modem down gracefully and cuts its power rail.

Sends AT+QPOWD first so the modem detaches cleanly, then drives the power rail low and holds it through deep sleep.

Returns:

True on success, False if the shutdown raised.

Return type:

bool

reset()[source]

Power-cycles the modem.

This board has no dedicated RESET_GSM pin, so instead of pulsing a reset line the driver power-cycles P_Power_GSM.

registerNetwork(registrationTimeout=310)[source]

Blocks until the modem registers on the network or times out.

Parameters:

registrationTimeout (int) – Maximum seconds to wait, measured from the global registrationStart stamped by turnOn().

Returns:

True when registered, False on timeout.

Return type:

bool

sleep()[source]

Puts the modem into Quectel slow-clock mode.

Sends AT+QSCLK=1 and raises DTR; the modem stays registered but drops its supply current to the sleep floor.

wakeUp()[source]

Wakes the modem from slow-clock mode (DTR low + AT+QSCLK=0).

isReg()[source]

Checks whether the modem is registered and has a usable IP.

Accepts CREG state 5 (roaming) or CEREG state 1 (home). State 3 logs a forbidden network, state 4 resets the modem, state 0 waits for the modem to start trying. When registered but the PDP address is 0.0.0.0, the PDP context is bounced.

Returns:

True when registered with a real IP address.

Return type:

bool

getImei()[source]

Reads the modem IMEI.

Also performs the one-time UART setup (PDP context for the APN, echo, SMS text mode, URC routing to UART1) - which is why the firmware calls it once during first boot.

Returns:

The IMEI (15 digits), or an empty string when the modem is not responding (callers retry on len != 15).

Return type:

str

getBat()[source]

Reads the battery voltage via AT+CBC.

Returns:

Legacy shape [0, voltage_mV, '100'] - index 1 is the supply voltage in millivolts as a string.

Return type:

list

getTimeF()[source]

Fetches the current server time via the /t/ endpoint.

Returns:

Epoch seconds as a 10-digit string, or False on error.

Return type:

str

getCPSI()[source]

Returns the raw engineering info (AT+QENG=0).

Returns:

The QENG payload with whitespace collapsed to ; separators.

Return type:

str

connectHiGPS(num)[source]

Prepares the data connection to the HiGPS server.

Closes any stale socket, ensures network registration and activates the PDP context.

Parameters:

num (int) – Kept for compatibility, unused.

getData(response)[source]

Extracts the payload from a raw +QIRD response.

Parameters:

response (bytes) – Raw bytes as returned by AT+QIRD=0.

Returns:

The payload; empty when nothing was received.

Return type:

bytes

sendHiGPS(message, fast=False)[source]

Sends a GET request to the HiGPS server and returns the reply.

Checks the socket with socketState() and (re)opens it via openHiGPS() when it is not Connected, then sends GET <message> and extracts the payload from the +QIRD response.

Parameters:
  • message (str) – Request path, e.g. '/t/' or '/input.php?IMEI=...'. Truncated to 1400 chars.

  • fast (bool) – Kept for compatibility, unused.

Returns:

The reply payload, or False on send failure.

Return type:

bytes

socketState(connectID=0)[source]

Returns the Quectel <socket_state> of a socket, or None.

Modem-agnostic replacement for substring-matching the raw AT+QISTATE reply. The +QISTATE: line is positional:

+QISTATE: <connectID>,<service_type>,<IP>,<remote_port>,
          <local_port>,<socket_state>,<contextID>,<serverID>,
          <access_mode>[,<AT_port>]

EG915 and EG916 differ only in the trailing fields and in the echo/OK-only reply when no socket exists, so <socket_state> is read by field position (index 5) and the rest is ignored.

Parameters:

connectID (int) – Socket index; 0 is the HiGPS connection.

Returns:

0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing - or None when the socket does not exist or the modem is silent.

Return type:

int

qiopenResult(resp)[source]

Extracts <err> from a +QIOPEN: <connectID>,<err> reply.

Parses by field position, so it behaves identically on EG915 and EG916 regardless of the surrounding echo/URC noise.

Parameters:

resp (bytes) – Raw reply as returned by us().

Returns:

The result code (0 = success), or None when no +QIOPEN: line is present.

Return type:

int

openHiGPS(retries=3, timeout=15)[source]

Opens TCP socket 0 to application.higps.org:80.

AT+QIOPEN in buffer access mode is asynchronous: the modem answers OK at once and emits +QIOPEN: 0,<err> seconds later, when DNS and the TCP handshake finish. This waits for that URC (up to timeout s), reads the real result code with qiopenResult() and only then confirms with socketState(). Every attempt starts from a closed socket, because both modems reject QIOPEN on a connectID already in use.

Parameters:
  • retries (int) – Open attempts before giving up.

  • timeout (int) – Seconds to wait for the +QIOPEN: URC.

Returns:

True once the socket is Connected (state 2).

Return type:

bool

cipClose()[source]

Closes TCP socket 0 (AT+QICLOSE=0).

qird(want, timeout=10)[source]

Reads exactly <len> payload bytes with AT+QIRD=0,<want>.

us() is line-oriented and truncates a payload at the first newline, which a binary firmware image contains - so OTA reads must not go through it. This reads the +QIRD: <len> header and then exactly <len> bytes off the UART.

Parameters:
  • want (int) – Maximum bytes to request.

  • timeout (int) – Seconds to wait for the data.

Returns:

The payload; empty when the socket has nothing yet (+QIRD: 0) or on timeout.

Return type:

bytes

downloadChunk(path, expected, timeout=20)[source]

Fetches one OTA chunk over the raw HiGPS socket.

Sends GET <path> and reads via qird() in a loop until expected bytes arrive (LTE packets are far larger than the single read that sufficed on 2G) or the socket idles past timeout.

Parameters:
  • path (str) – Repository path, e.g. /repo/<name>/1460/<n>/<imei>.

  • expected (int) – Chunk size the caller expects.

  • timeout (int) – Idle seconds before giving up.

Returns:

Whatever arrived. A caller that gets fewer than expected bytes must retry the same chunk index and never advance the file, so a short read cannot misalign the download.

Return type:

bytes

getVersion()[source]

Returns the modem firmware version string (AT+QGMR).

Returns:

Version line, or an empty string when unavailable.

Return type:

str

getNetworkInfo()[source]

Reads the serving-cell information (LTE only).

Parses AT+QENG="servingcell".

Returns:

Keys NetworkBearer, Band, SignalRSSI, RSRP, RSRQ, SINR, LinkQuality, CellID, SMNC, SMCC, LAC, PCI, EARFCN, TimingAdvance. False when not on an LTE cell.

Return type:

dict

turnOnGPS()[source]

Enables the built-in GNSS receiver (AT+QGPS=1).

turnOffGPS()[source]

Disables the built-in GNSS receiver (AT+QGPS=0).

gpsIsFixed()[source]

Checks whether the GNSS receiver has a position fix.

Returns:

False while the modem answers +CME ERROR: 516.

Return type:

bool

getGPS()[source]

Reads the current GNSS position (AT+QGPSLOC=2).

Returns:

Keys time, latitude, longitude, HDOP, altitude, fix, COG, spkm, spkn, date, nsat, errcode. False when there is no fix.

Return type:

dict

check_inbox()[source]

Lists the SMS messages stored on the modem.

Returns:

One dict per message with keys index, sender, date and message.

Return type:

list

delete_sms(index)[source]

Deletes an SMS from the modem memory.

Parameters:

index (int) – Message index as reported by check_inbox().

send_sms(phone, text)[source]

Sends an SMS.

Parameters:
  • phone (str) – Destination number in international format.

  • text (str) – Message body.

Returns:

Always True (the modem confirms asynchronously).

Return type:

bool

us(arg, t=0, t2=0, expect=None)[source]

Sends a raw AT command and collects the reply.

Parameters:
  • arg (str or bytes) – The command, without the trailing CRLF.

  • t (float) – Maximum extra seconds to poll for a reply.

  • t2 (float) – Fixed sleep before the first read.

  • expect (str or bytes) – Optional substring; polling stops early once it appears in the reply.

Returns:

The raw reply, or the string "False" when nothing arrived (legacy sentinel).

Return type:

bytes

us2(arg, t=0)[source]

Simplified one-shot variant of us().

Parameters:
  • arg (str or bytes) – The command, without the trailing CRLF.

  • t (float) – Fixed seconds to wait before reading the reply.

Returns:

The raw reply, or "False" when nothing arrived.

Return type:

bytes