Examples: Modem console

This commit is contained in:
David Cermak
2021-03-16 21:36:13 +01:00
parent b6a005a43e
commit da15b3d423
12 changed files with 712 additions and 364 deletions

View File

@ -1,4 +1,5 @@
idf_component_register(SRCS "modem_console_main.c"
idf_component_register(SRCS "modem_console_main.cpp"
"console_helper.cpp"
"httpget_handle.c"
"ping_handle.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,97 @@
//
// Created by david on 3/17/21.
//
#include "console_helper.hpp"
#include "esp_log.h"
static const char *TAG = "modem_console_helper";
ConsoleCommand::ConsoleCommand(const char* command, const char* help, std::vector<CommandArgs>& args, std::function<bool(ConsoleCommand *)> f):
func(std::move(f))
{
RegisterCommand(command, help, args);
}
void ConsoleCommand::RegisterCommand(const char* command, const char* help, std::vector<CommandArgs>& args)
{
assert(last_command <= MAX_REPEAT_NR);
void * common_arg = nullptr;
for (auto it: args) {
switch(it.type) {
case ARG_END:
break;
case STR0:
common_arg = arg_str0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case STR1:
common_arg = arg_str1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT0:
common_arg = arg_int0(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
case INT1:
common_arg = arg_int1(it.shortopts, it.longopts, it.datatype, it.glossary);
break;
}
if (common_arg) {
arg_table.emplace_back(common_arg);
} else {
ESP_LOGE(TAG, "Creating argument parser failed for %s", it.glossary);
abort();
}
}
arg_table.emplace_back( arg_end(1));
const esp_console_cmd_t command_def = {
.command = command,
.help = help,
.hint = nullptr,
.func = command_func_pts[last_command],
.argtable = &arg_table[0]
};
ESP_ERROR_CHECK(esp_console_cmd_register(&command_def));
last_command++;
console_commands.emplace_back(this);
}
int ConsoleCommand::get_count(int index)
{
return ((struct arg_str *)arg_table[index])->count;
}
std::string ConsoleCommand::get_string(int index)
{
if (get_count(index) > 0) {
return std::string(((struct arg_str *)arg_table[index])->sval[0]);
}
return std::string();
}
std::vector<ConsoleCommand*> ConsoleCommand::console_commands;
int ConsoleCommand::last_command = 0;
const esp_console_cmd_func_t ConsoleCommand::command_func_pts[] = {
#define TEMPLATE(index) ConsoleCommand::command_func_ ## index ,
REPEAT_TEMPLATE_DEF(list of function pointers to command_func_XX() )
#undef TEMPLATE
};
int ConsoleCommand::command_func(int argc, char **argv) {
void * plain_arg_array = &arg_table[0];
int nerrors = arg_parse(argc, argv, (void **)plain_arg_array);
if (nerrors != 0) {
arg_print_errors(stderr, (struct arg_end *) arg_table.back(), argv[0]);
return 1;
}
if (func) {
return func(this);
}
return 0;
}

View File

@ -0,0 +1,76 @@
//
// Created by david on 3/17/21.
//
#ifndef MODEM_CONSOLE_CONSOLE_HELPER_H
#define MODEM_CONSOLE_CONSOLE_HELPER_H
#include <vector>
#include <algorithm>
#include <functional>
#include <esp_console.h>
#include "argtable3/argtable3.h"
#include "repeat_helper.inc"
#define MAX_REPEAT_NR 10
enum arg_type {
STR0,
STR1,
INT0,
INT1,
ARG_END,
};
struct CommandArgs {
CommandArgs(arg_type t, const char * shopts, const char * lopts, const char * data, const char * glos):
type(t), shortopts(shopts), longopts(lopts), datatype(data), glossary(glos) {}
arg_type type;
const char *shortopts;
const char *longopts;
const char *datatype;
const char *glossary;
};
class ConsoleCommand {
public:
template<typename T> explicit ConsoleCommand(const char* command, const char* help,
const T *arg_struct, size_t srg_struct_size , std::function<bool(ConsoleCommand *)> f):
func(std::move(f))
{
size_t args_plain_size = srg_struct_size / sizeof(CommandArgs);
auto first_arg = reinterpret_cast<const CommandArgs *>(arg_struct);
std::vector<CommandArgs> args(first_arg, first_arg + args_plain_size);
RegisterCommand(command, help, args);
}
explicit ConsoleCommand(const char* command, const char* help, std::vector<CommandArgs>& args, std::function<bool(ConsoleCommand *)> f);
int get_count(int index);
template<typename T> int get_count_of(CommandArgs T::*member) { return get_count(index_arg(member)); }
template<typename T> std::string get_string_of(CommandArgs T::*member) { return get_string(index_arg(member)); }
std::string get_string(int index);
private:
void RegisterCommand(const char* command, const char* help, std::vector<CommandArgs>& args);
template<typename T> static constexpr size_t index_arg(CommandArgs T::*member)
{ return ((uint8_t *)&((T*)nullptr->*member) - (uint8_t *)nullptr)/sizeof(CommandArgs); }
std::function<bool(ConsoleCommand *)> func;
std::vector<void*> arg_table;
int command_func(int argc, char **argv);
#define TEMPLATE(index) \
static inline int command_func_ ## index(int argc, char **argv) \
{ return console_commands[index]->command_func(argc, argv); }
REPEAT_TEMPLATE_DEF(definition of command_func_XX() )
#undef TEMPLATE
static std::vector<ConsoleCommand*> console_commands;
static int last_command;
const static esp_console_cmd_func_t command_func_pts[];
};
#endif //MODEM_CONSOLE_CONSOLE_HELPER_H

View File

@ -1,309 +0,0 @@
/* Modem console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <esp_modem_dce_common_commands.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "argtable3/argtable3.h"
#include "esp_modem.h"
#include "esp_log.h"
// utilities to check network connectivity
void modem_console_register_http(void);
void modem_console_register_ping(void);
static esp_modem_dce_t *s_dce = NULL;
static const char *TAG = "modem_console";
static struct {
struct arg_str *command;
struct arg_int *param_int;
struct arg_str *param_str;
struct arg_str *param_pdp;
struct arg_str *param_bool;
struct arg_str *param;
struct arg_str *result;
struct arg_end *end;
} at_args;
static struct {
struct arg_str *param;
struct arg_end *end;
} modem_args;
static struct {
struct arg_str *command;
struct arg_int *timeout;
struct arg_str *pattern;
struct arg_lit *no_cr;
struct arg_end *end;
} generic_at_args;
static char s_common_in_str[100]; // used as common string input param holder
static char s_common_out_str[100]; // used as output string/command result holder
static esp_err_t handle_line_pattern(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "handle_line_pattern: DCE response: %s\n", line);
if (strstr(line, dce->handle_line_ctx)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
}
return err;
}
static int do_dce(int argc, char **argv)
{
// specific DCE generic command params
static bool bool_result;
static char pdp_type[10];
static char pdp_apn[10];
static esp_modem_dce_pdp_ctx_t pdp = { .type = pdp_type, .apn = pdp_apn };
static esp_modem_dce_csq_ctx_t csq;
static esp_modem_dce_cbc_ctx_t cbc;
int nerrors = arg_parse(argc, argv, (void **) &at_args);
if (nerrors != 0) {
arg_print_errors(stderr, at_args.end, argv[0]);
return 1;
}
void * command_param = NULL;
void * command_result = NULL;
// parse input params
if (at_args.param_int->count > 0) {
command_param = (void*)(at_args.param_int->ival[0]);
} else if (at_args.param_bool->count > 0) {
const char * bool_in_str = at_args.param_bool->sval[0];
s_common_out_str[0] = '\0';
command_result = s_common_out_str;
if (strstr(bool_in_str,"true") || strstr(bool_in_str,"1")) {
command_param = (void*)true;
} else {
command_param = (void*)false;
}
} else if (at_args.param_pdp->count > 0) {
// parse out three comma separated sub-arguments
sscanf(at_args.param_pdp->sval[0], "%d,%s", &pdp.cid, pdp_type);
char *str_apn = strchr(pdp_type, ',');
if (str_apn) {
strncpy(pdp_apn, str_apn + 1, sizeof(pdp_apn));
str_apn[0] = '\0';
}
command_param = &pdp;
} else if (at_args.param_str->count > 0) {
strncpy(s_common_in_str, at_args.param_str->sval[0], sizeof(s_common_in_str));
command_param = s_common_in_str;
} else if (at_args.param->count > 0) { // default param is treated as string
strncpy(s_common_in_str, at_args.param->sval[0], sizeof(s_common_in_str));
command_param = s_common_in_str;
}
// parse output params
if (at_args.result->count > 0) {
const char *res = at_args.result->sval[0];
if (strstr(res, "csq")) {
command_result = &csq;
}else if (strstr(res, "cbc")) {
command_result = &cbc;
} else if (strstr(res, "str")) {
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
} else if (strstr(res, "bool")) {
command_result = &bool_result;
} else {
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
}
}
// by default (if no param/result provided) expect string output
if (command_param == NULL && command_result == NULL) {
s_common_out_str[0] = '\0';
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
}
esp_err_t err = esp_modem_command_list_run(s_dce, at_args.command->sval[0], command_param, command_result);
if (err == ESP_OK) {
printf("Command %s succeeded\n", at_args.command->sval[0]);
if (command_result == s_common_out_str && s_common_out_str[0] != '\0') {
ESP_LOGI(TAG, "Command string output: %s", s_common_out_str);
} else if (command_result == &csq) {
ESP_LOGI(TAG, "Command CSQ output: rssi:%d, ber:%d", csq.rssi, csq.ber);
} else if (command_result == &cbc) {
ESP_LOGI(TAG, "Command battery output:%d mV", cbc.battery_status);
} else if (command_result == &bool_result) {
ESP_LOGI(TAG, "Command bool output: %s", bool_result ? "true" : "false");
}
return 0;
}
ESP_LOGE(TAG, "Command %s failed with %d", at_args.command->sval[0], err);
return 1;
}
static int do_at_command(int argc, char **argv)
{
int timeout = 1000;
int nerrors = arg_parse(argc, argv, (void **)&generic_at_args);
if (nerrors != 0) {
arg_print_errors(stderr, generic_at_args.end, argv[0]);
return 1;
}
esp_modem_dce_handle_line_t handle_line = esp_modem_dce_handle_response_default;
strncpy(s_common_in_str, generic_at_args.command->sval[0], sizeof(s_common_in_str) - 2);
if (generic_at_args.no_cr->count == 0) {
size_t cmd_len = strlen(generic_at_args.command->sval[0]);
s_common_in_str[cmd_len] = '\r';
s_common_in_str[cmd_len + 1] = '\0';
}
if (generic_at_args.timeout->count > 0) {
timeout = generic_at_args.timeout->ival[0];
}
if (generic_at_args.pattern->count > 0) {
strncpy(s_common_out_str, generic_at_args.pattern->sval[0], sizeof(s_common_out_str));
handle_line = handle_line_pattern;
}
if (esp_modem_dce_generic_command(s_dce, s_common_in_str, timeout, handle_line, s_common_out_str) == ESP_OK) {
return 0;
}
return 1;
}
static int do_modem_lifecycle(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&modem_args);
if (nerrors != 0) {
arg_print_errors(stderr, modem_args.end, argv[0]);
return 1;
}
if (modem_args.param->count > 0) {
esp_err_t err = ESP_FAIL;
if (strstr(modem_args.param->sval[0], "PPP")) {
err = esp_modem_start_ppp(s_dce->dte);
} else if (strstr(modem_args.param->sval[0], "CMD")) {
err = esp_modem_stop_ppp(s_dce->dte);
} else {
return 1;
}
if (err == ESP_OK) {
ESP_LOGI(TAG, "set_working_mode %s succeeded", at_args.param->sval[0]);
} else {
ESP_LOGI(TAG, "set_working_mode %s failed with %d", at_args.param->sval[0], err);
}
return 0;
}
return 1;
}
static void register_dce(void)
{
at_args.command = arg_str1(NULL, NULL, "<command>", "Symbolic name of DCE command");
at_args.param_int = arg_int0("i", "int", "<num>", "Input parameter of integer type");
at_args.param_str = arg_str0("s", "str", "<str>", "Input parameter of string type");
at_args.param_pdp = arg_str0("p", "pdp", "<pdp>", "Comma separated string with PDP context");
at_args.param_bool = arg_str0("b", "bool", "<true/false>", "Input parameter of bool type");
at_args.param = arg_str0(NULL, NULL, "<param>", "Default input argument treated as string");
at_args.result = arg_str0("o", "out", "<type>", "Type of output parameter");
at_args.end = arg_end(1);
const esp_console_cmd_t at_cmd = {
.command = "dce",
.help = "send symbolic command to the modem",
.hint = NULL,
.func = &do_dce,
.argtable = &at_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&at_cmd));
}
static void register_at_command(void)
{
generic_at_args.command = arg_str1(NULL, NULL, "<command>", "AT command to send to the modem");
generic_at_args.timeout = arg_int0("t", "timeout", "<timeout>", "command timeout");
generic_at_args.pattern = arg_str0("p", "pattern", "<pattern>", "command response to wait for");
generic_at_args.no_cr = arg_litn("n", "no-cr", 0, 1, "not add trailing CR to the command");
generic_at_args.end = arg_end(1);
const esp_console_cmd_t at_command = {
.command = "at",
.help = "send generic AT command to the modem",
.hint = NULL,
.func = &do_at_command,
.argtable = &generic_at_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&at_command));
}
static void register_modem_lifecycle(void)
{
modem_args.param = arg_str1(NULL, NULL, "<mode>", "PPP or CMD");
modem_args.end = arg_end(1);
const esp_console_cmd_t modem_cmd = {
.command = "modem",
.help = "set modem mode",
.hint = NULL,
.func = &do_modem_lifecycle,
.argtable = &modem_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&modem_cmd));
}
static esp_console_repl_t *s_repl = NULL;
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// init the DTE
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.pattern_queue_size = 100;
dte_config.event_task_stack_size = 4096;
dte_config.event_task_priority = 15;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
dce_config.populate_command_list = true;
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
esp_modem_dte_t *dte = esp_modem_dte_new(&dte_config);
s_dce = esp_modem_dce_new(&dce_config);
assert(s_dce != NULL);
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
assert(esp_netif);
ESP_ERROR_CHECK(esp_modem_default_attach(dte, s_dce, esp_netif));
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
register_dce();
register_at_command();
register_modem_lifecycle();
modem_console_register_http();
modem_console_register_ping();
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(s_repl));
}

View File

@ -0,0 +1,420 @@
/* Modem console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "argtable3/argtable3.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "esp_log.h"
#include <map>
#include <utility>
#include <vector>
#include <algorithm>
#include "console_helper.hpp"
// utilities to check network connectivity
extern "C" void modem_console_register_http(void);
extern "C" void modem_console_register_ping(void);
//static esp_modem_dce_t *s_dce = NULL;
static const char *TAG = "modem_console";
//static struct {
// struct arg_str *command;
// struct arg_int *param_int;
// struct arg_str *param_str;
// struct arg_str *param_pdp;
// struct arg_str *param_bool;
// struct arg_str *param;
// struct arg_str *result;
// struct arg_end *end;
//} at_args;
//
//static struct {
// struct arg_str *param;
// struct arg_end *end;
//} modem_args;
//
//
//
//static struct {
// struct arg_str *command;
// struct arg_int *timeout;
// struct arg_str *pattern;
// struct arg_lit *no_cr;
// struct arg_end *end;
//} generic_at_args;
//static char s_common_in_str[100]; // used as common string input param holder
//static char s_common_out_str[100]; // used as output string/command result holder
//static esp_err_t handle_line_pattern(esp_modem_dce_t *dce, const char *line)
//{
// esp_err_t err = ESP_OK;
// ESP_LOGI(TAG, "handle_line_pattern: DCE response: %s\n", line);
// if (strstr(line, dce->handle_line_ctx)) {
// err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
// }
// return err;
//}
//
//static int do_dce(int argc, char **argv)
//{
// // specific DCE generic command params
// static bool bool_result;
// static char pdp_type[10];
// static char pdp_apn[10];
// static esp_modem_dce_pdp_ctx_t pdp = { .type = pdp_type, .apn = pdp_apn };
// static esp_modem_dce_csq_ctx_t csq;
// static esp_modem_dce_cbc_ctx_t cbc;
//
// int nerrors = arg_parse(argc, argv, (void **) &at_args);
// if (nerrors != 0) {
// arg_print_errors(stderr, at_args.end, argv[0]);
// return 1;
// }
// void * command_param = NULL;
// void * command_result = NULL;
//
// // parse input params
// if (at_args.param_int->count > 0) {
// command_param = (void*)(at_args.param_int->ival[0]);
// } else if (at_args.param_bool->count > 0) {
// const char * bool_in_str = at_args.param_bool->sval[0];
// s_common_out_str[0] = '\0';
// command_result = s_common_out_str;
// if (strstr(bool_in_str,"true") || strstr(bool_in_str,"1")) {
// command_param = (void*)true;
// } else {
// command_param = (void*)false;
// }
// } else if (at_args.param_pdp->count > 0) {
// // parse out three comma separated sub-arguments
// sscanf(at_args.param_pdp->sval[0], "%d,%s", &pdp.cid, pdp_type);
// char *str_apn = strchr(pdp_type, ',');
// if (str_apn) {
// strncpy(pdp_apn, str_apn + 1, sizeof(pdp_apn));
// str_apn[0] = '\0';
// }
// command_param = &pdp;
// } else if (at_args.param_str->count > 0) {
// strncpy(s_common_in_str, at_args.param_str->sval[0], sizeof(s_common_in_str));
// command_param = s_common_in_str;
// } else if (at_args.param->count > 0) { // default param is treated as string
// strncpy(s_common_in_str, at_args.param->sval[0], sizeof(s_common_in_str));
// command_param = s_common_in_str;
// }
//
// // parse output params
// if (at_args.result->count > 0) {
// const char *res = at_args.result->sval[0];
// if (strstr(res, "csq")) {
// command_result = &csq;
// }else if (strstr(res, "cbc")) {
// command_result = &cbc;
// } else if (strstr(res, "str")) {
// command_param = (void*)sizeof(s_common_out_str);
// command_result = s_common_out_str;
// } else if (strstr(res, "bool")) {
// command_result = &bool_result;
// } else {
// command_param = (void*)sizeof(s_common_out_str);
// command_result = s_common_out_str;
// }
// }
//
// // by default (if no param/result provided) expect string output
// if (command_param == NULL && command_result == NULL) {
// s_common_out_str[0] = '\0';
// command_param = (void*)sizeof(s_common_out_str);
// command_result = s_common_out_str;
// }
//
// esp_err_t err = esp_modem_command_list_run(s_dce, at_args.command->sval[0], command_param, command_result);
// if (err == ESP_OK) {
// printf("Command %s succeeded\n", at_args.command->sval[0]);
// if (command_result == s_common_out_str && s_common_out_str[0] != '\0') {
// ESP_LOGI(TAG, "Command string output: %s", s_common_out_str);
// } else if (command_result == &csq) {
// ESP_LOGI(TAG, "Command CSQ output: rssi:%d, ber:%d", csq.rssi, csq.ber);
// } else if (command_result == &cbc) {
// ESP_LOGI(TAG, "Command battery output:%d mV", cbc.battery_status);
// } else if (command_result == &bool_result) {
// ESP_LOGI(TAG, "Command bool output: %s", bool_result ? "true" : "false");
// }
// return 0;
// }
// ESP_LOGE(TAG, "Command %s failed with %d", at_args.command->sval[0], err);
// return 1;
//}
//
//
//
//static int do_at_command(int argc, char **argv)
//{
// int timeout = 1000;
// int nerrors = arg_parse(argc, argv, (void **)&generic_at_args);
// if (nerrors != 0) {
// arg_print_errors(stderr, generic_at_args.end, argv[0]);
// return 1;
// }
// esp_modem_dce_handle_line_t handle_line = esp_modem_dce_handle_response_default;
//
// strncpy(s_common_in_str, generic_at_args.command->sval[0], sizeof(s_common_in_str) - 2);
// if (generic_at_args.no_cr->count == 0) {
// size_t cmd_len = strlen(generic_at_args.command->sval[0]);
// s_common_in_str[cmd_len] = '\r';
// s_common_in_str[cmd_len + 1] = '\0';
// }
//
// if (generic_at_args.timeout->count > 0) {
// timeout = generic_at_args.timeout->ival[0];
// }
//
// if (generic_at_args.pattern->count > 0) {
// strncpy(s_common_out_str, generic_at_args.pattern->sval[0], sizeof(s_common_out_str));
// handle_line = handle_line_pattern;
// }
//
// if (esp_modem_dce_generic_command(s_dce, s_common_in_str, timeout, handle_line, s_common_out_str) == ESP_OK) {
// return 0;
// }
//
// return 1;
//}
//static int do_the_work(int argc, char **argv)
//{
// int nerrors = arg_parse(argc, argv, (void **)&modem_args);
// if (nerrors != 0) {
// arg_print_errors(stderr, modem_args.end, argv[0]);
// return 1;
// }
//
// return 0;
//}
//static int do_modem_lifecycle(int argc, char **argv)
//{
// int nerrors = arg_parse(argc, argv, (void **)&modem_args);
// if (nerrors != 0) {
// arg_print_errors(stderr, modem_args.end, argv[0]);
// return 1;
// }
// if (modem_args.param->count > 0) {
// esp_err_t err = ESP_FAIL;
// if (strstr(modem_args.param->sval[0], "PPP")) {
// err = esp_modem_set_mode(s_dce, ESP_MODEM_MODE_DATA);
// } else if (strstr(modem_args.param->sval[0], "CMD")) {
// err = esp_modem_set_mode(s_dce, ESP_MODEM_MODE_COMMAND);
// } else {
// return 1;
// }
// if (err == ESP_OK) {
// ESP_LOGI(TAG, "set_working_mode %s succeeded", at_args.param->sval[0]);
// } else {
// ESP_LOGI(TAG, "set_working_mode %s failed with %d", at_args.param->sval[0], err);
// }
// return 0;
// }
// return 1;
//}
//static void register_dce(void)
//{
// at_args.command = arg_str1(NULL, NULL, "<command>", "Symbolic name of DCE command");
// at_args.param_int = arg_int0("i", "int", "<num>", "Input parameter of integer type");
// at_args.param_str = arg_str0("s", "str", "<str>", "Input parameter of string type");
// at_args.param_pdp = arg_str0("p", "pdp", "<pdp>", "Comma separated string with PDP context");
// at_args.param_bool = arg_str0("b", "bool", "<true/false>", "Input parameter of bool type");
// at_args.param = arg_str0(NULL, NULL, "<param>", "Default input argument treated as string");
// at_args.result = arg_str0("o", "out", "<type>", "Type of output parameter");
// at_args.end = arg_end(1);
// const esp_console_cmd_t at_cmd = {
// .command = "dce",
// .help = "send symbolic command to the modem",
// .hint = NULL,
// .func = &do_dce,
// .argtable = &at_args
// };
// ESP_ERROR_CHECK(esp_console_cmd_register(&at_cmd));
//}
//static void register_at_command(void)
//{
// generic_at_args.command = arg_str1(NULL, NULL, "<command>", "AT command to send to the modem");
// generic_at_args.timeout = arg_int0("t", "timeout", "<timeout>", "command timeout");
// generic_at_args.pattern = arg_str0("p", "pattern", "<pattern>", "command response to wait for");
// generic_at_args.no_cr = arg_litn("n", "no-cr", 0, 1, "not add trailing CR to the command");
// generic_at_args.end = arg_end(1);
//
// const esp_console_cmd_t at_command = {
// .command = "at",
// .help = "send generic AT command to the modem",
// .hint = NULL,
// .func = &do_at_command,
// .argtable = &generic_at_args
// };
// ESP_ERROR_CHECK(esp_console_cmd_register(&at_command));
//}
//enum arg_type {
// STR0,
// STR1,
// INT0,
// INT1,
// ARG_END,
//};
//
//struct CommandArgs {
// CommandArgs(arg_type t, const char * shopts, const char * lopts, const char * data, const char * glos):
// type(t), shortopts(shopts), longopts(lopts), datatype(data), glossary(glos) {}
// arg_type type;
// const char *shortopts;
// const char *longopts;
// const char *datatype;
// const char *glossary;
//};
#define MAX_COMMAND 10
//#define TEMPLATE(index) _TEMPLATE(index)
//#define REPEAT(a) _REPEAT(a)
#define MY_REPEAT(a) \
TEMPLATE(0) \
TEMPLATE(1) \
TEMPLATE(2)
//static void register_modem_lifecycle(DCE<SIM7600> *dce)
//{
// modem_args.param = arg_str1(NULL, NULL, "<mode>", "PPP or CMD");
// modem_args.end = arg_end(1);
// const esp_console_cmd_t modem_cmd = {
// .command = "set_mode",
// .help = "set modem mode",
// .hint = nullptr,
// .func = do_the_work,
// .argtable = &modem_args
// };
// ESP_ERROR_CHECK(esp_console_cmd_register(&modem_cmd));
//}
static esp_console_repl_t *s_repl = NULL;
//template<typename T, typename U> constexpr size_t offset_of(U T::*member)
//{
// return (char*)&((T*)nullptr->*member) - (char*)nullptr;
//}
extern "C" void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// init the DTE
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.pattern_queue_size = 100;
dte_config.event_task_stack_size = 4096;
dte_config.event_task_priority = 15;
// esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
assert(esp_netif);
auto uart_dte = create_uart_dte(&dte_config);
std::string apn = "internet";
auto dce = create_SIM7600_dce(uart_dte, esp_netif, apn);
assert(dce != NULL);
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
// register_dce();
// register_at_command();
// register_modem_lifecycle(dce.get());
modem_console_register_http();
modem_console_register_ping();
const struct SetModeArgs {
SetModeArgs(): mode(STR1, nullptr, nullptr, "<mode>", "PPP or CMD") {}
CommandArgs mode;
} set_mode_args;
ConsoleCommand SetModeParser("set_mode", "sets modem mode", &set_mode_args, sizeof(set_mode_args), [&](ConsoleCommand *c){
if (c->get_count_of(&SetModeArgs::mode)) {
auto mode = c->get_string_of(&SetModeArgs::mode);
if (mode == "CMD") {
ESP_LOGI(TAG, "Switching to command mode...");
dce->exit_data();
} else if (mode == "PPP") {
ESP_LOGI(TAG, "Switching to data mode...");
dce->set_data();
} else {
ESP_LOGE(TAG, "Unsupported mode: %s", mode.c_str());
return 1;
}
}
return 0;
});
const struct SetPinArgs {
SetPinArgs(): pin(STR1, nullptr, nullptr, "<pin>", "PIN") {}
CommandArgs pin;
} set_pin_args;
ConsoleCommand SetPinParser("set_pin", "sets SIM card PIN", &set_pin_args, sizeof(set_pin_args), [&](ConsoleCommand *c){
if (c->get_count_of(&SetPinArgs::pin)) {
auto pin = c->get_string_of(&SetPinArgs::pin);
ESP_LOGI(TAG, "Setting pin=%s...", pin.c_str());
auto err = dce->set_pin(pin);
if (err == command_result::OK) {
ESP_LOGI(TAG, "OK");
} else {
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT":"");
return 1;
}
}
return 0;
});
std::vector<CommandArgs> no_args;
ConsoleCommand ReadPinArgs("read_pin", "checks if SIM is unlocked", no_args, [&](ConsoleCommand *c){
bool pin_ok;
ESP_LOGI(TAG, "Checking pin...");
auto err = dce->read_pin(pin_ok);
if (err == command_result::OK) {
ESP_LOGI(TAG, "OK. Pin status: %s", pin_ok ? "true": "false");
} else {
ESP_LOGE(TAG, "Failed %s", err == command_result::TIMEOUT ? "TIMEOUT":"");
return 1;
}
return 0;
});
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(s_repl));
ESP_LOGE(TAG, "Exit console!!!");
while(1) {
vTaskDelay(pdMS_TO_TICKS(50000));
ESP_LOGI(TAG, "working!");
}
}

View File

@ -0,0 +1,27 @@
//
// Created by david on 3/17/21.
//
#ifndef MODEM_CONSOLE_REPEAT_HELPER_INC_H
#define MODEM_CONSOLE_REPEAT_HELPER_INC_H
#if MAX_REPEAT_NR < 10
#define REPEAT_TEMPLATE_DEF(...) REPEAT_10_TIMES(VA_ARG)
#else
#define REPEAT_TEMPLATE_DEF(...) REPEAT_100_TIMES(VA_ARG)
#endif
#define REPEAT_10_TIMES(a) \
TEMPLATE(0) \
TEMPLATE(1) \
TEMPLATE(2) \
TEMPLATE(3) \
TEMPLATE(4) \
TEMPLATE(5) \
TEMPLATE(6) \
TEMPLATE(7) \
TEMPLATE(8) \
TEMPLATE(9)
#endif //MODEM_CONSOLE_REPEAT_HELPER_INC_H

View File

@ -8,8 +8,8 @@
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
#define THROW(exception) throw(exception)
class esp_err_exception: virtual public std::exception {
public:
explicit esp_err_exception(esp_err_t err): esp_err(err) {}
@ -24,25 +24,28 @@ private:
esp_err_t esp_err;
std::string message;
};
#else
#define THROW(exception) abort()
#endif
static inline void throw_if_false(bool condition, std::string message)
{
if (!condition) {
throw(esp_err_exception(std::move(message)));
THROW(esp_err_exception(std::move(message)));
}
}
static inline void throw_if_esp_fail(esp_err_t err, std::string message)
{
if (err != ESP_OK) {
throw(esp_err_exception(std::move(message), err));
THROW(esp_err_exception(std::move(message), err));
}
}
static inline void throw_if_esp_fail(esp_err_t err)
{
if (err != ESP_OK) {
throw(esp_err_exception(err));
THROW(esp_err_exception(err));
}
}

View File

@ -5,20 +5,28 @@
#ifndef SIMPLE_CXX_CLIENT_ESP_MODEM_API_H
#define SIMPLE_CXX_CLIENT_ESP_MODEM_API_H
#pragma once
#include "generate/esp_modem_command_declare.inc"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_modem_dce_wrap esp_modem_t;
typedef struct esp_modem_dce_wrap esp_modem_dce_t;
typedef struct esp_modem_dte_config esp_modem_dte_config_t;
typedef enum esp_modem_dce_mode
{
ESP_MODEM_MODE_COMMAND,
ESP_MODEM_MODE_DATA,
} esp_modem_dce_mode_t;
esp_modem_t *esp_modem_new(const esp_modem_dte_config *config, esp_netif_t *netif, const char* apn);
esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *config, esp_netif_t *netif, const char* apn);
void esp_modem_destroy(esp_modem_t * dce);
void esp_modem_destroy(esp_modem_dce_t * dce);
esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce, esp_modem_dce_mode_t mode);
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, TEMPLATE_ARG, MUX_ARG, ...) \
esp_err_t esp_modem_ ## name(esp_modem_t *dce, ##__VA_ARGS__);
esp_err_t esp_modem_ ## name(esp_modem_dce_t *dce, ##__VA_ARGS__);
DECLARE_ALL_COMMAND_APIS(declares esp_modem_<API>(esp_modem_t * dce, ...);)

View File

@ -6,11 +6,20 @@
#define SIMPLE_CXX_CLIENT_ESP_MODEM_COMMAND_DECLARE_INC
#ifdef __cplusplus
#define STRING_IN const std::string&
#define STRING_OUT std::string&
#include <string>
#define STRING_IN const std::string& string_in
#define STRING_OUT std::string& string_out
#define BOOL_IN const bool bool_in
#define BOOL_OUT bool& bool_out
#define STRUCT_OUT(struct_name) struct_name& struct_out
#else
#define STRING_IN const char*
#define STRING_OUT char*
struct PdpContext;
#define STRING_IN const char* string_in
#define STRING_OUT char* string_out
#define BOOL_IN const bool bool_in
#define BOOL_OUT bool* bool_out
#define STRUCT_OUT(struct_name) struct struct_name* struct_out
#endif
@ -30,28 +39,28 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, TEMPLATE_ARG, MUX_ARG, S
*
* @param[out] pin_ok Pin
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, TEMPLATE_ARG, MUX_ARG, bool& pin_ok) \
ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, TEMPLATE_ARG, MUX_ARG, BOOL_OUT) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, TEMPLATE_ARG, MUX_ARG, bool on) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_echo, command_result, TEMPLATE_ARG, MUX_ARG, BOOL_IN) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, TEMPLATE_ARG, MUX_ARG) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, TEMPLATE_ARG, MUX_ARG, PdpContext& pdp_context) \
ESP_MODEM_DECLARE_DCE_COMMAND(set_pdp_context, command_result, TEMPLATE_ARG, MUX_ARG, STRUCT_OUT(PdpContext)) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(set_command_mode, command_result, TEMPLATE_ARG, MUX_ARG) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(set_cmux, command_result, TEMPLATE_ARG, MUX_ARG) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, TEMPLATE_ARG, MUX_ARG, std::string& imsi_number) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imsi, command_result, TEMPLATE_ARG, MUX_ARG, STRING_OUT) \
\
ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, TEMPLATE_ARG, MUX_ARG, std::string& imsi_number) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_imei, command_result, TEMPLATE_ARG, MUX_ARG, STRING_OUT) \
\
/**
* @brief Reads the module name
*
* @param[out] module name
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, TEMPLATE_ARG, MUX_ARG, std::string& name) \
ESP_MODEM_DECLARE_DCE_COMMAND(get_module_name, command_result, TEMPLATE_ARG, MUX_ARG, STRING_OUT) \
\
/**
* @brief Sets the modem to data mode

View File

@ -0,0 +1,26 @@
//
// Created by david on 3/16/21.
//
#ifndef MODEM_CONSOLE_EXCEPTION_STUB_HPP
#define MODEM_CONSOLE_EXCEPTION_STUB_HPP
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
#define TRY_CATCH_RET_NULL(block) \
try { block } \
} catch (std::bad_alloc& e) { \
ESP_LOGE(TAG, "Out of memory"); \
return nullptr; \
} catch (esp_err_exception& e) { \
esp_err_t err = e.get_err_t(); \
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err); \
ESP_LOGE(TAG, "%s", e.what()); \
return nullptr; \
}
#else
#define TRY_CATCH_RET_NULL(block) \
block
#endif
#endif //MODEM_CONSOLE_EXCEPTION_STUB_HPP

View File

@ -7,41 +7,24 @@
#include "cxx_include/esp_modem_api.hpp"
#include "esp_modem_api.h"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
static const char *TAG = "dce_factory";
struct PdpContext;
std::shared_ptr<DTE> create_uart_dte(const esp_modem_dte_config *config)
{
try {
TRY_CATCH_RET_NULL(
auto term = create_uart_terminal(config);
return std::make_shared<DTE>(std::move(term));
} catch (std::bad_alloc& e) {
ESP_LOGE(TAG, "Out of memory");
return nullptr;
} catch (esp_err_exception& e) {
esp_err_t err = e.get_err_t();
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err);
ESP_LOGE(TAG, "%s", e.what());
return nullptr;
}
)
}
template<typename SpecificModule>
std::unique_ptr<DCE<SpecificModule>> create_dce(const std::shared_ptr<DTE>& dte, const std::shared_ptr<SpecificModule>& dev, esp_netif_t *netif)
{
try {
TRY_CATCH_RET_NULL(
return std::make_unique<DCE<SpecificModule>>(dte, dev, netif);
} catch (std::bad_alloc& e) {
ESP_LOGE(TAG, "Out of memory");
return nullptr;
} catch (esp_err_exception& e) {
esp_err_t err = e.get_err_t();
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err);
ESP_LOGE(TAG, "%s", e.what());
return nullptr;
}
)
}
std::unique_ptr<DCE<GenericModule>> create_generic_dce_from_module(const std::shared_ptr<DTE>& dte, const std::shared_ptr<GenericModule>& dev, esp_netif_t *netif)
@ -86,7 +69,7 @@ static inline esp_err_t command_response_to_esp_err(command_result res)
return ESP_ERR_INVALID_ARG;
}
extern "C" esp_modem_t *esp_modem_new(const esp_modem_dte_config *config, esp_netif_t *netif, const char* apn)
extern "C" esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *config, esp_netif_t *netif, const char* apn)
{
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
if (dce_wrap == nullptr)
@ -99,7 +82,7 @@ extern "C" esp_modem_t *esp_modem_new(const esp_modem_dte_config *config, esp_ne
return dce_wrap;
}
extern "C" void esp_modem_destroy(esp_modem_t * dce)
extern "C" void esp_modem_destroy(esp_modem_dce_t * dce)
{
assert(dce->modem_type == esp_modem_dce_wrap::MODEM_SIM7600);
auto dce_sim7600 = static_cast<DCE<SIM7600>*>(dce->dce_ptr);
@ -107,7 +90,21 @@ extern "C" void esp_modem_destroy(esp_modem_t * dce)
delete dce;
}
extern "C" esp_err_t esp_modem_read_pin(esp_modem_t * dce, bool &x)
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t * dce, esp_modem_dce_mode_t mode)
{
assert(dce->modem_type == esp_modem_dce_wrap::MODEM_SIM7600);
auto dce_sim7600 = static_cast<DCE<SIM7600>*>(dce->dce_ptr);
if (mode == ESP_MODEM_MODE_DATA) {
dce_sim7600->set_data();
} else if (mode == ESP_MODEM_MODE_COMMAND) {
dce_sim7600->set_data();
} else {
return ESP_ERR_NOT_SUPPORTED;
}
return ESP_OK;
}
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t * dce, bool &x)
{
assert(dce->modem_type == esp_modem_dce_wrap::MODEM_SIM7600);
auto dce_sim7600 = static_cast<DCE<SIM7600>*>(dce->dce_ptr);

View File

@ -7,6 +7,7 @@
#include "esp_event.h"
#include "driver/uart.h"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
@ -164,19 +165,11 @@ private:
std::unique_ptr<Terminal> create_uart_terminal(const esp_modem_dte_config *config)
{
try {
TRY_CATCH_RET_NULL(
auto term = std::make_unique<uart_terminal>(config);
term->start();
return term;
} catch (std::bad_alloc& e) {
ESP_LOGE(TAG, "Out of memory");
return nullptr;
} catch (esp_err_exception& e) {
esp_err_t err = e.get_err_t();
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err);
ESP_LOGE(TAG, "%s", e.what());
return nullptr;
}
)
}
void uart_terminal::task()