Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(esp_modem): Add mqtt example in AT-only mode #156

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/modem__build-host-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ jobs:
strategy:
matrix:
idf_ver: ["latest", "release-v4.2", "release-v4.3", "release-v4.4", "release-v5.0"]
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
exclude:
- idf_ver: "release-v4.2"
example: simple_cmux_client
- idf_ver: "release-v4.2"
example: modem_tcp_client
- idf_ver: "release-v4.3"
example: modem_tcp_client
include:
- idf_ver: "release-v4.2"
skip_config: usb
Expand Down
3 changes: 2 additions & 1 deletion components/esp_modem/examples/modem_console/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
idf_component_register(SRCS "modem_console_main.cpp"
"console_helper.cpp"
"my_module_dce.cpp"
gabsuren marked this conversation as resolved.
Show resolved Hide resolved
"httpget_handle.c"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
114 changes: 114 additions & 0 deletions components/esp_modem/examples/modem_console/main/my_module_dce.cpp
Original file line number Diff line number Diff line change
@@ -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
euripedesrocha marked this conversation as resolved.
Show resolved Hide resolved
//

// 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)
david-cermak marked this conversation as resolved.
Show resolved Hide resolved
{
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;
}
85 changes: 79 additions & 6 deletions components/esp_modem/examples/modem_console/main/my_module_dce.hpp
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -11,6 +11,8 @@
#pragma once


#include <utility>

#include "cxx_include/esp_modem_dce_factory.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"

Expand All @@ -28,13 +30,84 @@ class MyShinyModem: public esp_modem::GenericModule {
}
};

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);
9 changes: 9 additions & 0 deletions components/esp_modem/examples/modem_tcp_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 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.8)
set(CMAKE_CXX_STANDARD 17)

set(EXTRA_COMPONENT_DIRS "../..")

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modem_tcp_client)
10 changes: 10 additions & 0 deletions components/esp_modem/examples/modem_tcp_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Modem TCP client

(See the README.md file in the upper level 'examples' directory for more information about examples.)

## Overview
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)

### Supported IDF versions

This example is supported from IDF `v4.4`.
11 changes: 11 additions & 0 deletions components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
set(device_srcs sock_commands_bg96.cpp)
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
set(device_srcs sock_commands_sim7600.cpp)
endif()

idf_component_register(SRCS "modem_client.cpp"
"sock_dce.cpp"
"${device_srcs}"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
david-cermak marked this conversation as resolved.
Show resolved Hide resolved
Loading