/* * 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: Transfer DSP functions to APRS folder * TODO: Abstract includes to be nicer (#include aprs.h) for example */ /* Standard Includes */ #include #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 enum { TNC_TX=0, TNC_RX, TNC_IDLE } tnc_status_t; 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; uint8_t frequency_index; uint8_t mode_index; uint32_t freq; radio_config_t mode; tnc_status_t status; TaskHandle_t tnc_task; } 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; uint32_t ax_freq_table[16] = { 144390000, // APRS - North America 145512500, // Arrow - AX25 test frequencies... 145537500, 145562500, 145587500, 145612500, 145637500, 145662500, 145687500, 145712500, 145737500, 145762500, 145787500, 145812500, 145837500, // ...145.5 to 145.8 is experimental band 145862500 }; // 25kHz spacing /* 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 tnc_print_settings(void) { ESP_LOGI("tnc", "TX Delay: %d", tnc_settings.tx_delay*10); ESP_LOGI("tnc", "TX Tail: %d", tnc_settings.tx_tail*10); ESP_LOGI("tnc", "Slot Time: %d", tnc_settings.slot_time*10); ESP_LOGI("tnc", "P: %d", tnc_settings.P); ESP_LOGI("tnc", "Frequency: %d", tnc_settings.freq); } 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; // remove 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 { ESP_LOGI("tnc", "received tnc command"); switch(data_byte) { case KISS_CMD_TXDELAY: tnc_settings.tx_delay = frame[1]; break; case KISS_CMD_P: tnc_settings.P = frame[1]; break; case KISS_CMD_SLOTTIME: tnc_settings.slot_time = frame[1]; break; case KISS_CMD_TXTAIL: tnc_settings.tx_tail = frame[1]; break; case KISS_CMD_FULLDUPLEX: tnc_settings.full_duplex = frame[1]; break; case KISS_CMD_SETHARDWARE: { uint8_t freq_index = frame[1] & 0x0F; tnc_settings.freq = ax_freq_table[freq_index]; break; } default: ESP_LOGE("tnc", "unknown data byte for KISS protocol"); } // wait for radio to finish transmitting ESP_LOGW("tnc", "waiting to disable tnc..."); while(tnc_settings.status != TNC_RX) { vTaskDelay(100 / portTICK_PERIOD_MS); // wait 100ms and try again } // show user new settings tnc_print_settings(); // disable TNC task vTaskSuspend(tnc_settings.tnc_task); ESP_LOGW("tnc", "disabled tnc"); // re-initialize radio ax25_param_t ax25_param; ax25_param.tx_tail = tnc_settings.tx_tail; ax25_param.tx_delay = tnc_settings.tx_delay; 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_reinit(AX25, &ax25_param); radio_set_frequency(tnc_settings.freq); // set radio back to RX radio_packet_rx(NULL); // resume TNC task vTaskResume(tnc_settings.tnc_task); ESP_LOGI("tnc", "re-enabled tnc"); } } /* TNC Tasks */ void tnc_task(void *para) { uint8_t *packet; size_t packet_size; uint8_t P; radio_packet_rx(NULL); tnc_settings.status = TNC_RX; 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 tnc_settings.status = TNC_TX; radio_packet_tx(packet, packet_size, NULL); // sleep until packet is finished being sent xSemaphoreTake(xRadioTXSemaphore, portMAX_DELAY); tnc_settings.status = TNC_IDLE; // 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); tnc_settings.status = TNC_RX; } } void 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 default TNC settings tnc_settings.P = 63; tnc_settings.tx_tail = 0; // 10ms units tnc_settings.tx_delay = 10; // 10ms units tnc_settings.P = 63; tnc_settings.slot_time = 10; // 10ms units tnc_settings.tnc_number = 0; tnc_settings.status = TNC_IDLE; tnc_settings.freq = 144390000; tnc_settings.mode = AX25; // Radio Initialize ax25_param_t ax25_param; ax25_param.tx_tail = tnc_settings.tx_tail; ax25_param.tx_delay = tnc_settings.tx_delay; 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; // TODO: setup switch statement for settings parameters or for mode.... tnc_print_settings(); radio_init(AX25, &ax25_param); radio_set_frequency(tnc_settings.freq); // Kiss Decoder and Encoder kiss_init(tnc_settings.tnc_number, kiss_tx_cb, kiss_rx_cb); // BLE and SPP bt_spp_init(); // Tasks xTaskCreatePinnedToCore(tnc_task, "tnc task", 1024*4, 0, 2, &tnc_settings.tnc_task, 1); }