After successfully running the GPS and acquiring position the application goes into an infinite wait loop when attempting to start it again, but only if either or both of the following are true:
- The GNSS is stopped using nrf_setsockopt with the NRF_SO_GNSS_STOP option
- The NRF_AF_LOCAL socket is closed
Specifically it hangs at the first attempt to create or use the socket on the second attempt.
If the GNSS is not stopped and the socket is not closed subsequent acquisitions can happen, but this means the GPS is running the whole time. What is the correct approach to achieve low power?
Here is my prj.conf:
# General config CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_MAIN_STACK_SIZE=4096 CONFIG_GPIO=y # LOG stuff CONFIG_LOG=y # Networking CONFIG_NETWORKING=y CONFIG_NET_SOCKETS_OFFLOAD=y CONFIG_NET_SOCKETS=y CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS_POSIX_NAMES=y # BSD library CONFIG_BSD_LIBRARY=y CONFIG_BSD_LIBRARY_TRACE_ENABLED=n
And here is example code which shows this issue:
#include <stdio.h> #include <string.h> #include <zephyr.h> #include <nrf_socket.h> #define AT_XSYSTEMMODE "AT\%XSYSTEMMODE=0,0,1,0" #define AT_MAGPIO "AT\%XMAGPIO=1,0,0,1,1,1574,1577" #define AT_COEX0 "AT\%XCOEX0=1,1,1570,1580" #define AT_CFUN "AT+CFUN=1" static const char at_commands[][31] = { AT_XSYSTEMMODE, AT_MAGPIO, AT_COEX0, AT_CFUN }; static int at_sock = -1; static int fd = -1; static char nmea_strings[10][NRF_GNSS_NMEA_MAX_LEN]; static u32_t nmea_string_cnt; nrf_gnss_data_frame_t last_fix; K_SEM_DEFINE(gps_sem, 1, 1); static void test_periodic_acq(struct k_timer *timer); static bool acquire_gps(void); static int init_gps(void); static int enable_gps(void); static int start_gps(void); static int disable_gps(void); static int process_gps_data(nrf_gnss_data_frame_t *gps_data, bool *p_is_fix_valid, u64_t *p_fix_timestamp); static void print_satellite_stats(nrf_gnss_data_frame_t *pvt_data, u64_t fix_timestamp); K_TIMER_DEFINE(meas_msg_timer, test_periodic_acq, 0); int main(void) { test_periodic_acq(&meas_msg_timer); return 0; } static void test_periodic_acq(struct k_timer *timer) { if (acquire_gps()) { printk("Acquired GPS. Set timer to retry in 10 seconds\n"); k_timer_start(&meas_msg_timer, K_SECONDS(10), 0); } else { printk("Couldn't get GPS semaphore. Try again in 5 seconds\n"); k_timer_start(&meas_msg_timer, K_SECONDS(5), 0); } } static bool acquire_gps(void) { if (0 != k_sem_take(&gps_sem, K_NO_WAIT)) { return false; } printk("Calling init_gps\n"); init_gps(); nrf_gnss_data_frame_t gps_data; u64_t fix_timestamp = k_uptime_get(); bool is_fix_valid = false; u32_t count = 0; printk("Getting GPS data...\n"); start_gps(); printk("GPS started\n"); while (!is_fix_valid) { int num_gps_bytes; do { num_gps_bytes = process_gps_data(&gps_data, &is_fix_valid, &fix_timestamp); } while (num_gps_bytes > 0); count++; if (!is_fix_valid && (1 == (count % 5))) { print_satellite_stats(&gps_data, fix_timestamp); } if (is_fix_valid) { print_satellite_stats(&gps_data, fix_timestamp); break; } k_sleep(K_MSEC(1000)); } printk("Acquisition took %d seconds. Disable returned %d\n", count, disable_gps()); k_sem_give(&gps_sem); return true; } static int init_gps(void) { int retval; nrf_gnss_fix_retry_t fix_retry = 0; nrf_gnss_fix_interval_t fix_interval = 1; nrf_gnss_nmea_mask_t nmea_mask = NRF_GNSS_NMEA_GSV_MASK | NRF_GNSS_NMEA_GSA_MASK | NRF_GNSS_NMEA_GLL_MASK | NRF_GNSS_NMEA_GGA_MASK | NRF_GNSS_NMEA_RMC_MASK; if (enable_gps() != 0) { printk("Failed to enable GPS\n"); return -1; } if (fd < 0) { fd = nrf_socket(NRF_AF_LOCAL, NRF_SOCK_DGRAM, NRF_PROTO_GNSS); if (fd >= 0) { printk("GPS socket created\n"); } else { printk("Could not init socket (err: %d)\n", fd); return -1; } } else { printk("GPS socket already created\n"); } retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_FIX_RETRY, &fix_retry, sizeof(fix_retry)); if (retval != 0) { printk("Failed to set fix retry value\n"); return -1; } retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_FIX_INTERVAL, &fix_interval, sizeof(fix_interval)); if (retval != 0) { printk("Failed to set fix interval value\n"); return -1; } retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_NMEA_MASK, &nmea_mask, sizeof(nmea_mask)); if (retval != 0) { printk("Failed to set nmea mask\n"); return -1; } return 0; } static int enable_gps(void) { int bytes_sent; int bytes_received; char buf[2]; if (at_sock < 0) { at_sock = nrf_socket(NRF_AF_LTE, 0, NRF_PROTO_AT); } else { // GPS has already been configured return 0; } if (at_sock < 0) { return -1; } for (int i = 0; i < ARRAY_SIZE(at_commands); i++) { printk("Sending %s to modem\n", at_commands[i]); bytes_sent = nrf_send(at_sock, at_commands[i], strlen(at_commands[i]), 0); if (bytes_sent < 0) { nrf_close(at_sock); at_sock = -1; return -1; } do { bytes_received = nrf_recv(at_sock, buf, 2, 0); } while (bytes_received == 0); if (memcmp(buf, "OK", 2) != 0) { nrf_close(at_sock); at_sock = -1; return -1; } } nrf_close(at_sock); at_sock = -1; return 0; } static int start_gps(void) { nrf_gnss_delete_mask_t delete_mask = 0; int retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_START, &delete_mask, sizeof(delete_mask)); if (retval != 0) { printk("Failed to start GPS\n"); return -1; } return 0; } static int process_gps_data(nrf_gnss_data_frame_t *gps_data, bool *p_is_fix_valid, u64_t *p_fix_timestamp) { int retval; retval = nrf_recv(fd, gps_data, sizeof(nrf_gnss_data_frame_t), NRF_MSG_DONTWAIT); if (retval > 0) { switch (gps_data->data_id) { case NRF_GNSS_PVT_DATA_ID: if ((gps_data->pvt.flags & NRF_GNSS_PVT_FLAG_FIX_VALID_BIT) == NRF_GNSS_PVT_FLAG_FIX_VALID_BIT) { *p_fix_timestamp = k_uptime_get(); *p_is_fix_valid = true; memcpy(&last_fix, gps_data, sizeof(nrf_gnss_data_frame_t)); nmea_string_cnt = 0; } break; case NRF_GNSS_NMEA_DATA_ID: if (nmea_string_cnt < 10) { memcpy(nmea_strings[nmea_string_cnt++], gps_data->nmea, retval); } break; default: break; } } return retval; } static void print_satellite_stats(nrf_gnss_data_frame_t *p_gps_data, u64_t fix_timestamp) { u8_t tracked = 0; u8_t in_fix = 0; u8_t unhealthy = 0; for (int i = 0; i < NRF_GNSS_MAX_SATELLITES; ++i) { if ((p_gps_data->pvt.sv[i].sv > 0) && (p_gps_data->pvt.sv[i].sv < 33)) { tracked++; if (p_gps_data->pvt.sv[i].flags & NRF_GNSS_SV_FLAG_USED_IN_FIX) { in_fix++; } if (p_gps_data->pvt.sv[i].flags & NRF_GNSS_SV_FLAG_UNHEALTHY) { unhealthy++; } } } printk("Tracking: %d Using: %d Unhealthy: %d\n", tracked, in_fix, unhealthy); printk("Seconds since last fix %lld\n", (k_uptime_get() - fix_timestamp) / 1000); } static int disable_gps(void) { int retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_STOP, NULL, 0); while (retval != 0) { printk("Failed to stop GPS (retval %d)\n", retval); retval = nrf_setsockopt(fd, NRF_SOL_GNSS, NRF_SO_GNSS_STOP, NULL, 0); k_sleep(3000); } retval = nrf_close(fd); if (retval != 0) { printk("Failed to close fd (retval %d)\n", retval); } fd = -1; return 0; }
If the contents of the disable_gps function are commented out periodic GPS acquisitions happen but the code as-is hangs during the second call to init_gps. Example debug output:
***** Booting Zephyr OS build v2.0.99-ncs1 ***** Calling init_gps Sending AT%XSYSTEMMODE=0,0,1,0 to modem Sending AT%XMAGPIO=1,0,0,1,1,1574,1577 to modem Sending AT%XCOEX0=1,1,1570,1580 to modem Sending AT+CFUN=1 to modem GPS socket created Getting GPS data... GPS started Tracking: 1 Using: 0 Unhealthy: 0 Seconds since last fix 0 Tracking: 9 Using: 0 Unhealthy: 0 Seconds since last fix 5 Tracking: 10 Using: 0 Unhealthy: 0 Seconds since last fix 10 Tracking: 11 Using: 0 Unhealthy: 0 Seconds since last fix 15 Tracking: 11 Using: 0 Unhealthy: 0 Seconds since last fix 20 Tracking: 12 Using: 0 Unhealthy: 0 Seconds since last fix 25 Tracking: 12 Using: 0 Unhealthy: 0 Seconds since last fix 30 Tracking: 11 Using: 8 Unhealthy: 0 Seconds since last fix 0 Acquisition took 35 seconds. Disable returned 0 Acquired GPS. Set timer to retry in 10 seconds Calling init_gps