rmt + some of link layer

This commit is contained in:
Justin Chow
2025-07-06 00:28:01 -04:00
committed by Johnathon Slightham
parent 701f56dc31
commit e16f332476
18 changed files with 2555 additions and 169 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
/build /build
.DS_Store .DS_Store
sdkconfig.old sdkconfig.old
.vscode/*

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "DataLinkManager.cpp"
PRIV_REQUIRES driver esp_event nvs_flash esp_netif rmt
INCLUDE_DIRS "include")

View File

@@ -0,0 +1,635 @@
#include "DataLinkManager.h"
#include "RMTManager.h"
#include "esp_log.h"
#include "nvs_flash.h"
/**
* @brief Construct a new Data Link Manager:: Data Link Manager object
*
* @param board_id Board ID of the current board. Will be written to the NVM under key "board" if not already written.
*/
DataLinkManager::DataLinkManager(uint8_t board_id){
//init table for this board and set up link layer priority queue
phys_comms = std::make_unique<RMTManager>();
if (phys_comms == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "RMT object was not created. Link layer communications will not function.");
return;
}
if (get_board_id(this_board_id) != ESP_OK){
//failed to read from NVM for board id under key "board". Will write a new entry
this_board_id = board_id;
set_board_id(this_board_id);
}
if (this_board_id != board_id){
//NVM board id is different from `board_id` -> update entry to the new board id
this_board_id = board_id;
set_board_id(this_board_id);
}
init_rip();
}
DataLinkManager::~DataLinkManager(){
phys_comms.reset(); //not strictly necessary to do this explicitly
}
esp_err_t DataLinkManager::set_board_id(uint8_t board_id){
if (board_id == BROADCAST_ADDR || board_id == PC_ADDR){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid board id");
return ESP_FAIL;
}
nvs_handle_t handle;
esp_err_t res = nvs_open("board", NVS_READWRITE, &handle);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to open NVS Handle");
return res;
}
res = nvs_set_u8(handle, "id", board_id);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to write ID %d to NVM", board_id);
nvs_close(handle);
return res;
}
res = nvs_commit(handle);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to commit write");
nvs_close(handle);
return res;
}
this_board_id = board_id;
printf("Successfully wrote %d to NVM\n", board_id);
nvs_close(handle);
return ESP_OK;
}
esp_err_t DataLinkManager::get_board_id(uint8_t& board_id){
nvs_handle_t handle;
esp_err_t res = nvs_open("board", NVS_READWRITE, &handle);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to open NVS Handle");
return res;
}
res = nvs_get_u8(handle, "id", &board_id);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to get ID from NVM. Please make sure NVM is already assigned a board id!");
nvs_close(handle);
return res;
}
printf("Successfully got board id %d from NVM\n", board_id);
nvs_close(handle);
return ESP_OK;
}
/**
* @brief Sends a frame to another board (node to node communication) via RMT (physical layer)
*
* @param dest_board 8 bit ID of the destination board
* @param data
* @param data_len Length of the data in bytes
* @param type
* @return esp_err_t
*/
esp_err_t DataLinkManager::send(uint8_t dest_board, uint8_t* data, uint16_t data_len, FrameType type, uint8_t curr_channel){
if (phys_comms == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to send frame due to no RMT object");
return ESP_FAIL;
}
if (data == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "Data array does not exist");
return ESP_FAIL;
}
if (this_board_id == PC_ADDR){
ESP_LOGE(DEBUG_LINK_TAG, "This board is not assigned a board id");
return ESP_FAIL;
}
if (curr_channel >= MAX_CHANNELS){
return ESP_FAIL;
}
if (IS_CONTROL_FRAME(static_cast<uint8_t>(type))){
//control frame
if (data_len > MAX_CONTROL_DATA_LEN){
ESP_LOGE(DEBUG_LINK_TAG, "Data for control frame is too large. Maximum size is %d. Current data length is %d", MAX_CONTROL_DATA_LEN, data_len);
return ESP_FAIL;
}
control_frame new_frame = {
.preamble = START_OF_FRAME,
.sender_id = this_board_id,
.receiver_id = dest_board,
.seq_num = sequence_num_map[dest_board]++,
.type_flag = static_cast<uint8_t>(type),
.data_len = static_cast<uint8_t>(data_len),
.crc_16 = 0, //not made yet
};
// printf("size of control frame %d\n", sizeof(control_frame));
// printf("size of message %d\n", new_frame.data_len);
// printf("message %s\n", data);
// print_buffer_binary(data, new_frame.data_len);
size_t frame_size = sizeof(control_frame) + new_frame.data_len - MAX_CONTROL_DATA_LEN;
// printf("frame size %d\n", frame_size);
uint8_t send_data[frame_size];
size_t offset = 0;
send_data[offset++] = new_frame.preamble;
send_data[offset++] = new_frame.sender_id;
send_data[offset++] = new_frame.receiver_id;
send_data[offset++] = new_frame.seq_num & 0xFF;
send_data[offset++] = (new_frame.seq_num >> 8) & 0xFF;
send_data[offset++] = new_frame.type_flag;
send_data[offset++] = new_frame.data_len;
memcpy(&send_data[offset], data, new_frame.data_len);
offset += new_frame.data_len;
geneate_crc_16(send_data, offset, &new_frame.crc_16);
send_data[offset++] = new_frame.crc_16 & 0xFF;
send_data[offset++] = (new_frame.crc_16 >> 8) & 0xFF;
rmt_transmit_config_t config = {
.loop_count = 0,
.flags = {
.eot_level = 0 // typically 0 or 1, depending on your output idle level
}
};
// printf("sending message:\n");
// print_buffer_binary(send_data, frame_size);
phys_comms->send(send_data, offset, &config, curr_channel);
//can wait for the rmt to finish
// esp_err_t res = phys_comms->wait_until_send_complete(curr_channel); //this cannot be here in deployment but until the RMT manager can hold this copy of data this will have to be here
// if (res != ESP_OK){
// ESP_LOGE(DEBUG_LINK_TAG, "Failed to send message");
// return ESP_FAIL;
// } else{
// // printf("Sent message to board %d\n", dest_board);
// }
} else {
//generic frame
printf("not implemented yet\n");
}
return ESP_OK;
}
void DataLinkManager::print_binary(uint8_t byte) {
for (int i = 7; i >= 0; --i) {
printf("%d", (byte >> i) & 1);
}
}
void DataLinkManager::print_buffer_binary(const uint8_t* buffer, size_t length) {
for (size_t i = 0; i < length; ++i) {
print_binary(buffer[i]);
printf(" ");
}
printf("\n");
}
/**
* @brief Starts the RMT async receive job to start listening for a new frame over a given channel
*
* @param curr_channel
* @return esp_err_t
*/
esp_err_t DataLinkManager::start_receive_frames(uint8_t curr_channel){
if (curr_channel >= MAX_CHANNELS){
return ESP_FAIL;
}
return phys_comms->start_receiving(curr_channel);
}
esp_err_t DataLinkManager::receive(uint8_t* data, size_t data_len, size_t* recv_len, uint8_t curr_channel){
if (data == NULL){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid data array");
return ESP_FAIL;
}
if (curr_channel >= MAX_CHANNELS){
return ESP_FAIL;
}
if (data_len < MAX_CONTROL_DATA_LEN + CONTROL_FRAME_OVERHEAD){
return ESP_FAIL;
}
// uint8_t recv_buf[256];
esp_err_t res = phys_comms->receive(data, data_len, recv_len, curr_channel);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "RMT Failed to receive");
return ESP_FAIL;
}
if (*recv_len > MAX_CONTROL_DATA_LEN + CONTROL_FRAME_OVERHEAD){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid control frame");
return ESP_FAIL;
}
//check for a rip frame
if (static_cast<FrameType>((data[5])) == FrameType::RIP_TABLE_CONTROL){
printf("Got a RIP frame\n");
uint8_t rip_message[rip_table_valid_rows*2] = {};
size_t rip_message_size = 0;
res = get_data_from_frame(data, *recv_len, rip_message, &rip_message_size);
if (res != ESP_OK){
return ESP_FAIL; //crc or data len failed
}
for (size_t i = 0; i < rip_message_size-1; i+=2){
uint8_t board_id = rip_message[i];
uint8_t hops = rip_message[i+1];
printf("Received: board_id %d and number of hops %d on channel %d\n", board_id, hops, curr_channel);
RIPRow* entry = nullptr;
res = rip_find_entry(board_id, &entry);
if (res != ESP_OK){
return ESP_FAIL;
}
if (entry == nullptr){
return ESP_FAIL; //no room for more entries in the table
}
if (entry->valid == RIP_INVALID_ROW){
//adding a new entry
rip_add_entry(board_id, hops + 1, curr_channel, &entry);
} else {
//updating an entry
rip_update_entry(hops + 1, curr_channel, &entry);
}
}
*recv_len = 0;
}
return ESP_OK;
}
esp_err_t DataLinkManager::get_data_from_frame(uint8_t* data, size_t data_len, uint8_t* message, size_t* message_size){
if (data == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid data array");
return ESP_FAIL;
}
if (message == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid message array");
return ESP_FAIL;
}
if (message_size == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "Invalid message size ptr");
return ESP_FAIL;
}
if (IS_CONTROL_FRAME(data[5])){
control_frame temp = {0};
temp.preamble = data[0];
temp.sender_id = data[1];
temp.receiver_id = data[2];
temp.seq_num = (uint16_t)data[3] | ((uint16_t)data[4] << 8);
temp.type_flag = data[5];
temp.data_len = data[6];
if (temp.data_len > data_len){
return ESP_FAIL;
}
*message_size = temp.data_len;
memcpy(temp.data, &data[7], temp.data_len);
memcpy(message, &data[7], temp.data_len);
geneate_crc_16(data, 7*sizeof(uint8_t) + temp.data_len, &temp.crc_16);
if (((uint16_t)data[7 + temp.data_len] | ((uint16_t)data[8 + temp.data_len] << 8)) != temp.crc_16){
//CRC mismatch
ESP_LOGE(DEBUG_LINK_TAG, "CRC Mismatch");
return ESP_FAIL;
}
// printf("Frame Information:\n");
// printf("%-10s %-12s %-13s %-15s %-12s %-10s %-6s\n",
// "Preamble", "Sender ID", "Receiver ID", "Sequence Num", "Type+Flag", "Data Len", "CRC");
// printf("0x%02X %-12d %-13d %-15d 0x%02X %-10d 0x%04X\n",
// temp.preamble, temp.sender_id, temp.receiver_id, temp.seq_num, temp.type_flag, temp.data_len, temp.crc_16);
} else {
//not implemented yet
}
return ESP_OK;
}
/**
* @brief This function implements the CRC-16/CCITT algorithm
*
* @param data
* @param data_len
* @param crc
* @return esp_err_t
*/
esp_err_t DataLinkManager::geneate_crc_16(uint8_t* data, size_t data_len, uint16_t* crc){
if (data == nullptr){
return ESP_FAIL;
}
if (data_len == 0){
return ESP_FAIL; //fail if the data len is 0
}
*crc = 0x0;
for (size_t i = 0; i < data_len; i++){
uint8_t tbl_idx = (*crc >> 8) ^ data[i];
*crc = (*crc << 8) ^ crc16_table[tbl_idx];
}
return ESP_OK;
}
esp_err_t DataLinkManager::print_frame_info(uint8_t* data, size_t data_len, uint8_t* message){
printf("Received frame of size %d:\n", data_len);
size_t message_size;
// print_buffer_binary(data, data_len);
return get_data_from_frame(data, data_len, message, &message_size);
}
/**
* @brief Initializes the RIP table
*
*/
void DataLinkManager::init_rip(){
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
rip_table[i] = {
.info = {
.board_id = BROADCAST_ADDR, //invalid addr
.hops = RIP_MAX_HOPS + 1, //infinite
},
.channel = MAX_CHANNELS + 1, //invalid channels
.ttl = 0,
.valid = RIP_INVALID_ROW,
.row_sem = NULL
};
rip_table[i].row_sem = xSemaphoreCreateMutexStatic(&rip_table[i].mutex_buf);
}
//add the self route to the table
rip_table[0].info = {
.board_id = this_board_id,
.hops = 0,
};
rip_table[0].channel = MAX_CHANNELS + 1;
rip_table[0].ttl = RIP_TTL_START;
rip_table[0].valid = 1;
rip_table_valid_rows++;
//temp debug
rip_table[1].info = {
.board_id = 69,
.hops = 69,
};
rip_table[1].channel = MAX_CHANNELS + 1;
rip_table[1].ttl = RIP_TTL_START;
rip_table[1].valid = 1;
rip_table_valid_rows++;
rip_table[2].info = {
.board_id = 3,
.hops = 2,
};
rip_table[2].channel = MAX_CHANNELS + 1,
rip_table[2].ttl = RIP_TTL_START,
rip_table[2].valid = 1;
rip_table_valid_rows++;
start_rip_tasks();
}
esp_err_t DataLinkManager::rip_add_entry(uint8_t board_id, uint8_t hops, uint8_t channel, RIPRow** entry){
if (entry == nullptr){
return ESP_FAIL;
}
if (xSemaphoreTake((*entry)->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
return ESP_FAIL;
}
(*entry)->channel = channel;
(*entry)->info = {
.board_id = board_id,
.hops = hops
};
(*entry)->ttl = RIP_TTL_START;
(*entry)->valid = 1;
ESP_LOGI(DEBUG_LINK_TAG, "board_id %d now has hops %d from channel %d", (*entry)->info.board_id, (*entry)->info.hops, channel);
xSemaphoreGive((*entry)->row_sem);
return ESP_OK;
}
esp_err_t DataLinkManager::rip_reset_entry_ttl(uint8_t board_id){
RIPRow* entry = nullptr;
esp_err_t res;
res = rip_find_entry(board_id, &entry);
if (res != ESP_OK){
return ESP_FAIL;
}
if (entry == nullptr){
return ESP_FAIL; //board doesn't exist
}
if (xSemaphoreTake(entry->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
return ESP_FAIL;
}
entry->ttl = RIP_TTL_START;
xSemaphoreGive(entry->row_sem);
return ESP_OK;
}
esp_err_t DataLinkManager::rip_update_entry(uint8_t new_hop, uint8_t channel, RIPRow** entry){
if (entry == nullptr){
return ESP_FAIL; //board doesn't exist
}
if (xSemaphoreTake((*entry)->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
return ESP_FAIL;
}
if (new_hop < (*entry)->info.hops){
(*entry)->info.hops = new_hop;
(*entry)->channel = channel;
}
(*entry)->ttl = RIP_TTL_START;
(*entry)->valid = 1;
ESP_LOGI(DEBUG_LINK_TAG, "board_id %d now has hops %d from channel %d", (*entry)->info.board_id, (*entry)->info.hops, channel);
xSemaphoreGive((*entry)->row_sem);
return ESP_OK;
}
/**
* @brief Finds the board_id in the table. If board_id does not exist in the table, `entry` will contain an empty row to write into.
* TODO: use an unordered map instead of an array?
*
* @param board_id
* @param entry
* @return esp_err_t
*/
esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry){
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
if (xSemaphoreTake(rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
return ESP_FAIL;
}
if (rip_table[i].valid != RIP_INVALID_ROW && rip_table[i].info.board_id == board_id){
*entry = &rip_table[i];
xSemaphoreGive(rip_table[i].row_sem);
break;
}
if (rip_table[i].valid == RIP_INVALID_ROW){
*entry = &rip_table[i];
}
xSemaphoreGive(rip_table[i].row_sem);
}
return ESP_OK;
}
esp_err_t DataLinkManager::broadcast_rip_frame(){
//use the control frame for the demo (as the number of rows increase, we will need to use the generic frame)
//data will be [board_id (1), hops (1), board_id (2), hops (2), ...]
uint8_t rip_message[rip_table_valid_rows*2] = {};
size_t message_idx = 0;
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
xSemaphoreTake(rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT);
if (rip_table[i].valid == RIP_INVALID_ROW){
xSemaphoreGive(rip_table[i].row_sem);
continue;
}
rip_message[message_idx++] = rip_table[i].info.board_id;
rip_message[message_idx++] = rip_table[i].info.hops;
xSemaphoreGive(rip_table[i].row_sem);
}
esp_err_t res;
for (uint8_t channel = 0; channel < MAX_CHANNELS; channel++){
ESP_LOGI(DEBUG_LINK_TAG, "sending type %x",static_cast<uint8_t>(FrameType::RIP_TABLE_CONTROL));
res = send(BROADCAST_ADDR, rip_message, message_idx, FrameType::RIP_TABLE_CONTROL, channel);
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to send rip frame on channel %d", channel);
}
}
return ESP_OK;
}
[[noreturn]] void DataLinkManager::rip_broadcast_timer_function(void* args){
DataLinkManager* link_layer_obj = static_cast<DataLinkManager*>(args);
if (link_layer_obj == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "RIP Broadacst task failed to start due to invalid pointer");
vTaskDelete(nullptr);
}
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP broadcast task");
esp_err_t res;
while(true){
vTaskDelay(pdMS_TO_TICKS(RIP_BROADCAST_INTERVAL)); //wait RIP_BROADCAST_INTERVAL ms
ESP_LOGI(DEBUG_LINK_TAG, "Broadcasting table..."); //debug
res = link_layer_obj->broadcast_rip_frame();
if (res != ESP_OK){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to broadcast rip frame");
}
}
}
[[noreturn]] void DataLinkManager::rip_ttl_decrement_task(void* args){
DataLinkManager* link_layer_obj = static_cast<DataLinkManager*>(args);
if (link_layer_obj == nullptr){
ESP_LOGE(DEBUG_LINK_TAG, "RIP Broadacst task failed to start due to invalid pointer");
vTaskDelete(nullptr);
}
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP ttl decrement task");
while(true){
vTaskDelay(pdMS_TO_TICKS(RIP_MS_TO_SEC)); //run every second
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
ESP_LOGI(DEBUG_LINK_TAG, "Decrementing ttl on entry %d", i);
if (xSemaphoreTake(link_layer_obj->rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) !=pdTRUE){
ESP_LOGE(DEBUG_LINK_TAG, "Failed to get sem from entry %d", i);
continue;
}
if (link_layer_obj->rip_table[i].valid == RIP_INVALID_ROW){
xSemaphoreGive(link_layer_obj->rip_table[i].row_sem);
continue;
}
if (link_layer_obj->rip_table[i].ttl == 0){
link_layer_obj->rip_table[i].valid = RIP_INVALID_ROW;
} else {
link_layer_obj->rip_table[i].ttl--;
}
ESP_LOGI(DEBUG_LINK_TAG, "Entry %d now has ttl %d", i, link_layer_obj->rip_table[i].ttl);
xSemaphoreGive(link_layer_obj->rip_table[i].row_sem);
}
}
}
/**
* @brief This function will start the tasks required for RIP to function.
* Currently, this function will:
* - start the task to periodically broadcast the board's current copy of the RIP table to all other boards via the 4 RMT channels
* - start a task to periodically decrement the ttl values of each row in the RIP table (WIP) - this will require some sort of mutex on the table itself
*/
void DataLinkManager::start_rip_tasks(){
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP Broadcast task");
xTaskCreate(DataLinkManager::rip_broadcast_timer_function, "RIPBroadcastTask", 4096, static_cast<void*>(this), 5, NULL);
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP TTL task");
xTaskCreate(DataLinkManager::rip_ttl_decrement_task, "RIPTTLTask", 4096, static_cast<void*>(this), 5, NULL);
}

