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:
parent
f094346e18
commit
aba8a21001
3 changed files with 373 additions and 1 deletions
|
@ -244,7 +244,8 @@ endif # MODEM_HL7800_LOW_POWER_MODE
|
|||
|
||||
config MODEM_HL7800_RX_STACK_SIZE
|
||||
int "Size of the stack for the HL7800 modem driver RX thread"
|
||||
default 1024
|
||||
default 1536 if MODEM_HL7800_POLTE
|
||||
default 1280
|
||||
help
|
||||
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"
|
||||
depends on MODEM_HL7800_GPS
|
||||
|
||||
config MODEM_HL7800_POLTE
|
||||
bool "Enable PoLTE commands and handlers"
|
||||
|
||||
endif # MODEM_HL7800
|
||||
|
|
|
@ -1162,6 +1162,64 @@ error:
|
|||
}
|
||||
#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)
|
||||
{
|
||||
hl7800_lock();
|
||||
|
@ -2547,6 +2605,240 @@ static bool on_cmd_ver_speed(struct net_buf **buf, uint16_t len)
|
|||
}
|
||||
#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)
|
||||
{
|
||||
int i;
|
||||
|
@ -3768,6 +4060,11 @@ static void hl7800_rx(void)
|
|||
CMD_HANDLER("VerSpeed: ", ver_speed),
|
||||
#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) {
|
||||
|
|
|
@ -72,6 +72,18 @@ struct mdm_hl7800_apn {
|
|||
|
||||
#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_event {
|
||||
|
@ -90,6 +102,9 @@ enum mdm_hl7800_event {
|
|||
HL7800_EVENT_REVISION,
|
||||
HL7800_EVENT_GPS,
|
||||
HL7800_EVENT_GPS_POSITION_STATUS,
|
||||
HL7800_EVENT_POLTE_REGISTRATION,
|
||||
HL7800_EVENT_POLTE_LOCATE_STATUS,
|
||||
HL7800_EVENT_POLTE
|
||||
};
|
||||
|
||||
enum mdm_hl7800_startup_state {
|
||||
|
@ -182,6 +197,26 @@ enum mdm_hl7800_gps_string_types {
|
|||
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_data - Pointer to event specific data structure
|
||||
|
@ -199,6 +234,9 @@ enum mdm_hl7800_gps_string_types {
|
|||
* HL7800_EVENT_REVISION - string
|
||||
* HL7800_EVENT_GPS - compound event
|
||||
* 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,
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue