esp_modem: Moved to component folder

This commit is contained in:
David Čermák
2021-05-19 23:00:28 +08:00
committed by David Cermak
parent 61f264f97a
commit 90641c89eb
133 changed files with 21 additions and 21 deletions

View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS # Add esp_modem component and linux port components
../..
../../port/linux)
set(COMPONENTS main)
project(host_modem_test)
idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_COMPILER_CXX_EXCEPTIONS")
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
target_link_options(${esp_modem} INTERFACE -fsanitize=address)

View File

@ -0,0 +1,5 @@
# Host test for esp_modem
This test uses linux port and some idf mocks in order to compile and execute it under linux.
This test uses `catch` as a test framework and implements a test terminal class `LoopbackTerm`

View File

@ -0,0 +1,10 @@
idf_component_register(SRCS "test_modem.cpp" "LoopbackTerm.cpp"
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
REQUIRES esp_modem)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(${COMPONENT_LIB} PRIVATE Threads::Threads)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")

View File

@ -0,0 +1,82 @@
#include <memory>
#include <future>
#include <cstring>
#include "LoopbackTerm.h"
void LoopbackTerm::start()
{
status = status_t::STARTED;
}
void LoopbackTerm::stop()
{
status = status_t::STOPPED;
}
int LoopbackTerm::write(uint8_t *data, size_t len)
{
if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder
std::string command((char*)data, len);
std::string response;
if (command == "+++") {
response = "NO CARRIER\r\n";
} else if (command == "ATE1\r" || command == "ATE0\r") {
response = "OK\r\n";
} else if (command == "ATO\r") {
response = "ERROR\r\n";
} else if (command.find("ATD") != std::string::npos) {
response = "CONNECT\r\n";
} else if (command.find("AT+CSQ\r") != std::string::npos) {
response = "+CSQ: 123,456\n\r\nOK\r\n";
} else if (command.find("AT+CBC\r") != std::string::npos) {
response = is_bg96 ? "+CBC: 1,2,123456V\r\r\n\r\nOK\r\n\n\r\n":
"+CBC: 123.456V\r\r\n\r\nOK\r\n\n\r\n";
} else if (command.find("AT+CPIN=1234\r") != std::string::npos) {
response = "OK\r\n";
pin_ok = true;
} else if (command.find("AT+CPIN?\r") != std::string::npos) {
response = pin_ok?"+CPIN: READY\r\nOK\r\n":"+CPIN: SIM PIN\r\nOK\r\n";
} else if (command.find("AT") != std::string::npos) {
response = "OK\r\n";
}
if (!response.empty()) {
data_len = response.length();
loopback_data.resize(data_len);
memcpy(&loopback_data[0], &response[0], data_len);
auto ret = std::async(on_data, nullptr, data_len);
return len;
}
}
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
// turn the request into a reply -> implements CMUX loopback
if (data[2] == 0x3f) // SABM command
data[2] = 0x73;
else if (data[2] == 0xef) { // Generic request
data[2] = 0xff; // generic reply
}
}
loopback_data.resize(data_len + len);
memcpy(&loopback_data[data_len], data, len);
data_len += len;
auto ret = std::async(on_data, nullptr, data_len);
return len;
}
int LoopbackTerm::read(uint8_t *data, size_t len)
{
size_t read_len = std::min(data_len, len);
if (read_len) {
if (loopback_data.capacity() < len)
loopback_data.reserve(len);
memcpy(data, &loopback_data[0], read_len);
loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len);
data_len -= read_len;
}
return read_len;
}
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96) {}
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false) {}
LoopbackTerm::~LoopbackTerm() = default;

View File

@ -0,0 +1,36 @@
#ifndef _LOOPBACKTERM_H_
#define _LOOPBACKTERM_H_
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_terminal.hpp"
using namespace esp_modem;
class LoopbackTerm : public Terminal {
public:
explicit LoopbackTerm(bool is_bg96);
explicit LoopbackTerm();
~LoopbackTerm() override;
void start() override;
void stop() override;
int write(uint8_t *data, size_t len) override;
int read(uint8_t *data, size_t len) override;
private:
enum class status_t {
STARTED,
STOPPED
};
status_t status;
SignalGroup signal;
std::vector<uint8_t> loopback_data;
size_t data_len;
bool pin_ok;
bool is_bg96;
};
#endif //_LOOPBACKTERM_H_

