490 lines
15 KiB
C
490 lines
15 KiB
C
/* UART asynchronous example, that uses separate RX and TX tasks
|
|
|
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
|
Unless required by applicable law or agreed to in writing, this
|
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
*/
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include "driver/uart.h"
|
|
#include "string.h"
|
|
#include "driver/gpio.h"
|
|
#include "soc/uart_struct.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
static const int RX_BUF_SIZE = 256;
|
|
|
|
#define TXD_PIN (GPIO_NUM_16)
|
|
#define RXD_PIN (GPIO_NUM_17)
|
|
|
|
typedef uint8_t u8; // 8-bit unsigned
|
|
typedef uint16_t u16; // 16-bit unsigned
|
|
|
|
/* IrDA CRC-16 table (polynomial 0x1021, LSB-first) */
|
|
static const u16 fcstab[256] = {
|
|
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
|
|
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
|
|
0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
|
|
0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
|
|
0x2102, 0x308B, 0x0210, 0x1299, 0x6726, 0x76AF, 0x4434, 0x55BD,
|
|
0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
|
|
0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
|
|
0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
|
|
0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
|
|
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
|
|
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
|
|
0xDEcd, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
|
|
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
|
|
0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
|
|
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
|
|
0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
|
|
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
|
|
0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
|
|
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
|
|
0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
|
|
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
|
|
0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
|
|
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
|
|
0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
|
|
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
|
|
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
|
|
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
|
|
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
|
|
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
|
|
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
|
|
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
|
|
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
|
|
};
|
|
|
|
#define PPPINITFCS 0xFFFF
|
|
#define PPPGOODFCS 0xF0B8
|
|
|
|
u16 irda_crc(u16 fcs, unsigned char *cp, int len) {
|
|
printf("irda_crc - FCS len: %d\n", len);
|
|
|
|
printf("irda_crc - FCS_Payload: ");
|
|
for (size_t j = 0; j < len; j++) {
|
|
uint8_t byte = cp[j];
|
|
printf("%02X ", byte);
|
|
}
|
|
printf("\n");
|
|
|
|
while (len--) {
|
|
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
|
|
}
|
|
ESP_LOGI("irda_crc1", "CRC: 0x%04X", fcs);
|
|
|
|
return fcs;
|
|
}
|
|
|
|
int sendData(const char* logName, const char* data, int data_length)
|
|
{
|
|
for (size_t j = 0; j < data_length; j++) {
|
|
uint8_t byte = data[j];
|
|
printf("%02X ", byte);
|
|
}
|
|
printf("\n");
|
|
|
|
const int txBytes = uart_write_bytes(UART_NUM_1, data, data_length);
|
|
ESP_LOGI(logName, "Wrote %d bytes", txBytes);
|
|
return txBytes;
|
|
}
|
|
|
|
|
|
uint32_t read_address(const uint8_t* buffer) {
|
|
return (uint32_t)(
|
|
(buffer[0] << 24) |
|
|
(buffer[1] << 16) |
|
|
(buffer[2] << 8) |
|
|
buffer[3]
|
|
);
|
|
}
|
|
|
|
uint32_t parse_pxid_packets(const uint8_t *data, size_t len) {
|
|
static const char *TASK_TAG = "PXID_TASK";
|
|
esp_log_level_set(TASK_TAG, ESP_LOG_INFO);
|
|
|
|
uint32_t destination_address = 0;
|
|
uint32_t source_address = 0;
|
|
|
|
ESP_LOGI(TASK_TAG, "Parsing P-XID Packet: %d bytes", len);
|
|
// Print payload in hex
|
|
for (size_t j = 0; j < len; j++) {
|
|
uint8_t byte = data[j];
|
|
printf("%02X ", byte);
|
|
|
|
// Start new line every 16 bytes
|
|
if ((j + 1) % 16 == 0) {
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
|
|
size_t i = 0;
|
|
int packet_count = 0;
|
|
|
|
while (i < len) {
|
|
// Find beginning of P-XID Payload
|
|
if (data[i] != 0x01) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Extract address fields
|
|
|
|
uint8_t source_address_arr[4];
|
|
source_address_arr[0] = data[i+1];
|
|
source_address_arr[1] = data[i+2];
|
|
source_address_arr[2] = data[i+3];
|
|
source_address_arr[3] = data[i+4];
|
|
|
|
source_address = read_address(source_address_arr);
|
|
|
|
ESP_LOGI(TASK_TAG, "Source Address: 0x%04lX", source_address);
|
|
|
|
uint8_t destination_address_arr[4];
|
|
destination_address_arr[0] = data[i+5];
|
|
destination_address_arr[1] = data[i+6];
|
|
destination_address_arr[2] = data[i+7];
|
|
destination_address_arr[3] = data[i+8];
|
|
destination_address = read_address(destination_address_arr);
|
|
|
|
ESP_LOGI(TASK_TAG, "Destination Address: 0x%04lX", destination_address);
|
|
|
|
uint8_t discovery_flag = data[i+9];
|
|
uint8_t slot_num = data[i+10];
|
|
uint8_t version_num = data[i+11];
|
|
|
|
ESP_LOGI(TASK_TAG, "Discovery Flag: 0x%02X", discovery_flag);
|
|
ESP_LOGI(TASK_TAG, "Slot Number: 0x%02X", slot_num);
|
|
ESP_LOGI(TASK_TAG, "Version Number: 0x%02X", version_num);
|
|
|
|
// Move up i
|
|
i = i + 12;
|
|
// Print payload in hex and ASCII
|
|
for (size_t j = 0; j < (len - i); j++) {
|
|
uint8_t byte = data[i + j];
|
|
printf("%02X ", byte);
|
|
|
|
// Start new line every 16 bytes
|
|
if ((j + 1) % 16 == 0) {
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
|
|
// Print ASCII representation
|
|
for (size_t j = 0; j < (len - i); j++) {
|
|
uint8_t byte = data[i + j];
|
|
putchar((byte >= 0x20 && byte <= 0x7E) ? byte : '.');
|
|
}
|
|
printf("\n\n");
|
|
|
|
break;
|
|
}
|
|
return source_address;
|
|
}
|
|
|
|
uint8_t reply_to_pxid(const uint32_t destination_address) {
|
|
static const char *TASK_TAG = "PXID REPLY";
|
|
esp_log_level_set(TASK_TAG, ESP_LOG_INFO);
|
|
|
|
ESP_LOGI(TASK_TAG, "Construct reply packet");
|
|
uint8_t reply_packet[36];
|
|
|
|
//XBOF
|
|
reply_packet[0] = 0xFF;
|
|
reply_packet[1] = 0xFF;
|
|
reply_packet[2] = 0xFF;
|
|
reply_packet[3] = 0xFF;
|
|
reply_packet[4] = 0xFF;
|
|
reply_packet[5] = 0xFF;
|
|
reply_packet[6] = 0xFF;
|
|
reply_packet[7] = 0xFF;
|
|
reply_packet[8] = 0xFF;
|
|
reply_packet[9] = 0xFF;
|
|
|
|
//BOF
|
|
reply_packet[10] = 0xC0;
|
|
//A
|
|
reply_packet[11] = 0xfe;
|
|
//C
|
|
reply_packet[12] = 0xbf;
|
|
//I
|
|
reply_packet[13] = 0x01;
|
|
reply_packet[14] = 0xfe;
|
|
reply_packet[15] = 0x42;
|
|
reply_packet[16] = 0x28;
|
|
reply_packet[17] = 0x66;
|
|
reply_packet[18] = 0x06;
|
|
reply_packet[19] = (destination_address >> 24) & 0xFF;
|
|
reply_packet[20] = (destination_address >> 16) & 0xFF;
|
|
reply_packet[21] = (destination_address >> 8) & 0xFF;
|
|
reply_packet[22] = (destination_address) & 0xFF;
|
|
reply_packet[23] = 0x01;
|
|
reply_packet[24] = 0x00;
|
|
reply_packet[25] = 0x00;
|
|
//IrDA Link Manager
|
|
//Service Hint
|
|
reply_packet[26] = 0x90;
|
|
reply_packet[27] = 0x24;
|
|
//Char Set
|
|
reply_packet[28] = 0x00;
|
|
//Device Nickname
|
|
reply_packet[29] = 0x54;
|
|
reply_packet[30] = 0x45;
|
|
reply_packet[31] = 0x53;
|
|
reply_packet[32] = 0x54;
|
|
|
|
// CRC
|
|
reply_packet[33] = 0x00;
|
|
reply_packet[34] = 0x00;
|
|
//EOF
|
|
reply_packet[35] = 0xC1;
|
|
|
|
//FCS = A + C + I (not FCS + EOF)
|
|
uint8_t fcs_payload[22];
|
|
memcpy(fcs_payload, &reply_packet[11], 22 * sizeof(uint8_t));
|
|
|
|
printf("FCS_Payload: ");
|
|
for (size_t j = 0; j < sizeof(fcs_payload); j++) {
|
|
uint8_t byte = fcs_payload[j];
|
|
printf("%02X ", byte);
|
|
}
|
|
printf("\n");
|
|
|
|
// Calculate CRC over Address + Control + Payload + CRC bytes
|
|
uint16_t calculated = irda_crc(PPPINITFCS, fcs_payload, 22);
|
|
|
|
calculated = ~calculated;
|
|
|
|
reply_packet[33] = (calculated) & 0xFF;
|
|
reply_packet[34] = (calculated >> 8) & 0xFF;
|
|
|
|
// Calculate CRC over Address + Control + Payload + CRC bytes
|
|
|
|
uint16_t calculated_crc = irda_crc(PPPINITFCS, &reply_packet[11], 24 * sizeof(uint8_t)); // Total length including CRC
|
|
|
|
ESP_LOGI(TASK_TAG, "CRC Check: %s\n", (calculated_crc == PPPGOODFCS) ? "VALID" : "INVALID");
|
|
|
|
for (size_t j = 0; j < sizeof(reply_packet); j++) {
|
|
uint8_t byte = reply_packet[j];
|
|
printf("%02X ", byte);
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
ESP_LOGI(TASK_TAG, "Send reply packet");
|
|
|
|
char char_array[sizeof(reply_packet)];
|
|
memcpy(char_array, reply_packet, sizeof(char_array));
|
|
|
|
sendData(TASK_TAG, char_array, sizeof(char_array));
|
|
|
|
ESP_LOGI(TASK_TAG, "Sent reply packet");
|
|
return 1;
|
|
}
|
|
|
|
void parse_irda_packets(const uint8_t *data, size_t len) {
|
|
static const char *RX_TASK_TAG = "RX_TASK";
|
|
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
|
|
|
|
size_t i = 0;
|
|
int packet_count = 0;
|
|
ESP_LOGI(RX_TASK_TAG, "Starting to Parse: %d", len);
|
|
|
|
while (i < len) {
|
|
// Find beginning of frame (BOF)
|
|
if (data[i] != 0xC0) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// Basic packet validation
|
|
if (i + 5 > len) { // Minimum valid packet size: C0 FF 3F [CRC] C1
|
|
ESP_LOGI(RX_TASK_TAG, "Truncated packet at offset %zu\n", i);
|
|
break;
|
|
}
|
|
|
|
// Extract header fields
|
|
uint8_t address = data[i+1];
|
|
uint8_t control = data[i+2];
|
|
|
|
// Find end of frame (EOF)
|
|
size_t eof_pos = i;
|
|
while (eof_pos < len && data[eof_pos] != 0xC1) eof_pos++;
|
|
|
|
if (eof_pos >= len) {
|
|
ESP_LOGI(RX_TASK_TAG, "Unterminated packet at offset %zu\n", i);
|
|
break;
|
|
}
|
|
|
|
// Calculate packet length
|
|
size_t packet_len = eof_pos - i + 1;
|
|
|
|
// Verify packet structure
|
|
if (address != 0xFF || control != 0x3F) {
|
|
ESP_LOGI(RX_TASK_TAG, "Invalid header in packet %d at offset %zu\n", packet_count+1, i);
|
|
i = eof_pos + 1;
|
|
continue;
|
|
}
|
|
|
|
// Extract payload and CRC
|
|
size_t payload_start = i + 3;
|
|
size_t payload_len = packet_len - 6; // Subtract header, CRC, and EOF
|
|
uint16_t crc = (data[eof_pos-2] << 8) | data[eof_pos-1];
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "\n=== Packet %d (offset %zu, length %zu) ===\n",
|
|
++packet_count, i, packet_len);
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "Address: 0x%02X", address);
|
|
|
|
// Check if the command bit (7th bit) is set
|
|
if (address & 0x80) {
|
|
// Bit is 1 (command frame)
|
|
ESP_LOGI(RX_TASK_TAG, "Command bit: 1");
|
|
} else {
|
|
// Bit is 0 (response frame)
|
|
ESP_LOGI(RX_TASK_TAG, "Command bit: 0");
|
|
}
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "Control: 0x%02X", control);
|
|
|
|
if (control == 0x3F){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: P-XID");
|
|
}
|
|
else if (control == 0xBF){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: F-XID");
|
|
}
|
|
else if (control == 0x93){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: P-NRM");
|
|
}
|
|
else if (control == 0x73){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: F-UA");
|
|
}
|
|
else if (control == 0x11){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: P-RR");
|
|
}
|
|
else if (control == 0x54){
|
|
ESP_LOGI(RX_TASK_TAG, "Control: P-DISC");
|
|
}
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "CRC: 0x%04X", crc);
|
|
|
|
// Calculate CRC over Address + Control + Payload + CRC bytes
|
|
uint16_t calculated_crc = irda_crc(PPPINITFCS,
|
|
&data[i+1], // Address byte
|
|
(eof_pos - i - 1)); // Total length including CRC
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "CRC Check: %s\n",
|
|
(calculated_crc == PPPGOODFCS) ? "VALID" : "INVALID");
|
|
|
|
ESP_LOGI(RX_TASK_TAG, "Payload (%zu bytes):", payload_len);
|
|
|
|
uint8_t payload[payload_len + 1];
|
|
|
|
// Print payload in hex and ASCII
|
|
for (size_t j = 0; j < payload_len; j++) {
|
|
uint8_t byte = data[payload_start + j];
|
|
printf("%02X ", byte);
|
|
payload[j] = data[payload_start + j];
|
|
|
|
// Start new line every 16 bytes
|
|
if ((j + 1) % 16 == 0) {
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
|
|
// Print ASCII representation
|
|
ESP_LOGI(RX_TASK_TAG, "\nASCII: ");
|
|
for (size_t j = 0; j < payload_len; j++) {
|
|
uint8_t byte = data[payload_start + j];
|
|
putchar((byte >= 0x20 && byte <= 0x7E) ? byte : '.');
|
|
}
|
|
printf("\n\n");
|
|
|
|
|
|
//Now process the payload
|
|
// This is a P-XID
|
|
if (control == 0x3F){
|
|
|
|
uint32_t destination_address = parse_pxid_packets(payload, payload_len);
|
|
uint8_t result = reply_to_pxid(destination_address);
|
|
|
|
if (result == 1){
|
|
ESP_LOGI(RX_TASK_TAG, "Send Success");
|
|
}
|
|
else {
|
|
ESP_LOGI(RX_TASK_TAG, "Send Failed");
|
|
}
|
|
}
|
|
|
|
// Move to next potential packet
|
|
i = eof_pos + 1;
|
|
}
|
|
}
|
|
|
|
void init(void)
|
|
{
|
|
const uart_config_t uart_config = {
|
|
.baud_rate = 9600,
|
|
.data_bits = UART_DATA_8_BITS,
|
|
.parity = UART_PARITY_DISABLE,
|
|
.stop_bits = UART_STOP_BITS_1,
|
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
|
.source_clk = UART_SCLK_DEFAULT,
|
|
};
|
|
// We won't use a buffer for sending data.
|
|
uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
|
|
uart_param_config(UART_NUM_1, &uart_config);
|
|
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
|
uart_set_mode(UART_NUM_1, UART_MODE_IRDA);
|
|
uart_set_line_inverse(UART_NUM_1, UART_SIGNAL_IRDA_RX_INV);
|
|
//ESP_LOGI("INIT", "%d", outcome);
|
|
}
|
|
|
|
static void tx_task(void *arg)
|
|
{
|
|
static const char *TX_TASK_TAG = "TX_TASK";
|
|
esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
|
|
while (1) {
|
|
sendData(TX_TASK_TAG, "Hello world", 10);
|
|
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
static void rx_task(void *arg)
|
|
{
|
|
static const char *RX_TASK_TAG = "RX_TASK";
|
|
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
|
|
uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);
|
|
while (1) {
|
|
const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);
|
|
if (rxBytes > 0) {
|
|
data[rxBytes] = 0;
|
|
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
|
|
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
|
|
|
|
parse_irda_packets(data, rxBytes);
|
|
|
|
}
|
|
}
|
|
free(data);
|
|
}
|
|
|
|
void app_main(void)
|
|
{
|
|
init();
|
|
|
|
xTaskCreate(rx_task, "uart_rx_task", 1024 * 2, NULL, configMAX_PRIORITIES - 1, NULL);
|
|
// xTaskCreate(tx_task, "uart_tx_task", 1024 * 2, NULL, configMAX_PRIORITIES - 2, NULL);
|
|
}
|