Azuma_wifi_switch/main/modem.c

3110 lines
96 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* bg96.c
*
* Created on: Jan 11, 2023
* Author: Sword
*/
/* Includes -----------------------------------------------------------------*/
#include "uart_ifx.h"
#include "port.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
#include <stdlib.h>
#include "esp_timer.h"
#include <stdbool.h>
#include <stdio.h>
#include "rtc.h"
#include "modem.h"
static const char* TAG = "BG77";
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "esp_log.h"
/* Logging configuration */
#define LOG_MODULE "BG77"
#define LOG_LEVEL LOG_LEVEL_INFO
/* Local buffer to formulate commands as necessary */
#define MODEM_TX_BUFFER_LEN 3072
/* Modem receiver buffer: expected to hold entire response for any command */
#define MODEM_RX_BUFFER_LEN 4096
/* This drive is expecting this particular device in the ATI response */
#define MODEM_DEVICE_MATCH_STRING "BG77"
/* Match the expected service mode int the QCSQ command for signal strength.
* For CAT-M1, string differs my modem model, e.g. "CAT-M1" for BG96, "eMTC" for BG77
*/
#define MODEM_QCSQ_SERIVCE_MODE_MATCH_1 "eMTC"
#define MODEM_QCSQ_SERIVCE_MODE_MATCH_2 "NBIoT"
/* Modem QFREAD chunk size. This must fit into the RX buffer taking into
* account possible responses
*/
#define MODEM_FREAD_SIZE (MODEM_RX_BUFFER_LEN - 128)
/* Maximum number of QFREAD command issuances (retries) we will use to transfer a file.
* The actual number of QFREAD command issuances is dependent upon the file size determined by QFLST.
*/
#define MODEM_FREAD_MAX_PACKET_TRANSFERS 196
#define MODEM_IMEI_START 2 /* ignore first 2 characters */
#define MODEM_IMEI_LEN 16
#define BG96_ICCID_START 10 /* ignore first 8 characters */
#define BG96_ICCID_LEN 20
#define JWT_LEN 442
/* Max number of bytes that can be sent after QMTPUB command */
#define MODEM_MQTT_PUB_BYTES_MAX 1548
/* Set to 1 to enable the use of BG96 STATUS pin to improve reliability
* and speed of modem power up and shutdown operations.
*/
#define MODEM_USE_STATUS_PIN 1
/* Time to wait for modem to turn on */
#define MODEM_STARTUP_STATUS_TIMEOUT_MS 10000
/* Time to wait for modem AT interface to ready */
#define MODEM_STARTUP_RDY_TIMEOUT_MS 5000
/* Granularity of the process timer for polling events */
#define MODEM_TIMER_GRANULARITY_MS 500
/* Maximum number of commands supported in one sequence */
#define MODEM_MAX_SEQUENCE 17
/* Unique timer ID for this process for use with timer module */
//#define MODEM_TIMER_ID 0
/* 500-1000 ms */
#define MODEM_PWRKEY_ON_DELAY_MS 750
/* 650-1500 ms */
#define MODEM_PWRKEY_OFF_DELAY_MS 1000
/* >= 2000 ms */
#define MODEM_PWRKEY_RESET_DELAY_MS 2500
/* Set to enable QHTTP OK parsing, clear to timeout and parse anyways */
#define USE_QHTTPREAD_OK 1
/* Periodic monitoring of STATUS while waiting to confirm shutdown */
#define MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS 2000
#define MODEM_SHUTDOWN_COUNT_WARNING_SEC 10
#define MODEM_SHUTDOWN_COUNT_LIMIT_SEC 65
/* Indicate modem power as indicated with STATUS line on green LED. */
#define MODEM_DEBUG_MODEM_POWER_STATE 0
/* Enable the "blind modem" option to ignore the STATUS line state
* and the "APP RDY" indication when turning on and off the modem.
*/
#define MODEM_BLIND_MODEM 0
#define MODEM_MODEM_FWVER_FROM_QGMR 1
/* ------------------------------------------------------------------------- */
typedef enum {
MODEM_EVENT_START,
MODEM_EVENT_TIMER,
MODEM_EVENT_UART,
MODEM_EVENT_STOP,
MODEM_EVENT_SEND_CMD,
} modem_event_t;
/* State of the bg96 process */
typedef enum {
MODEM_STATE_INIT,
MODEM_STATE_READY,
MODEM_STATE_WAIT_POWERUP,
MODEM_STATE_WAIT_STARTUP,
MODEM_STATE_WAIT_STARTUP_RDY,
MODEM_STATE_WAIT_POWERDOWN,
MODEM_STATE_WAIT_POWERKEY,
MODEM_STATE_CMD,
} modem_state_t;
/* Status is OK (match response), ERROR (match error) or TIMEOUT (retries and/or wait exhausted).
* Returns OK or ERROR in processing to help direct which command to call subsequently in
* the sequence.
*/
typedef int (*modem_cmd_handler_t)(int status, const char *buf, int len);
/* Command setup function, passed the parameters
* Intended to write to tx buf and assign that as the command.
* Return OK to assign buf as cmd_str.
* Must print a null-terminated string.
*/
typedef int (*modem_cmd_setup_t)(const void *params, char *buf, int len);
/* Allows command to send custom information */
typedef int (*modem_send_t)(void);
typedef struct {
const char *host;
int port;
const char *client_id;
//const char* jwt;
const char *pub_topic;
const char *pub_data;
int pub_data_len;
} modem_mqtt_setup_params_t;
typedef struct {
/* input params */
const char *url;
const char *file; /* for get_to_file */
const char *post_message;
/* output params */
char *buf;
int len;
} modem_http_setup_params_t;
typedef struct {
const char *file;
uint32_t file_size;
uint32_t transferred_bytes;
uint16_t chunk_size;
uint16_t expected_bytes;
uint16_t read_size;
uint16_t file_handle; /* from +QFOPEN: <filehandle> */
modem_read_from_file_open open_cb;
modem_read_from_file_data data_cb;
} modem_file_params_t;
typedef struct {
const char *cmd_str;
const char *response_match_str; /* null allowed */
const char *err_match_str; /* null allowed */
const int wait_ms;
const int retries;
/* Called after command is sent */
const modem_send_t send;
/* Called on timeout or match on response or err strings. */
const modem_cmd_handler_t handler;
/* Called on command setup */
const modem_cmd_setup_t setup;
/* Pointer to setup parameters */
const void *params;
} modem_cmd_t;
typedef struct {
modem_state_t state;
/* Timer */
bool poll_timer;
/* UART queue flag */
bool poll_uart;
/* Poll periods counter in units of timer granularity */
int poll_periods;
/* User start callback */
modem_start_cb_t start_cb;
/* Command processing state */
bool is_startup; /* in startup mode */
/* Command sequencer */
const modem_cmd_t *cmd_list[MODEM_MAX_SEQUENCE]; /* list of commands*/
int cmd_num; /* number in list */
int cmd_retries; /* count of retries */
int cmd_sequence; /* current command in list */
bool cmd_repeat; /* option to repeat a command if set true */
modem_op_cb_t cmd_complete_cb;
/* Device IMEI */
char imei[MODEM_IMEI_LEN];
char iccid[BG96_ICCID_LEN];
/* Device FW version e.g. BG95M2LAR01A01 or BG77LAR02A02_01.001.01.001 */
char fw_ver[MODEM_FWVER_LEN];
modem_signal_strength_t sig_strength;
/* Command parameters */
modem_mqtt_setup_params_t mqtt_params;
modem_http_setup_params_t http_params;
modem_file_params_t file_params;
/* Shutdown state monitoring */
int shutdown_counter;
bool shutdown_qpowd;
uint16_t shutdown_qpowd_failures;
uint16_t shutdown_pwrkey_failures;
} modem_t;
/* Local process state */
static modem_t bg96;
esp_timer_handle_t modem_timer;
/* ------------------------------------------------------------------------- */
/* comm buffers */
static char txBuf[MODEM_TX_BUFFER_LEN];
static char rxBuf[MODEM_RX_BUFFER_LEN];
static uint16_t rx_idx = 0;
/************** Device-Comms-Data *************/
extern uint8_t comms_mode;
/* Signal strength data stored after GetSignalStrength is called. */
//static MODEM_SignalStrength_t m_signalStrength;
/* Wrapping scanner treats RX buffer like a circular buffer and retains state
* over calls. It allows scanning for strings that might wrap around.
*/
typedef struct {
int RxScannerState;
int RxScannerIdx;
} ScanContext_t;
static ScanContext_t RxScanContextSuccess; /* scanner for success string */
static ScanContext_t RxScanContextError; /* scanner for failure string */
/* AT Commands -------------------------------------------------------------*/
// Note: These AT commands end with AT_OK response
// ATI,
#define ATI "ATI\r\n"
#define AT_QPOWD "AT+QPOWD\r\n"
//#define AT_CGDCONT "AT+CGDCONT=1,\"IPV4V6\",\"\",\"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0\",0,0,0,0\r\n"
//#define AT_CGDCONT_ALT "AT+CGDCONT=1,\"IP\",\"\"\r\n"
#define AT_CGDCONT "AT+CGDCONT=1,\"IPV4V6\",\"hologram\"\r\n"
#define AT_QICSGP "AT+QICSGP=1,1,\"\",\"\",\"\",0\r\n"
#define AT_QIACT "AT+QIACT=1\r\n"
#define AT_QHTTPPOST "AT+QHTTPPOST=%u,80,60\r\n"
#define AT_QHTTPCFG_RHD "AT+QHTTPCFG=\"requestheader\",1\r\n"
#define AT_QHTTPCFG_RHD_0 "AT+QHTTPCFG=\"requestheader\",0\r\n"
#define AT_QHTTPCFG_CTX "AT+QHTTPCFG=\"contextid\",1\r\n"
#define AT_QHTTPURL "AT+QHTTPURL=%u,80\r\n" /* host */
#define AT_QHTTPGET "AT+QHTTPGET=80\r\n"
#define AT_QHTTPREAD "AT+QHTTPREAD=80\r\n"
#define AT_QHTTPREADFILE "AT+QHTTPREADFILE=\"%s\",80\r\n" /* file name */
#define AT_QFLDS "AT+QFLDS=\"UFS\""
#define AT_QFLST "AT+QFLST=\"%s\"\r\n" /* file name */
#define AT_QFOPEN "AT+QFOPEN=\"%s\",2\r\n" /* file name, mode=2 (readonly, fail if not found) */
#define AT_QFREAD "AT+QFREAD=%d,%u\r\n" /* file handle, chunk size */
#define AT_QFCLOSE "AT+QFCLOSE=%d\r\n" /* file handle */
#define AT_QFOTADL "AT+QFOTADL=%s\r\n" /* host */
#define AT_ECHO_OFF "ATE0\r\n"
#define AT_CGSN "AT+CGSN\r\n"
#define AT_QCCID "AT+QCCID\r\n"
#define AT_CGMR "AT+CGMR\r\n"
#define AT_QGMR "AT+QGMR\r\n"
#define AT_CPIN "AT+CPIN?\r\n"
#define AT_CFUN_OFF "AT+CFUN=0\r\n"
#define AT_CFUN_FULL "AT+CFUN=1\r\n"
#define AT_COPS "AT+COPS?\r\n"
#define AT_QCFG_IOTOPMODE "AT+QCFG=\"iotopmode\",2,1\r\n" /* LTE-M immediately */
#define AT_QMTCFG_WILL "AT+QMTCFG=\"will\",0\r\n"
#define AT_QMTCFG_TIMEOUT "AT+QMTCFG=\"timeout\",0,60,3,1\r\n" /* Qianwen, used to be: "AT+QMTCFG=\"timeout\",0,60,3,0\r\n", 1000 */
#define AT_QMTCFG_KEEPALIVE "AT+QMTCFG=\"keepalive\",0,60\r\n"
#define AT_QMTOPEN "AT+QMTOPEN=0,\"%s\",%d\r\n" /* host, port */
#define AT_QMTCLOSE "AT+QMTCLOSE=0\r\n"
#define AT_QMTDISC "AT+QMTDISC=0\r\n"
//#define AT_QMTCONN_CID "AT+QMTCONN=0,\"projects/pwc-production/locations/us-central1/registries/unwiredprime/devices/CTD-%s\",\"unused\",\"%s\"\r\n" /* clientID, username,password */
#define AT_QMTCONN_CID "AT+QMTCONN=0,\"%s\"\r\n" /* clientID*/
#define AT_QMTPUB "AT+QMTPUB=0,0,0,0,\"/devices/CTD-%s/events\"\r\n" /* topic, note that actual message data follows " and that \r\n is likely sent to the server */
#define AT_QCSQ "AT+QCSQ\r\n"
#define AT_CTZU "AT+CTZU=1\r\n"
#define AT_CCLK "AT+CCLK?\r\n"
#define AT_IFC "AT+IFC=2,2\r\n"
#define AT_QFDEL "AT+QFDEL=\"" MCU_OTA_FILE_LOCAL "\"\r\n"
#define AT_QFUPL "AT+QFUPL=\"cacert.pem\",1244,100\r\n"
#define AT_QMTCFG_SSL "AT+QMTCFG=\"ssl\",0,1,0\r\n"
#define AT_QSSLCFG_CACERT "AT+QSSLCFG=\"cacert\",0,\"cacert.pem\"\r\n"
#define AT_QSSLCFG_SECLEVEL "AT+QSSLCFG=\"seclevel\",0,0\r\n"
#define AT_QSSLCFG_SSLVER "AT+QSSLCFG=\"sslversion\",0,3\r\n"
#define AT_QSSLCFG_CIPH "AT+QSSLCFG=\"ciphersuite\",0,0XFFFF\r\n"
#define AT_QSSLCFG_IGNRTIME "AT+QSSLCFG=\"ignorelocaltime\",0,1\r\n"
#define AT_QMTCFG_VERSION "AT+QMTCFG=\"version\",0,4\r\n"
/* Responses */
#define AT_OK "\r\nOK\r\n"
#define AT_ERROR "\r\nERROR\r\n"
#define AT_CONNECT "CONNECT\r\n"
#define AT_HTTPPOST_SUCCESS "+QHTTPPOST: 0,200"
#define AT_QHTTPGET_SUCCESS "\r\n+QHTTPGET: 0,200"
#define AT_QHTTPREAD_SUCCESS "+QHTTPREAD: 0"
#define AT_QHTTPREADFILE_SUCCESS "\r\n+QHTTPREADFILE: 0"
#define AT_QFLST_SUCCESS "\r\n+QFLST: \"%s\""
#define AT_QFOPEN_SUCCESS "\r\n+QFOPEN:"
#define AT_CPIN_RESPONSE "\r\n+CPIN: READY" /* every response will be at least this long */
#define AT_IND_QMTOPEN "\r\n+QMTOPEN: 0,0"
#define AT_IND_QMTCONN "\r\n+QMTCONN: 0,0,0"
#define AT_IND_QMTPUB "\r\n+QMTPUB: 0,0,0"
#define AT_IND_QFUPL "\r\n+QFUPL:"
#define AT_IND_QMTCLOSE "\r\n+QMTCLOSE: 0,0"
#define AT_IND_CME_ERROR "\r\n+CME ERROR:"
#define AT_QENG_RESPONSE "\r\n+QENG:" //Partha = March 11, 2022
/* Unsolicited Result Codes */
#define URC_RDY "\r\nRDY\r\n"
#define CTRLZ (0x1A)
#define ESC (0x1B)
/* Notes:
* Possiblity of getting time from the network.
* Using NITZ method, BGxx may automatically obtain it and set it in internal RTC.
* Command sequence:
* AT+CTZU=1 enable automatic timezone update (in effect automatic time update)
* optional: AT+CTZR=2 automatic timezone reporting: emit +CTZV indication
* Get time: AT+CCLK?
*
*/
/* --------------------------------------------------------------------------*/
//definition a new function to return signal strength
int modem_Rssi() {
return bg96.sig_strength.rssi;
}
char* modem_get_rxbuf(void)
{
return (char*)(rxBuf);
}
/* Transfer bytes from queue to supplied buffer */
int board_uart_rx_stream_poll(uint8_t* buf, uint16_t* rx_idx, int buf_len)
{
int bytesCopied = 0;
//vTaskDelay(10 / portTICK_PERIOD_MS);
while(uart_ifx_uart1_ifx_get_rx_data(&buf[(*rx_idx)++]) && (*rx_idx <= buf_len))
{
bytesCopied++;
}
return bytesCopied;
}
/* forward decl */
static int start_cmd_sequence(void);
static int handle_imei_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
if (status == MODEM_STATUS_OK) {
strncpy(bg96.imei, buf + MODEM_IMEI_START, MODEM_IMEI_LEN);
bg96.imei[MODEM_IMEI_LEN - 1] = 0;
ESP_LOGI(TAG,"IMEI No: %s\r\n", bg96.imei);
retval = MODEM_STATUS_OK;
}
return retval;
}
#define ATI_REVISION_STR "Revision: "
static int handle_iccid_cmd(int status, const char* buf, int len){
int retval = MODEM_STATUS_ERROR;
if (status == MODEM_STATUS_OK)
{
strncpy(bg96.iccid, buf + BG96_ICCID_START, BG96_ICCID_LEN);
bg96.iccid[BG96_ICCID_LEN - 1] = '\0';
ESP_LOGI(TAG,"ICCID No: %s", bg96.iccid);
retval = MODEM_STATUS_OK;
}
return retval;
}
static int handle_ati_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
if (status == MODEM_STATUS_OK) {
/* Find the expected device type */
if (strstr(buf, MODEM_DEVICE_MATCH_STRING) != NULL) {
retval = MODEM_STATUS_OK;
ESP_LOGI(TAG,MODEM_DEVICE_MATCH_STRING " detected!\r\n");
} else {
ESP_LOGW(TAG,"Device " MODEM_DEVICE_MATCH_STRING "not detected\r\n");
}
#if MODEM_MODEM_FWVER_FROM_QGMR == 0
/* Find the firmware version */
char* start_ptr = strstr(buf, ATI_REVISION_STR);
if (start_ptr) {
start_ptr += strlen(ATI_REVISION_STR);
char* end_ptr = strstr(start_ptr, "\r");
if (end_ptr) {
int len = end_ptr - start_ptr;
len = (len > MODEM_FWVER_LEN-1) ? MODEM_FWVER_LEN-1 : len;
memcpy(bg96.fw_ver, start_ptr, len);
bg96.fw_ver[MODEM_FWVER_LEN - 1] = 0; /* ensure terminated */
}
}
if (bg96.fw_ver[0]) {
ESP_LOGI(TAG,"FW: %s\r\n", bg96.fw_ver);
}
else {
ESP_LOGI(TAG,"Cannot get firmware version\r\n");
}
#endif
}
return retval;
}
#if MODEM_MODEM_FWVER_FROM_QGMR
static int handle_qgmr_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
if (status == MODEM_STATUS_OK) {
/* firmware version is modem+application string returned by modem before OK
e.g. BG77LAR02A02_01.001.01.001
*/
const char *start_ptr = buf;
if (start_ptr) {
/* If modem starts revision string with \r\n, skip those. */
if (start_ptr[0] == '\r' && start_ptr[1] == '\n') {
start_ptr += 2;
}
/* look for a trailing \r\n, which may be part of version, or part of \r\nOK\r\n sent after
*/
const char *end_ptr = strstr(start_ptr, "\r\n");
if (end_ptr) {
int len = end_ptr - start_ptr;
len = (len > MODEM_FWVER_LEN - 1) ? MODEM_FWVER_LEN - 1 : len;
memcpy(bg96.fw_ver, start_ptr, len);
bg96.fw_ver[MODEM_FWVER_LEN - 1] = 0; /* ensure terminated */
}
}
if (bg96.fw_ver[0]) {
ESP_LOGI(TAG,"FW: %s\r\n", bg96.fw_ver);
} else {
ESP_LOGI(TAG,"Cannot get firmware version\r\n");
}
/* OK regardless; modem can continue to run */
retval = MODEM_STATUS_OK;
}
return retval;
}
#endif
/*static int handle_flds_cmd(int status, const char *buf, int len) {
ESP_LOGI(TAG,"AT+QFLDS response is: %s",buf);
return status;
}*/
static int handle_cops_cmd(int status, const char *buf, int len) {
#if FAKE_NETWORK
return MODEM_STATUS_OK;
#else
/* Return OK to pass this command, or anything else to continue polling. */
char *token;
if ((strstr(rxBuf, "\"") != NULL)) {
token = strtok(rxBuf, "\"");
token = strtok(NULL, "\"");
ESP_LOGI(TAG,"The Operator is: %s\r\n", token);
ESP_LOGI(TAG,"Connected to network.\r\n");
status = MODEM_STATUS_OK;
}
else
{
/* If the comms_mode is CELL-ONLY then make at cops command-retries infinite. otherwise make it limited to 120 retries */
if(comms_mode == 3)
{
bg96.cmd_repeat = true;
}
else
{
status = MODEM_STATUS_TIMEOUT;
}
}
return status;
#endif
}
static int handle_qfdel_cmd(int status, const char *buf, int len) {
status = MODEM_STATUS_OK;
return status;
}
#define AT_CCLK_IND "+CCLK:"
static int handle_cclk_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
/* Working with response like: +CCLK: "20/09/09,16:11:40-16" yy/MM/dd,hh:mm:ss-zz
* Timezone offset is in quarter-hour units. -16 is -4 hours from GMT/UTC.
* A negative offset means we need to add those hours to the local epoch to get back
* to GMT.
* NOTE!! GMT is reqired to be the system-wide time base:
* - Each modem provides local time with offset; we convert to GMT
* - Server time must also be in GMT. Currently, this is correct: SP1 timestamps
* generated by are in GMT. (https://dev.web.unwiredprime.com/api/v1/trackers/tracker/866349040012464/mcu_config_download)
*/
/* The modem under test actually spits out a nonsense time of +CCLK: "80/01/06,00:25:27
* when not connected to a network. (Year = 2080)
*/
if (status == MODEM_STATUS_OK) {
char *s = strstr(rxBuf, AT_CCLK_IND);
//char* s = "+CCLK: \"20/09/09,16:11:40-16\"";
if (s) {
rtc_timestamp_t ts;
s += strlen(AT_CCLK_IND) + 2; /* past space and " */
ts.Year = atoi(s) + 2000;
s += 3;
ts.Month = atoi(s);
s += 3;
ts.MonthDay = atoi(s);
s += 3;
ts.Hour = atoi(s);
s += 3;
ts.Minute = atoi(s);
s += 3;
ts.Second = atoi(s);
/* Try to convert the offset, plus or minus. If it can't it is 0 and
* no harm done.
*/
s += 2;
int tz_offset_sec = atoi(s) * 15 * 60; /* modem provides 1/4 hour increments */
#if SET_TIME_FROM_SERVER == 0
/* Set time just once - here, or from the SP1 record */
if (!rtc_is_set()) {
rtc_set_timestamp_tz(&ts, tz_offset_sec);
}
ESP_LOGI(TAG,"Set time: %d/%d/%d,%d:%d:%d %d -> %ld\r\n",
ts.Year, ts.Month, ts.MonthDay, ts.Hour, ts.Minute, ts.Second, tz_offset_sec,
rtc_get_epoch());
#else
/* Print time modem provides, but do not use it. */
ESP_LOGI(TAG,"Modem time: %d/%d/%d,%d:%d:%d %d\r\n",
ts.Year, ts.Month, ts.MonthDay, ts.Hour, ts.Minute, ts.Second, tz_offset_sec);
#endif
}
retval = MODEM_STATUS_OK;
}
return retval;
}
static int handle_qcsq_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
/* Working with response like: +QCSQ: "CAT-M1",-52,-81,195,-10 */
if (status == MODEM_STATUS_OK) {
char *s = strtok(rxBuf, ",");
if ((s != NULL) && (strstr(s, MODEM_QCSQ_SERIVCE_MODE_MATCH_1) || strstr(s, MODEM_QCSQ_SERIVCE_MODE_MATCH_2))) {
/* parse the 4 fields */
s = strtok(NULL, ",");
if (s){
bg96.sig_strength.rssi = atoi(s);
s = strtok(NULL, ",");
if (s){
bg96.sig_strength.rsrp = atoi(s);
s = strtok(NULL, ",");
if (s){
bg96.sig_strength.sinr = atoi(s);
s = strtok(NULL, ",");
if (s){
bg96.sig_strength.rsrq = atoi(s);
retval = MODEM_STATUS_OK;
ESP_LOGI(TAG,"Signal strength: %d %d %d %d\r\n",
bg96.sig_strength.rssi,
bg96.sig_strength.rsrp,
bg96.sig_strength.sinr,
bg96.sig_strength.rsrq);
}
}
}
}
retval = MODEM_STATUS_OK;
} else {
ESP_LOGW(TAG,"Signal strength : did not find expected service mode\r\n");
bg96.cmd_repeat = true;
}
// to pass the command anyways
// retval = MODEM_STATUS_ERROR;
}
return retval;
}
/*
static int setup_qmtopen_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QMTOPEN, bg96.mqtt_params.host,
bg96.mqtt_params.port);
return MODEM_STATUS_OK;
}
static int setup_qmtconn_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QMTCONN_CID,bg96.mqtt_params.client_id); //, bg96.imei, bg96.mqtt_params.jwt);//bg96.mqtt_params.client_id,);
return MODEM_STATUS_OK;
}
static int setup_qmtpub_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QMTPUB, bg96.imei); //bg96.mqtt_params.pub_topic);
return MODEM_STATUS_OK;
}
*/
/* Write the length of the URL in the command */
static int setup_qhttpurl_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QHTTPURL, strlen(bg96.http_params.url));
return MODEM_STATUS_OK;
}
/* Write the length of the POST message in the command */
static int setup_qhttppost_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QHTTPPOST, strlen(bg96.http_params.post_message));
vTaskDelay(1000 / portTICK_PERIOD_MS);
return MODEM_STATUS_OK;
}
/* Write the length of the URL in the command */
static int setup_qhttpreadfile_cmd(const void *params, char *buf, int len) {
snprintf(buf, len, AT_QHTTPREADFILE, bg96.http_params.file);
return MODEM_STATUS_OK;
}
static int setup_qfotadl_cmd(const void *params, char *buf, int len) {
/* Send the URL */
snprintf(buf, len, AT_QFOTADL, bg96.http_params.url);
ESP_LOGW(TAG,"setup_qfotadl_cmd");
ESP_LOGI(TAG,"buf: %s",buf);
return MODEM_STATUS_OK;
}
static int handle_qhttpread_cmd(int status, const char *buf, int len) {
bg96.http_params.buf = 0;
bg96.http_params.len = 0;
/* Parse response */
#if USE_QHTTPREAD_OK
if (status == MODEM_STATUS_OK)
#else
if (status == MODEM_STATUS_TIMEOUT)
#endif
{
/* status is error until we prove correctness of response and
* user arguments are updated
*/
status = MODEM_STATUS_ERROR;
/* Ensure null-termination when searching for strings */
rxBuf[MODEM_RX_BUFFER_LEN - 1] = 0;
/* Find the connect response */
char *start_ptr = strstr(rxBuf, AT_CONNECT);
if (start_ptr != NULL) {
start_ptr += strlen(AT_CONNECT); /* point to next */
#if USE_QHTTPREAD_OK /* Use OK to frame response. */
/* Find the trailing OK response */
char *end_ptr = strstr(start_ptr, AT_OK);
if (end_ptr != NULL) {
int rlen = end_ptr - start_ptr;
bg96.http_params.buf = start_ptr;
bg96.http_params.len = rlen;
/* terminate the buffer at the OK before returning to user */
*end_ptr = 0;
/* now we are complete */
status = MODEM_STATUS_OK;
}
#else /* Do NOT use OK to frame response. */
/* Without OK to frame response, we just gather all bytes received. */
bg96.http_params.buf = start_ptr;
bg96.http_params.len = strlen(start_ptr);
status = MODEM_STATUS_OK;
#endif
}
}
return status;
}
/* Send the URL after the CONNECT response or as a send
* handler for any command.
*/
static int send_qhttpurl_cmd(void) {
ESP_LOGI(TAG,"Send URL: %s\r\n", bg96.http_params.url);
uart_ifx_uart1_send_bytes((uint8_t *)bg96.http_params.url, strlen(bg96.http_params.url));
return MODEM_STATUS_OK;
}
static int send_qhttpost_cmd(void) {
ESP_LOGI(TAG,"Send post_message: %s\r\n", bg96.http_params.post_message);
uart_ifx_uart1_send_bytes((uint8_t *)bg96.http_params.post_message, strlen(bg96.http_params.post_message));
return MODEM_STATUS_OK;
}
#if 0
static int handle_qhttppost_cmd(int status, const char *buf, int len)
{
/* Parse the response from the rx buffer */
char *ptr = strstr((char*) buf, "+QHTTPPOST: 0,200,");
if (ptr)
{
/*if that response exists then returns OK*/
return MODEM_STATUS_OK;
}
else
{
/*if that response doesn't exists ----> then search if there's an CME error message*/
ptr = strstr((char*) buf, "+CME ERROR:");
if (ptr)
return MODEM_STATUS_ERROR;
else
return MODEM_STATUS_HTTP_POST_WAITRES;
}
}
#endif
static int send_qmtpub_cmd(void) {
if (bg96.mqtt_params.pub_data && bg96.mqtt_params.pub_data_len > 0) {
ESP_LOGI(TAG,"publishing: [%s]\r\n", bg96.mqtt_params.pub_data);
uart_ifx_uart1_send_bytes((uint8_t*)bg96.mqtt_params.pub_data, bg96.mqtt_params.pub_data_len);
} else {
ESP_LOGW(TAG,"publishing null data\r\n");
}
char buf[1] = {
/* Send end of message character (ctrl+z) */
CTRLZ,
//0 /* null-terminate for nice trace printing */
};
uart_ifx_uart1_send_bytes((uint8_t*)buf, sizeof(buf));
return MODEM_STATUS_OK;
}
static int send_qfupl_cmd(void) {
char buf[1244] =
"-----BEGIN CERTIFICATE----- \
MIIDazCCAlOgAwIBAgIUHMeQp85hERg7OQNJpIpRenYRRwkwDQYJKoZIhvcNAQEL \
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM \
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAyMjUxMzUwNDdaFw0yMTAz \
MjcxMzUwNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw \
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB \
AQUAA4IBDwAwggEKAoIBAQDFMVwzDmlbfaZ+oYQwva49wQ5MB2uTLlJ3APHmSOos \
2bsRXzZf/68rsvZNF/khLoreoW/oi0lJAA/vi7oJuKvdZKovtrwiOk8z4uYEZR8D \
I3Y7xnIen4jZMjUrGnAUpRd2NQOqt2kScTbSxXvS2GuDxt+nBh2PS34WWBWp1HP0 \
4zztRYHFUfsnsc21/DMzL86uc2jZQq0fJEWu0qwxTDunIXn6tQYFDFgLasSmZv4d \
/NSNcbKLYjvUoUnbdqdTEzeshGTBM+zLxakN+Zdxc88Bb83vvNa686lzPZF/LAXT \
erEr+Yq4SxpQ5Hn9Nj5d5wxRQSDzmtns/jJmk1zhLrIZAgMBAAGjUzBRMB0GA1Ud \
DgQWBBQrE77qN2Tw7ia5DMHBvgBmwr0zHDAfBgNVHSMEGDAWgBQrE77qN2Tw7ia5 \
DMHBvgBmwr0zHDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBm \
eqBPGy1TCNEDa3dMWgO9pK9zsuDGv+J9b2TiqnTvub5uW5z+qV2f7bBsuOZRWzv3 \
4P9oGjgH+/HFpz+kSvBIRoEjjtX48LyD5H9ah0ALzQt5vXN7Q6QR4bWEtZFYlaOW \
6Yd4v2DOtyBCa6g33Jf0Hw1OaYPzRmRsbqdB/Izg60ENZFXKCPFvHNIVP7IytEaz \
gdghpaHdqGSFlbBxtfzk7vNqVoepX9kULaOhNdaDlMZE+nA+unHZc4zd0wDLVB+p \
8fYZf6JfLRkQoZmdprsa0mtVOXLBwH6C2+lj8mxz6T9Or07LjxpkJB2dxGBiE6b9 \
zkcHc7vljutOr+sbR1aK \
-----END CERTIFICATE-----";
int len = strlen(buf);
ESP_LOGI(TAG,"Buffer: [%d]\r\n", len);
uart_ifx_uart1_send_bytes((uint8_t*)buf, len);
char cbuf[1] = {
/* Send end of message character (ctrl+z) */
CTRLZ,
//0 /* null-terminate for nice trace printing */
};
uart_ifx_uart1_send_bytes((uint8_t*)cbuf, sizeof(cbuf));
return MODEM_STATUS_OK;
}
/* --------------------------------------------------------------------------*/
/* File read command processing.
*/
static int setup_qflst_cmd(const void *params, char *buf, int len) {
/* Send the file name */
snprintf(buf, len, AT_QFLST, bg96.file_params.file);
return MODEM_STATUS_OK;
}
static int handle_qflst_cmd(int status, const char *buf, int len) {
int retval = MODEM_STATUS_ERROR;
bg96.file_params.file_size = 0;
if (status == MODEM_STATUS_OK) {
/* Parse the file size from the received response */
char *ptr = strstr((char*) rxBuf, ",");
if (ptr) {
ptr++;
bg96.file_params.file_size = (uint32_t) atoi(ptr);
ESP_LOGI(TAG,"Found file [%s] on modem. size=%ld\r\n",
bg96.file_params.file, bg96.file_params.file_size);
retval = MODEM_STATUS_OK;
} else {
ESP_LOGI(TAG,"Cannot find file size for [%s]\r\n",
bg96.file_params.file);
}
} else {
ESP_LOGI(TAG,"Cannot find downloaded file [%s] on modem\r\n",
bg96.file_params.file);
}
return retval;
}
static int handle_qfotadl_cmd(int status, const char* buf, int len){
ESP_LOGI(TAG,"DFOTA handle_qfotadl_cmd ");
char* s = NULL;
char errorcode[2] ={0};
int dfotaerr_idx = -1;
s = strstr(rxBuf, "HTTPEND");
if(s == NULL)
{
ESP_LOGI(TAG,"DFOTA package checking\\validation has been failed ");
return MODEM_STATUS_ERROR;
}
ESP_LOGI(TAG,"DFOTA ERROR %s",s);
strncpy(errorcode, &s[9], 1 /*only error index*/);
dfotaerr_idx = atoi(errorcode);
ESP_LOGI(TAG,"DFOTA ERROR IDX %d",dfotaerr_idx);
if(dfotaerr_idx != 0){
status = MODEM_STATUS_ERROR;
}
else{
status = MODEM_STATUS_OK;
}
return status;
}
static int setup_qfopen_cmd(const void *params, char *buf, int len) {
/* Send the file name */
snprintf(buf, len, AT_QFOPEN, bg96.file_params.file);
return MODEM_STATUS_OK;
}
static int handle_qfopen_cmd(int status, const char *buf, int len) {
if (status == MODEM_STATUS_OK) {
/* Need the file handle */
char *ptr = strstr((char*) rxBuf, "+QFOPEN:");
if (ptr) {
ptr += strlen("+QFOPEN:");
bg96.file_params.file_handle = atoi(ptr);
/* Initialize the file state */
bg96.file_params.transferred_bytes = 0;
bg96.file_params.chunk_size = MODEM_FREAD_SIZE;
ESP_LOGI(TAG,"Opened file %s with handle: %d\r\n",
bg96.file_params.file, bg96.file_params.file_handle);
} else {
/* Can't get handle? Uh oh */
status = MODEM_STATUS_ERROR;
ESP_LOGI(TAG,"Unable to get handle for %s\r\n", bg96.file_params.file);
}
if (status == MODEM_STATUS_OK) {
/* Call the user callback */
if (bg96.file_params.open_cb) {
/* User callback can return error, which will fail the whole read procedure */
status = bg96.file_params.open_cb(bg96.file_params.file_size);
bg96.file_params.open_cb = 0; /* call only once */
}
}
}
return status;
}
/* BG96 response search strings */
#define SFU_APP_MODEM_CONNECT_SEARCH "\r\nCONNECT 0\r\n"
#define SFU_APP_MODEM_CONNECT_PREFIX "\r\nCONNECT "
/* Length of the CONNECT response string that we will support, including null-terminator */
#define SFU_APP_MODEM_CONNECT_BUF_LEN sizeof("\r\nCONNECT 999999\r\n")
/* Compute the expected number of bytes in the modem's response to an FREAD */
static int expectedNumFREADResponseBytes(uint32_t len) {
int n = strlen(SFU_APP_MODEM_CONNECT_PREFIX);
/* digits in CONNECT read_length field */
n += (len < 10) ? 1 : (len < 100) ? 2 : (len < 1000) ? 3 : 4;
n += 2; /* trailing \r\n */
n += len;
n += strlen(AT_OK);
ESP_LOGI(TAG,"FREAD expect response len=%d for %ld\r\n", n, len);
return n;
}
/*
* Process the FREAD response from the BG96 modem.
*
* The FREAD response must begin at offset 0 in the provided buffer.
* The potential length of the entire response
* (e.g. maximum size of provided buffer) must be specified in buf_len parameter.
* It is not required to know the exact response length as this API will figure it out safely.
*
* Return MODEM_STATUS_ERROR if anything fails including buffer not containing a CONNECT response
* or properly formed CONNECT response.
*/
static int ParseFREAD(const char *buf, int buf_len, /* input arguments: pointer to receive buffer and its length */
const char **data_, int *data_len_) /* output arguments: pointer to recovered data and its length */
{
/* Deal with the buffer expecting a response from the BG96 of the form:
* \r\nCONNECT <read_length>\r\n
* <data of length read_length>
* \r\nOK\r\n
*
* We cannot assume the caller's buffer is null-terminated anywhere.
*
* We must:
* a) extract the read_length parameter
* b) find the offset of the data in the buffer
* c) copy the data to a word-aligned buffer for processing the SFU_APP_Data().
*
* The input buffer can be of any length. We will chunk it into our own buffer
* and call the underlying API as often as needed to consume all bytes in the
* caller's buffer before returning.
*/
/* Temporary buffer to search for the connect string safely */
char connect_buf[SFU_APP_MODEM_CONNECT_BUF_LEN];
/* Check arguments and basic buffer length sanity check */
if (buf == NULL || buf_len < strlen(SFU_APP_MODEM_CONNECT_SEARCH)) {
return MODEM_STATUS_ERROR;
}
/* First we need to build a null-terminated string to operate on, so we copy what
* could be the modem response into our buffer and terminate it.
* Maximum size will be limited to 6 digits, so that is
* "\r\nCONNECT 999999\r\n" = 18 characters.
*/
const int max_copy = SFU_APP_MODEM_CONNECT_BUF_LEN - 1;
/* Copy up to BUF LEN characters into temp buffer */
const int init_copy = buf_len > max_copy ? max_copy : buf_len;
memcpy(connect_buf, buf, init_copy);
connect_buf[init_copy] = 0; /* null terminate for safe string operations on it */
ESP_LOGI(TAG,"Copied %d bytes\r\n", init_copy);
/* Find the length */
const char *start_ptr = strstr(connect_buf, SFU_APP_MODEM_CONNECT_PREFIX);
if (start_ptr == NULL) {
return MODEM_STATUS_ERROR;
}
int offset = start_ptr - connect_buf; /* should be 0 but support other cases too */
ESP_LOGI(TAG,"Offset=%d\r\n", offset);
const char *ptr = start_ptr + strlen(SFU_APP_MODEM_CONNECT_PREFIX);
int data_len = atoi(ptr);
if (data_len <= 0) {
return MODEM_STATUS_ERROR;
}
ESP_LOGI(TAG,"data_len=%d\r\n", data_len);
/* Offset of actual data immediately follows the \r\n */
ptr = strstr(ptr, "\r\n");
if (ptr == NULL) {
return MODEM_STATUS_ERROR;
}
ptr += 2;
/* data offset into user buffer is specified finally here. */
offset += (ptr - start_ptr);
ESP_LOGI(TAG,"Offset=%d\n", offset);
/* check that the data length reported by CONNECT response is actually
* present in the buffer supplied.
*/
if (data_len > (buf_len - offset)) {
return MODEM_STATUS_ERROR;
}
/* Return the actual data length and a pointer to it */
*data_ = buf + offset;
*data_len_ = data_len;
return MODEM_STATUS_OK;
}
static int setup_qfread_cmd(const void *params, char *buf, int len) {
/* Send the number of bytes we read this call */
bg96.file_params.read_size = bg96.file_params.chunk_size;
/* Adjust to the actual number of bytes remaining */
if ((bg96.file_params.file_size - bg96.file_params.transferred_bytes)
< bg96.file_params.chunk_size) {
bg96.file_params.read_size = bg96.file_params.file_size
- bg96.file_params.transferred_bytes;
}
/* This is used in the handler to mark when we have received the response */
bg96.file_params.expected_bytes = expectedNumFREADResponseBytes(
bg96.file_params.read_size);
ESP_LOGI(TAG,
"setup qfread: handle:%d read_size:%d file_size:%ld chunk_size:%d xfered:%ld expect:%d\r\n",
bg96.file_params.file_handle, bg96.file_params.read_size,
bg96.file_params.file_size, bg96.file_params.chunk_size,
bg96.file_params.transferred_bytes,
bg96.file_params.expected_bytes);
snprintf(buf, len, AT_QFREAD, bg96.file_params.file_handle,
bg96.file_params.read_size);
return MODEM_STATUS_OK;
}
/* This is how we are handling the QFREAD command response.
* We need to frame it somehow, and we do that by waiting for the expected number of
* bytes to arrive. But to have this handler called, we need to kick it with something,
* and we use the "\r\nOK\r\n" string expected at the end. We will, however, see this
* string embedded within the firmware image, so we must be careful not to end prematurely.
*
* Coming into this function, we will expect status = MODEM_STATUS_OK to indicate match for the \r\nOK\r\n string.
* then, we look for the number of bytes recevied and compare. If these match, we return STATUS_OK.
* If they do not, we must return TIMEOUT to keep looking. If the error condition was found, we have a
* similar problem with finding it in the stream, so we don't look for it and instead rely on the
* command timeout to abort.
*
* Also, we need to retry the FREAD command until the transfer is done,
* to do that we must return TIMEOUT always until it is done, then OK, or ERROR in any error state.
*/
static int handle_qfread_cmd(int status, const char *buf, int len) {
/* Pass ERROR and TIMEOUT through, only handle OK */
if (status == MODEM_STATUS_OK) {
/* Found \r\nOK\r\n in the stream. Do we have the expected number of bytes? */
if (rx_idx < bg96.file_params.expected_bytes) {
/* No, this was an embedded \r\nOK\r\n so continue scanning */
status = MODEM_STATUS_TIMEOUT;
/* Required to reset the scanner state (and update the index) for next OK */
RxScanContextSuccess.RxScannerState = 0;
RxScanContextSuccess.RxScannerIdx = rx_idx;
/* In the last UART event we got a numnber of bytes to bring it to rx_idx,
* and in that chunk we found OK but NOT expected number of bytes. This means
* we expect at least another UART event to kick the next scan.
*/
ESP_LOGI(TAG,"Found embedded OK, rx_idx=%d scanidx=%d\r\n", rx_idx,
RxScanContextSuccess.RxScannerIdx);
} else {
const char *data;
int data_len;
status = ParseFREAD(rxBuf, MODEM_RX_BUFFER_LEN, &data, &data_len);
if (status == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"%d bytes file data read successfully\r\n", data_len);
/* Invoke user callback, which can also fail the transfer */
if (bg96.file_params.data_cb) {
status = bg96.file_params.data_cb(data, data_len);
if (status == MODEM_STATUS_OK) {
/* Increment the number of bytes transfered only after success on user callback */
bg96.file_params.transferred_bytes +=
bg96.file_params.read_size;
ESP_LOGI(TAG,"%d bytes processed\r\n", data_len);
} else {
ESP_LOGI(TAG,"%d bytes failed to process.\r\n", data_len);
}
}
} else {
ESP_LOGI(TAG,"Failed to parse FREAD response\r\n");
}
ESP_LOGI(TAG,"Transfer %ld/%ld %ld%% complete\r\n",
bg96.file_params.transferred_bytes,
bg96.file_params.file_size,
(bg96.file_params.transferred_bytes * 100)
/ bg96.file_params.file_size);
/* If status is OK at this point, we must set it to TIMEOUT to continue reading the file */
if (status == MODEM_STATUS_OK) {
/* If we have received all bytes, we can terminate the transfer */
if (bg96.file_params.transferred_bytes
< bg96.file_params.file_size) {
bg96.cmd_repeat = true;
}
}
}
}
/* Must return TIMEOUT until all bytes have been tranferred, unless ERROR to signal
* transfer failed and abort the command sequence and OK to signal all done.
*/
return status;
}
static int setup_qfclose_cmd(const void *params, char *buf, int len) {
/* Send the file handle */
snprintf(buf, len, AT_QFCLOSE, bg96.file_params.file_handle);
return MODEM_STATUS_OK;
}
/* Command table
* Allows command chaining
* Each entry has a response process function
*/
#define MODEM_DEFAULT_CMD_TIMEOUT_MS 5000
#define MODEM_DEFAULT_CMD_RETRIES 5
/* Align with timeout given to modem in AT_QMTCFG_TIMEOUT */
#define MODEM_MQTT_CONN_CMD_TIMEOUT_MS 60000
typedef enum {
AT_CMD_IDX_ECHO_OFF = 0,
AT_CMD_IDX_IFC,
AT_CMD_IDX_IDENT,
#if MODEM_MODEM_FWVER_FROM_QGMR
AT_CMD_IDX_QGMR,
#endif
AT_CMD_IDX_IMEI,
AT_CMD_IDX_ICCID, //AT_QCCID to get ICCID
#if FAKE_NETWORK
AT_CMD_IDX_SETCCLK,
#endif
AT_CMD_IDX_CTZU,
AT_CMD_IDX_CPIN,
AT_CMD_IDX_CGDCONT_ALT,
AT_CMD_IDX_CFUN_OFF,
AT_CMD_IDX_CFUN,
AT_CMD_IDX_COPS,
AT_CMD_IDX_QCSQ,
AT_CMD_IDX_QCFG_IOTMODE,
AT_CMD_IDX_CCLK,
AT_CMD_IDX_QICSGP,
AT_CMD_IDX_QIACT,
//AT_CMD_IDX_QMTCFG_SSL,
AT_CMD_IDX_QFDEL,
//AT_CMD_IDX_QFUPL,
/*AT_CMD_IDX_QFUPL_SEND,
AT_CMD_IDX_QSSLCFG_CACERT,
AT_CMD_IDX_QSSLCFG_SECLEVEL,
AT_CMD_IDX_QSSLCFG_SSLVER,
AT_CMD_IDX_QSSLCFG_CIPH,
AT_CMD_IDX_QSSLCFG_IGNRTIME,
AT_CMD_IDX_QMTCFG_WILL,
AT_CMD_IDX_QMTCFG_TIMEOUT,
AT_CMD_IDX_QMTCFG_KEEPALIVE,
AT_CMD_IDX_QMTCFG_VERSION,
AT_CMD_IDX_QMTOPEN,
AT_CMD_IDX_QMTCONN_CID,*/
//AT_CMD_IDX_QMTPUB,
//AT_CMD_IDX_QMTPUB_SEND, /* not an actual command, part 2 of the QMTPUB operation */
//AT_CMD_IDX_QMTCLOSE,
AT_CMD_IDX_QHTTPCFG_CTX,
AT_CMD_IDX_QHTTPCFG_RHD,
AT_CMD_IDX_QHTTPCFG_RHD_0,
AT_CMD_IDX_QHTTPURL,
AT_CMD_IDX_QHTTPURL_SET,
AT_CMD_IDX_QHTTPPOST,
AT_CMD_IDX_QHTTPPOST_SET,
AT_CMD_IDX_QHTTPGET,
AT_CMD_IDX_QHTTPREAD,
AT_CMD_IDX_QHTTPREADFILE,
AT_CMD_IDX_QPOWD,
AT_CMD_IDX_QFOTADL,
AT_CMD_IDX_QFOTADL_WAIT_HTTPEND,
AT_CMD_IDX_QFOTADL_WAIT_END,
//AT_CMD_IDX_QFLDS,
AT_CMD_IDX_QFLST,
AT_CMD_IDX_QFOPEN,
AT_CMD_IDX_QFREAD,
AT_CMD_IDX_QFCLOSE,
// AT_CMD_IDX_QENG_SVCELL, //Partha - March 11, 2022
// AT_CMD_IDX_QENG_NBCELL //Partha - March 11, 2022
} modem_at_cmd_idx_t;
static const modem_cmd_t cmd_table[] =
{
/* ATE0 */
{ AT_ECHO_OFF, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* IFC */
{ AT_IFC, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* ATI */
{ ATI, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, handle_ati_cmd, 0, 0 },
#if MODEM_MODEM_FWVER_FROM_QGMR
/* QGMR */
{ AT_QGMR, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, handle_qgmr_cmd, 0, 0 },
#endif
/* CGSN */
{ AT_CGSN, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, handle_imei_cmd, 0, 0 },
/* QCCID */
{ AT_QCCID, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, handle_iccid_cmd, 0, 0 },
#if FAKE_NETWORK
/* SETCCLK */
/* Set the modem's RTC for testing without network
* Note: timezone offset is in units of 1/4 hour, so -20 is -5 hours (EST timezone)
* Below time is local to EST. It should be converted to GMT epoch and that is used
* as the system-wide time base for SP1/2 publish messages.
*/
{ "AT+CCLK=\"20/10/04,15:43:10-20\"\r\n", AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
#endif
/* Network connection */
/* CTZU */
{ AT_CTZU, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* CPIN */
{ AT_CPIN, AT_OK, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* CGDCONT_ALT */
{ AT_CGDCONT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* CFUN_OFF */
{ AT_CFUN_OFF, AT_OK, AT_IND_CME_ERROR, 15000,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* CFUN */
{ AT_CFUN_FULL, AT_OK, AT_IND_CME_ERROR, 15000,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* COPS
* Wait for timeout and use handler function to look for the operators we want to connect with.
* Retry for up to 2 minutes.
*/
// { AT_COPS, 0, 0, 1000, 120, 0, handle_cops_cmd, 0, 0 },
{ AT_COPS, AT_OK, 0, 2000, 60, 0, handle_cops_cmd, 0, 0 },
/* QCSQ */
{ AT_QCSQ, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
20, 0, handle_qcsq_cmd, 0, 0 },
/* QCFG_IOTMODE */
{ AT_QCFG_IOTOPMODE, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* CCLK */
{ AT_CCLK, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, handle_cclk_cmd, 0, 0 },
/* PDP context commands:
* CGDCONT sets it up
* CGACT activates it
* OR
* QICSGP sets it up
* QIACT activates it
* QI commands are in the TCP(IP) AT command manual, while CG are standard AT commands.
* https://forums.quectel.com/t/what-is-the-difference-between-cgdcont-and-qicsgp/152
* "When using modules embedded stacks like TCP and UDP protocol, you need to use AT+QICSGP to configure the APN.
* While using PPP, you need to use AT+CGDCONT to configure the APN."
* I also suspect that these commands need to be run before MQTT communications. We are not using PPP.
* They are included in the network connection sequence.
*/
/* QICSGP */
{ AT_QICSGP, AT_OK, AT_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* QIACT
* Maximum response time 150 seconds.
*/
{ AT_QIACT, 0, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* QSSLCFG_CACERT */
/*{ AT_QMTCFG_SSL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* MQTT connection */
{ AT_QFDEL, 0, 0, 5000, MODEM_DEFAULT_CMD_RETRIES, 0,
handle_qfdel_cmd, 0, 0 },
/* MQTT connection */
/*{ AT_QFUPL, "CONNECT", AT_IND_CME_ERROR, 5000,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* Send data then wait for indication. */
//{ 0 /* no command */, AT_OK, AT_IND_CME_ERROR, 180 * 1000,
// 0 /* no retries */, send_qfupl_cmd, 0, 0, 0 },
/* QSSLCFG_CACERT */
/*{ AT_QSSLCFG_CACERT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTCFG_WILL */
/*{ AT_QSSLCFG_SECLEVEL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTCFG_WILL */
/*{ AT_QSSLCFG_SSLVER, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTCFG_WILL */
/*{ AT_QSSLCFG_CIPH, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTCFG_WILL */
/*{ AT_QSSLCFG_IGNRTIME, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTCFG_WILL */
/*{ AT_QMTCFG_WILL, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* AT_QMTCFG_TIMEOUT */
/*{ AT_QMTCFG_TIMEOUT, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* AT_QMTCFG_KEEPALIVE */
/*{ AT_QMTCFG_KEEPALIVE, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* AT_QMTCFG_VERSION */
/*{ AT_QMTCFG_VERSION, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },*/
/* QMTOPEN */
/*{ AT_QMTOPEN, AT_IND_QMTOPEN, 0, MODEM_MQTT_CONN_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qmtopen_cmd, 0 },*/
/* QMTCONN_CID */
/*{ AT_QMTCONN_CID, AT_IND_QMTCONN, 0,
MODEM_MQTT_CONN_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, setup_qmtconn_cmd, 0 },*/
/* MQTT publish */
/* QMTPUB
* Publish messages.
* AT+QMTPUB=0,0,0,0,"topic/pub"
* >This is test data, hello MQTT. //After receiving >, input data "This is test data, hello MQTT." and
* then send it. The maximum length of the data is 1548 bytes and the
* data that beyond 1548 bytes will be omitted. After inputting data,
* tap Ctrl+Z to send.
* OK
* +QMTPUB: 0,0,0
*
* Note: need to wait for ">" from modem after command is sent before sending our data.
* Publish is then split into two operations: AT_QMTPUB command which waits for ">",
* then send the data and waits for the indication.
*/
/*{ AT_QMTPUB, ">", AT_IND_CME_ERROR, 5000,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qmtpub_cmd, 0 },*/
/* Send data then wait for indication. */
//{ 0 /* no command */, AT_IND_QMTPUB, AT_IND_CME_ERROR, 180
// * 1000, 0 /* no retries */, send_qmtpub_cmd, 0, 0, 0 },
/* QMTCLOSE
* Note:
* The QMTCLOSE: indication does not appear within a 10s timeout. The manual has no specification on how long
* this might take. Instead, we wait for the command's OK response and continue as if the connection
* had closed.
*/
//{ AT_QMTCLOSE, AT_OK /* AT_IND_QMTCLOSE */, 0,
// MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
// 0, 0, 0, 0 },
/* HTTP */
/* QHTTPCFG_CTX */
{ AT_QHTTPCFG_CTX, AT_OK, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, 0, 0 },
/* QHTTPCFG_RHD */
{ AT_QHTTPCFG_RHD, AT_OK, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, 0, 0 },
{ AT_QHTTPCFG_RHD_0, AT_OK, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, 0, 0 },
/* QHTTPURL
* Wait for CONNECT
*/
{ AT_QHTTPURL, AT_CONNECT, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, setup_qhttpurl_cmd, 0 },
/* then send URL then wait for OK */
{ 0, AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, send_qhttpurl_cmd, 0, 0, 0 },
/* QHTTPPOST
* Wait for CONNECT
*/
{ AT_QHTTPPOST, AT_CONNECT, AT_IND_CME_ERROR,
80000, MODEM_DEFAULT_CMD_RETRIES,
0, 0, setup_qhttppost_cmd, 0 },
/* then send POST-Message then wait for OK */
{ 0, AT_HTTPPOST_SUCCESS, AT_IND_CME_ERROR, 60000,
MODEM_DEFAULT_CMD_RETRIES, send_qhttpost_cmd, 0, 0, 0 },
/* QHTTPGET
* If <request_header> is 0, then wait for +QHTTPGET indication. Then use QHTTPREAD to get the response.
* If <request_header> is 1, then wait for CONNECT then OK to frame response from this command
* Default appears to be 0.
*/
{ AT_QHTTPGET, AT_QHTTPGET_SUCCESS, AT_IND_CME_ERROR, 80000,
0 /* no retries */, 0, 0, 0, 0 },
/* QHTTPREAD
* Look for OK response before +QHTTPREAD indication to frame the GET content. */
#if USE_QHTTPREAD_OK
/* OK, +QHTTPREAD response expected. Timeout equal to QHTTPREAD command timeout. */
{ AT_QHTTPREAD, AT_QHTTPREAD_SUCCESS, /*AT_IND_CME_ERROR*/ 0, 80000,
3, 0, handle_qhttpread_cmd, 0, 0 },
#else
/* No OK Response expected, wait for timeout then parse. */
{ AT_QHTTPREAD, 0, 0, 3000, MODEM_DEFAULT_CMD_RETRIES, 0, handle_qhttpread_cmd, 0, 0 },
#endif
/* QHTTPREADFILE
* Look for OK response to frame the GET content. */
{ AT_QHTTPREADFILE, AT_QHTTPREADFILE_SUCCESS, AT_IND_CME_ERROR,
5*60*1000, MODEM_DEFAULT_CMD_RETRIES, 0, 0,
setup_qhttpreadfile_cmd, 0 },
/* QPOWD
* This command default is "1" - normal shutdown.
It is recommended to execute AT+QPOWD command to power off the module, as it is the safest and best
way. This procedure is realized by letting the module log off from the network and allowing the software to
enter a secure and safe data state before disconnecting the power supply.
After sending AT+QPOWD, do not enter any other AT commands. When the command is executed
successfully, the module will output POWERED DOWN and set the STATUS pin as low to enter power-off
state. In order to avoid data loss, it is suggested to wait for 1s at least to disconnect the power supply after
the STATUS pin is set as low and the URC POWERED DOWN is outputted. If POWERED DOWN cannot
be received within 65s, the power supply shall be disconnected compulsorily.
*
* Our procedure is to enter command and wait for standard OK to complete command sequence,
* then we monitor STATUS pin for the 65 seconds. If it is still up after that we
* can try PWRKEY and start waiting again.
*/
{ AT_QPOWD, AT_OK, 0, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, 0, 0 },
/* Send QFOTADL with URL, wait for OK */
{ AT_QFOTADL, AT_OK, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, 0, setup_qfotadl_cmd, 0 },
/* then wait 25 mins for process to complete with "END"
* Need multiple phases: download from server phase: HTTPEND
* Update phase: END. If server download didn't complete, fail it.
*/
{ 0, "\"HTTPEND\",0", "\"HTTPEND\",", 25 * 60 * 1000, 0, 0, handle_qfotadl_cmd,0, 0 },
{ 0, "\"END\"", AT_IND_CME_ERROR, 25 * 60* 1000, 0, 0, 0, 0, 0 },
//{ AT_QFLDS, AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES, 0, handle_flds_cmd, 0, 0 },
/* File transfer procedure:
* 1. Look for specific file, get file size from response. setup, handler : set size
* 2. Open file. handler: success : get file handle, call user open cb, init transfer state.
* 3 read file repeatedly until bytes consumed. Use handler to control this as a retry.
*/
{ AT_QFLST, AT_OK, AT_IND_CME_ERROR,
MODEM_DEFAULT_CMD_TIMEOUT_MS, MODEM_DEFAULT_CMD_RETRIES,
0, handle_qflst_cmd, setup_qflst_cmd, 0 }, { AT_QFOPEN,
AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, handle_qfopen_cmd,
setup_qfopen_cmd, 0 },
/* Look for OK response to frame FREAD data. This is dangerous by itself because \r\nOK\r\n is likely located within
* the byte stream. To make this work, we use the handler in combination to check that we have found \r\nOK\r\n AND the
* expected number of bytes.
*/
{ AT_QFREAD, AT_OK /* See note */,
0 /* do not look for error response */,
10000 /* timeout */, 0 /* no retries */, 0,
handle_qfread_cmd, setup_qfread_cmd, 0 }, { AT_QFCLOSE,
AT_OK, AT_IND_CME_ERROR, MODEM_DEFAULT_CMD_TIMEOUT_MS,
MODEM_DEFAULT_CMD_RETRIES, 0, 0, setup_qfclose_cmd, 0 }
};
#define MODEM_NUM_CMDS (sizeof(cmd_table) / sizeof(cmd_table[0]))
static int add_cmd(const modem_cmd_t *cmd) {
int retval = MODEM_STATUS_ERROR;
if (cmd) {
if (bg96.cmd_num < MODEM_MAX_SEQUENCE) {
bg96.cmd_list[bg96.cmd_num++] = cmd;
retval = MODEM_STATUS_OK;
}
}
return retval;
}
int modem_network_connect(modem_op_cb_t cb) {
bg96.cmd_num = 0;
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CTZU])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CPIN])) {
return MODEM_STATUS_ERROR;
}
/*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CGDCONT_ALT])) {
return MODEM_STATUS_ERROR;
}*/
/*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CFUN_OFF])) {
return MODEM_STATUS_ERROR;
}*/
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CFUN])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_COPS])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCSQ])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCFG_IOTMODE])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFDEL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCFG_IOTMODE])) {
return MODEM_STATUS_ERROR;
}
/*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QICSGP])) {
return MODEM_STATUS_ERROR;
}*/
#if FAKE_NETWORK
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QIACT])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting connect network sequence %d\r\n", bg96.cmd_num);
}
return retval;
}
#if 0
int modem_mqtt_connect(modem_op_cb_t cb, const char *host, int port,
const char *client_id) //, const char* jwt)
{
bg96.cmd_num = 0;
/*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_SSL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFDEL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFUPL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFUPL_SEND])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_CACERT])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_SECLEVEL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_SSLVER])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_CIPH])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QSSLCFG_IGNRTIME])) {
return MODEM_STATUS_ERROR;
}*/
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_WILL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_TIMEOUT])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_KEEPALIVE])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCFG_VERSION])) {
return MODEM_STATUS_ERROR;
}
/* Get signal strength at this location, more reliable than right after COPS. */
/*if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QCSQ])) { //Not needed - Partha March 14, 2022
return MODEM_STATUS_ERROR;
}*/
/* Also get time here, may be more reliable. Time is needed before the first publish, below. */
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_CCLK])) {
return MODEM_STATUS_ERROR;
}
#if FAKE_NETWORK
/*Don't do the actual MQTT connection */
//For testing the new WF1 and WF2 messages - Partha - March 16, 2022
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTOPEN])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCONN_CID])) {
return MODEM_STATUS_ERROR;
}
// //Get Serving cell data - Partha - March 12, 2022
// if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QENG_SVCELL])) {
// return MODEM_STATUS_ERROR;
// }
//
// //GEt neighbout cell list - Partha - March 12, 2022
// if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QENG_NBCELL])) {
// return MODEM_STATUS_ERROR;
// }
#endif
bg96.cmd_complete_cb = cb;
bg96.mqtt_params.host = host;
bg96.mqtt_params.port = port;
//bg96.mqtt_params.jwt = jwt;
bg96.mqtt_params.client_id = client_id;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting connect mqtt sequence %d\r\n", bg96.cmd_num);
}
return retval;
}
/* Publish data to the specific topic */
int modem_mqtt_publish(modem_op_cb_t cb, const char *topic, const char *data,
int len) {
bg96.cmd_num = 0;
if (!topic || !data) {
return MODEM_STATUS_ERROR;
}
#if FAKE_NETWORK
/*Don't do the actual MQTT connection */
#else
/* Two-step operation, see QMTPUB command description. */
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTPUB])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTPUB_SEND])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
bg96.mqtt_params.pub_topic = topic;
bg96.mqtt_params.pub_data = data;
bg96.mqtt_params.pub_data_len = len;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting mqtt publish %d bytes to %s\r\n", len, topic);
}
return retval;
}
int modem_mqtt_close(modem_op_cb_t cb) {
bg96.cmd_num = 0;
#if FAKE_NETWORK
/*Don't do the actual MQTT connection */
#else
/*
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTDISC])) {
return MODEM_STATUS_ERROR;
}
*/
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QMTCLOSE])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting mqtt close\r\n");
}
return retval;
}
#endif
/* Setup the HTTP context */
int modem_http_setup(modem_op_cb_t cb) {
bg96.cmd_num = 0;
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_CTX])) {
return MODEM_STATUS_ERROR;
}
bg96.cmd_complete_cb = cb;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting http setup\r\n");
}
return retval;
}
/* Make an HTTP GET request and store result in supplied buffer */
int modem_http_get_to_buf(modem_op_cb_t cb, const char *url) {
bg96.cmd_num = 0;
#if FAKE_NETWORK
/*Don't do the actual HTTP connection */
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_RHD_0])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPGET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREAD])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
bg96.http_params.url = url;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting http get to buffer from url: %s\r\n", url);
}
return retval;
}
/* Make an HTTP GET request and store result to file on modem file system.
* Use modem_read_from_file to get contents.
*/
int modem_http_get_to_file(modem_op_cb_t cb, const char *url, const char *file) {
bg96.cmd_num = 0;
#if FAKE_NETWORK
/*Don't do the actual HTTP connection */
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPGET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREADFILE])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
bg96.http_params.url = url;
bg96.http_params.file = file;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting http get to file from url: %s file: %s\r\n",
bg96.http_params.url, bg96.http_params.file);
}
return retval;
}
int modem_http_post_message(modem_op_cb_t cb, const char *url, const char *message) {
bg96.cmd_num = 0;
#if FAKE_NETWORK
/*Don't do the actual HTTP connection */
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPCFG_RHD])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPURL_SET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPPOST])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPPOST_SET])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QHTTPREAD])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
bg96.http_params.url = url;
bg96.http_params.post_message = message;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting http-post to send the json-formatted message");//: %s to url: %s \r\n",
//bg96.http_params.post_message, bg96.http_params.url);
}
return retval;
}
int modem_read_from_file(modem_op_cb_t cb, const char *file,
modem_read_from_file_open cbOpen, modem_read_from_file_data cbData) {
bg96.cmd_num = 0;
#if FAKE_NETWORK
/*Don't do the actual read from file connection */
#else
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFLST])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOPEN])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFREAD])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFCLOSE])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
/* setup handler for QFLST uses this field for file name */
bg96.file_params.file = file;
bg96.file_params.open_cb = cbOpen;
bg96.file_params.data_cb = cbData;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting read from file: %s\r\n", file);
}
return retval;
}
/* Update modem command sequence. */
int modem_update_modem(modem_op_cb_t cb, const char *url) {
bg96.cmd_num = 0;
ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL");
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL])) {
return MODEM_STATUS_ERROR;
}
ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL_WAIT_HTTPEND");
if (MODEM_STATUS_ERROR
== add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL_WAIT_HTTPEND])) {
return MODEM_STATUS_ERROR;
}
ESP_LOGI(TAG,"cmd-> AT_CMD_IDX_QFOTADL_WAIT_END");
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QFOTADL_WAIT_END])) {
return MODEM_STATUS_ERROR;
}
bg96.cmd_complete_cb = cb;
bg96.http_params.url = url;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Started modem update sequence\r\n");
}
return retval;
}
/* Internal command sequence */
static int shutdown_sequence(modem_op_cb_t cb) {
bg96.cmd_num = 0;
#if 1 // Set 0 for testing error handling
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QPOWD])) {
return MODEM_STATUS_ERROR;
}
#endif
bg96.cmd_complete_cb = cb;
int retval = start_cmd_sequence();
if (retval == MODEM_STATUS_OK) {
ESP_LOGI(TAG,"Starting shutdown sequence\r\n");
}
return retval;
}
/* Functions ----------------------------------------------------------------*/
extern bool uart1_rx_needed;
static int start_rx_stream() {
memset(&RxScanContextSuccess, 0, sizeof(RxScanContextSuccess));
memset(&RxScanContextError, 0, sizeof(RxScanContextError));
memset(rxBuf, 0, MODEM_RX_BUFFER_LEN);
rx_idx = 0;
uart1_rx_needed = true;
//bg96.poll_uart = true;
return MODEM_STATUS_OK;
}
static void stop_rx_stream() {
uart1_rx_needed = false;
bg96.poll_uart = false;
}
/* Scan RX buffer with len characters in it for given string.
* Different from strstr, this function won't terminate on finding 0 in rxBuf.
* This handles modem's startup \0RDY indication.
*/
int scan_rx_buffer(int num_bytes, const char *str, ScanContext_t *scanContext,
const char *buf, int len) {
int str_len = strlen(str);
while (num_bytes > 0) {
/* Look for consecutive sequence of characters from str in buffer */
if (buf[scanContext->RxScannerIdx]
== str[scanContext->RxScannerState]) {
scanContext->RxScannerState++;
if (scanContext->RxScannerState == str_len) {
return MODEM_STATUS_OK;
}
} else if (buf[scanContext->RxScannerIdx] == str[0]) {
scanContext->RxScannerState = 1;
if (scanContext->RxScannerState == str_len) {
return MODEM_STATUS_OK;
}
} else {
scanContext->RxScannerState = 0;
}
if (++scanContext->RxScannerIdx == len) {
/* wrap the scanner around */
scanContext->RxScannerIdx = 0;
}
num_bytes--;
}
return MODEM_STATUS_ERROR;
}
/* ------------------------------------------------------------------------- */
static const char* state_str(modem_state_t s) {
switch (s) {
case MODEM_STATE_INIT:
return "INIT";
case MODEM_STATE_READY:
return "READY";
case MODEM_STATE_WAIT_POWERUP:
return "WAIT_POWERUP";
case MODEM_STATE_WAIT_STARTUP:
return "WAIT_STARTUP";
case MODEM_STATE_WAIT_STARTUP_RDY:
return "WAIT_STARTUP_RDY";
case MODEM_STATE_WAIT_POWERDOWN:
return "WAIT_POWERDOWN";
case MODEM_STATE_CMD:
return "CMD";
default:
return "<unknown>";
}
}
static void transition(modem_state_t state_to) {
ESP_LOGI(TAG,"%s->%s\n", state_str(bg96.state), state_str(state_to));
bg96.state = state_to;
}
static int transition_ready(void) {
transition(MODEM_STATE_READY);
if (bg96.start_cb) {
bg96.start_cb(MODEM_STATUS_OK);
bg96.start_cb = 0; /* only send notification once */
}
return MODEM_STATUS_OK;
}
/* Monitor the stream periodically */
static int monitor_rx_stream(const char *success, const char *error) {
/* Copy bytes from the circular buffer to the receive buffer for processing.
* The goal is to look for complete success or error strings
*/
uint16_t prev_rx_idx = rx_idx;
int bytesCopied = board_uart_rx_stream_poll((uint8_t*) rxBuf, &rx_idx, sizeof(rxBuf));
if(bytesCopied > 0)
{
//ESP_LOGI(TAG,"Got %d bytes %d\r\n", bytesCopied, rx_idx);
//ESP_LOGI(TAG, "Uart RX: %s", rxBuf);
if(strstr(rxBuf,"+QIND: \"FOTA\",\"DOWNLOADING\""))
{
rx_idx = 0;
//ESP_LOGI(TAG, "Unecessary DFOTA DOWNLOADING LOGS");
ESP_LOGI(TAG, "%s",rxBuf);
}
else
{
ESP_LOGI(TAG, "Uart RX:");
}
}
else
{
rx_idx = prev_rx_idx;
}
if (rx_idx != 0) // if something in the buffer
{
/* Check for success and error strings in buffer */
if (success && scan_rx_buffer(bytesCopied, success, &RxScanContextSuccess, rxBuf, MODEM_RX_BUFFER_LEN - 1) == MODEM_STATUS_OK)
{
ESP_LOGI(TAG, "VERIFIED RESPONSE: %s", success);
return MODEM_STATUS_OK;
}
if (error && scan_rx_buffer(bytesCopied, error, &RxScanContextError, rxBuf, MODEM_RX_BUFFER_LEN - 1) == MODEM_STATUS_OK)
{
return MODEM_STATUS_ERROR;
}
}
if(strstr(rxBuf,success))
{
ESP_LOGI(TAG, "VERIFIED RESPONSE: %s", success);
return MODEM_STATUS_OK;
}
if(strstr(rxBuf,error))
{
return MODEM_STATUS_ERROR;
}
//ESP_LOGI(TAG, "Timeout .... didn't fined %s or %s", success,error);
/* So far found nothing that matches */
return MODEM_STATUS_TIMEOUT;
}
/* Send the current command in the sequence */
static int send_command(const modem_cmd_t *cmd) // const char* cmd, int timeout_ms)
{
int retval = MODEM_STATUS_OK;
/*Checking the passed command(check it it's zero or not)*/
if (cmd->response_match_str) {
/* Start the receive stream */
retval = start_rx_stream();
} else {
/*return MODEM_STATUS_OK */
retval = MODEM_STATUS_OK;
}
if (MODEM_STATUS_OK == retval) {
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*(cmd->wait_ms)))
{
retval = MODEM_STATUS_OK;
}
if (MODEM_STATUS_OK == retval) {
const char *cmd_str = cmd->cmd_str;
/* Invoke setup function */
if (cmd->setup) {
if (MODEM_STATUS_OK
== cmd->setup(cmd->params, txBuf, sizeof(txBuf))) {
cmd_str = txBuf;
}
}
if (cmd_str) {
/* trace the command sent to modem */
ESP_LOGI(TAG,"SEND: %s",cmd_str);
uart_ifx_uart1_send_bytes((uint8_t *)cmd_str, strlen(cmd_str));
}
/* Invoke the optional command-specific send function */
if (cmd->send) {
cmd->send();
}
transition(MODEM_STATE_CMD);
} else {
stop_rx_stream();
}
}
return retval;
}
/* Run a command sequence
* Return index of last command the suceeded,
* if it matchs the number of commands-1 then it completed.
*/
static int start_cmd_sequence(void) {
#if FAKE_COMMS
/* Don't issue any commands */
if (bg96.cmd_complete_cb) {
bg96.cmd_complete_cb(MODEM_STATUS_OK);
bg96.cmd_complete_cb = 0;
}
return MODEM_STATUS_OK;
#else
int retval = MODEM_STATUS_OK;
bg96.cmd_sequence = 0;
/* Submit each command to the command state */
if (bg96.cmd_sequence < bg96.cmd_num) {
const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence];
if (cmd) {
bg96.cmd_retries = 0;
retval = send_command(cmd);
}
} else {
/* Sequence wasn't started for whatever reason, indicate to caller
* that it is done.
*/
if (bg96.cmd_complete_cb) {
bg96.cmd_complete_cb(MODEM_STATUS_OK);
bg96.cmd_complete_cb = 0;
}
}
return retval;
#endif
}
static int end_cmd_sequence(int status) {
stop_rx_stream();
ESP_LOGI(TAG,"Ending command sequence : %d %d\r\n", bg96.is_startup, status);
/* end a command sequence
* notify callback with result
*/
if (bg96.cmd_complete_cb) {
bg96.cmd_complete_cb(status);
bg96.cmd_complete_cb = 0;
}
if (bg96.is_startup) {
bg96.is_startup = false;
/* First time in ready */
transition_ready();
} else {
/* Go back to ready - only if we were in the CMD state.
* This is to support cases where callbacks modify the state
*/
if (bg96.state == MODEM_STATE_CMD) {
transition(MODEM_STATE_READY);
}
}
return MODEM_STATUS_OK;
}
/* Go to the next command if any.
Otherwise end it successfully.
*/
static int next_cmd_sequence(void) {
int retval = MODEM_STATUS_ERROR;
if (bg96.cmd_repeat) {
ESP_LOGI(TAG,"Repeating command\r\n");
/* repeat the same command if requested by a command handler. */
bg96.cmd_repeat = false;
} else {
/* Move to next command in sequence */
bg96.cmd_sequence++;
}
ESP_LOGI(TAG,"Next command sequence: %d/%d\r\n", bg96.cmd_sequence + 1,
bg96.cmd_num);
if (bg96.cmd_sequence == bg96.cmd_num) {
/* Notify success because we've completed all commands. */
retval = end_cmd_sequence(MODEM_STATUS_OK);
} else {
const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence];
if (cmd) {
bg96.cmd_retries = 0;
retval = send_command(cmd);
}
}
return retval;
}
/* Command sequence performed by modem at each startup before entering READY state. */
static int start_ready_cmd_sequence(void) {
bg96.cmd_num = 0;
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_ECHO_OFF])) {
return MODEM_STATUS_ERROR;
}
#if BOARD_USE_CTS_RTS
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IFC])) {
return MODEM_STATUS_ERROR;
}
#endif
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IDENT])) {
return MODEM_STATUS_ERROR;
}
#if MODEM_MODEM_FWVER_FROM_QGMR
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_QGMR])) {
return MODEM_STATUS_ERROR;
}
#endif
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_IMEI])) {
return MODEM_STATUS_ERROR;
}
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_ICCID])) {
return MODEM_STATUS_ERROR;
}
#if FAKE_NETWORK
/* Set time on modem to test programming of time with CCLK otherwise
* modem's default time is year 2080!!
*/
if (MODEM_STATUS_ERROR == add_cmd(&cmd_table[AT_CMD_IDX_SETCCLK])) {
return MODEM_STATUS_ERROR;
}
#endif
ESP_LOGI(TAG,"Starting READY command sequence %d\r\n", bg96.cmd_num);
return start_cmd_sequence();
}
static int start_modem(void) {
int retval = MODEM_STATUS_OK;
bg96.is_startup = true;
#if MODEM_BLIND_MODEM == 0
/* Modem already started ?! */
if (port_modem_is_on()) {
#if MODEM_DEBUG_MODEM_POWER_STATE
//board_led_green_on();
hmi_choose_led(HMI_LED_GREEN);
#endif
ESP_LOGI(TAG,"Modem already started. Interface ready.\r\n");
retval = start_ready_cmd_sequence();
} else
#endif /* MODEM_BLIND_MODEM */
{
/* Start the receive stream before we start the modem to support the
* RDY method of determining when modem interface is up.
* We go through two phase: one is waiting for status to go high, then wait
* for RDY or a timeout.
*/
retval = start_rx_stream();
port_modem_ldo_pin(1);
vTaskDelay(100 / portTICK_PERIOD_MS);
if (MODEM_STATUS_OK == retval) {
/* Start a timer to time power key duration. */
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_PWRKEY_ON_DELAY_MS))
{
retval = MODEM_STATUS_OK;
}
if (MODEM_STATUS_OK == retval) {
ESP_LOGI(TAG,"Turning on - asserting power key\r\n");
port_modem_assert_pwrkey();
transition(MODEM_STATE_WAIT_POWERUP);
#if MODEM_DEBUG_MODEM_POWER_STATE
//board_led_green_on();
hmi_choose_led(HMI_LED_GREEN);
#endif
} else {
ESP_LOGI(TAG,"Failed to turn on modem\r\n");
stop_rx_stream();
}
}
}
return retval;
}
#if MODEM_BLIND_MODEM == 0
static int stop_modem_pwrkey(void) {
int retval = MODEM_STATUS_OK;
/* Stop any timer that might be on going */
esp_timer_stop(modem_timer);
if (port_modem_is_on()) {
/* Start a timer to time power key duration. */
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_PWRKEY_OFF_DELAY_MS))
{
retval = MODEM_STATUS_OK;
}
if (MODEM_STATUS_OK == retval) {
ESP_LOGI(TAG,"Turning off - asserting power key\r\n");
bg96.shutdown_counter = 0;
bg96.shutdown_qpowd = false;
port_modem_assert_pwrkey();
transition(MODEM_STATE_WAIT_POWERKEY);
} else {
ESP_LOGI(TAG,"Failed to turn off modem\r\n");
transition(MODEM_STATE_INIT);
port_modem_ldo_pin(0);
}
} else {
ESP_LOGI(TAG,"Turning off - modem already off\r\n");
transition(MODEM_STATE_INIT);
}
return retval;
}
#endif
static void shutdown_seq_complete_cb(int retval) {
if (MODEM_STATUS_OK == retval) {
esp_timer_stop(modem_timer);
/* Start a timer to monitor STATUS for shutdown. */
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS))
{
retval = MODEM_STATUS_OK;
}
bg96.shutdown_counter = 0;
bg96.shutdown_qpowd = true;
transition(MODEM_STATE_WAIT_POWERDOWN);
} else {
ESP_LOGI(TAG,"Failed to turn off with command\r\n");
#if MODEM_BLIND_MODEM
transition(MODEM_STATE_INIT);
port_modem_ldo_pin(0);
#else
stop_modem_pwrkey();
#endif
}
}
static int stop_modem(void) {
int retval = MODEM_STATUS_OK;
/* Stop any timer that might be on going */
esp_timer_stop(modem_timer);
#if MODEM_BLIND_MODEM == 0
if (port_modem_is_on())
#endif
{
/* Start the shutdown sequence */
retval = shutdown_sequence(shutdown_seq_complete_cb);
if (MODEM_STATUS_OK != retval) {
ESP_LOGI(TAG,"Failed to turn off modem with command\r\n");
#if MODEM_BLIND_MODEM == 0
retval = stop_modem_pwrkey();
#endif
}
}
#if MODEM_BLIND_MODEM == 0
else {
ESP_LOGI(TAG,"Turning off - modem already off\r\n");
transition(MODEM_STATE_INIT);
}
#endif
return retval;
}
/* !! Wait an additional period for the UART interface to become active.
* An alternative is: (from BG96 UART app note)
* Note: AT command can be input through UART port only after module is powered
* on and the Unsolicited Result Code "RDY" is output.
* Note: observed that a 0 is output before the RDY code.
* Note: Device outputs "RDY", then later "APP RDY". It doesn't respond until APP RDY.
* Note: Normally this is about 4500 ms after bootup. In some instances, this period
* elapses without detecting "APP RDY", even though modem responds to AT commands.
*
* Notes for BG77:
* RDY seems to appear about when STATUS is raised, this is about 1-2 seconds later.
* APP RDY seems to appear about 3 seconds after RDY.
*/
static int wait_modem_ready(void) {
int retval = MODEM_STATUS_OK;
/* Note that modem rx stream was started at begining of startup process
* to capture RDY whenever it arrives
*/
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_STARTUP_RDY_TIMEOUT_MS))
{
retval = MODEM_STATUS_OK;
}
if (MODEM_STATUS_OK == retval) {
transition(MODEM_STATE_WAIT_STARTUP_RDY);
ESP_LOGI(TAG,"Waiting for AT interface activation\r\n");
}
return retval;
}
/* ------------------------------------------------------------------------- */
#if (WIFI_NEEDED == 1)
extern bool server_uart_flag;
#endif
void uart_ifx_uart1_rx_cb(void) {
bg96.poll_uart = true;
#if (WIFI_NEEDED == 1)
server_uart_flag = 1;
#endif
}
static void timer_handler(void *context)
{
bg96.poll_timer = true;
}
static int state_machine(modem_event_t evt) {
int retval = MODEM_STATUS_OK;
switch (bg96.state) {
case MODEM_STATE_INIT:
if (evt == MODEM_EVENT_START) {
/* Indicate offloading data */
//hmi_set_offload_mode(true);
ESP_LOGI(TAG,"Process started.\n");
/* Start modem and set next state */
retval = start_modem();
} else {
//ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
}
break;
/* wait for modem powerkey assertion delay */
case MODEM_STATE_WAIT_POWERUP:
if (evt == MODEM_EVENT_TIMER) {
//board_modem_deassert_pwrkey();
port_modem_deassert_pwrkey();
/* Start a timer to poll for status. */
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_TIMER_GRANULARITY_MS))
{
retval = MODEM_STATUS_OK;
}
bg96.poll_periods = MODEM_STARTUP_STATUS_TIMEOUT_MS
/ MODEM_TIMER_GRANULARITY_MS;
transition(MODEM_STATE_WAIT_STARTUP);
ESP_LOGI(TAG,"Waiting for status activation\r\n");
} else if (evt == MODEM_EVENT_UART) {
/* Ignore UART events */
} else {
ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
}
break;
/* wait for modem status to go high */
case MODEM_STATE_WAIT_STARTUP:
if (evt == MODEM_EVENT_TIMER) {
#if MODEM_BLIND_MODEM
/* Wait for the AT interface to ready */
retval = wait_modem_ready();
#else
if (port_modem_is_on()) {
/* Wait for the AT interface to ready */
retval = wait_modem_ready();
} else if (--bg96.poll_periods > 0) {
/* Keep waiting */
esp_timer_start_once(modem_timer, 1000*MODEM_TIMER_GRANULARITY_MS);
} else {
/* Timed out. */
ESP_LOGI(TAG,"Modem startup timed out\r\n");
stop_rx_stream();
transition(MODEM_STATE_INIT);
bg96.start_cb(MODEM_STATUS_ERROR);
}
#endif /* MODEM_BLIND_MODEM */
} else if (evt == MODEM_EVENT_UART) {
/* Ignore UART events */
} else {
ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
retval = MODEM_STATUS_ERROR;
}
break;
/* wait for modem AT interface to come up */
case MODEM_STATE_WAIT_STARTUP_RDY:
if (evt == MODEM_EVENT_UART) {
/* monitor for the modem interface "ready" indication */
int res = monitor_rx_stream("RDY", NULL);
if (MODEM_STATUS_OK == res) {
/* Stop the timeout timer waiting for APP RDY now that we have it */
/* Note: APP RDY seems to come about 3 seconds after RDY. */
esp_timer_stop(modem_timer);
ESP_LOGI(TAG,"AT interface activated.\r\n");
// hmi_set_modem_error(false); /* clear modem error on off chance it actually recovered. */
retval = start_ready_cmd_sequence();
} else {
/* Keep listening */
}
} else if (evt == MODEM_EVENT_TIMER) {
#if MODEM_BLIND_MODEM
/* clear this anyways for blind modem testing */
hmi_set_modem_error(false);
#endif
/* Timed out. */
ESP_LOGW(TAG,"timeout waiting for modem ready\r\n");
/* Start ready sequence anyways */
//retval = start_ready_cmd_sequence();
retval = MODEM_STATUS_ERROR;
} else {
ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
retval = MODEM_STATUS_ERROR;
}
break;
/* waiting for response from command */
case MODEM_STATE_CMD:
if (evt == MODEM_EVENT_TIMER) {
/* Timeout */
stop_rx_stream();
const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence];
if (cmd && cmd->response_match_str) {
/* We will stop the sequence unless a handler can reverse this decision by
* returning an OK or there are retries available.
*/
int res = MODEM_STATUS_ERROR;
/* If the command had no responses to wait for, invoke the handler now */
//if (cmd->response_match_str == 0 && cmd->err_match_str == 0)
{
if (cmd->handler) {
res = cmd->handler(MODEM_STATUS_TIMEOUT, rxBuf, rx_idx);
}
}
if (res == MODEM_STATUS_OK) {
/* A success response from the handler can continue the sequence. */
retval = next_cmd_sequence();
} else {
/* Handler was not available or did not reverse the error condition. */
ESP_LOGW(TAG,"Command response timed out\r\n");
/* See if we need to retry */
if (bg96.cmd_retries++ < cmd->retries) {
ESP_LOGW(TAG,"retry (%d/%d) command (%d/%d): %s\r\n",
bg96.cmd_retries, cmd->retries,
bg96.cmd_sequence + 1, bg96.cmd_num,
cmd->cmd_str);
retval = send_command(cmd);
} else {
if(cmd->cmd_str)
{
ESP_LOGW(TAG,"Command failed: %s", cmd->cmd_str); /* command strings have \r\n */
}
else
{
ESP_LOGW(TAG,"The command that has the response %s ---> get failed", cmd->response_match_str);
}
retval = end_cmd_sequence(MODEM_STATUS_ERROR);
}
}
} else if (cmd->response_match_str == 0) {
/*Check if there's a timeout handle to be executed*/
if (cmd->handler) {
/*Call the Command handler*/
cmd->handler(MODEM_STATUS_OK, rxBuf, rx_idx);
}
/*Execute the next command in the command list*/
retval = next_cmd_sequence();
}
}
else if (evt == MODEM_EVENT_UART) {
/* Process the current command */
const modem_cmd_t *cmd = bg96.cmd_list[bg96.cmd_sequence];
if (cmd && cmd->response_match_str)
{
/* monitor for responses */
int res = monitor_rx_stream(cmd->response_match_str, cmd->err_match_str);
/* Run the handler for found success or error responses */
if((MODEM_STATUS_OK == res) || (MODEM_STATUS_ERROR == res))
{
if (cmd->handler)
{
/* A success response from the handler can continue the sequence. */
ESP_LOGI(TAG," going to handler");
res = cmd->handler(res, rxBuf, rx_idx);
}
}
if (MODEM_STATUS_ERROR == res)
{
esp_timer_stop(modem_timer);
/* matched the expected error response, fail the sequence */
ESP_LOGI(TAG,"MODEM_STATUS_ERROR -> end_cmd_sequence ");
retval = end_cmd_sequence(MODEM_STATUS_ERROR);
}
else if (MODEM_STATUS_OK == res)
{
esp_timer_stop(modem_timer);
ESP_LOGI(TAG,"MODEM_STATUS_OK -> next_cmd_sequence ");
/* matched the expected success response move on to the next */
retval = next_cmd_sequence();
}
else
{
/* nothing found yet */
}
}
} else {
ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
retval = MODEM_STATUS_ERROR;
}
break;
case MODEM_STATE_READY:
if (evt == MODEM_EVENT_TIMER) {
} else if (evt == MODEM_EVENT_SEND_CMD) {
/* request to send a command sequence */
/* must have already been setup */
} else if (evt == MODEM_EVENT_STOP) {
ESP_LOGI(TAG,"Process stopping.\n");
retval = stop_modem();
} else {
ESP_LOGI(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
}
break;
case MODEM_STATE_WAIT_POWERDOWN:
if (evt == MODEM_EVENT_TIMER) {
/* Monitor STATUS for the shutdown duration. If it is still active every 10 seconds
* we note that in a warning log.
* If it doesn't shutdown after 65 seconds as datasheet says we start an LED
* error sequence.
*/
#if MODEM_BLIND_MODEM == 0
if (port_modem_is_on()) {
bg96.shutdown_counter++;
/* Start a timer to monitor STATUS for shutdown. */
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS))
{
retval = MODEM_STATUS_OK;
}
if ((bg96.shutdown_counter % MODEM_SHUTDOWN_COUNT_WARNING_SEC)
== 0) {
ESP_LOGW(TAG,"Shutting down, modem still on %d\r\n",
bg96.shutdown_counter
/ MODEM_SHUTDOWN_COUNT_WARNING_SEC);
}
if (bg96.shutdown_counter > MODEM_SHUTDOWN_COUNT_LIMIT_SEC) {
/* Waited as long as we're going to. Try power key approach if we were
* doing the QPOWD approach.
*/
if (bg96.shutdown_qpowd) {
bg96.shutdown_qpowd_failures++;
ESP_LOGI(TAG,"Failed to shutdown modem with command. %d\r\n",
bg96.shutdown_qpowd_failures);
retval = stop_modem_pwrkey();
} else {
/* This is bad. Modem does not appear to be shutdown,
* and it is likely on next power up cycle it won't
* be in the right state to use.
* Solution is to have hard power control of the modem:
* disconnect the power rails, hopefully guaranteeing
* a startup in a known state.
* Without hard power control user must remove batteries.
*/
bg96.shutdown_pwrkey_failures++;
ESP_LOGI(TAG,"Failed to shutdown modem. %d\r\n",
bg96.shutdown_pwrkey_failures);
/* Install an LED sequence to indicate this error condition.
* User would need to remove batteries to recover likely.
*/
// hmi_set_modem_error(true);
transition(MODEM_STATE_INIT);
port_modem_ldo_pin(0);
}
}
} else
#endif /* MODEM_BLIND_MODEM */
{
#if MODEM_DEBUG_MODEM_POWER_STATE
board_led_green_off();
#endif
ESP_LOGI(TAG,"Powered down, count=%d [%d %d]\r\n",
bg96.shutdown_counter, bg96.shutdown_qpowd_failures,
bg96.shutdown_pwrkey_failures);
transition(MODEM_STATE_INIT);
port_modem_ldo_pin(0);
}
} else {
ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
}
break;
case MODEM_STATE_WAIT_POWERKEY:
if (evt == MODEM_EVENT_TIMER) {
port_modem_deassert_pwrkey();
ESP_LOGI(TAG,"Powering down with power key.\r\n");
if(ESP_OK == esp_timer_start_once(modem_timer, 1000*MODEM_SHUTDOWN_STATUS_MONITOR_PERIOD_MS))
{
retval = MODEM_STATUS_OK;
}
transition(MODEM_STATE_WAIT_POWERDOWN);
} else {
ESP_LOGW(TAG,"Unhandled event %d : state %d\r\n", evt, bg96.state);
}
break;
default:
break;
}
return retval;
}
/* ------------------------------------------------------------------------- */
/* Initialize. */
int modem_init(void) {
int retval = MODEM_STATUS_OK;
memset(&bg96, 0, sizeof(bg96));
/* Note imei is 15 character string */
memset(bg96.imei, '0', sizeof(bg96.imei) - 1);
//timer_initiating(MODEM_TIMER_ID, timer_handler);
//timer_starting(MODEM_TIMER_ID, 1000);
const esp_timer_create_args_t modem_timer_args = {
.callback = &timer_handler,
/* argument specified here will be passed to timer callback function */
.arg = (void*) modem_timer,
.name = "modem-timer"
};
esp_timer_create(&modem_timer_args, &modem_timer);
ESP_LOGI(TAG,"Process initialized.\n");
return retval;
}
/* Start the process */
int modem_start(modem_start_cb_t cb) {
#if FAKE_COMMS
/* Go directly to ready state. Add an IMEI and fw ver */
strcpy(bg96.fw_ver, "BG95M2LAR01A01"); // BG77LAR02A04
// Units deployed
//strcpy(bg96.imei, "866349041329859");
//strcpy(bg96.imei, "866349041332549");
//strcpy(bg96.imei, "866349041331970");
// 866349041329859 -> (extract) 904132985 -> (hex) 35E3F979 -> major=0x35E3 minor=0xF979
strcpy(bg96.imei, "866349041329859");
ESP_LOGI(TAG,"IMEI No: %s\r\n", bg96.imei);
config_set_device_id_from_imei(bg96.imei);
transition(MODEM_STATE_READY);
if (cb) {
cb(MODEM_STATUS_OK);
}
return MODEM_STATUS_OK;
#else
bg96.start_cb = cb;
int retval = state_machine(MODEM_EVENT_START);
//xTaskCreate(modem_poll, "BG77_POLL", 2048, NULL, 9, NULL);
return retval;
#endif
}
/* Stop the process */
int modem_stop(void) {
#if FAKE_COMMS
transition(MODEM_STATE_INIT);
return MODEM_STATUS_OK;
#else
int retval = state_machine(MODEM_EVENT_STOP);
return retval;
#endif
}
/* Poll the process */
int modem_poll(void) {
int retval = MODEM_STATUS_OK;
if (bg96.poll_timer) {
bg96.poll_timer = false;
retval = state_machine(MODEM_EVENT_TIMER);
} else if (bg96.poll_uart) {
//bg96.poll_uart = false;
retval = state_machine(MODEM_EVENT_UART);
}
return retval;
}
/* Check if the process needs to be polled */
bool modem_needs_poll(void) {
return bg96.poll_timer || bg96.poll_uart;
}
/* Check if the process is started */
bool modem_is_started(void) {
return bg96.state != MODEM_STATE_INIT;
}
/* Check if the process is ready */
bool modem_is_ready(void) {
return bg96.state == MODEM_STATE_READY;
}
const char* modem_get_imei(void) {
return bg96.imei;
}
const char* modem_get_iccid(void)
{
return bg96.iccid;
}
const modem_signal_strength_t* modem_get_signal_strength(void) {
return &bg96.sig_strength;
}
const char* modem_get_fw_ver(void) {
return bg96.fw_ver;
}
int modem_http_get_buf(char **bufptr, int *len) {
#if FAKE_COMMS || FAKE_NETWORK
#if 0 // TEST MODEM UPDATE SP5 SEQUENCE
/* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST.
* scond comms: GET CONFIG, check flags, then GET WHITELIST
*/
static int iter = 0;
if (iter == 0) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
/* 0x1 - Whitelist
* 0x2 - MCU update
* 0x4 - Modem update
*/
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 1) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005511C4EAB522599000000 02,20026425603AB04037501A9E3E3EC785 03,9FFE0000000000000000000000000000 EP", bg96.imei);
}
else if (iter == 2) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 3) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei);
}
else if (iter == 4) {
/* Put in a test configuration record SP5 for comms process to use */
bg96.http_params.buf = rxBuf;
//bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei);
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei);
}
if (++iter > 4) {
iter = 0;
}
#endif
#if 0 // TEST MCU UPDATE SP5 SEQUENCE
/* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST.
* scond comms: GET CONFIG, check flags, then GET WHITELIST
*/
static int iter = 0;
if (iter == 0) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
/* 0x1 - Whitelist
* 0x2 - MCU update
* 0x4 - Modem update
*/
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 02 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 1) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 LRSM0005_v2 EP", bg96.imei);
}else if (iter == 2) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 01 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 3) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei);
}
else if (iter == 4) {
/* Put in a test configuration record SP5 for comms process to use */
bg96.http_params.buf = rxBuf;
//bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei);
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei);
}
if (++iter > 4) {
iter = 0;
}
#endif
#if 1
/* First comms: GET CONFIG (ignore flags because first contact) then GET WHITELIST.
* scond comms: GET CONFIG, check flags, then GET WHITELIST
*/
static int iter = 0;
if (iter == 0) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
/* 0x1 - Whitelist
* 0x2 - MCU update
* 0x4 - Modem update
*/
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 01 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 1) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005511C4EAB522599000000 02,20026425603AB04037501A9E3E3EC785 03,9FFE0000000000000000000000000000 EP", bg96.imei);
}
else if (iter == 2) {
/* Put in a test configuration record SP3 for comms process to use */
bg96.http_params.buf = rxBuf;
/* flags scan_int_SEC scan_win_MSEC tx_pwr_DB broadcast_int_MSEC comms_int_MIN */
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP3 %s 1599777618 05 15 2500 1 800 2 EP", bg96.imei);
}
else if (iter == 3) {
/* Put in a test configuration record SP4 for comms process to use */
bg96.http_params.buf = rxBuf;
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP4 %s 1599777618 01,0AFF4C001005091C2AC447CC28D56500 02,000906031EC0A8014500000001000000 03,000906031EC0A8014500000001000000 EP", bg96.imei);
}
else if (iter == 4) {
/* Put in a test configuration record SP5 for comms process to use */
bg96.http_params.buf = rxBuf;
//bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A04 EP", bg96.imei);
bg96.http_params.len = snprintf(bg96.http_params.buf, sizeof(rxBuf), "SP5 %s 1599777618 BG77LAR02A03 EP", bg96.imei);
}
if (++iter > 4) {
iter = 0;
}
#endif
#endif
if (bufptr && len) {
if (bg96.http_params.buf && bg96.http_params.len > 0) {
*bufptr = bg96.http_params.buf;
*len = bg96.http_params.len;
return MODEM_STATUS_OK;
}
*bufptr = 0;
*len = 0;
}
return MODEM_STATUS_ERROR;
}
/* ------ Process Callbacks ------------------------- */
__attribute__((weak)) void modem_ready_cb(void) {
}