View File

@ -0,0 +1,136 @@
#define CATCH_CONFIG_MAIN // This tells the catch header to generate a main
#include <memory>
#include <future>
#include "catch.hpp"
#include "cxx_include/esp_modem_api.hpp"
#include "LoopbackTerm.h"
using namespace esp_modem;
TEST_CASE("DCE AT parser", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>(true);
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_BG96_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
CHECK(dce->set_command_mode() == command_result::OK);
int milli_volt, bcl, bcs;
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
CHECK(milli_volt == 123456);
CHECK(bcl == 1);
CHECK(bcs == 2);
int rssi, ber;
CHECK(dce->get_signal_quality(rssi, ber) == command_result::OK);
CHECK(rssi == 123);
CHECK(ber == 456);
bool pin_ok;
CHECK(dce->read_pin(pin_ok) == command_result::OK);
CHECK(pin_ok == false);
CHECK(dce->set_pin("1234") == command_result::OK);
CHECK(dce->read_pin(pin_ok) == command_result::OK);
CHECK(pin_ok == true);
}
TEST_CASE("DTE send/receive command", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_unique<DTE>(std::move(term));
const auto test_command = "Test\n";
CHECK(term == nullptr);
CHECK(dte->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
auto ret = dte->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char*)data, len);
CHECK(response == test_command);
return command_result::OK;
}, 1000);
CHECK(ret == command_result::OK);
}
TEST_CASE("DCE commands", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
const auto test_command = "Test\n";
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char*)data, len);
CHECK(response == test_command);
return command_result::OK;
}, 1000);
CHECK(ret == command_result::OK);
}
TEST_CASE("DCE AT commands", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
int milli_volt, bcl, bcs;
CHECK(dce->set_echo(false) == command_result::OK);
CHECK(dce->set_echo(true) == command_result::OK);
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
CHECK(milli_volt == 123456);
CHECK(dce->resume_data_mode() == command_result::FAIL);
}
TEST_CASE("DCE modes", "[esp_modem]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == false);
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("DCE CMUX test", "[esp_modem]") {
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MODE) == true);
const auto test_command = "Test\n";
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
std::string response((char *) data, len);
std::cout << "Response:" << response << std::endl;
CHECK(response == test_command);
return command_result::OK;
}, 1000);
CHECK(ret == command_result::OK);
}

View File

@ -0,0 +1,5 @@
CONFIG_IDF_TARGET="linux"
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_RTTI=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_COMPILER_STACK_CHECK_NONE=y

View File

@ -0,0 +1,74 @@
#
# Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) Project Configuration
#
CONFIG_IDF_CMAKE=y
CONFIG_IDF_TARGET="linux"
CONFIG_IDF_FIRMWARE_CHIP_ID=0xFFFF
#
# SDK tool configuration
#
CONFIG_SDK_TOOLPREFIX=""
# CONFIG_SDK_TOOLCHAIN_SUPPORTS_TIME_WIDE_64_BITS is not set
# end of SDK tool configuration
#
# Build type
#
CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y
# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
CONFIG_APP_BUILD_GENERATE_BINARIES=y
CONFIG_APP_BUILD_BOOTLOADER=y
CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y
# end of Build type
#
# Compiler options
#
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set
CONFIG_COMPILER_HIDE_PATHS_MACROS=y
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_COMPILER_CXX_RTTI=y
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set
# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set
# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set
# CONFIG_COMPILER_DUMP_RTL_FILES is not set
# end of Compiler options
#
# Component config
#
#
# Compatibility options
#
# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set
# end of Compatibility options
# Deprecated options for backward compatibility
CONFIG_TOOLPREFIX=""
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_CXX_EXCEPTIONS=y
CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set
# CONFIG_STACK_CHECK_ALL is not set
# CONFIG_WARN_WRITE_STRINGS is not set
# CONFIG_DISABLE_GCC8_WARNINGS is not set
# End of deprecated options

View File

@ -0,0 +1,8 @@
# The following five 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)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pppd_test)

View File

@ -0,0 +1,6 @@
idf_component_register(SRCS "pppd_test.cpp"
"NetworkDCE.cpp"
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
REQUIRES esp_modem)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)

View File

@ -0,0 +1,26 @@
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_WIFI_CHANNEL
int "WiFi Channel"
range 1 13
default 1
help
WiFi channel (network channel) for the example to use.
config ESP_MAX_STA_CONN
int "Maximal STA connections"
default 4
help
Max number of the STA connects to AP.
endmenu

