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 andapn, 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 inisOn().
- isOn()[source]
Checks whether the modem answers AT commands.
(Re)creates UART1 (falling back to smaller buffers when RAM is low), sends
ATand looks forOK. 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
registrationStartused byregisterNetwork().- 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+QPOWDfirst 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
registrationStartstamped byturnOn().- Returns:
True when registered, False on timeout.
- Return type:
bool
- sleep()[source]
Puts the modem into Quectel slow-clock mode.
Sends
AT+QSCLK=1and raises DTR; the modem stays registered but drops its supply current to the sleep floor.
- 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
QENGpayload 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
+QIRDresponse.- 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 viaopenHiGPS()when it is not Connected, then sendsGET <message>and extracts the payload from the+QIRDresponse.- 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+QISTATEreply. 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+QIOPENin 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 totimeouts), reads the real result code withqiopenResult()and only then confirms withsocketState(). 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
- qird(want, timeout=10)[source]
Reads exactly
<len>payload bytes withAT+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 viaqird()in a loop untilexpectedbytes arrive (LTE packets are far larger than the single read that sufficed on 2G) or the socket idles pasttimeout.- 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
expectedbytes 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
- 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,dateandmessage.- 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