Browse Source

updated for TNC command support

curiousmuch 4 years ago
parent
commit
46c0205da9

+ 1 - 1
components/aprs/aprs_encoder.c

@@ -47,7 +47,7 @@ void ax25_encoder_reset(void)
 void ax25_encoder_init(uint8_t tx_delay, uint8_t tx_tail)
 {
 	// set preamble and tx_tail length
-	enc_param.tx_delay = round(tx_delay * 1.5 + 1);	// calculate the number of
+	enc_param.tx_delay = round(tx_delay * 1.5 + 1);	// calculate the number of flags to send
 	enc_param.tx_tail = round(tx_tail * 1.5 + 1);
 
 	//enc_var.state = STOP;

+ 135 - 4
components/board/board.c

@@ -9,6 +9,8 @@
 #include "freertos/task.h"
 #include "driver/gpio.h"
 #include "driver/adc.h"
+#include "esp_adc_cal.h"
+#include "esp_log.h"
 #include "board.h"
 
 // Debugging IO Functions
@@ -44,16 +46,120 @@ void disable_green_led(void)
 	gpio_set_level(GREEN_LED, 0);
 }
 
+// ADC Functions
+
+#define DEFAULT_VREF 1100 // units in mV
+static esp_adc_cal_characteristics_t *adc_chars;
+
+#define ADC_CONSTANT (100) / (100 + 350)
+
+void enable_voltage_divider(void)
+{
+	gpio_set_level(ENABLE_VOLTAGE_DIVIDER, 0);
+}
+
+void disable_voltage_divider(void)
+{
+	// TODO: set to Hi-Z instead
+	gpio_set_level(ENABLE_VOLTAGE_DIVIDER, 1);
+}
+
+static void check_efuse(void)
+{
+	// check TP is burned into eFuse
+	if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK)
+	{
+		ESP_LOGI("board", "eFuse two point: supported");
+	}
+	else
+	{
+		ESP_LOGW("board", "eFuse two point: NOT supported");
+	}
+
+	// check Vref is burned into eFuse
+	if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK)
+	{
+		ESP_LOGI("board", "eFuse Vref: supported");
+	}
+	else
+	{
+		ESP_LOGW("board", "eFuse Vref: NOT supported");
+	}
+
+}
+
+static void print_char_val_type(esp_adc_cal_value_t val_type)
+{
+    if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
+        ESP_LOGI("board", "characterized using two point value");
+    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
+        ESP_LOGI("board", "characterized using eFuse Vref");
+    } else {
+        ESP_LOGW("board", "characterized using default Vref");
+    }
+}
+
 int32_t battery_measure(void)
 {
-	   adc1_config_width(ADC_WIDTH_BIT_12);
-	   adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_0);
-	   int val = adc1_get_raw(BATTERY_ADC_CHANNEL);
-	   return val;
+
+	int32_t adc_val;
+	float bat_val;
+
+	adc_val = adc1_get_raw(BATTERY_ADC_CHANNEL);
+	bat_val = ((float) esp_adc_cal_raw_to_voltage(adc_val, adc_chars)) / 1000;
+	bat_val = bat_val * ADC_CONSTANT;
+
+	if (adc_val < 0)
+	{
+	   ESP_LOGE("board", "invalid measurement");
+	   bat_val = -1;
+	}
+
+	return bat_val;
+}
+
+void battery_monitor_task(void *para)
+{
+	int32_t adc_val;
+	float bat_val, avg_bat_val;
+	uint32_t cycle_count;
+
+	// take 1st measurement
+	bat_val = battery_measure();
+	avg_bat_val = bat_val;
+
+	// measure the battery every X seconds
+	// if the battery voltage is <3.4V flash the
+	// RX LED every 1 second
+	// TODO: This is hella ghetto and likely will cause more issues
+	// than it is worth.
+	while(1)
+	{
+		if (avg_bat_val >= 3.4)
+		{
+			vTaskDelay(10000 / portTICK_PERIOD_MS);
+		}
+		else
+		{
+			disable_red_led();
+			for(cycle_count=0;cycle_count<10;cycle_count++)
+			{
+				enable_red_led();
+				vTaskDelay(250 / portTICK_PERIOD_MS);
+				disable_red_led();
+				vTaskDelay(740 / portTICK_PERIOD_MS);
+			}
+			enable_red_led();
+		}
+		bat_val = battery_measure();
+		avg_bat_val = avg_bat_val*0.9 + bat_val*0.1;
+	}
 }
 
 void board_init(void)
 {
+	ESP_LOGI("board", "LED, debugging, and ADC initialization");
+
 	// setup LED IO
 	gpio_config_t led_pin_config =
 	{
@@ -75,4 +181,29 @@ void board_init(void)
 			.intr_type = GPIO_INTR_DISABLE
 	};
 	gpio_config(&debug_pin_config);
+
+
+	// setup ADC pins
+/*	gpio_config_t adc_pin_config =
+	{
+			.pin_bit_mask = (uint64_t) (BIT64(ENABLE_VOLTAGE_DIVIDER)),
+			.mode = GPIO_MODE_OUTPUT,
+			.pull_up_en = GPIO_PULLUP_DISABLE,
+			.pull_down_en = GPIO_PULLDOWN_DISABLE,
+			.intr_type = GPIO_INTR_DISABLE
+	};
+	gpio_config(&adc_pin_config);*/
+
+	// check if ADC calibration is stored
+	check_efuse();
+
+	// configure ADC
+	adc1_config_width(ADC_WIDTH_BIT_12);
+	adc1_config_channel_atten(BATTERY_ADC_CHANNEL,ADC_ATTEN_DB_0);
+
+	// attempt to load calibraiton information
+	adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
+	esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
+	print_char_val_type(val_type);
+
 }

