/* * 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 */ tnc_settings_t tnc_settings; uint8_t ax25_rx_buf[1024]; RingbufHandle_t ax25_tx_buf; /* Functions and Main */ /* Radio Callback Functions */ // callback function for when radio rx'd AX25 packet // unblocks task on other core and packet to another buffer // so we can continue processing void radio_rx_ax25_cb(uint8_t *frame, uint32_t len) { ESP_LOGV("AX25 CB", "We Here"); // unblock receive task on CPU0 } /* KISS callback Functions */ void kiss_tx_cb(uint8_t *frame, uint32_t len) { // send data to UART / via bluetooth bt_spp_tx(frame, len); } 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) { // feed packet to ring buffer uint32_t fcs = fcs_calc(&frame[1], len-1); frame[len] = fcs & 0xFF; frame[len+1] = fcs>>8 & 0xFF; xRingbufferSend(ax25_tx_buf, &frame[1], ((len+1)*sizeof(uint8_t)),10/portTICK_PERIOD_MS); } 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("KISS", "unknown data byte"); } // reload for parameter changes to take effect } } /* TNC Tasks */ // schedules transmissions based on p-persistance // settings and carrier sense from radio a // TODO: Latch CS during slot time? void tnc_task(void *para) { radio_packet_rx(NULL); uint8_t *packet; size_t packet_size; while(1) { // indicate RX enable_green_led(); disable_red_led(); // check ring buffer for packet packet = xRingbufferReceive(ax25_tx_buf, &packet_size, portMAX_DELAY); if (packet != NULL) { uint8_t P; // wait based on persistance algorithm while (1) { if (!radio_get_cs()) { P = (uint8_t) esp_random(); if (P <= tnc_settings.P) { break; } else { vTaskDelay(tnc_settings.slot_time * 10 / portTICK_PERIOD_MS); } } taskYIELD(); // make sure to yield for RX'ing task } // indicate TX enable_red_led(); disable_green_led(); // dump all the packets in the buffer while(packet != NULL) { radio_packet_tx(packet, packet_size, NULL); vRingbufferReturnItem(ax25_tx_buf, (void *)packet); packet = xRingbufferReceive(ax25_tx_buf, &packet_size, 0); } } else { printf("Why we here \n"); } // setup again for reception radio_packet_rx(NULL); } } void IRAM_ATTR app_main() { // 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 Task Initialize ax25_param_t ax25_param; ax25_param.tx_tail = 10; ax25_param.tx_delay = 10; 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.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(); // Inter-Task Communication ax25_tx_buf = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT); // Tasks xTaskCreatePinnedToCore(tnc_task, "tnc task", 1024*4, 0, 2, NULL, 1); }