Conversation
Add support for the SIMCom SIM800L 2G module, following the existing driver architecture (Quectel, Sequans, u-blox). Key implementation details: - GSM/GPRS only: uses +CGREG instead of +CEREG for registration - SIMCom TCP/IP stack: AT+CSTT/CIICR/CIFSR for GPRS bearer, AT+CIPSTART/CIPSEND/CIPRXGET for sockets - Manual data receive mode (AT+CIPRXGET=1) for clean AT parsing - Async DNS via AT+CDNSGIP - Multi-connection mode (up to 6 simultaneous sockets) Also extends the AT session to support prefixed terminations (e.g. "0, SEND OK") used by SIM800L in multi-connection mode.
Add comments referencing the SIM800 Series AT Command Manual V1.09 and Hardware Design V1.00 to all AT commands and hardware interactions in the driver code, to help reviewers verify correctness.
examples/sim800l-tcall.toit
Outdated
|
|
||
| main: | ||
| // Enable the board's power management IC. | ||
| power-control := gpio.Pin --output 23 |
There was a problem hiding this comment.
It would be nice to have this happen when something connects to the network.
That would require subclassing the existing class.
Investigate.
If you find a solution, don't forget to update the README.
There was a problem hiding this comment.
Following the Olimex PoE Ethernet pattern, the power IC is now managed at the service provider level. Sim800lTCallService overrides open-network/close-network to enable/disable GPIO 23. Tested with two concurrent clients — power IC stays on while any client is connected, shuts off when the last disconnects.
done
src/modules/simcom/README.md
Outdated
| The SIM800L operates at **3.7-4.2V** and can draw up to **2A** during | ||
| transmission bursts. Make sure your power supply can handle this. | ||
|
|
||
| - Do **not** power the module from the ESP32's 3.3V pin. |
There was a problem hiding this comment.
More importantly: don't power it with the 5V.
| - Do **not** power the module from the ESP32's 3.3V pin. | ||
| - A LiPo battery (3.7V, 1200mAh+) or a 2A-capable DC-DC converter is recommended. | ||
| - Some boards (like the LilyGO T-Call) include a power management IC. | ||
|
|
There was a problem hiding this comment.
Also add a note that the 3.3V can't go directly to the module for the RX.
src/modules/simcom/README.md
Outdated
| ## Limitations | ||
|
|
||
| - 2G (GSM/GPRS) only — no LTE support. | ||
| - Maximum 6 simultaneous TCP/UDP connections. |
There was a problem hiding this comment.
How does our driver handle things when there are more connections?
There was a problem hiding this comment.
The driver throws ResourceExhaustedException when all 6 slots are in use (tested on hardware). Updated README.
done
src/modules/simcom/README.md
Outdated
|
|
||
| - 2G (GSM/GPRS) only — no LTE support. | ||
| - Maximum 6 simultaneous TCP/UDP connections. | ||
| - Maximum ~1460 bytes per send operation. |
There was a problem hiding this comment.
Same question: what happens if we try to send something bigger? Do we chunk correctly?
There was a problem hiding this comment.
TCP try-write_ chunks automatically (if to - from > MAX-SIZE_: to = from + MAX-SIZE_). UDP throws if over MTU. Updated README.
done
src/modules/simcom/simcom.toit
Outdated
| connect_: | ||
| // On SIM800L, the connection is established after CIPSTART returns OK. | ||
| // The "<id>, CONNECT OK" notification is not a standard URC and is | ||
| // handled by the sleep in the constructor. |
There was a problem hiding this comment.
Not a fan of this. Is there no way to wait for this? We can change the 'at.toit' if necessary.
There was a problem hiding this comment.
Addressed together with the comment above.
done
src/modules/simcom/simcom.toit
Outdated
| if to - from > MAX-SIZE_: to = from + MAX-SIZE_ | ||
| data = data.byte-slice from to | ||
|
|
||
| e := catch --unwind=(: it is not UnavailableException): |
There was a problem hiding this comment.
no need to assign to a local if we don't do anything with it.
| cellular_.sockets_.remove id | ||
|
|
||
| mtu -> int: | ||
| return 1500 |
There was a problem hiding this comment.
Is this related to the MAX-SIZE?
There was a problem hiding this comment.
They are related but distinct: MTU (1500) is the IP-level maximum, MAX-SIZE_ (1460) is the SIM800L per-AT-command data limit (≈ MTU minus TCP/IP headers). Added a clarifying comment.
done
src/modules/simcom/simcom.toit
Outdated
| "$address.port", | ||
| ] | ||
| // Wait for CONNECT OK. | ||
| sleep --ms=3000 |
There was a problem hiding this comment.
Same as for the tcp socket. Can we monitor the TX somehow?
There was a problem hiding this comment.
Addressed together with comment on simcom.toit:94.
done
src/modules/simcom/simcom.toit
Outdated
| else: | ||
| parts | ||
|
|
||
| // CCID response parser (ICCID is too large for int). |
There was a problem hiding this comment.
What does this mean?
ints in Toit are 64 bit.
There was a problem hiding this comment.
Reworded: ICCIDs are 19-20 digits. A 64-bit signed int holds up to ~9.2×10^18 (19 digits), so 20-digit ICCIDs overflow. The custom parser reads the value as a string to avoid inconsistent behavior from the default int-parsing fallback.
done
| // Wait for CONNECT OK. | ||
| sleep --ms=3000 | ||
| // Wait for the async "<n>, CONNECT OK" response. | ||
| cellular_.wait-for-urc_: state_.wait-for CONNECTED-STATE_ |
There was a problem hiding this comment.
Do we risk to miss the message because we do the socket-call first and then start waiting?
There was a problem hiding this comment.
No risk of missing the message. The socket is registered in sockets_ before the connection is initiated (UDP: udp-open registers, then user calls connect; TCP: tcp-connect registers, then calls connect_). The on-unhandled-line callback sets CONNECTED-STATE_ as soon as CONNECT OK arrives. If it arrives before wait-for-urc_ starts, the state bit is already set and state_.wait-for returns immediately. done
| cellular_.at_.do: | session/at.Session | | ||
| if not session.is-closed: | ||
| session.set "+CIPCLOSE" [id] | ||
| catch: |
There was a problem hiding this comment.
Is this catch really necessary?
There was a problem hiding this comment.
The catch around CIPCLOSE handles the case where the remote side already closed the connection (<n>, CLOSED notification was received). Without it, +CME ERROR: operation not allowed is thrown. We hit this in testing with concurrent clients. done
|
TBR. |
Add support for the SIMCom SIM800L 2G module, following the existing driver architecture (Quectel, Sequans, u-blox).
Key implementation details:
Also extends the AT session to support prefixed terminations (e.g. "0, SEND OK") used by SIM800L in multi-connection mode.