View File

@@ -0,0 +1,21 @@
# Data Link Layer
This component represents the data link layer. It will handle board to board communication (abstracting away the RMT specific details of transmitting physical raw bits over wires).
## Frame Definitions
See `./include/Frames.h` for frame definitions.
There will be two types of frames: Control and Generic.
Control frames will contain all control information (eg. Spinning a DC motor, moving a servo to a particular angle, sensor information). These frames will have a limit of 32B of data size with a total packet size of 41B. These frames will not be fragmented.
Generic frames will contain all other information. They will have a max data size of 8KiB with a total packet size of 8206B (8.013672 KiB). These frames can be fragmented.
To differentiate between the two frames, the `type` field in both frames will be compared against. If the MSB is set to 1, it will be determined to be a control packet.
## Routing Information Protocol (RIP) Table
See `./include/Tables.h` for table definitions.
WIP

View File

@@ -0,0 +1,91 @@
#ifndef DATA_LINK
#define DATA_LINK
#include "Frames.h"
#include "Tables.h"
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <queue> //c++ priority queue
#include <memory>
#include "RMTManager.h"
#include <unordered_map>
#define DEBUG_LINK_TAG "LinkLayer"
#define CRC_POLYNOMIAL 0x1021
static const uint16_t crc16_table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
}; //look up table for crc
class DataLinkManager{
public:
DataLinkManager(uint8_t board_id);
~DataLinkManager();
esp_err_t send(uint8_t dest_board, uint8_t* data, uint16_t data_len, FrameType type, uint8_t curr_channel);
esp_err_t start_receive_frames(uint8_t curr_channel);
esp_err_t receive(uint8_t* data, size_t data_len, size_t* recv_len, uint8_t curr_channel);
esp_err_t print_frame_info(uint8_t* data, size_t data_len, uint8_t* message);
esp_err_t send_discover_frame();
private:
uint8_t this_board_id = 0;
std::priority_queue<Frame, std::vector<Frame>, FrameCompare> frame_queue; //create a priority queue
std::unique_ptr<RMTManager> phys_comms;
std::unordered_map<uint8_t, uint16_t> sequence_num_map;
esp_err_t set_board_id(uint8_t board_id);
esp_err_t get_board_id(uint8_t& board_id);
void print_binary(uint8_t byte);
void print_buffer_binary(const uint8_t* buffer, size_t length);
esp_err_t get_data_from_frame(uint8_t* data, size_t data_len, uint8_t* message, size_t* message_size);
esp_err_t geneate_crc_16(uint8_t* data, size_t data_len, uint16_t* crc);
//==== RIP related functions ====
/**
* TODO for RIP:
* Periodic Routing Updates via timer (broadcast routing table every 30 seconds)
* Handle RIP table updates when a RIP table arrives (ensure there are no loops between boards of sending tables back and forth)
* TTL handling and route expiration
* TTLs in all rows should decrement at once via timer
*/
void init_rip();
esp_err_t rip_find_entry(uint8_t board_id, RIPRow** entry);
esp_err_t rip_update_entry(uint8_t new_hop, uint8_t channel, RIPRow** entry);
esp_err_t rip_add_entry(uint8_t board_id, uint8_t hops, uint8_t channel, RIPRow** entry);
esp_err_t rip_reset_entry_ttl(uint8_t board_id);
//this is stored locally with metadata `ttl`
// std::unordered_map<uint8_t, RIPRow> rip_table; //using a hash map to store the routes to other boards - will be used as we scale up
RIPRow rip_table[RIP_MAX_ROUTES]; //temp using a static array
uint8_t rip_table_valid_rows = 0;
void start_rip_tasks();
esp_err_t broadcast_rip_frame();
[[noreturn]] static void rip_broadcast_timer_function(void* args);
[[noreturn]] static void rip_ttl_decrement_task(void* args);
esp_err_t route_frame(uint8_t dest_id, uint8_t* channel_to_send);
};
#endif //DATA_LINK

View File

