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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 4 additions & 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 Expand Up @@ -63,4 +63,7 @@ config MODEM_CELLULAR_NEW_BAUDRATE_DELAY
default 100 if DT_HAS_U_BLOX_LARA_R6_ENABLED
default 300

config MODEM_CELLULAR_POWER_ON_GNSS
bool "Power on the modem's GNSS module"
depends on DT_HAS_SIMCOM_A76XX_ENABLED
endif
88 changes: 87 additions & 1 deletion drivers/modem/modem_cellular.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,9 @@ 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)
#if DT_HAS_COMPAT_STATUS_OKAY(swir_hl7800) || \
DT_HAS_COMPAT_STATUS_OKAY(sqn_gm02s) || \
DT_HAS_COMPAT_STATUS_OKAY(simcom_a76xx)
MODEM_CHAT_MATCH_DEFINE(connect_match, "CONNECT", "", NULL);
#endif

Expand Down Expand Up @@ -2067,6 +2069,66 @@ 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),
#ifdef CONFIG_MODEM_CELLULAR_POWER_ON_GNSS
/* 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),
#endif
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));

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);

#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 @@ -2570,6 +2632,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 @@ -2733,6 +2815,10 @@ DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_QUECTEL_EG25_G)
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) 2021 metraTec GmbH
# 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
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.


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 @@
``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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe mention CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME?

Copy link
Author

Choose a reason for hiding this comment

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

Added a small paragraph about that 👍.

************

Deploy the server on a publicly accessible host, then set the
`CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME` variable in `prj.conf` to the

Check failure on line 63 in samples/net/cellular_modem/README.rst

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

SphinxLint

samples/net/cellular_modem/README.rst:63 default role used (hint: for inline literals, use double backticks) (default-role)
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
3 changes: 3 additions & 0 deletions samples/net/cellular_modem/boards/nrf52840dk_nrf52840.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Otherwise we run into DLCI buffer overruns (maybe due to lack of UART hardware flow control?)
Copy link
Collaborator

Choose a reason for hiding this comment

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

FYI there is now an async UART backend with HWFC available.

Copy link
Author

Choose a reason for hiding this comment

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

I'm pretty new to Zephyr/embedded but the module I'm testing with doesn't have a CTS pin, so unless I misunderstand something, I don't think it's possible to fix that in software (e.g. by using a different UART driver). The modem still won't know whether the microcontroller is ready to receive. Or maybe I'm misunderstanding something... PS: I am currently waiting for a better module with UART flow control pins, but shipping is slow.

CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE=1507
CONFIG_NET_IPV6=n
27 changes: 27 additions & 0 deletions samples/net/cellular_modem/boards/nrf52840dk_nrf52840.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/ {
Copy link
Member

Choose a reason for hiding this comment

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

This does not look right. Now anyone having this DK will get this simcom modem added automatically even if he/she does not have one.

Copy link
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.

I used the nrf52840dk board for testing the modem as none of the supported Zephyr boards uses it. There's no reason to run the cellular modem sample with it since it doesn't have a modem, so the chance of confusing someone is low. Is there a better way to do this? Or should i just remove it from the sample? I don't mind either way it was mostly just to facilitate my testing.

aliases {
modem = &modem;
};
};

/* Tested with Simcom A7672SA module connected via UART
* TX: P1.2
* RX: P1.1
* PWRKEY: P0.02
*/
&uart1 {
compatible = "nordic,nrf-uarte";
status = "okay";
current-speed = <115200>;
/* The module I tested with doesn't expose RCS/CTS pins
* hw-flow-control;
*/

pinctrl-0 = <&uart1_default>;

modem: modem {
compatible = "simcom,a76xx";
status = "okay";
mdm-power-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH)>;
};
};
1 change: 1 addition & 0 deletions samples/net/cellular_modem/prj.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0
CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME=""

CONFIG_POSIX_API=y

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
2 changes: 1 addition & 1 deletion subsys/modem/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ 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_SQN_GM02S_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