Skip to content

modem_cellular: Add support for the simcom a76xx modem #90081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/modem/Kconfig.cellular
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ config MODEM_CELLULAR
select NET_L2_PPP_OPTION_MRU
select NET_L2_PPP_PAP
select NET_L2_PPP_MGMT
depends on (DT_HAS_QUECTEL_BG95_ENABLED || \
depends on (DT_HAS_QUECTEL_BG95_ENABLED || DT_HAS_SIMCOM_A76XX_ENABLED || \
DT_HAS_SIMCOM_SIM7080_ENABLED || DT_HAS_U_BLOX_SARA_R4_ENABLED || \
DT_HAS_U_BLOX_SARA_R5_ENABLED || DT_HAS_SWIR_HL7800_ENABLED || \
DT_HAS_TELIT_ME910G1_ENABLED || DT_HAS_TELIT_ME310G1_ENABLED || \
Expand Down
88 changes: 86 additions & 2 deletions drivers/modem/modem_cellular.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,10 @@ MODEM_CHAT_MATCHES_DEFINE(dial_abort_matches,
MODEM_CHAT_MATCH("NO CARRIER", "", NULL),
MODEM_CHAT_MATCH("NO DIALTONE", "", NULL));

#if DT_HAS_COMPAT_STATUS_OKAY(swir_hl7800) || DT_HAS_COMPAT_STATUS_OKAY(sqn_gm02s) || \
DT_HAS_COMPAT_STATUS_OKAY(quectel_eg800q)
#if DT_HAS_COMPAT_STATUS_OKAY(swir_hl7800) || \
DT_HAS_COMPAT_STATUS_OKAY(sqn_gm02s) || \
DT_HAS_COMPAT_STATUS_OKAY(quectel_eg800q) || \
DT_HAS_COMPAT_STATUS_OKAY(simcom_a76xx)
MODEM_CHAT_MATCH_DEFINE(connect_match, "CONNECT", "", NULL);
#endif

Expand Down Expand Up @@ -2116,6 +2118,64 @@ MODEM_CHAT_SCRIPT_DEFINE(simcom_sim7080_periodic_chat_script,
modem_cellular_chat_callback_handler, 4);
#endif

#if DT_HAS_COMPAT_STATUS_OKAY(simcom_a76xx)
MODEM_CHAT_SCRIPT_CMDS_DEFINE(simcom_a76xx_init_chat_script_cmds,
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 100),
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 100),
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 100),
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 100),
MODEM_CHAT_SCRIPT_CMD_RESP("ATE0", ok_match),
/* Power on the GNSS module.
* We need to do this early, otherwise it does not work when
* doing it later (e.g. from a user pipe).
*/
MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGNSSPWR=1", allow_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CFUN=4", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CMEE=1", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CREG=1", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG=1", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CEREG=1", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CREG?", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CEREG?", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG?", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGSN", imei_match),
MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGMM", cgmm_match),
MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT+CMUX=0,0,5,127", 300));

MODEM_CHAT_SCRIPT_DEFINE(simcom_a76xx_init_chat_script, simcom_a76xx_init_chat_script_cmds,
abort_matches, modem_cellular_chat_callback_handler, 10);

MODEM_CHAT_SCRIPT_CMDS_DEFINE(simcom_a76xx_dial_chat_script_cmds,
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGDCONT=1,\"IP\","
"\""CONFIG_MODEM_CELLULAR_APN"\"",
ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CGACT=0,1", allow_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CFUN=1", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("ATD*99***1#", connect_match),);

MODEM_CHAT_SCRIPT_DEFINE(simcom_a76xx_dial_chat_script, simcom_a76xx_dial_chat_script_cmds,
dial_abort_matches, modem_cellular_chat_callback_handler, 10);