@@ -0,0 +1,98 @@
#ifdef DATA_LINK
#include "freertos/FreeRTOS.h"
#include <variant>
#include <cstdint>
#define BROADCAST_ADDR 0xFF //used for discovery (finding the board's neighbours). this will mean the board ids will have 2^8-2 = 254 unique IDs that could be assigned
#define PC_ADDR 0x0 //setting 0 to be the PC
#define START_OF_FRAME 0xAB //0b1010_1011 - denotes the start of frame
#define MAX_GENERIC_DATA_LEN (180) //Max 180B
#define MAX_CONTROL_DATA_LEN (1 << 5) // Max 32B
//Flags
#define FLAG_FRAG 0x8 //0b1000 //this fragmented frame is part of a larger frame
#define FLAG_DISCOVERY 0x4 //0b0100
#define FLAG_NEIGH_TABLE 0x2 //0b0010 - used to denote the frame contains the neighbour tables (used for finding the configuration/topology of the network); similar to an ARP or MAC table
#define FLAG_ACK 0x1 //0b0001_0000 - used for confirming receipt of different types of frames from the neighbours
#define GET_TYPE(x) ((x) & 0xF0)
#define GET_FLAG(x) ((x) & 0x0F)
#define MAKE_TYPE_FLAG(type, flag) ((type) | (flag))
#define IS_CONTROL_FRAME(x) (((x) & 0x80) != 0)
#define CONTROL_FRAME_OVERHEAD 9
#define CONTROL_FRAME_TYPE 0x80 //if the frame type MSB is set to 1, use the control frame
//Types (total 2^4 = 16 different types)
enum class FrameType : uint8_t {
MOTOR_TYPE = 0x80, //0b1000_0000
SERVO_TYPE = 0xC0, //0b1100_0000
DISTANCE_SENSOR_TYPE = 0xE0, //0b1110_0000
DEBUG_CONTROL_TYPE = 0xC0, //0b1100_0000
DEBUG_GENERIC_TYPE = 0x00, //0b0000_0000
SYSTEM_TYPE = 0x30, //0x0011_0000 - used for statuses, discovery, and other maintainence requests
RIP_TABLE_CONTROL = 0x90, //0b1001_0000 - using the control frame to broadcast the RIP table
RIP_TABLE_GENERIC = 0x10 //0b0001_000 - using the generic frame to broadcast the RIP table
};
#pragma pack(push, 1) //these structs will be transmitted as is (ensure the structs are structured using 1B alignment - no padding)
typedef struct _control_frame{
uint8_t preamble; //Start of Frame
uint8_t sender_id; //sender board id
uint8_t receiver_id; //receiver board id
uint16_t seq_num; //sequence number to differentiate frames being sent from sender to receiver
uint8_t type_flag; //(type << 4) | flag - both are 4 bits
uint8_t data_len; //Data Length (max 32B)
uint8_t data[MAX_CONTROL_DATA_LEN]; //Variable Length of Data
uint16_t crc_16; //CRC-16
} control_frame; //this will have a max size of 9 + 32B = 41B
typedef struct _data_link_frame{
uint8_t preamble; //Start of Frame
uint8_t sender_id; //sender board id
uint8_t receiver_id; //receiver board id
uint16_t seq_num; //sequence number to differentiate frames being sent from sender to receiver
uint8_t type_flag; //(type << 4) | flag - both are 4 bits
uint16_t frag_info; //(total_frag_num << 8) | frag_num - total_frag_num denotes the total number of fragmented frames to expect for this sequence number(?) and frag_num denotes the fragment frame num
uint16_t data_len; //Data Length (max 178B)
uint8_t data[MAX_GENERIC_DATA_LEN]; //Variable Length of Data
uint16_t crc_16; //CRC-16
} data_link_frame; //this will have a max size of 12 + 180 B = 192B
#pragma pack(pop)
using Frame = std::variant<control_frame, data_link_frame>;
//defining a comparison operation for comparing two frames -- not tested
struct FrameCompare {
bool operator()(const Frame& a, const Frame& b) const {
auto msb_set = [](uint8_t type_flag) {
return (type_flag & 0x80) != 0; // 0x80 == 1000 0000
};
auto get_type_flag = [](const Frame& pkt) -> uint8_t {
return std::visit([](auto&& p) -> uint8_t {
return p.type_flag;
}, pkt);
};
uint8_t type_flag_a = get_type_flag(a);
uint8_t type_flag_b = get_type_flag(b);
bool a_msb = msb_set(type_flag_a);
bool b_msb = msb_set(type_flag_b);
if (a_msb != b_msb) {
return !a_msb; // Frame with MSB set (true) should come first
}
// Tie-breaker: use seq_num if MSB is the same
return std::visit([](auto&& p1, auto&& p2) {
return p1.seq_num > p2.seq_num; // smaller seq_num = higher priority (older)
}, a, b);
}
};
#endif //DATA_LINK

View File

@@ -0,0 +1,32 @@
#pragma once
#ifdef DATA_LINK
#include "freertos/FreeRTOS.h"
#define RIP_MAX_HOPS 15 //16 or more is infinite
#define RIP_MAX_ROUTES 10 //for the demo we will use up to 10 boards in total (9 other boards will be connected = 9 rows)
#define RIP_INVALID_ROW 0
// #define RIP_BROADCAST_INTERVAL 30000 //broadcast every 30 seconds (30000ms)
#define RIP_BROADCAST_INTERVAL 3000 //temp broadcast every 3 seconds (3000ms)
#define RIP_TTL_START 180 //seconds
#define RIP_MS_TO_SEC 1000 //1000 ms to 1 sec
#define RIP_MAX_SEM_WAIT 30
/**
* @brief Routing data to a board
* This struct will be sent to other boards
*/
typedef struct _rip_hops{
uint8_t board_id; //ID of the destination
uint8_t hops; //hop count to `board_id`
} RIPHop;
typedef struct _rip_row{
RIPHop info;
uint8_t channel; //rmt channel
uint8_t ttl; //how long this entry is valid for. starting value is 180 seconds
uint8_t valid; //is this a valid entry?
StaticSemaphore_t mutex_buf; //where mutex state is stored
SemaphoreHandle_t row_sem; //mutex sem handle of mutex_buf
} RIPRow;
#endif //DATA_LINK

View File

@@ -0,0 +1,4 @@
idf_component_register(SRCS "RMTManager.cpp"
PRIV_REQUIRES driver esp_event nvs_flash esp_netif
REQUIRES esp_driver_rmt
INCLUDE_DIRS "include")

41
components/rmt/README.md Normal file
View File

