Closed mjm987 closed 1 year ago
@rerickson1 do you know if anyone can look into this?
I don't have the hardware to test.
@bjarki-trackunit or @warasilapm know anyone with bg9x hardware?
I don't have the hardware to test.
@bjarki-trackunit or @warasilapm know anyone with bg9x hardware?
I do not have access to that modem.
I can make it happen :) I have a dev board with the modem, I will add it to this PR https://github.com/zephyrproject-rtos/zephyr/pull/48530, i have set aside tomorrow to get it reviewed an merged, adding and testing UDP is no issue :)
Adding UDP support to the BG9x driver is futile, I suggest another solution that will work now (and way better):
I suggest you use the PPP driver instead, this uses zephyrs IP stack and a protocol which is faster and more flexible than the socket implementation in the bg9x driver. To use it, simply add the following to your projects KConfig
CONFIG_GPIO=y
# GSM modem support
CONFIG_MODEM=y
CONFIG_MODEM_GSM_PPP=y
CONFIG_MODEM_CONTEXT=y
CONFIG_GSM_PPP_AUTOSTART=n
CONFIG_PPP_NET_IF_NO_AUTO_START=n
CONFIG_MODEM_GSM_QUECTEL=y
CONFIG_MODEM_GSM_APN="your apn"
CONFIG_MODEM_CELL_INFO=y
CONFIG_MODEM_SIM_NUMBERS=n
CONFIG_GSM_MUX=y
CONFIG_MAIN_STACK_SIZE=4096
# PPP networking support
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=y
CONFIG_NET_DRIVERS=y
CONFIG_NET_PPP=y
CONFIG_NET_L2_PPP=y
CONFIG_NET_L2_PPP_TIMEOUT=10000
# IPv4 enables PPP IPCP support
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=n
CONFIG_NET_UDP=y
CONFIG_NET_SOCKETS=y
# DNS
CONFIG_DNS_RESOLVER=y
CONFIG_DNS_RESOLVER_MAX_SERVERS=1
CONFIG_DNS_SERVER_IP_ADDRESSES=y
CONFIG_DNS_SERVER1="8.8.8.8"
# Network management events
CONFIG_NET_CONNECTION_MANAGER=y
# Network management events
CONFIG_NET_CONNECTION_MANAGER=y
and remember to update the CONFIG_MODEM_GSM_APN="your apn"
and set the modem compatible to "zephyr,gsm_ppp"
/* BG95 */
&usart2 {
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3 &usart2_rts_pa1 &usart2_cts_pa0>;
pinctrl-names = "default";
current-speed = <115200>;
status = "okay";
hw-flow-control;
gsm: gsm-modem {
compatible = "zephyr,gsm_ppp";
label = "gsm_ppp";
status = "okay";
};
};
then, you can use this sample application main.c
#include <zephyr/zephyr.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/modem/gsm_ppp.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_conn_mgr.h>
#include <zephyr/net/net_event.h>
#include <zephyr/net/net_mgmt.h>
#define HTTP_PORT 80
#define HTTP_IP ("142.250.74.110")
#define HTTP_HOST "www.google.com"
#define HTTP_PATH "/"
#define SSTRLEN(s) (sizeof(s) - 1)
#define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); exit(1); } }
#define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n"
//static const struct gpio_dt_spec mdm_power = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), mdm_power_gpios);
//static const struct device *modem = DEVICE_DT_GET(DT_ALIAS(modem));
static char recv_buf[1024];
static struct net_mgmt_event_callback mgmt_cb;
static bool network_connected = false;
static void event_handler(struct net_mgmt_event_callback *unus, uint32_t mgmt_event, struct net_if * unus1) {
if (mgmt_event == NET_EVENT_L4_CONNECTED) {
network_connected = true;
}
if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {
network_connected = false;
}
}
int main(void)
{
volatile int ret;
net_mgmt_init_event_callback(&mgmt_cb, event_handler, NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED);
net_mgmt_add_event_callback(&mgmt_cb);
/* Put your GPIO toggles here to turn on modem if not manually turned on */
gsm_ppp_start(modem);
while(network_connected == false) {
k_msleep(500);
}
// Variables
struct zsock_addrinfo address;
static struct sockaddr sock_address;
volatile int sock_fd;
volatile int err;
// Setup info
address.ai_family = AF_INET;
address.ai_protocol = IPPROTO_UDP;
address.ai_socktype = SOCK_DGRAM;
address.ai_addr = &sock_address;
address.ai_addrlen = sizeof(sock_address);
// Create socket address
struct sockaddr_in * sockaddr_cast_ipv4 = net_sin(&sock_address);
sockaddr_cast_ipv4->sin_family = AF_INET;
sockaddr_cast_ipv4->sin_port = htons(HTTP_PORT);
ret = net_addr_pton(AF_INET, HTTP_IP, &sockaddr_cast_ipv4->sin_addr);
// Open socket
sock_fd = zsock_socket(AF_INET, address.ai_socktype, address.ai_protocol);
// Connect
ret = zsock_connect(sock_fd, address.ai_addr, address.ai_addrlen);
err = errno;
// Send
ret = zsock_send(sock_fd, REQUEST, SSTRLEN(REQUEST), 0);
err = errno;
// Disconnect
ret = zsock_close(sock_fd);
err = errno;
gsm_ppp_stop(modem);
return 0;
}
We are working on a completely rewritten driver for the quectel BG95 and BG96 devices based on PPP and which implement power management, but these will not be ready within the next months. With the above solution, you have a stable driver, you just need to manage powering it on and off manually. (which is not possible with the current BG9x driver currently anyway)
Hope this is a satisfactory alternative
Out of curiosity, why is adding UDP support futile, other than the PPP solution already exists?
Additionally, it may be possible to move the sara driver in this direction going forward as well, assuming the PPP works correctly. Are you using the standard PPP driver included in Zephyr?
Out of curiosity, why is adding UDP support futile, other than the PPP solution already exists?
The BG9x driver is at a stage closer to proof of concept than a production ready driver. Adding functionality to it would simply add more points of failure to something that is already unstable.
More importantly for future drivers, the BG9x driver is using socket offload, which means that the entire IP stack is on the modem, so zephyrs well tested implementations based on mbedtls are not available, like MQTT, TLS etc. The modem does support these features (in theory... from experience, the implementations on less used features like FTP(S) are often broken, and since the source code is proprietary, we can't fix it), but the BG9x driver does not even attempt to implement them, getsockopt and setsockopt are simply not implemented.
There is one argument for using the socket_offload, which is to save processing power and other resources, since the entire IP stack is on the modem. So being able to choose the implementation would in theory be a good option for the end user, but, having a single portable library for PPP and implementing it in the drivers is simpler and allows for higher code quality (through reuse and testing)
More relevant info: There is work being done on creating a new device driver model which will allow for multiple interfaces pr device, so we properly can implement extra features like SMS, proper info regarding cell towers for cellular modems etc. For now, modems are "just" network interfaces...
I agree that some of the modem drivers (namely those that use socket offload primarily) are not exactly production ready, especially the BG9x.
That said, if you didn't already know, you can use the TLS_NATIVE
socket options to achieve TLS and other features using socket offload. You can even set the native tls socket priority higher than the offloaded socket priority to achieve this by default for TLS enabled sockets. I'm not sure why the socket offload situation would prevent you from using the MQTT implementation in Zephyr though with TLS_NATIVE
in use.
Not that I disagree with using PPP if available and the host can support the full IP stack. I'd personally prefer to do this on our products using the SARA-R4, which is a very similar application to what you're looking at with the BG9x with GNSS. We may end up writing very similar end drivers down the road.
I would also like to look at what it would take to make the HL7800 work with PPP.
TLS_NATIVE
TLS_NATIVE can work with UDP and TCP support only then, which is good, because that means the socket_offload would only need to support those. The throughput is slower using an offloaded UDP or TCP stack since the communication between modem and driver has a way bigger overhead (open socket -> wait for ok -> connect socket -> wait for connect -> send chunk of 1500 bytes or so -> wait for ok -> repeat) whereas PPP, which is just a L2 interface multiplexed over the uart line has no such limitation.
The plan which i think we should get started on is to leave the drivers that currently exists as they are for now, then create a portable library like the modem_cmd_handler, named modem_ppp, and pull out all portable code from the gsm_ppp.c driver to it, then we can write new drivers and update the old ones, to use this new modem_ppp libary with the modem_cmd_handler and modem_iface_uart libraries.
This should allow us to mass create stable drivers for many different modems, and focus primarily on power management and secondary features (and quirks) in the individual modem drivers.
I think we agree there are many benefits.
The gsm_ppp.c
is the portable modem, if my understanding is correct. Its documentation illustrates how to multiplex AT commands to the driver.
What prevents the current PPP driver from being used portably with the components for specific modems? Is it necessary to wrap the PPP implementation in the modem specific code and is there an obstacle to doing that? The intention seems to be the exact opposite. That doesn't mean the intention makes sense long term.
I think we agree there are many benefits.
The
gsm_ppp.c
is the portable modem, if my understanding is correct. Its documentation illustrates how to multiplex AT commands to the driver.What prevents the current PPP driver from being used portably with the components for specific modems? Is it necessary to wrap the PPP implementation in the modem specific code and is there an obstacle to doing that? The intention seems to be the exact opposite. That doesn't mean the intention makes sense long term.
Power management and modem specific features. PPP only sets up a channel for network communication. CONFIG_MODEM_GSM_SIMCOM and CONFIG_MODEM_GSM_QUECTEL illustrate that there are some settings that are modem specific regarding setting up the communcations channel as well. (typically called chat scripts, here are ones for the quectel modems https://github.com/Quectel-Community/meta-quectel-community/tree/master/recipes-quectel-community/quectel-ppp/quectel-ppp-0.1)
The BG95 driver would contain the ppp functionality, which would set it up the networking. It would also set up the modem specific PPP configurations, control gpios like mdm_power and mdm_reset etc. expose more features like SMS, built in GNSS, possible GPIO muxes etc.
The portable modem_ppp would be similar to the linux ppp daemon (pppd) which sets up networking, using modem specific configurations through chat scripts https://github.com/Quectel-Community/meta-quectel-community/tree/master/recipes-quectel-community/quectel-ppp/quectel-ppp-0.1
The portable modem_ppp would be similar to the linux ppp daemon (pppd) which sets up networking, using modem specific configurations through chat scripts https://github.com/Quectel-Community/meta-quectel-community/tree/master/recipes-quectel-community/quectel-ppp/quectel-ppp-0.1
Right, that's actually my reasoning here. The PPP driver right now is meant to fulfil that same role. Apparently, the KConfigs fill the role of those scripts, in the sense that they select application code.
I suppose whether we refactor gsm_ppp.c
to support more configuration without writing code or entirely replace it by way of creating a new modem_ppp.c
is not really important. Either way I agree we should shore up what we have already. There's a lot to figure out for this new an exciting proposal. I'll try and find some time to work through your PR waiting on the sara driver ASAP.
@rerickson1 is this something to put on a roadmap? Is there a committee for the modem drivers?
If my understanding is correct, another benefit of moving away from socket_offload()
is that it allows the network to be brought "up" after launching the application rather than having to wait for registration/connection like the SARA-R4 driver does now.
If my understanding is correct, another benefit of moving away from
socket_offload()
is that it allows the network to be brought "up" after launching the application rather than having to wait for registration/connection like the SARA-R4 driver does now.
Yes, that is one of the best features IMO, having the driver init just set up the hardware and command handlers etc.
Doesn't PPP encapsulate the UDP packages which are decoded/removed on the remote side by a PPP daemon? In this case it isn't suitable for using with CoAP, isn't it?
Further, extended LPWA modem/network power features as RAI couldn't be used.
@mjm987
PPP is a data link layer, https://en.wikipedia.org/wiki/OSI_model, it simply allows for bytes to be sent, untreated, from one node to another, the IP stack formats the data (or packets) that must be sent from one node to another. PPP is just above the physical layer, which could be ethernet, or GPRS, or some LPWA technology. PPP creates what could best be described as a RAW tunnel, or socket, on which bytes are sent.
This flow shows how data is sent from application to destination using PPP to the modem Application -> IP stack -> (CoAP / MQTT / FTP etc.) -> (TCP / UDP) -> modem -> PPP -> (LPWA, Ethernet, GPRS etc.) -> Destination
Using UDP/TCP stack inside the modem looks like this: Application -> IP stack -> (CoAP / MQTT / FTP etc.) -> modem -> (TCP / UDP) -> (LPWA, Ethernet, GPRS etc.) -> Destination
On Linux, the PPP daemon just sets up the raw socket, which CURL or WGET can then use to send data to a destination. CURL and WGET manage the transfer protocol, like MQTT, HTTPS etc.
Ok, I agree it should be possible but there are still some problem when using PPP:
What I would prefer is a simple generic modem API where I could register the modem response codes, and execute AT command with timeouts which returns a device state as return code.
Is there any way to use the modem command handler directly from an application?
Ok, I agree it should be possible but there are still some problem when using PPP:
1. The server side needs modification by using a PPP Deaemon 2. Additional 6..8 bytes overhead which consumes some power 3. No influence about the connection according provider selection, select the lwpa thechnology (LTE-M or NB-IoT) baseband channels to use, power control (eDRX, RAI) etc. etc.
What I would prefer is a simple generic modem API where I could register the modem response codes, and execute AT command with timeouts which returns a device state as return code.
Is there any way to use the modem command handler directly from an application?
In the future, we will be able to expose configurations like the technology used during runtime using the new mutli API device model. One API i want to create for modems and other co processors is a DTM or direct-access mode which exposes a stream like API to the modem (read/write), to perform things like firmware updating, direct test commands, or other complex actions not easily supported by generic APIs. This could also be used in cases where the modem driver is part of the application which is often the case due to currently poor API support.
Thx for explanation, i got it now ;-) I tested your code with th a little help from the actual gsm-ppp sample and it generally it works! But unfortunately not very reliable: if the modem connection is unreliable or if the modem goes in automatic power down mode, sending data is no more possible. It seems the network stack locks up without error message. Even if the modem is woken-up by gpio or suspend/resume the modem via shell command or adding timeouts by socketopt SO_SNDTIMEO / SO_RCVTIMEO did not help.
I guess for now I will proceed by using the modem command handler directly in my application to have full control about timeouts and errors.
But neverthelss many thx for helping understand the basics and your effort!
@warasilapm Have you been able to find time to work through the issues with this PR and the SARA-R4 update?
This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time.
The IoT LWPA modem driver "quectel-bg9x" does not support UDP but only TCP. Thus low power and low payload protocols as CoAP and LwM2M are not suppported.