/* * Project: Arrow * Author: curiousmuch * Description: This project is an APRS transceiver based on the ESP32 and CC1200. * TODO: Refactor Code to isolate into seperate components * TODO: Isolate BT_SPP.C functionality * TODO: Isolate KISS.C functionality * TODO: Remove any dependancies on Direwolf. * TODO: Transfer DSP functions to APRS folder * TODO: Abstract includes to be nicer (#include aprs.h) for example * TODO: Isolate CC1200 functions from other parts / maybe integrate most of TX scheme into task. */ /* Standard Includes */ #include /* ESP-IDF */ #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "freertos/ringbuf.h" #include "nvs.h" #include "nvs_flash.h" #include "esp_log.h" /* Application Specific */ #include "bt_spp.h" #include "board.h" #include "radio.h" #include "kiss.h" #include "fcs_calc.h" /* Data Structures */ typedef struct { uint8_t tnc_number; uint8_t tx_delay; uint8_t P; uint8_t slot_time; uint8_t full_duplex; uint8_t tx_tail; } tnc_settings_t; /* Public Variables */ static tnc_settings_t tnc_settings; static SemaphoreHandle_t xRadioTXSemaphore; uint8_t ax25_rx_buf[1024]; RingbufHandle_t ax25_tx_buf; /* Radio Callback Functions */ void radio_rx_ax25_cb(uint8_t *frame, uint32_t len) { // TODO: move kiss processing to other core kiss_transmit(KISS_DATAFRAME, frame, len); } void radio_tx_ax25_cb(void) { // unlock scheduling task to allow next packet to be sent xSemaphoreGive(xRadioTXSemaphore); } /* KISS callback Functions */ void kiss_tx_cb(uint8_t *frame, uint32_t len) { // TODO: move function to other core // TODO: Add functionality which indicates if BLE connection is present or not // send data to UART via bluetooth bt_spp_tx(frame, len); ESP_LOGI("tnc", "kiss packet sent"); } void kiss_rx_cb(uint8_t *frame, uint32_t len) { uint8_t data_byte; data_byte = frame[0] & 0x0F; // tnc_number information // process dataframe if (data_byte == KISS_DATAFRAME) { ESP_LOGI("tnc", "kiss packet received"); // feed packet to ring buffer uint32_t fcs = fcs_calc(&frame[1], len-1); frame[len] = fcs & 0xFF; frame[len+1] = fcs>>8 & 0xFF; if (xRingbufferSend(ax25_tx_buf, &frame[1], ((len+1)*sizeof(uint8_t)),10/portTICK_PERIOD_MS) == pdFALSE) { ESP_LOGE("tnc", "packet buffer overflow"); } } else // assume command { switch(data_byte) { case KISS_CMD_TXDELAY: ESP_LOGI("tnc", "updated tx delay"); tnc_settings.tx_delay = frame[1]; break; case KISS_CMD_P: ESP_LOGI("tnc", "updated persistance"); tnc_settings.P = frame[1]; break; case KISS_CMD_SLOTTIME: ESP_LOGI("tnc", "updated slot time"); tnc_settings.slot_time = frame[1]; break; case KISS_CMD_TXTAIL: ESP_LOGI("tnc", "updated tx tail"); tnc_settings.tx_tail = frame[1]; break; case KISS_CMD_FULLDUPLEX: ESP_LOGI("tnc", "updated duplex setting"); tnc_settings.full_duplex = frame[1]; break; case KISS_CMD_SETHARDWARE: ESP_LOGI("tnc", "updated hardware settings"); break; case KISS_CMD_RETURN: ESP_LOGI("tnc", "wtf is return"); break; default: ESP_LOGE("tnc", "unknown data byte for KISS protocol"); } // reload for parameter changes to take effect } } /* TNC Tasks */ void tnc_task(void *para) { uint8_t *packet; size_t packet_size; uint8_t P; radio_packet_rx(NULL); while(1) { // indicate RX enable_green_led(); disable_red_led(); // check ring buffer for packet packet = xRingbufferReceive(ax25_tx_buf, &packet_size, portMAX_DELAY); ESP_LOGI("tnc", "scheduling transmission"); if (packet != NULL) { // wait based on persistance algorithm while (1) { // TODO: carrier detect is causing a crash somehow when it gets locked on // to high. I'm confused as the unit should continue to run despite this... if (radio_get_cs() == 0) { ESP_LOGV("radio", "carrier not detected"); P = (uint8_t) esp_random(); if (P <= tnc_settings.P) { ESP_LOGI("radio", "transmission scheduled"); break; } else { ESP_LOGV("radio", "transmission postponed"); vTaskDelay(tnc_settings.slot_time * 10 / portTICK_PERIOD_MS); } } else { ESP_LOGV("radio", "carrier detected"); vTaskDelay(10 / portTICK_PERIOD_MS); // re-check in 10ms } } // indicate TX enable_red_led(); disable_green_led(); // TODO: modify code so the full TX DELAY isn't used when multiple packets are sent. // dump all the packets in the buffer while(packet != NULL) { // setup packet to be transmitted radio_packet_tx(packet, packet_size, NULL); // sleep until packet is finished being sent xSemaphoreTake(xRadioTXSemaphore, portMAX_DELAY); // remove packet from ring buffer and load next one if it exists vRingbufferReturnItem(ax25_tx_buf, (void *)packet); packet = xRingbufferReceive(ax25_tx_buf, &packet_size, 0); } } else { ESP_LOGE("tnc", "buffer error"); } // setup again for reception radio_packet_rx(NULL); } } void IRAM_ATTR app_main() { // Inter-Task Communication ax25_tx_buf = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT); xRadioTXSemaphore = xSemaphoreCreateBinary(); // Initialize Flash 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 ); // Board IO Initialize board_init(); // Load Settings from Flash // Radio Initialize ax25_param_t ax25_param; ax25_param.tx_tail = 10; ax25_param.tx_delay = 100; ax25_param.sample_rate = 13200; ax25_param.symbol0_freq = 1200; ax25_param.symbol1_freq = 2200; ax25_param.rx_cb = radio_rx_ax25_cb; ax25_param.tx_cb = radio_tx_ax25_cb; ax25_param.rx_buf = ax25_rx_buf; ax25_param.rx_buf_len = sizeof(ax25_rx_buf) / sizeof(ax25_rx_buf[0]); ax25_param.cpu_core = 1; radio_init(AX25, &ax25_param); // Kiss Decoder and Encoder kiss_init(0, kiss_tx_cb, kiss_rx_cb); // BLE and SPP bt_spp_init(); // TNC Settings tnc_settings.slot_time = 10; tnc_settings.P = 63; tnc_settings.tnc_number = 0; // Tasks xTaskCreatePinnedToCore(tnc_task, "tnc task", 1024*4, 0, 2, NULL, 1); }