MODEM_CHAT_SCRIPT_CMDS_DEFINE(simcom_a76xx_periodic_chat_script_cmds,
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CREG?", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CEREG?", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGREG?", ok_match));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AT+CREG? command is used to query circuit-switched network (2G/3G) registration status. The AT+CGREG? command is used to query GPRS (2G) registration status, and the AT+CEREG? is used to query EPS registration status (LTE, essentially). Not all A76XX series modems support all of these technologies, so it is not unexpected to receive a +CGREG: 0,0 or +CREG: 0,0 while you are connected to an LTE network.
The way the current state machine works is it will start some mechanism to recover from a loss of registration. I suspect running these commands in an LTE-only module is going to potentially introduce issues. In general, this area needs to be improved because for multi-RAT modems the logic is finicky at best.


MODEM_CHAT_SCRIPT_DEFINE(simcom_a76xx_periodic_chat_script,
simcom_a76xx_periodic_chat_script_cmds, abort_matches,
modem_cellular_chat_callback_handler, 4);

MODEM_CHAT_SCRIPT_CMDS_DEFINE(simcom_a76xx_shutdown_chat_script_cmds,
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CPOF", ok_match));

MODEM_CHAT_SCRIPT_DEFINE(simcom_a76xx_shutdown_chat_script,
simcom_a76xx_shutdown_chat_script_cmds, abort_matches,
modem_cellular_chat_callback_handler, 15);

Comment on lines +2169 to +2176
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that important but this commit could be squashed into the first one.

#endif

#if DT_HAS_COMPAT_STATUS_OKAY(u_blox_sara_r4)
MODEM_CHAT_SCRIPT_CMDS_DEFINE(u_blox_sara_r4_init_chat_script_cmds,
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("AT", 100),
Expand Down Expand Up @@ -2638,6 +2698,26 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
&simcom_sim7080_dial_chat_script, \
&simcom_sim7080_periodic_chat_script, NULL)

#define MODEM_CELLULAR_DEVICE_SIMCOM_A76XX(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
\
static struct modem_cellular_data MODEM_CELLULAR_INST_NAME(data, inst) = { \
.chat_delimiter = "\r", \
.chat_filter = "\n", \
.ppp = &MODEM_CELLULAR_INST_NAME(ppp, inst), \
}; \
\
MODEM_CELLULAR_DEFINE_AND_INIT_USER_PIPES(inst, \
(user_pipe_0, 3), \
(user_pipe_1, 4)) \
\
MODEM_CELLULAR_DEFINE_INSTANCE(inst, 500, 100, 20000, 5000, false, \
NULL, \
&simcom_a76xx_init_chat_script, \
&simcom_a76xx_dial_chat_script, \
&simcom_a76xx_periodic_chat_script, \
&simcom_a76xx_shutdown_chat_script)

#define MODEM_CELLULAR_DEVICE_U_BLOX_SARA_R4(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
\
Expand Down Expand Up @@ -2805,6 +2885,10 @@ DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_QUECTEL_EG800Q)
DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_SIMCOM_SIM7080)
#undef DT_DRV_COMPAT

#define DT_DRV_COMPAT simcom_a76xx
DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_SIMCOM_A76XX)
#undef DT_DRV_COMPAT

#define DT_DRV_COMPAT u_blox_sara_r4
DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_U_BLOX_SARA_R4)
#undef DT_DRV_COMPAT
Expand Down
13 changes: 13 additions & 0 deletions dts/bindings/modem/simcom,a76xx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (C) 2025 Olivier Lalonde
# SPDX-License-Identifier: Apache-2.0

description: Simcom A76XX modem

compatible: "simcom,a76xx"

include: uart-device.yaml

properties:
mdm-power-gpios:
type: phandle-array
required: true
12 changes: 12 additions & 0 deletions samples/net/cellular_modem/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2025 Olivier Lalonde
# SPDX-License-Identifier: Apache-2.0

# Configuration options for cellular modem sample

mainmenu "Cellular modem sample application"

config SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME
string "Endpoint hostname"
default "test-endpoint.com"
Comment on lines +8 to +10
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! @bjarki-andreasen something really should be done with regards to test-endpoint.com... cc @kartben

Copy link
Contributor Author

@olalonde olalonde May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a side note, in my own project, I recently added this to my CMakeLists.txt to avoid committing secrets to git:

if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/local.conf)
    file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/local.conf 
"# This file is not version controlled, you can use it for local only configuration")
endif()

set(EXTRA_CONF_FILE 
    ${CMAKE_CURRENT_SOURCE_DIR}/local.conf
)

And I also added local.conf to .gitignore.

It would be useful here to allow developers to set a private CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME without having to edit a version controlled file. And for other configurations (e.g. logging) that are useful for testing but that you don't necessarily want to commit to git.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think it makes sense to stop pretending test-endpoint.com should be functional:

Suggested change
default "test-endpoint.com"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it's really not ideal.
The sample/instructions should be updated to not depend it anymore, or the doc should be updated to indicate how one may host their own server (with the caveat that users might struggle to make their endpoint publicly accessible)

IMO the sample should be a really minimal showcase of printing modem-info, showing that we can ping 8.8.8.8 or whatever, and that's it. Things like testing throughput etc. are really something that's left for a test IMO, and probably don't belong to the sample.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree as well :) With the state of the modem subsystem and drivers, we should be looking to networking samples to test networking, the cellular part really should not make a difference :)


source "Kconfig.zephyr"
13 changes: 13 additions & 0 deletions samples/net/cellular_modem/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,16 @@ Next, the UART API must be specified using ``CONFIG_UART_INTERRUPT_DRIVEN=y`` or
``CONFIG_UART_ASYNC_API=y``. The driver doesn't support UART polling.

Lastly, the APN must be configured using ``CONFIG_MODEM_CELLULAR_APN=""``.

Server setup
************

Deploy the server on a publicly accessible host, then set the
:kconfig:option:`CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME` variable in ``prj.conf`` to the
server's hostname.

.. code-block:: shell

git clone --depth=1 https://github.com/zephyrproject-rtos/zephyr.git
cd zephyr/samples/net/cellular_modem/server
python te.py
1 change: 0 additions & 1 deletion samples/net/cellular_modem/prj.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

CONFIG_POSIX_API=y

# Networking
Expand Down
2 changes: 1 addition & 1 deletion samples/net/cellular_modem/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include <zephyr/drivers/cellular.h>

#define SAMPLE_TEST_ENDPOINT_HOSTNAME ("test-endpoint.com")
#define SAMPLE_TEST_ENDPOINT_HOSTNAME CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME
#define SAMPLE_TEST_ENDPOINT_UDP_ECHO_PORT (7780)
#define SAMPLE_TEST_ENDPOINT_UDP_RECEIVE_PORT (7781)
#define SAMPLE_TEST_PACKET_SIZE (1024)
Expand Down
3 changes: 2 additions & 1 deletion subsys/modem/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ config MODEM_CMUX_DEFAULT_MTU_127
DT_HAS_SIMCOM_SIM7080_ENABLED || DT_HAS_U_BLOX_SARA_R4_ENABLED || \
DT_HAS_U_BLOX_SARA_R5_ENABLED || DT_HAS_SWIR_HL7800_ENABLED || \
DT_HAS_TELIT_ME910G1_ENABLED || DT_HAS_TELIT_ME310G1_ENABLED || \
DT_HAS_SQN_GM02S_ENABLED || DT_HAS_QUECTEL_EG800Q_ENABLED)
DT_HAS_SQN_GM02S_ENABLED || DT_HAS_QUECTEL_EG800Q_ENABLED || \
DT_HAS_SIMCOM_A76XX_ENABLED)
help
Use the default MTU size of 127 bytes for the CMUX module on certain modems.
This must match the AT+CMUX commands in the modem_cellular driver.
Expand Down
Loading