+ 1 - 1
components/kiss/kiss.c

@@ -104,7 +104,7 @@ void kiss_append_buffer(uint8_t chr)
 void kiss_process_frame(void)
 {
 	// minimum kiss frame is 2 bytes
-    if (rx_buffer.index < 1)
+    if (rx_buffer.index < 2)
     	return;
 
     uint8_t data_byte, tnc_number;

+ 5 - 0
components/radio/include/radio.h

@@ -10,6 +10,8 @@
 #ifndef RADIO_H_
 #define RADIO_H_
 
+#include "freertos/task.h"
+
 /* Data Structures */
 typedef enum {
 	NOT_CONFIGURED = 0,
@@ -30,6 +32,8 @@ typedef struct {
 	radio_status_t status;
 	void (*rx_cb)(uint8_t *, uint32_t);
 	void (*tx_cb)(void);
+	TaskHandle_t tx_task;
+	TaskHandle_t rx_task;
 } radio_param_t;
 
 // holds all the settings for AX25
@@ -50,6 +54,7 @@ typedef struct {
 
 /* Public Functions */
 void radio_init(radio_config_t type, void *settings);
+void radio_reinit(radio_config_t type, void *settings);
 void radio_set_frequency(uint32_t);
 void radio_set_power(int8_t);
 uint8_t radio_get_cs(void);

+ 93 - 4
components/radio/radio.c

@@ -38,8 +38,11 @@ typedef enum {
 } timer_radio_semaphore_t;
 
 /* Local Global Variables */
-static radio_param_t radio_param;				// radio configuraiton
-//static SemaphoreHandle_t xRadioSemaphore;		// semphore for sample timing
+static radio_param_t radio_param = { .type = NOT_CONFIGURED,
+									 .status = RADIO_IDLE,
+									 .rx_cb = NULL,
+									 .tx_cb = NULL };
+
 static SemaphoreHandle_t xRadioTXSemaphore;
 static SemaphoreHandle_t xRadioRXSemaphore;
 static SemaphoreHandle_t xRadioMutex;
@@ -171,6 +174,11 @@ void radio_rx(void)
 	radio_param.status = RADIO_RX;
 }
 
+radio_status_t radio_get_status(void)
+{
+	return radio_param.status;
+}
+
 uint8_t radio_get_cs(void)
 {
 	return ax25_decoder_get_cs();
@@ -205,6 +213,16 @@ void radio_packet_tx(uint8_t *data, int32_t len, void *settings)
 
 }
 
+void radio_set_frequency(uint32_t freq)
+{
+	xSemaphoreTake( xRadioMutex, portMAX_DELAY );	// lock radio
+
+	cc1200_radio_frequency(freq);
+
+	xSemaphoreGive( xRadioMutex );
+
+}
+
 void radio_tx_task(void *para)
 {
 	// variables for transmission
@@ -420,6 +438,70 @@ void radio_packet_rx(void *settings)
 
 }
 
+void radio_reinit(radio_config_t type, void *settings)
+{
+	// lock radio
+	xSemaphoreTake(xRadioMutex, portMAX_DELAY);
+
+	// disable radio
+	radio_idle();
+
+	// suspend radio tasks
+	if (radio_param.type == AX25)
+	{
+		vTaskSuspend(radio_param.tx_task);
+		vTaskSuspend(radio_param.rx_task);
+	}
+
+	switch(type)
+	{
+		case AX25:
+			ESP_LOGI(RADIO_TAG, "APRS Mode");
+			radio_param.type = AX25;
+
+			// cast setting struct
+			ax25_param_t *p = (ax25_param_t *)settings;
+
+			// setup LUT for AFSK demodulator
+			afsk_mod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
+
+			// setup frequency detectors for AFSK demodulator
+			afsk_demod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
+
+			// setup encoder for ax25
+			ax25_encoder_init(p->tx_delay, p->tx_tail);
+
+			// setup ax25 decoder
+			ax25_decoder_init(p->rx_buf, p->rx_buf_len);
+			radio_param.rx_cb = p->rx_cb;
+			radio_param.tx_cb = p->tx_cb;
+
+			// load AX25_settings without reseting / reloading SPI
+			cc1200_radio_config(AX25_SETTINGS, sizeof(AX25_SETTINGS)/sizeof(cc1200_reg_settings_t));
+
+			// put chip to idle
+			radio_idle();
+
+			// resume radio tasks
+
+			break;
+		default:
+			ESP_LOGE(RADIO_TAG, "invalid configuration");
+			radio_param.type = NOT_CONFIGURED;
+			break;
+	}
+
+	// resume tasks
+	// suspend radio tasks
+	if (radio_param.type == AX25)
+	{
+		vTaskResume(radio_param.tx_task);
+		vTaskResume(radio_param.rx_task);
+	}
+
+	xSemaphoreGive(xRadioMutex);
+}
+
 void radio_init(radio_config_t type, void *settings)
 {
 	// create mutux so SPI transactions will be multi-thread safe
@@ -458,6 +540,13 @@ void radio_init(radio_config_t type, void *settings)
 			// configure CC1200
 			cc1200_radio_init(AX25_SETTINGS, sizeof(AX25_SETTINGS)/sizeof(cc1200_reg_settings_t));
 
+			/*
+			 * Experimential Band - 145.50 - 145.80
+			 * Note: Signal BW = 15 / 25kHz
+			 */
+
+			cc1200_radio_frequency(145560000);	// 145.560MHz
+
 			// put chip to idle
 			radio_idle();
 
@@ -467,8 +556,8 @@ void radio_init(radio_config_t type, void *settings)
 
 			// create task for sampling
 			// TODO: Reduce stack requirements?
-			xTaskCreatePinnedToCore(radio_rx_task, "radio rx", 1024*4, 0, 1, NULL, p->cpu_core);
-			xTaskCreatePinnedToCore(radio_tx_task, "radio tx", 1024*4, 0, 2, NULL, p->cpu_core);
+			xTaskCreatePinnedToCore(radio_rx_task, "radio rx", 1024*4, 0, 1, &radio_param.tx_task, p->cpu_core);
+			xTaskCreatePinnedToCore(radio_tx_task, "radio tx", 1024*4, 0, 2, &radio_param.rx_task, p->cpu_core);
 			//vTaskSuspend(radio_rx_task);
 			break;
 		}

+ 105 - 29
main/main.c

@@ -4,15 +4,13 @@
  * 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 <stdio.h>
+#include <stddef.h>
 
 /* ESP-IDF */
 #include "sdkconfig.h"
@@ -34,6 +32,12 @@
 
 
 /* Data Structures */
+typedef enum {
+	TNC_TX=0,
+	TNC_RX,
+	TNC_IDLE
+} tnc_status_t;
+
 typedef struct {
 	uint8_t tnc_number;
 	uint8_t tx_delay;
@@ -41,15 +45,38 @@ typedef struct {
 	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 freq_table[16] = { 144390000, 		// APRS - North America
+						    145512500,		// Arrow Test Freqs...
+						    145537500,
+						    145562500,
+						    145587500,
+						    145612500,
+						    145637500,
+						    145662500,
+						    145687500,
+						    145712500,
+						    145737500,
+						    145762500,
+						    145787500,
+						    145812500,
+						    145837500,		// ...145.5 to 145.8 is experimential band
+						    145862500 };	// 25kHz spacing
+
 /* Radio Callback Functions */
 
 void radio_rx_ax25_cb(uint8_t *frame, uint32_t len)
@@ -75,10 +102,20 @@ void kiss_tx_cb(uint8_t *frame, uint32_t len)
 	ESP_LOGI("tnc", "kiss packet sent");
 }
 
+void print_tnc_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_rx_cb(uint8_t *frame, uint32_t len)
 {
 	uint8_t data_byte;
-	data_byte = frame[0] & 0x0F;	// tnc_number information
+	data_byte = frame[0] & 0x0F;	// remove tnc_number information
 
 	// process dataframe
 	if (data_byte == KISS_DATAFRAME)
@@ -97,44 +134,73 @@ void kiss_rx_cb(uint8_t *frame, uint32_t len)
 	}
 	else // assume command
 	{
+		ESP_LOGI("tnc", "received tnc 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");
+			{
+				uint8_t freq_index = frame[1] & 0x0F;
+				tnc_settings.freq = freq_table[freq_index];
 				break;
+			}
 			default:
 				ESP_LOGE("tnc", "unknown data byte for KISS protocol");
 		}
 
-		// reload for parameter changes to take effect
+		// 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
+		}
+
+		print_tnc_settings();
+
+		// disable TNC task
+		vTaskSuspend(tnc_settings.tnc_task);
+		ESP_LOGW("tnc", "disabled tnc");
+
+		// re-initalize 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;
@@ -142,6 +208,7 @@ void tnc_task(void *para)
 	uint8_t P;
 
 	radio_packet_rx(NULL);
+	tnc_settings.status = TNC_RX;
 
 	while(1)
 	{
@@ -173,7 +240,7 @@ void tnc_task(void *para)
 					else
 					{
 						ESP_LOGV("radio", "transmission postponed");
-						vTaskDelay(tnc_settings.slot_time * 10 / portTICK_PERIOD_MS);
+						vTaskDelay((tnc_settings.slot_time * 10) / portTICK_PERIOD_MS);
 					}
 				}
 				else
@@ -192,10 +259,12 @@ void tnc_task(void *para)
 			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);
@@ -208,6 +277,7 @@ void tnc_task(void *para)
 		}
 		// setup again for reception
 		radio_packet_rx(NULL);
+		tnc_settings.status = TNC_RX;
 	}
 }
 
@@ -228,14 +298,22 @@ void IRAM_ATTR app_main()
 	// Board IO Initialize
 	board_init();
 
-	// Load Settings from Flash
-
+	// 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 = 10;
-	ax25_param.tx_delay = 100;
+	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;
@@ -245,21 +323,19 @@ void IRAM_ATTR app_main()
 	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 simplify....
+	print_tnc_settings();
 	radio_init(AX25, &ax25_param);
 
+	radio_set_frequency(tnc_settings.freq);
+
 	// Kiss Decoder and Encoder
-	kiss_init(0, kiss_tx_cb, kiss_rx_cb);
+	kiss_init(tnc_settings.tnc_number, 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);
+	xTaskCreatePinnedToCore(tnc_task, "tnc task", 1024*4, 0, 2, &tnc_settings.tnc_task, 1);
 
 }