/* 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 #include #include 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); }