@@ -0,0 +1,41 @@
# ESP32-S3 RMT
WIP
The related ESP32-S3 latest documentation on RMT can be found [here](https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/rmt.html).
## Encoding
Using the Ethernet Manchester encoding method where a bit 1 is encoded as a falling edge transition (high -> low) and a bit 0 is encoded as a rising edge transition (low -> high).
Specific timings are defined in `RMTSymbols.h` but it has been tested with 10us intervals (each symbol has length 20us) with 1MHz resolution.
Physical Layer will be using Manchester encoding (see the doc/detailed_design_timeline document for more information).
### Testing with other Encoding Methods
We may test and compare with other encoding methods to see which has better performance. Currently, we are using manchester at 1MHz resolution - probably good enough for 10Mbps? (needs to be tested)
The following are some potential other encoding methods (inspired from http://units.folder101.com/cisco/sem1/Notes/ch7-technologies/encoding.htm)
- Manchester with shorter symbol durations (and with higher resolutions?) - Used by 100Mbps Ethernet
- NRZ Inverted - Used by 100BASE-FX networks
- 8b/10b with NRZ - Used by 1000BASE-X
## Transceiver Operations
We are currently only using one channel for TX and one channel for RX. This will be changed in the future to use multiple channels at the same time (transmitting separate data however/transmitting independent of each other).
Currently, TX is being transmitted from `RMT_TX_GPIO` and RX being received from `RMT_RX_GPIO`.
## Completed Encoding Methods
- Manchester
- NRZ-I
## Testing
Use `-D TIME_TEST=1` to measure the average transmission rate (over 1000 iterations) on a chosen encoding scheme.
To change encoding schemes, use one of the following compiler flags:
- `MANCHESTER_40=1` for 40MHz resolution Manchester
- `NRZ-INVERTED=1` for Non-Return-to-Zero Inverted
- with no specified encoding schemes, the program will use Manchester at 1MHz resolution.
### Notes
You may need to perform a `idf.py clean` or `idf.py fullclean` to undefine the unwanted compiler flags previously set (eg. when changing encoding schemes or not running the time test)

View File

@@ -0,0 +1,733 @@
#include "RMTManager.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
RMTManager::RMTManager(){
esp_err_t res = init();
if (res != ESP_OK){
//failed
ESP_LOGE(DEBUG_TAG, "Failed to initialize the RMTManager");
return;
}
ESP_LOGD(DEBUG_TAG, "RMTManager has been initialized");
}
esp_err_t RMTManager::init_tx_channel(){
esp_err_t res_tx = ESP_FAIL;
//setup encoder config
for (uint8_t i = 0; i < MAX_CHANNELS; i++){
reset_encoder_context(&channels[i].encoder_context); //ensure the encoder context is initialized
rmt_simple_encoder_config_t encoder_config = {
.callback = encoder_callback,
.arg = &channels[i].encoder_context
};
//create encoder
res_tx = rmt_new_simple_encoder(&encoder_config, &channels[i].encoder);
if (res_tx != ESP_OK){
// printf("Failed to create encoder\n");
ESP_LOGE(DEBUG_TAG, "Failed to create encoder");
channels[i].encoder = NULL;
return ESP_FAIL;
}
//enable the callback
rmt_tx_event_callbacks_t tx_cbs = {
.on_trans_done = RMTManager::rmt_tx_done_callback
};
rmt_tx_channel_config_t tx_channel_config_template = {
.gpio_num = tx_gpio[i],
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = RMT_RESOLUTION_HZ,
.mem_block_symbols = RMT_SYMBOL_BLOCK_SIZE, //giving each channel ~192B of memory
.trans_queue_depth = 4,
.flags = {
.invert_out = 0,
.with_dma = 0,
}
};
channels[i].tx_gpio = tx_gpio[i];
channels[i].status = CHANNEL_NOT_READY_STATUS;
if (channels[i].tx_rmt_handle != NULL) {
rmt_disable(channels[i].tx_rmt_handle);
rmt_del_channel(channels[i].tx_rmt_handle);
channels[i].tx_rmt_handle = NULL;
}
if (channels[i].tx_done_semaphore != NULL) {
vSemaphoreDelete(channels[i].tx_done_semaphore);
channels[i].tx_done_semaphore = NULL;
}
channels[i].tx_queue = xQueueCreate(QUEUE_SIZE, sizeof(TxBuffer)); //can store up to 10 queued transmissions (each transmission size being 192B; based ont he RMT_SYMBOL_BLOCK_SIZE)
res_tx = rmt_new_tx_channel(&tx_channel_config_template, &channels[i].tx_rmt_handle);
//init tx channel
if (res_tx != ESP_OK) {
// printf("Failed to init TX channel\n");
ESP_LOGE(DEBUG_TAG, "Failed to init TX channel %d", i);
continue;
}
if (channels[i].tx_rmt_handle == NULL) {
// printf("TX channel handle is NULL\n");
ESP_LOGE(DEBUG_TAG, "TX channel handle is NULL on channel %d", i);
continue;
}
channels[i].tx_done_semaphore = xSemaphoreCreateBinary(); //create a binary sem
TxCallbackContext* tx_callback_ctx = new TxCallbackContext {
.tx_done_sem = channels[i].tx_done_semaphore,
.transmit_queue = channels[i].tx_queue,
.tx_context = &channels[i].encoder_context
};
if (channels[i].tx_done_semaphore == NULL){
ESP_LOGE(DEBUG_TAG, "Failed to create TX done semaphore on channel %d", i);
continue;
}
// res_tx = rmt_tx_register_event_callbacks(channels[i].tx_rmt_handle, &tx_cbs, channels[i].tx_done_semaphore);
res_tx = rmt_tx_register_event_callbacks(channels[i].tx_rmt_handle, &tx_cbs, static_cast<void*>(tx_callback_ctx));
if (res_tx != ESP_OK) {
// printf("Failed to register TX callback\n");
ESP_LOGE(DEBUG_TAG, "Failed to register TX callback on channel %d", i);
continue;
}
//enable tx channels
res_tx = rmt_enable(channels[i].tx_rmt_handle);
if (res_tx != ESP_OK) {
// printf("Failed to enable TX channel\n");
ESP_LOGE(DEBUG_TAG, "Failed to enable TX channel %d", i);
continue;
}
printf("Successfully enabled TX channel %d\n", i);
}
return ESP_OK;
}
bool RMTManager::rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data){
BaseType_t high_task_wakeup = pdFALSE;
// SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data;
TxCallbackContext* args = static_cast<TxCallbackContext*>(user_data);
SemaphoreHandle_t sem = args->tx_done_sem;
QueueHandle_t queue = args->transmit_queue;
rmt_encoder_context_t* encoder_context = args->tx_context;
TxBuffer buf = {};
BaseType_t xTaskWokenByReceive = pdFALSE;
xQueueReceiveFromISR(queue, static_cast<TxBuffer*>(&buf), &xTaskWokenByReceive); //remove from the queue
if (buf.data != nullptr){
vPortFree((void*)buf.data);
}
if (encoder_context != nullptr){
encoder_context->bit_index = 0;
encoder_context->byte_index = 0;
encoder_context->num_symbols = 0;
}
xSemaphoreGiveFromISR(sem, &high_task_wakeup);
return high_task_wakeup == pdTRUE;
}
esp_err_t RMTManager::wait_until_send_complete(uint8_t channel_num){
if (channel_num >= MAX_CHANNELS){
ESP_LOGE(DEBUG_TAG, "Invalid channel number");
return ESP_FAIL;
}
if(this->channels[channel_num].tx_done_semaphore == NULL){
return ESP_FAIL;
}
if (xSemaphoreTake(this->channels[channel_num].tx_done_semaphore, pdMS_TO_TICKS(10000)) == pdTRUE){
return ESP_OK;
}
ESP_LOGE(DEBUG_TAG, "Timeout of 10000 ms when waiting for RMT TX to complete");
return ESP_FAIL;
}
bool RMTManager::rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data){
BaseType_t high_task_wakeup = pdFALSE;
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
// send the received RMT symbols to the parser task
BaseType_t res = xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
if (res != pdTRUE){
// printf("RX Callback: Failed to enqueue received data\n");
ESP_LOGE(DEBUG_TAG, "RX Callback: Failed to enqueue received data");
}
// return whether any task is woken up
return high_task_wakeup == pdTRUE;
}
esp_err_t RMTManager::init_rx_channel(){
for (uint8_t i = 0; i < MAX_CHANNELS; i++){
rmt_rx_channel_config_t rx_channel_config = {
.gpio_num = rx_gpio[i],
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = RMT_RESOLUTION_HZ,
.mem_block_symbols = RMT_SYMBOL_BLOCK_SIZE,
.flags = {
.invert_in = false,
.with_dma = 0
}
}; //temp for one rx channel
//temp
channels[i].rx_gpio = rx_gpio[i];
esp_err_t res_rx = rmt_new_rx_channel(&rx_channel_config, &channels[i].rx_rmt_handle);
if (res_rx != ESP_OK) {
// printf("Failed to init RX channel - reason %s\n", esp_err_to_name(res_rx));
ESP_LOGE(DEBUG_TAG, "Failed to init RX channel - reason %s", esp_err_to_name(res_rx));
return ESP_FAIL;
}
if (channels[i].rx_rmt_handle == NULL) {
// printf("RX channel handle is NULL\n");
ESP_LOGE(DEBUG_TAG, "RX channel handle is NULL");
return ESP_FAIL;
}
channels[i].rx_queue = xQueueCreate(QUEUE_SIZE, sizeof(rmt_rx_done_event_data_t)); //creating queue with some random size
rmt_rx_event_callbacks_t cbs = {
.on_recv_done = RMTManager::rmt_rx_done_callback
};
rmt_rx_register_event_callbacks(channels[i].rx_rmt_handle, &cbs, channels[i].rx_queue);
res_rx = rmt_enable(channels[i].rx_rmt_handle);
if (res_rx != ESP_OK) {
// printf("Failed to enable RX channel\n");
ESP_LOGE(DEBUG_TAG, "Failed to enable RX channel");
return ESP_FAIL;
}
}
return ESP_OK;
}
esp_err_t RMTManager::init(){
esp_err_t res = this->init_tx_channel();
if (res != ESP_OK) {
// printf("Failed to init TX channel\n");
ESP_LOGE(DEBUG_TAG, "Failed to init TX channel");
return ESP_FAIL;
}
res = this->init_rx_channel();
if (res != ESP_OK) {
// printf("Failed to init RX channel\n");
ESP_LOGE(DEBUG_TAG, "Failed to init RX channel");
return ESP_FAIL;
}
for (uint8_t i = 0; i < MAX_CHANNELS; i++){
if (channels[i].tx_rmt_handle != NULL && channels[i].rx_rmt_handle != NULL && channels[i].tx_done_semaphore != NULL && channels[i].rx_queue != NULL){
channels[i].status = CHANNEL_READY_STATUS;
}
}
// printf("Free heap before encoder creation: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
// heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
// printf("Free DMA-capable heap before encoder creation: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_DMA));
// heap_caps_print_heap_info(MALLOC_CAP_DMA);
return ESP_OK;
}
/**
* @brief This is a callback function called by RMT when transmitting. This function will encode the user data `data` with rising and falling edges based on the bit.a64l
* The symbols are defined in `RMTManager.h`, where a bit 1 is transmitted as a `RMT_SYMBOL_ONE` and a bit 0 is transmitted as a `RMT_SYMBOL_ZERO`
*
* @param data
* @param data_size
* @param symbols_written
* @param symbols_free
* @param symbols
* @param done
* @param arg
*/
size_t RMTManager::encoder_callback(const void* data, size_t data_size, size_t symbols_written,
size_t symbols_free, rmt_symbol_word_t* symbols, bool* done, void* arg){
rmt_encoder_context_t* ctx = (rmt_encoder_context_t*) arg; //get the current context
if (symbols_free == 0){ //no space in the tx buffer; don't encode any more bytes until there is space left
*done = (ctx->byte_index >= data_size);
return 0;
}
const uint8_t* bytes = (const uint8_t*)data; //get the user data as an array of bytes
size_t symbols_used = 0; //number of symbols used
while (ctx->byte_index < data_size && symbols_used < symbols_free){ //loop until we have reached the end of the data or filled the RMT symbol buffer (`symbols_free`)
uint8_t byte = bytes[ctx->byte_index]; //get the byte from the data
uint8_t bit = (byte >> (7 - ctx->bit_index)) & 0x01; //get the current bit, as determined from the bit index (MSB first)
#ifndef NRZ_INVERTED
//Manchester (Ethernet Standard) Encoding
symbols[symbols_used++] = bit ? RMT_SYMBOL_ONE : RMT_SYMBOL_ZERO; //if the bit is a 1, transmit a 1 symbol; otherwise, transmit 0 symbol
ctx->num_symbols++;
#else
//NRZ-I encoding. Must change the voltage level whenever a bit 1 is detected
if (ctx->byte_index == 0 && ctx->bit_index == 0){
//MSB of the first byte - send a rising edge 1 to allow any succeeding 0s to be detected by the receiver
symbols[symbols_used++] = RMT_SYMBOL_ONE_RISING;
ctx->current_level = !ctx->current_level; //current level is high
ctx->num_symbols++;
}
if (ctx->zero_count == CONSEC_ZERO_THRESHOLD){
ctx->current_level = !ctx->current_level;
symbols[symbols_used++] = ctx->current_level ? RMT_SYMBOL_ONE_RISING : RMT_SYMBOL_ONE_FALLING;
ctx->num_symbols++;
ctx->zero_count = 0;
// Don't advance to next bit reprocess the current bit
continue;
}
if (bit == 1){
ctx->current_level = !ctx->current_level; //invert current level
symbols[symbols_used++] = ctx->current_level ? RMT_SYMBOL_ONE_RISING : RMT_SYMBOL_ONE_FALLING; //if current level is 0 (low), it must be a falling edge. otherwise, it is a rising edge
ctx->num_symbols++;
ctx->zero_count = 0;
} else {
//bit 0s, maintain current level
if (ctx->current_level){
//check if the previous symbol was RMT_SYMBOL_ZERO_HIGH. if it is, simply add another RMT_DURATION_MAX on duration1 (this is a slight optimization to send less symbols)
if (symbols[symbols_used-1].level0 == 1 && symbols[symbols_used-1].level1 == 1){
symbols[symbols_used-1].duration1 += RMT_DURATION_MAX;
} else {
//previous symbol was not RMT_SYMBOL_ZERO_HIGH
symbols[symbols_used++] = RMT_SYMBOL_ZERO_HIGH;
ctx->num_symbols++;
}
} else {
if (symbols[symbols_used-1].level0 == 0 && symbols[symbols_used-1].level1 == 0){
symbols[symbols_used-1].duration1 += RMT_DURATION_MAX;
} else {
symbols[symbols_used++] = ctx->current_level ? RMT_SYMBOL_ZERO_HIGH : RMT_SYMBOL_ZERO_LOW;
ctx->num_symbols++;
}
}
ctx->zero_count++;
}
#endif //NRZ_INVERTED
ctx->bit_index++;
if (ctx->bit_index >= 8) {
//reached the end of the byte; go to the next byte
ctx->bit_index = 0;
ctx->byte_index++;
}
}
*done = (ctx->byte_index >= data_size); //if the transmit is done, set the `done` flag to true (all bytes have been encoded)
return symbols_used;
}
void RMTManager::reset_encoder_context(rmt_encoder_context_t* ctx){
ctx->bit_index = 0;
ctx->byte_index = 0;
ctx->num_symbols = 0;
#ifdef NRZ_INVERTED
ctx->current_level = false;
#endif //NRZ_INVERTED
}
/**
* @brief Sends the string `data` of size `size`, with config `config`
*
* @param data
* @param size
* @param config
* @return int
*/
int RMTManager::send(uint8_t* data, size_t size, rmt_transmit_config_t* config, uint8_t channel_num){
if (channel_num >= MAX_CHANNELS){
ESP_LOGE(DEBUG_TAG, "send() error: invalid channel number");
}
if (channels[channel_num].status == CHANNEL_NOT_READY_STATUS){
ESP_LOGE(DEBUG_TAG, "send() error: Channel %d is not ready", channel_num);
}
if (this->channels[channel_num].tx_rmt_handle == nullptr) {
// printf("send() error: tx_chan is NULL\n");
ESP_LOGE(DEBUG_TAG, "send() error: tx_chan is NULL");
return ESP_FAIL;
}
if (this->channels[channel_num].encoder == nullptr) {
// printf("send() error: encoder is NULL\n");
ESP_LOGE(DEBUG_TAG, "send() error: encoder is NULL");
return ESP_FAIL;
}
if (data == nullptr || size == 0 || size > (RMT_SYMBOL_BLOCK_SIZE*4)) {
// printf("send() error: data pointer NULL or size 0\n");
ESP_LOGE(DEBUG_TAG, "send() error: data pointer NULL or size 0");
return ESP_FAIL;
}
if (config == nullptr) {
// printf("send() error: config pointer is NULL\n");
ESP_LOGE(DEBUG_TAG, "send() error: config pointer is NULL");
return ESP_FAIL;
}
TxBuffer new_data_to_send_buf = {
.data = (uint8_t*)pvPortMalloc(size), //this may not be thread safe but each channel should be on its own thread so maybe it's ok???
.length = size
};
if (new_data_to_send_buf.data == nullptr){
ESP_LOGE(DEBUG_TAG, "failed to malloc");
return ESP_FAIL;
}
memcpy((void*)(new_data_to_send_buf.data), data, size);
if (xQueueSendToBack(channels[channel_num].tx_queue, (void*)&new_data_to_send_buf, (TickType_t) 10) != pdPASS){ //note this may not work very well since im not checking the return value; this function can fail if the queue is full
vPortFree((void*)new_data_to_send_buf.data);
ESP_LOGE(DEBUG_TAG, "Failed to queue data");
return ESP_FAIL;
}
esp_err_t res = rmt_transmit(this->channels[channel_num].tx_rmt_handle, this->channels[channel_num].encoder, new_data_to_send_buf.data, new_data_to_send_buf.length, config);
if (res != ESP_OK){
// printf("Failed to send %s\n", data);
vPortFree((void*)new_data_to_send_buf.data);
ESP_LOGE(DEBUG_TAG, "Failed to send %s", data);
return ESP_FAIL;
}
return ESP_OK;
}
/**
* @brief This function, given the `symbols` and the length `num`, will convert the received symbols into the symbols defined in `RMTSymbols.h`
* this somehow works first try????? (tested with 't', 'O', and 'THIS IS A SAMPLE TEXT MESSAGE')
* @param symbols received symbols
* @param num number of received symbols
* @param decoded decoded symbol string
* @param output_num size of `decoded`
* @return int - returns the number of symbols written to the buffer
*/
int RMTManager::decode_symbols(rmt_symbol_word_t* symbols, size_t num, rmt_symbol_word_t* decoded, size_t output_num){
if (symbols == NULL || decoded == NULL || num == 0 || output_num == 0){
return ESP_FAIL;
}
size_t output_index = 0;
size_t i = 0;
bool curr_high_low = true; //flag to maintain where we are (either high or low)
#ifdef NRZ_INVERTED
uint32_t num_0_symbols_duration = 0, num_0_symbols = 0;
uint8_t consecutive_zeros = 0;
#endif //NRZ_INVERTED
while (output_index < output_num && i < num){
// printf("duration0 %d level0 %d duration1 %d level1 %d\n", symbols[i].duration0, symbols[i].level0, symbols[i].duration1, symbols[i].level1); //dummy print receive
#ifndef NRZ_INVERTED
//manchester encoding
/*there are two cases in the beginning:
1. if duration0 = 20, then we are in between two symbols (low to high and high to low).
in this case, we need to insert a low in the beginning and "split" the current symbol into 2
2. if duration0 = 10, then the first symbol should be high to low
*/
if (symbols[i].duration0 != RMT_DURATION_SYMBOL){
if (i != 0){
if (curr_high_low){
decoded[output_index++] = RMT_SYMBOL_ONE;
} else {
decoded[output_index++] = RMT_SYMBOL_ZERO;
}
curr_high_low = !curr_high_low;
} else {
//need to insert a 0 before received symbols
decoded[output_index++] = RMT_SYMBOL_ZERO;
}
}
if (curr_high_low){
decoded[output_index++] = RMT_SYMBOL_ONE;
} else {
decoded[output_index++] = RMT_SYMBOL_ZERO;
}
//if duration1 = 20, then we are starting low
if (symbols[i].duration1 != RMT_DURATION_SYMBOL){
curr_high_low = !curr_high_low;
}
#else
//nrz-i encoding - bit stuffing doesn't work
//there is always a rising edge (period of RMT_DURATION_SYMBOL on high as the first half isn't captured)
// if (i == 0){
// curr_high_low = true;
// if (symbols[i].duration0 == RMT_DURATION_MAX){
// //next symbol is a 1 - can continue (first RMT_DURATION is from the first symbol (init rising edge). second RMT_DURATION is second symbol)
// i++;
// continue;
// }
// }
//need to "split"
if (symbols[i].duration0 % (RMT_DURATION_SYMBOL * 2) != 0){
num_0_symbols_duration = symbols[i].duration0 - RMT_DURATION_SYMBOL; //last waveform has duration0 with some duration that's only a multiple of RMT_DURATION_SYMBOL
}else {
num_0_symbols_duration = symbols[i].duration0 - RMT_DURATION_SYMBOL * 2; //one from the rising edge, one from the falling edge
}
num_0_symbols = num_0_symbols_duration / RMT_DURATION_MAX; //should be the number of 0 symbols
for (int j = 0; j < num_0_symbols && output_index < output_num; j++){
decoded[output_index++] = curr_high_low ? RMT_SYMBOL_ZERO_HIGH : RMT_SYMBOL_ZERO_LOW;
consecutive_zeros++;
}
curr_high_low = !curr_high_low;
if (output_index >= output_num){
break;
}
if (!curr_high_low){
decoded[output_index++] = RMT_SYMBOL_ONE_FALLING;
} else {
decoded[output_index++] = RMT_SYMBOL_ONE_RISING;
}
// if (consecutive_zeros == MAX_ZER){
// consecutive_zeros = 0;
// } else {
// if (!curr_high_low) {
// decoded[output_index++] = RMT_SYMBOL_ONE_FALLING;
// } else {
// decoded[output_index++] = RMT_SYMBOL_ONE_RISING;
// }
// consecutive_zeros = 0; // reset zero count after a real 1 bit
// }
if (symbols[i].duration1 == 0){
break; //last waveform has duration1 = 0
}
num_0_symbols_duration = symbols[i].duration1 - RMT_DURATION_SYMBOL * 2; //one from the falling edge, one from the rising edge
num_0_symbols = num_0_symbols_duration / RMT_DURATION_MAX; //should be the number of 0 symbols
for (int j = 0; j < num_0_symbols && output_index < output_num; j++){
decoded[output_index++] = curr_high_low ? RMT_SYMBOL_ZERO_HIGH : RMT_SYMBOL_ZERO_LOW;
}
curr_high_low = !curr_high_low;
if (output_index >= output_num){
break;
}
if (!curr_high_low){
decoded[output_index++] = RMT_SYMBOL_ONE_FALLING;
} else {
decoded[output_index++] = RMT_SYMBOL_ONE_RISING;
}
// if (consecutive_zeros == 5){
// consecutive_zeros = 0;
// } else {
// if (!curr_high_low) {
// decoded[output_index++] = RMT_SYMBOL_ONE_FALLING;
// } else {
// decoded[output_index++] = RMT_SYMBOL_ONE_RISING;
// }
// consecutive_zeros = 0; // reset zero count after a real 1 bit
// }
#endif //NRZ_INVERTED
i++;
}
return (int)output_index;
}
/**
* @brief This converts the parsed symbols into a string of size `output_index`
*
* @param symbols Parsed received symbols (see `RMTSymbols.h` for the definitions of the symbols)
* @param num Length of `symbols`
* @param string Output string encoded by the symbols
* @param output_num `length of the char array`
* @return int - length of the output string (-1 if failure)
*/
int RMTManager::convert_symbols_to_char(rmt_symbol_word_t* symbols, size_t num, uint8_t* string, size_t output_num){
if (symbols == NULL || string == NULL || num == 0 || output_num == 0){
return ESP_FAIL;
}
size_t bit_count = 0;
char byte = 0;
size_t output_index = 0;
int i = 0;
while (i < num && output_index < output_num){
#ifndef NRZ_INVERTED
if (symbols[i].level0 == 0 && symbols[i].level1 == 1){
//zero
byte = byte << 1;
}else if (symbols[i].level0 == 1 && symbols[i].level1 == 0) {
byte = (byte << 1) + 1;
} else {
return ESP_FAIL;
}
#else
//nrz-i
if (symbols[i].level0 != symbols[i].level1){
//bit 1
byte = (byte << 1) + 1;
} else if (symbols[i].level0 == symbols[i].level1){
//bit 0
byte = byte << 1;
} else {
return ESP_FAIL;
}
#endif //NRZ_INVERTED
bit_count++;
if (bit_count == 8){
//a byte has been parsed
// printf("inserting %b\n", byte);
string[output_index++] = byte;
byte = 0;
bit_count = 0;
}
i++;
}
printf("output_index %d\n", output_index);
return (int)output_index;
}
/**
* @brief Start async RX job
*
* @return esp_err_t
*/
esp_err_t RMTManager::start_receiving(uint8_t channel_num){
if (channel_num >= MAX_CHANNELS){
return ESP_FAIL;
}
if (channels[channel_num].status == CHANNEL_LISTENING){
return ESP_OK; //failed to receive earlier; no need to start the async rx job again (alreayd running)
}
if (channels[channel_num].status == CHANNEL_NOT_READY_STATUS){
ESP_LOGE(DEBUG_TAG, "RX Channel is not ready");
return ESP_FAIL;
}
if (channels[channel_num].rx_rmt_handle == NULL){
ESP_LOGE(DEBUG_TAG, "RX Channel not ready");
return ESP_FAIL;
}
esp_err_t res = rmt_receive(channels[channel_num].rx_rmt_handle, channels[channel_num].raw_symbols, sizeof(channels[channel_num].raw_symbols), &this->receive_config);
if (res != ESP_OK){
// printf("Failed to start receive\n");
ESP_LOGE(DEBUG_TAG, "Failed to start receive");
}
channels[channel_num].status = CHANNEL_LISTENING;
return res;
}
/**
* @brief Function to get the received messages
*
* @return int
*/
int RMTManager::receive(uint8_t* recv_buf, size_t size, size_t* output_size, uint8_t channel_num){
if (channel_num >= MAX_CHANNELS){
return ESP_FAIL;
}
if (channels[channel_num].status != CHANNEL_LISTENING){
ESP_LOGE(DEBUG_TAG, "receive(): Receive channel %d is not ready to receive due to init fail or async job was not started", channel_num);
return ESP_FAIL;
}
rmt_rx_done_event_data_t rx_data;
if (xQueueReceive(channels[channel_num].rx_queue, &rx_data, pdMS_TO_TICKS(15000)) != pdTRUE){ //this will wait until a message has arrived or not
// printf("Timeout occurred while waiting for RX event\n");
ESP_LOGE(DEBUG_TAG, "Timeout occurred while waiting for RX event - didn't receive a message in time");
return ESP_FAIL;
}
channels[channel_num].status = CHANNEL_READY_STATUS;
// printf("Got %d symbols\n", rx_data.num_symbols);
// printf("raw symbols:\n");
// for (int i = 0; i < rx_data.num_symbols; i++){
// printf("duration0 %d level0 %d duration1 %d level1 %d\n", rx_data.received_symbols[i].duration0, rx_data.received_symbols[i].level0, rx_data.received_symbols[i].duration1, rx_data.received_symbols[i].level1);
// }
int num = this->decode_symbols(rx_data.received_symbols, rx_data.num_symbols, channels[channel_num].decoded_recv_symbols, sizeof(channels[channel_num].decoded_recv_symbols));
if (num < 0){
return ESP_FAIL;
}
// printf("\n\nparsed symbols:\n");
// for (int i = 0; i < num; i++){
// printf("duration0 %d level0 %d duration1 %d level1 %d\n", decoded_recv_symbols[i].duration0, decoded_recv_symbols[i].level0, decoded_recv_symbols[i].duration1, decoded_recv_symbols[i].level1);
// }
*output_size = this->convert_symbols_to_char(channels[channel_num].decoded_recv_symbols, num, recv_buf, size);
if (*output_size < 0){
return ESP_FAIL;
}
return ESP_OK;
}
RMTManager::~RMTManager(){
for (uint8_t i = 0; i < MAX_CHANNELS; i++){
if (this->channels[i].tx_rmt_handle) {
rmt_disable(this->channels[i].tx_rmt_handle);
rmt_del_channel(this->channels[i].tx_rmt_handle);
}
if (channels[i].rx_rmt_handle) {
rmt_disable(channels[i].rx_rmt_handle);
rmt_del_channel(channels[i].rx_rmt_handle);
}
if (channels[i].rx_queue) {
vQueueDelete(channels[i].rx_queue);
}
}
}

