mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-03 00:21:44 +01:00
Merge remote-tracking branch 'origin/master' into feature/github-7022
This commit is contained in:
@@ -7,4 +7,4 @@ cmake_minimum_required(VERSION 3.5)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_chat_client)
|
||||
project(asio_chat)
|
||||
@@ -2,7 +2,7 @@
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
PROJECT_NAME := asio_chat_server
|
||||
PROJECT_NAME := asio_chat
|
||||
|
||||
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
|
||||
|
||||
62
examples/protocols/asio/asio_chat/README.md
Normal file
62
examples/protocols/asio/asio_chat/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
# Asio chat client and server examples
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The application aims to demonstrate a simple use of Asio library in different modes.
|
||||
In project settings it could be configured to run either a Asio chat server, a Asio chat client, or both.
|
||||
|
||||
## How to use example
|
||||
|
||||
The example is configured by default as an Asio chat client.
|
||||
|
||||
Note that the example uses string representation of IP addresses and ports.
|
||||
|
||||
You can find the upstream asio chat implementation [here] https://github.com/chriskohlhoff/asio/tree/master/asio/src/examples/cpp11/chat
|
||||
|
||||
### Asio Client
|
||||
|
||||
In the client mode, the example connects to the configured address, sends the message, which was inserted as an input in the terminal, and receives a response.
|
||||
|
||||
### Asio Server
|
||||
|
||||
In the server mode, Asio chat server with a specified port number is created and being polled till a connection request from the client arrives.
|
||||
Chat server echoes a message (received from any client) to all connected clients.
|
||||
|
||||
## Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
Set following parameters under Example Configuration Options:
|
||||
|
||||
* Set `EXAMPLE_CHAT_SERVER` to use the example as an ASIO chat server
|
||||
* Configure `EXAMPLE_CHAT_SERVER_BIND_PORT` to the port number.
|
||||
|
||||
* Set `EXAMPLE_CHAT_CLIENT` to use the example as an ASIO chat client
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS` to a string representation of the address to connect the client to.
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_PORT` to the port number.
|
||||
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more d etails.
|
||||
|
||||
## Running the example in server mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- Connect to the server using multiple clients, for example using any option below.
|
||||
- build and run asio chat client on your host machine
|
||||
- run chat_client asio example on ESP platform
|
||||
- since chat messages consists of ASCII size and message, it is possible to
|
||||
netcat `nc IP PORT` and type for example ` 4ABC<CR>` to transmit 'ABC\n'
|
||||
|
||||
## Running the example in client mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Start chat server either on host machine or as another ESP device running chat_server example.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet.
|
||||
- Receive and send messages to/from other clients on stdin/stdout via serial terminal.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
29
examples/protocols/asio/asio_chat/example_test.py
Normal file
29
examples/protocols/asio/asio_chat/example_test.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# 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.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_asio_chat(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
msg = 'asio-chat: received hi'
|
||||
dut = env.get_dut('asio_chat', 'examples/protocols/asio/asio_chat')
|
||||
# start the test and expect the client to receive back it's original data
|
||||
dut.start_app()
|
||||
dut.expect(re.compile(r'{}'.format('Waiting for input')), timeout=30)
|
||||
dut.write(msg)
|
||||
dut.write('exit')
|
||||
dut.expect(re.compile(r'{}'.format(msg)), timeout=30)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_chat()
|
||||
2
examples/protocols/asio/asio_chat/main/CMakeLists.txt
Normal file
2
examples/protocols/asio/asio_chat/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "asio_chat.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
39
examples/protocols/asio/asio_chat/main/Kconfig.projbuild
Normal file
39
examples/protocols/asio/asio_chat/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,39 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_CHAT_SERVER
|
||||
bool "Asio example chat server"
|
||||
default n
|
||||
help
|
||||
This example will setup a chat server, binds it to the specified address
|
||||
and starts listening.
|
||||
|
||||
if EXAMPLE_CHAT_SERVER
|
||||
config EXAMPLE_CHAT_SERVER_BIND_PORT
|
||||
string "Asio example server bind port"
|
||||
default "3344"
|
||||
help
|
||||
Server listener's socket would be bound to this port.
|
||||
endif
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT
|
||||
bool "Asio example chat client"
|
||||
default y
|
||||
help
|
||||
This example will setup an asio chat client.
|
||||
and sends the data.
|
||||
|
||||
if EXAMPLE_CHAT_CLIENT
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS
|
||||
string "Client connection address"
|
||||
default "192.168.0.1"
|
||||
help
|
||||
Client's socket would connect to this address/host.
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_PORT
|
||||
string "Client connection port"
|
||||
default "3344"
|
||||
help
|
||||
Client's connection port.
|
||||
endif
|
||||
|
||||
endmenu
|
||||
119
examples/protocols/asio/asio_chat/main/asio_chat.cpp
Normal file
119
examples/protocols/asio/asio_chat/main/asio_chat.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* ASIO chat server client 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 "protocol_examples_common.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "server.hpp"
|
||||
#include "client.hpp"
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
static const char *TAG = "asio-chat";
|
||||
|
||||
// This variable is necessary for `python test` execution, it provides synchronisation between server/client(as server should be started before client)
|
||||
std::mutex server_ready;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
static void get_string(char *line, size_t size)
|
||||
{
|
||||
int count = 0;
|
||||
while (count < size) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
line[count] = '\0';
|
||||
break;
|
||||
} else if (c > 0 && c < 127) {
|
||||
line[count] = c;
|
||||
++count;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void start_client(void)
|
||||
{
|
||||
const std::string port(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_PORT);
|
||||
const std::string name(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS);
|
||||
asio::io_context io_context;
|
||||
char line[128];
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(name, port);
|
||||
chat_client c(io_context, endpoints);
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
#endif
|
||||
std::thread t([&io_context]() { try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during client thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});
|
||||
do {
|
||||
ESP_LOGI(TAG, "CLIENT: Waiting for input");
|
||||
get_string(line, sizeof(line));
|
||||
|
||||
chat_message msg;
|
||||
msg.body_length(std::strlen(line));
|
||||
std::memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
sleep(1);
|
||||
} while (strcmp(line, "exit") != 0);
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
try {
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
asio::io_context io_context;
|
||||
chat_server server(io_context, tcp::endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_CHAT_SERVER_BIND_PORT)));
|
||||
std::thread t = std::thread([&io_context]() { // Chat server starting here
|
||||
try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during server thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
start_client();
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
t.join();
|
||||
#endif
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during run %s", e.what());
|
||||
} catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
||||
126
examples/protocols/asio/asio_chat/main/client.hpp
Normal file
126
examples/protocols/asio/asio_chat/main/client.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// client.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_CLIENT_HPP
|
||||
#define CHAT_CLIENT_HPP
|
||||
|
||||
#include <deque>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
: io_context_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
do_connect(endpoints);
|
||||
}
|
||||
|
||||
void write(const chat_message& msg)
|
||||
{
|
||||
asio::post(io_context_,
|
||||
[this, msg]()
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
asio::post(io_context_, [this]() { socket_.close(); });
|
||||
}
|
||||
|
||||
private:
|
||||
void do_connect(const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[this](std::error_code ec, asio::ip::tcp::endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_header()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
#endif // CHAT_CLIENT_HPP
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// chat_server.cpp
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
@@ -8,28 +8,25 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#ifndef CHAT_SERVER_HPP
|
||||
#define CHAT_SERVER_HPP
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
extern std::mutex server_ready;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
@@ -74,12 +71,13 @@ private:
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public std::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(tcp::socket socket, chat_room& room)
|
||||
chat_session(asio::ip::tcp::socket socket, chat_room& room)
|
||||
: socket_(std::move(socket)),
|
||||
room_(room)
|
||||
{
|
||||
@@ -117,56 +115,57 @@ private:
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
void do_read_body()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
void do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
@@ -174,7 +173,7 @@ class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::io_context& io_context,
|
||||
const tcp::endpoint& endpoint)
|
||||
const asio::ip::tcp::endpoint& endpoint)
|
||||
: acceptor_(io_context, endpoint)
|
||||
{
|
||||
do_accept();
|
||||
@@ -183,47 +182,21 @@ public:
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<chat_session>(std::move(socket), room_)->start();
|
||||
}
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<chat_session>(std::move(socket), room_)->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* This helper function configures blocking UART I/O */
|
||||
ESP_ERROR_CHECK(example_configure_stdin_stdout());
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
std::list<chat_server> servers;
|
||||
|
||||
{
|
||||
tcp::endpoint endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_PORT));
|
||||
servers.emplace_back(io_context, endpoint);
|
||||
}
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
#endif // CHAT_SERVER_HPP
|
||||
6
examples/protocols/asio/asio_chat/sdkconfig.ci
Normal file
6
examples/protocols/asio/asio_chat/sdkconfig.ci
Normal file
@@ -0,0 +1,6 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT=y
|
||||
CONFIG_EXAMPLE_CHAT_SERVER=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS="localhost"
|
||||
2
examples/protocols/asio/asio_chat/sdkconfig.defaults
Normal file
2
examples/protocols/asio/asio_chat/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
@@ -1,20 +0,0 @@
|
||||
# Asio chat client example
|
||||
|
||||
Simple Asio chat client using WiFi STA or Ethernet.
|
||||
|
||||
## Example workflow
|
||||
|
||||
- Wi-Fi or Ethernet connection is established, and IP address is obtained.
|
||||
- Asio chat client connects to the corresponding server whose port number and IP are defined through the project configuration menu.
|
||||
- Chat client receives all messages from other chat clients, also it sends message received from stdin using `idf.py -p PORT monitor`.
|
||||
|
||||
## Running the example
|
||||
|
||||
- Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
- Set server IP address and port number in menuconfig, "Example configuration".
|
||||
- Start chat server either on host machine or as another ESP device running chat_server example.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet.
|
||||
- Receive and send messages to/from other clients on stdin/stdout via serial terminal.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
@@ -1,94 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
global g_client_response
|
||||
global g_msg_to_client
|
||||
|
||||
g_client_response = b''
|
||||
g_msg_to_client = b' 3XYZ'
|
||||
|
||||
|
||||
def get_my_ip():
|
||||
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s1.connect(('8.8.8.8', 80))
|
||||
my_ip = s1.getsockname()[0]
|
||||
s1.close()
|
||||
return my_ip
|
||||
|
||||
|
||||
def chat_server_sketch(my_ip):
|
||||
global g_client_response
|
||||
print('Starting the server on {}'.format(my_ip))
|
||||
port = 2222
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(600)
|
||||
s.bind((my_ip, port))
|
||||
s.listen(1)
|
||||
q,addr = s.accept()
|
||||
print('connection accepted')
|
||||
q.settimeout(30)
|
||||
q.send(g_msg_to_client)
|
||||
data = q.recv(1024)
|
||||
# check if received initial empty message
|
||||
if (len(data) > 4):
|
||||
g_client_response = data
|
||||
else:
|
||||
g_client_response = q.recv(1024)
|
||||
print('received from client {}'.format(g_client_response))
|
||||
s.close()
|
||||
print('server closed')
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_chat_client(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. Test to start simple tcp server
|
||||
2. `dut1` joins AP
|
||||
3. Test injects server IP to `dut1`via stdin
|
||||
4. Test evaluates `dut1` receives a message server placed
|
||||
5. Test injects a message to `dut1` to be sent as chat_client message
|
||||
6. Test evaluates received test message in host server
|
||||
"""
|
||||
global g_client_response
|
||||
global g_msg_to_client
|
||||
test_msg = 'ABC'
|
||||
dut1 = env.get_dut('chat_client', 'examples/protocols/asio/chat_client', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_chat_client.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_chat_client_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start a tcp server on the host
|
||||
host_ip = get_my_ip()
|
||||
thread1 = Thread(target=chat_server_sketch, args=(host_ip,))
|
||||
thread1.start()
|
||||
# 2. start the dut test and wait till client gets IP address
|
||||
dut1.start_app()
|
||||
dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. send host's IP to the client i.e. the `dut1`
|
||||
dut1.write(host_ip)
|
||||
# 4. client `dut1` should receive a message
|
||||
dut1.expect(g_msg_to_client[4:].decode()) # Strip out the front 4 bytes of message len (see chat_message protocol)
|
||||
# 5. write test message from `dut1` chat_client to the server
|
||||
dut1.write(test_msg)
|
||||
while len(g_client_response) == 0:
|
||||
time.sleep(1)
|
||||
g_client_response = g_client_response.decode()
|
||||
print(g_client_response)
|
||||
# 6. evaluate host_server received this message
|
||||
if (g_client_response[4:7] == test_msg):
|
||||
print('PASS: Received correct message')
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asi tcp server: {} (expected:{})'.format(g_client_response[4:7], test_msg))
|
||||
thread1.join()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_chat_client()
|
||||
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "chat_client.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -1,16 +0,0 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PORT
|
||||
string "Asio example server port number"
|
||||
default "2222"
|
||||
help
|
||||
Port number used by Asio example.
|
||||
|
||||
config EXAMPLE_SERVER_IP
|
||||
string "Asio example server ip"
|
||||
default "FROM_STDIN"
|
||||
help
|
||||
Asio example server ip for this client to connect to.
|
||||
Leave default "FROM_STDIN" to enter the server address via serial terminal.
|
||||
|
||||
endmenu
|
||||
@@ -1,183 +0,0 @@
|
||||
//
|
||||
// chat_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
: io_context_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
do_connect(endpoints);
|
||||
}
|
||||
|
||||
void write(const chat_message& msg)
|
||||
{
|
||||
asio::post(io_context_,
|
||||
[this, msg]()
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
asio::post(io_context_, [this]() { socket_.close(); });
|
||||
}
|
||||
|
||||
private:
|
||||
void do_connect(const tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[this](std::error_code ec, tcp::endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_header()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::cout.write(read_msg_.body(), read_msg_.body_length());
|
||||
std::cout << "\n";
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
void read_line(char * line, int max_chars);
|
||||
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* This helper function configures blocking UART I/O */
|
||||
ESP_ERROR_CHECK(example_configure_stdin_stdout());
|
||||
|
||||
std::string name(CONFIG_EXAMPLE_SERVER_IP);
|
||||
std::string port(CONFIG_EXAMPLE_PORT);
|
||||
char line[chat_message::max_body_length + 1] = { 0 };
|
||||
|
||||
if (name == "FROM_STDIN") {
|
||||
std::cout << "Please enter ip address of chat server" << std::endl;
|
||||
if (std::cin.getline(line, chat_message::max_body_length + 1)) {
|
||||
name = line;
|
||||
std::cout << "Chat server IP:" << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(name, port);
|
||||
|
||||
chat_client c(io_context, endpoints);
|
||||
|
||||
std::thread t([&io_context](){ io_context.run(); });
|
||||
|
||||
while (std::cin.getline(line, chat_message::max_body_length + 1) && std::string(line) != "exit") {
|
||||
chat_message msg;
|
||||
msg.body_length(std::strlen(line));
|
||||
std::memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
}
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
# Leave some room for larger apps without needing to reduce other features
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
@@ -1,23 +0,0 @@
|
||||
# Asio chat server example
|
||||
|
||||
Simple Asio chat server using WiFi STA or Ethernet.
|
||||
|
||||
## Example workflow
|
||||
|
||||
- Wi-Fi or Ethernet connection is established, and IP address is obtained.
|
||||
- Asio chat server is started on port number defined through the project configuration.
|
||||
- Chat server echoes a message (received from any client) to all connected clients.
|
||||
|
||||
## Running the example
|
||||
|
||||
- Open project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
- Set server port number in menuconfig, "Example configuration".
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- Connect to the server using multiple clients, for example using any option below.
|
||||
- build and run asi chat client on your host machine
|
||||
- run chat_client asio example on ESP platform
|
||||
- since chat message consist of ascii size and message, it is possible to
|
||||
netcat `nc IP PORT` and type for example ` 4ABC<CR>` to transmit 'ABC\n'
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
@@ -1,43 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_chat_server(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Start server
|
||||
3. Test connects to server and sends a test message
|
||||
4. Test evaluates received test message from server
|
||||
"""
|
||||
test_msg = b' 4ABC\n'
|
||||
dut1 = env.get_dut('chat_server', 'examples/protocols/asio/chat_server', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_chat_server.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_chat_server_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start test
|
||||
dut1.start_app()
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
if (data == test_msg):
|
||||
print('PASS: Received correct message {}'.format(data))
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asi tcp server: {} (expoected:{})'.format(data, test_msg))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_chat_server()
|
||||
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "chat_server.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -1,9 +0,0 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PORT
|
||||
string "Asio example server port number"
|
||||
default "2222"
|
||||
help
|
||||
Port number used by Asio example
|
||||
|
||||
endmenu
|
||||
@@ -1,91 +0,0 @@
|
||||
//
|
||||
// chat_message.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
enum { header_length = 4 };
|
||||
enum { max_body_length = 512 };
|
||||
|
||||
chat_message()
|
||||
: body_length_(0)
|
||||
{
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::size_t length() const
|
||||
{
|
||||
return header_length + body_length_;
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
std::size_t body_length() const
|
||||
{
|
||||
return body_length_;
|
||||
}
|
||||
|
||||
void body_length(std::size_t new_length)
|
||||
{
|
||||
body_length_ = new_length;
|
||||
if (body_length_ > max_body_length)
|
||||
body_length_ = max_body_length;
|
||||
}
|
||||
|
||||
bool decode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::strncat(header, data_, header_length);
|
||||
body_length_ = std::atoi(header);
|
||||
if (body_length_ > max_body_length)
|
||||
{
|
||||
body_length_ = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::sprintf(header, "%4d", static_cast<int>(body_length_));
|
||||
std::memcpy(data_, header, header_length);
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[header_length + max_body_length];
|
||||
std::size_t body_length_;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
||||
@@ -1,8 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
@@ -1,7 +0,0 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
# Leave some room for larger apps without needing to reduce other features
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
@@ -4,6 +4,3 @@ CONFIG_EXAMPLE_SERVER_NAME="localhost"
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CLIENT_VERIFY_PEER=y
|
||||
# IDF-3090
|
||||
CONFIG_ESP32C3_REV_MIN_0=y
|
||||
CONFIG_ESP32C3_REV_MIN=0
|
||||
|
||||
@@ -26,6 +26,7 @@ def test_examples_protocol_asio_tcp_server(env, extra_data):
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
|
||||
@@ -102,5 +102,7 @@ extern "C" void app_main(void)
|
||||
|
||||
server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
|
||||
|
||||
std::cout << "ASIO engine is up and running" << std::endl;
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ def test_examples_protocol_asio_udp_server(env, extra_data):
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
|
||||
@@ -84,5 +84,7 @@ extern "C" void app_main(void)
|
||||
|
||||
server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
|
||||
|
||||
std::cout << "ASIO engine is up and running" << std::endl;
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ Component config --->
|
||||
CoAP Configuration --->
|
||||
* Set encryption method definition, PSK (default) or PKI
|
||||
* Enable CoAP debugging if required
|
||||
High resolution timer (esp_timer) --->
|
||||
* Hardware timer to use for esp_timer - change if required (FRC2 for QEMU)
|
||||
|
||||
### Build and Flash
|
||||
|
||||
@@ -59,7 +61,7 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
|
||||
|
||||
## Example Output
|
||||
Prerequisite: we startup a CoAP server on coap server example,
|
||||
or use the default of coap://californium.eclipse.org.
|
||||
or use the default of coap://californium.eclipseprojects.io.
|
||||
|
||||
and you could receive data from CoAP server if succeed,
|
||||
such as the following log:
|
||||
@@ -76,26 +78,30 @@ I (1692) wifi: pm start, type: 1
|
||||
|
||||
I (2582) event: sta ip: 192.168.3.89, mask: 255.255.255.0, gw: 192.168.3.1
|
||||
I (2582) CoAP_client: Connected to AP
|
||||
I (2582) CoAP_client: DNS lookup succeeded. IP=104.196.15.150
|
||||
I (2582) CoAP_client: DNS lookup succeeded. IP=35.185.40.182
|
||||
Received:
|
||||
************************************************************
|
||||
CoAP RFC 7252 Cf 2.0.0-SNAPSHOT
|
||||
************************************************************
|
||||
****************************************************************
|
||||
CoAP RFC 7252 Cf 3.0.0-SNAPSHOT
|
||||
****************************************************************
|
||||
This server is using the Eclipse Californium (Cf) CoAP framework
|
||||
published under EPL+EDL: http://www.eclipse.org/californium/
|
||||
|
||||
(c) 2014, 2015, 2016 Institute for Pervasive Computing, ETH Zurich and others
|
||||
************************************************************
|
||||
(c) 2014-2020 Institute for Pervasive Computing, ETH Zurich and others
|
||||
****************************************************************
|
||||
...
|
||||
```
|
||||
|
||||
## libcoap Documentation
|
||||
This can be found at https://libcoap.net/doc/reference/4.2.0/
|
||||
This can be found at [libcoap Documentation](https://libcoap.net/documentation.html).
|
||||
The current API is 4.3.0.
|
||||
|
||||
## libcoap Specific Issues
|
||||
These can be raised at [libcoap Issues](https://github.com/obgm/libcoap/issues).
|
||||
|
||||
## Troubleshooting
|
||||
* Please make sure Target Url includes valid `host`, optional `port`,
|
||||
optional `path`, and begins with `coap://`, `coaps://` or `coap+tcp://`
|
||||
for a coap server that supports TCP
|
||||
(not all do including coap+tcp://californium.eclipse.org).
|
||||
(not all do including coap+tcp://californium.eclipseprojects.io).
|
||||
|
||||
* CoAP logging can be enabled by running 'idf.py menuconfig -> Component config -> CoAP Configuration' and setting appropriate log level
|
||||
|
||||
@@ -2,7 +2,7 @@ menu "Example CoAP Client Configuration"
|
||||
|
||||
config EXAMPLE_TARGET_DOMAIN_URI
|
||||
string "Target Uri"
|
||||
default "coaps://californium.eclipse.org"
|
||||
default "coaps://californium.eclipseprojects.io"
|
||||
help
|
||||
Target uri for the example to use. Use coaps:// prefix for encrypted traffic
|
||||
using Pre-Shared Key (PSK) or Public Key Infrastructure (PKI).
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
|
||||
VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
|
||||
BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
|
||||
ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
|
||||
DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
|
||||
DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
|
||||
bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
|
||||
A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
|
||||
AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
|
||||
1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
|
||||
vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
|
||||
AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
|
||||
A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
|
||||
BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
|
||||
A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
|
||||
LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
|
||||
5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
|
||||
d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
|
||||
euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
|
||||
S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
|
||||
PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
|
||||
MIICDzCCAbSgAwIBAgIIAbOUoVFDz/QwDAYIKoZIzj0EAwIFADBcMRAwDgYDVQQD
|
||||
EwdjZi1yb290MRQwEgYDVQQLEwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBz
|
||||
ZSBJb1QxDzANBgNVBAcTBk90dGF3YTELMAkGA1UEBhMCQ0EwHhcNMjAxMTExMTAz
|
||||
MDMzWhcNMjExMTExMTAzMDMzWjBaMQ4wDAYDVQQDEwVjZi1jYTEUMBIGA1UECxML
|
||||
Q2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZPdHRh
|
||||
d2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7/3EXOZn
|
||||
GZXNEIj7LuQAMZ8lfRYSCnpME1TBjKjZPtVeztLtGWgkkLvIX11pAJcBh51cpi7Z
|
||||
fQtGpVE9CLOh6aNgMF4wHQYDVR0OBBYEFEvf57UcJhYYkx14twkeitd691fVMAsG
|
||||
A1UdDwQEAwIBBjAPBgNVHRMECDAGAQH/AgEBMB8GA1UdIwQYMBaAFAsi3KbVERiK
|
||||
JzFCfC/GVrYksGzEMAwGCCqGSM49BAMCBQADRwAwRAIgc5nVF/5Pip0XB17IZXqi
|
||||
V84FXanWdn9Z0SiPdpOgvZMCIH13vL9tkCCjPN3tg3TYRY/bzyGohFGBcTrrEtUr
|
||||
rVIm
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB4DCCAYWgAwIBAgIIQR8ro8AQ02AwDAYIKoZIzj0EAwIFADBcMRAwDgYDVQQD
|
||||
EwdjZi1yb290MRQwEgYDVQQLEwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBz
|
||||
ZSBJb1QxDzANBgNVBAcTBk90dGF3YTELMAkGA1UEBhMCQ0EwHhcNMjAxMTExMTAz
|
||||
MDMyWhcNMjExMTExMTAzMDMyWjBcMRAwDgYDVQQDEwdjZi1yb290MRQwEgYDVQQL
|
||||
EwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBzZSBJb1QxDzANBgNVBAcTBk90
|
||||
dGF3YTELMAkGA1UEBhMCQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATZ1BRM
|
||||
T1//Fzh9sneRZNwS4kgCxN1PvgwT271qCpYqyxnjLEa38AP1IAanhpiD/OkVc0Zd
|
||||
7NgDPCw7n94EULMyoy8wLTAdBgNVHQ4EFgQUCyLcptURGIonMUJ8L8ZWtiSwbMQw
|
||||
DAYDVR0TBAUwAwEB/zAMBggqhkjOPQQDAgUAA0cAMEQCIAdLEgcUWdpAl9jwdJiz
|
||||
/cHW7/CBIWEvqiQfzE+XLyLOAiAvuxSdOtSDjh2aC5qEjUCH8CSKCxWB74j23tmp
|
||||
aqPH4A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,70 +1,13 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 48 (0x30)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority
|
||||
Validity
|
||||
Not Before: Jun 7 08:06:49 2017 GMT
|
||||
Not After : Jun 5 08:06:49 2027 GMT
|
||||
Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:d2:f6:be:72:a5:ab:2e:56:0c:dd:f2:3b:2c:7c:
|
||||
e0:5d:05:40:af:0c:8c:f3:82:0c:d0:18:34:b4:e3:
|
||||
7d:5f:8d:0a:3e:aa:79:02:f9:96:ad:10:00:ec:51:
|
||||
e9:dc:3f:fb:ea:b0:57:eb:48:c7:ca:ef:e8:05:ab:
|
||||
ee:3f:66:ba:5c:9e:7f:40:85:9f:25:a0:e0:e3:7c:
|
||||
cf:b6:e6:31:f5:fd:24:03:c8:f4:fb:d8:a4:f3:92:
|
||||
29:05:aa:55:43:80:f7:3e:13:10:43:3a:89:24:be:
|
||||
d8:01:86:d1:69:73:44:7d:f8:b9:46:2b:6b:51:d0:
|
||||
11:31:4b:06:ae:9f:45:fa:12:17:0c:ef:6a:fa:d0:
|
||||
f7:36:46:eb:2e:db:4e:20:46:01:33:ac:b1:f7:4a:
|
||||
e6:18:3d:53:22:dc:e8:4a:12:78:11:2f:e4:3b:92:
|
||||
bd:d7:07:5a:c9:81:5d:48:58:c8:0f:9b:e9:a4:0f:
|
||||
bb:89:b1:ad:38:07:6f:93:d0:a6:12:56:f9:07:48:
|
||||
d2:23:2f:a3:a9:93:b0:11:0a:27:4c:48:0a:8d:70:
|
||||
41:68:76:7a:dd:bc:54:c3:42:33:b0:7b:f6:ae:1f:
|
||||
e7:95:5e:11:ca:f2:b4:4b:5c:ba:47:64:f0:f3:d7:
|
||||
87:95:7f:93:06:a1:72:c9:81:12:a5:b7:8f:9d:7e:
|
||||
d1:ef
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
URI:http://www.example.com/example_ca.crl
|
||||
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
2d:02:bc:7b:88:b8:5c:e1:07:b8:bb:ba:b2:f3:98:14:8f:cb:
|
||||
b0:21:13:b5:e5:6f:05:4f:92:fa:ac:c0:53:a7:b0:cd:7e:ba:
|
||||
87:36:85:25:d7:41:c5:29:84:22:74:af:bf:3e:34:36:d5:24:
|
||||
7a:81:e2:1b:54:52:85:6f:76:de:dc:63:98:45:fc:2c:31:fa:
|
||||
22:a4:72:3a:8d:d4:6a:2e:de:33:10:41:eb:94:1d:e3:59:cd:
|
||||
b2:be:ab:f0:b6:20:86:9c:b8:46:ee:c5:64:ba:b6:6c:cc:53:
|
||||
44:7a:80:12:77:7c:e7:51:67:91:32:2f:88:9d:93:a8:ef:d6:
|
||||
cd:de
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDTjCCAregAwIBAgIBMDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
|
||||
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
|
||||
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
|
||||
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
|
||||
ODA2NDlaFw0yNzA2MDUwODA2NDlaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
|
||||
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt
|
||||
cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANL2vnKlqy5WDN3yOyx84F0FQK8MjPOC
|
||||
DNAYNLTjfV+NCj6qeQL5lq0QAOxR6dw/++qwV+tIx8rv6AWr7j9mulyef0CFnyWg
|
||||
4ON8z7bmMfX9JAPI9PvYpPOSKQWqVUOA9z4TEEM6iSS+2AGG0WlzRH34uUYra1HQ
|
||||
ETFLBq6fRfoSFwzvavrQ9zZG6y7bTiBGATOssfdK5hg9UyLc6EoSeBEv5DuSvdcH
|
||||
WsmBXUhYyA+b6aQPu4mxrTgHb5PQphJW+QdI0iMvo6mTsBEKJ0xICo1wQWh2et28
|
||||
VMNCM7B79q4f55VeEcrytEtcukdk8PPXh5V/kwahcsmBEqW3j51+0e8CAwEAAaNP
|
||||
ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov
|
||||
L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB
|
||||
gQAtArx7iLhc4Qe4u7qy85gUj8uwIRO15W8FT5L6rMBTp7DNfrqHNoUl10HFKYQi
|
||||
dK+/PjQ21SR6geIbVFKFb3be3GOYRfwsMfoipHI6jdRqLt4zEEHrlB3jWc2yvqvw
|
||||
tiCGnLhG7sVkurZszFNEeoASd3znUWeRMi+InZOo79bN3g==
|
||||
MIICAzCCAaagAwIBAgIJAJnE6sMNQNAoMAwGCCqGSM49BAMCBQAwWjEOMAwGA1UE
|
||||
AxMFY2YtY2ExFDASBgNVBAsTC0NhbGlmb3JuaXVtMRQwEgYDVQQKEwtFY2xpcHNl
|
||||
IElvVDEPMA0GA1UEBxMGT3R0YXdhMQswCQYDVQQGEwJDQTAeFw0yMDExMTExMDMw
|
||||
NDVaFw0yMTExMTExMDMwNDVaMF4xEjAQBgNVBAMTCWNmLWNsaWVudDEUMBIGA1UE
|
||||
CxMLQ2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZP
|
||||
dHRhd2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFGno
|
||||
107kFgJ4AvABQviE9hTJlEeB4wfS3L58Q5J8srWxSunEgniIbfr0p8Shw+C1XAcz
|
||||
FxJrn8SjFCVqOKjrrqNPME0wHQYDVR0OBBYEFIwAAdmpYSm184Jx1ycc3BQGybhN
|
||||
MAsGA1UdDwQEAwIHgDAfBgNVHSMEGDAWgBRL3+e1HCYWGJMdeLcJHorXevdX1TAM
|
||||
BggqhkjOPQQDAgUAA0kAMEYCIQC+w/hm8TfTCqUV6midHAvmNxJN7MfMcpAiyi4e
|
||||
6NBrPAIhAPeMUrnOlykTMcpYMRZs4YnyM6ihgU/F8UjknhDpkywm
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,27 +1,4 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0va+cqWrLlYM3fI7LHzgXQVArwyM84IM0Bg0tON9X40KPqp5
|
||||
AvmWrRAA7FHp3D/76rBX60jHyu/oBavuP2a6XJ5/QIWfJaDg43zPtuYx9f0kA8j0
|
||||
+9ik85IpBapVQ4D3PhMQQzqJJL7YAYbRaXNEffi5RitrUdARMUsGrp9F+hIXDO9q
|
||||
+tD3NkbrLttOIEYBM6yx90rmGD1TItzoShJ4ES/kO5K91wdayYFdSFjID5vppA+7
|
||||
ibGtOAdvk9CmElb5B0jSIy+jqZOwEQonTEgKjXBBaHZ63bxUw0IzsHv2rh/nlV4R
|
||||
yvK0S1y6R2Tw89eHlX+TBqFyyYESpbePnX7R7wIDAQABAoIBAQC5PncO3tBIeMEF
|
||||
pu007FZq9/DLhP7D2B9+HrMxX0y4uXUUf8aQyS74ukPFP0xV3U1M0BnzfU4KscyQ
|
||||
Jl+nBoKAT6C3vF15wiGXQAJ4vPuD4Ate03fjKWH2ixJAakhCZR01QbIXBnBkdrvf
|
||||
401BBjlPUDcIGZo8FbLzEMlGTo84vE9v3Qmkbi+PzPCh2YC+NDmsOcIW1zpmwyYC
|
||||
ZYCpoWgl4++kqXXn0NGhuaOgB0JLsJOBpx/hOOjBU/wXCKaXZ1vchYqfbvvx2gf2
|
||||
WX4P0CiTH1z7MEAHanaZkcnNyxV/oF1EIMY5p0vDDzgrKtppvPOqspjydje03+CE
|
||||
t0wKGPi5AoGBAPAG2Y4efgwLcoWdPjKZtsHLhDhLJnvxkqnNkzdPnLZojNi8pKkV
|
||||
/Yu++pPemJZZa4YAp+OnqyEfhcha+HYqKMwRC8t3YrEVOlRQTfW/OoSrp059JIRV
|
||||
jTvq/u7DdYGJRRgMLUJiEI+7xj1WbTc2EceJAgn0qfKvbvBtVJP0LH1TAoGBAOEA
|
||||
xZB7SwyX+zDGRTugqMYg+sYobbQHJ7utLyoX+ckeG+sPEjEYLpQQfshET/gwF8ZK
|
||||
4aILkACx/tna799xCjQdmyyc338NO9WULlY1xF+65WfeaxrtTAsqVikX3p19McRI
|
||||
ijX8k7Msy3gYWJXev3MCtPT2+g68IgbL/W2wY+l1AoGAT7xGy0Jv5vpqid5pig+s
|
||||
OYatHrJAT445hXUIQaiNy77Bg0JvhMgMWT8RKMwabl+4K2TOYP8TB0bcf2lQ/pgU
|
||||
w22qOGYpf+AoZ1fh/hAPlYEcbCOAXQG6kDwJgjGmOGjsbgelhVbkX4smWLv8PgoV
|
||||
L+7goYQIbNlAhlgbb6b+nIcCgYBB7Zr2Cdpkt0en9ACnRx0M6O7yDziNzqbqzAUM
|
||||
3XeYYZUmnATlk8NaKTcs8S9JdrYQqTJR6/dm7MDTDt7IZvPpb19fhBvMu5DztPaa
|
||||
1ihTMI01kStq+WsVvnL+mXrmRJ/HdsXgqcCReKep6eBTEbChP4LMYG3G0YNa4HzC
|
||||
njO4XQKBgQDRnbqqg2CNTnS94BN2D3uzzELtwsIG6aVCtl09ZsLnGaBKVVDtP6BI
|
||||
j2hGD7xw4g5JeSPIJU5J03nALTY3hz1JyI7AJCX7+JRtUTX2A8C4mlbeul7ilGaU
|
||||
A7MFT8GqhjYYa84GzNcA1mK8ynlixpL8+yzTT/8lWInWRBa69SkktA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBgNuyqKuRW0RxU1DVs
|
||||
aEpBPtVRVLRZYq6hZRzvZ6igBw==
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -31,17 +31,12 @@
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#if 1
|
||||
/* Needed until coap_dtls.h becomes a part of libcoap proper */
|
||||
#include "libcoap.h"
|
||||
#include "coap_dtls.h"
|
||||
#endif
|
||||
#include "coap.h"
|
||||
#include "coap3/coap.h"
|
||||
|
||||
#define COAP_DEFAULT_TIME_SEC 5
|
||||
#define COAP_DEFAULT_TIME_SEC 60
|
||||
|
||||
/* The examples use simple Pre-Shared-Key configuration that you can set via
|
||||
'make menuconfig'.
|
||||
'idf.py menuconfig'.
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key"
|
||||
@@ -54,7 +49,7 @@
|
||||
#define EXAMPLE_COAP_PSK_IDENTITY CONFIG_EXAMPLE_COAP_PSK_IDENTITY
|
||||
|
||||
/* The examples use uri Logging Level that
|
||||
you can set via 'make menuconfig'.
|
||||
you can set via 'idf.py menuconfig'.
|
||||
|
||||
If you'd rather not, just change the below entry to a value
|
||||
that is between 0 and 7 with
|
||||
@@ -62,11 +57,11 @@
|
||||
*/
|
||||
#define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL
|
||||
|
||||
/* The examples use uri "coap://californium.eclipse.org" that
|
||||
/* The examples use uri "coap://californium.eclipseprojects.io" that
|
||||
you can set via the project configuration (idf.py menuconfig)
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define COAP_DEFAULT_DEMO_URI "coaps://californium.eclipse.org"
|
||||
the config you want - ie #define COAP_DEFAULT_DEMO_URI "coaps://californium.eclipseprojects.io"
|
||||
*/
|
||||
#define COAP_DEFAULT_DEMO_URI CONFIG_EXAMPLE_TARGET_DOMAIN_URI
|
||||
|
||||
@@ -81,8 +76,9 @@ static int wait_ms;
|
||||
Client cert, taken from coap_client.crt
|
||||
Client key, taken from coap_client.key
|
||||
|
||||
The PEM, CRT and KEY file are examples taken from the wpa2 enterprise
|
||||
example.
|
||||
The PEM, CRT and KEY file are examples taken from
|
||||
https://github.com/eclipse/californium/tree/master/demo-certs/src/main/resources
|
||||
as the Certificate test (by default) is against the californium server.
|
||||
|
||||
To embed it in the app binary, the PEM, CRT and KEY file is named
|
||||
in the component.mk COMPONENT_EMBED_TXTFILES variable.
|
||||
@@ -95,84 +91,39 @@ extern uint8_t client_key_start[] asm("_binary_coap_client_key_start");
|
||||
extern uint8_t client_key_end[] asm("_binary_coap_client_key_end");
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
|
||||
static void message_handler(coap_context_t *ctx, coap_session_t *session,
|
||||
coap_pdu_t *sent, coap_pdu_t *received,
|
||||
const coap_tid_t id)
|
||||
static coap_response_t
|
||||
message_handler(coap_session_t *session,
|
||||
const coap_pdu_t *sent,
|
||||
const coap_pdu_t *received,
|
||||
const coap_mid_t mid)
|
||||
{
|
||||
unsigned char *data = NULL;
|
||||
const unsigned char *data = NULL;
|
||||
size_t data_len;
|
||||
coap_pdu_t *pdu = NULL;
|
||||
coap_opt_t *block_opt;
|
||||
coap_opt_iterator_t opt_iter;
|
||||
unsigned char buf[4];
|
||||
coap_optlist_t *option;
|
||||
coap_tid_t tid;
|
||||
size_t offset;
|
||||
size_t total;
|
||||
coap_pdu_code_t rcvd_code = coap_pdu_get_code(received);
|
||||
|
||||
if (COAP_RESPONSE_CLASS(received->code) == 2) {
|
||||
/* Need to see if blocked response */
|
||||
block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
|
||||
if (block_opt) {
|
||||
uint16_t blktype = opt_iter.type;
|
||||
|
||||
if (coap_opt_block_num(block_opt) == 0) {
|
||||
printf("Received:\n");
|
||||
}
|
||||
if (coap_get_data(received, &data_len, &data)) {
|
||||
printf("%.*s", (int)data_len, data);
|
||||
}
|
||||
if (COAP_OPT_BLOCK_MORE(block_opt)) {
|
||||
/* more bit is set */
|
||||
|
||||
/* create pdu with request for next block */
|
||||
pdu = coap_new_pdu(session);
|
||||
if (!pdu) {
|
||||
ESP_LOGE(TAG, "coap_new_pdu() failed");
|
||||
goto clean_up;
|
||||
}
|
||||
pdu->type = COAP_MESSAGE_CON;
|
||||
pdu->tid = coap_new_message_id(session);
|
||||
pdu->code = COAP_REQUEST_GET;
|
||||
|
||||
/* add URI components from optlist */
|
||||
for (option = optlist; option; option = option->next ) {
|
||||
switch (option->number) {
|
||||
case COAP_OPTION_URI_HOST :
|
||||
case COAP_OPTION_URI_PORT :
|
||||
case COAP_OPTION_URI_PATH :
|
||||
case COAP_OPTION_URI_QUERY :
|
||||
coap_add_option(pdu, option->number, option->length,
|
||||
option->data);
|
||||
break;
|
||||
default:
|
||||
; /* skip other options */
|
||||
}
|
||||
}
|
||||
|
||||
/* finally add updated block option from response, clear M bit */
|
||||
/* blocknr = (blocknr & 0xfffffff7) + 0x10; */
|
||||
coap_add_option(pdu,
|
||||
blktype,
|
||||
coap_encode_var_safe(buf, sizeof(buf),
|
||||
((coap_opt_block_num(block_opt) + 1) << 4) |
|
||||
COAP_OPT_BLOCK_SZX(block_opt)), buf);
|
||||
|
||||
tid = coap_send(session, pdu);
|
||||
|
||||
if (tid != COAP_INVALID_TID) {
|
||||
resp_wait = 1;
|
||||
wait_ms = COAP_DEFAULT_TIME_SEC * 1000;
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
if (coap_get_data(received, &data_len, &data)) {
|
||||
printf("Received: %.*s\n", (int)data_len, data);
|
||||
if (COAP_RESPONSE_CLASS(rcvd_code) == 2) {
|
||||
if (coap_get_data_large(received, &data_len, &data, &offset, &total)) {
|
||||
if (data_len != total) {
|
||||
printf("Unexpected partial data received offset %u, length %u\n", offset, data_len);
|
||||
}
|
||||
printf("Received:\n%.*s\n", (int)data_len, data);
|
||||
resp_wait = 0;
|
||||
}
|
||||
return COAP_RESPONSE_OK;
|
||||
}
|
||||
printf("%d.%02d", (rcvd_code >> 5), rcvd_code & 0x1F);
|
||||
if (coap_get_data_large(received, &data_len, &data, &offset, &total)) {
|
||||
printf(": ");
|
||||
while(data_len--) {
|
||||
printf("%c", isprint(*data) ? *data : '.');
|
||||
data++;
|
||||
}
|
||||
}
|
||||
clean_up:
|
||||
printf("\n");
|
||||
resp_wait = 0;
|
||||
return COAP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PKI
|
||||
@@ -193,210 +144,303 @@ verify_cn_callback(const char *cn,
|
||||
}
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
|
||||
static void coap_example_client(void *p)
|
||||
static void
|
||||
coap_log_handler (coap_log_t level, const char *message)
|
||||
{
|
||||
struct hostent *hp;
|
||||
coap_address_t dst_addr;
|
||||
static coap_uri_t uri;
|
||||
const char *server_uri = COAP_DEFAULT_DEMO_URI;
|
||||
uint32_t esp_level = ESP_LOG_INFO;
|
||||
char *cp = strchr(message, '\n');
|
||||
|
||||
if (cp)
|
||||
ESP_LOG_LEVEL(esp_level, TAG, "%.*s", (int)(cp-message), message);
|
||||
else
|
||||
ESP_LOG_LEVEL(esp_level, TAG, "%s", message);
|
||||
}
|
||||
|
||||
static coap_address_t *
|
||||
coap_get_address(coap_uri_t *uri)
|
||||
{
|
||||
static coap_address_t dst_addr;
|
||||
char *phostname = NULL;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *addrres;
|
||||
int error;
|
||||
char tmpbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
|
||||
phostname = (char *)calloc(1, uri->host.length + 1);
|
||||
if (phostname == NULL) {
|
||||
ESP_LOGE(TAG, "calloc failed");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(phostname, uri->host.s, uri->host.length);
|
||||
|
||||
while (1) {
|
||||
#define BUFSIZE 40
|
||||
unsigned char _buf[BUFSIZE];
|
||||
unsigned char *buf;
|
||||
size_t buflen;
|
||||
int res;
|
||||
coap_context_t *ctx = NULL;
|
||||
coap_session_t *session = NULL;
|
||||
coap_pdu_t *request = NULL;
|
||||
memset ((char *)&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
optlist = NULL;
|
||||
if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) {
|
||||
ESP_LOGE(TAG, "CoAP server uri error");
|
||||
break;
|
||||
}
|
||||
|
||||
if (uri.scheme == COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) {
|
||||
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
|
||||
break;
|
||||
}
|
||||
if (uri.scheme == COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported()) {
|
||||
ESP_LOGE(TAG, "CoAP server uri coaps+tcp:// scheme is not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
phostname = (char *)calloc(1, uri.host.length + 1);
|
||||
if (phostname == NULL) {
|
||||
ESP_LOGE(TAG, "calloc failed");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(phostname, uri.host.s, uri.host.length);
|
||||
hp = gethostbyname(phostname);
|
||||
error = getaddrinfo(phostname, NULL, &hints, &addrres);
|
||||
if (error != 0) {
|
||||
ESP_LOGE(TAG, "DNS lookup failed for destination address %s. error: %d", phostname, error);
|
||||
free(phostname);
|
||||
return NULL;
|
||||
}
|
||||
if (addrres == NULL) {
|
||||
ESP_LOGE(TAG, "DNS lookup %s did not return any addresses", phostname);
|
||||
free(phostname);
|
||||
return NULL;
|
||||
}
|
||||
free(phostname);
|
||||
coap_address_init(&dst_addr);
|
||||
switch (addrres->ai_family) {
|
||||
case AF_INET:
|
||||
memcpy(&dst_addr.addr.sin, addrres->ai_addr, sizeof(dst_addr.addr.sin));
|
||||
dst_addr.addr.sin.sin_port = htons(uri->port);
|
||||
inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf));
|
||||
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(&dst_addr.addr.sin6, addrres->ai_addr, sizeof(dst_addr.addr.sin6));
|
||||
dst_addr.addr.sin6.sin6_port = htons(uri->port);
|
||||
inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf));
|
||||
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "DNS lookup response failed");
|
||||
return NULL;
|
||||
}
|
||||
freeaddrinfo(addrres);
|
||||
|
||||
if (hp == NULL) {
|
||||
ESP_LOGE(TAG, "DNS lookup failed");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
free(phostname);
|
||||
continue;
|
||||
return &dst_addr;
|
||||
}
|
||||
|
||||
static int
|
||||
coap_build_optlist(coap_uri_t *uri)
|
||||
{
|
||||
#define BUFSIZE 40
|
||||
unsigned char _buf[BUFSIZE];
|
||||
unsigned char *buf;
|
||||
size_t buflen;
|
||||
int res;
|
||||
|
||||
optlist = NULL;
|
||||
|
||||
if (uri->scheme == COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) {
|
||||
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
|
||||
return 0;
|
||||
}
|
||||
if (uri->scheme == COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported()) {
|
||||
ESP_LOGE(TAG, "CoAP server uri->+tcp:// scheme is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (uri->path.length) {
|
||||
buflen = BUFSIZE;
|
||||
buf = _buf;
|
||||
res = coap_split_path(uri->path.s, uri->path.length, buf, &buflen);
|
||||
|
||||
while (res--) {
|
||||
coap_insert_optlist(&optlist,
|
||||
coap_new_optlist(COAP_OPTION_URI_PATH,
|
||||
coap_opt_length(buf),
|
||||
coap_opt_value(buf)));
|
||||
|
||||
buf += coap_opt_size(buf);
|
||||
}
|
||||
char tmpbuf[INET6_ADDRSTRLEN];
|
||||
coap_address_init(&dst_addr);
|
||||
switch (hp->h_addrtype) {
|
||||
case AF_INET:
|
||||
dst_addr.addr.sin.sin_family = AF_INET;
|
||||
dst_addr.addr.sin.sin_port = htons(uri.port);
|
||||
memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr));
|
||||
inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf));
|
||||
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
|
||||
break;
|
||||
case AF_INET6:
|
||||
dst_addr.addr.sin6.sin6_family = AF_INET6;
|
||||
dst_addr.addr.sin6.sin6_port = htons(uri.port);
|
||||
memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr));
|
||||
inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf));
|
||||
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "DNS lookup response failed");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (uri->query.length) {
|
||||
buflen = BUFSIZE;
|
||||
buf = _buf;
|
||||
res = coap_split_query(uri->query.s, uri->query.length, buf, &buflen);
|
||||
|
||||
while (res--) {
|
||||
coap_insert_optlist(&optlist,
|
||||
coap_new_optlist(COAP_OPTION_URI_QUERY,
|
||||
coap_opt_length(buf),
|
||||
coap_opt_value(buf)));
|
||||
|
||||
buf += coap_opt_size(buf);
|
||||
}
|
||||
|
||||
if (uri.path.length) {
|
||||
buflen = BUFSIZE;
|
||||
buf = _buf;
|
||||
res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
|
||||
|
||||
while (res--) {
|
||||
coap_insert_optlist(&optlist,
|
||||
coap_new_optlist(COAP_OPTION_URI_PATH,
|
||||
coap_opt_length(buf),
|
||||
coap_opt_value(buf)));
|
||||
|
||||
buf += coap_opt_size(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (uri.query.length) {
|
||||
buflen = BUFSIZE;
|
||||
buf = _buf;
|
||||
res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
|
||||
|
||||
while (res--) {
|
||||
coap_insert_optlist(&optlist,
|
||||
coap_new_optlist(COAP_OPTION_URI_QUERY,
|
||||
coap_opt_length(buf),
|
||||
coap_opt_value(buf)));
|
||||
|
||||
buf += coap_opt_size(buf);
|
||||
}
|
||||
}
|
||||
|
||||
ctx = coap_new_context(NULL);
|
||||
if (!ctx) {
|
||||
ESP_LOGE(TAG, "coap_new_context() failed");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that if the URI starts with just coap:// (not coaps://) the
|
||||
* session will still be plain text.
|
||||
*
|
||||
* coaps+tcp:// is NOT supported by the libcoap->mbedtls interface
|
||||
* so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above,
|
||||
* but the code is left in for completeness.
|
||||
*/
|
||||
if (uri.scheme == COAP_URI_SCHEME_COAPS || uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
|
||||
#ifndef CONFIG_MBEDTLS_TLS_CLIENT
|
||||
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
|
||||
goto clean_up;
|
||||
#endif /* CONFIG_MBEDTLS_TLS_CLIENT */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PSK
|
||||
session = coap_new_client_session_psk(ctx, NULL, &dst_addr,
|
||||
uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
|
||||
EXAMPLE_COAP_PSK_IDENTITY,
|
||||
(const uint8_t *)EXAMPLE_COAP_PSK_KEY,
|
||||
sizeof(EXAMPLE_COAP_PSK_KEY) - 1);
|
||||
static coap_session_t *
|
||||
coap_start_psk_session(coap_context_t *ctx, coap_address_t *dst_addr, coap_uri_t *uri)
|
||||
{
|
||||
static coap_dtls_cpsk_t dtls_psk;
|
||||
static char client_sni[256];
|
||||
|
||||
memset(client_sni, 0, sizeof(client_sni));
|
||||
memset (&dtls_psk, 0, sizeof(dtls_psk));
|
||||
dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
|
||||
dtls_psk.validate_ih_call_back = NULL;
|
||||
dtls_psk.ih_call_back_arg = NULL;
|
||||
if (uri->host.length)
|
||||
memcpy(client_sni, uri->host.s, MIN(uri->host.length, sizeof(client_sni) - 1));
|
||||
else
|
||||
memcpy(client_sni, "localhost", 9);
|
||||
dtls_psk.client_sni = client_sni;
|
||||
dtls_psk.psk_info.identity.s = (const uint8_t *)EXAMPLE_COAP_PSK_IDENTITY;
|
||||
dtls_psk.psk_info.identity.length = sizeof(EXAMPLE_COAP_PSK_IDENTITY)-1;
|
||||
dtls_psk.psk_info.key.s = (const uint8_t *)EXAMPLE_COAP_PSK_KEY;
|
||||
dtls_psk.psk_info.key.length = sizeof(EXAMPLE_COAP_PSK_KEY)-1;
|
||||
return coap_new_client_session_psk2(ctx, NULL, dst_addr,
|
||||
uri->scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
|
||||
&dtls_psk);
|
||||
}
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PSK */
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PKI
|
||||
unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
|
||||
unsigned int client_crt_bytes = client_crt_end - client_crt_start;
|
||||
unsigned int client_key_bytes = client_key_end - client_key_start;
|
||||
coap_dtls_pki_t dtls_pki;
|
||||
static char client_sni[256];
|
||||
static coap_session_t *
|
||||
coap_start_pki_session(coap_context_t *ctx, coap_address_t *dst_addr, coap_uri_t *uri)
|
||||
{
|
||||
unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
|
||||
unsigned int client_crt_bytes = client_crt_end - client_crt_start;
|
||||
unsigned int client_key_bytes = client_key_end - client_key_start;
|
||||
static coap_dtls_pki_t dtls_pki;
|
||||
static char client_sni[256];
|
||||
|
||||
memset (&dtls_pki, 0, sizeof(dtls_pki));
|
||||
dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
|
||||
if (ca_pem_bytes) {
|
||||
/*
|
||||
* Add in additional certificate checking.
|
||||
* This list of enabled can be tuned for the specific
|
||||
* requirements - see 'man coap_encryption'.
|
||||
*
|
||||
* Note: A list of root ca file can be setup separately using
|
||||
* coap_context_set_pki_root_cas(), but the below is used to
|
||||
* define what checking actually takes place.
|
||||
*/
|
||||
dtls_pki.verify_peer_cert = 1;
|
||||
dtls_pki.require_peer_cert = 1;
|
||||
dtls_pki.allow_self_signed = 1;
|
||||
dtls_pki.allow_expired_certs = 1;
|
||||
dtls_pki.cert_chain_validation = 1;
|
||||
dtls_pki.cert_chain_verify_depth = 2;
|
||||
dtls_pki.check_cert_revocation = 1;
|
||||
dtls_pki.allow_no_crl = 1;
|
||||
dtls_pki.allow_expired_crl = 1;
|
||||
dtls_pki.allow_bad_md_hash = 1;
|
||||
dtls_pki.allow_short_rsa_length = 1;
|
||||
dtls_pki.validate_cn_call_back = verify_cn_callback;
|
||||
dtls_pki.cn_call_back_arg = NULL;
|
||||
dtls_pki.validate_sni_call_back = NULL;
|
||||
dtls_pki.sni_call_back_arg = NULL;
|
||||
memset(client_sni, 0, sizeof(client_sni));
|
||||
if (uri.host.length) {
|
||||
memcpy(client_sni, uri.host.s, MIN(uri.host.length, sizeof(client_sni)));
|
||||
} else {
|
||||
memcpy(client_sni, "localhost", 9);
|
||||
}
|
||||
dtls_pki.client_sni = client_sni;
|
||||
}
|
||||
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
|
||||
dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start;
|
||||
dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes;
|
||||
dtls_pki.pki_key.key.pem_buf.private_key = client_key_start;
|
||||
dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes;
|
||||
dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
|
||||
dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
|
||||
|
||||
session = coap_new_client_session_pki(ctx, NULL, &dst_addr,
|
||||
uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
|
||||
&dtls_pki);
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
memset (&dtls_pki, 0, sizeof(dtls_pki));
|
||||
dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
|
||||
if (ca_pem_bytes) {
|
||||
/*
|
||||
* Add in additional certificate checking.
|
||||
* This list of enabled can be tuned for the specific
|
||||
* requirements - see 'man coap_encryption'.
|
||||
*
|
||||
* Note: A list of root cas file can be setup separately using
|
||||
* coap_context_set_pki_root_cas(), but the below is used to
|
||||
* define what checking actually takes place.
|
||||
*/
|
||||
dtls_pki.verify_peer_cert = 1;
|
||||
dtls_pki.check_common_ca = 1;
|
||||
dtls_pki.allow_self_signed = 1;
|
||||
dtls_pki.allow_expired_certs = 1;
|
||||
dtls_pki.cert_chain_validation = 1;
|
||||
dtls_pki.cert_chain_verify_depth = 2;
|
||||
dtls_pki.check_cert_revocation = 1;
|
||||
dtls_pki.allow_no_crl = 1;
|
||||
dtls_pki.allow_expired_crl = 1;
|
||||
dtls_pki.allow_bad_md_hash = 1;
|
||||
dtls_pki.allow_short_rsa_length = 1;
|
||||
dtls_pki.validate_cn_call_back = verify_cn_callback;
|
||||
dtls_pki.cn_call_back_arg = NULL;
|
||||
dtls_pki.validate_sni_call_back = NULL;
|
||||
dtls_pki.sni_call_back_arg = NULL;
|
||||
memset(client_sni, 0, sizeof(client_sni));
|
||||
if (uri->host.length) {
|
||||
memcpy(client_sni, uri->host.s, MIN(uri->host.length, sizeof(client_sni)));
|
||||
} else {
|
||||
session = coap_new_client_session(ctx, NULL, &dst_addr,
|
||||
uri.scheme == COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
|
||||
COAP_PROTO_UDP);
|
||||
}
|
||||
if (!session) {
|
||||
ESP_LOGE(TAG, "coap_new_client_session() failed");
|
||||
goto clean_up;
|
||||
memcpy(client_sni, "localhost", 9);
|
||||
}
|
||||
dtls_pki.client_sni = client_sni;
|
||||
}
|
||||
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
|
||||
dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start;
|
||||
dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes;
|
||||
dtls_pki.pki_key.key.pem_buf.private_key = client_key_start;
|
||||
dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes;
|
||||
dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
|
||||
dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
|
||||
|
||||
coap_register_response_handler(ctx, message_handler);
|
||||
return coap_new_client_session_pki(ctx, NULL, dst_addr,
|
||||
uri->scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
|
||||
&dtls_pki);
|
||||
}
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
|
||||
request = coap_new_pdu(session);
|
||||
static void coap_example_client(void *p)
|
||||
{
|
||||
coap_address_t *dst_addr;
|
||||
static coap_uri_t uri;
|
||||
const char *server_uri = COAP_DEFAULT_DEMO_URI;
|
||||
coap_context_t *ctx = NULL;
|
||||
coap_session_t *session = NULL;
|
||||
coap_pdu_t *request = NULL;
|
||||
unsigned char token[8];
|
||||
size_t tokenlength;
|
||||
|
||||
/* Set up the CoAP logging */
|
||||
coap_set_log_handler(coap_log_handler);
|
||||
coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
|
||||
|
||||
/* Set up the CoAP context */
|
||||
ctx = coap_new_context(NULL);
|
||||
if (!ctx) {
|
||||
ESP_LOGE(TAG, "coap_new_context() failed");
|
||||
goto clean_up;
|
||||
}
|
||||
coap_context_set_block_mode(ctx,
|
||||
COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY);
|
||||
|
||||
coap_register_response_handler(ctx, message_handler);
|
||||
|
||||
if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) {
|
||||
ESP_LOGE(TAG, "CoAP server uri error");
|
||||
goto clean_up;
|
||||
}
|
||||
if (!coap_build_optlist(&uri))
|
||||
goto clean_up;
|
||||
|
||||
dst_addr = coap_get_address(&uri);
|
||||
if (!dst_addr)
|
||||
goto clean_up;
|
||||
|
||||
/*
|
||||
* Note that if the URI starts with just coap:// (not coaps://) the
|
||||
* session will still be plain text.
|
||||
*
|
||||
* coaps+tcp:// is NOT yet supported by the libcoap->mbedtls interface
|
||||
* so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above,
|
||||
* but the code is left in for completeness.
|
||||
*/
|
||||
if (uri.scheme == COAP_URI_SCHEME_COAPS || uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
|
||||
#ifndef CONFIG_MBEDTLS_TLS_CLIENT
|
||||
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
|
||||
goto clean_up;
|
||||
#endif /* CONFIG_MBEDTLS_TLS_CLIENT */
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PSK
|
||||
session = coap_start_psk_session(ctx, dst_addr, &uri);
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PSK */
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PKI
|
||||
session = coap_start_pki_session(ctx, dst_addr, &uri);
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
} else {
|
||||
session = coap_new_client_session(ctx, NULL, dst_addr,
|
||||
uri.scheme == COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
|
||||
COAP_PROTO_UDP);
|
||||
}
|
||||
if (!session) {
|
||||
ESP_LOGE(TAG, "coap_new_client_session() failed");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
request = coap_new_pdu(coap_is_mcast(dst_addr) ? COAP_MESSAGE_NON : COAP_MESSAGE_CON,
|
||||
COAP_REQUEST_CODE_GET, session);
|
||||
if (!request) {
|
||||
ESP_LOGE(TAG, "coap_new_pdu() failed");
|
||||
goto clean_up;
|
||||
}
|
||||
request->type = COAP_MESSAGE_CON;
|
||||
request->tid = coap_new_message_id(session);
|
||||
request->code = COAP_REQUEST_GET;
|
||||
/* Add in an unique token */
|
||||
coap_session_new_token(session, &tokenlength, token);
|
||||
coap_add_token(request, tokenlength, token);
|
||||
|
||||
/*
|
||||
* To make this a POST, you will need to do the following
|
||||
* Change COAP_REQUEST_CODE_GET to COAP_REQUEST_CODE_POST for coap_new_pdu()
|
||||
* Add in here a Content-Type Option based on the format of the POST text. E.G. for JSON
|
||||
* u_char buf[4];
|
||||
* coap_insert_optlist(&optlist,
|
||||
* coap_new_optlist(COAP_OPTION_CONTENT_FORMAT,
|
||||
* coap_encode_var_safe (buf, sizeof (buf),
|
||||
* COAP_MEDIATYPE_APPLICATION_JSON));
|
||||
* Add in here the POST data of length length. E.G.
|
||||
* coap_add_data_large_request(session, request length, data, NULL, NULL);
|
||||
*/
|
||||
|
||||
coap_add_optlist_pdu(request, &optlist);
|
||||
|
||||
resp_wait = 1;
|
||||
@@ -405,35 +449,37 @@ static void coap_example_client(void *p)
|
||||
wait_ms = COAP_DEFAULT_TIME_SEC * 1000;
|
||||
|
||||
while (resp_wait) {
|
||||
int result = coap_run_once(ctx, wait_ms > 1000 ? 1000 : wait_ms);
|
||||
int result = coap_io_process(ctx, wait_ms > 1000 ? 1000 : wait_ms);
|
||||
if (result >= 0) {
|
||||
if (result >= wait_ms) {
|
||||
ESP_LOGE(TAG, "select timeout");
|
||||
ESP_LOGE(TAG, "No response from server");
|
||||
break;
|
||||
} else {
|
||||
wait_ms -= result;
|
||||
}
|
||||
}
|
||||
}
|
||||
clean_up:
|
||||
if (optlist) {
|
||||
coap_delete_optlist(optlist);
|
||||
optlist = NULL;
|
||||
for(int countdown = 10; countdown >= 0; countdown--) {
|
||||
ESP_LOGI(TAG, "%d... ", countdown);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (session) {
|
||||
coap_session_release(session);
|
||||
}
|
||||
if (ctx) {
|
||||
coap_free_context(ctx);
|
||||
}
|
||||
coap_cleanup();
|
||||
/*
|
||||
* change the following line to something like sleep(2)
|
||||
* if you want the request to continually be sent
|
||||
*/
|
||||
break;
|
||||
ESP_LOGI(TAG, "Starting again!");
|
||||
}
|
||||
|
||||
clean_up:
|
||||
if (optlist) {
|
||||
coap_delete_optlist(optlist);
|
||||
optlist = NULL;
|
||||
}
|
||||
if (session) {
|
||||
coap_session_release(session);
|
||||
}
|
||||
if (ctx) {
|
||||
coap_free_context(ctx);
|
||||
}
|
||||
coap_cleanup();
|
||||
|
||||
ESP_LOGI(TAG, "Finished");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
|
||||
CONFIG_MBEDTLS_PSK_MODES=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
|
||||
CONFIG_LWIP_NETBUF_RECVINFO=y
|
||||
|
||||
@@ -40,6 +40,9 @@ Component config --->
|
||||
CoAP Configuration --->
|
||||
* Set encryption method definition, PSK (default) or PKI
|
||||
* Enable CoAP debugging if required
|
||||
High resolution timer (esp_timer) --->
|
||||
* Hardware timer to use for esp_timer - change if required (FRC2 for QEMU)
|
||||
|
||||
|
||||
### Build and Flash
|
||||
|
||||
@@ -77,7 +80,11 @@ If a CoAP client queries the `/Espressif` resource, CoAP server will return `"He
|
||||
until a CoAP client does a PUT with different data.
|
||||
|
||||
## libcoap Documentation
|
||||
This can be found at https://libcoap.net/doc/reference/4.2.0/
|
||||
This can be found at [libcoap Documentation](https://libcoap.net/documentation.html).
|
||||
The current API is 4.3.0.
|
||||
|
||||
## libcoap Specific Issues
|
||||
These can be raised at [libcoap Issues](https://github.com/obgm/libcoap/issues).
|
||||
|
||||
## Troubleshooting
|
||||
* Please make sure CoAP client fetchs or puts data under path: `/Espressif` or
|
||||
|
||||
@@ -8,4 +8,54 @@ menu "Example CoAP Server Configuration"
|
||||
The Preshared Key to use to encrypt the communicatons. The same key must be
|
||||
used at both ends of the CoAP connection, and the CoaP client must request
|
||||
an URI prefixed with coaps:// instead of coap:// for DTLS to be used.
|
||||
|
||||
choice EXAMPLE_COAP_MCAST_IP_MODE
|
||||
prompt "Receive Multicast IP type"
|
||||
help
|
||||
Example can receive multicast IPV4, IPV6, both or none.
|
||||
|
||||
config EXAMPLE_COAP_MCAST_NONE
|
||||
bool "None"
|
||||
|
||||
config EXAMPLE_COAP_MCAST_IPV4_V6
|
||||
bool "IPV4 & IPV6"
|
||||
select EXAMPLE_COAP_MCAST_IPV4
|
||||
select EXAMPLE_COAP_MCAST_IPV6
|
||||
|
||||
config EXAMPLE_COAP_MCAST_IPV4_ONLY
|
||||
bool "IPV4"
|
||||
select EXAMPLE_COAP_MCAST_IPV4
|
||||
|
||||
config EXAMPLE_COAP_MCAST_IPV6_ONLY
|
||||
bool "IPV6"
|
||||
select EXAMPLE_COAP_MCAST_IPV6
|
||||
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_COAP_MCAST_IPV4
|
||||
bool
|
||||
config EXAMPLE_COAP_MCAST_IPV6
|
||||
bool
|
||||
select EXAMPLE_CONNECT_IPV6 if IDF_TARGET_ESP32
|
||||
|
||||
config EXAMPLE_COAP_MULTICAST_IPV4_ADDR
|
||||
string "CoAP Multicast IPV4 Address (receive)"
|
||||
default "224.0.1.187"
|
||||
depends on EXAMPLE_COAP_MCAST_IPV4
|
||||
help
|
||||
IPV4 multicast address to receive multicast traffic on.
|
||||
|
||||
The default CoAP IPV4 address is 224.0.1.187.
|
||||
|
||||
config EXAMPLE_COAP_MULTICAST_IPV6_ADDR
|
||||
string "CoAP Multicast IPV6 Address (receive)"
|
||||
default "FF02::FD"
|
||||
depends on EXAMPLE_COAP_MCAST_IPV6
|
||||
help
|
||||
IPV6 multicast address to receive multicast traffic on.
|
||||
|
||||
The default CoAP FF02::FD address is a link-local multicast address.
|
||||
Consult IPV6 specifications or documentation for information about
|
||||
meaning of different IPV6 multicast ranges.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
|
||||
VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
|
||||
BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
|
||||
ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
|
||||
DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
|
||||
DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
|
||||
bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
|
||||
A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
|
||||
AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
|
||||
1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
|
||||
vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
|
||||
AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
|
||||
A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
|
||||
BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
|
||||
A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
|
||||
LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
|
||||
5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
|
||||
d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
|
||||
euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
|
||||
S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
|
||||
PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
|
||||
MIICDzCCAbSgAwIBAgIIAbOUoVFDz/QwDAYIKoZIzj0EAwIFADBcMRAwDgYDVQQD
|
||||
EwdjZi1yb290MRQwEgYDVQQLEwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBz
|
||||
ZSBJb1QxDzANBgNVBAcTBk90dGF3YTELMAkGA1UEBhMCQ0EwHhcNMjAxMTExMTAz
|
||||
MDMzWhcNMjExMTExMTAzMDMzWjBaMQ4wDAYDVQQDEwVjZi1jYTEUMBIGA1UECxML
|
||||
Q2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZPdHRh
|
||||
d2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7/3EXOZn
|
||||
GZXNEIj7LuQAMZ8lfRYSCnpME1TBjKjZPtVeztLtGWgkkLvIX11pAJcBh51cpi7Z
|
||||
fQtGpVE9CLOh6aNgMF4wHQYDVR0OBBYEFEvf57UcJhYYkx14twkeitd691fVMAsG
|
||||
A1UdDwQEAwIBBjAPBgNVHRMECDAGAQH/AgEBMB8GA1UdIwQYMBaAFAsi3KbVERiK
|
||||
JzFCfC/GVrYksGzEMAwGCCqGSM49BAMCBQADRwAwRAIgc5nVF/5Pip0XB17IZXqi
|
||||
V84FXanWdn9Z0SiPdpOgvZMCIH13vL9tkCCjPN3tg3TYRY/bzyGohFGBcTrrEtUr
|
||||
rVIm
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB4DCCAYWgAwIBAgIIQR8ro8AQ02AwDAYIKoZIzj0EAwIFADBcMRAwDgYDVQQD
|
||||
EwdjZi1yb290MRQwEgYDVQQLEwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBz
|
||||
ZSBJb1QxDzANBgNVBAcTBk90dGF3YTELMAkGA1UEBhMCQ0EwHhcNMjAxMTExMTAz
|
||||
MDMyWhcNMjExMTExMTAzMDMyWjBcMRAwDgYDVQQDEwdjZi1yb290MRQwEgYDVQQL
|
||||
EwtDYWxpZm9ybml1bTEUMBIGA1UEChMLRWNsaXBzZSBJb1QxDzANBgNVBAcTBk90
|
||||
dGF3YTELMAkGA1UEBhMCQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATZ1BRM
|
||||
T1//Fzh9sneRZNwS4kgCxN1PvgwT271qCpYqyxnjLEa38AP1IAanhpiD/OkVc0Zd
|
||||
7NgDPCw7n94EULMyoy8wLTAdBgNVHQ4EFgQUCyLcptURGIonMUJ8L8ZWtiSwbMQw
|
||||
DAYDVR0TBAUwAwEB/zAMBggqhkjOPQQDAgUAA0cAMEQCIAdLEgcUWdpAl9jwdJiz
|
||||
/cHW7/CBIWEvqiQfzE+XLyLOAiAvuxSdOtSDjh2aC5qEjUCH8CSKCxWB74j23tmp
|
||||
aqPH4A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,70 +1,13 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 47 (0x2f)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority
|
||||
Validity
|
||||
Not Before: Jun 7 08:06:49 2017 GMT
|
||||
Not After : Jun 5 08:06:49 2027 GMT
|
||||
Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:c9:d8:e2:e0:75:91:83:87:d8:c8:80:c6:20:4d:
|
||||
e9:14:24:30:98:33:53:fa:56:0e:ec:9a:43:7f:87:
|
||||
a9:22:94:26:06:c7:ac:b5:d9:ec:55:06:81:b7:0d:
|
||||
c9:24:51:49:fa:47:fb:4b:4e:fc:ed:75:8a:e1:28:
|
||||
32:bc:c5:e0:4c:45:c4:58:60:15:67:1e:6b:40:19:
|
||||
3f:f0:ab:92:61:92:2d:71:10:2e:f2:eb:bc:81:2f:
|
||||
5a:3b:74:ca:5f:fd:e0:ee:d1:d9:07:6a:6c:20:c0:
|
||||
07:88:b4:8b:0f:ad:1e:c9:4f:7c:11:98:37:89:15:
|
||||
de:24:b1:11:1a:7c:97:4a:cf:f3:c8:cb:79:9e:9c:
|
||||
c3:71:da:a6:94:97:f5:95:fd:61:06:44:e2:3f:12:
|
||||
43:0b:1d:33:48:91:d2:ce:4f:97:a1:ed:6a:30:c7:
|
||||
5d:98:b5:6e:0a:b7:4f:d9:03:ec:80:76:09:b0:40:
|
||||
a1:a1:af:ab:2a:59:c4:0f:56:22:bc:be:14:be:18:
|
||||
df:10:7d:5d:22:bf:e5:04:77:7a:75:6b:3e:eb:6d:
|
||||
20:a1:a7:60:d4:f1:87:9d:9f:60:b9:d3:db:2c:25:
|
||||
f4:91:4a:f1:d2:40:e5:a1:10:88:a0:41:5a:98:40:
|
||||
ca:15:d7:e3:e6:3e:c0:6a:d5:46:b2:b4:90:b4:ae:
|
||||
3b:e3
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
URI:http://www.example.com/example_ca.crl
|
||||
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
a4:25:21:51:0b:22:6c:63:8d:a9:c1:4f:04:33:69:79:34:f0:
|
||||
36:dd:8f:6a:27:5f:07:a2:1d:ef:8b:f0:96:e6:e7:a3:b8:3b:
|
||||
85:5e:3f:26:43:8a:8e:95:58:9c:a6:db:9c:51:bf:ea:53:16:
|
||||
3e:c1:a8:11:1a:c6:cf:0e:a1:17:18:64:d2:05:f1:c0:9c:a6:
|
||||
2b:16:c4:29:54:03:d2:17:bd:15:74:d6:ad:8a:8f:2d:cc:27:
|
||||
3b:88:88:f2:ea:d0:a2:cb:e9:42:57:df:26:9f:8a:a2:02:2f:
|
||||
35:b6:19:1d:26:43:44:af:12:4b:bc:b9:84:50:02:fd:1d:fa:
|
||||
50:e8
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
|
||||
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
|
||||
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
|
||||
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
|
||||
ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
|
||||
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT
|
||||
ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
|
||||
Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG
|
||||
IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy
|
||||
vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL
|
||||
D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS
|
||||
zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6
|
||||
dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ
|
||||
tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug
|
||||
KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG
|
||||
SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4
|
||||
O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0
|
||||
1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo
|
||||
MIICAzCCAaagAwIBAgIJANqCHDjOKHh+MAwGCCqGSM49BAMCBQAwWjEOMAwGA1UE
|
||||
AxMFY2YtY2ExFDASBgNVBAsTC0NhbGlmb3JuaXVtMRQwEgYDVQQKEwtFY2xpcHNl
|
||||
IElvVDEPMA0GA1UEBxMGT3R0YXdhMQswCQYDVQQGEwJDQTAeFw0yMDExMTExMDMw
|
||||
MzRaFw0yMTExMTExMDMwMzRaMF4xEjAQBgNVBAMTCWNmLXNlcnZlcjEUMBIGA1UE
|
||||
CxMLQ2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZP
|
||||
dHRhd2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+obM
|
||||
gHmMB7zS4KArciXPD7CrvgEYqlnAf7NOTdb54RbTr4qEpPL+OJ6Pg8VhrF4hGEne
|
||||
T6Aa4qqpmTkxmfT0vqNPME0wHQYDVR0OBBYEFE4XpfFad+F3+RcwI+s1cmJbTZWG
|
||||
MAsGA1UdDwQEAwIHgDAfBgNVHSMEGDAWgBRL3+e1HCYWGJMdeLcJHorXevdX1TAM
|
||||
BggqhkjOPQQDAgUAA0kAMEYCIQCEo+O5zqYKdwi/ElB4wfNVIf76P1OhIXAT5CHc
|
||||
3ebBPQIhAN6UhCgQ0av6kf7INCazV3KmN7HmPXARaY4YKWsRwsg+
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,27 +1,4 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAydji4HWRg4fYyIDGIE3pFCQwmDNT+lYO7JpDf4epIpQmBses
|
||||
tdnsVQaBtw3JJFFJ+kf7S0787XWK4SgyvMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu
|
||||
8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSLD60eyU98EZg3iRXeJLERGnyXSs/zyMt5
|
||||
npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHSzk+Xoe1qMMddmLVuCrdP2QPsgHYJsECh
|
||||
oa+rKlnED1YivL4UvhjfEH1dIr/lBHd6dWs+620goadg1PGHnZ9gudPbLCX0kUrx
|
||||
0kDloRCIoEFamEDKFdfj5j7AatVGsrSQtK474wIDAQABAoIBAQC2kGDEPBJdMSW2
|
||||
VCLfXRiPixwYzXQLXIMrJWwfkQg9qlmqkDd6U50aWkRA2UswegW7RhfYSZ0i+cmf
|
||||
VMhvTVpOIlwwwtcY6b5/v1bBy60eaySGuuh79xQMlFO8qynQIMStvUfbGTqrdIRb
|
||||
9VBB4YeS9T12fILejtTZwv2BQ2dj1Y1SCay6Ri85UzJqSClRKgHISybvVdLNjPvP
|
||||
0TRFBr57zyjL6WE8teKiKchzQko2u86No5uBCdKGsrAkrsdcR0YqlM/pZxd3VKNm
|
||||
+eny0k+dZZlvcPxzkzP4hEp9+Rw5rP9/s3s/cCwvuuC5JO32ATBWKCbTvPv/XPDb
|
||||
MdSJtOshAoGBAPzk0eswkcbFYtpnpBNmBAr1dtAdW1lfjUI2ucMMwt7Wns0P/tt+
|
||||
gq6Hi1wTaGP0l/dIECgeHwjtWj31ZJjQtFJ1y/kafxo4o9cA8vCydpdvSZaldAfg
|
||||
sbLlDTDYzEpelaDIbNQBBXFoC5U9JlBhBsIFCL5Z8ZuIeFPsb7t5wwuHAoGBAMxT
|
||||
jyWfNm1uNxp1xgCnrRsLPQPVnURrSFAqcHrECqRu3F7sozTN7q/cZViemxPvVDGQ
|
||||
p9c+9bHwaYvW4trO5qDHJ++gGwm5L52bMAY1VUfeTt67fqrey43XpdmzcTX1V9Uj
|
||||
QWawPUCSDzFjL1MjfCIejtyYf5ash53vj+T8r/vFAoGAA/OPVB1uKazr3n3AEo2F
|
||||
gqZTNO1AgCT+EArK3EFWyiSQVqPpV4SihheYFdg3yVgJB9QYbIgL9BfBUTaEW97m
|
||||
8mLkzP+c/Mvlw3ZAVYJ0V+llPPVY2saoACOUES9SAdd4fwqiqK1baGo3xB0wfBEI
|
||||
CgAKIu9E1ylKuAT5ufQtGAECgYEAtP/kU5h5N3El4QupTdU7VDSdZTMqsHw0v8cI
|
||||
gsf9AXKvRmtrnBA8u46KPHmruHoO5CVXeSZtsaXdaaH+rYQQ6yXg67WxnehtFLlv
|
||||
TmCaXiLBTS9cYvMf8FOyuGnsBLeEietEOTov2G5KhR5uwsAxa2wUc7endor5S9/2
|
||||
YQuyvV0CgYALbiFpILd5l1ip65eE6JdA3hfttUbV2j2NSW12ej69vqbeOfaSgNse
|
||||
uYCcXFsBbQPhNPwA+4d1oCe8SyXZg1f7gE812z2Tyr/3vdVnNZlitoxhsHmGiyS7
|
||||
gZdaTYCb78l9z0EBdaCVvA16owEle4SR6f9eCwzSI0WPOUra+x/hrA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCLBQT66xp2w4+1K+Ai
|
||||
/TXEC8tQZBxl9brFyK4F7AQNGw==
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -29,15 +29,10 @@
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#if 1
|
||||
/* Needed until coap_dtls.h becomes a part of libcoap proper */
|
||||
#include "libcoap.h"
|
||||
#include "coap_dtls.h"
|
||||
#endif
|
||||
#include "coap.h"
|
||||
#include "coap3/coap.h"
|
||||
|
||||
/* The examples use simple Pre-Shared-Key configuration that you can set via
|
||||
'make menuconfig'.
|
||||
'idf.py menuconfig'.
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key"
|
||||
@@ -49,7 +44,7 @@
|
||||
#define EXAMPLE_COAP_PSK_KEY CONFIG_EXAMPLE_COAP_PSK_KEY
|
||||
|
||||
/* The examples use CoAP Logging Level that
|
||||
you can set via 'make menuconfig'.
|
||||
you can set via 'idf.py menuconfig'.
|
||||
|
||||
If you'd rather not, just change the below entry to a value
|
||||
that is between 0 and 7 with
|
||||
@@ -67,8 +62,10 @@ static int espressif_data_len = 0;
|
||||
Server cert, taken from coap_server.crt
|
||||
Server key, taken from coap_server.key
|
||||
|
||||
The PEM, CRT and KEY file are examples taken from the wpa2 enterprise
|
||||
example.
|
||||
The PEM, CRT and KEY file are examples taken from
|
||||
https://github.com/eclipse/californium/tree/master/demo-certs/src/main/resources
|
||||
as the Certificate test (by default) for the coap_client is against the
|
||||
californium server.
|
||||
|
||||
To embed it in the app binary, the PEM, CRT and KEY file is named
|
||||
in the component.mk COMPONENT_EMBED_TXTFILES variable.
|
||||
@@ -87,39 +84,42 @@ extern uint8_t server_key_end[] asm("_binary_coap_server_key_end");
|
||||
* The resource handler
|
||||
*/
|
||||
static void
|
||||
hnd_espressif_get(coap_context_t *ctx, coap_resource_t *resource,
|
||||
hnd_espressif_get(coap_resource_t *resource,
|
||||
coap_session_t *session,
|
||||
coap_pdu_t *request, coap_binary_t *token,
|
||||
coap_string_t *query, coap_pdu_t *response)
|
||||
const coap_pdu_t *request,
|
||||
const coap_string_t *query,
|
||||
coap_pdu_t *response)
|
||||
{
|
||||
coap_add_data_blocked_response(resource, session, request, response, token,
|
||||
COAP_MEDIATYPE_TEXT_PLAIN, 0,
|
||||
(size_t)espressif_data_len,
|
||||
(const u_char *)espressif_data);
|
||||
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
|
||||
coap_add_data_large_response(resource, session, request, response,
|
||||
query, COAP_MEDIATYPE_TEXT_PLAIN, 60, 0,
|
||||
(size_t)espressif_data_len,
|
||||
(const u_char *)espressif_data,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
hnd_espressif_put(coap_context_t *ctx,
|
||||
coap_resource_t *resource,
|
||||
hnd_espressif_put(coap_resource_t *resource,
|
||||
coap_session_t *session,
|
||||
coap_pdu_t *request,
|
||||
coap_binary_t *token,
|
||||
coap_string_t *query,
|
||||
const coap_pdu_t *request,
|
||||
const coap_string_t *query,
|
||||
coap_pdu_t *response)
|
||||
{
|
||||
size_t size;
|
||||
unsigned char *data;
|
||||
size_t offset;
|
||||
size_t total;
|
||||
const unsigned char *data;
|
||||
|
||||
coap_resource_notify_observers(resource, NULL);
|
||||
|
||||
if (strcmp (espressif_data, INITIAL_DATA) == 0) {
|
||||
response->code = COAP_RESPONSE_CODE(201);
|
||||
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
|
||||
} else {
|
||||
response->code = COAP_RESPONSE_CODE(204);
|
||||
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
|
||||
}
|
||||
|
||||
/* coap_get_data() sets size to 0 on error */
|
||||
(void)coap_get_data(request, &size, &data);
|
||||
/* coap_get_data_large() sets size to 0 on error */
|
||||
(void)coap_get_data_large(request, &size, &data, &offset, &total);
|
||||
|
||||
if (size == 0) { /* re-init */
|
||||
snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
|
||||
@@ -131,18 +131,16 @@ hnd_espressif_put(coap_context_t *ctx,
|
||||
}
|
||||
|
||||
static void
|
||||
hnd_espressif_delete(coap_context_t *ctx,
|
||||
coap_resource_t *resource,
|
||||
hnd_espressif_delete(coap_resource_t *resource,
|
||||
coap_session_t *session,
|
||||
coap_pdu_t *request,
|
||||
coap_binary_t *token,
|
||||
coap_string_t *query,
|
||||
const coap_pdu_t *request,
|
||||
const coap_string_t *query,
|
||||
coap_pdu_t *response)
|
||||
{
|
||||
coap_resource_notify_observers(resource, NULL);
|
||||
snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
|
||||
espressif_data_len = strlen(espressif_data);
|
||||
response->code = COAP_RESPONSE_CODE(202);
|
||||
coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PKI
|
||||
@@ -163,6 +161,18 @@ verify_cn_callback(const char *cn,
|
||||
}
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PKI */
|
||||
|
||||
static void
|
||||
coap_log_handler (coap_log_t level, const char *message)
|
||||
{
|
||||
uint32_t esp_level = ESP_LOG_INFO;
|
||||
char *cp = strchr(message, '\n');
|
||||
|
||||
if (cp)
|
||||
ESP_LOG_LEVEL(esp_level, TAG, "%.*s", (int)(cp-message), message);
|
||||
else
|
||||
ESP_LOG_LEVEL(esp_level, TAG, "%s", message);
|
||||
}
|
||||
|
||||
static void coap_example_server(void *p)
|
||||
{
|
||||
coap_context_t *ctx = NULL;
|
||||
@@ -171,6 +181,7 @@ static void coap_example_server(void *p)
|
||||
|
||||
snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
|
||||
espressif_data_len = strlen(espressif_data);
|
||||
coap_set_log_handler(coap_log_handler);
|
||||
coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
|
||||
|
||||
while (1) {
|
||||
@@ -179,15 +190,16 @@ static void coap_example_server(void *p)
|
||||
|
||||
/* Prepare the CoAP server socket */
|
||||
coap_address_init(&serv_addr);
|
||||
serv_addr.addr.sin.sin_family = AF_INET;
|
||||
serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
|
||||
serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT);
|
||||
serv_addr.addr.sin6.sin6_family = AF_INET6;
|
||||
serv_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
|
||||
|
||||
ctx = coap_new_context(NULL);
|
||||
if (!ctx) {
|
||||
ESP_LOGE(TAG, "coap_new_context() failed");
|
||||
continue;
|
||||
}
|
||||
coap_context_set_block_mode(ctx,
|
||||
COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY);
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PSK
|
||||
/* Need PSK setup before we set up endpoints */
|
||||
coap_context_set_psk(ctx, "CoAP",
|
||||
@@ -196,6 +208,7 @@ static void coap_example_server(void *p)
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PSK */
|
||||
|
||||
#ifdef CONFIG_COAP_MBEDTLS_PKI
|
||||
/* Need PKI setup before we set up endpoints */
|
||||
unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
|
||||
unsigned int server_crt_bytes = server_crt_end - server_crt_start;
|
||||
unsigned int server_key_bytes = server_key_end - server_key_start;
|
||||
@@ -214,7 +227,7 @@ static void coap_example_server(void *p)
|
||||
* define what checking actually takes place.
|
||||
*/
|
||||
dtls_pki.verify_peer_cert = 1;
|
||||
dtls_pki.require_peer_cert = 1;
|
||||
dtls_pki.check_common_ca = 1;
|
||||
dtls_pki.allow_self_signed = 1;
|
||||
dtls_pki.allow_expired_certs = 1;
|
||||
dtls_pki.cert_chain_validation = 1;
|
||||
@@ -256,7 +269,7 @@ static void coap_example_server(void *p)
|
||||
/* This is not critical as unencrypted support is still available */
|
||||
ESP_LOGI(TAG, "MbedTLS (D)TLS Server Mode not configured");
|
||||
#else /* CONFIG_MBEDTLS_TLS_SERVER */
|
||||
serv_addr.addr.sin.sin_port = htons(COAPS_DEFAULT_PORT);
|
||||
serv_addr.addr.sin6.sin6_port = htons(COAPS_DEFAULT_PORT);
|
||||
ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_DTLS);
|
||||
if (!ep) {
|
||||
ESP_LOGE(TAG, "dtls: coap_new_endpoint() failed");
|
||||
@@ -267,7 +280,7 @@ static void coap_example_server(void *p)
|
||||
/* This is not critical as unencrypted support is still available */
|
||||
ESP_LOGI(TAG, "MbedTLS (D)TLS Server Mode not configured");
|
||||
}
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PSK CONFIG_COAP_MBEDTLS_PKI */
|
||||
#endif /* CONFIG_COAP_MBEDTLS_PSK || CONFIG_COAP_MBEDTLS_PKI */
|
||||
resource = coap_resource_init(coap_make_str_const("Espressif"), 0);
|
||||
if (!resource) {
|
||||
ESP_LOGE(TAG, "coap_resource_init() failed");
|
||||
@@ -280,10 +293,26 @@ static void coap_example_server(void *p)
|
||||
coap_resource_set_get_observable(resource, 1);
|
||||
coap_add_resource(ctx, resource);
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV4) || defined(CONFIG_EXAMPLE_COAP_MCAST_IPV6)
|
||||
esp_netif_t *netif = NULL;
|
||||
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
|
||||
char buf[8];
|
||||
netif = esp_netif_next(netif);
|
||||
esp_netif_get_netif_impl_name(netif, buf);
|
||||
#if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV4)
|
||||
coap_join_mcast_group_intf(ctx, CONFIG_EXAMPLE_COAP_MULTICAST_IPV4_ADDR, buf);
|
||||
#endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV4 */
|
||||
#if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV6)
|
||||
/* When adding IPV6 esp-idf requires ifname param to be filled in */
|
||||
coap_join_mcast_group_intf(ctx, CONFIG_EXAMPLE_COAP_MULTICAST_IPV6_ADDR, buf);
|
||||
#endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV6 */
|
||||
}
|
||||
#endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV4 || CONFIG_EXAMPLE_COAP_MCAST_IPV6 */
|
||||
|
||||
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
|
||||
|
||||
while (1) {
|
||||
int result = coap_run_once(ctx, wait_ms);
|
||||
int result = coap_io_process(ctx, wait_ms);
|
||||
if (result < 0) {
|
||||
break;
|
||||
} else if (result && (unsigned)result < wait_ms) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
|
||||
CONFIG_MBEDTLS_PSK_MODES=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
|
||||
CONFIG_LWIP_NETBUF_RECVINFO=y
|
||||
|
||||
@@ -603,7 +603,7 @@ static void http_native_request(void)
|
||||
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
|
||||
esp_http_client_get_status_code(client),
|
||||
esp_http_client_get_content_length(client));
|
||||
ESP_LOG_BUFFER_HEX(TAG, output_buffer, strlen(output_buffer));
|
||||
ESP_LOG_BUFFER_HEX(TAG, output_buffer, data_read);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to read response");
|
||||
}
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
|
||||
MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
|
||||
AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
|
||||
jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
|
||||
Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
|
||||
U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
|
||||
gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
|
||||
/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
|
||||
oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
|
||||
BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
|
||||
ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
|
||||
p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
|
||||
AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
|
||||
Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
|
||||
LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
|
||||
r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
|
||||
ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
|
||||
S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
|
||||
qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
|
||||
O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
|
||||
UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
|
||||
----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -28,12 +28,12 @@ Sample output:
|
||||
After you've tested the name resolution, run:
|
||||
|
||||
```
|
||||
python scripts/esp_local_ctrl.py
|
||||
python scripts/esp_local_ctrl.py --sec_ver 0
|
||||
```
|
||||
Sample output:
|
||||
|
||||
```
|
||||
python scripts/esp_local_ctrl.py
|
||||
python scripts/esp_local_ctrl.py --sec_ver 0
|
||||
|
||||
==== Acquiring properties information ====
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ def test_examples_esp_local_ctrl(env, extra_data):
|
||||
# Running mDNS services in docker is not a trivial task. Therefore, the script won't connect to the host name but
|
||||
# to IP address. However, the certificates were generated for the host name and will be rejected.
|
||||
cmd = ' '.join([sys.executable, os.path.join(idf_path, rel_project_path, 'scripts/esp_local_ctrl.py'),
|
||||
'--sec_ver 0',
|
||||
'--name', dut_ip,
|
||||
'--dont-check-hostname']) # don't reject the certificate because of the hostname
|
||||
esp_local_ctrl_log = os.path.join(idf_path, rel_project_path, 'esp_local_ctrl.log')
|
||||
|
||||
@@ -178,6 +178,11 @@ void start_esp_local_ctrl_service(void)
|
||||
.transport_config = {
|
||||
.httpd = &https_conf
|
||||
},
|
||||
.proto_sec = {
|
||||
.version = 0,
|
||||
.custom_handle = NULL,
|
||||
.pop = NULL,
|
||||
},
|
||||
.handlers = {
|
||||
/* User defined handler functions */
|
||||
.get_prop_values = get_property_values,
|
||||
|
||||
@@ -18,19 +18,28 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import ssl
|
||||
import struct
|
||||
import sys
|
||||
import textwrap
|
||||
from builtins import input
|
||||
|
||||
import proto
|
||||
import proto_lc
|
||||
from future.utils import tobytes
|
||||
|
||||
# The tools directory is already in the PATH in environment prepared by install.sh which would allow to import
|
||||
# esp_prov as file but not as complete module.
|
||||
sys.path.insert(0, os.path.join(os.environ['IDF_PATH'], 'tools/esp_prov'))
|
||||
import esp_prov # noqa: E402
|
||||
try:
|
||||
import esp_prov
|
||||
import security
|
||||
|
||||
except ImportError:
|
||||
idf_path = os.environ['IDF_PATH']
|
||||
sys.path.insert(0, idf_path + '/components/protocomm/python')
|
||||
sys.path.insert(1, idf_path + '/tools/esp_prov')
|
||||
|
||||
import esp_prov
|
||||
import security
|
||||
|
||||
# Set this to true to allow exceptions to be thrown
|
||||
config_throw_except = False
|
||||
@@ -118,6 +127,14 @@ def on_except(err):
|
||||
print(err)
|
||||
|
||||
|
||||
def get_security(secver, pop=None, verbose=False):
|
||||
if secver == 1:
|
||||
return security.Security1(pop, verbose)
|
||||
elif secver == 0:
|
||||
return security.Security0(verbose)
|
||||
return None
|
||||
|
||||
|
||||
def get_transport(sel_transport, service_name, check_hostname):
|
||||
try:
|
||||
tp = None
|
||||
@@ -140,29 +157,99 @@ def get_transport(sel_transport, service_name, check_hostname):
|
||||
return None
|
||||
|
||||
|
||||
def version_match(tp, expected, verbose=False):
|
||||
def version_match(tp, protover, verbose=False):
|
||||
try:
|
||||
response = tp.send_data('esp_local_ctrl/version', expected)
|
||||
return (response.lower() == expected.lower())
|
||||
response = tp.send_data('proto-ver', protover)
|
||||
|
||||
if verbose:
|
||||
print('proto-ver response : ', response)
|
||||
|
||||
# First assume this to be a simple version string
|
||||
if response.lower() == protover.lower():
|
||||
return True
|
||||
|
||||
try:
|
||||
# Else interpret this as JSON structure containing
|
||||
# information with versions and capabilities of both
|
||||
# provisioning service and application
|
||||
info = json.loads(response)
|
||||
if info['prov']['ver'].lower() == protover.lower():
|
||||
return True
|
||||
|
||||
except ValueError:
|
||||
# If decoding as JSON fails, it means that capabilities
|
||||
# are not supported
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
on_except(e)
|
||||
return None
|
||||
|
||||
|
||||
def get_all_property_values(tp):
|
||||
def has_capability(tp, capability='none', verbose=False):
|
||||
# Note : default value of `capability` argument cannot be empty string
|
||||
# because protocomm_httpd expects non zero content lengths
|
||||
try:
|
||||
response = tp.send_data('proto-ver', capability)
|
||||
|
||||
if verbose:
|
||||
print('proto-ver response : ', response)
|
||||
|
||||
try:
|
||||
# Interpret this as JSON structure containing
|
||||
# information with versions and capabilities of both
|
||||
# provisioning service and application
|
||||
info = json.loads(response)
|
||||
supported_capabilities = info['prov']['cap']
|
||||
if capability.lower() == 'none':
|
||||
# No specific capability to check, but capabilities
|
||||
# feature is present so return True
|
||||
return True
|
||||
elif capability in supported_capabilities:
|
||||
return True
|
||||
return False
|
||||
|
||||
except ValueError:
|
||||
# If decoding as JSON fails, it means that capabilities
|
||||
# are not supported
|
||||
return False
|
||||
|
||||
except RuntimeError as e:
|
||||
on_except(e)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def establish_session(tp, sec):
|
||||
try:
|
||||
response = None
|
||||
while True:
|
||||
request = sec.security_session(response)
|
||||
if request is None:
|
||||
break
|
||||
response = tp.send_data('esp_local_ctrl/session', request)
|
||||
if (response is None):
|
||||
return False
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
on_except(e)
|
||||
return None
|
||||
|
||||
|
||||
def get_all_property_values(tp, security_ctx):
|
||||
try:
|
||||
props = []
|
||||
message = proto.get_prop_count_request()
|
||||
message = proto_lc.get_prop_count_request(security_ctx)
|
||||
response = tp.send_data('esp_local_ctrl/control', message)
|
||||
count = proto.get_prop_count_response(response)
|
||||
count = proto_lc.get_prop_count_response(security_ctx, response)
|
||||
if count == 0:
|
||||
raise RuntimeError('No properties found!')
|
||||
indices = [i for i in range(count)]
|
||||
message = proto.get_prop_vals_request(indices)
|
||||
message = proto_lc.get_prop_vals_request(security_ctx, indices)
|
||||
response = tp.send_data('esp_local_ctrl/control', message)
|
||||
props = proto.get_prop_vals_response(response)
|
||||
props = proto_lc.get_prop_vals_response(security_ctx, response)
|
||||
if len(props) != count:
|
||||
raise RuntimeError('Incorrect count of properties!')
|
||||
raise RuntimeError('Incorrect count of properties!', len(props), count)
|
||||
for p in props:
|
||||
p['value'] = decode_prop_value(p, p['value'])
|
||||
return props
|
||||
@@ -171,20 +258,27 @@ def get_all_property_values(tp):
|
||||
return []
|
||||
|
||||
|
||||
def set_property_values(tp, props, indices, values, check_readonly=False):
|
||||
def set_property_values(tp, security_ctx, props, indices, values, check_readonly=False):
|
||||
try:
|
||||
if check_readonly:
|
||||
for index in indices:
|
||||
if prop_is_readonly(props[index]):
|
||||
raise RuntimeError('Cannot set value of Read-Only property')
|
||||
message = proto.set_prop_vals_request(indices, values)
|
||||
message = proto_lc.set_prop_vals_request(security_ctx, indices, values)
|
||||
response = tp.send_data('esp_local_ctrl/control', message)
|
||||
return proto.set_prop_vals_response(response)
|
||||
return proto_lc.set_prop_vals_response(security_ctx, response)
|
||||
except RuntimeError as e:
|
||||
on_except(e)
|
||||
return False
|
||||
|
||||
|
||||
def desc_format(*args):
|
||||
desc = ''
|
||||
for arg in args:
|
||||
desc += textwrap.fill(replace_whitespace=False, text=arg) + '\n'
|
||||
return desc
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
|
||||
@@ -199,6 +293,22 @@ if __name__ == '__main__':
|
||||
parser.add_argument('--name', dest='service_name', type=str,
|
||||
help='BLE Device Name / HTTP Server hostname or IP', default='')
|
||||
|
||||
parser.add_argument('--sec_ver', dest='secver', type=int, default=None,
|
||||
help=desc_format(
|
||||
'Protocomm security scheme used by the provisioning service for secure '
|
||||
'session establishment. Accepted values are :',
|
||||
'\t- 0 : No security',
|
||||
'\t- 1 : X25519 key exchange + AES-CTR encryption',
|
||||
'\t + Authentication using Proof of Possession (PoP)',
|
||||
'In case device side application uses IDF\'s provisioning manager, '
|
||||
'the compatible security version is automatically determined from '
|
||||
'capabilities retrieved via the version endpoint'))
|
||||
|
||||
parser.add_argument('--pop', dest='pop', type=str, default='',
|
||||
help=desc_format(
|
||||
'This specifies the Proof of possession (PoP) when security scheme 1 '
|
||||
'is used'))
|
||||
|
||||
parser.add_argument('--dont-check-hostname', action='store_true',
|
||||
# If enabled, the certificate won't be rejected for hostname mismatch.
|
||||
# This option is hidden because it should be used only for testing purposes.
|
||||
@@ -220,6 +330,31 @@ if __name__ == '__main__':
|
||||
print('---- Invalid transport ----')
|
||||
exit(1)
|
||||
|
||||
# If security version not specified check in capabilities
|
||||
if args.secver is None:
|
||||
# First check if capabilities are supported or not
|
||||
if not has_capability(obj_transport):
|
||||
print('Security capabilities could not be determined. Please specify \'--sec_ver\' explicitly')
|
||||
print('---- Invalid Security Version ----')
|
||||
exit(2)
|
||||
|
||||
# When no_sec is present, use security 0, else security 1
|
||||
args.secver = int(not has_capability(obj_transport, 'no_sec'))
|
||||
print('Security scheme determined to be :', args.secver)
|
||||
|
||||
if (args.secver != 0) and not has_capability(obj_transport, 'no_pop'):
|
||||
if len(args.pop) == 0:
|
||||
print('---- Proof of Possession argument not provided ----')
|
||||
exit(2)
|
||||
elif len(args.pop) != 0:
|
||||
print('---- Proof of Possession will be ignored ----')
|
||||
args.pop = ''
|
||||
|
||||
obj_security = get_security(args.secver, args.pop, False)
|
||||
if obj_security is None:
|
||||
print('---- Invalid Security Version ----')
|
||||
exit(2)
|
||||
|
||||
if args.version != '':
|
||||
print('\n==== Verifying protocol version ====')
|
||||
if not version_match(obj_transport, args.version, args.verbose):
|
||||
@@ -227,8 +362,15 @@ if __name__ == '__main__':
|
||||
exit(2)
|
||||
print('==== Verified protocol version successfully ====')
|
||||
|
||||
print('\n==== Starting Session ====')
|
||||
if not establish_session(obj_transport, obj_security):
|
||||
print('Failed to establish session. Ensure that security scheme and proof of possession are correct')
|
||||
print('---- Error in establishing session ----')
|
||||
exit(3)
|
||||
print('==== Session Established ====')
|
||||
|
||||
while True:
|
||||
properties = get_all_property_values(obj_transport)
|
||||
properties = get_all_property_values(obj_transport, obj_security)
|
||||
if len(properties) == 0:
|
||||
print('---- Error in reading property values ----')
|
||||
exit(4)
|
||||
@@ -245,7 +387,7 @@ if __name__ == '__main__':
|
||||
select = 0
|
||||
while True:
|
||||
try:
|
||||
inval = input("\nSelect properties to set (0 to re-read, 'q' to quit) : ")
|
||||
inval = input('\nSelect properties to set (0 to re-read, \'q\' to quit) : ')
|
||||
if inval.lower() == 'q':
|
||||
print('Quitting...')
|
||||
exit(5)
|
||||
@@ -274,5 +416,5 @@ if __name__ == '__main__':
|
||||
set_values += [value]
|
||||
set_indices += [select - 1]
|
||||
|
||||
if not set_property_values(obj_transport, properties, set_indices, set_values):
|
||||
if not set_property_values(obj_transport, obj_security, properties, set_indices, set_values):
|
||||
print('Failed to set values!')
|
||||
|
||||
@@ -36,35 +36,39 @@ constants_pb2 = _load_source('constants_pb2', idf_path + '/components/protocomm/
|
||||
local_ctrl_pb2 = _load_source('esp_local_ctrl_pb2', idf_path + '/components/esp_local_ctrl/python/esp_local_ctrl_pb2.py')
|
||||
|
||||
|
||||
def get_prop_count_request():
|
||||
def get_prop_count_request(security_ctx):
|
||||
req = local_ctrl_pb2.LocalCtrlMessage()
|
||||
req.msg = local_ctrl_pb2.TypeCmdGetPropertyCount
|
||||
payload = local_ctrl_pb2.CmdGetPropertyCount()
|
||||
req.cmd_get_prop_count.MergeFrom(payload)
|
||||
return req.SerializeToString()
|
||||
enc_cmd = security_ctx.encrypt_data(req.SerializeToString())
|
||||
return enc_cmd
|
||||
|
||||
|
||||
def get_prop_count_response(response_data):
|
||||
def get_prop_count_response(security_ctx, response_data):
|
||||
decrypt = security_ctx.decrypt_data(tobytes(response_data))
|
||||
resp = local_ctrl_pb2.LocalCtrlMessage()
|
||||
resp.ParseFromString(tobytes(response_data))
|
||||
resp.ParseFromString(decrypt)
|
||||
if (resp.resp_get_prop_count.status == 0):
|
||||
return resp.resp_get_prop_count.count
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def get_prop_vals_request(indices):
|
||||
def get_prop_vals_request(security_ctx, indices):
|
||||
req = local_ctrl_pb2.LocalCtrlMessage()
|
||||
req.msg = local_ctrl_pb2.TypeCmdGetPropertyValues
|
||||
payload = local_ctrl_pb2.CmdGetPropertyValues()
|
||||
payload.indices.extend(indices)
|
||||
req.cmd_get_prop_vals.MergeFrom(payload)
|
||||
return req.SerializeToString()
|
||||
enc_cmd = security_ctx.encrypt_data(req.SerializeToString())
|
||||
return enc_cmd
|
||||
|
||||
|
||||
def get_prop_vals_response(response_data):
|
||||
def get_prop_vals_response(security_ctx, response_data):
|
||||
decrypt = security_ctx.decrypt_data(tobytes(response_data))
|
||||
resp = local_ctrl_pb2.LocalCtrlMessage()
|
||||
resp.ParseFromString(tobytes(response_data))
|
||||
resp.ParseFromString(decrypt)
|
||||
results = []
|
||||
if (resp.resp_get_prop_vals.status == 0):
|
||||
for prop in resp.resp_get_prop_vals.props:
|
||||
@@ -77,7 +81,7 @@ def get_prop_vals_response(response_data):
|
||||
return results
|
||||
|
||||
|
||||
def set_prop_vals_request(indices, values):
|
||||
def set_prop_vals_request(security_ctx, indices, values):
|
||||
req = local_ctrl_pb2.LocalCtrlMessage()
|
||||
req.msg = local_ctrl_pb2.TypeCmdSetPropertyValues
|
||||
payload = local_ctrl_pb2.CmdSetPropertyValues()
|
||||
@@ -86,10 +90,12 @@ def set_prop_vals_request(indices, values):
|
||||
prop.index = i
|
||||
prop.value = v
|
||||
req.cmd_set_prop_vals.MergeFrom(payload)
|
||||
return req.SerializeToString()
|
||||
enc_cmd = security_ctx.encrypt_data(req.SerializeToString())
|
||||
return enc_cmd
|
||||
|
||||
|
||||
def set_prop_vals_response(response_data):
|
||||
def set_prop_vals_response(security_ctx, response_data):
|
||||
decrypt = security_ctx.decrypt_data(tobytes(response_data))
|
||||
resp = local_ctrl_pb2.LocalCtrlMessage()
|
||||
resp.ParseFromString(tobytes(response_data))
|
||||
resp.ParseFromString(decrypt)
|
||||
return (resp.resp_set_prop_vals.status == 0)
|
||||
@@ -4,3 +4,69 @@ Established HTTP/2 connection with https://http2.golang.org
|
||||
- Performs a GET on /clockstream
|
||||
- Performs a PUT on /ECHO
|
||||
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (9637) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (9637) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
Connecting to server
|
||||
Connection done
|
||||
[data-prvd] Sending 11 bytes
|
||||
[echo-response] HELLO WORLD
|
||||
[echo-response] Frame fully received
|
||||
[echo-response] Frame fully received
|
||||
[echo-response] Stream Closed
|
||||
[get-response] # ~1KB of junk to force browsers to start rendering immediately:
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
2021-09-07 08:22:10.73175944 +0000 UTC m=+530259.401042400
|
||||
|
||||
[get-response] Frame fully received
|
||||
[get-response] 2021-09-07 08:22:11.73205314 +0000 UTC m=+530260.401336111
|
||||
|
||||
[get-response] Frame fully received
|
||||
[get-response] 2021-09-07 08:22:12.7320106 +0000 UTC m=+530261.401293569
|
||||
|
||||
[get-response] Frame fully received
|
||||
[get-response] 2021-09-07 08:22:13.732013218 +0000 UTC m=+530262.401296183
|
||||
```
|
||||
|
||||
@@ -36,7 +36,10 @@ def is_test_server_available(): # type: () -> bool
|
||||
return False
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
# Disabling the Test in CI as the leaf certificate of http2.golang.org is expired from 8 July.
|
||||
# There is no timeline when the cert will be updated.
|
||||
# Disabling this test till an alternative is found for testing the http2 support.
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1', ignore=True)
|
||||
def test_examples_protocol_http2_request(env, extra_data): # type: (tiny_test_fw.Env.Env, None) -> None # pylint: disable=unused-argument
|
||||
"""
|
||||
steps: |
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
|
||||
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
|
||||
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
|
||||
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
|
||||
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
|
||||
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
|
||||
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
|
||||
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
|
||||
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
|
||||
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
|
||||
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
|
||||
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
|
||||
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
|
||||
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
||||
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,5 +1,111 @@
|
||||
# HTTP Request Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Uses a POSIX socket to make a very simple HTTP request.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (10557) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (10557) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
W (10577) wifi:<ba-add>idx:0 (ifx:0, ee:6d:19:60:f6:0e), tid:0, ssn:3, winSize:64
|
||||
I (10587) example: DNS lookup succeeded. IP=93.184.216.34
|
||||
I (10587) example: ... allocated socket
|
||||
I (10917) example: ... connected
|
||||
I (10917) example: ... socket send success
|
||||
I (10927) example: ... set socket receiving timeout success
|
||||
HTTP/1.0 200 OK
|
||||
Age: 317271
|
||||
Cache-Control: max-age=604800
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Date: Mon, 06 Sep 2021 08:09:49 GMT
|
||||
Etag: "3147526947+ident"
|
||||
Expires: Mon, 13 Sep 2021 08:09:49 GMT
|
||||
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
|
||||
Server: ECS (nyb/1D2B)
|
||||
Vary: Accept-Encoding
|
||||
X-Cache: HIT
|
||||
Content-Length: 1256
|
||||
Connection: close
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Example Domain</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #f0f0f2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
|
||||
}
|
||||
div {
|
||||
width: 600px;
|
||||
margin: 5em auto;
|
||||
padding: 2em;
|
||||
background-color: #fdfdff;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
|
||||
}
|
||||
a:link, a:visited {
|
||||
color: #38488f;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
div {
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Example Domain</h1>
|
||||
<p>This domain is for use in illustrative examples in documents. You may use this
|
||||
domain in literature without prior coordination or asking for permission.</p>
|
||||
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
I (11467) example: ... done reading from socket. Last read return=0 errno=128.
|
||||
I (11477) example: 10...
|
||||
I (12477) example: 9...
|
||||
I (13477) example: 8...
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ def test_examples_protocol_http_request(env, extra_data): # type: (tiny_test_fw
|
||||
ttfw_idf.log_performance('http_request_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# start test
|
||||
dut1.start_app()
|
||||
dut1.expect(re.compile(r'DNS lookup succeeded.'))
|
||||
dut1.expect(re.compile(r'DNS lookup succeeded.'), timeout=30)
|
||||
# check if connected or not
|
||||
dut1.expect(' ... connected', timeout=60)
|
||||
dut1.expect(' ... socket send success')
|
||||
|
||||
49
examples/protocols/http_server/advanced_tests/README.md
Normal file
49
examples/protocols/http_server/advanced_tests/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# HTTP server advanced tests
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
HTTP server example to perform some advanced tests
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (5561) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (5561) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5581) TESTS: Started HTTP server on port: '1234'
|
||||
I (5581) TESTS: Max URI handlers: '9'
|
||||
I (5581) TESTS: Max Open Sessions: '7'
|
||||
I (5591) TESTS: Max Header Length: '512'
|
||||
I (5591) TESTS: Max URI Length: '512'
|
||||
I (5601) TESTS: Max Stack Size: '4096'
|
||||
I (5601) TESTS: Registering basic handlers
|
||||
I (5601) TESTS: No of handlers = 9
|
||||
I (5611) TESTS: Success
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(captive_portal)
|
||||
8
examples/protocols/http_server/captive_portal/Makefile
Normal file
8
examples/protocols/http_server/captive_portal/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := captive_portal
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
96
examples/protocols/http_server/captive_portal/README.md
Normal file
96
examples/protocols/http_server/captive_portal/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Captive Portal Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates a simple captive portal that will redirect all DNS IP questions to point to the softAP and redirect all HTTP requests to the captive portal root page. Triggers captive portal (sign in) pop up on Android, iOS and Windows. Note that the example will not redirect HTTPS requests.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
* WiFi interface
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Set the Wi-Fi configuration.
|
||||
* Set `SoftAP SSID`
|
||||
* Set `SoftAP Password`
|
||||
* Set `Maximal STA connections`
|
||||
|
||||
When using the legacy GNU Make build system, set `Default serial port` under `Serial flasher config`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (733) example: Set up softAP with IP: 192.168.4.1
|
||||
I (743) example: wifi_init_softap finished. SSID:'esp32_ssid' password:'esp32_pwd'
|
||||
I (753) example: Starting server on port: '80'
|
||||
I (753) example: Registering URI handlers
|
||||
I (763) example_dns_redirect_server: Socket created
|
||||
I (763) example_dns_redirect_server: Socket bound, port 53
|
||||
I (773) example_dns_redirect_server: Waiting for data
|
||||
I (1873) wifi:new:<1,1>, old:<1,1>, ap:<1,1>, sta:<255,255>, prof:1
|
||||
I (1873) wifi:station: e8:84:a5:18:8f:80 join, AID=1, bgn, 40U
|
||||
I (2203) example: station e8:84:a5:18:8f:80 join, AID=1
|
||||
I (2833) example_dns_redirect_server: Received 50 bytes from 192.168.4.2 | DNS reply with len: 66
|
||||
I (2843) example_dns_redirect_server: Waiting for data
|
||||
I (3043) example_dns_redirect_server: Received 39 bytes from 192.168.4.2 | DNS reply with len: 55
|
||||
I (3043) example_dns_redirect_server: Waiting for data
|
||||
I (3043) example_dns_redirect_server: Received 42 bytes from 192.168.4.2 | DNS reply with len: 58
|
||||
I (3053) example_dns_redirect_server: Waiting for data
|
||||
W (3203) wifi:<ba-add>idx:4 (ifx:1, e8:84:a5:18:8f:80), tid:0, ssn:9, winSize:64
|
||||
I (3533) example: Redirecting to root
|
||||
I (5693) example_dns_redirect_server: Received 37 bytes from 192.168.4.2 | DNS reply with len: 53
|
||||
I (5693) example_dns_redirect_server: Waiting for data
|
||||
I (5783) example_dns_redirect_server: Received 46 bytes from 192.168.4.2 | DNS reply with len: 62
|
||||
I (5783) example_dns_redirect_server: Waiting for data
|
||||
I (6303) example_dns_redirect_server: Received 41 bytes from 192.168.4.2 | DNS reply with len: 57
|
||||
I (6303) example_dns_redirect_server: Waiting for data
|
||||
I (6303) example_dns_redirect_server: Received 41 bytes from 192.168.4.2 | DNS reply with len: 57
|
||||
I (6313) example_dns_redirect_server: Waiting for data
|
||||
I (6593) example: Redirecting to root
|
||||
I (9623) example: Redirecting to root
|
||||
I (12913) example: Redirecting to root
|
||||
I (13263) example_dns_redirect_server: Received 34 bytes from 192.168.4.2 | DNS reply with len: 50
|
||||
I (13273) example_dns_redirect_server: Waiting for data
|
||||
I (13273) example_dns_redirect_server: Received 34 bytes from 192.168.4.2 | DNS reply with len: 50
|
||||
I (13283) example_dns_redirect_server: Waiting for data
|
||||
I (16303) example_dns_redirect_server: Received 32 bytes from 192.168.4.2 | DNS reply with len: 48
|
||||
I (16303) example_dns_redirect_server: Waiting for data
|
||||
I (18073) example: Redirecting to root
|
||||
I (18273) example_dns_redirect_server: Received 34 bytes from 192.168.4.2 | DNS reply with len: 50
|
||||
I (18273) example_dns_redirect_server: Waiting for data
|
||||
I (18273) example_dns_redirect_server: Received 34 bytes from 192.168.4.2 | DNS reply with len: 50
|
||||
I (18283) example_dns_redirect_server: Waiting for data
|
||||
I (20683) example_dns_redirect_server: Received 42 bytes from 192.168.4.2 | DNS reply with len: 58
|
||||
I (20683) example_dns_redirect_server: Waiting for data
|
||||
I (20753) example: Redirecting to root
|
||||
I (21323) example: Redirecting to root
|
||||
I (22683) example_dns_redirect_server: Received 48 bytes from 192.168.4.2 | DNS reply with len: 64
|
||||
I (22693) example_dns_redirect_server: Waiting for data
|
||||
I (23443) example_dns_redirect_server: Received 48 bytes from 192.168.4.2 | DNS reply with len: 64
|
||||
I (23453) example_dns_redirect_server: Waiting for data
|
||||
I (23473) example: Serve root
|
||||
I (23503) example_dns_redirect_server: Received 48 bytes from 192.168.4.2 | DNS reply with len: 64
|
||||
I (23513) example_dns_redirect_server: Waiting for data
|
||||
```
|
||||
142
examples/protocols/http_server/captive_portal/example_test.py
Normal file
142
examples/protocols/http_server/captive_portal/example_test.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import http.client
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
|
||||
import tiny_test_fw
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
try:
|
||||
import wifi_tools
|
||||
except ImportError:
|
||||
wifi_tools_path = str(os.getenv('IDF_PATH')) + '/examples/provisioning/softap_prov/utils'
|
||||
if wifi_tools_path and wifi_tools_path not in sys.path:
|
||||
sys.path.insert(0, wifi_tools_path)
|
||||
import wifi_tools
|
||||
|
||||
|
||||
def test_redirect(ip, port): # type: (str, str) -> str # pylint: disable=unused-argument
|
||||
# Establish HTTP connection
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
uri = '/test'
|
||||
# GET response
|
||||
sess.request('GET', url=uri)
|
||||
resp = sess.getresponse()
|
||||
resp_hdrs = resp.getheaders()
|
||||
|
||||
if resp.status != 302:
|
||||
raise RuntimeError('Redirect failed, response status: {}'.format(resp.status))
|
||||
|
||||
for hdr in resp_hdrs:
|
||||
if hdr[0] == 'location':
|
||||
uri = hdr[1]
|
||||
|
||||
print('Redirected to uri: {}'.format(uri))
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
return uri
|
||||
|
||||
|
||||
def test_captive_page(ip, port, uri): # type: (str, str, str) -> bool # pylint: disable=unused-argument
|
||||
# Establish HTTP connection
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
# GET response
|
||||
sess.request('GET', url=uri)
|
||||
resp = sess.getresponse()
|
||||
|
||||
resp_data = resp.read().decode()
|
||||
search_str = 'Redirect to the captive portal'
|
||||
|
||||
if search_str not in resp_data:
|
||||
raise RuntimeError('Failed to match {} with data from captive portal: {}'.format(search_str, resp_data))
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return True
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_BT', ignore=True)
|
||||
def test_example_captive_portal(env, extra_data): # type: (tiny_test_fw.Env.Env, None) -> None # pylint: disable=unused-argument
|
||||
# Acquire DUT
|
||||
dut1 = env.get_dut('captive_portal', 'examples/protocols/http_server/captive_portal', dut_class=ttfw_idf.ESP32DUT)
|
||||
|
||||
# Get binary file
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'captive_portal.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('captive_portal_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
|
||||
# Upload binary and start testing
|
||||
dut1.start_app()
|
||||
|
||||
# Parse IP address of STA
|
||||
Utility.console_log('Waiting to connect with softAP')
|
||||
ap_ip = dut1.expect(re.compile(r'Set up softAP with IP: (\d+.\d+.\d+.\d+)'), timeout=60)[0]
|
||||
|
||||
[ssid, password] = dut1.expect(re.compile(r"wifi_init_softap finished. SSID:'(\S+)' password:'(\S+)'"), timeout=30)
|
||||
port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: '(\d+)'"), timeout=30)[0]
|
||||
|
||||
iface = wifi_tools.get_wiface_name()
|
||||
if iface is None:
|
||||
raise RuntimeError('Failed to get Wi-Fi interface on host')
|
||||
print('Interface name : ' + iface)
|
||||
print('SoftAP SSID : ' + ssid)
|
||||
print('SoftAP Password : ' + password)
|
||||
|
||||
try:
|
||||
ctrl = wifi_tools.wpa_cli(iface, reset_on_exit=True)
|
||||
print('Connecting to DUT SoftAP...')
|
||||
try:
|
||||
ip = ctrl.connect(ssid, password)
|
||||
except RuntimeError as err:
|
||||
Utility.console_log('error: {}'.format(err))
|
||||
try:
|
||||
got_ip = dut1.expect(re.compile(r'DHCP server assigned IP to a station, IP is: (\d+.\d+.\d+.\d+)'), timeout=60)
|
||||
Utility.console_log('got_ip: {}'.format(got_ip))
|
||||
got_ip = got_ip[0]
|
||||
if ip != got_ip:
|
||||
raise RuntimeError('SoftAP connected to another host! {} != {}'.format(ip, got_ip))
|
||||
except tiny_test_fw.DUT.ExpectTimeout:
|
||||
# print what is happening on DUT side
|
||||
Utility.console_log('in exception tiny_test_fw.DUT.ExpectTimeout')
|
||||
Utility.console_log(dut1.read())
|
||||
raise
|
||||
print('Connected to DUT SoftAP')
|
||||
|
||||
host_name = 'www.google.com'
|
||||
host = socket.gethostbyname(host_name)
|
||||
print('hostname: {} resolved to: {}'.format(host_name, host))
|
||||
if host != ap_ip:
|
||||
raise RuntimeError("DNS server failed to redirect question to the softAP's IP")
|
||||
|
||||
uri = test_redirect(ap_ip, port)
|
||||
test_captive_page(ap_ip, port, uri)
|
||||
finally:
|
||||
ctrl.reset()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_example_captive_portal() # pylint: disable=no-value-for-parameter
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "main.c" "dns_server.c"
|
||||
INCLUDE_DIRS "include"
|
||||
EMBED_FILES root.html)
|
||||
@@ -0,0 +1,20 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config ESP_WIFI_SSID
|
||||
string "SoftAP SSID"
|
||||
default "esp32_ssid"
|
||||
help
|
||||
SSID (network name) to set up the softAP with.
|
||||
|
||||
config ESP_WIFI_PASSWORD
|
||||
string "SoftAP Password"
|
||||
default "esp32_pwd"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use for the softAP.
|
||||
|
||||
config ESP_MAX_STA_CONN
|
||||
int "Maximal STA connections"
|
||||
default 4
|
||||
help
|
||||
Max number of the STA connects to AP.
|
||||
endmenu
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_EMBED_FILES := root.html
|
||||
247
examples/protocols/http_server/captive_portal/main/dns_server.c
Normal file
247
examples/protocols/http_server/captive_portal/main/dns_server.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/* Captive Portal 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 <sys/param.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#define DNS_PORT (53)
|
||||
#define DNS_MAX_LEN (256)
|
||||
|
||||
#define OPCODE_MASK (0x7800)
|
||||
#define QR_FLAG (1 << 7)
|
||||
#define QD_TYPE_A (0x0001)
|
||||
#define ANS_TTL_SEC (300)
|
||||
|
||||
static const char *TAG = "example_dns_redirect_server";
|
||||
|
||||
// DNS Header Packet
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint16_t qd_count;
|
||||
uint16_t an_count;
|
||||
uint16_t ns_count;
|
||||
uint16_t ar_count;
|
||||
} dns_header_t;
|
||||
|
||||
// DNS Question Packet
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
} dns_question_t;
|
||||
|
||||
// DNS Answer Packet
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint16_t ptr_offset;
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
uint32_t ttl;
|
||||
uint16_t addr_len;
|
||||
uint32_t ip_addr;
|
||||
} dns_answer_t;
|
||||
|
||||
/*
|
||||
Parse the name from the packet from the DNS name format to a regular .-seperated name
|
||||
returns the pointer to the next part of the packet
|
||||
*/
|
||||
static char *parse_dns_name(char *raw_name, char *parsed_name, size_t parsed_name_max_len)
|
||||
{
|
||||
|
||||
char *label = raw_name;
|
||||
char *name_itr = parsed_name;
|
||||
int name_len = 0;
|
||||
|
||||
do {
|
||||
int sub_name_len = *label;
|
||||
// (len + 1) since we are adding a '.'
|
||||
name_len += (sub_name_len + 1);
|
||||
if (name_len > parsed_name_max_len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy the sub name that follows the the label
|
||||
memcpy(name_itr, label + 1, sub_name_len);
|
||||
name_itr[sub_name_len] = '.';
|
||||
name_itr += (sub_name_len + 1);
|
||||
label += sub_name_len + 1;
|
||||
} while (*label != 0);
|
||||
|
||||
// Terminate the final string, replacing the last '.'
|
||||
parsed_name[name_len - 1] = '\0';
|
||||
// Return pointer to first char after the name
|
||||
return label + 1;
|
||||
}
|
||||
|
||||
// Parses the DNS request and prepares a DNS response with the IP of the softAP
|
||||
static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t dns_reply_max_len)
|
||||
{
|
||||
if (req_len > dns_reply_max_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Prepare the reply
|
||||
memset(dns_reply, 0, dns_reply_max_len);
|
||||
memcpy(dns_reply, req, req_len);
|
||||
|
||||
// Endianess of NW packet different from chip
|
||||
dns_header_t *header = (dns_header_t *)dns_reply;
|
||||
ESP_LOGD(TAG, "DNS query with header id: 0x%X, flags: 0x%X, qd_count: %d",
|
||||
ntohs(header->id), ntohs(header->flags), ntohs(header->qd_count));
|
||||
|
||||
// Not a standard query
|
||||
if ((header->flags & OPCODE_MASK) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set question response flag
|
||||
header->flags |= QR_FLAG;
|
||||
|
||||
uint16_t qd_count = ntohs(header->qd_count);
|
||||
header->an_count = htons(qd_count);
|
||||
|
||||
int reply_len = qd_count * sizeof(dns_answer_t) + req_len;
|
||||
if (reply_len > dns_reply_max_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Pointer to current answer and question
|
||||
char *cur_ans_ptr = dns_reply + req_len;
|
||||
char *cur_qd_ptr = dns_reply + sizeof(dns_header_t);
|
||||
char name[128];
|
||||
|
||||
// Respond to all questions with the ESP32's IP address
|
||||
for (int i = 0; i < qd_count; i++) {
|
||||
char *name_end_ptr = parse_dns_name(cur_qd_ptr, name, sizeof(name));
|
||||
if (name_end_ptr == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to parse DNS question: %s", cur_qd_ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dns_question_t *question = (dns_question_t *)(name_end_ptr);
|
||||
uint16_t qd_type = ntohs(question->type);
|
||||
uint16_t qd_class = ntohs(question->class);
|
||||
|
||||
ESP_LOGD(TAG, "Received type: %d | Class: %d | Question for: %s", qd_type, qd_class, name);
|
||||
|
||||
if (qd_type == QD_TYPE_A) {
|
||||
dns_answer_t *answer = (dns_answer_t *)cur_ans_ptr;
|
||||
|
||||
answer->ptr_offset = htons(0xC000 | (cur_qd_ptr - dns_reply));
|
||||
answer->type = htons(qd_type);
|
||||
answer->class = htons(qd_class);
|
||||
answer->ttl = htonl(ANS_TTL_SEC);
|
||||
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
|
||||
ESP_LOGD(TAG, "Answer with PTR offset: 0x%X and IP 0x%X", ntohs(answer->ptr_offset), ip_info.ip.addr);
|
||||
|
||||
answer->addr_len = htons(sizeof(ip_info.ip.addr));
|
||||
answer->ip_addr = ip_info.ip.addr;
|
||||
}
|
||||
}
|
||||
return reply_len;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets up a socket and listen for DNS queries,
|
||||
replies to all type A queries with the IP of the softAP
|
||||
*/
|
||||
void dns_server_task(void *pvParameters)
|
||||
{
|
||||
char rx_buffer[128];
|
||||
char addr_str[128];
|
||||
int addr_family;
|
||||
int ip_protocol;
|
||||
|
||||
while (1) {
|
||||
|
||||
struct sockaddr_in dest_addr;
|
||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
dest_addr.sin_family = AF_INET;
|
||||
dest_addr.sin_port = htons(DNS_PORT);
|
||||
addr_family = AF_INET;
|
||||
ip_protocol = IPPROTO_IP;
|
||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
||||
|
||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
|
||||
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", DNS_PORT);
|
||||
|
||||
while (1) {
|
||||
ESP_LOGI(TAG, "Waiting for data");
|
||||
struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
|
||||
socklen_t socklen = sizeof(source_addr);
|
||||
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
|
||||
|
||||
// Error occurred during receiving
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
|
||||
close(sock);
|
||||
break;
|
||||
}
|
||||
// Data received
|
||||
else {
|
||||
// Get the sender's ip address as string
|
||||
if (source_addr.sin6_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
|
||||
} else if (source_addr.sin6_family == PF_INET6) {
|
||||
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
}
|
||||
|
||||
// Null-terminate whatever we received and treat like a string...
|
||||
rx_buffer[len] = 0;
|
||||
|
||||
char reply[DNS_MAX_LEN];
|
||||
int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN);
|
||||
|
||||
ESP_LOGI(TAG, "Received %d bytes from %s | DNS reply with len: %d", len, addr_str, reply_len);
|
||||
if (reply_len <= 0) {
|
||||
ESP_LOGE(TAG, "Failed to prepare a DNS reply");
|
||||
} else {
|
||||
int err = sendto(sock, reply, reply_len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sock != -1) {
|
||||
ESP_LOGE(TAG, "Shutting down socket");
|
||||
shutdown(sock, 0);
|
||||
close(sock);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void start_dns_server(void)
|
||||
{
|
||||
xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, NULL);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Captive Portal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set ups and starts a simple DNS server that will respond to all queries
|
||||
* with the soft AP's IP address
|
||||
*
|
||||
*/
|
||||
void start_dns_server(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
163
examples/protocols/http_server/captive_portal/main/main.c
Normal file
163
examples/protocols/http_server/captive_portal/main/main.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/* Captive Portal 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 <sys/param.h>
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
#include "esp_http_server.h"
|
||||
#include "dns_server.h"
|
||||
|
||||
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
|
||||
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
|
||||
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
|
||||
|
||||
extern const char root_start[] asm("_binary_root_html_start");
|
||||
extern const char root_end[] asm("_binary_root_html_end");
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
|
||||
wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
|
||||
ESP_LOGI(TAG, "station " MACSTR " join, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
||||
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
|
||||
ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_init_softap(void)
|
||||
{
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
.ssid = EXAMPLE_ESP_WIFI_SSID,
|
||||
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
|
||||
.password = EXAMPLE_ESP_WIFI_PASS,
|
||||
.max_connection = EXAMPLE_MAX_STA_CONN,
|
||||
.authmode = WIFI_AUTH_WPA_WPA2_PSK
|
||||
},
|
||||
};
|
||||
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
|
||||
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
|
||||
|
||||
char ip_addr[16];
|
||||
inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);
|
||||
ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:'%s' password:'%s'",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
|
||||
}
|
||||
|
||||
// HTTP GET Handler
|
||||
static esp_err_t root_get_handler(httpd_req_t *req)
|
||||
{
|
||||
const uint32_t root_len = root_end - root_start;
|
||||
|
||||
ESP_LOGI(TAG, "Serve root");
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_send(req, root_start, root_len);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const httpd_uri_t root = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = root_get_handler
|
||||
};
|
||||
|
||||
// HTTP Error (404) Handler - Redirects all requests to the root page
|
||||
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
|
||||
{
|
||||
// Set status
|
||||
httpd_resp_set_status(req, "302 Temporary Redirect");
|
||||
// Redirect to the "/" root directory
|
||||
httpd_resp_set_hdr(req, "Location", "/");
|
||||
// iOS requires content in the response to detect a captive portal, simply redirecting is not sufficient.
|
||||
httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN);
|
||||
|
||||
ESP_LOGI(TAG, "Redirecting to root");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static httpd_handle_t start_webserver(void)
|
||||
{
|
||||
httpd_handle_t server = NULL;
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_open_sockets = 13;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
// Start the httpd server
|
||||
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
|
||||
if (httpd_start(&server, &config) == ESP_OK) {
|
||||
// Set URI handlers
|
||||
ESP_LOGI(TAG, "Registering URI handlers");
|
||||
httpd_register_uri_handler(server, &root);
|
||||
httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
Turn of warnings from HTTP server as redirecting traffic will yield
|
||||
lots of invalid requests
|
||||
*/
|
||||
esp_log_level_set("httpd_uri", ESP_LOG_ERROR);
|
||||
esp_log_level_set("httpd_txrx", ESP_LOG_ERROR);
|
||||
esp_log_level_set("httpd_parse", ESP_LOG_ERROR);
|
||||
|
||||
|
||||
// Initialize networking stack
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
// Create default event loop needed by the main app
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
// Initialize NVS needed by Wi-Fi
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
|
||||
// Initialize Wi-Fi including netif with default config
|
||||
esp_netif_create_default_wifi_ap();
|
||||
|
||||
// Initialise ESP32 in SoftAP mode
|
||||
wifi_init_softap();
|
||||
|
||||
// Start the server for the first time
|
||||
start_webserver();
|
||||
|
||||
// Start the DNS server that will redirect all queries to the softAP IP
|
||||
start_dns_server();
|
||||
}
|
||||
15
examples/protocols/http_server/captive_portal/main/root.html
Normal file
15
examples/protocols/http_server/captive_portal/main/root.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<title>ESP Captive Portal</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ESP Captive Portal</h1>
|
||||
<p>Hello World, this is ESP32!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
|
||||
CONFIG_LWIP_MAX_SOCKETS=16
|
||||
@@ -1,3 +1,6 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
||||
|
||||
# Simple HTTP File Server Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
@@ -49,3 +52,22 @@ File server implementation can be found under `main/file_server.c` which uses SP
|
||||
## Note
|
||||
|
||||
Browsers often send large header fields when an HTML form is submit. Therefore, for the purpose of this example, `HTTPD_MAX_REQ_HDR_LEN` has been increased to 1024 in `sdkconfig.defaults`. User can adjust this value as per their requirement, keeping in mind the memory constraint of the hardware in use.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (5583) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5583) example_connect: Connected to example_connect: sta
|
||||
I (5593) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (5593) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5603) example: Initializing SPIFFS
|
||||
I (5723) example: Partition size: total: 896321, used: 0
|
||||
I (5723) file_server: Starting HTTP Server on port: '80'
|
||||
I (28933) file_server: Receiving file : /test.html...
|
||||
I (28933) file_server: Remaining size : 574
|
||||
I (28943) file_server: File reception complete
|
||||
I (28993) file_server: Found file : test.html (574 bytes)
|
||||
I (35943) file_server: Sending file : /test.html (574 bytes)...
|
||||
I (35953) file_server: File sending complete
|
||||
I (45363) file_server: Deleting file : /test.html
|
||||
```
|
||||
|
||||
@@ -3,13 +3,75 @@
|
||||
The Example consists of HTTPD server persistent sockets demo.
|
||||
This sort of persistency enables the server to have independent sessions/contexts per client.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
* In order to test the HTTPD server persistent sockets demo :
|
||||
1. compile and burn the firmware `idf.py -p PORT flash`
|
||||
2. run `idf.py -p PORT monitor` and note down the IP assigned to your ESP module. The default port is 80
|
||||
3. run the test script "python scripts/adder.py \<IP\> \<port\> \<N\>"
|
||||
* the provided test script sends (POST) numbers from 1 to N to the server which has a URI POST handler for adding these numbers into an accumulator that is valid throughout the lifetime of the connection socket, hence persistent
|
||||
* the script does a GET before closing and displays the final value of the accumulator
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
### Test the example
|
||||
|
||||
In order to test the HTTPD server persistent sockets demo :
|
||||
|
||||
* run the test script "python scripts/adder.py \<IP\> \<port\> \<N\>"
|
||||
* the provided test script sends (POST) numbers from 1 to N to the server which has a URI POST handler for adding these numbers into an accumulator that is valid throughout the lifetime of the connection socket, hence persistent
|
||||
* the script does a GET before closing and displays the final value of the accumulator
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (9570) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (9580) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (9590) example: Starting server on port: '80'
|
||||
I (9600) example: Registering URI handlers
|
||||
I (12860) example: /adder visitor count = 1
|
||||
I (12860) example: /adder PUT handler read 0
|
||||
I (12860) example: /adder PUT allocating new session
|
||||
I (13070) example: /adder visitor count = 2
|
||||
I (13070) example: /adder handler read 1
|
||||
I (13090) example: /adder visitor count = 3
|
||||
I (13090) example: /adder handler read 2
|
||||
I (13110) example: /adder visitor count = 4
|
||||
I (13110) example: /adder handler read 3
|
||||
I (13170) example: /adder visitor count = 5
|
||||
I (13170) example: /adder handler read 4
|
||||
I (13190) example: /adder visitor count = 6
|
||||
I (13190) example: /adder handler read 5
|
||||
I (13210) example: /adder visitor count = 7
|
||||
I (13210) example: /adder handler read 6
|
||||
I (13270) example: /adder visitor count = 8
|
||||
I (13270) example: /adder handler read 7
|
||||
I (13290) example: /adder visitor count = 9
|
||||
I (13290) example: /adder handler read 8
|
||||
I (13300) example: /adder visitor count = 10
|
||||
I (13310) example: /adder handler read 9
|
||||
I (13370) example: /adder visitor count = 11
|
||||
I (13370) example: /adder handler read 10
|
||||
I (13390) example: /adder visitor count = 12
|
||||
I (13390) example: /adder GET handler send 55
|
||||
I (13420) example: /adder Free Context function called
|
||||
```
|
||||
|
||||
@@ -4,12 +4,35 @@ The Example consists of HTTPD server demo with demostration of URI handling :
|
||||
1. URI \hello for GET command returns "Hello World!" message
|
||||
2. URI \echo for POST command echoes back the POSTed message
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
* In order to test the HTTPD server persistent sockets demo :
|
||||
1. compile and burn the firmware `idf.py -p PORT flash`
|
||||
2. run `idf.py -p PORT monitor` and note down the IP assigned to your ESP module. The default port is 80
|
||||
3. test the example :
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
### Test the example :
|
||||
* run the test script : "python scripts/client.py \<IP\> \<port\> \<MSG\>"
|
||||
* the provided test script first does a GET \hello and displays the response
|
||||
* the script does a POST to \echo with the user input \<MSG\> and displays the response
|
||||
@@ -20,7 +43,16 @@ The Example consists of HTTPD server demo with demostration of URI handling :
|
||||
* since the server echoes back the request body, the two files should be same, as can be confirmed using : "cmp anyfile tmpfile"
|
||||
3. "curl -X PUT -d "0" 192.168.43.130:80/ctrl" - disable /hello and /echo handlers
|
||||
4. "curl -X PUT -d "1" 192.168.43.130:80/ctrl" - enable /hello and /echo handlers
|
||||
|
||||
* If the server log shows "httpd_parse: parse_block: request URI/header too long", especially when handling POST requests, then you probably need to increase HTTPD_MAX_REQ_HDR_LEN, which you can find in the project configuration menu (`idf.py menuconfig`): Component config -> HTTP Server -> Max HTTP Request Header Length
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
## Example Output
|
||||
```
|
||||
I (9580) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (9580) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (9590) example: Starting server on port: '80'
|
||||
I (9600) example: Registering URI handlers
|
||||
I (66450) example: Found header => Host: 192.168.194.219
|
||||
I (66460) example: Request headers lost
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
* If the server log shows "httpd_parse: parse_block: request URI/header too long", especially when handling POST requests, then you probably need to increase HTTPD_MAX_REQ_HDR_LEN, which you can find in the project configuration menu (`idf.py menuconfig`): Component config -> HTTP Server -> Max HTTP Request Header Length
|
||||
|
||||
@@ -5,16 +5,50 @@ This example demonstrates the HTTPD server using the WebSocket feature.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
The example starts a WS server on a local network, so a WS client is needed to interact with the server (an example test
|
||||
ws_server_example_test.py could be used as a simple WS client).
|
||||
The example starts a websocket server on a local network. You need a websocket client to interact with the server (an example test
|
||||
ws_server_example_test.py could be used as the simple websocket client).
|
||||
|
||||
The server registers WebSocket handler which echoes back the received WebSocket frame. It also demonstrates
|
||||
The server registers websocket handler which echoes back the received WebSocket frame. It also demonstrates
|
||||
use of asynchronous send, which is triggered on reception of a certain message.
|
||||
|
||||
Please note that the WebSocket HTTP server does not automatically fragment messages.
|
||||
Each outgoing frame has the FIN flag set by default.
|
||||
In case an application wants to send fragmented data, it must be done manually by setting the
|
||||
`fragmented` option and using the `final` flag as described in [RFC6455, section 5.4](https://tools.ietf.org/html/rfc6455#section-5.4).
|
||||
### Websocket support in `http_server`
|
||||
|
||||
Websocket echo server is build on top of the HTTP server component using [Websocket server](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/esp_http_server.html#websocket-server) configuration.
|
||||
This feature is very limited, and a special care must be taken while implementing websocket URI handlers.
|
||||
|
||||
#### Configure URI handler
|
||||
|
||||
We register the URI handler with the standard API `httpd_register_uri_handler()` with `is_websocket` enabled and other optional parameters:
|
||||
|
||||
```c
|
||||
static const httpd_uri_t ws_uri_handler_options = {
|
||||
... // httpd options
|
||||
|
||||
.is_websocket = true, // Mandatory: set to `true` to handler websocket protocol
|
||||
.handle_ws_control_frames = false, // Optional: set to `true` for the handler to receive control packets, too
|
||||
.supported_subprotocol = "chat", // Optional: set supported subprotocol for this handler
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
#### Implement URI handler
|
||||
|
||||
The URI handler is called on every URI request, but also before the websocket handshake, so it is very important to check the request type before reading websocket frame:
|
||||
|
||||
```c
|
||||
// beginning of the ws URI handler
|
||||
if (req->method == HTTP_GET) {
|
||||
// action before ws handshake
|
||||
return ESP_OK;
|
||||
}
|
||||
// action after the handshake (read frames)
|
||||
```
|
||||
|
||||
#### Handling incoming data
|
||||
|
||||
To receive websocket frames, use `httpd_ws_recv_frame()` after the websocket handshake, with `httpd_ws_frame_t` parameters set accordingly:
|
||||
* `payload` to a valid buffer for the received data
|
||||
* `len` set to `0` for the first call of `httpd_ws_recv_frame()`. Note that this value is used to indicate the packet length has been read in the previous call if we use dynamic buffers.
|
||||
|
||||
`httpd_ws_recv_frame` support two ways to get frame payload.
|
||||
* Static buffer -- Allocate maximum expected packet length (either statically or dynamically) and call `httpd_ws_recv_frame()` referencing this buffer and it's size. (Unnecessarily large buffers might cause memory waste)
|
||||
@@ -27,9 +61,17 @@ ws_pkt.payload = buf;
|
||||
httpd_ws_recv_frame(req, &ws_pkt, MAX_PAYLOAD_LEN);
|
||||
```
|
||||
* Dynamic buffer -- Refer to the examples, which receive websocket data in these three steps:
|
||||
1) Call `httpd_ws_recv_frame()` with zero buffer size
|
||||
2) Allocate the size based on the received packet length
|
||||
3) Call `httpd_ws_recv_frame()` with the allocated buffer
|
||||
1) Call `httpd_ws_recv_frame()` with zero buffer size
|
||||
2) Allocate the size based on the received packet length
|
||||
3) Call `httpd_ws_recv_frame()` with the allocated buffer
|
||||
|
||||
#### Handling outgoing data
|
||||
|
||||
Please note that the WebSocket HTTP server does not automatically fragment messages.
|
||||
Each outgoing frame has the FIN flag set by default.
|
||||
In case an application wants to send fragmented data, it must be done manually by setting the
|
||||
`fragmented` option and using the `final` flag as described in [RFC6455, section 5.4](https://tools.ietf.org/html/rfc6455#section-5.4).
|
||||
|
||||
|
||||
### Hardware Required
|
||||
|
||||
|
||||
69
examples/protocols/https_mbedtls/README.md
Normal file
69
examples/protocols/https_mbedtls/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# HTTP server with TLS support using mbedTLS
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Simple HTTPS example that uses mbedTLS to establish a secure socket connection using the certificate bundle with two custom certificates added for verification:
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (9599) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (9599) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (9609) example: Seeding the random number generator
|
||||
I (9619) example: Attaching the certificate bundle...
|
||||
I (9619) example: Setting hostname for TLS session...
|
||||
I (9629) example: Setting up the SSL/TLS structure...
|
||||
I (9639) example: Connecting to www.howsmyssl.com:443...
|
||||
I (10109) example: Connected.
|
||||
I (10109) example: Performing the SSL/TLS handshake...
|
||||
I (10789) esp-x509-crt-bundle: Certificate validated
|
||||
I (15019) example: Verifying peer X.509 certificate...
|
||||
I (15019) example: Certificate verified.
|
||||
I (15019) example: Cipher suite is TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
|
||||
I (15029) example: Writing HTTP request...
|
||||
I (15039) example: 106 bytes written
|
||||
I (15039) example: Reading HTTP response...
|
||||
HTTP/1.0 200 OK
|
||||
Content-Length: 2091
|
||||
Access-Control-Allow-Origin: *
|
||||
Content-Type: application/json
|
||||
Date: Wed, 08 Sep 2021 09:28:59 GMT
|
||||
Strict-Transport-Security: max-age=631138519; includeSubdomains; preload
|
||||
|
||||
{"given_cipher_suites":["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_CCM","TLS_DHE_RSA_WITH_AES_256_CCM","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_DHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8","TLS_DHE_RSA_WITH_AES_256_CCM_8","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CCM","TLS_DHE_RSA_WITH_AES_128_CCM","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256","TLS_DHE_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_DHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8","TLS_DHE_RSA_WITH_AES_128_CCM_8","TLS_RSA_WITH_AES_256_GCM_SHA384","TLS_RSA_WITH_AES_256_CCM","TLS_RSA_WITH_AES_256_CBC_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_256_CCM_8","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_128_CCM","TLS_RSA_WITH_AES_128_CBC_SHA256","TLS_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA","TLS_RSA_WITH_AES_128_CCM_8","TLS_EMPTY_RENEGOTIATION_INFO_SCSV"],"ephemeral_keys_supported":true,"session_ticket_supported":true,"tls_compression_supported":false,"unknown_cipher_suite_supported":false,"beast_vuln":false,"able_to_detect_n_minus_one_splitting":false,"insecure_cipher_suites":{},"tls_version":"TLS 1.2","rating":"Probably Okay"}
|
||||
I (15829) example: Completed 1 requests
|
||||
Minimum free heap size: 189136 bytes
|
||||
I (15839) example: 10...
|
||||
I (16839) example: 9...
|
||||
I (17839) example: 8...
|
||||
I (18839) example: 7...
|
||||
I (19839) example: 6...
|
||||
```
|
||||
@@ -2,4 +2,85 @@
|
||||
|
||||
Uses APIs from `esp-tls` component to make a very simple HTTPS request over a secure connection, including verifying the server TLS certificate.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
### Session Tickets
|
||||
|
||||
Session Tickets, specified in [RFC 5077](https://datatracker.ietf.org/doc/html/rfc5077) are a mechanism to distribute encrypted
|
||||
session-state information to the client in the form of a ticket and a mechanism to present the ticket back to the server. The ticket is created by a TLS server and sent to a TLS client. The TLS client presents the ticket to the TLS server to resume a session. In TLS 1.2, this speeds up handshakes from two to one round-trip.
|
||||
|
||||
In ESP-IDF, this feature is supported (for both server and client) when mbedTLS is used as the SSL library.
|
||||
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
#### Configuring Client Session Tickets
|
||||
|
||||
Note: This example has client session tickets enabled by default.
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* In the `Component Config` -> `ESP-TLS` submenu, select the `Enable client session tickets` option.
|
||||
|
||||
Ensure that the server has the session tickets feature enabled.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (5634) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (5634) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5644) example: Start https_request example
|
||||
I (5654) example: https_request using crt bundle
|
||||
W (6514) wifi:<ba-add>idx:1 (ifx:0, ee:6d:19:60:f6:0e), tid:4, ssn:0, winSize:64
|
||||
I (7074) esp-x509-crt-bundle: Certificate validated
|
||||
I (9384) example: Connection established...
|
||||
I (9384) example: 107 bytes written
|
||||
I (9384) example: Reading HTTP response...
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 2091
|
||||
Access-Control-Allow-Origin: *
|
||||
Connection: close
|
||||
Content-Type: application/json
|
||||
Date: Tue, 07 Sep 2021 08:30:00 GMT
|
||||
Strict-Transport-Security: max-age=631138519; includeSubdomains; preload
|
||||
|
||||
{"given_cipher_suites":["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_CCM","TLS_DHE_RSA_WITH_AES_256_CCM","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDHE_RSA_WITH_AES_
|
||||
256_CBC_SHA384","TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_DHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8","TLS_DHE_RSA_WITH_AES_256_CCM_8","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CCM","TLS_DHE_RSA_WITH_AES_128_CCM","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256","TLS_DHE
|
||||
_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_DHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_ECDSA
|
||||
_WITH_AES_128_CCM_8","TLS_DHE_RSA_WITH_AES_128_CCM_8","TLS_RSA_WITH_AES_256_GCM_SHA384","TLS_RSA_WITH_AES_256_CCM","TLS_RSA_WITH_AES_256_CBC_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_256_CCM_8","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_128_CCM","TLS_RS
|
||||
A_WITH_AES_128_CBC_SHA256","TLS_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA","TLS_RSA_WITH_AES_128_CCM_8","TLS_EMPTY_RENEGOTIATION_INFO_SCSV"],"ephemeral_keys_supported":true,"session_ticket_supported":true,"tls_compression_supported":false,"unknown_cipher_suite_supported":false,"beast_vuln":fal
|
||||
se,"able_to_detect_n_minus_one_splitting":false,"insecure_cipher_suites":{},"tls_version":"TLS 1.2","rating":"Probably Okay"}
|
||||
I (10204) example: connection closed
|
||||
I (10204) example: 10...
|
||||
I (11204) example: 9...
|
||||
I (12204) example: 8...
|
||||
I (13204) example: 7...
|
||||
I (14204) example: 6...
|
||||
I (15204) example: 5...
|
||||
I (16204) example: 4...
|
||||
```
|
||||
|
||||
@@ -63,6 +63,19 @@ def test_examples_protocol_https_request(env, extra_data):
|
||||
raise
|
||||
Utility.console_log("Passed the test for \"https_request using global ca_store\"")
|
||||
|
||||
# Check for connection using already saved client session
|
||||
Utility.console_log("Testing for \"https_request using saved client session\"")
|
||||
try:
|
||||
dut1.expect(re.compile('https_request using saved client session'), timeout=20)
|
||||
dut1.expect_all('Connection established...',
|
||||
'Reading HTTP response...',
|
||||
'HTTP/1.1 200 OK',
|
||||
re.compile('connection closed'))
|
||||
except Exception:
|
||||
Utility.console_log("Failed the test for \"https_request using saved client session\"")
|
||||
raise
|
||||
Utility.console_log("Passed the test for \"https_request using saved client session\"")
|
||||
|
||||
# Check for connection using crt bundle with mbedtls dynamic resource enabled
|
||||
dut1 = env.get_dut('https_request', 'examples/protocols/https_request', dut_class=ttfw_idf.ESP32DUT, app_config_name='ssldyn')
|
||||
# check and log bin size
|
||||
|
||||
@@ -67,7 +67,9 @@ static const char REQUEST[] = "GET " WEB_URL " HTTP/1.1\r\n"
|
||||
*/
|
||||
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
|
||||
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
esp_tls_client_session_t *tls_client_session = NULL;
|
||||
#endif
|
||||
static void https_get_request(esp_tls_cfg_t cfg)
|
||||
{
|
||||
char buf[512];
|
||||
@@ -82,6 +84,12 @@ static void https_get_request(esp_tls_cfg_t cfg)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
/* The TLS session is successfully established, now saving the session ctx for reuse */
|
||||
if (tls_client_session == NULL) {
|
||||
tls_client_session = esp_tls_get_client_session(tls);
|
||||
}
|
||||
#endif
|
||||
size_t written_bytes = 0;
|
||||
do {
|
||||
ret = esp_tls_conn_write(tls,
|
||||
@@ -143,6 +151,8 @@ static void https_get_request_using_crt_bundle(void)
|
||||
https_get_request(cfg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void https_get_request_using_cacert_buf(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "https_request using cacert_buf");
|
||||
@@ -169,6 +179,19 @@ static void https_get_request_using_global_ca_store(void)
|
||||
esp_tls_free_global_ca_store();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
static void https_get_request_using_already_saved_session(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "https_request using saved client session");
|
||||
esp_tls_cfg_t cfg = {
|
||||
.client_session = tls_client_session,
|
||||
};
|
||||
https_get_request(cfg);
|
||||
free(tls_client_session);
|
||||
tls_client_session = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void https_request_task(void *pvparameters)
|
||||
{
|
||||
ESP_LOGI(TAG, "Start https_request example");
|
||||
@@ -176,7 +199,9 @@ static void https_request_task(void *pvparameters)
|
||||
https_get_request_using_crt_bundle();
|
||||
https_get_request_using_cacert_buf();
|
||||
https_get_request_using_global_ca_store();
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
https_get_request_using_already_saved_session();
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Finish https_request example");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
|
||||
MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
|
||||
AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
|
||||
jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
|
||||
Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
|
||||
U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
|
||||
gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
|
||||
/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
|
||||
oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
|
||||
BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
|
||||
ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
|
||||
p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
|
||||
AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
|
||||
Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
|
||||
LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
|
||||
r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
|
||||
ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
|
||||
S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
|
||||
qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
|
||||
O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
|
||||
UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -9,3 +9,4 @@ CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS=y
|
||||
|
||||
98
examples/protocols/https_request/sdkconfig.ci.mbedtls_config
Normal file
98
examples/protocols/https_request/sdkconfig.ci.mbedtls_config
Normal file
@@ -0,0 +1,98 @@
|
||||
# This is a test sdkconfig file for only build purpose. It is not intended to be used for the example.
|
||||
# This disables most of the mbedtls configurations by default.
|
||||
# If any component using mbedtls does not select respective configurations,
|
||||
# then this should fail at build stage.
|
||||
|
||||
# Few example dependancies need to be enabled by default for the build to succeed
|
||||
##############
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
|
||||
##############
|
||||
|
||||
CONFIG_MBEDTLS_HARDWARE_AES=n
|
||||
CONFIG_MBEDTLS_HARDWARE_MPI=n
|
||||
CONFIG_MBEDTLS_HARDWARE_SHA=n
|
||||
CONFIG_MBEDTLS_ROM_MD5=n
|
||||
CONFIG_MBEDTLS_HAVE_TIME=n
|
||||
CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=n
|
||||
CONFIG_MBEDTLS_SHA512_C=n
|
||||
|
||||
CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=n
|
||||
CONFIG_MBEDTLS_TLS_SERVER=n
|
||||
CONFIG_MBEDTLS_TLS_CLIENT=n
|
||||
CONFIG_MBEDTLS_TLS_ENABLED=n
|
||||
|
||||
#
|
||||
# TLS Key Exchange Methods
|
||||
#
|
||||
CONFIG_MBEDTLS_PSK_MODES=n
|
||||
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=n
|
||||
# end of TLS Key Exchange Methods
|
||||
|
||||
CONFIG_MBEDTLS_SSL_RENEGOTIATION=n
|
||||
CONFIG_MBEDTLS_SSL_PROTO_SSL3=n
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=n
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1=n
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=n
|
||||
|
||||
CONFIG_MBEDTLS_SSL_ALPN=n
|
||||
CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=n
|
||||
CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=n
|
||||
|
||||
#
|
||||
# Symmetric Ciphers
|
||||
#
|
||||
CONFIG_MBEDTLS_AES_C=n
|
||||
CONFIG_MBEDTLS_CAMELLIA_C=n
|
||||
CONFIG_MBEDTLS_DES_C=n
|
||||
CONFIG_MBEDTLS_RC4_DISABLED=n
|
||||
CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT=n
|
||||
CONFIG_MBEDTLS_RC4_ENABLED=n
|
||||
CONFIG_MBEDTLS_BLOWFISH_C=n
|
||||
CONFIG_MBEDTLS_XTEA_C=n
|
||||
CONFIG_MBEDTLS_CCM_C=n
|
||||
CONFIG_MBEDTLS_GCM_C=n
|
||||
CONFIG_MBEDTLS_NIST_KW_C=n
|
||||
# end of Symmetric Ciphers
|
||||
|
||||
CONFIG_MBEDTLS_RIPEMD160_C=n
|
||||
|
||||
#
|
||||
# Certificates
|
||||
#
|
||||
CONFIG_MBEDTLS_PEM_PARSE_C=n
|
||||
CONFIG_MBEDTLS_PEM_WRITE_C=n
|
||||
CONFIG_MBEDTLS_X509_CRL_PARSE_C=n
|
||||
CONFIG_MBEDTLS_X509_CSR_PARSE_C=n
|
||||
# end of Certificates
|
||||
|
||||
CONFIG_MBEDTLS_ECP_C=n
|
||||
CONFIG_MBEDTLS_ECDH_C=n
|
||||
CONFIG_MBEDTLS_ECDSA_C=n
|
||||
CONFIG_MBEDTLS_ECJPAKE_C=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_NIST_OPTIM=n
|
||||
CONFIG_MBEDTLS_POLY1305_C=n
|
||||
CONFIG_MBEDTLS_CHACHA20_C=n
|
||||
CONFIG_MBEDTLS_HKDF_C=n
|
||||
CONFIG_MBEDTLS_THREADING_C=n
|
||||
CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI=n
|
||||
CONFIG_MBEDTLS_SECURITY_RISKS=n
|
||||
# end of mbedTLS
|
||||
1
examples/protocols/https_request/sdkconfig.defaults
Normal file
1
examples/protocols/https_request/sdkconfig.defaults
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS=y
|
||||
@@ -4,7 +4,29 @@ This example creates a SSL server that returns a simple HTML page when you visit
|
||||
|
||||
See the `esp_https_server` component documentation for details.
|
||||
|
||||
Before using the example, open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Certificates
|
||||
|
||||
@@ -24,3 +46,21 @@ Please see the openssl man pages (man openssl-req) for more details.
|
||||
|
||||
It is **strongly recommended** to not reuse the example certificate in your application;
|
||||
it is included only for demonstration.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (8596) example: Starting server
|
||||
I (8596) esp_https_server: Starting server
|
||||
I (8596) esp_https_server: Server listening on port 443
|
||||
I (8596) example: Registering URI handlers
|
||||
I (8606) esp_netif_handlers: example_connect: sta ip: 192.168.194.219, mask: 255.255.255.0, gw: 192.168.194.27
|
||||
I (8616) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.194.219
|
||||
I (9596) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (9596) example_connect: Connected to example_connect: sta
|
||||
W (9606) wifi:<ba-add>idx:0 (ifx:0, ee:6d:19:60:f6:0e), tid:0, ssn:2, winSize:64
|
||||
I (9616) example_connect: - IPv4 address: 192.168.194.219
|
||||
I (9616) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
W (14426) wifi:<ba-add>idx:1 (ifx:0, ee:6d:19:60:f6:0e), tid:4, ssn:0, winSize:64
|
||||
I (84896) esp_https_server: performing session handshake
|
||||
```
|
||||
|
||||
@@ -6,22 +6,40 @@ This example creates a SSL server and employs a simple Websocket request handler
|
||||
|
||||
See the `esp_https_server` component documentation for details.
|
||||
|
||||
Before using the example, open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
`httpd_ws_recv_frame` support two ways to get frame payload.
|
||||
* Static buffer -- Allocate maximum expected packet length (either statically or dynamically) and call `httpd_ws_recv_frame()` referencing this buffer and it's size. (Unnecessarily large buffers might cause memory waste)
|
||||
### Websocket support in `http_server`
|
||||
|
||||
Please refer to the documentation of [Websocket server](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/esp_http_server.html#websocket-server) feature in the documentation,
|
||||
or to the description of using websocket handlers in httpd in the [simple ws echo](../../http_server/ws_echo_server/README.md#how-to-use-example) example.
|
||||
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
#define MAX_PAYLOAD_LEN 128
|
||||
uint8_t buf[MAX_PAYLOAD_LEN] = { 0 };
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
ws_pkt.payload = buf;
|
||||
httpd_ws_recv_frame(req, &ws_pkt, MAX_PAYLOAD_LEN);
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Dynamic buffer -- Refer to the examples, which receive websocket data in these three steps:
|
||||
1) Call `httpd_ws_recv_frame()` with zero buffer size
|
||||
2) Allocate the size based on the received packet length
|
||||
3) Call `httpd_ws_recv_frame()` with the allocated buffer
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Certificates
|
||||
|
||||
|
||||
@@ -38,8 +38,10 @@ I (5971) example_connect: Connected to Ethernet
|
||||
I (5971) example_connect: IPv4 address: 192.168.2.137
|
||||
I (5971) example_connect: IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed4:a92b
|
||||
I (5981) example: Connecting to 2 URLs
|
||||
I (7100) esp-x509-crt-bundle: Certificate validated
|
||||
I (8371) example: Connection established to https://www.howsmyssl.com/a/check
|
||||
I (11730) esp-x509-crt-bundle: Certificate validated
|
||||
I (11821) example: Connection established to https://espressif.com
|
||||
I (12821) example: Completed 2 connections
|
||||
I (12821) example: Starting over again...
|
||||
|
||||
```
|
||||
|
||||
@@ -204,6 +204,9 @@ void app_main(void)
|
||||
#elif CONFIG_ESP_CONSOLE_USB_CDC
|
||||
esp_console_dev_usb_cdc_config_t cdc_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&cdc_config, &repl_config, &s_repl));
|
||||
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
esp_console_dev_usb_serial_jtag_config_t usbjtag_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&usbjtag_config, &repl_config, &repl));
|
||||
#endif
|
||||
|
||||
/* register command `ping` */
|
||||
|
||||
@@ -5,6 +5,7 @@ Shows how to use mDNS to advertise lookup services and hosts
|
||||
## Example workflow
|
||||
|
||||
- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
|
||||
- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host.
|
||||
- WiFi STA is started and trying to connect to the access point defined through the project configuration
|
||||
- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down
|
||||
- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press
|
||||
|
||||
@@ -12,6 +12,12 @@ menu "Example Configuration"
|
||||
help
|
||||
mDNS Instance Name for example to use
|
||||
|
||||
config MDNS_PUBLISH_DELEGATE_HOST
|
||||
bool "Publish a delegated host"
|
||||
help
|
||||
Enable publishing a delegated host other than ESP32.
|
||||
The example will also add a mock service for this host.
|
||||
|
||||
config MDNS_RESOLVE_TEST_SERVICES
|
||||
bool "Resolve test services"
|
||||
default n
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
@@ -23,8 +24,8 @@
|
||||
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
|
||||
#define EXAMPLE_BUTTON_GPIO 0
|
||||
|
||||
static const char *TAG = "mdns-test";
|
||||
static char* generate_hostname(void);
|
||||
static const char * TAG = "mdns-test";
|
||||
static char * generate_hostname(void);
|
||||
|
||||
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
|
||||
static void query_mdns_host_with_gethostbyname(char * host);
|
||||
@@ -33,7 +34,8 @@ static void query_mdns_host_with_getaddrinfo(char * host);
|
||||
|
||||
static void initialise_mdns(void)
|
||||
{
|
||||
char* hostname = generate_hostname();
|
||||
char * hostname = generate_hostname();
|
||||
|
||||
//initialize mDNS
|
||||
ESP_ERROR_CHECK( mdns_init() );
|
||||
//set mDNS hostname (required if you want to advertise services)
|
||||
@@ -44,17 +46,36 @@ static void initialise_mdns(void)
|
||||
|
||||
//structure with TXT records
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"board","esp32"},
|
||||
{"u","user"},
|
||||
{"p","password"}
|
||||
{"board", "esp32"},
|
||||
{"u", "user"},
|
||||
{"p", "password"}
|
||||
};
|
||||
|
||||
//initialize service
|
||||
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) );
|
||||
|
||||
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
|
||||
char *delegated_hostname;
|
||||
if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
mdns_ip_addr_t addr4, addr6;
|
||||
esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4);
|
||||
addr4.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6);
|
||||
addr6.addr.type = ESP_IPADDR_TYPE_V6;
|
||||
addr4.next = &addr6;
|
||||
addr6.next = NULL;
|
||||
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
|
||||
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) );
|
||||
free(delegated_hostname);
|
||||
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
|
||||
|
||||
//add another TXT item
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
|
||||
//change TXT item value
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "u", "admin") );
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) );
|
||||
free(hostname);
|
||||
}
|
||||
|
||||
@@ -64,28 +85,30 @@ static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
|
||||
/* these strings match mdns_ip_protocol_t enumeration */
|
||||
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
|
||||
|
||||
static void mdns_print_results(mdns_result_t * results){
|
||||
mdns_result_t * r = results;
|
||||
mdns_ip_addr_t * a = NULL;
|
||||
static void mdns_print_results(mdns_result_t *results)
|
||||
{
|
||||
mdns_result_t *r = results;
|
||||
mdns_ip_addr_t *a = NULL;
|
||||
int i = 1, t;
|
||||
while(r){
|
||||
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
|
||||
if(r->instance_name){
|
||||
printf(" PTR : %s\n", r->instance_name);
|
||||
while (r) {
|
||||
printf("%d: Interface: %s, Type: %s, TTL: %u\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol],
|
||||
r->ttl);
|
||||
if (r->instance_name) {
|
||||
printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto);
|
||||
}
|
||||
if(r->hostname){
|
||||
if (r->hostname) {
|
||||
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
|
||||
}
|
||||
if(r->txt_count){
|
||||
printf(" TXT : [%u] ", r->txt_count);
|
||||
for(t=0; t<r->txt_count; t++){
|
||||
printf("%s=%s; ", r->txt[t].key, r->txt[t].value?r->txt[t].value:"NULL");
|
||||
if (r->txt_count) {
|
||||
printf(" TXT : [%zu] ", r->txt_count);
|
||||
for (t = 0; t < r->txt_count; t++) {
|
||||
printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
a = r->addr;
|
||||
while(a){
|
||||
if(a->addr.type == ESP_IPADDR_TYPE_V6){
|
||||
while (a) {
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
@@ -94,7 +117,6 @@ static void mdns_print_results(mdns_result_t * results){
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void query_mdns_service(const char * service_name, const char * proto)
|
||||
@@ -116,6 +138,54 @@ static void query_mdns_service(const char * service_name, const char * proto)
|
||||
mdns_query_results_free(results);
|
||||
}
|
||||
|
||||
static bool check_and_print_result(mdns_search_once_t *search)
|
||||
{
|
||||
// Check if any result is available
|
||||
mdns_result_t * result = NULL;
|
||||
if (!mdns_query_async_get_results(search, 0, &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result) { // search timeout, but no result
|
||||
return true;
|
||||
}
|
||||
|
||||
// If yes, print the result
|
||||
mdns_ip_addr_t * a = result->addr;
|
||||
while (a) {
|
||||
if(a->addr.type == ESP_IPADDR_TYPE_V6){
|
||||
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
|
||||
} else {
|
||||
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
// and free the result
|
||||
mdns_query_results_free(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void query_mdns_hosts_async(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name);
|
||||
|
||||
mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL);
|
||||
mdns_query_async_delete(s_a);
|
||||
mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL);
|
||||
while (s_a || s_aaaa) {
|
||||
if (s_a && check_and_print_result(s_a)) {
|
||||
ESP_LOGI(TAG, "Query A %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_a);
|
||||
s_a = NULL;
|
||||
}
|
||||
if (s_aaaa && check_and_print_result(s_aaaa)) {
|
||||
ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name);
|
||||
mdns_query_async_delete(s_aaaa);
|
||||
s_aaaa = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void query_mdns_host(const char * host_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Query A: %s.local", host_name);
|
||||
@@ -152,6 +222,7 @@ static void check_button(void)
|
||||
static bool old_level = true;
|
||||
bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO);
|
||||
if (!new_level && old_level) {
|
||||
query_mdns_hosts_async("esp32-mdns");
|
||||
query_mdns_host("esp32");
|
||||
query_mdns_service("_arduino", "_tcp");
|
||||
query_mdns_service("_http", "_tcp");
|
||||
@@ -174,7 +245,7 @@ static void mdns_example_task(void *pvParameters)
|
||||
query_mdns_host_with_getaddrinfo("tinytester-lwip.local");
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
while (1) {
|
||||
check_button();
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
@@ -10,15 +11,17 @@ import dpkt
|
||||
import dpkt.dns
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import DUT
|
||||
from tiny_test_fw.Utility import console_log
|
||||
|
||||
stop_mdns_server = Event()
|
||||
esp_answered = Event()
|
||||
esp_delegated_answered = Event()
|
||||
|
||||
|
||||
def get_dns_query_for_esp(esp_host):
|
||||
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
|
||||
dns.qd[0].name = esp_host + u'.local'
|
||||
print('Created query for esp host: {} '.format(dns.__repr__()))
|
||||
console_log('Created query for esp host: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
@@ -32,7 +35,7 @@ def get_dns_answer_to_mdns(tester_host):
|
||||
arr.name = tester_host
|
||||
arr.ip = socket.inet_aton('127.0.0.1')
|
||||
dns. an.append(arr)
|
||||
print('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
@@ -56,31 +59,44 @@ def mdns_server(esp_host):
|
||||
MCAST_GRP = '224.0.0.251'
|
||||
TESTER_NAME = u'tinytester.local'
|
||||
TESTER_NAME_LWIP = u'tinytester-lwip.local'
|
||||
QUERY_TIMEOUT = 0.2
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
sock.setblocking(False)
|
||||
sock.bind((UDP_IP,UDP_PORT))
|
||||
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
||||
sock.settimeout(30)
|
||||
last_query_timepoint = time.time()
|
||||
while not stop_mdns_server.is_set():
|
||||
try:
|
||||
if not esp_answered.is_set():
|
||||
sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT))
|
||||
time.sleep(0.2)
|
||||
current_time = time.time()
|
||||
if current_time - last_query_timepoint > QUERY_TIMEOUT:
|
||||
last_query_timepoint = current_time
|
||||
if not esp_answered.is_set():
|
||||
sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT))
|
||||
if not esp_delegated_answered.is_set():
|
||||
sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), (MCAST_GRP,UDP_PORT))
|
||||
timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
|
||||
read_socks, _, _ = select.select([sock], [], [], timeout)
|
||||
if not read_socks:
|
||||
continue
|
||||
data, addr = sock.recvfrom(1024)
|
||||
dns = dpkt.dns.DNS(data)
|
||||
if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A:
|
||||
if dns.qd[0].name == TESTER_NAME:
|
||||
print('Received query: {} '.format(dns.__repr__()))
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP,UDP_PORT))
|
||||
elif dns.qd[0].name == TESTER_NAME_LWIP:
|
||||
print('Received query: {} '.format(dns.__repr__()))
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr)
|
||||
if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
|
||||
if dns.an[0].name == esp_host + u'.local':
|
||||
print('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
esp_answered.set()
|
||||
if dns.an[0].name == esp_host + u'-delegated.local':
|
||||
console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
|
||||
esp_delegated_answered.set()
|
||||
except socket.timeout:
|
||||
break
|
||||
except dpkt.UnpackError:
|
||||
@@ -105,21 +121,20 @@ def test_examples_protocol_mdns(env, extra_data):
|
||||
# 1. start mdns application
|
||||
dut1.start_app()
|
||||
# 2. get the dut host name (and IP address)
|
||||
specific_host = dut1.expect(re.compile(r'mdns hostname set to: \[([^\]]+)\]'), timeout=30)
|
||||
specific_host = str(specific_host[0])
|
||||
thread1 = Thread(target=mdns_server, args=(specific_host,))
|
||||
thread1.start()
|
||||
specific_host = dut1.expect(re.compile(r'mdns hostname set to: \[([^\]]+)\]'), timeout=30)[0]
|
||||
mdns_responder = Thread(target=mdns_server, args=(str(specific_host),))
|
||||
try:
|
||||
ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)[0]
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
console_log('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
stop_mdns_server.set()
|
||||
thread1.join()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
try:
|
||||
# 3. check the mdns name is accessible
|
||||
mdns_responder.start()
|
||||
if not esp_answered.wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer within timeout')
|
||||
if not esp_delegated_answered.wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout')
|
||||
# 4. check DUT output if mdns advertized host is resolved
|
||||
dut1.expect(re.compile(r'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut1.expect(re.compile(r'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
|
||||
@@ -127,13 +142,13 @@ def test_examples_protocol_mdns(env, extra_data):
|
||||
# 5. check the DUT answers to `dig` command
|
||||
dig_output = subprocess.check_output(['dig', '+short', '-p', '5353', '@224.0.0.251',
|
||||
'{}.local'.format(specific_host)])
|
||||
print('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
|
||||
console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
|
||||
if not ip_address.encode('utf-8') in dig_output:
|
||||
raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig'
|
||||
"Output should've contained DUT's IP address:{}".format(ip_address))
|
||||
finally:
|
||||
stop_mdns_server.set()
|
||||
thread1.join()
|
||||
mdns_responder.join()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "string.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
@@ -46,12 +47,6 @@
|
||||
|
||||
#define MASTER_TAG "MASTER_TEST"
|
||||
|
||||
#define MASTER_CHECK(a, ret_val, str, ...) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
// The macro to get offset for parameter in the appropriate structure
|
||||
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
|
||||
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
|
||||
@@ -365,6 +360,7 @@ static void master_destroy_slave_list(char** table)
|
||||
for (int i = 0; ((i < MB_DEVICE_COUNT) && table[i] != NULL); i++) {
|
||||
if (table[i]) {
|
||||
free(table[i]);
|
||||
table[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,48 +496,52 @@ static void master_operation_func(void *arg)
|
||||
}
|
||||
ESP_LOGI(MASTER_TAG, "Destroy master...");
|
||||
vTaskDelay(100);
|
||||
ESP_ERROR_CHECK(mbc_master_destroy());
|
||||
}
|
||||
|
||||
// Modbus master initialization
|
||||
static esp_err_t master_init(void)
|
||||
static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(result);
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"nvs_flash_init fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
result = esp_netif_init();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"esp_netif_init fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
result = esp_event_loop_create_default();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"esp_event_loop_create_default fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
// Start mdns service and register device
|
||||
master_start_mdns_service();
|
||||
#endif
|
||||
|
||||
// This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
// Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
// examples/protocols/README.md for more information about this function.
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
|
||||
mb_communication_info_t comm_info = { 0 };
|
||||
comm_info.ip_port = MB_TCP_PORT;
|
||||
#if !CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
comm_info.ip_addr_type = MB_IPV4;
|
||||
#else
|
||||
comm_info.ip_addr_type = MB_IPV6;
|
||||
result = example_connect();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"example_connect fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
result = esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"esp_wifi_set_ps fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#endif
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
comm_info.ip_addr = (void*)slave_ip_address_table;
|
||||
comm_info.ip_netif_ptr = (void*)get_example_netif();
|
||||
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
int res = 0;
|
||||
for (int retry = 0; (res < num_device_parameters) && (retry < 10); retry++) {
|
||||
res = master_query_slave_service("_modbus", "_tcp", comm_info.ip_addr_type);
|
||||
res = master_query_slave_service("_modbus", "_tcp", ip_addr_type);
|
||||
}
|
||||
if (res < num_device_parameters) {
|
||||
ESP_LOGE(MASTER_TAG, "Could not resolve one or more slave IP addresses, resolved: %d out of %d.", res, num_device_parameters );
|
||||
@@ -555,46 +555,109 @@ static esp_err_t master_init(void)
|
||||
ESP_LOGI(MASTER_TAG, "Configured %d IP addresse(s).", ip_cnt);
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Fail to get IP address from stdin. Continue.");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t destroy_services(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
master_destroy_slave_list(slave_ip_address_table);
|
||||
#endif
|
||||
err = example_disconnect();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"example_disconnect fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = esp_event_loop_delete_default();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"esp_event_loop_delete_default fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = esp_netif_deinit();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"esp_netif_deinit fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = nvs_flash_deinit();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"nvs_flash_deinit fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Modbus master initialization
|
||||
static esp_err_t master_init(mb_communication_info_t* comm_info)
|
||||
{
|
||||
void* master_handler = NULL;
|
||||
|
||||
esp_err_t err = mbc_master_init_tcp(&master_handler);
|
||||
MASTER_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
|
||||
ESP_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mb controller initialization fail.");
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mb controller initialization fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
err = mbc_master_setup((void*)&comm_info);
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
err = mbc_master_setup((void*)comm_info);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mb controller setup fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mb controller set descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
ESP_LOGI(MASTER_TAG, "Modbus master stack initialized...");
|
||||
|
||||
err = mbc_master_start();
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mb controller start fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
vTaskDelay(5);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t master_destroy(void)
|
||||
{
|
||||
esp_err_t err = mbc_master_destroy();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
MASTER_TAG,
|
||||
"mbc_master_destroy fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
ESP_LOGI(MASTER_TAG, "Modbus master stack destroy...");
|
||||
return err;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialization of device peripheral and objects
|
||||
ESP_ERROR_CHECK(master_init());
|
||||
vTaskDelay(10);
|
||||
mb_tcp_addr_type_t ip_addr_type;
|
||||
#if !CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
ip_addr_type = MB_IPV4;
|
||||
#else
|
||||
ip_addr_type = MB_IPV6;
|
||||
#endif
|
||||
ESP_ERROR_CHECK(init_services(ip_addr_type));
|
||||
|
||||
mb_communication_info_t comm_info = { 0 };
|
||||
comm_info.ip_port = MB_TCP_PORT;
|
||||
comm_info.ip_addr_type = ip_addr_type;
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
comm_info.ip_addr = (void*)slave_ip_address_table;
|
||||
comm_info.ip_netif_ptr = (void*)get_example_netif();
|
||||
|
||||
ESP_ERROR_CHECK(master_init(&comm_info));
|
||||
vTaskDelay(50);
|
||||
|
||||
master_operation_func(NULL);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
master_destroy_slave_list(slave_ip_address_table);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(master_destroy());
|
||||
ESP_ERROR_CHECK(destroy_services());
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
@@ -87,7 +87,7 @@ static inline char* gen_host_name_str(char* service_name, char* name)
|
||||
return name;
|
||||
}
|
||||
|
||||
static void start_mdns_service()
|
||||
static void start_mdns_service(void)
|
||||
{
|
||||
char temp_str[32] = {0};
|
||||
uint8_t sta_mac[6] = {0};
|
||||
@@ -115,6 +115,11 @@ static void start_mdns_service()
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set("_modbus", "_tcp", "mb_id", gen_id_str("\0", temp_str)));
|
||||
}
|
||||
|
||||
static void stop_mdns_service(void)
|
||||
{
|
||||
mdns_free();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Set register values into known state
|
||||
@@ -152,104 +157,9 @@ static void setup_reg_data(void)
|
||||
input_reg_params.input_data7 = 4.78;
|
||||
}
|
||||
|
||||
// An example application of Modbus slave. It is based on freemodbus stack.
|
||||
// See deviceparams.h file for more information about assigned Modbus parameters.
|
||||
// These parameters can be accessed from main application and also can be changed
|
||||
// by external Modbus master host.
|
||||
void app_main(void)
|
||||
static void slave_operation_func(void *arg)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(result);
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
start_mdns_service();
|
||||
#endif
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
|
||||
// Set UART log level
|
||||
esp_log_level_set(SLAVE_TAG, ESP_LOG_INFO);
|
||||
void* mbc_slave_handler = NULL;
|
||||
|
||||
ESP_ERROR_CHECK(mbc_slave_init_tcp(&mbc_slave_handler)); // Initialization of Modbus controller
|
||||
|
||||
mb_param_info_t reg_info; // keeps the Modbus registers access information
|
||||
mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
|
||||
|
||||
mb_communication_info_t comm_info = { 0 };
|
||||
comm_info.ip_port = MB_TCP_PORT_NUMBER;
|
||||
#if !CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
comm_info.ip_addr_type = MB_IPV4;
|
||||
#else
|
||||
comm_info.ip_addr_type = MB_IPV6;
|
||||
#endif
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
comm_info.ip_addr = NULL;
|
||||
comm_info.ip_netif_ptr = (void*)get_example_netif();
|
||||
// Setup communication parameters and start stack
|
||||
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
|
||||
|
||||
// The code below initializes Modbus register area descriptors
|
||||
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
||||
// Initialization should be done for each supported Modbus register area according to register map.
|
||||
// When external master trying to access the register in the area that is not initialized
|
||||
// by mbc_slave_set_descriptor() API call then Modbus stack
|
||||
// will send exception response for this register area.
|
||||
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
||||
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
|
||||
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
|
||||
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
||||
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
|
||||
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
|
||||
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Input Registers area
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
|
||||
reg_area.address = (void*)&input_reg_params.input_data0;
|
||||
reg_area.size = sizeof(float) << 2;
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
// Initialization of Input Registers area
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
|
||||
reg_area.address = (void*)&input_reg_params.input_data4;
|
||||
reg_area.size = sizeof(float) << 2;
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Coils register area
|
||||
reg_area.type = MB_PARAM_COIL;
|
||||
reg_area.start_offset = MB_REG_COILS_START;
|
||||
reg_area.address = (void*)&coil_reg_params;
|
||||
reg_area.size = sizeof(coil_reg_params);
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Discrete Inputs register area
|
||||
reg_area.type = MB_PARAM_DISCRETE;
|
||||
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
||||
reg_area.address = (void*)&discrete_reg_params;
|
||||
reg_area.size = sizeof(discrete_reg_params);
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
setup_reg_data(); // Set values into known state
|
||||
|
||||
// Starts of modbus controller and stack
|
||||
ESP_ERROR_CHECK(mbc_slave_start());
|
||||
|
||||
ESP_LOGI(SLAVE_TAG, "Modbus slave stack initialized.");
|
||||
ESP_LOGI(SLAVE_TAG, "Start modbus test...");
|
||||
@@ -310,8 +220,221 @@ void app_main(void)
|
||||
// Destroy of Modbus controller on alarm
|
||||
ESP_LOGI(SLAVE_TAG,"Modbus controller destroyed.");
|
||||
vTaskDelay(100);
|
||||
ESP_ERROR_CHECK(mbc_slave_destroy());
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
mdns_free();
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t init_services(void)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"nvs_flash_init fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
result = esp_netif_init();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"esp_netif_init fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
result = esp_event_loop_create_default();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"esp_event_loop_create_default fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
// Start mdns service and register device
|
||||
start_mdns_service();
|
||||
#endif
|
||||
// This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
// Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
// examples/protocols/README.md for more information about this function.
|
||||
result = example_connect();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"example_connect fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
result = esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"esp_wifi_set_ps fail, returns(0x%x).",
|
||||
(uint32_t)result);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t destroy_services(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
err = example_disconnect();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"example_disconnect fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = esp_event_loop_delete_default();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"esp_event_loop_delete_default fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = esp_netif_deinit();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"esp_netif_deinit fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = nvs_flash_deinit();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"nvs_flash_deinit fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
stop_mdns_service();
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
// Modbus slave initialization
|
||||
static esp_err_t slave_init(mb_communication_info_t* comm_info)
|
||||
{
|
||||
mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
|
||||
|
||||
void* slave_handler = NULL;
|
||||
|
||||
// Initialization of Modbus controller
|
||||
esp_err_t err = mbc_slave_init_tcp(&slave_handler);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK && slave_handler != NULL), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mb controller initialization fail.");
|
||||
|
||||
comm_info->ip_addr = NULL; // Bind to any address
|
||||
comm_info->ip_netif_ptr = (void*)get_example_netif();
|
||||
|
||||
// Setup communication parameters and start stack
|
||||
err = mbc_slave_setup((void*)comm_info);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_setup fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// The code below initializes Modbus register area descriptors
|
||||
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
||||
// Initialization should be done for each supported Modbus register area according to register map.
|
||||
// When external master trying to access the register in the area that is not initialized
|
||||
// by mbc_slave_set_descriptor() API call then Modbus stack
|
||||
// will send exception response for this register area.
|
||||
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
||||
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
|
||||
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
|
||||
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
||||
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
|
||||
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
|
||||
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// Initialization of Input Registers area
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
|
||||
reg_area.address = (void*)&input_reg_params.input_data0;
|
||||
reg_area.size = sizeof(float) << 2;
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
|
||||
reg_area.address = (void*)&input_reg_params.input_data4;
|
||||
reg_area.size = sizeof(float) << 2;
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// Initialization of Coils register area
|
||||
reg_area.type = MB_PARAM_COIL;
|
||||
reg_area.start_offset = MB_REG_COILS_START;
|
||||
reg_area.address = (void*)&coil_reg_params;
|
||||
reg_area.size = sizeof(coil_reg_params);
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// Initialization of Discrete Inputs register area
|
||||
reg_area.type = MB_PARAM_DISCRETE;
|
||||
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
||||
reg_area.address = (void*)&discrete_reg_params;
|
||||
reg_area.size = sizeof(discrete_reg_params);
|
||||
err = mbc_slave_set_descriptor(reg_area);
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// Set values into known state
|
||||
setup_reg_data();
|
||||
|
||||
// Starts of modbus controller and stack
|
||||
err = mbc_slave_start();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_start fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
vTaskDelay(5);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t slave_destroy(void)
|
||||
{
|
||||
esp_err_t err = mbc_slave_destroy();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
SLAVE_TAG,
|
||||
"mbc_slave_destroy fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// An example application of Modbus slave. It is based on freemodbus stack.
|
||||
// See deviceparams.h file for more information about assigned Modbus parameters.
|
||||
// These parameters can be accessed from main application and also can be changed
|
||||
// by external Modbus master host.
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(init_services());
|
||||
|
||||
// Set UART log level
|
||||
esp_log_level_set(SLAVE_TAG, ESP_LOG_INFO);
|
||||
|
||||
mb_communication_info_t comm_info = { 0 };
|
||||
|
||||
#if !CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
comm_info.ip_addr_type = MB_IPV4;
|
||||
#else
|
||||
comm_info.ip_addr_type = MB_IPV6;
|
||||
#endif
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
|
||||
comm_info.ip_port = MB_TCP_PORT_NUMBER;
|
||||
ESP_ERROR_CHECK(slave_init(&comm_info));
|
||||
|
||||
// The Modbus slave logic is located in this function (user handling of Modbus)
|
||||
slave_operation_func(NULL);
|
||||
|
||||
ESP_ERROR_CHECK(slave_destroy());
|
||||
ESP_ERROR_CHECK(destroy_services());
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-C3 |
|
||||
| Supported Targets | ESP32-S2 | ESP32-C3 | ESP32-S3 |
|
||||
# ESP-MQTT SSL Mutual Authentication with Digital Signature
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Espressif's ESP32-S2 and ESP32-C3 MCU have a built-in Digital Signature (DS) Peripheral, which provides hardware acceleration for RSA signature. More details can be found at [Digital Signature with ESP-TLS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/protocols/esp_tls.html#digital-signature-with-esp-tls).
|
||||
Espressif's ESP32-S2, ESP32-S3 and ESP32-C3 MCU have a built-in Digital Signature (DS) Peripheral, which provides hardware acceleration for RSA signature. More details can be found at [Digital Signature with ESP-TLS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/protocols/esp_tls.html#digital-signature-with-esp-tls).
|
||||
|
||||
This example connects to the broker test.mosquitto.org using ssl transport with client certificate(RSA) and as a demonstration subscribes/unsubscribes and sends a message on certain topic.The RSA signature operation required in the ssl connection is performed with help of the Digital Signature (DS) peripheral.
|
||||
(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org)
|
||||
@@ -12,12 +12,12 @@ It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any ESP32-S2, ESP32-C3 board (which has a built-in DS peripheral), the only required interface is WiFi and connection to internet.
|
||||
This example can be executed on any ESP32-S2, ESP32-S3, ESP32-C3 board (which has a built-in DS peripheral), the only required interface is WiFi and connection to internet.
|
||||
|
||||
### Configure the project
|
||||
|
||||
#### 1) Selecting the target
|
||||
As the project is to be built for the target ESP32-S2, ESP32-C3 it should be selected with the following command
|
||||
As the project is to be built for the target ESP32-S2, ESP32-S3, ESP32-C3 it should be selected with the following command
|
||||
```
|
||||
idf.py set-target /* target */
|
||||
```
|
||||
@@ -99,7 +99,7 @@ DATA=data
|
||||
|
||||
|
||||
### configure_ds.py
|
||||
The script [configure_ds.py](./configure_ds.py) is used for configuring the DS peripheral on the ESP32-S2/ESP32-C3 SoC. The steps in the script are based on technical details of certain operations in the Digital Signature calculation, which can be found at Digital Signature Section of [ESP32-S2 TRM](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
|
||||
The script [configure_ds.py](./configure_ds.py) is used for configuring the DS peripheral on the ESP32-S2/ESP32-S3/ESP32-C3 SoC. The steps in the script are based on technical details of certain operations in the Digital Signature calculation, which can be found at Digital Signature Section of [ESP32-S2 TRM](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
|
||||
|
||||
The configuration script performs the following steps -
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ csv_filename = esp_ds_data_dir + '/pre_prov.csv'
|
||||
bin_filename = esp_ds_data_dir + '/pre_prov.bin'
|
||||
expected_json_path = os.path.join('build', 'config', 'sdkconfig.json')
|
||||
# Targets supported by the script
|
||||
supported_targets = {'esp32s2', 'esp32c3'}
|
||||
supported_key_size = {'esp32s2':[1024, 2048, 3072, 4096], 'esp32c3':[1024, 2048, 3072]}
|
||||
supported_targets = {'esp32s2', 'esp32c3', 'esp32s3'}
|
||||
supported_key_size = {'esp32s2':[1024, 2048, 3072, 4096], 'esp32c3':[1024, 2048, 3072], 'esp32s3':[1024, 2048, 3072, 4096]}
|
||||
|
||||
|
||||
# @return
|
||||
@@ -89,7 +89,7 @@ def number_as_bytes(number, pad_bits=None):
|
||||
# privkey : path to the RSA private key
|
||||
# priv_key_pass : path to the RSA privaete key password
|
||||
# hmac_key : HMAC key value ( to calculate DS params)
|
||||
# idf_target : The target chip for the script (e.g. esp32s2, esp32c3)
|
||||
# idf_target : The target chip for the script (e.g. esp32s2, esp32c3, esp32s3)
|
||||
# @info
|
||||
# The function calculates the encrypted private key parameters.
|
||||
# Consult the DS documentation (available for the ESP32-S2) in the esp-idf programming guide for more details about the variables and calculations.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRED_IDF_TARGETS esp32s2 esp32c3)
|
||||
REQUIRED_IDF_TARGETS esp32s2 esp32c3 esp32s3)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
**Note:** This example uses an internal implementation of the [modem component](components/modem) that will not be maintained in `v5.0` and this example will be migrated to [esp-modem component](https://components.espressif.com/component/espressif/esp_modem).
|
||||
|
||||
## Overview
|
||||
|
||||
A general PPP application consists of two parts: PPP server which is provided by cellular modem module and PPP client which is provided by ESP32 in this example.
|
||||
@@ -14,7 +16,7 @@ When PPP connection has been established, the IP packet flow from application si
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC).
|
||||
For test purpose, you also need a cellular modem module. Here we take the [SIM800L](https://www.simcom.com/product/SIM800.html) and [BG96](https://www.quectel.com/product/bg96.htm) as an example.
|
||||
For test purpose, you also need a cellular modem module. Here we take the [SIM800L](https://www.simcom.com/product/SIM800.html) and [BG96](https://www.quectel.com/product/lpwa-bg96-cat-m1-nb1-egprs/) as an example.
|
||||
You can also try other modules as long as they embedded PPP protocol.
|
||||
|
||||
**Note:** Since SIM800L only support **2G** which will **not** work in some countries. And also keep in mind that in some other countries it will stop working soon (many remaining 2G networks will be switched off in the next 2-3 years). So you should **check with your local providers for further details** if you try this example with any 2G modules.
|
||||
|
||||
@@ -56,11 +56,13 @@ typedef struct {
|
||||
int cts_io_num; /*!< CTS Pin Number */
|
||||
int rx_buffer_size; /*!< UART RX Buffer Size */
|
||||
int tx_buffer_size; /*!< UART TX Buffer Size */
|
||||
int pattern_queue_size; /*!< UART Pattern Queue Size */
|
||||
int event_queue_size; /*!< UART Event Queue Size */
|
||||
uint32_t event_task_stack_size; /*!< UART Event Task Stack size */
|
||||
int event_task_priority; /*!< UART Event Task Priority */
|
||||
int line_buffer_size; /*!< Line buffer size for command mode */
|
||||
union {
|
||||
int dte_buffer_size; /*!< Internal buffer size */
|
||||
int line_buffer_size; /*!< Compatible option for the internal buffer size */
|
||||
};
|
||||
} esp_modem_dte_config_t;
|
||||
|
||||
/**
|
||||
@@ -87,11 +89,10 @@ typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *contex
|
||||
.cts_io_num = 23, \
|
||||
.rx_buffer_size = 1024, \
|
||||
.tx_buffer_size = 512, \
|
||||
.pattern_queue_size = 20, \
|
||||
.event_queue_size = 30, \
|
||||
.event_task_stack_size = 2048, \
|
||||
.event_task_priority = 5, \
|
||||
.line_buffer_size = 512 \
|
||||
.dte_buffer_size = 512 \
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,28 +61,6 @@ static inline void strip_cr_lf_tail(char *str, uint32_t len)
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_response_default(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+CSQ (Get signal quality)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_csq(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+CBC (Get battery status)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_cbc(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from +++ (Set Working Mode)
|
||||
*
|
||||
@@ -105,50 +83,6 @@ esp_err_t esp_modem_dce_handle_exit_data_mode(modem_dce_t *dce, const char *line
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_atd_ppp(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+CGMM (Get DCE module name)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_cgmm(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+CGSN (Get DCE module IMEI number)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_cgsn(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+CIMI (Get DCE module IMSI number)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_cimi(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Handle response from AT+COPS? (Get Operator's name)
|
||||
*
|
||||
* @param dce Modem DCE object
|
||||
* @param line line string
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_modem_dce_handle_cops(modem_dce_t *dce, const char *line);
|
||||
|
||||
/**
|
||||
* @brief Syncronization
|
||||
*
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
|
||||
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
|
||||
|
||||
#define MIN_PATTERN_INTERVAL (9)
|
||||
#define MIN_POST_IDLE (0)
|
||||
#define MIN_PRE_IDLE (0)
|
||||
/**
|
||||
* @brief This sets the threshold for receiving data events when UART RX buffer reaches
|
||||
* this level. Decreasing the number causes more events and lowers changes of UART overflows,
|
||||
* but more allocations in lwIP. You can increase this number if you're using slower baudrates
|
||||
* or having the UART ISR in IRAM.
|
||||
*/
|
||||
#define ESP_MODEM_UART_RX_FULL_THRESHOLD (64)
|
||||
|
||||
/**
|
||||
* @brief Macro defined for error checking
|
||||
@@ -59,8 +63,8 @@ typedef struct {
|
||||
modem_dte_t parent; /*!< DTE interface that should extend */
|
||||
esp_modem_on_receive receive_cb; /*!< ptr to data reception */
|
||||
void *receive_cb_ctx; /*!< ptr to rx fn context data */
|
||||
int line_buffer_size; /*!< line buffer size in commnad mode */
|
||||
int pattern_queue_size; /*!< UART pattern queue size */
|
||||
int buffer_size; /*!< internal buffer size */
|
||||
int consumed; /*!< index to the consumed buffer pointer */
|
||||
} esp_modem_dte_t;
|
||||
|
||||
/**
|
||||
@@ -95,22 +99,31 @@ esp_err_t esp_modem_set_rx_cb(modem_dte_t *dte, esp_modem_on_receive receive_cb,
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
static esp_err_t esp_dte_handle_line(esp_modem_dte_t *esp_dte)
|
||||
static esp_err_t esp_dte_handle_line(esp_modem_dte_t *esp_dte, char * line, size_t len, char separator)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
modem_dce_t *dce = esp_dte->parent.dce;
|
||||
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
|
||||
const char *line = (const char *)(esp_dte->buffer);
|
||||
size_t len = strlen(line);
|
||||
/* Skip pure "\r\n" lines */
|
||||
if (len > 2 && !is_only_cr_lf(line, len)) {
|
||||
if (dce->handle_line == NULL) {
|
||||
/* Received an asynchronous line, but no handler waiting this this */
|
||||
ESP_LOGD(MODEM_TAG, "No handler for line: %s", line);
|
||||
err = ESP_OK; /* Not an error, just propagate the line to user handler */
|
||||
goto post_event_unknown;
|
||||
}
|
||||
if (separator != '\n' && dce->handle_line) {
|
||||
/* If waiting for a specific separator, just pass the entire string */
|
||||
MODEM_CHECK(dce->handle_line(dce, line) == ESP_OK, "handle line failed", post_event_unknown);
|
||||
return ESP_OK;
|
||||
}
|
||||
/* Tokenize the data to call handlers separately for each *line* */
|
||||
char *str_ptr = NULL;
|
||||
char *p = strtok_r(line, "\n", &str_ptr);
|
||||
while (p) {
|
||||
if (len > 2 && !is_only_cr_lf(p, strlen(p))) {
|
||||
ESP_LOGD(MODEM_TAG, "Handling line: >>%s\n<<", p);
|
||||
if (dce->handle_line == NULL) {
|
||||
/* Received an asynchronous line, but no handler waiting this this */
|
||||
ESP_LOGD(MODEM_TAG, "No handler for line: %s", p);
|
||||
err = ESP_OK; /* Not an error, just propagate the line to user handler */
|
||||
goto post_event_unknown;
|
||||
}
|
||||
MODEM_CHECK(dce->handle_line(dce, p) == ESP_OK, "handle line failed", post_event_unknown);
|
||||
}
|
||||
p = strtok_r(NULL, "\n", &str_ptr);
|
||||
}
|
||||
return ESP_OK;
|
||||
post_event_unknown:
|
||||
@@ -121,54 +134,6 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle when a pattern has been detected by UART
|
||||
*
|
||||
* @param esp_dte ESP32 Modem DTE object
|
||||
*/
|
||||
static void esp_handle_uart_pattern(esp_modem_dte_t *esp_dte)
|
||||
{
|
||||
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
|
||||
int read_len = 0;
|
||||
|
||||
if (esp_dte->parent.dce->mode == MODEM_PPP_MODE) {
|
||||
ESP_LOGD(MODEM_TAG, "Pattern event in PPP mode ignored");
|
||||
// Ignore potential pattern detection events in PPP mode
|
||||
// Note 1: the interrupt is disabled, but some events might still be pending
|
||||
// Note 2: checking the mode *after* uart_pattern_pop_pos() to consume the event
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos != -1) {
|
||||
if (pos < esp_dte->line_buffer_size - 1) {
|
||||
/* read one line(include '\n') */
|
||||
read_len = pos + 1;
|
||||
} else {
|
||||
ESP_LOGW(MODEM_TAG, "ESP Modem Line buffer too small");
|
||||
read_len = esp_dte->line_buffer_size - 1;
|
||||
}
|
||||
read_len = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, read_len, pdMS_TO_TICKS(100));
|
||||
if (read_len) {
|
||||
/* make sure the line is a standard string */
|
||||
esp_dte->buffer[read_len] = '\0';
|
||||
/* Send new line to handle */
|
||||
esp_dte_handle_line(esp_dte);
|
||||
} else {
|
||||
ESP_LOGE(MODEM_TAG, "uart read bytes failed");
|
||||
}
|
||||
} else {
|
||||
size_t length = 0;
|
||||
uart_get_buffered_data_len(esp_dte->uart_port, &length);
|
||||
if (length) {
|
||||
ESP_LOGD(MODEM_TAG, "Pattern not found in the pattern queue, uart data length = %d", length);
|
||||
length = MIN(esp_dte->line_buffer_size-1, length);
|
||||
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem-pattern: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
|
||||
}
|
||||
uart_flush(esp_dte->uart_port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle when new data received by UART
|
||||
*
|
||||
@@ -178,40 +143,28 @@ static void esp_handle_uart_data(esp_modem_dte_t *esp_dte)
|
||||
{
|
||||
size_t length = 0;
|
||||
uart_get_buffered_data_len(esp_dte->uart_port, &length);
|
||||
ESP_LOGV(MODEM_TAG, "uart_get_buffered_data_len()=%d", length);
|
||||
if (esp_dte->parent.dce->mode != MODEM_PPP_MODE && length) {
|
||||
// Check if matches the pattern to process the data as pattern
|
||||
int pos = uart_pattern_get_pos(esp_dte->uart_port);
|
||||
if (pos > -1) {
|
||||
esp_handle_uart_pattern(esp_dte);
|
||||
return;
|
||||
}
|
||||
// Read the data and process it using `handle_line` logic
|
||||
length = MIN(esp_dte->line_buffer_size-1, length);
|
||||
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
|
||||
esp_dte->buffer[length] = '\0';
|
||||
if (strchr((char*)esp_dte->buffer, '\n') == NULL) {
|
||||
size_t max = esp_dte->line_buffer_size-1;
|
||||
size_t bytes;
|
||||
// if pattern not found in the data,
|
||||
// continue reading as long as the modem is in MODEM_STATE_PROCESSING, checking for the pattern
|
||||
while (length < max && esp_dte->buffer[length-1] != '\n' &&
|
||||
esp_dte->parent.dce->state == MODEM_STATE_PROCESSING) {
|
||||
bytes = uart_read_bytes(esp_dte->uart_port,
|
||||
esp_dte->buffer + length, 1, pdMS_TO_TICKS(100));
|
||||
length += bytes;
|
||||
ESP_LOGV("esp-modem: debug_data", "Continuous read in non-data mode: length: %d char: %x", length, esp_dte->buffer[length-1]);
|
||||
}
|
||||
length = MIN(esp_dte->buffer_size - 1, length);
|
||||
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer + esp_dte->consumed, length - esp_dte->consumed, portMAX_DELAY);
|
||||
const char separator = esp_dte->parent.dce->prompt == NULL ? '\n' : (esp_dte->parent.dce->prompt)[strlen(esp_dte->parent.dce->prompt)-1];
|
||||
if (memchr(esp_dte->buffer + esp_dte->consumed, separator, length)) {
|
||||
esp_dte->buffer[length] = '\0';
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
|
||||
if (esp_dte->parent.dce->handle_line) {
|
||||
/* Send new line to handle if handler registered */
|
||||
esp_dte_handle_line(esp_dte);
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: pattern-detection", esp_dte->buffer, length, ESP_LOG_VERBOSE);
|
||||
if (esp_dte->parent.dce->handle_line) {
|
||||
/* Send new line to handle if handler registered */
|
||||
if (esp_dte_handle_line(esp_dte, (char*)esp_dte->buffer, length, separator) == ESP_OK) {
|
||||
esp_dte->consumed = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
esp_dte->consumed += length;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
length = MIN(esp_dte->line_buffer_size, length);
|
||||
length = MIN(esp_dte->buffer_size, length);
|
||||
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
|
||||
/* pass the input data to configured callback */
|
||||
if (length) {
|
||||
@@ -266,16 +219,12 @@ static void uart_event_task_entry(void *param)
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGE(MODEM_TAG, "Frame Error");
|
||||
break;
|
||||
case UART_PATTERN_DET:
|
||||
esp_handle_uart_pattern(esp_dte);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(MODEM_TAG, "unknown uart event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,9 +241,11 @@ static esp_err_t esp_modem_dte_send_cmd(modem_dte_t *dte, const char *command, u
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
modem_dce_t *dce = dte->dce;
|
||||
ESP_LOGD(MODEM_TAG, "Sending command:%s", command);
|
||||
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
|
||||
MODEM_CHECK(command, "command is NULL", err);
|
||||
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
|
||||
esp_dte->consumed = 0;
|
||||
/* Calculate timeout clock tick */
|
||||
/* Reset runtime information */
|
||||
dce->state = MODEM_STATE_PROCESSING;
|
||||
@@ -364,19 +315,14 @@ static esp_err_t esp_modem_dte_send_wait(modem_dte_t *dte, const char *data, uin
|
||||
MODEM_CHECK(prompt, "prompt is NULL", err_param);
|
||||
modem_dce_t *dce = dte->dce;
|
||||
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err_param);
|
||||
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
|
||||
// We'd better change pattern detection here for a moment in case prompt string contains the pattern character
|
||||
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, ' ', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
|
||||
dce->prompt = prompt;
|
||||
dce->prompt = prompt; // the last character of this prompt will be used as a separator to call the line handker
|
||||
dce->handle_line = esp_modem_dte_send_wait_default_handler;
|
||||
MODEM_CHECK(dte->send_cmd(dte, data, timeout) == ESP_OK, "wait for prompt timeout", err);
|
||||
MODEM_CHECK(dce->state == MODEM_STATE_SUCCESS, "wait for prompt failed", err);
|
||||
dce->prompt = NULL;
|
||||
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
|
||||
return ESP_OK;
|
||||
err:
|
||||
dce->prompt = NULL;
|
||||
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
|
||||
err_param:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -394,27 +340,11 @@ static esp_err_t esp_modem_dte_change_mode(modem_dte_t *dte, modem_mode_t new_mo
|
||||
{
|
||||
modem_dce_t *dce = dte->dce;
|
||||
MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
|
||||
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
|
||||
modem_mode_t current_mode = dce->mode;
|
||||
MODEM_CHECK(current_mode != new_mode, "already in mode: %d", err, new_mode);
|
||||
dce->mode = MODEM_TRANSITION_MODE; // mode switching will be finished in set_working_mode() on success
|
||||
// (or restored on failure)
|
||||
switch (new_mode) {
|
||||
case MODEM_PPP_MODE:
|
||||
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
|
||||
uart_disable_pattern_det_intr(esp_dte->uart_port);
|
||||
uart_enable_rx_intr(esp_dte->uart_port);
|
||||
break;
|
||||
case MODEM_COMMAND_MODE:
|
||||
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
|
||||
uart_disable_rx_intr(esp_dte->uart_port);
|
||||
uart_flush(esp_dte->uart_port);
|
||||
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
|
||||
uart_pattern_queue_reset(esp_dte->uart_port, esp_dte->pattern_queue_size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
|
||||
return ESP_OK;
|
||||
err_restore_mode:
|
||||
dce->mode = current_mode;
|
||||
@@ -464,8 +394,8 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
|
||||
esp_modem_dte_t *esp_dte = calloc(1, sizeof(esp_modem_dte_t));
|
||||
MODEM_CHECK(esp_dte, "calloc esp_dte failed", err_dte_mem);
|
||||
/* malloc memory to storing lines from modem dce */
|
||||
esp_dte->line_buffer_size = config->line_buffer_size;
|
||||
esp_dte->buffer = calloc(1, config->line_buffer_size);
|
||||
esp_dte->buffer_size = config->dte_buffer_size;
|
||||
esp_dte->buffer = calloc(1, config->dte_buffer_size);
|
||||
MODEM_CHECK(esp_dte->buffer, "calloc line memory failed", err_line_mem);
|
||||
/* Set attributes */
|
||||
esp_dte->uart_port = config->port_num;
|
||||
@@ -514,15 +444,9 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
|
||||
res = uart_set_rx_timeout(esp_dte->uart_port, 1);
|
||||
MODEM_CHECK(res == ESP_OK, "set rx timeout failed", err_uart_config);
|
||||
|
||||
/* Set pattern interrupt, used to detect the end of a line. */
|
||||
res = uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
|
||||
/* Set pattern queue size */
|
||||
esp_dte->pattern_queue_size = config->pattern_queue_size;
|
||||
res |= uart_pattern_queue_reset(esp_dte->uart_port, config->pattern_queue_size);
|
||||
/* Starting in command mode -> explicitly disable RX interrupt */
|
||||
uart_disable_rx_intr(esp_dte->uart_port);
|
||||
res = uart_set_rx_full_threshold(config->port_num, ESP_MODEM_UART_RX_FULL_THRESHOLD);
|
||||
MODEM_CHECK(res == ESP_OK, "config rx full threshold failed", err_uart_config);
|
||||
|
||||
MODEM_CHECK(res == ESP_OK, "config uart pattern failed", err_uart_pattern);
|
||||
/* Create Event loop */
|
||||
esp_event_loop_args_t loop_args = {
|
||||
.queue_size = ESP_MODEM_EVENT_QUEUE_SIZE,
|
||||
@@ -553,8 +477,6 @@ err_sem:
|
||||
err_sem1:
|
||||
esp_event_loop_delete(esp_dte->event_loop_hdl);
|
||||
err_eloop:
|
||||
uart_disable_pattern_det_intr(esp_dte->uart_port);
|
||||
err_uart_pattern:
|
||||
uart_driver_delete(esp_dte->uart_port);
|
||||
err_uart_config:
|
||||
free(esp_dte->buffer);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user