mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-22 23:07:28 +02:00
feat(modem): Add mqtt example in AT-only mode
Similar to pppos-client, but without PPP mode. This uses standard mqtt client and a loopback service that forwards the TCP traffic from localhost to the modem and vice-versa. (next step is to create a dedicated tcp-transport layer for modem) Console example uses DCE commandable to demontrate how to handle URC
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(modem-console)
|
||||
|
@ -1,5 +1,6 @@
|
||||
idf_component_register(SRCS "modem_console_main.cpp"
|
||||
"console_helper.cpp"
|
||||
"my_module_dce.cpp"
|
||||
"httpget_handle.c"
|
||||
"ping_handle.c"
|
||||
REQUIRES console esp_http_client nvs_flash
|
||||
|
@ -17,7 +17,7 @@ menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
default EXAMPLE_MODEM_DEVICE_SHINY
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
config EXAMPLE_MODEM_DEVICE_SHINY
|
||||
|
@ -89,6 +89,14 @@ void wakeup_modem(void)
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
|
||||
command_result handle_urc(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
|
||||
return command_result::TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
static RTC_RODATA_ATTR char apn_rtc[20] = DEFAULT_APN;
|
||||
@ -122,19 +130,19 @@ extern "C" void app_main(void)
|
||||
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
|
||||
auto uart_dte = create_uart_dte(&dte_config);
|
||||
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_SHINY == 1
|
||||
#if defined(CONFIG_EXAMPLE_MODEM_DEVICE_SHINY)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SHINY module...");
|
||||
auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
|
||||
auto dce = create_BG96_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM800)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module...");
|
||||
auto dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7000 module...");
|
||||
auto dce = create_SIM7000_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7070 module...");
|
||||
auto dce = create_SIM7070_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
@ -303,8 +311,9 @@ extern "C" void app_main(void)
|
||||
|
||||
const ConsoleCommand GetOperatorName("get_operator_name", "reads the operator name", no_args, [&](ConsoleCommand * c) {
|
||||
std::string operator_name;
|
||||
int act;
|
||||
ESP_LOGI(TAG, "Reading operator name...");
|
||||
CHECK_ERR(dce->get_operator_name(operator_name), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
|
||||
CHECK_ERR(dce->get_operator_name(operator_name, act), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
|
||||
});
|
||||
|
||||
const struct GenericCommandArgs {
|
||||
@ -356,6 +365,20 @@ extern "C" void app_main(void)
|
||||
ESP_LOGI(TAG, "Resetting the module...");
|
||||
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
||||
});
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
|
||||
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
static int cnt = 0;
|
||||
if (++cnt % 2) {
|
||||
ESP_LOGI(TAG, "Adding URC handler");
|
||||
dce->set_on_read(handle_urc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "URC removed");
|
||||
dce->set_on_read(nullptr);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
#endif
|
||||
|
||||
const struct SetApn {
|
||||
SetApn(): apn(STR1, nullptr, nullptr, "<apn>", "APN (Access Point Name)") {}
|
||||
CommandArgs apn;
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* Modem console example: Custom DCE
|
||||
|
||||
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 <cstring>
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "generate/esp_modem_command_declare.inc"
|
||||
#include "my_module_dce.hpp"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
//
|
||||
// Define preprocessor's forwarding to dce_commands definitions
|
||||
//
|
||||
|
||||
// Helper macros to handle multiple arguments of declared API
|
||||
#define ARGS0
|
||||
#define ARGS1 , p1
|
||||
#define ARGS2 , p1 , p2
|
||||
#define ARGS3 , p1 , p2 , p3
|
||||
#define ARGS4 , p1 , p2 , p3, p4
|
||||
#define ARGS5 , p1 , p2 , p3, p4, p5
|
||||
#define ARGS6 , p1 , p2 , p3, p4, p5, p6
|
||||
|
||||
#define _ARGS(x) ARGS ## x
|
||||
#define ARGS(x) _ARGS(x)
|
||||
|
||||
#define CMD_OK (1)
|
||||
#define CMD_FAIL (2)
|
||||
|
||||
//
|
||||
// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
|
||||
//
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
|
||||
return_type Shiny::DCE::name(__VA_ARGS__) { return esp_modem::dce_commands::name(this ARGS(arg_nr) ); }
|
||||
|
||||
DECLARE_ALL_COMMAND_APIS(return_type name(...) )
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return Shiny::Factory::create(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Definition of the command API, which makes the Shiny::DCE "command-able class"
|
||||
* @param cmd Command to send
|
||||
* @param got_line Recv line callback
|
||||
* @param time_ms timeout in ms
|
||||
* @param separator line break separator
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
command_result Shiny::DCE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
if (!handling_urc) {
|
||||
return dte->command(cmd, got_line, time_ms, separator);
|
||||
}
|
||||
handle_cmd = got_line;
|
||||
signal.clear(CMD_OK | CMD_FAIL);
|
||||
esp_modem::DTE_Command command{cmd};
|
||||
dte->write(command);
|
||||
signal.wait_any(CMD_OK | CMD_FAIL, time_ms);
|
||||
handle_cmd = nullptr;
|
||||
if (signal.is_any(CMD_OK)) {
|
||||
return esp_modem::command_result::OK;
|
||||
}
|
||||
if (signal.is_any(CMD_FAIL)) {
|
||||
return esp_modem::command_result::FAIL;
|
||||
}
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle received data
|
||||
*
|
||||
* @param data Data received from the device
|
||||
* @param len Length of the data
|
||||
* @return standard command return code (OK|FAIL|TIMEOUT)
|
||||
*/
|
||||
command_result Shiny::DCE::handle_data(uint8_t *data, size_t len)
|
||||
{
|
||||
if (std::memchr(data, '\n', len)) {
|
||||
if (handle_urc) {
|
||||
handle_urc(data, len);
|
||||
}
|
||||
if (handle_cmd) {
|
||||
auto ret = handle_cmd(data, len);
|
||||
if (ret == esp_modem::command_result::TIMEOUT) {
|
||||
return command_result::TIMEOUT;
|
||||
}
|
||||
if (ret == esp_modem::command_result::OK) {
|
||||
signal.set(CMD_OK);
|
||||
}
|
||||
if (ret == esp_modem::command_result::FAIL) {
|
||||
signal.set(CMD_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return command_result::TIMEOUT;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -11,6 +11,8 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
|
||||
@ -28,13 +30,84 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace Shiny {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
class DCE : public esp_modem::DCE_T<MyShinyModem>, public CommandableIf {
|
||||
public:
|
||||
using DCE_T<MyShinyModem>::DCE_T;
|
||||
|
||||
command_result
|
||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
|
||||
{
|
||||
return command(cmd, got_line, time_ms, '\n');
|
||||
}
|
||||
|
||||
command_result
|
||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator) override;
|
||||
|
||||
int write(uint8_t *data, size_t len) override
|
||||
{
|
||||
return dte->write(data, len);
|
||||
}
|
||||
|
||||
void on_read(got_line_cb on_data) override
|
||||
{
|
||||
return dte->on_read(on_data);
|
||||
}
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(__VA_ARGS__);
|
||||
|
||||
DECLARE_ALL_COMMAND_APIS(forwards name(...))
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
void set_on_read(esp_modem::got_line_cb on_read_cb)
|
||||
{
|
||||
if (on_read_cb == nullptr) {
|
||||
handling_urc = false;
|
||||
handle_urc = nullptr;
|
||||
dte->on_read(nullptr);
|
||||
return;
|
||||
}
|
||||
handle_urc = std::move(on_read_cb);
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->handle_data(data, len);
|
||||
return command_result::TIMEOUT;
|
||||
});
|
||||
handling_urc = true;
|
||||
}
|
||||
|
||||
private:
|
||||
got_line_cb handle_urc{nullptr};
|
||||
got_line_cb handle_cmd{nullptr};
|
||||
SignalGroup signal;
|
||||
bool handling_urc {false};
|
||||
|
||||
command_result handle_data(uint8_t *data, size_t len);
|
||||
|
||||
};
|
||||
|
||||
class Factory: public ::esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
|
||||
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<MyShinyModem, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace Shiny
|
||||
|
||||
/**
|
||||
* @brief Helper create method which employs the DCE factory for creating DCE objects templated by a custom module
|
||||
* @return unique pointer of the resultant DCE
|
||||
*/
|
||||
std::unique_ptr<esp_modem::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return esp_modem::dce_factory::Factory::build_unique<MyShinyModem>(config, std::move(dte), netif);
|
||||
}
|
||||
esp_netif_t *netif);
|
||||
|
Reference in New Issue
Block a user