Quectel.Modem¶
- class Quectel.Modem(modem_port: str = '/dev/ttyUSB3', modem_baud: int = 115200)¶
AT-command interface for Quectel/Telit cellular modems.
Wraps a
serial.Serialconnection to the modem’s AT-command port and provides high-level helpers around the standard 3GPP AT commands (AT+CSQ,AT+CREG?,AT+CGREG?,AT+CEREG?,AT+ICCID,AT+CGSN,ATI3). The vendor is auto-detected viaidentify_module(), which probes USB devices throughdrivers.DeviceDetector.DeviceDetector.DeviceDetector.- Variables:
portwrite (
str) – Serial device path for AT commands (e.g./dev/ttyUSB3).module_name (
str | None) – Detected module vendor –"Quectel","Telit"orNoneif detection failed.detector (
DeviceDetector) – USB device scanner used during port discovery.network_towers (
list[str]) – Unicode block characters used to render signal-strength bar graphs.signalMap (
list[tuple[int,int,str,int]]) – RSSI-to-(dBm, label, bars) lookup table populated from theAT+CSQspecification.cmd_delay (
float) – Inter-command delay in seconds.dataDelay (
int) – Generic data-wait delay in seconds.DEBUG (
bool) – Verbose-logging flag.TIMEOUT (
int) – Read timeout in milliseconds.port_write (
str | None) – Class-level default AT port (overridden per instance).
Example
>>> from drivers.Quectel.Quectel import Modem >>> modem = Modem() >>> modem.signalstrength() -73
Auto-detect the modem module and open the serial AT port.
Instantiates a
drivers.DeviceDetector.DeviceDetector.DeviceDetector, callsidentify_module()to bindportwrite, opens aserial.Serialconnection with a 10 s read timeout and a 3 s write timeout, and immediately closes it so subsequent helpers can re-open the port idempotently.- Parameters:
- Returns:
Constructor.
- Return type:
None
- Raises:
serial.SerialException – If the underlying
/dev/ttyUSB*device cannot be opened.
Example
>>> from drivers.Quectel.Quectel import Modem >>> modem = Modem("/dev/ttyUSB3", 115200)
Note
This constructor does not issue any AT command – the first command goes out when one of the
get*accessors is invoked.- getGPRSNetwork() str | None | Literal[0]¶
Query GPRS / packet-domain registration status via
AT+CGREG?.Issues
AT+CGREG?, discards the echo line, and parses the<n>,<stat>payload from the second response line. The raw comma-separated string is returned for caller-side interpretation.- Parameters:
None.
- Returns:
Trimmed registration status string (e.g.
"0,1"),Noneif the response did not contain the expected+CGREG:prefix, or0if a serial exception was caught.- Return type:
str | None | Literal[0]
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getGPRSNetwork() '0,1'
Note
AT command:
AT+CGREG?– GPRS Network Registration Status (3GPP TS 27.007 section 7.2).
- getIMEI() str | None | Literal[0]¶
Read the modem IMEI via
AT+CGSN=1.Issues
AT+CGSN=1(the SVN-tagged variant that prefixes the response with+CGSN:), strips the surrounding quotes, and returns the bare 15-digit identifier.- Parameters:
None.
- Returns:
15-digit IMEI string,
Nonewhen the+CGSN:prefix was missing, or0on serial exception.- Return type:
str | None | Literal[0]
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getIMEI() '352099001761481'
Note
AT command:
AT+CGSN=1– Request Product Serial Number Identification (3GPP TS 27.007 section 5.4).
- getIccid() str | None | Literal[0]¶
Read the SIM-card ICCID via
AT+ICCID.Issues
AT+ICCID, drops the echo, and extracts the Integrated Circuit Card Identifier from the response line beginning with+ICCID:.- Parameters:
None.
- Returns:
19-/20-digit ICCID string trimmed of whitespace,
Nonewhen the prefix was missing, or0on serial exception.- Return type:
str | None | Literal[0]
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getIccid() '8991000900000000123'
Note
AT command:
AT+ICCID(Quectel proprietary alias forAT+CRSMSIM-file read of EF-ICCID, 3GPP TS 51.011).
- getLTECheck() str | None | Literal[0]¶
Query LTE / EPS registration status via
AT+CEREG?.Issues
AT+CEREG?, discards the echo, and parses the<n>,<stat>payload from the response.- Parameters:
None.
- Returns:
Trimmed registration status string (e.g.
"0,1"),Nonewhen the response lacked the+CEREG:prefix, or0on serial exception.- Return type:
str | None | Literal[0]
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getLTECheck() '0,1'
Note
AT command:
AT+CEREG?– EPS Network Registration Status (3GPP TS 27.007 section 7.10).
- getModuleInfo() tuple[str, str] | Literal[0]¶
Read the modem manufacturer and model identification via
ATI3.Issues
ATI3, treats the first response line as the manufacturer (_company) and the second as the model (_model), and returns them as a tuple.- Parameters:
None.
- Returns:
(company, model)strings (e.g.("Quectel", "EC25")) or0on serial exception.- Return type:
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getModuleInfo() ('Quectel', 'EC25')
Note
AT command:
ATI3– Display Product Identification Information (V.250 / vendor-specific extension).
- getNetworkCheck() str | None | Literal[0]¶
Query circuit-switched (2G/3G) registration status via
AT+CREG?.Issues
AT+CREG?, skips the echo line, and parses the<n>,<stat>payload from the response.- Parameters:
None.
- Returns:
Trimmed registration status string (e.g.
"0,1"),Nonewhen the response lacked the+CREG:prefix, or0on serial exception.- Return type:
str | None | Literal[0]
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().getNetworkCheck() '0,1'
Note
AT command:
AT+CREG?– Network Registration Status (3GPP TS 27.007 section 7.2).
- identify_module() None | Literal['Quectel'] | Literal['Telit']¶
Auto-detect the modem vendor by scanning USB serial devices.
Polls
DeviceDetector.scan_gsm_devices()up to three times (with a one-second back-off between retries) looking for a recognised Quectel or Telit USB descriptor. On a successful match the detected modem/AT port is assigned toportwrite; if the descriptor exists but the named port mapping is incomplete, the driver falls back to/dev/ttyUSB2.- Parameters:
None – Operates on instance state only.
- Returns:
The detected vendor tag (
"Quectel"or"Telit") orNoneif no module was found after three retries.- Return type:
None | Literal[“Quectel”] | Literal[“Telit”]
- Raises:
None – All scan errors are swallowed and logged via
context_logger.
Example
>>> from drivers.Quectel.Quectel import Modem >>> m = Modem() >>> m.identify_module() 'Quectel'
Note
Vendor detection is descriptor-based (USB VID/PID parsed in
DeviceDetector); it does not issue any AT command yet, so it is safe to call before the serial port is opened.
- micros() int¶
Return the current monotonic-ish wall-clock time in microseconds.
- Parameters:
None.
- Returns:
Integer microsecond timestamp derived from
time.time()scaled by1_000_000.- Return type:
- Raises:
None. –
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().micros() 1721650000123456
Note
Not an AT command – internal timing helper used to bound
readline()waits.
- millis() int¶
Return the current wall-clock time in milliseconds.
- Parameters:
None.
- Returns:
Integer millisecond timestamp derived from
time.time()scaled by1_000.- Return type:
- Raises:
None. –
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().millis() 1721650000123
Note
Not an AT command – used to back the
TIMEOUTwatchdog insideread()andGps.enable_module().
- read() list[bytes]¶
Wait for and drain all available response lines from the modem.
Polls
__modem_port.inWaitinguntil at least one byte is available, then loopsserial.Serial.readline()until the input buffer is empty. Heartbeat seconds are emitted viacontext_loggerwhile waiting.- Parameters:
None – Operates on the bound serial port.
- Returns:
All response lines received from the modem, including the trailing
\r\n.- Return type:
- Raises:
serial.SerialException – If the port disappears mid-read.
Example
>>> from drivers.Quectel.Quectel import Modem >>> m = Modem() >>> m.read() [b'OK\r\n']
Note
Low-level helper used by
send_command(); does not issue any AT command itself.
- send_command(command: str, read_counter: int = 1) None¶
Send an arbitrary AT command and log every response line received.
Opens
__modem_portif needed, appends a carriage return, writes the command, then readsread_counterbatches of response lines viaread(). Each line is logged throughcontext_loggerand the port is always closed in thefinallyclause.- Parameters:
- Returns:
Response lines are logged, not returned. Use the specialised
get*helpers when you need the value back.- Return type:
None
- Raises:
None – All
serial.SerialExceptionand decoding errors arecaught –
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().send_command("AT")
Note
Generic AT pipe – accepts any 3GPP TS 27.007 command supported by the underlying module.
- signalstrength() int | None¶
Query cellular signal strength via
AT+CSQand translate to dBm.Issues
AT+CSQ, parses the<rssi>field from the response, and looks the value up insignalMapto produce a dBm reading plus a human-readable label (Marginal/OK/Good/Excellent) and a bar-graph string drawn fromnetwork_towers. An<rssi>of31is treated as the “unknown but strong” sentinel (-51 dBm).- Parameters:
None.
- Returns:
Signal strength in dBm (negative integer in the
[-113, -51]range), orNoneon any serial / parsing error.- Return type:
int | None
- Raises:
None – All errors are caught and logged via
context_logger –
Example
>>> from drivers.Quectel.Quectel import Modem >>> Modem().signalstrength() -73
Note
AT command:
AT+CSQ– Signal Quality Report (3GPP TS 27.007 section 8.5).
- DEBUG = False¶
- TIMEOUT = 60000¶
- __modem_port¶
- _timeout_ = 60¶
- cmd_delay = 0.1¶
- dataDelay = 2¶
- detector¶
- module_name = 'Quectel'¶
- port_write = None¶
- portwrite = '/dev/ttyUSB3'¶