Upcoming maintenance
Dear Customers and Partners.
This website will be undergoing scheduled maintenance on June 14, 2023. Please be aware there may be disruption to the developer portal website and associated services during the scheduled maintenance period.
This upgrade is essential to ensure the continued performance, reliability, and security of Developer World.
We apologize for any inconvenience.
[Arduino SDK] Signal setup in GNSS library causes polling file descriptors incl. sleep to fail
-
Observation
The GNSS library sets up a signal in its begin method to get notifications for GNSS ready events.
However after GNSS.begin, poll functions start to fail with errno 4 "Interrupted system call".Issue
- I am setting up several threads in my modules to periodically perform some actions and sleep in between. However the sleep() and usleep() return early exactly after 1 second. The issue is that the underlying clock_nanosleep returns because it has been interrupted by a signal (documentation in the sig_nanosleep.c file)
- I have the geofence sample in another thread. (I am mixing Arduino and SDK samples as Geofence is not exposed in the Arduino SDK) The poll functions there also returns with errno 4.
Questions
- Is there a proper solution besides
- just removing the signal setup and just poll without knowing when the next GNSS data is ready
- or guard the sleep with additional checks like if (navData.time.sec != LastPrintMin) or just poll again for geofence ignoring the error.
- Is my approach of having multiple threads that wait with sleep to achieve a periodically recurrent task good or is there a better approach (Initial idea was to stick with the Arduino SDK)?
- I think I read somewhere that you can only have one timer for a delayed task.
- Maybe I need to have a look into this? It looks like <nuttx/wqueue.h> is available in the Arduino SDK.
https://nuttx.apache.org/docs/latest/reference/os/wqueue.html#work-queue-interfaces
-
Hey, @jens6151-0-1-1
I'm still unsure on how to approach this..
Could you think of a minimal application that still presents this issue and send the code so we can better analyze it together? -
I tried to strip it down to a minimal application.
Base is mostly the GNSS and Geofence sample.
It is not required to actually get any GNSS signal for reproduction.Main .ino file
#include <Arduino.h> #include <GNSS.h> SpGnss Gnss; SpNavData navData; pthread_t pollGnssThread = -1; pthread_t pollGeofenceThread = -1; #define DEFAULT_GEOFENCE_RADIUS_HOME 100 #define DEFAULT_GEOFENCE_RADIUS_CURRENT 50 // Not important #define SECRET_LOCATION_HOME1_LAT 20.0 #define SECRET_LOCATION_HOME1_LNG 40.0 int add_geofence(double targetLatD, double targetLngD, uint8_t id, uint16_t radius, uint16_t deadzone = 5, uint16_t dwell_detecttime = 10); int poll_geofence(); void _pollGnss() { if (Gnss.waitUpdate(1)) { /* Get NaviData. */ Gnss.getNavData(&navData); Serial.printf("%d Satellites seen.\n", navData.numSatellites); if (navData.posDataExist && navData.posFixMode != FixInvalid) { Serial.println("GPS fix."); } } else { /* Not update. */ Serial.println("no Gnss data update"); } } int pollGnss(int argc, FAR char *argv[]) { while (true) { _pollGnss(); } return 0; } void initGnss() { if (pollGnssThread < 0) { int result; /* Activate GNSS device */ result = Gnss.begin(); if (result != 0) { Serial.println("Gnss begin error!!"); } else { Gnss.select(GPS); // skipped setting the time received by LTE for the HOT_START /* Start positioning */ result = Gnss.start(HOT_START); if (result != 0) { Serial.printf("Gnss hot start failed with %d. Retry with cold start.\n", result); } else { Serial.println("Gnss setup OK"); } } add_geofence(SECRET_LOCATION_HOME1_LAT, SECRET_LOCATION_HOME1_LNG, 0, DEFAULT_GEOFENCE_RADIUS_HOME); pthread_create(&pollGnssThread, NULL, (pthread_startroutine_t)pollGnss, NULL); pthread_setname_np(pollGnssThread, "pollGnss"); assert(pollGnssThread > 0); } if (pollGeofenceThread < 0) { pthread_create(&pollGeofenceThread, NULL, (pthread_startroutine_t)poll_geofence, NULL); pthread_setname_np(pollGeofenceThread, "pollGeofenceThread"); assert(pollGeofenceThread > 0); } } void setup() { Serial.begin(115200); while (!Serial) { } initGnss(); } void loop() { sleep(1); }
.cpp file in the Arduino project next to the ino file
/**************************************************************************** * geofence/geofence_main.c * * Copyright 2018 Sony Semiconductor Solutions Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of Sony Semiconductor Solutions Corporation nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ #include <Arduino.h> // for the Serial.print // must be included first!! one of below includes requires this!! #include <stdint.h> /**************************************************************************** * Included Files ****************************************************************************/ #include <arch/chip/geofence.h> #include <arch/chip/gnss.h> #include <errno.h> #include <fcntl.h> #include <nuttx/config.h> #include <poll.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/types.h> /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define POLL_FD_NUM 1 #define POLL_TIMEOUT_FOREVER -1 #define DOUBLE_TO_LONG(x) ((long)(x * 1000000.0)) /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ static int g_fdgeo = -1; static struct pollfd g_fds[POLL_FD_NUM] = {{0}}; static struct cxd56_geofence_status_s g_geofence_status; /**************************************************************************** * Private Functions ****************************************************************************/ int add_geofence(double targetLatD, double targetLngD, uint8_t id, uint16_t radius, uint16_t deadzone = 5, uint16_t dwell_detecttime = 10) { int ret; struct cxd56_geofence_mode_s mode; long targetLat = DOUBLE_TO_LONG(targetLatD); long targetLng = DOUBLE_TO_LONG(targetLngD); /* Region data. */ struct cxd56_geofence_region_s region; /* Get file descriptor to control Geofence. */ g_fdgeo = open("/dev/geofence", O_RDONLY); if (g_fdgeo <= 0) { Serial.println("Error Geofence open"); return -ENODEV; } mode.deadzone = deadzone; mode.dwell_detecttime = dwell_detecttime; ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_SET_MODE, (unsigned long)&mode); if (ret < 0) { Serial.println("Error Geofence set mode"); return ret; } /* All clean region data */ ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_DELETE, id); if (ret < 0) { Serial.printf("Delete region %d error code %d\n", id, ret); // return ret; } /* Set region data */ region.id = id; region.latitude = targetLat; region.longitude = targetLng; region.radius = radius; ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_ADD, (unsigned long)®ion); if (ret < 0) { Serial.println("Error Add region"); return ret; } Serial.printf("Added Geofence at (%ld, %ld) r=%d\n", region.latitude, region.longitude, region.radius); return ret; } int remove_geofence(uint8_t id) { int ret; ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_DELETE, id); if (ret < 0) { Serial.printf("Delete region %d error code: %d\n", id, ret); return ret; } return ret; } int poll_geofence() { int ret; g_fdgeo = open("/dev/geofence", O_RDONLY); if (g_fdgeo < 0) { Serial.println("Geofence fd not open"); return; } /* Wait transition status notify */ g_fds[0].fd = g_fdgeo; g_fds[0].events = POLLIN; while (1) { ret = poll(g_fds, POLL_FD_NUM, POLL_TIMEOUT_FOREVER); if (ret <= 0) { Serial.printf("poll error %d,%x,%x - errno:%d\n", ret, g_fds[0].events, g_fds[0].revents, get_errno()); break; } if (g_fds[0].revents & POLLIN) { ret = read(g_fdgeo, &g_geofence_status, sizeof(struct cxd56_geofence_status_s)); if (ret < 0) { Serial.println("Error read geofence status data"); break; } else if (ret != sizeof(struct cxd56_geofence_status_s)) { Serial.printf("Size error read geofence status data %d:%d\n", ret, sizeof(struct cxd56_geofence_status_s)); ret = ERROR; break; } else { ret = OK; } /* Check updated region */ Serial.printf("[GEO] Updated region:%d \n", g_geofence_status.update); /* Check region status */ for (int i = 0; i < g_geofence_status.update; i++) { Serial.printf(" ID:%d, Status:\n", g_geofence_status.status[i].id); switch (g_geofence_status.status[i].status) { case CXD56_GEOFENCE_TRANSITION_EXIT: { Serial.println("EXIT"); } break; case CXD56_GEOFENCE_TRANSITION_ENTER: { Serial.println("ENTER"); } break; case CXD56_GEOFENCE_TRANSITION_DWELL: { Serial.println("DWELL"); } break; default: { Serial.println("UNKNOWN"); } break; } } } } Serial.println("Exit Geofence poll thread."); return ret; } int stop_geofence() { int ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_STOP, 0); if (ret < 0) { Serial.println("Error stop geofence"); } ret = close(g_fdgeo); if (ret < 0) { printf("Error close geofence"); } g_fdgeo = -1; return ret; }
The serial output
Gnss setup OK Delete region 0 error code -1 Added Geofence at (20000000, 40000000) r=100 no Gnss data update no Gnss data update poll error -1,1,0 - errno:4 Exit Geofence poll thread. 0 Satellites seen.
Note the poll error
-
This is the sample of the architecture I am wondering if it is good.
#include <Arduino.h> pthread_t app1ThreadId = -1; pthread_t app2ThreadId = -1; int app1Thread(int argc, FAR char *argv[]) { while (true) { Serial.println("App 1 does some stuff"); sleep(10); } return 0; } void initApp1() { /* Do some initialization */ pthread_create(&app1ThreadId, NULL, (pthread_startroutine_t)app1Thread, NULL); pthread_setname_np(app1ThreadId, "app1Thread"); } int app2Thread(int argc, FAR char *argv[]) { while (true) { Serial.println("App 2 does some stuff"); sleep(1); } return 0; } void initApp2() { /* Do some initialization */ pthread_create(&app2ThreadId, NULL, (pthread_startroutine_t)app2Thread, NULL); pthread_setname_np(app2ThreadId, "app2Thread"); } void setup() { Serial.begin(115200); while (!Serial) { } initApp1(); initApp2(); // initApp3, initApp4, initApp5, initApp6, initApp7 // Apps could be Camera, GNSS, LTE, Cloud, Display, etc. } void loop() { sleep(1); }
Actually I am running also an event loop inside the loop functions and apps communicate with each other via these events.
These are the types for triggering actions
- polling (GNSS)
- required call in a loop (lvgl in module Display)
- recurring task (update weather forecast every hour)
- user interaction or mqtt callback. (send me the current position)
-
Hey, @jens6151-0-1-1
I'll take a look at it as soon as possible! -
@CamilaSouza
Thanks for taking care.
Just to clarify about my priorities as you are helping a lot of people here.Regarding the GNSS library issue, I can live with the workaround to just comment it out. Knowing more would be nice.
Getting some ideas on the architecture would be appreciated.
Otherwise I am also interested in "Access SPI Flash via memory map" as I have a font with CJK characters (about 30K in size). Unfortunately the characters arrive by http so I cannot reduce glyphs to what I need. I investigated the code, drivers and data sheet and did not see a possibility. -
@jens6151-0-1-1
Thank you, that's awesome.
I noticed you have been helping out in the forum as well. That's highly appreciated!I'm waiting for answers on the "Access SPI Flash via memory map", so I'll let you guys know when I get them
-
Hey @jens6151-0-1-1
I also get the same error while running your sample application.
I'm thinking..
I know you said you wanted to stay withing the Arduino IDE. But..
In the SDK you have the geofence example and I found this nuttx example on polling:
https://github.com/sonydevworld/spresense-nuttx-apps/blob/new-master/examples/poll/poll_main.cDoes this help you at all?
-
@jens6151-0-1-1
Also, I received a response about accessing Flash via memory map.Unfortunately, because of security issue, we don’t open the memory mapped access by application CPUs.
Application CPU must request read from/write to the flash to M0P. -
Thanks for the info on the Flash.
Also for pointing me to the example for polling. I had a look at it and interpret it as an idea to communicate between modules that have been started by different threads.To use the Arduino IDE was my self-imposed guideline for my project. However there are limitations, so I do not always stick to it. So it is fine to follow SDK examples.
I think this topic can be marked as soved.