drivers: modem: hl7800: Add Position over LTE (PoLTE)

Position over LTE (PoLTE) can be used to locate a device as
an alternative to GNSS.
Increase RX thread stack size for PoLTE processing.

Signed-off-by: Ryan Erickson <ryan.erickson@lairdconnect.com>
This commit is contained in:
Ryan Erickson 2021-08-19 14:18:55 -05:00 committed by Anas Nashif
commit aba8a21001
3 changed files with 373 additions and 1 deletions

View file

@ -244,7 +244,8 @@ endif # MODEM_HL7800_LOW_POWER_MODE
config MODEM_HL7800_RX_STACK_SIZE config MODEM_HL7800_RX_STACK_SIZE
int "Size of the stack for the HL7800 modem driver RX thread" int "Size of the stack for the HL7800 modem driver RX thread"
default 1024 default 1536 if MODEM_HL7800_POLTE
default 1280
help help
This stack is used by the HL7800 RX thread. This stack is used by the HL7800 RX thread.
@ -299,4 +300,7 @@ config MODEM_HL7800_USE_GLONASS
bool "Use GLONASS in addition to GPS" bool "Use GLONASS in addition to GPS"
depends on MODEM_HL7800_GPS depends on MODEM_HL7800_GPS
config MODEM_HL7800_POLTE
bool "Enable PoLTE commands and handlers"
endif # MODEM_HL7800 endif # MODEM_HL7800

View file