View File

@ -0,0 +1,102 @@
/* softAP to PPPoS Example (modem_board)
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 "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_factory.hpp"
#include <memory>
#include <utility>
using namespace esp_modem;
using namespace esp_modem::dce_factory;
class NetModule;
typedef DCE_T<NetModule> NetDCE;
/**
* @brief Custom factory which can build and create a DCE using a custom module
*/
class NetDCE_Factory: public Factory {
public:
template <typename T, typename ...Args>
static DCE_T<T>* create(const config *cfg, Args&&... args)
{
return build_generic_DCE<T>(cfg, std::forward<Args>(args)...);
}
};
/**
* @brief This is a null-module, doesn't define any AT commands, just passes everything to pppd
*/
class NetModule: public ModuleIf {
public:
explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg):
dte(std::move(dte)) {}
bool setup_data_mode() override
{
return true;
}
bool set_mode(modem_mode mode) override
{
return true;
}
static esp_err_t init(esp_netif_t *netif)
{
// configure
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
esp_modem_dte_config_t dte_config2 = {
.dte_buffer_size = 512,
.vfs_config = {.port_num = UART_NUM_1,
.dev_name = "/dev/uart/1",
.rx_buffer_size = 1024,
.tx_buffer_size = 1024,
.baud_rate = 115200,
.tx_io_num = 25,
.rx_io_num = 26,
.task_stack_size = 4096,
.task_prio = 5}
};
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("");
// create DTE and minimal network DCE
auto uart_dte = create_vfs_dte(&dte_config2);
// auto uart_dte = create_uart_dte(&dte_config);
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif);
return dce == nullptr ? ESP_FAIL : ESP_OK;
}
static void deinit() { delete dce; }
static void start() { dce->set_data(); }
static void stop() { dce->exit_data(); }
private:
static NetDCE *dce;
std::shared_ptr<DTE> dte;
};
NetDCE *NetModule::dce = nullptr;
esp_err_t modem_init_network(esp_netif_t *netif)
{
return NetModule::init(netif);
}
void modem_start_network()
{
NetModule::start();
}
void modem_stop_network()
{
NetModule::stop();
}

View File

@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@ -0,0 +1,134 @@
/* pppd test
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 "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
static const char *TAG = "pppd_test";
static EventGroupHandle_t event_group = NULL;
static void on_modem_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == IP_EVENT) {
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "GOT ip event!!!");
xEventGroupSetBits(event_group, 1);
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
}
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = static_cast<esp_netif_t *>(event_data);
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
xEventGroupSetBits(event_group, 2);
}
}
esp_err_t modem_init_network(esp_netif_t *netif);
void modem_start_network();
void modem_stop_network();
extern "C" void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
event_group = xEventGroupCreate();
// init the DTE
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *ppp_netif = esp_netif_new(&ppp_netif_config);
assert(ppp_netif);
esp_netif_ppp_config_t ppp_config = { true, true };
esp_netif_ppp_set_params(ppp_netif, &ppp_config);
ESP_ERROR_CHECK(modem_init_network(ppp_netif));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_modem_event, nullptr));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, nullptr));
modem_start_network();
Catch::Session session;
int numFailed = session.run();
if (numFailed > 0) {
ESP_LOGE(TAG, "Test FAILED!");
} else {
ESP_LOGI(TAG, "Test passed!");
}
}
TEST_CASE("Connect test", "[esp_modem]")
{
EventBits_t b = xEventGroupWaitBits(event_group, 1, pdTRUE, pdFALSE, pdMS_TO_TICKS(15000));
CHECK(b == 1);
}
TEST_CASE("Disconnection test", "[esp_modem]")
{
modem_stop_network();
EventBits_t b = xEventGroupWaitBits(event_group, 2, pdTRUE, pdFALSE, pdMS_TO_TICKS(15000));
CHECK(b == 2);
}
extern "C" {
static void handle(int nr)
{
ESP_LOGE(TAG, "Signal handler %d", nr);
}
_sig_func_ptr signal (int nr, _sig_func_ptr)
{
return handle;
}
}

View File

@ -0,0 +1,4 @@
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_CXX_EXCEPTIONS=y
CONFIG_PPP_SUPPORT=y