View File

@@ -0,0 +1,163 @@
#ifndef RMT_COMMUNICATIONS
#define RMT_COMMUNICATIONS
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "soc/gpio_num.h"
#include "RMTSymbols.h"
#include <cstring>
#define MAX_CHANNELS 4
#define RMT_SYMBOL_BLOCK_SIZE 48
#define RECEIVE_BUFFER_SIZE 1024 //this is some value (we should probably set it to some packet size that we predetermine in some custom protocol:tm:)
#define DEBUG_TAG "RMTManager"
#define CHANNEL_LISTENING (0x2) //channel waiting to receive
#define CHANNEL_READY_STATUS (0x1) //channel able to send and ready to start receive async job
#define CHANNEL_NOT_READY_STATUS (0x0) //channel is not ready (cannot send and/or receive)
#define QUEUE_SIZE 10
/**
* @brief This struct keeps track of the current byte and bit index of the user data being transmmitted via RMT
*
*/
typedef struct {
size_t byte_index; //which byte is currently being encoded when transmitting
uint8_t bit_index; //which bit in the `byte_index` is currently being encoded (into high/low waveforms)
size_t num_symbols; //temp
#ifdef NRZ_INVERTED
bool current_level;
uint8_t zero_count;
#endif //NRZ_INVERTED
} rmt_encoder_context_t;
typedef struct _rmt_channel{
//TX
uint8_t tx_gpio;
rmt_channel_handle_t tx_rmt_handle;
SemaphoreHandle_t tx_done_semaphore;
QueueHandle_t tx_queue;
rmt_encoder_handle_t encoder; //encoder config
rmt_encoder_context_t encoder_context;
//RX
uint8_t rx_gpio;
rmt_channel_handle_t rx_rmt_handle;
QueueHandle_t rx_queue;
rmt_symbol_word_t raw_symbols[RECEIVE_BUFFER_SIZE]; //buffer to store the symbols on receive
rmt_symbol_word_t decoded_recv_symbols[RECEIVE_BUFFER_SIZE]; //allocating some dummy size buffer for decoded string
//General
uint8_t status;
} rmt_channel;
class RMTManager{
public:
RMTManager();
~RMTManager();
int send(uint8_t* data, size_t size, rmt_transmit_config_t* config, uint8_t channel_num); //temp function to send some string data
int receive(uint8_t* recv_buf, size_t size, size_t* output_size, uint8_t channel_num);
static size_t encoder_callback(const void* data, size_t data_size, size_t symbols_written,
size_t symbols_free, rmt_symbol_word_t* symbols, bool* done, void* arg);
static bool rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data);
static bool rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data);
esp_err_t start_receiving(uint8_t channel_num);
esp_err_t wait_until_send_complete(uint8_t channel_num);
private:
esp_err_t init();
void reset_encoder_context(rmt_encoder_context_t* ctx);
esp_err_t init_tx_channel();
esp_err_t init_rx_channel();
int decode_symbols(rmt_symbol_word_t* symbols, size_t num, rmt_symbol_word_t* decoded, size_t output_num);
int convert_symbols_to_char(rmt_symbol_word_t* symbols, size_t num, uint8_t* string, size_t output_num);
rmt_channel channels[MAX_CHANNELS] = {0};
//=====================TX=====================
// rmt_channel_handle_t tx_chan;
const gpio_num_t tx_gpio[MAX_CHANNELS] = {GPIO_NUM_1, GPIO_NUM_2, GPIO_NUM_3, GPIO_NUM_4}; //using pins 1,2,3,4 for channels 0,1,2,3 respectively for tx
// gpio_num_t tx_gpio[MAX_CHANNELS] = {GPIO_NUM_1}; //using pins 1,2,3,4 for channels 0,1,2,3 respectively for tx
// rmt_encoder_context_t encoder_context = {0};
//semaphore to indicate it is done
// SemaphoreHandle_t tx_done_semaphore;
//will be used to temporarily hold the bits that are being wait to be sent -- not working
// QueueHandle_t transmit_queue = NULL;
// TxCallbackContext tx_context;
//=====================RX=====================
rmt_channel_handle_t rx_chan;
const gpio_num_t rx_gpio[MAX_CHANNELS] = {GPIO_NUM_12, GPIO_NUM_13, GPIO_NUM_14, GPIO_NUM_15}; //using pins 12,13,14,15 for channels 0,1,2,3 respectively for rx
// gpio_num_t rx_gpio[MAX_CHANNELS] = {GPIO_NUM_12}; //using pins 12,13,14,15 for channels 0,1,2,3 respectively for rx
// QueueHandle_t receive_queue = NULL;
//rx_receive_config
rmt_receive_config_t receive_config = {
.signal_range_min_ns = 100,
.signal_range_max_ns = 200 * 1000,
.flags = {
.en_partial_rx = true
}
};
// bool ready_to_receive = false;
};
//will need to keep the data alive until it has been transmitted (not working or being used atm)
struct TxCallbackContext{
SemaphoreHandle_t tx_done_sem;
QueueHandle_t transmit_queue;
rmt_encoder_context_t* tx_context;
};
typedef struct {
const uint8_t* data;
size_t length;
} TxBuffer;
typedef struct _gpio_channel_pair {
gpio_num_t tx_pin;
gpio_num_t rx_pin;
} GPIO_Channel_Pair;
static const GPIO_Channel_Pair gpio_channel_pairs[MAX_CHANNELS] = {
{
.tx_pin = GPIO_NUM_1,
.rx_pin = GPIO_NUM_12
},
{
.tx_pin = GPIO_NUM_2,
.rx_pin = GPIO_NUM_13
},
{
.tx_pin = GPIO_NUM_3,
.rx_pin = GPIO_NUM_14
},
{
.tx_pin = GPIO_NUM_4,
.rx_pin = GPIO_NUM_15
}
}; //todo: use these pairs directly instead of the two arrays in the class definition above
#endif //RMT_COMMUNICATIONS

View File

@@ -0,0 +1,136 @@
#ifdef RMT_COMMUNICATIONS
#include "driver/rmt_tx.h"
// #ifdef MANCHESTER_40
// #define RMT_RESOLUTION_HZ 40 * 1000 * 1000 // 40MHz resolution
// #define RMT_DURATION_SYMBOL 10 //0.25 us bit duration
// #elif NRZ_INVERTED
// #ifdef NRZ_INVERTED_40_HZ
// #define RMT_RESOLUTION_HZ 40 * 1000 * 1000 // 40MHz resolution
// #else
// #define RMT_RESOLUTION_HZ 20 * 1000 * 1000 // 20MHz resolution
// #endif //NRZ_INVERTED_40_HZ
// #ifdef NRZ_INVERTED_10
// #define RMT_DURATION_SYMBOL 10 //0.5us bit duration
// #elif NRZ_INVERTED_20
// #define RMT_DURATION_SYMBOL 5 //0.25 us bit duration
// #elif NRZ_INVERTED_2
// #define RMT_DURATION_SYMBOL 2 //0.1 us bit duration
// #else
// #define RMT_DURATION_SYMBOL 20 //1us bit duration
// #endif //NRZ_INVERTED_10
// #else
// #define RMT_RESOLUTION_HZ 1 * 1000 * 1000 // 1MHz resolution
// #define RMT_DURATION_SYMBOL 10
// #endif //MANCHESTER_40
// #define NRZ_INVERTED //using NRZ_I
#define RMT_RESOLUTION_HZ 40 * 1000 * 1000 // 40MHz resolution
#define RMT_DURATION_SYMBOL 12 //0.6us
// #define RMT_DURATION_SYMBOL ((RMT_RESOLUTION_HZ * 3) / 1000000) // duration time for a symbol - this is 3us
#define RMT_DURATION_MAX (2 * RMT_DURATION_SYMBOL)
// #define RMT_TX_GPIO GPIO_NUM_1 //RMT will use GPIO pin 1 to transmit (on one channel)
// #define RMT_RX_GPIO GPIO_NUM_12 // RMT will use GPIO pin 12 to receive (on one channel)
#ifndef NRZ_INVERTED
//MANCHESTER ENCODING (ETHERNET STANDARD)
/**
* @brief This struct represents a 1 symbol being transmitted over RMT. This will create a falling edge (low for `RMT_DURATION_SYMBOL` and high for `RMT_DURATION_SYMBOL`)
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ONE = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 1,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 0,
};
/**
* @brief This struct represents a 0 symbol being transmitted over RMT. This will create a rising edge (low for `RMT_DURATION_SYMBOL` and high for `RMT_DURATION_SYMBOL`)
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ZERO = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 0,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 1,
};
#else
//Non-Return-to-Zero Inverted (NRZ-I)
#define CONSEC_ZERO_THRESHOLD 3 //max number of consecutive zeros before adding a bit 1
// Logic 1 inverts the current voltage state
/**
* @brief This struct represents a 1 symbol being transmitted over RMT. This will create a falling edge (low for `RMT_DURATION_SYMBOL` and high for `RMT_DURATION_SYMBOL`)
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ONE_FALLING = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 1,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 0,
};
/**
* @brief This struct represents a 1 symbol being transmitted over RMT. This will create a rising edge (low for `RMT_DURATION_SYMBOL` and high for `RMT_DURATION_SYMBOL`)
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ONE_RISING = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 0,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 1,
};
/**
* @brief This struct will represent a bit 0. In NRZ-I, this represents a no change in the voltage
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ZERO_HIGH = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 1,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 1,
};
/**
* @brief This struct will represent a bit 0. In NRZ-I, this represents a no change in the voltage
*
*/
static const rmt_symbol_word_t RMT_SYMBOL_ZERO_LOW = {
.duration0 = RMT_DURATION_SYMBOL,
.level0 = 0,
.duration1 = RMT_DURATION_SYMBOL,
.level1 = 0,
};
#endif //NRZ_INVERTED
//not used at the moment
// static const rmt_symbol_word_t RMT_SYMBOL_HIGH_STOP = {
// .duration0 = RMT_DURATION_SYMBOL / 2,
// .level0 = 1,
// .duration1 = RMT_DURATION_SYMBOL / 2,
// .level1 = 0,
// };
// static const rmt_symbol_word_t RMT_SYMBOL_LOW_STOP = {
// .duration0 = RMT_DURATION_SYMBOL / 2,
// .level0 = 0,
// .duration1 = RMT_DURATION_SYMBOL / 2,
// .level1 = 1,
// };
#endif //RMT_COMMUNICATIONS

View File

@@ -1,3 +1,40 @@
idf_component_register(SRCS "main.cpp" idf_component_register(SRCS "main.cpp"
PRIV_REQUIRES spi_flash nvs_flash esp_event rpc constants config PRIV_REQUIRES spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink
INCLUDE_DIRS "") INCLUDE_DIRS "")
if(DEFINED BOARD_NAME AND BOARD_NAME)
message(STATUS "Building for board name: " ${BOARD_NAME})
idf_build_set_property(COMPILE_DEFINITIONS BOARD_${BOARD_NAME} APPEND)
endif()
if(DEFINED TIME_TEST AND TIME_TEST)
message(STATUS "Building with TIME_TEST: " ${TIME_TEST})
idf_build_set_property(COMPILE_DEFINITIONS TIME_TEST APPEND)
endif()
if(DEFINED MANCHESTER_40 AND MANCHESTER_40)
message(STATUS "Building with MANCHESTER_40: " ${MANCHESTER_40})
idf_build_set_property(COMPILE_DEFINITIONS MANCHESTER_40 APPEND)
endif()
if(DEFINED NRZ_INVERTED AND NRZ_INVERTED)
message(STATUS "Building with NRZ_INVERTED: " ${NRZ_INVERTED})
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED APPEND)
endif()
if(DEFINED NRZ_INVERTED_10 AND NRZ_INVERTED_10)
message(STATUS "Building with NRZ_INVERTED_10: " ${NRZ_INVERTED_10})
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_10 APPEND)
endif()
if(DEFINED NRZ_INVERTED_20 AND NRZ_INVERTED_20)
message(STATUS "Building with NRZ_INVERTED_20: " ${NRZ_INVERTED_20})
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_20 APPEND)
endif()
if(DEFINED NRZ_INVERTED_2 AND NRZ_INVERTED_2)
message(STATUS "Building with NRZ_INVERTED_2: " ${NRZ_INVERTED_2})
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_2 APPEND)
endif()
if(DEFINED NRZ_INVERTED_40_HZ AND NRZ_INVERTED_40_HZ)
message(STATUS "Building with NRZ_INVERTED_40_HZ: " ${NRZ_INVERTED_40_HZ})
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_40_HZ APPEND)
endif()
if(DEFINED VERIFY_RECEIVE AND VERIFY_RECEIVE)
message(STATUS "Building with VERIFY_RECEIVE: " ${VERIFY_RECEIVE})
idf_build_set_property(COMPILE_DEFINITIONS VERIFY_RECEIVE APPEND)
endif()

5
main/README.md Normal file
View File

@@ -0,0 +1,5 @@
# RMT Sample Code
WIP
Creates 2 threads per channel (there are 4 channels - 4 TX and 4 RX). Each tx thread is sending messages and the corresponding rx thread will receive the message and print its contents if it is a valid frame.

256
main/link_layer_main.cpp Normal file
View File

@@ -0,0 +1,256 @@
//Used for link layer testing (change name to main.cpp to use)
#include <cstdio>
#include <memory>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "nvs_flash.h"
#include "RMTManager.h"
#include "DataLinkManager.h"
#include <esp_netif.h>
#include <esp_event.h>
#include <freertos/semphr.h>
#include "driver/gptimer.h"
#include "esp_log.h"
struct TaskArgs{
DataLinkManager* link_layer_obj;
uint8_t task_id;
uint8_t receiver_id;
QueueHandle_t receive_queue;
};
struct ReceviedFrame{
uint8_t buf[MAX_CONTROL_DATA_LEN + CONTROL_FRAME_OVERHEAD]; //max 41B
size_t len;
};
void receive_frames(void* arg){
TaskArgs* args = (TaskArgs*)arg;
DataLinkManager* obj = args->link_layer_obj;
if (obj == nullptr){
ESP_LOGE("thread", "bad pointer\n");
vTaskDelete(NULL); //should never get here
}
QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
uint8_t curr_channel = args-> task_id;
printf("RX JOB for task %d starting...\n", curr_channel);
esp_err_t res;
uint8_t recv_buf[256];
memset(recv_buf, 0, 256);
size_t recv_len = 0;
ReceviedFrame recv_frame = {};
while(true){
res = obj->start_receive_frames(curr_channel); // this will be moved to a separate thread with a shared queue
if (res != ESP_OK){
ESP_LOGE("thread", "Failed to start rx async job on thread %d", curr_channel);
continue;
}
res = obj->receive(recv_buf, sizeof(recv_buf), &recv_len, curr_channel);
if (res != ESP_OK){
ESP_LOGE("thread", "Failed to receive message on thread %d", curr_channel);
continue;
} else {
// printf("Successfully receive message\n");
}
if (recv_len == 0){
continue;
}
recv_frame.len = recv_len;
memcpy((void*)recv_frame.buf, (void*)recv_buf, recv_len);
if (xQueueSendToBack(shared_queue, (void*)&recv_frame, (TickType_t) 10) != pdPASS){
ESP_LOGE("RX Job", "Failed to push received frame onto shared queue for channel %d", curr_channel);
}
}
}
void multi_transceiver(void* arg) {
TaskArgs* args = (TaskArgs*)arg;
DataLinkManager* obj = args->link_layer_obj;
if (obj == nullptr){
ESP_LOGE("thread", "bad pointer\n");
vTaskDelete(NULL); //should never get here
}
xTaskCreate(receive_frames, "receive_frame_job", 4096, arg, 5, NULL);
uint8_t dest_board_id = args->receiver_id; //using a dummy number for now - there is no board with id 2 right now
const char* message = "THIS IS A TEXT MESSAGE";
uint8_t curr_channel = args->task_id;
QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
uint8_t send_buf[256];
uint8_t recv_buf[256];
memset(recv_buf, 0, 256);
memset(send_buf, 0, 256);
size_t recv_len = 0;
uint8_t iteration = 0;
esp_err_t res;
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer));
uint64_t start_count = 0, end_count = 0;
uint32_t num_incorrect = 0;
uint32_t total_transactions = 0;
ReceviedFrame recv_frame = {};
printf("task %d starting...\n", curr_channel);
while(true){
vTaskDelay(1000 / portTICK_PERIOD_MS); // wait 1 second before trying to send again
snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "%s %d CH. %d", message, iteration, curr_channel);
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count));
res = obj->send(dest_board_id, send_buf, strlen(reinterpret_cast<char*>(send_buf)), FrameType::DEBUG_CONTROL_TYPE, curr_channel);
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count));
snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "%s RANDOM", message); //modifying the data while it transmits shouldn't affect the actual transmission here
if (res != ESP_OK){
ESP_LOGE("thread", "Failed to send message on thread %d", curr_channel);
} else {
printf("Successfully sent message\n");
// printf("Sent %zu B sized in %" PRIu64 " us\n", strlen(reinterpret_cast<char*>(send_buf)) + CONTROL_FRAME_OVERHEAD, end_count-start_count);
}
//wait on a queue for a few ms (if there's nothing, just send another frame. otherwise pop from queue and read it)
if (xQueueReceive(shared_queue, (void*)&recv_frame, (TickType_t) 50) != pdPASS){
memset(send_buf, 0, 256);
continue; //nothing or failed to pop from queue
}
res = obj->print_frame_info(recv_frame.buf, recv_frame.len, recv_buf);
if (res != ESP_OK){
num_incorrect++;
printf("Received %ld bad frames on tx/rx round %ld for thread %d\n", num_incorrect, total_transactions, curr_channel);
} else {
printf("Received message %s on channel %d on round %ld. Total bad frames %ld\n", recv_buf, curr_channel, total_transactions, num_incorrect);
}
total_transactions++;
iteration++;
if (iteration == 100){
iteration = 0;
}
// vTaskDelay(1000 / portTICK_PERIOD_MS); // wait 1 second before trying to send again
//reset temp buffers
memset(recv_buf, 0, 256);
memset(send_buf, 0, 256);
}
}
void print_binary(unsigned char c) {
for (int i = 7; i >= 0; i--) {
printf("%d", (c >> i) & 1);
}
}
void print_string_binary(const char *str) {
while (*str) {
print_binary((unsigned char)*str);
printf(" "); // space between bytes for readability
str++;
}
printf("\n");
}
extern "C" [[noreturn]] void app_main(void) {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
esp_netif_init();
esp_event_loop_create_default();
printf("finished esp init\n");
printf("Hello world!\n");
// uint8_t iteration = 0;
// const char* message = "THIS IS A TEXT MESSAGE";
uint8_t board_id = 69;
std::unique_ptr<DataLinkManager> obj = std::make_unique<DataLinkManager>(board_id);
// uint8_t dest_board_id = 2; //using a dummy number for now - there is no board with id 2 right now
// esp_err_t res;
// uint8_t send_buf[256];
// uint8_t recv_buf[256];
// size_t recv_len = 0;
// uint8_t curr_channel = 0;
DataLinkManager* obj_to_send = obj.release();
TaskArgs args[4] = {};
for (uint8_t i = 0; i < MAX_CHANNELS; i++){
args[i].link_layer_obj = obj_to_send;
args[i].task_id = i;
args[i].receiver_id = i+1;
args[i].receive_queue = xQueueCreate(10, sizeof(ReceviedFrame)); //queue storing up to 10 control frames
xTaskCreate(multi_transceiver, "multi_transceiver", 4096, static_cast<void*>(&args[i]), 5, NULL);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
printf("Tasks have been created\n");
while(true){
//do nothing
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
for (int i = 5; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
while(true){
//dummy wait
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}

View File

@@ -1,3 +1,4 @@
//mdns and other stuff main file
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>

237
main/main_rmt_timing.cpp Normal file
View File

@@ -0,0 +1,237 @@
#include <cstdio>
#include <memory>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "nvs_flash.h"
#include "RMTManager.h"
#include <esp_netif.h>
#include <esp_event.h>
#include <freertos/semphr.h>
#define BOARD_A_MESSAGE "MESSAGE FROM BOARD A"
#define BOARD_B_MESSAGE "MESSAGE FROM BOARD B"
#ifdef TIME_TEST
#include <inttypes.h>
#include "driver/gptimer.h"
#endif //TIME_TEST
// void rmt_task(void* arg) {
// vTaskDelay(pdMS_TO_TICKS(3000)); // wait 3 seconds to stabilize heap
// const auto obj = std::make_unique<RMTManager>();
// const char* message = "THIS IS A SAMPLE TEXT MESSAGE";
// rmt_transmit_config_t tx_config = {
// .loop_count = 0,
// .flags = {
// .eot_level = 0 // typically 0 or 1, depending on your output idle level
// }
// };
// int res = obj->send(message, strlen(message), &tx_config);
// if (res == ESP_OK){
// printf("Successfully sent '%s'\n", message);
// } else{
// printf("Failed to send '%s'\n", message);
// }
// vTaskDelete(NULL);
// }
void print_binary(unsigned char c) {
for (int i = 7; i >= 0; i--) {
printf("%d", (c >> i) & 1);
}
}
void print_string_binary(const char *str) {
while (*str) {
print_binary((unsigned char)*str);
printf(" "); // space between bytes for readability
str++;
}
printf("\n");
}
/**
* @brief This main function shows the RMT TX and RX working by sending a message string in `message` over a GPIO pin and receiving on another pin
*
*/
extern "C" [[noreturn]] void app_main(void) {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
esp_netif_init();
esp_event_loop_create_default();
printf("finished esp init\n");
printf("Hello world!\n");
const auto obj = std::make_unique<RMTManager>();
#ifdef TIME_TEST
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer));
uint64_t start_count = 0, end_count = 0;
uint64_t sum = 0; //used to calculate the average send time
uint64_t num_iterations = 0;
#endif //TIME_TEST
#ifdef BOARD_A
const char* message = BOARD_A_MESSAGE;
#elif BOARD_B
const char* message = BOARD_B_MESSAGE;
#else
const char* message = "THIS IS A SAMPLE TEXT MESSAGE";
#endif
#ifdef VERIFY_RECEIVE
uint64_t num_received = 0;
uint64_t num_corrupted = 0;
#endif //VERIFY_RECEIVE
// const char* message = "t";
rmt_transmit_config_t tx_config = {
.loop_count = 0,
.flags = {
.eot_level = 0 // typically 0 or 1, depending on your output idle level
}
};
int res = ESP_OK;
char recv_message[256];
// xTaskCreate(rmt_task, "rmt_task", 4096, NULL, 5, NULL);
while(true){
#ifndef TIME_TEST
printf("Starting RX receive\n");
res = obj->start_receiving();
if (res != ESP_OK){
printf("Something went wrong... terminating..\n");
continue;
}
#endif //TIME_TEST
printf("sending message %s - binary:\n", message);
print_string_binary(message);
#ifdef TIME_TEST
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count));
#endif //TIME_TEST
res = obj->send(message, strlen(message), &tx_config);
if (res == ESP_OK){
// printf("Successfully started send job for message '%s'\n", message);
} else{
printf("Failed to start send job for message '%s'\n", message);
// continue; //do not continue on
}
res = obj->wait_until_send_complete(); //will wait until the the message is sent
#ifdef TIME_TEST
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count));
#endif //TIME_TEST
if (res == ESP_OK){
#ifndef TIME_TEST
printf("Successfully sent message '%s'\n", message);
#else
printf("Sent %zu B sized message %s in %" PRIu64 " us on iteration %" PRIu64 "\n", strlen(message), message, end_count-start_count, num_iterations);
sum += (end_count - start_count);
#endif //TIME_TEST
} else{
printf("Failed to send '%s'\n", message);
continue;
}
#ifndef TIME_TEST
res = obj->receive(recv_message, sizeof(recv_message));
if (res != 0){
printf("Failed to receive message\n");
} else {
printf("Received message %s\n", recv_message);
}
#ifdef VERIFY_RECEIVE
printf("Checking message for corruption on iteration %lld\n", num_received);
#ifdef BOARD_A
//check if BOARD_B_MESSAGE was received correctly
if (strcmp(recv_message, BOARD_B_MESSAGE) != 0){
num_corrupted++;
}
#elif BOARD_B
if (strcmp(recv_message, BOARD_A_MESSAGE) != 0){
num_corrupted++;
}
#endif //BOARD_B
num_received++;
#endif //VERIFY_RECEIVE
memset(recv_message, 0, sizeof(recv_message));
#endif //TIME_TEST
vTaskDelay(2000 / portTICK_PERIOD_MS);
#ifdef TIME_TEST
num_iterations++;
if (num_iterations > 100){
break;
}
#endif //TIME_TEST
#ifdef VERIFY_RECEIVE
if (num_received > 100){
break;
}
#endif //VERIFY_RECEIVE
}
#ifdef TIME_TEST
float avg = (sum/num_iterations) / 1e6; //avg send time us to s
printf("Average Transmission Rate is: %.9f bits per second\n", (float)((strlen(message) * 8)/avg));
printf("Average sent time is: %.9f seconds\n", avg);
#endif //TIME_TEST
#ifdef VERIFY_RECEIVE
float avg_received_corrupted = (num_corrupted * 100) / (num_received-1);
printf("Average corruption rate is: %.6f %% \n", avg_received_corrupted);
printf("Total number of corrupted messages over %lld iterations is: %lld\n", num_received-1, num_corrupted);
#endif //VERIFY_RECEIVE
for (int i = 5; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
while(true){
//dummy wait
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}

228
sdkconfig
View File

@@ -93,7 +93,6 @@ CONFIG_SOC_APB_BACKUP_DMA=y
CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y
CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y
CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y
CONFIG_SOC_CACHE_ACS_INVALID_STATE_ON_PANIC=y
CONFIG_SOC_CPU_CORES_NUM=2 CONFIG_SOC_CPU_CORES_NUM=2
CONFIG_SOC_CPU_INTR_NUM=32 CONFIG_SOC_CPU_INTR_NUM=32
CONFIG_SOC_CPU_HAS_FPU=y CONFIG_SOC_CPU_HAS_FPU=y
@@ -145,10 +144,8 @@ CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y
CONFIG_SOC_I2S_SUPPORTS_PCM=y CONFIG_SOC_I2S_SUPPORTS_PCM=y
CONFIG_SOC_I2S_SUPPORTS_PDM=y CONFIG_SOC_I2S_SUPPORTS_PDM=y
CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y
CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y
CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y
CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y
CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2
CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y
CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4 CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4
CONFIG_SOC_I2S_SUPPORTS_TDM=y CONFIG_SOC_I2S_SUPPORTS_TDM=y
CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y
@@ -170,8 +167,6 @@ CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3
CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y
CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1
CONFIG_SOC_MMU_PERIPH_NUM=1 CONFIG_SOC_MMU_PERIPH_NUM=1
CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000
CONFIG_SOC_MPU_REGIONS_MAX_NUM=8
CONFIG_SOC_PCNT_GROUPS=1 CONFIG_SOC_PCNT_GROUPS=1
CONFIG_SOC_PCNT_UNITS_PER_GROUP=4 CONFIG_SOC_PCNT_UNITS_PER_GROUP=4
CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2
@@ -232,7 +227,7 @@ CONFIG_SOC_SPI_SCT_SUPPORTED=y
CONFIG_SOC_SPI_SCT_REG_NUM=14 CONFIG_SOC_SPI_SCT_REG_NUM=14
CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y
CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA
CONFIG_SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED=y CONFIG_SOC_MEMSPI_SRC_FREQ_120M=y
CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y
CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y
CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y
@@ -255,13 +250,9 @@ CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
CONFIG_SOC_TOUCH_SENSOR_VERSION=2 CONFIG_SOC_TOUCH_SENSOR_VERSION=2
CONFIG_SOC_TOUCH_SENSOR_NUM=15 CONFIG_SOC_TOUCH_SENSOR_NUM=15
CONFIG_SOC_TOUCH_MIN_CHAN_ID=y
CONFIG_SOC_TOUCH_MAX_CHAN_ID=14
CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y
CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y
CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y
CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y
CONFIG_SOC_TOUCH_SUPPORT_DENOISE_CHAN=y
CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3 CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3
CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y
CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1
@@ -279,7 +270,6 @@ CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y
CONFIG_SOC_UART_SUPPORT_APB_CLK=y CONFIG_SOC_UART_SUPPORT_APB_CLK=y
CONFIG_SOC_UART_SUPPORT_RTC_CLK=y CONFIG_SOC_UART_SUPPORT_RTC_CLK=y
CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y
CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y
CONFIG_SOC_USB_OTG_PERIPH_NUM=1 CONFIG_SOC_USB_OTG_PERIPH_NUM=1
CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968
CONFIG_SOC_SHA_SUPPORT_DMA=y CONFIG_SOC_SHA_SUPPORT_DMA=y
@@ -322,7 +312,6 @@ CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y
CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y
CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y
CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y
CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y
CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y
CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y
CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y
@@ -349,7 +338,7 @@ CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y
CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y
CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y
CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y
CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y CONFIG_SOC_SPI_MEM_SUPPORT_OPI_MODE=y
CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y
CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y
CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y
@@ -411,17 +400,6 @@ CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y
CONFIG_BOOTLOADER_PROJECT_VER=1 CONFIG_BOOTLOADER_PROJECT_VER=1
# end of Bootloader manager # end of Bootloader manager
#
# Application Rollback
#
# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set
# end of Application Rollback
#
# Bootloader Rollback
#
# end of Bootloader Rollback
CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set
@@ -431,8 +409,6 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
# #
# Log # Log
# #
CONFIG_BOOTLOADER_LOG_VERSION_1=y
CONFIG_BOOTLOADER_LOG_VERSION=1
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
@@ -447,14 +423,6 @@ CONFIG_BOOTLOADER_LOG_LEVEL=3
# CONFIG_BOOTLOADER_LOG_COLORS is not set # CONFIG_BOOTLOADER_LOG_COLORS is not set
CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y
# end of Format # end of Format
#
# Settings
#
CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y
CONFIG_BOOTLOADER_LOG_MODE_TEXT=y
# CONFIG_BOOTLOADER_LOG_MODE_BINARY is not set
# end of Settings
# end of Log # end of Log
# #
@@ -471,6 +439,7 @@ CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y
CONFIG_BOOTLOADER_WDT_ENABLE=y CONFIG_BOOTLOADER_WDT_ENABLE=y
# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set # CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set
CONFIG_BOOTLOADER_WDT_TIME_MS=9000 CONFIG_BOOTLOADER_WDT_TIME_MS=9000
# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set
# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set
# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set
# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set # CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set
@@ -514,7 +483,6 @@ CONFIG_ESP_ROM_HAS_HAL_WDT=y
CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y
CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y
CONFIG_ESP_ROM_HAS_SPI_FLASH=y CONFIG_ESP_ROM_HAS_SPI_FLASH=y
CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y
CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y
CONFIG_ESP_ROM_HAS_NEWLIB=y CONFIG_ESP_ROM_HAS_NEWLIB=y
CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y
@@ -528,7 +496,6 @@ CONFIG_ESP_ROM_HAS_SW_FLOAT=y
CONFIG_ESP_ROM_HAS_VERSION=y CONFIG_ESP_ROM_HAS_VERSION=y
CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y
CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y
CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y
# #
# Boot ROM Behavior # Boot ROM Behavior
@@ -698,12 +665,6 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set # CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2S Driver Configurations # end of Legacy I2S Driver Configurations
#
# Legacy I2C Driver Configurations
#
# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2C Driver Configurations
# #
# Legacy PCNT Driver Configurations # Legacy PCNT Driver Configurations
# #
@@ -724,13 +685,6 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set # CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set # CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Temperature Sensor Driver Configurations # end of Legacy Temperature Sensor Driver Configurations
#
# Legacy Touch Sensor Driver Configurations
#
# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Touch Sensor Driver Configurations
# end of Driver Configurations # end of Driver Configurations
# #
@@ -766,8 +720,7 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
# #
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set # CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set # CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set # CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:GPTimer Configurations # end of ESP-Driver:GPTimer Configurations
@@ -777,7 +730,6 @@ CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
# CONFIG_I2C_ISR_IRAM_SAFE is not set # CONFIG_I2C_ISR_IRAM_SAFE is not set
# CONFIG_I2C_ENABLE_DEBUG_LOG is not set # CONFIG_I2C_ENABLE_DEBUG_LOG is not set
# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set # CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set
CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
# end of ESP-Driver:I2C Configurations # end of ESP-Driver:I2C Configurations
# #
@@ -812,15 +764,9 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
# #
# ESP-Driver:RMT Configurations # ESP-Driver:RMT Configurations
# #
CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y
CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y
CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y
# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set
# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set
# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set
CONFIG_RMT_OBJ_CACHE_SAFE=y
# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
# CONFIG_RMT_ISR_IRAM_SAFE is not set # CONFIG_RMT_ISR_IRAM_SAFE is not set
# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set
# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:RMT Configurations # end of ESP-Driver:RMT Configurations
# #
@@ -933,18 +879,15 @@ CONFIG_RTC_CLK_CAL_CYCLES=1024
# #
# Peripheral Control # Peripheral Control
# #
CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y
# end of Peripheral Control # end of Peripheral Control
# #
# GDMA Configurations # GDMA Configurations
# #
CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y
CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y
CONFIG_GDMA_OBJ_DRAM_SAFE=y
# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set
# CONFIG_GDMA_ISR_IRAM_SAFE is not set # CONFIG_GDMA_ISR_IRAM_SAFE is not set
# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set
# end of GDMA Configurations # end of GDMA Configurations
# #
@@ -954,28 +897,7 @@ CONFIG_XTAL_FREQ_40=y
CONFIG_XTAL_FREQ=40 CONFIG_XTAL_FREQ=40
# end of Main XTAL Config # end of Main XTAL Config
#
# Power Supplier
#
#
# Brownout Detector
#
CONFIG_ESP_BROWNOUT_DET=y
CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_ESP_BROWNOUT_DET_LVL=7
CONFIG_ESP_BROWNOUT_USE_INTR=y
# end of Brownout Detector
# end of Power Supplier
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
CONFIG_ESP_INTR_IN_IRAM=y
# end of Hardware Settings # end of Hardware Settings
# #
@@ -1026,7 +948,6 @@ CONFIG_ESP_PHY_CALIBRATION_MODE=0
# #
# Power Management # Power Management
# #
CONFIG_PM_SLEEP_FUNC_IN_IRAM=y
# CONFIG_PM_ENABLE is not set # CONFIG_PM_ENABLE is not set
CONFIG_PM_SLP_IRAM_OPT=y CONFIG_PM_SLP_IRAM_OPT=y
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
@@ -1039,12 +960,6 @@ CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y
# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set
# end of ESP Ringbuf # end of ESP Ringbuf
#
# ESP-ROM
#
CONFIG_ESP_ROM_PRINT_IN_IRAM=y
# end of ESP-ROM
# #
# ESP Security Specific # ESP Security Specific
# #
@@ -1144,6 +1059,22 @@ CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set # CONFIG_ESP_DEBUG_STUBS_ENABLE is not set
CONFIG_ESP_DEBUG_OCDAWARE=y CONFIG_ESP_DEBUG_OCDAWARE=y
CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y
#
# Brownout Detector
#
CONFIG_ESP_BROWNOUT_DET=y
CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_ESP_BROWNOUT_DET_LVL=7
# end of Brownout Detector
CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y
CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y
# end of ESP System Settings # end of ESP System Settings
@@ -1158,7 +1089,6 @@ CONFIG_ESP_IPC_ISR_ENABLE=y
# #
# ESP Timer (High Resolution Timer) # ESP Timer (High Resolution Timer)
# #
CONFIG_ESP_TIMER_IN_IRAM=y
# CONFIG_ESP_TIMER_PROFILING is not set # CONFIG_ESP_TIMER_PROFILING is not set
CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y
CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y
@@ -1201,12 +1131,10 @@ CONFIG_ESP_WIFI_IRAM_OPT=y
CONFIG_ESP_WIFI_RX_IRAM_OPT=y CONFIG_ESP_WIFI_RX_IRAM_OPT=y
CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y
CONFIG_ESP_WIFI_ENABLE_SAE_PK=y CONFIG_ESP_WIFI_ENABLE_SAE_PK=y
CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y
CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y
CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y
# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set # CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set
CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50
# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set
CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10
CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15
# CONFIG_ESP_WIFI_FTM_ENABLE is not set # CONFIG_ESP_WIFI_FTM_ENABLE is not set
@@ -1307,7 +1235,6 @@ CONFIG_FREERTOS_DEBUG_OCDAWARE=y
CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y
CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
CONFIG_FREERTOS_NUMBER_OF_CORES=2 CONFIG_FREERTOS_NUMBER_OF_CORES=2
CONFIG_FREERTOS_IN_IRAM=y
# end of FreeRTOS # end of FreeRTOS
# #
@@ -1319,6 +1246,8 @@ CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y
# CONFIG_HAL_ASSERTION_ENABLE is not set # CONFIG_HAL_ASSERTION_ENABLE is not set
CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2
CONFIG_HAL_WDT_USE_ROM_IMPL=y CONFIG_HAL_WDT_USE_ROM_IMPL=y
CONFIG_HAL_SPI_MASTER_FUNC_IN_IRAM=y
CONFIG_HAL_SPI_SLAVE_FUNC_IN_IRAM=y
# end of Hardware Abstraction Layer (HAL) and Low Level (LL) # end of Hardware Abstraction Layer (HAL) and Low Level (LL)
# #
@@ -1339,9 +1268,6 @@ CONFIG_HEAP_TRACING_OFF=y
# #
# Log # Log
# #
CONFIG_LOG_VERSION_1=y
# CONFIG_LOG_VERSION_2 is not set
CONFIG_LOG_VERSION=1
# #
# Log Level # Log Level
@@ -1379,16 +1305,6 @@ CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
# end of Format # end of Format
#
# Settings
#
CONFIG_LOG_MODE_TEXT_EN=y
CONFIG_LOG_MODE_TEXT=y
# CONFIG_LOG_MODE_BINARY is not set
# end of Settings
CONFIG_LOG_IN_IRAM=y
# end of Log # end of Log
# #
@@ -1396,6 +1312,7 @@ CONFIG_LOG_IN_IRAM=y
# #
CONFIG_LWIP_ENABLE=y CONFIG_LWIP_ENABLE=y
CONFIG_LWIP_LOCAL_HOSTNAME="espressif" CONFIG_LWIP_LOCAL_HOSTNAME="espressif"
# CONFIG_LWIP_NETIF_API is not set
CONFIG_LWIP_TCPIP_TASK_PRIO=18 CONFIG_LWIP_TCPIP_TASK_PRIO=18
# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set # CONFIG_LWIP_TCPIP_CORE_LOCKING is not set
# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set # CONFIG_LWIP_CHECK_THREAD_SAFETY is not set
@@ -1541,7 +1458,6 @@ CONFIG_LWIP_DNS_MAX_HOST_IP=1
CONFIG_LWIP_DNS_MAX_SERVERS=3 CONFIG_LWIP_DNS_MAX_SERVERS=3
# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set # CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set
# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set # CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set
# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set
# end of DNS # end of DNS
CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7 CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7
@@ -1562,9 +1478,6 @@ CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y
CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y
# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set
# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set # CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set
CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y
# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set
# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_CUSTOM is not set
CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set # CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set # CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set
@@ -1631,7 +1544,6 @@ CONFIG_MBEDTLS_HAVE_TIME=y
# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set # CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set
# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y
CONFIG_MBEDTLS_SHA1_C=y
CONFIG_MBEDTLS_SHA512_C=y CONFIG_MBEDTLS_SHA512_C=y
# CONFIG_MBEDTLS_SHA3_C is not set # CONFIG_MBEDTLS_SHA3_C is not set
CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
@@ -1713,27 +1625,23 @@ CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
# CONFIG_MBEDTLS_THREADING_C is not set # CONFIG_MBEDTLS_THREADING_C is not set
CONFIG_MBEDTLS_ERROR_STRINGS=y CONFIG_MBEDTLS_ERROR_STRINGS=y
CONFIG_MBEDTLS_FS_IO=y CONFIG_MBEDTLS_FS_IO=y
# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set
# end of mbedTLS # end of mbedTLS
# #
# LibC # Newlib
# #
CONFIG_LIBC_NEWLIB=y CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
CONFIG_LIBC_MISC_IN_IRAM=y # CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set
CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y # CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set
CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y # CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set
# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set # CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set
# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set # CONFIG_NEWLIB_NANO_FORMAT is not set
# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_LIBC_STDIN_LINE_ENDING_CR=y # CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set
# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set # CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set
CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y # CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set
# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set # end of Newlib
# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set
# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set
# end of LibC
# #
# NVS # NVS
@@ -1791,8 +1699,6 @@ CONFIG_SPI_FLASH_HPM_DC_AUTO=y
# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set # CONFIG_SPI_FLASH_AUTO_SUSPEND is not set
CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50
# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set # CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set
# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set
CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y
# end of Optional and Experimental Features (READ DOCS FIRST) # end of Optional and Experimental Features (READ DOCS FIRST)
# end of Main Flash configuration # end of Main Flash configuration
@@ -1901,7 +1807,6 @@ CONFIG_MDNS_PREDEF_NETIF_AP=y
# Deprecated options for backward compatibility # Deprecated options for backward compatibility
# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set # CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
# CONFIG_NO_BLOBS is not set # CONFIG_NO_BLOBS is not set
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
@@ -1909,6 +1814,7 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3 CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set # CONFIG_FLASH_ENCRYPTION_ENABLED is not set
# CONFIG_FLASHMODE_QIO is not set # CONFIG_FLASHMODE_QIO is not set
# CONFIG_FLASHMODE_QOUT is not set # CONFIG_FLASHMODE_QOUT is not set
@@ -1932,7 +1838,6 @@ CONFIG_STACK_CHECK_NONE=y
# CONFIG_WARN_WRITE_STRINGS is not set # CONFIG_WARN_WRITE_STRINGS is not set
# CONFIG_EXTERNAL_COEX_ENABLE is not set # CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set # CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
# CONFIG_MCPWM_ISR_IN_IRAM is not set # CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set # CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_ISR=y
@@ -1945,26 +1850,6 @@ CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set # CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set # CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024
CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y
CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_BROWNOUT_DET_LVL=7
CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
@@ -1998,6 +1883,24 @@ CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set # CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
CONFIG_ESP32S3_DEBUG_OCDAWARE=y CONFIG_ESP32S3_DEBUG_OCDAWARE=y
CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y
CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_BROWNOUT_DET_LVL=7
CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_IPC_TASK_STACK_SIZE=1280 CONFIG_IPC_TASK_STACK_SIZE=1280
CONFIG_TIMER_TASK_STACK_SIZE=3584 CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP32_WIFI_ENABLED=y CONFIG_ESP32_WIFI_ENABLED=y
@@ -2060,22 +1963,11 @@ CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_PPP_SUPPORT is not set # CONFIG_PPP_SUPPORT is not set
CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set
CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
# CONFIG_NEWLIB_NANO_FORMAT is not set
CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y
# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set # CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set
# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set # CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set # CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set
# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set # CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072