@ -1162,6 +1162,64 @@ error:
} }
#endif /* CONFIG_MODEM_HL7800_GPS */ #endif /* CONFIG_MODEM_HL7800_GPS */
#ifdef CONFIG_MODEM_HL7800_POLTE
int32_t mdm_hl7800_polte_register(void)
{
int ret = -1;
hl7800_lock();
wakeup_hl7800();
/* register for events */
SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"REGISTER\",1");
SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"LOCATION\",1");
/* register with polte.io */
SEND_AT_CMD_EXPECT_OK("AT%POLTECMD=\"REGISTER\"");
error:
LOG_DBG("PoLTE register status: %d", ret);
allow_sleep(true);
hl7800_unlock();
return ret;
}
int32_t mdm_hl7800_polte_enable(char *user, char *password)
{
int ret = -1;
char buf[sizeof(MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR) +
MDM_HL7800_MAX_POLTE_USER_ID_SIZE + MDM_HL7800_MAX_POLTE_PASSWORD_SIZE] = { 0 };
hl7800_lock();
wakeup_hl7800();
/* register for events */
SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"REGISTER\",1");
SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"LOCATION\",1");
/* restore user and password (not saved in NV by modem) */
snprintk(buf, sizeof(buf), MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR, user, password);
ret = send_at_cmd(NULL, buf, MDM_CMD_SEND_TIMEOUT, MDM_DEFAULT_AT_CMD_RETRIES, false);
error:
LOG_DBG("PoLTE register status: %d", ret);
allow_sleep(true);
hl7800_unlock();
return ret;
}
int32_t mdm_hl7800_polte_locate(void)
{
int ret = -1;
hl7800_lock();
wakeup_hl7800();
SEND_AT_CMD_EXPECT_OK("AT%POLTECMD=\"LOCATE\",2,1");
error:
LOG_DBG("PoLTE locate status: %d", ret);
allow_sleep(true);
hl7800_unlock();
return ret;
}
#endif /* CONFIG_MODEM_HL7800_POLTE */
void mdm_hl7800_generate_status_events(void) void mdm_hl7800_generate_status_events(void)
{ {
hl7800_lock(); hl7800_lock();
@ -2547,6 +2605,240 @@ static bool on_cmd_ver_speed(struct net_buf **buf, uint16_t len)
} }
#endif /* CONFIG_MODEM_HL7800_GPS */ #endif /* CONFIG_MODEM_HL7800_GPS */
#ifdef CONFIG_MODEM_HL7800_POLTE
/* Handler: %POLTEEVU: "REGISTER",0, <mqttAuthUser>, <mqttAuthPassword> */
static bool on_cmd_polte_registration(struct net_buf **buf, uint16_t len)
{
char rsp[MDM_MAX_RESP_SIZE] = { 0 };
size_t rsp_len = sizeof(rsp) - 1;
char *rsp_end = rsp + rsp_len;
struct mdm_hl7800_polte_registration_event_data data;
struct net_buf *frag = NULL;
size_t out_len;
char *location;
bool parsed;
memset(&data, 0, sizeof(data));
wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
location = rsp;
parsed = false;
frag = NULL;
len = net_buf_findcrlf(*buf, &frag);
do {
if (!frag) {
LOG_ERR("Unable to find end");
break;
}
if (len > rsp_len) {
LOG_WRN("string too long (len:%d)", len);
len = rsp_len;
}
out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
rsp[out_len] = 0;
/* Command handler looks for string up to the user field */
location = strstr(location, "\"");
if (location != NULL && location < rsp_end) {
location += 1;
if (location >= rsp_end) {
break;
}
data.user = location;
} else {
break;
}
/* Find end of user field and null terminate string */
location = strstr(location, "\"");
if (location != NULL && location < rsp_end) {
*location = 0;
location += 1;
if (location >= rsp_end) {
break;
}
} else {
break;
}
location = strstr(location, ",\"");
if (location != NULL && location < rsp_end) {
location += 2;
if (location >= rsp_end) {
break;
}
data.password = location;
} else {
break;
}
location = strstr(location, "\"");
if (location != NULL && location < rsp_end) {
*location = 0;
} else {
break;
}
parsed = true;
} while (0);
if (parsed && data.user && data.password) {
data.status = 0;
} else {
data.status = -1;
LOG_ERR("Unable to parse PoLTE registration");
}
event_handler(HL7800_EVENT_POLTE_REGISTRATION, &data);
return true;
}
/* Handler: %POLTECMD: "LOCATE",<res> */
static bool on_cmd_polte_locate_cmd_rsp(struct net_buf **buf, uint16_t len)
{
char rsp[sizeof("99")] = { 0 };
size_t rsp_len = sizeof(rsp) - 1;
size_t out_len;
struct net_buf *frag = NULL;
struct mdm_hl7800_polte_location_data data;
memset(&data, 0, sizeof(data));
wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
data.status = -1;
frag = NULL;
len = net_buf_findcrlf(*buf, &frag);
do {
if (!frag) {
LOG_ERR("Unable to find end");
break;
}
if (len > rsp_len) {
LOG_WRN("string too long (len:%d)", len);
len = rsp_len;
}
out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
rsp[out_len] = 0;
data.status = (uint32_t)strtoul(rsp, NULL, 10);
} while (0);
event_handler(HL7800_EVENT_POLTE_LOCATE_STATUS, &data);
return true;
}
/* Handler:
* %POLTEEVU: "LOCATION",<stat>[,<latitude>,<longitude>,<time>,<confidence>]
*/
static bool on_cmd_polte_location(struct net_buf **buf, uint16_t len)
{
char rsp[MDM_MAX_RESP_SIZE] = { 0 };
size_t rsp_len = sizeof(rsp) - 1;
char *rsp_end = rsp + rsp_len;
struct net_buf *frag = NULL;
size_t out_len = 0;
char *start;
char *end;
bool parsed;
struct mdm_hl7800_polte_location_data data;
static const char POLTE_LOC_DELIMITER[] = "\",\"";
memset(&data, 0, sizeof(data));
wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
parsed = false;
frag = NULL;
len = net_buf_findcrlf(*buf, &frag);
do {
if (!frag) {
LOG_ERR("Unable to find end");
break;
}
if (len > rsp_len) {
LOG_WRN("string too long (len:%d)", len);
len = rsp_len;
}
out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
rsp[out_len] = 0;
data.status = -1;
start = rsp;
end = "";
/* Comma isn't present when there is an error. */
start = strstr(start, ",");
if (start != NULL && start < rsp_end) {
*start = ' ';
start += 1;
}
data.status = (uint32_t)strtoul(rsp, &end, 10);
if (data.status != 0) {
LOG_WRN("Response not received from PoLTE server: %d", data.status);
data.status = MDM_HL7800_POLTE_SERVER_ERROR;
parsed = true;
break;
} else if (start >= rsp_end) {
break;
}
start = strstr(start, "\"") + 1;
end = strstr(start, POLTE_LOC_DELIMITER);
if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
memcpy(data.latitude, start, MIN(end - start, sizeof(data.latitude) - 1));
} else {
break;
}
start = end + strlen(POLTE_LOC_DELIMITER);
end = strstr(start, POLTE_LOC_DELIMITER);
if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
memcpy(data.longitude, start, MIN(end - start, sizeof(data.longitude) - 1));
} else {
break;
}
start = end + strlen(POLTE_LOC_DELIMITER);
end = strstr(start, POLTE_LOC_DELIMITER);
if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
data.timestamp = (uint32_t)strtoul(start, NULL, 10);
} else {
break;
}
start = end + strlen(POLTE_LOC_DELIMITER);
end = strstr(start, "\"");
if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
memcpy(data.confidence_in_meters, start,
MIN(end - start, sizeof(data.confidence_in_meters) - 1));
} else {
break;
}
parsed = true;
} while (0);
if (!parsed) {
LOG_HEXDUMP_ERR(rsp, out_len, "Unable to parse PoLTE location");
} else {
LOG_HEXDUMP_DBG(rsp, out_len, "PoLTE Location");
}
event_handler(HL7800_EVENT_POLTE, &data);
return true;
}
#endif /* CONFIG_MODEM_HL7800_POLTE */
static void notify_all_tcp_sockets_closed(void) static void notify_all_tcp_sockets_closed(void)
{ {
int i; int i;
@ -3768,6 +4060,11 @@ static void hl7800_rx(void)
CMD_HANDLER("VerSpeed: ", ver_speed), CMD_HANDLER("VerSpeed: ", ver_speed),
#endif #endif
#ifdef CONFIG_MODEM_HL7800_POLTE
CMD_HANDLER("%POLTEEVU: \"REGISTER\",0,", polte_registration),
CMD_HANDLER("%POLTECMD: \"LOCATE\",", polte_locate_cmd_rsp),
CMD_HANDLER("%POLTEEVU: \"LOCATION\",", polte_location),
#endif
}; };
while (true) { while (true) {

View file

@ -72,6 +72,18 @@ struct mdm_hl7800_apn {
#define MDM_HL7800_MAX_GPS_STR_SIZE 33 #define MDM_HL7800_MAX_GPS_STR_SIZE 33
#define MDM_HL7800_MAX_POLTE_USER_ID_SIZE 16
#define MDM_HL7800_MAX_POLTE_PASSWORD_SIZE 16
#define MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE 33
/* Assign the server error code (location response) to a value
* that isn't used by locate response so that a single status
* callback can be used.
*/
#define MDM_HL7800_POLTE_SERVER_ERROR 10
#define MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR "AT%%POLTECMD=\"SERVERAUTH\",\"%s\",\"%s\""
enum mdm_hl7800_radio_mode { MDM_RAT_CAT_M1 = 0, MDM_RAT_CAT_NB1 }; enum mdm_hl7800_radio_mode { MDM_RAT_CAT_M1 = 0, MDM_RAT_CAT_NB1 };
enum mdm_hl7800_event { enum mdm_hl7800_event {
@ -90,6 +102,9 @@ enum mdm_hl7800_event {
HL7800_EVENT_REVISION, HL7800_EVENT_REVISION,
HL7800_EVENT_GPS, HL7800_EVENT_GPS,
HL7800_EVENT_GPS_POSITION_STATUS, HL7800_EVENT_GPS_POSITION_STATUS,
HL7800_EVENT_POLTE_REGISTRATION,
HL7800_EVENT_POLTE_LOCATE_STATUS,
HL7800_EVENT_POLTE
}; };
enum mdm_hl7800_startup_state { enum mdm_hl7800_startup_state {
@ -182,6 +197,26 @@ enum mdm_hl7800_gps_string_types {
HL7800_GPS_STR_VER_SPEED HL7800_GPS_STR_VER_SPEED
}; };
/* status: negative errno, 0 on success
* user and password aren't valid if status is non-zero.
*/
struct mdm_hl7800_polte_registration_event_data {
int status;
char *user;
char *password;
};
/* status: negative errno, 0 on success, non-zero error code
* Data is not valid if status is non-zero.
*/
struct mdm_hl7800_polte_location_data {
uint32_t timestamp;
int status;
char latitude[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
char longitude[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
char confidence_in_meters[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
};
/** /**
* event - The type of event * event - The type of event
* event_data - Pointer to event specific data structure * event_data - Pointer to event specific data structure
@ -199,6 +234,9 @@ enum mdm_hl7800_gps_string_types {
* HL7800_EVENT_REVISION - string * HL7800_EVENT_REVISION - string
* HL7800_EVENT_GPS - compound event * HL7800_EVENT_GPS - compound event
* HL7800_EVENT_GPS_POSITION_STATUS int * HL7800_EVENT_GPS_POSITION_STATUS int
* HL7800_EVENT_POLTE_REGISTRATION mdm_hl7800_polte_registration_event_data
* HL7800_EVENT_POLTE mdm_hl7800_polte_location_data
* HL7800_EVENT_POLTE_LOCATE_STATUS int
*/ */
typedef void (*mdm_hl7800_event_callback_t)(enum mdm_hl7800_event event, typedef void (*mdm_hl7800_event_callback_t)(enum mdm_hl7800_event event,
void *event_data); void *event_data);
@ -370,6 +408,39 @@ int32_t mdm_hl7800_set_functionality(enum mdm_hl7800_functionality mode);
*/ */
int32_t mdm_hl7800_set_gps_rate(uint32_t rate); int32_t mdm_hl7800_set_gps_rate(uint32_t rate);
/**
* @brief Register modem/SIM with polte.io
*
* @note It takes around 30 seconds for HL7800_EVENT_POLTE_REGISTRATION to
* be generated. If the applications saves the user and password
* information into non-volatile memory, then this command
* only needs to be run once.
*
* @return int32_t negative errno, 0 on success
*/
int32_t mdm_hl7800_polte_register(void);
/**
* @brief Enable PoLTE.
*
* @param user from polte.io or register command callback
* @param password from polte.io register command callback
* @return int32_t negative errno, 0 on success
*/
int32_t mdm_hl7800_polte_enable(char *user, char *password);
/**
* @brief Locate device using PoLTE.
*
* @note The first HL7800_EVENT_POLTE_LOCATE_STATUS event indicates
* the status of issuing the locate command. The second event
* requires 20-120 seconds to be generated and it contains the
* location information (or indicates server failure).
*
* @return int32_t negative errno, 0 on success
*/
int32_t mdm_hl7800_polte_locate(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif