mirror of
https://github.com/BotChain-Robots/firmware.git
synced 2026-07-08 17:47:21 +02:00
rmt + some of link layer
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
/build
|
||||
.DS_Store
|
||||
sdkconfig.old
|
||||
.vscode/*
|
||||
|
||||
3
components/dataLink/CMakeLists.txt
Normal file
3
components/dataLink/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "DataLinkManager.cpp"
|
||||
PRIV_REQUIRES driver esp_event nvs_flash esp_netif rmt
|
||||
INCLUDE_DIRS "include")
|
||||
635
components/dataLink/DataLinkManager.cpp
Normal file
635
components/dataLink/DataLinkManager.cpp
Normal 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);
|
||||
}
|
||||
21
components/dataLink/README.md
Normal file
21
components/dataLink/README.md
Normal 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
|
||||
91
components/dataLink/include/DataLinkManager.h
Normal file
91
components/dataLink/include/DataLinkManager.h
Normal 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
|
||||
98
components/dataLink/include/Frames.h
Normal file
98
components/dataLink/include/Frames.h
Normal 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
|
||||
32
components/dataLink/include/Tables.h
Normal file
32
components/dataLink/include/Tables.h
Normal 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
|
||||
4
components/rmt/CMakeLists.txt
Normal file
4
components/rmt/CMakeLists.txt
Normal 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
41
components/rmt/README.md
Normal 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)
|
||||
|
||||
733
components/rmt/RMTManager.cpp
Normal file
733
components/rmt/RMTManager.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
components/rmt/include/RMTManager.h
Normal file
163
components/rmt/include/RMTManager.h
Normal 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
|
||||
136
components/rmt/include/RMTSymbols.h
Normal file
136
components/rmt/include/RMTSymbols.h
Normal 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
|
||||
@@ -1,3 +1,40 @@
|
||||
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 "")
|
||||
|
||||
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
5
main/README.md
Normal 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
256
main/link_layer_main.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//mdns and other stuff main file
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
|
||||
237
main/main_rmt_timing.cpp
Normal file
237
main/main_rmt_timing.cpp
Normal 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
228
sdkconfig
@@ -93,7 +93,6 @@ CONFIG_SOC_APB_BACKUP_DMA=y
|
||||
CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y
|
||||
CONFIG_SOC_CACHE_WRITEBACK_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_INTR_NUM=32
|
||||
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_PDM=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_SUPPORTS_PDM_RX=y
|
||||
CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4
|
||||
CONFIG_SOC_I2S_SUPPORTS_TDM=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_MMU_LINEAR_ADDRESS_REGION_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_UNITS_PER_GROUP=4
|
||||
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_BUFFER_NUM_MAX=y
|
||||
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_40M_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_TOUCH_SENSOR_VERSION=2
|
||||
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_WATERPROOF=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_MEAS_DONE_SUPPORTED=y
|
||||
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_RTC_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_SHA_DMA_MAX_BUFFER_SIZE=3968
|
||||
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_CLK_RC_FAST_SUPPORT_CALIBRATION=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_DCACHE=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_RESUME=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_CONFIG_GPIO_BY_EFUSE=y
|
||||
CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y
|
||||
@@ -411,17 +400,6 @@ CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y
|
||||
CONFIG_BOOTLOADER_PROJECT_VER=1
|
||||
# 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_COMPILER_OPTIMIZATION_SIZE=y
|
||||
# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set
|
||||
@@ -431,8 +409,6 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
#
|
||||
# Log
|
||||
#
|
||||
CONFIG_BOOTLOADER_LOG_VERSION_1=y
|
||||
CONFIG_BOOTLOADER_LOG_VERSION=1
|
||||
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
|
||||
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR 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_TIMESTAMP_SOURCE_CPU_TICKS=y
|
||||
# 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
|
||||
|
||||
#
|
||||
@@ -471,6 +439,7 @@ CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y
|
||||
CONFIG_BOOTLOADER_WDT_ENABLE=y
|
||||
# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set
|
||||
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_ON_POWER_ON 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_HAS_LAYOUT_TABLE=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_NEWLIB=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_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y
|
||||
CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y
|
||||
CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y
|
||||
|
||||
#
|
||||
# Boot ROM Behavior
|
||||
@@ -698,12 +665,6 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
|
||||
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
|
||||
# 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
|
||||
#
|
||||
@@ -724,13 +685,6 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
|
||||
# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
|
||||
# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
|
||||
# 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
|
||||
|
||||
#
|
||||
@@ -766,8 +720,7 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
|
||||
#
|
||||
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
|
||||
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
|
||||
# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set
|
||||
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
|
||||
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
|
||||
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
|
||||
# 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_ENABLE_DEBUG_LOG 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
|
||||
|
||||
#
|
||||
@@ -812,15 +764,9 @@ CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
|
||||
#
|
||||
# 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_RECV_FUNC_IN_IRAM is not set
|
||||
# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
|
||||
# end of ESP-Driver:RMT Configurations
|
||||
|
||||
#
|
||||
@@ -933,18 +879,15 @@ CONFIG_RTC_CLK_CAL_CYCLES=1024
|
||||
#
|
||||
# Peripheral Control
|
||||
#
|
||||
CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
|
||||
# end of Peripheral Control
|
||||
|
||||
#
|
||||
# GDMA Configurations
|
||||
#
|
||||
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_ENABLE_DEBUG_LOG is not set
|
||||
# end of GDMA Configurations
|
||||
|
||||
#
|
||||
@@ -954,28 +897,7 @@ CONFIG_XTAL_FREQ_40=y
|
||||
CONFIG_XTAL_FREQ=40
|
||||
# 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_INTR_IN_IRAM=y
|
||||
# end of Hardware Settings
|
||||
|
||||
#
|
||||
@@ -1026,7 +948,6 @@ CONFIG_ESP_PHY_CALIBRATION_MODE=0
|
||||
#
|
||||
# Power Management
|
||||
#
|
||||
CONFIG_PM_SLEEP_FUNC_IN_IRAM=y
|
||||
# CONFIG_PM_ENABLE is not set
|
||||
CONFIG_PM_SLP_IRAM_OPT=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
|
||||
# end of ESP Ringbuf
|
||||
|
||||
#
|
||||
# ESP-ROM
|
||||
#
|
||||
CONFIG_ESP_ROM_PRINT_IN_IRAM=y
|
||||
# end of ESP-ROM
|
||||
|
||||
#
|
||||
# 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_OCDAWARE=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
|
||||
# end of ESP System Settings
|
||||
|
||||
@@ -1158,7 +1089,6 @@ CONFIG_ESP_IPC_ISR_ENABLE=y
|
||||
#
|
||||
# ESP Timer (High Resolution Timer)
|
||||
#
|
||||
CONFIG_ESP_TIMER_IN_IRAM=y
|
||||
# CONFIG_ESP_TIMER_PROFILING is not set
|
||||
CONFIG_ESP_TIME_FUNCS_USE_RTC_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_ENABLE_WPA3_SAE=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_ENABLE_WPA3_OWE_STA=y
|
||||
# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set
|
||||
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_WAIT_BROADCAST_DATA_TIME=15
|
||||
# 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_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
|
||||
CONFIG_FREERTOS_NUMBER_OF_CORES=2
|
||||
CONFIG_FREERTOS_IN_IRAM=y
|
||||
# end of FreeRTOS
|
||||
|
||||
#
|
||||
@@ -1319,6 +1246,8 @@ CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y
|
||||
# CONFIG_HAL_ASSERTION_ENABLE is not set
|
||||
CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2
|
||||
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)
|
||||
|
||||
#
|
||||
@@ -1339,9 +1268,6 @@ CONFIG_HEAP_TRACING_OFF=y
|
||||
#
|
||||
# Log
|
||||
#
|
||||
CONFIG_LOG_VERSION_1=y
|
||||
# CONFIG_LOG_VERSION_2 is not set
|
||||
CONFIG_LOG_VERSION=1
|
||||
|
||||
#
|
||||
# Log Level
|
||||
@@ -1379,16 +1305,6 @@ CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31
|
||||
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
|
||||
# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
|
||||
# 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
|
||||
|
||||
#
|
||||
@@ -1396,6 +1312,7 @@ CONFIG_LOG_IN_IRAM=y
|
||||
#
|
||||
CONFIG_LWIP_ENABLE=y
|
||||
CONFIG_LWIP_LOCAL_HOSTNAME="espressif"
|
||||
# CONFIG_LWIP_NETIF_API is not set
|
||||
CONFIG_LWIP_TCPIP_TASK_PRIO=18
|
||||
# CONFIG_LWIP_TCPIP_CORE_LOCKING 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_FALLBACK_DNS_SERVER_SUPPORT is not set
|
||||
# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set
|
||||
# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set
|
||||
# end of DNS
|
||||
|
||||
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_DEFAULT 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_DEFAULT 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_HAVE_TIME_DATE is not set
|
||||
CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y
|
||||
CONFIG_MBEDTLS_SHA1_C=y
|
||||
CONFIG_MBEDTLS_SHA512_C=y
|
||||
# CONFIG_MBEDTLS_SHA3_C is not set
|
||||
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_ERROR_STRINGS=y
|
||||
CONFIG_MBEDTLS_FS_IO=y
|
||||
# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set
|
||||
# end of mbedTLS
|
||||
|
||||
#
|
||||
# LibC
|
||||
# Newlib
|
||||
#
|
||||
CONFIG_LIBC_NEWLIB=y
|
||||
CONFIG_LIBC_MISC_IN_IRAM=y
|
||||
CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y
|
||||
CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y
|
||||
# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set
|
||||
# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set
|
||||
# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set
|
||||
# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set
|
||||
CONFIG_LIBC_STDIN_LINE_ENDING_CR=y
|
||||
# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set
|
||||
CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y
|
||||
# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set
|
||||
# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set
|
||||
# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set
|
||||
# end of LibC
|
||||
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_NEWLIB_TIME_SYSCALL_USE_RTC is not set
|
||||
# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set
|
||||
# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set
|
||||
# end of Newlib
|
||||
|
||||
#
|
||||
# NVS
|
||||
@@ -1791,8 +1699,6 @@ CONFIG_SPI_FLASH_HPM_DC_AUTO=y
|
||||
# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set
|
||||
CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50
|
||||
# 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 Main Flash configuration
|
||||
|
||||
@@ -1901,7 +1807,6 @@ CONFIG_MDNS_PREDEF_NETIF_AP=y
|
||||
# Deprecated options for backward compatibility
|
||||
# CONFIG_APP_BUILD_TYPE_ELF_RAM 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_ERROR 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_VERBOSE is not set
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL=3
|
||||
# CONFIG_APP_ROLLBACK_ENABLE is not set
|
||||
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
|
||||
# CONFIG_FLASHMODE_QIO 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_EXTERNAL_COEX_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_EVENT_LOOP_PROFILING is not set
|
||||
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_INT_8MD256 is not set
|
||||
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_INIT_DATA_IN_PARTITION is not set
|
||||
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_ESP32_DEBUG_STUBS_ENABLE is not set
|
||||
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_TIMER_TASK_STACK_SIZE=3584
|
||||
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=0x7FFFFFFF
|
||||
# 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_FRC1=y
|
||||
# CONFIG_NEWLIB_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_FRC1 is not set
|
||||
# CONFIG_NEWLIB_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_STACK_SIZE_DEFAULT=3072
|
||||
|
||||
Reference in New Issue
Block a user