diff --git a/decoder/html/Image.class.php b/decoder/html/Image.class.php
index d74fc0e9..36a05f10 100644
--- a/decoder/html/Image.class.php
+++ b/decoder/html/Image.class.php
@@ -3,15 +3,15 @@ class Image {
function __construct($sqlResult) {
- $this->id = $sqlResult['id'];
+ $this->id = (int)$sqlResult['id'];
$this->call = $sqlResult['call'];
- $this->time_first = $sqlResult['time_first'];
- $this->time_last = $sqlResult['time_last'];
+ $this->time_first = (int)$sqlResult['time_first'];
+ $this->time_last = (int)$sqlResult['time_last'];
- $this->imageID = $sqlResult['imageID'];
- $this->packetID = $sqlResult['packetID'];
- $this->count = $sqlResult['count'];
+ $this->imageID = (int)$sqlResult['imageID'];
+ $this->packetID = (int)$sqlResult['packetID'];
+ $this->count = (int)$sqlResult['count'];
}
}
diff --git a/decoder/html/Tracker.class.php b/decoder/html/Tracker.class.php
index 0d4cb189..63611706 100644
--- a/decoder/html/Tracker.class.php
+++ b/decoder/html/Tracker.class.php
@@ -16,14 +16,12 @@ class Tracker {
$query = Database::getInstance()->query("
SELECT * FROM (
- SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'pos' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'pos'
+ (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'pos' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'pos' ORDER BY `rxtime` DESC LIMIT 1)
UNION ALL
- SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'img' as `type` FROM `image` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "'
+ (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'img' as `type` FROM `image` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' ORDER BY `rxtime` DESC LIMIT 1)
UNION ALL
- SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'log' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'log'
+ (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'log' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'log' ORDER BY `rxtime` DESC LIMIT 1)
) AS d
- GROUP BY `type`
- ORDER BY `lasttime` DESC
");
while($row = $query->fetch_assoc())
@@ -38,8 +36,8 @@ class Tracker {
if($from > $to)
return array(); // Error $from is larger than $to
- if($from - $to > 64281600)
- $from = $from + 64281600; // Max. 744 days (2 non leap years + 14 weeks)
+ if($to - $from > 64281600)
+ $from = $to - 64281600; // Max. 744 days (2 non leap years + 14 weeks)
$query = Database::getInstance()->query("
SELECT t.`id`,`call`,MIN(`rxtime`) as `time_first`,MAX(`rxtime`) as `time_last`,
@@ -74,8 +72,8 @@ class Tracker {
if($from > $to)
return array(); // Error $from is larger than $to
- if($from - $to > 64281600)
- $from = $from + 64281600; // Max. 744 days (2 non leap years + 14 weeks)
+ if($to - $from > 64281600)
+ $from = $to - 64281600; // Max. 744 days (2 non leap years + 14 weeks)
$query = Database::getInstance()->query("
SELECT *,
diff --git a/decoder/html/telemetry.php b/decoder/html/telemetry.php
index e98dcd9b..c17e59f6 100644
--- a/decoder/html/telemetry.php
+++ b/decoder/html/telemetry.php
@@ -216,14 +216,29 @@ function loadRecentData() {
dataTemp.addRow([null,null,null,null,null,null]);
dataGPS.addRow([null,null,null,null]);
dataLight.addRow([null,null]);
+ dataAlt.addRow([null,null,null,null,null]);
}
dataBattery.addRow([time, data['adc_vbat'], data['pac_vbat'], data['pac_pbat']/10]);
dataSolar.addRow([time, data['adc_vsol'], data['pac_vsol'], data['pac_psol']/10]);
- dataTemp.addRow([time, data['sen_i1_temp']/100, data['sen_e1_temp']/100, data['sen_e2_temp']/100, data['stm32_temp']/100, data['si4464_temp']/100]);
+ dataTemp.addRow([
+ time,
+ data['sen_i1_temp'] && data['sen_i1_temp'] > -10000 && data['sen_i1_temp'] < 10000 ? data['sen_i1_temp']/100 : null,
+ data['sen_e1_temp'] && data['sen_e1_temp'] > -10000 && data['sen_e1_temp'] < 10000 ? data['sen_e1_temp']/100 : null,
+ data['sen_e2_temp'] && data['sen_e2_temp'] > -10000 && data['sen_e2_temp'] < 10000 ? data['sen_e2_temp']/100 : null,
+ data['stm32_temp'] && data['stm32_temp'] > -10000 && data['stm32_temp'] < 10000 ? data['stm32_temp']/100 : null,
+ data['si4464_temp'] && data['si4464_temp'] > -10000 && data['si4464_temp'] < 10000 ? data['si4464_temp']/100 : null
+ ]);
dataGPS.addRow([time, data['gps_sats'], data['gps_ttff'], data['gps_pdop']/20]);
dataLight.addRow([time, data['light_intensity']]);
+ dataAlt.addRow([
+ time,
+ data['gps_alt'],
+ data['sen_i1_press'] && data['sen_i1_press'] < 1100000 ? data['sen_i1_press']/10 : null,
+ data['sen_e1_press'] && data['sen_e1_press'] < 1100000 ? data['sen_e1_press']/10 : null,
+ data['sen_e2_press'] && data['sen_e2_press'] < 1100000 ? data['sen_e2_press']/10 : null
+ ]);
last = time;
});
@@ -237,6 +252,7 @@ function loadRecentData() {
dataTemp.removeRow(c);
dataGPS.removeRow(c);
dataLight.removeRow(c);
+ dataAlt.removeRow(c);
}
}
@@ -246,6 +262,7 @@ function loadRecentData() {
tempChart.draw(dataTemp, tempOptions);
gpsChart.draw(dataGPS, gpsOptions);
lightChart.draw(dataLight, lightOptions);
+ altChart.draw(dataAlt, altOptions);
lastChartUpdate = json['time'];
}
@@ -335,6 +352,30 @@ var gpsOptions = {
};
// Chart 5
+var altChart;
+var dataAlt;
+var altOptions = {
+ explorer: scroll,
+ width: 1285,
+ height: 300,
+ series: {
+ 0: {targetAxisIndex: 0},
+ 1: {targetAxisIndex: 1},
+ 2: {targetAxisIndex: 1},
+ 3: {targetAxisIndex: 1}
+ },
+ vAxes: {
+ 0: {title: 'Altitude'},
+ 1: {title: 'Airpressure'},
+ },
+ legend: {
+ position: 'top'
+ },
+ hAxis: xAxis,
+ chartArea: area
+};
+
+// Chart 6
var lightChart;
var dataLight;
var lightOptions = {
@@ -390,6 +431,15 @@ function drawChart() {
gpsChart = new google.visualization.LineChart(document.getElementById('gpsDiv'));
// Chart 5
+ dataAlt = new google.visualization.DataTable();
+ dataAlt.addColumn('date', 'Time');
+ dataAlt.addColumn('number', "GPS_ALT");
+ dataAlt.addColumn('number', "PRESS_BME_I1");
+ dataAlt.addColumn('number', "PRESS_BME_E1");
+ dataAlt.addColumn('number', "PRESS_BME_E2");
+ altChart = new google.visualization.LineChart(document.getElementById('altDiv'));
+
+ // Chart 6
dataLight = new google.visualization.DataTable();
dataLight.addColumn('date', 'Time');
dataLight.addColumn('number', "LIGHT");
@@ -448,6 +498,14 @@ include "sidebar.inc.php";
|
|
+
+ |
+ DIR: |
+ |
+ |
+ |
+ |
+
|
IMG: |
@@ -600,6 +658,9 @@ include "sidebar.inc.php";
|
+
+ |
+
|
diff --git a/tracker/hardware/10b/.gitignore b/tracker/hardware/10b/.gitignore
new file mode 100644
index 00000000..46f586da
--- /dev/null
+++ b/tracker/hardware/10b/.gitignore
@@ -0,0 +1 @@
+/pecanpico10-rescue.lib
diff --git a/tracker/software/.settings/language.settings.xml b/tracker/software/.settings/language.settings.xml
index 232008d6..8fed0175 100644
--- a/tracker/software/.settings/language.settings.xml
+++ b/tracker/software/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/tracker/software/ChibiOS/os/various/shell/shell.c b/tracker/software/ChibiOS/os/various/shell/shell.c
index 418b1300..9b382115 100644
--- a/tracker/software/ChibiOS/os/various/shell/shell.c
+++ b/tracker/software/ChibiOS/os/various/shell/shell.c
@@ -354,7 +354,7 @@ THD_FUNCTION(shellThread, p) {
chprintf(chp, SHELL_NEWLINE_STR);
chprintf(chp, "ChibiOS/RT Shell" SHELL_NEWLINE_STR);
- while (true) {
+ while (!chThdShouldTerminateX()) {
chprintf(chp, SHELL_PROMPT_STR);
if (shellGetLine(scfg, line, sizeof(line), shp)) {
#if (SHELL_CMD_EXIT_ENABLED == TRUE) && !defined(_CHIBIOS_NIL_)
diff --git a/tracker/software/Makefile b/tracker/software/Makefile
index 5e51bcdd..802b2e3e 100644
--- a/tracker/software/Makefile
+++ b/tracker/software/Makefile
@@ -253,7 +253,7 @@ CPPWARN = -Wall -Wextra -Wundef
# List all user C define here, like -D_DEBUG=1
UDEFS = -D_GNU_SOURCE -DARM_MATH_CM4 -DSHELL_CMD_TEST_ENABLED=0 \
- -DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=4 \
+ -DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=5 \
-DSHELL_CMD_MEM_ENABLED=0
# -DDISABLE_HW_WATCHDOG=1
diff --git a/tracker/software/board/board.h b/tracker/software/board/board.h
index 7db0621c..be41256c 100644
--- a/tracker/software/board/board.h
+++ b/tracker/software/board/board.h
@@ -297,7 +297,7 @@
// USB
#define LINE_USB_ID PAL_LINE(GPIOA, 10U)
-#define LINE_USB_VUSB PAL_LINE(GPIOA, 9U)
+#define LINE_USB_VUSB PAL_LINE(GPIOA, 9U)
#define LINE_USB_DM PAL_LINE(GPIOA, 11U)
#define LINE_USB_DP PAL_LINE(GPIOA, 12U)
diff --git a/tracker/software/config.c b/tracker/software/config.c
index b59d241c..8e34cecf 100644
--- a/tracker/software/config.c
+++ b/tracker/software/config.c
@@ -8,12 +8,12 @@
conf_t conf_sram;
const conf_t conf_flash_default = {
- // Primary position transmission thread
+ // Primary position node
.pos_pri = {
.thread_conf = {
- .active = true,
+ .active = false,
.cycle = TIME_S2I(60*30),
- .init_delay = TIME_S2I(10)
+ .init_delay = TIME_S2I(30)
},
.radio_conf = {
.pwr = 0x7F,
@@ -21,16 +21,16 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x4F,
},
-
+ // Node identity
.call = "VK2GJ-12",
.path = "WIDE2-1",
- .symbol = SYM_DIGIPEATER,
+ .symbol = SYM_ANTENNA,
.aprs_msg = true,
.tel_enc_cycle = TIME_S2I(10800)
},
- // Secondary position transmission thread
+ // Secondary position node
.pos_sec = {
.thread_conf = {
.active = false,
@@ -43,7 +43,7 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x4F
},
-
+ // Node identity
.call = "DL7AD-14",
.path = "WIDE1-1",
.symbol = SYM_BALLOON,
@@ -52,13 +52,13 @@ const conf_t conf_flash_default = {
.tel_enc_cycle = TIME_S2I(10800)
},
- // Primary image transmission thread
+ // Primary image node
.img_pri = {
.thread_conf = {
- .active = true,
- .cycle = TIME_S2I(60*30),
- .init_delay = TIME_S2I(60*1),
- .send_spacing = TIME_S2I(10)
+ .active = false,
+ .cycle = TIME_S2I(60*10),
+ .init_delay = TIME_S2I(60*5),
+ .send_spacing = TIME_S2I(5)
},
.radio_conf = {
.pwr = 0x7F,
@@ -67,22 +67,22 @@ const conf_t conf_flash_default = {
.rssi = 0x4F,
.redundantTx = false
},
-
+ // Node identity
.call = "VK2GJ-15",
.path = "",
.res = RES_VGA,
.quality = 4,
- .buf_size = 64*1024
+ .buf_size = 40*1024
},
- // Secondary image transmission thread
+ // Secondary image node
.img_sec = {
.thread_conf = {
.active = false,
- .cycle = TIME_S2I(600),
- .init_delay = TIME_S2I(20),
- .send_spacing = TIME_MS2I(100)
+ .cycle = TIME_S2I(60*5),
+ .init_delay = TIME_S2I(60*1),
+ .send_spacing = TIME_S2I(30)
},
.radio_conf = {
.pwr = 0x7F,
@@ -90,16 +90,16 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x4F
},
-
- .call = "VK2GJ-15",
+ // Node identity
+ .call = "VK2GJ-14",
.path = "",
- .res = RES_VGA,
+ .res = RES_QVGA,
.quality = 4,
- .buf_size = 64*1024
+ .buf_size = 15*1024
},
- // Log transmission thread
+ // Log node
.log = {
.thread_conf = {
.active = false,
@@ -112,13 +112,13 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x4F
},
-
+ // Node identity
.call = "VK2GJ-13",
.path = "WIDE1-1",
.density = 10
},
- // APRS system control
+ // APRS node
.aprs = {
.thread_conf = {
.active = true,
@@ -130,6 +130,7 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x3F
},
+ // Node rx identity
.call = "VK2GJ-4"
},
.tx = { // The transmit identity for digipeat transmit and messages responses
@@ -139,11 +140,17 @@ const conf_t conf_flash_default = {
.mod = MOD_AFSK,
.rssi = 0x4F
},
+ // Node tx identity
.call = "VK2GJ-5",
.path = "WIDE2-1",
- .symbol = SYM_DIGIPEATER
+ .symbol = SYM_DIGIPEATER,
+ .fixed = true,
+ .lat = -337331175,
+ .lon = 1511143478,
+ .alt = 144
},
- .base = { // The base station parameters - how and where tracker originated messages are sent
+ .base = {
+ // The base station identity - how and where tracker originated messages are sent
.enabled = true,
.call = "VK2GJ-7",
.path = "WIDE2-1",
diff --git a/tracker/software/config.h b/tracker/software/config.h
index 22660d11..e266727b 100644
--- a/tracker/software/config.h
+++ b/tracker/software/config.h
@@ -8,7 +8,7 @@
* 3V, because USB would not work at 1.8V. Note that the transmission power is increased
* too when operating at 3V. This option will also run the STM32 at 48MHz (AHB) permanently
* because USB needs that speed, otherwise it is running at 6MHz which saves a lot of power. */
-#define ENABLE_EXTERNAL_I2C TRUE /* The external port can be used for bit bang I2C. */
+#define ENABLE_EXTERNAL_I2C FALSE /* The external port can be used for bit bang I2C. */
#include "types.h"
@@ -18,5 +18,4 @@
extern conf_t conf_sram;
extern const conf_t conf_flash_default;
-#endif
-
+#endif /* __CONFIG_H__ */
diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c
index 93d32b7d..7436f232 100644
--- a/tracker/software/drivers/ov5640.c
+++ b/tracker/software/drivers/ov5640.c
@@ -978,24 +978,26 @@ void vsync_cb(void *arg) {
* Other drivers using resources that can cause DMA competition are locked.
*/
msg_t OV5640_LockResourcesForCapture(void) {
- I2C_Lock();
msg_t msg = pktAcquireRadio(PKT_RADIO_1, TIME_INFINITE);
if(msg != MSG_OK) {
return msg;
}
+ I2C_Lock();
pktPauseReception(PKT_RADIO_1);
- //chMtxLock(&trace_mtx);
+ /* Hold TRACE output on USB. */
+ if(isUSBactive())
+ chMtxLock(&trace_mtx);
return MSG_OK;
- /* FIXME: USB has to be locked? */
}
/*
* Unlock competing drivers.
*/
void OV5640_UnlockResourcesForCapture(void) {
- /* FIXME: USB has to be unlocked? */
- //chMtxUnlock(&trace_mtx);
+ /* Re-enable TRACE output on USB. */
+ if(isUSBactive())
+ chMtxUnlock(&trace_mtx);
I2C_Unlock();
pktResumeReception(PKT_RADIO_1);
pktReleaseRadio(PKT_RADIO_1);
diff --git a/tracker/software/drivers/ublox.c b/tracker/software/drivers/ublox.c
index 4ec672d3..53b14d2c 100644
--- a/tracker/software/drivers/ublox.c
+++ b/tracker/software/drivers/ublox.c
@@ -26,21 +26,20 @@ const SerialConfig gps_config =
/**
* Transmits a string of bytes to the GPS
*/
-void gps_transmit_string(uint8_t *cmd, uint8_t length)
-{
- #if defined(UBLOX_USE_I2C)
- I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length);
- #elif defined(UBLOX_USE_UART)
- sdWrite(&SD5, cmd, length);
- #endif
+void gps_transmit_string(uint8_t *cmd, uint8_t length) {
+ gps_calc_ubx_csum(cmd, length);
+#if defined(UBLOX_USE_I2C)
+ I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length);
+#elif defined(UBLOX_USE_UART)
+ sdWrite(&SD5, cmd, length);
+#endif
}
/**
* Receives a single byte from the GPS and assigns to supplied pointer.
* Returns false is there is no byte available else true
*/
-bool gps_receive_byte(uint8_t *data)
-{
+bool gps_receive_byte(uint8_t *data) {
#if defined(UBLOX_USE_I2C)
uint16_t len;
I2C_read16(UBLOX_MAX_ADDRESS, 0xFD, &len);
@@ -190,7 +189,7 @@ bool gps_get_fix(gpsFix_t *fix) {
static uint8_t navstatus[32];
// Transmit request
- uint8_t navpvt_req[] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x08, 0x19};
+ uint8_t navpvt_req[] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00};
gps_transmit_string(navpvt_req, sizeof(navpvt_req));
if(!gps_receive_payload(0x01, 0x07, navpvt, 3000)) { // Receive request
@@ -198,7 +197,7 @@ bool gps_get_fix(gpsFix_t *fix) {
return false;
}
- uint8_t navstatus_req[] = {0xB5, 0x62, 0x01, 0x03, 0x00, 0x00, 0x04, 0x0D};
+ uint8_t navstatus_req[] = {0xB5, 0x62, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00};
gps_transmit_string(navstatus_req, sizeof(navstatus_req));
if(!gps_receive_payload(0x01, 0x03, navstatus, 3000)) { // Receive request
@@ -250,13 +249,22 @@ bool gps_get_fix(gpsFix_t *fix) {
fix->time.second = navpvt[10];
fix->lat = (int32_t) (
- (uint32_t)(navpvt[28]) + ((uint32_t)(navpvt[29]) << 8) + ((uint32_t)(navpvt[30]) << 16) + ((uint32_t)(navpvt[31]) << 24)
+ (uint32_t)(navpvt[28])
+ + ((uint32_t)(navpvt[29]) << 8)
+ + ((uint32_t)(navpvt[30]) << 16)
+ + ((uint32_t)(navpvt[31]) << 24)
);
fix->lon = (int32_t) (
- (uint32_t)(navpvt[24]) + ((uint32_t)(navpvt[25]) << 8) + ((uint32_t)(navpvt[26]) << 16) + ((uint32_t)(navpvt[27]) << 24)
+ (uint32_t)(navpvt[24])
+ + ((uint32_t)(navpvt[25]) << 8)
+ + ((uint32_t)(navpvt[26]) << 16)
+ + ((uint32_t)(navpvt[27]) << 24)
);
int32_t alt_tmp = (((int32_t)
- ((uint32_t)(navpvt[36]) + ((uint32_t)(navpvt[37]) << 8) + ((uint32_t)(navpvt[38]) << 16) + ((uint32_t)(navpvt[39]) << 24))
+ ((uint32_t)(navpvt[36])
+ + ((uint32_t)(navpvt[37]) << 8)
+ + ((uint32_t)(navpvt[38]) << 16)
+ + ((uint32_t)(navpvt[39]) << 24))
) / 1000);
if (alt_tmp <= 0) {
fix->alt = 1;
@@ -298,7 +306,6 @@ uint8_t gps_disable_nmea_output(void) {
0x00, 0x00 // CRC place holders
};
- gps_calc_ubx_csum(nonmea, sizeof(nonmea));
gps_transmit_string(nonmea, sizeof(nonmea));
return gps_receive_ack(0x06, 0x00, 1000);
}
@@ -338,7 +345,6 @@ uint8_t gps_set_airborne_model(void) {
0x00, 0x00 // CRC place holders
};
- gps_calc_ubx_csum(model6, sizeof(model6));
gps_transmit_string(model6, sizeof(model6));
return gps_receive_ack(0x06, 0x24, 1000);
}
@@ -369,7 +375,6 @@ uint8_t gps_set_power_save(void) {
0x00, 0x00 // CRC place holders
};
- gps_calc_ubx_csum(powersave, sizeof(powersave));
gps_transmit_string(powersave, sizeof(powersave));
return gps_receive_ack(0x06, 0x3B, 1000);
}
@@ -386,7 +391,6 @@ uint8_t gps_power_save(int on) {
0x00, 0x00 // CRC place holders
};
- gps_calc_ubx_csum(recvmgmt, sizeof(recvmgmt));
gps_transmit_string(recvmgmt, sizeof(recvmgmt));
return gps_receive_ack(0x06, 0x11, 1000);
}
@@ -448,14 +452,15 @@ void GPS_Deinit(void)
/*
* Calculate checksum and inserts into buffer.
+ * Calling function must allocate space in message buff for csum.
*
*/
bool gps_calc_ubx_csum(uint8_t *mbuf, uint16_t mlen) {
uint16_t i;
uint8_t ck_a = 0, ck_b = 0;
+ /* Counting sync bytes there must be at least one byte to checksum. */
if(mlen < 5)
- /* Counting sync bytes there must be at least one byte to checksum. */
return false;
for (i = 2; i < mlen - 2; i++) {
diff --git a/tracker/software/drivers/usb/commands.c b/tracker/software/drivers/usb/commands.c
index c91a3b81..7e91e433 100644
--- a/tracker/software/drivers/usb/commands.c
+++ b/tracker/software/drivers/usb/commands.c
@@ -241,10 +241,10 @@ void usb_cmd_send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[]
chprintf(chp, "Message: %s\r\n", m);
- /* Send with ack request (last arg false). */
+ /* Send with ack request (last arg true). */
packet_t packet = aprs_encode_message(conf_sram.aprs.tx.call,
conf_sram.aprs.tx.path,
- argv[0], m, false);
+ argv[0], m, true);
if(packet == NULL) {
TRACE_WARN("CMD > No free packet objects");
return;
@@ -258,9 +258,5 @@ void usb_cmd_send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[]
conf_sram.aprs.tx.radio_conf.rssi);
chprintf(chp, "Message sent!\r\n");
-/*
- (void)argc;
- (void)argv;
- chprintf(chp, "TODO: Not implemented\r\n");*/
}
diff --git a/tracker/software/drivers/usb/usb.c b/tracker/software/drivers/usb/usb.c
index 4cf206f5..8a269b45 100644
--- a/tracker/software/drivers/usb/usb.c
+++ b/tracker/software/drivers/usb/usb.c
@@ -6,60 +6,137 @@
#include "pktconf.h"
static thread_t *shelltp;
-static bool usb_initialized;
+sdu_term_t sdu_chn_state;
-event_listener_t shell_el;
+event_listener_t sdu1_el;
static const ShellConfig shell_cfg = {
(BaseSequentialStream*)&SDU1,
commands
};
+/*
+ *
+ */
void startUSB(void) {
- if(usb_initialized)
- return; // Avoid duplicate initialization
+ usbObjectInit(&USBD1);
- /* Initialize USB. */
- sduObjectInit(&SDU1);
+ usbStart(&USBD1, &usbcfg);
/* Currently does nothing. */
- usbDisconnectBus(serusbcfg.usbp);
+ usbDisconnectBus(&USBD1);
+
chThdSleep(TIME_MS2I(100));
- usbStart(serusbcfg.usbp, &usbcfg);
-
/* Currently does nothing. */
- usbConnectBus(serusbcfg.usbp);
+ usbConnectBus(&USBD1);
- sduStart(&SDU1, &serusbcfg);
-
- // Initialize shell
- shelltp = NULL;
- shellInit();
-
- usb_initialized = true;
+ sdu_chn_state = TERM_SDU_INIT;
}
-void manageShell(void) {
- if(shelltp == NULL && isUSBactive()) {
-
- shelltp = chThdCreateFromHeap(NULL,
- THD_WORKING_AREA_SIZE(4*1024),
- "shell", NORMALPRIO + 1,
- shellThread,
- (void*)&shell_cfg);
-
-
- chEvtRegister(&shell_terminated, &shell_el, USB_SHELL_EVT);
- }
- chEvtWaitAnyTimeout(EVENT_MASK(USB_SHELL_EVT), TIME_S2I(1));
- if(chThdTerminatedX(shelltp)) {
- chThdWait(shelltp);
- shelltp = NULL;
- chEvtUnregister(&shell_terminated, &shell_el);
- }
+/*
+ *
+ */
+void startSDU(void) {
+ if(sdu_chn_state != TERM_SDU_INIT)
+ return;
+ sduObjectInit(&SDU1);
+ chEvtRegister(chnGetEventSource(&SDU1), &sdu1_el, USB_SDU1_EVT);
+ sduStart(&SDU1, &serusbcfg);
+ sdu_chn_state = TERM_SDU_IDLE;
}
+/**
+ * @brief Manage trace output and shell on Serial Over USB.
+ * @notes TRACE output is sent to USB serial.
+ * @notes TRACE output is suspended when any key is pressed on terminal.
+ * @notes A new shell is invoked and remains active until logout.
+ * @notes TRACE output is then resotored.
+ *
+ * @api
+ */
+void manageTraceAndShell(void) {
+
+ if(chEvtGetAndClearEvents(EVENT_MASK(USB_SDU1_EVT)) == 0)
+ return;
+
+ BaseSequentialStream *chp = (BaseSequentialStream *)&SDU1;
+
+ eventflags_t evtf = chEvtGetAndClearFlags(&sdu1_el);
+
+ switch(sdu_chn_state) {
+ case TERM_SDU_INIT:
+ return;
+
+ case TERM_SDU_IDLE: {
+ if(evtf == 0)
+ return;
+ if(evtf & CHN_CONNECTED) {
+ sdu_chn_state = TERM_SDU_OUT;
+ chprintf(chp, "\r\n*** Trace output enabled ***\r\n");
+ break;
+ }
+ break;
+ } /* End case TERM_SDU_IDLE */
+
+ case TERM_SDU_OUT: {
+ if(evtf & CHN_DISCONNECTED) {
+ sdu_chn_state = TERM_SDU_IDLE;
+ return;
+ }
+ if(evtf & CHN_INPUT_AVAILABLE) {
+ /* Flush the input queue. */
+ while(chnGetTimeout((SerialUSBDriver *)chp, TIME_MS2I(100)) != STM_TIMEOUT);
+ chprintf(chp, "\r\n*** Trace suspended - type ^D or use the "
+ "'exit' command to resume trace ***\r\n");
+ shellInit();
+ shelltp = chThdCreateFromHeap(NULL,
+ THD_WORKING_AREA_SIZE(4*1024),
+ "shell", NORMALPRIO + 1,
+ shellThread,
+ (void*)&shell_cfg);
+ if(shelltp == NULL) {
+ chprintf(chp, "\r\n*** Failed to open shell ***\r\n");
+ break;
+ }
+ sdu_chn_state = TERM_SDU_SHELL;
+ }
+ break;
+ } /* End case TERM_SDU_OUT */
+
+ case TERM_SDU_SHELL: {
+ /* USB disconnect. */
+ if(evtf & CHN_DISCONNECTED) {
+ chThdTerminate(shelltp);
+ sdu_chn_state = TERM_SDU_EXIT;
+ break;
+ }
+ /* Was shell terminated from CLI? */
+ if(chThdTerminatedX(shelltp)) {
+ chThdWait(shelltp);
+ shelltp = NULL;
+ sdu_chn_state = TERM_SDU_OUT;
+ chprintf(chp, "\r\n*** Trace resumed by user ***\r\n");
+ }
+ break;
+ } /* End case TERM_SDU_SHELL */
+
+ case TERM_SDU_EXIT: {
+ chThdWait(shelltp);
+ shelltp = NULL;
+ sdu_chn_state = TERM_SDU_IDLE;
+ break;
+ } /* End case TERM_SDU_EXIT */
+
+ default:
+ break;
+ } /* End switch. */
+}
+
+/*
+ *
+ */
bool isSDUAvailable(void) {
- return usb_initialized;
+ /* Return channel connection status of SDU. */
+ return (bool)(sdu_chn_state == TERM_SDU_OUT);
}
diff --git a/tracker/software/drivers/usb/usb.h b/tracker/software/drivers/usb/usb.h
index 52f1c183..194d0194 100644
--- a/tracker/software/drivers/usb/usb.h
+++ b/tracker/software/drivers/usb/usb.h
@@ -4,10 +4,19 @@
#include "ch.h"
#include "hal.h"
+typedef enum sduTermStates {
+ TERM_SDU_INIT = 0,
+ TERM_SDU_IDLE,
+ TERM_SDU_OUT,
+ TERM_SDU_SHELL,
+ TERM_SDU_EXIT
+} sdu_term_t;
+
#define isUSBactive() (SDU1.config->usbp->state == USB_ACTIVE)
void startUSB(void);
-void manageShell(void);
+void startSDU(void);
+void manageTraceAndShell(void);
bool isSDUAvailable(void);
#endif
diff --git a/tracker/software/drivers/usb/usbcfg.c b/tracker/software/drivers/usb/usbcfg.c
index f2f2d237..0ebf18de 100644
--- a/tracker/software/drivers/usb/usbcfg.c
+++ b/tracker/software/drivers/usb/usbcfg.c
@@ -1,344 +1,344 @@
-/*
- ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#include "hal.h"
-#include "config.h"
-
-/* Virtual serial port over USB.*/
-SerialUSBDriver SDU1;
-
-/*
- * Endpoints to be used for USBD1.
- */
-#define USBD1_DATA_REQUEST_EP 1
-#define USBD1_DATA_AVAILABLE_EP 1
-#define USBD1_INTERRUPT_REQUEST_EP 2
-
-/*
- * USB Device Descriptor.
- */
-static const uint8_t vcom_device_descriptor_data[18] = {
- USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */
- 0x02, /* bDeviceClass (CDC). */
- 0x00, /* bDeviceSubClass. */
- 0x00, /* bDeviceProtocol. */
- 0x40, /* bMaxPacketSize. */
- 0x0483, /* idVendor (ST). */
- 0x5740, /* idProduct. */
- 0x0200, /* bcdDevice. */
- 1, /* iManufacturer. */
- 2, /* iProduct. */
- 3, /* iSerialNumber. */
- 1) /* bNumConfigurations. */
-};
-
-/*
- * Device Descriptor wrapper.
- */
-static const USBDescriptor vcom_device_descriptor = {
- sizeof vcom_device_descriptor_data,
- vcom_device_descriptor_data
-};
-
-/* Configuration Descriptor tree for a CDC.*/
-static const uint8_t vcom_configuration_descriptor_data[67] = {
- /* Configuration Descriptor.*/
- USB_DESC_CONFIGURATION(67, /* wTotalLength. */
- 0x02, /* bNumInterfaces. */
- 0x01, /* bConfigurationValue. */
- 0, /* iConfiguration. */
- 0xC0, /* bmAttributes (self powered). */
- 50), /* bMaxPower (100mA). */
- /* Interface Descriptor.*/
- USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */
- 0x00, /* bAlternateSetting. */
- 0x01, /* bNumEndpoints. */
- 0x02, /* bInterfaceClass (Communications
- Interface Class, CDC section
- 4.2). */
- 0x02, /* bInterfaceSubClass (Abstract
- Control Model, CDC section 4.3). */
- 0x01, /* bInterfaceProtocol (AT commands,
- CDC section 4.4). */
- 0), /* iInterface. */
- /* Header Functional Descriptor (CDC section 5.2.3).*/
- USB_DESC_BYTE (5), /* bLength. */
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
- USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header
- Functional Descriptor. */
- USB_DESC_BCD (0x0110), /* bcdCDC. */
- /* Call Management Functional Descriptor. */
- USB_DESC_BYTE (5), /* bFunctionLength. */
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
- USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management
- Functional Descriptor). */
- USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */
- USB_DESC_BYTE (0x01), /* bDataInterface. */
- /* ACM Functional Descriptor.*/
- USB_DESC_BYTE (4), /* bFunctionLength. */
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
- USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract
- Control Management Descriptor). */
- USB_DESC_BYTE (0x02), /* bmCapabilities. */
- /* Union Functional Descriptor.*/
- USB_DESC_BYTE (5), /* bFunctionLength. */
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
- USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union
- Functional Descriptor). */
- USB_DESC_BYTE (0x00), /* bMasterInterface (Communication
- Class Interface). */
- USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class
- Interface). */
- /* Endpoint 2 Descriptor.*/
- USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80,
- 0x03, /* bmAttributes (Interrupt). */
- 0x0008, /* wMaxPacketSize. */
- 0xFF), /* bInterval. */
- /* Interface Descriptor.*/
- USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */
- 0x00, /* bAlternateSetting. */
- 0x02, /* bNumEndpoints. */
- 0x0A, /* bInterfaceClass (Data Class
- Interface, CDC section 4.5). */
- 0x00, /* bInterfaceSubClass (CDC section
- 4.6). */
- 0x00, /* bInterfaceProtocol (CDC section
- 4.7). */
- 0x00), /* iInterface. */
- /* Endpoint 3 Descriptor.*/
- USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/
- 0x02, /* bmAttributes (Bulk). */
- 0x0040, /* wMaxPacketSize. */
- 0x00), /* bInterval. */
- /* Endpoint 1 Descriptor.*/
- USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/
- 0x02, /* bmAttributes (Bulk). */
- 0x0040, /* wMaxPacketSize. */
- 0x00) /* bInterval. */
-};
-
-/*
- * Configuration Descriptor wrapper.
- */
-static const USBDescriptor vcom_configuration_descriptor = {
- sizeof vcom_configuration_descriptor_data,
- vcom_configuration_descriptor_data
-};
-
-/*
- * U.S. English language identifier.
- */
-static const uint8_t vcom_string0[] = {
- USB_DESC_BYTE(4), /* bLength. */
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
- USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */
-};
-
-/*
- * Vendor string.
- */
-static const uint8_t vcom_string1[] = {
- USB_DESC_BYTE(38), /* bLength. */
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
- 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
- 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
- 'c', 0, 's', 0
-};
-
-/*
- * Device Description string.
- */
-static const uint8_t vcom_string2[] = {
- USB_DESC_BYTE(56), /* bLength. */
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
- 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,
- 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,
- 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,
- 'o', 0, 'r', 0, 't', 0
-};
-
-/*
- * Serial Number string.
- */
-static const uint8_t vcom_string3[] = {
- USB_DESC_BYTE(8), /* bLength. */
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
- '0' + CH_KERNEL_MAJOR, 0,
- '0' + CH_KERNEL_MINOR, 0,
- '0' + CH_KERNEL_PATCH, 0
-};
-
-/*
- * Strings wrappers array.
- */
-static const USBDescriptor vcom_strings[] = {
- {sizeof vcom_string0, vcom_string0},
- {sizeof vcom_string1, vcom_string1},
- {sizeof vcom_string2, vcom_string2},
- {sizeof vcom_string3, vcom_string3}
-};
-
-/*
- * Handles the GET_DESCRIPTOR callback. All required descriptors must be
- * handled here.
- */
-static const USBDescriptor *get_descriptor(USBDriver *usbp,
- uint8_t dtype,
- uint8_t dindex,
- uint16_t lang) {
-
- (void)usbp;
- (void)lang;
- switch (dtype) {
- case USB_DESCRIPTOR_DEVICE:
- return &vcom_device_descriptor;
- case USB_DESCRIPTOR_CONFIGURATION:
- return &vcom_configuration_descriptor;
- case USB_DESCRIPTOR_STRING:
- if (dindex < 4)
- return &vcom_strings[dindex];
- }
- return NULL;
-}
-
-/**
- * @brief IN EP1 state.
- */
-static USBInEndpointState ep1instate;
-
-/**
- * @brief OUT EP1 state.
- */
-static USBOutEndpointState ep1outstate;
-
-/**
- * @brief EP1 initialization structure (both IN and OUT).
- */
-static const USBEndpointConfig ep1config = {
- USB_EP_MODE_TYPE_BULK,
- NULL,
- sduDataTransmitted,
- sduDataReceived,
- 0x0040,
- 0x0040,
- &ep1instate,
- &ep1outstate,
- 2,
- NULL
-};
-
-/**
- * @brief IN EP2 state.
- */
-static USBInEndpointState ep2instate;
-
-/**
- * @brief EP2 initialization structure (IN only).
- */
-static const USBEndpointConfig ep2config = {
- USB_EP_MODE_TYPE_INTR,
- NULL,
- sduInterruptTransmitted,
- NULL,
- 0x0010,
- 0x0000,
- &ep2instate,
- NULL,
- 1,
- NULL
-};
-
-/*
- * Handles the USB driver global events.
- */
-static void usb_event(USBDriver *usbp, usbevent_t event) {
- extern SerialUSBDriver SDU1;
-
- switch (event) {
- case USB_EVENT_ADDRESS:
- return;
- case USB_EVENT_CONFIGURED:
- chSysLockFromISR();
-
- /* Enables the endpoints specified into the configuration.
- Note, this callback is invoked from an ISR so I-Class functions
- must be used.*/
- usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);
- usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);
-
- /* Resetting the state of the CDC subsystem.*/
- sduConfigureHookI(&SDU1);
-
- chSysUnlockFromISR();
- return;
- case USB_EVENT_RESET:
- /* Falls into.*/
- case USB_EVENT_UNCONFIGURED:
- /* Falls into.*/
- case USB_EVENT_SUSPEND:
- chSysLockFromISR();
-
- /* Disconnection event on suspend.*/
- sduSuspendHookI(&SDU1);
-
- chSysUnlockFromISR();
- return;
- case USB_EVENT_WAKEUP:
- chSysLockFromISR();
-
- /* Disconnection event on suspend.*/
- sduWakeupHookI(&SDU1);
-
- chSysUnlockFromISR();
- return;
- case USB_EVENT_STALLED:
- return;
- }
- return;
-}
-
-/*
- * Handles the USB driver global events.
- */
-static void sof_handler(USBDriver *usbp) {
-
- (void)usbp;
-
- osalSysLockFromISR();
- sduSOFHookI(&SDU1);
- osalSysUnlockFromISR();
-}
-
-/*
- * USB driver configuration.
- */
-const USBConfig usbcfg = {
- usb_event,
- get_descriptor,
- sduRequestsHook,
- sof_handler
-};
-
-/*
- * Serial over USB driver configuration.
- */
-const SerialUSBConfig serusbcfg = {
- &USBD1,
- USBD1_DATA_REQUEST_EP,
- USBD1_DATA_AVAILABLE_EP,
- USBD1_INTERRUPT_REQUEST_EP
-};
-
+/*
+ ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "hal.h"
+#include "config.h"
+
+/* Virtual serial port over USB.*/
+SerialUSBDriver SDU1;
+
+/*
+ * Endpoints to be used for USBD1.
+ */
+#define USBD1_DATA_REQUEST_EP 1
+#define USBD1_DATA_AVAILABLE_EP 1
+#define USBD1_INTERRUPT_REQUEST_EP 2
+
+/*
+ * USB Device Descriptor.
+ */
+static const uint8_t vcom_device_descriptor_data[18] = {
+ USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */
+ 0x02, /* bDeviceClass (CDC). */
+ 0x00, /* bDeviceSubClass. */
+ 0x00, /* bDeviceProtocol. */
+ 0x40, /* bMaxPacketSize. */
+ 0x0483, /* idVendor (ST). */
+ 0x5740, /* idProduct. */
+ 0x0200, /* bcdDevice. */
+ 1, /* iManufacturer. */
+ 2, /* iProduct. */
+ 3, /* iSerialNumber. */
+ 1) /* bNumConfigurations. */
+};
+
+/*
+ * Device Descriptor wrapper.
+ */
+static const USBDescriptor vcom_device_descriptor = {
+ sizeof vcom_device_descriptor_data,
+ vcom_device_descriptor_data
+};
+
+/* Configuration Descriptor tree for a CDC.*/
+static const uint8_t vcom_configuration_descriptor_data[67] = {
+ /* Configuration Descriptor.*/
+ USB_DESC_CONFIGURATION(67, /* wTotalLength. */
+ 0x02, /* bNumInterfaces. */
+ 0x01, /* bConfigurationValue. */
+ 0, /* iConfiguration. */
+ 0xC0, /* bmAttributes (self powered). */
+ 50), /* bMaxPower (100mA). */
+ /* Interface Descriptor.*/
+ USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */
+ 0x00, /* bAlternateSetting. */
+ 0x01, /* bNumEndpoints. */
+ 0x02, /* bInterfaceClass (Communications
+ Interface Class, CDC section
+ 4.2). */
+ 0x02, /* bInterfaceSubClass (Abstract
+ Control Model, CDC section 4.3). */
+ 0x01, /* bInterfaceProtocol (AT commands,
+ CDC section 4.4). */
+ 0), /* iInterface. */
+ /* Header Functional Descriptor (CDC section 5.2.3).*/
+ USB_DESC_BYTE (5), /* bLength. */
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
+ USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header
+ Functional Descriptor. */
+ USB_DESC_BCD (0x0110), /* bcdCDC. */
+ /* Call Management Functional Descriptor. */
+ USB_DESC_BYTE (5), /* bFunctionLength. */
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
+ USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management
+ Functional Descriptor). */
+ USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */
+ USB_DESC_BYTE (0x01), /* bDataInterface. */
+ /* ACM Functional Descriptor.*/
+ USB_DESC_BYTE (4), /* bFunctionLength. */
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
+ USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract
+ Control Management Descriptor). */
+ USB_DESC_BYTE (0x02), /* bmCapabilities. */
+ /* Union Functional Descriptor.*/
+ USB_DESC_BYTE (5), /* bFunctionLength. */
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */
+ USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union
+ Functional Descriptor). */
+ USB_DESC_BYTE (0x00), /* bMasterInterface (Communication
+ Class Interface). */
+ USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class
+ Interface). */
+ /* Endpoint 2 Descriptor.*/
+ USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80,
+ 0x03, /* bmAttributes (Interrupt). */
+ 0x0008, /* wMaxPacketSize. */
+ 0xFF), /* bInterval. */
+ /* Interface Descriptor.*/
+ USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */
+ 0x00, /* bAlternateSetting. */
+ 0x02, /* bNumEndpoints. */
+ 0x0A, /* bInterfaceClass (Data Class
+ Interface, CDC section 4.5). */
+ 0x00, /* bInterfaceSubClass (CDC section
+ 4.6). */
+ 0x00, /* bInterfaceProtocol (CDC section
+ 4.7). */
+ 0x00), /* iInterface. */
+ /* Endpoint 3 Descriptor.*/
+ USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/
+ 0x02, /* bmAttributes (Bulk). */
+ 0x0040, /* wMaxPacketSize. */
+ 0x00), /* bInterval. */
+ /* Endpoint 1 Descriptor.*/
+ USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/
+ 0x02, /* bmAttributes (Bulk). */
+ 0x0040, /* wMaxPacketSize. */
+ 0x00) /* bInterval. */
+};
+
+/*
+ * Configuration Descriptor wrapper.
+ */
+static const USBDescriptor vcom_configuration_descriptor = {
+ sizeof vcom_configuration_descriptor_data,
+ vcom_configuration_descriptor_data
+};
+
+/*
+ * U.S. English language identifier.
+ */
+static const uint8_t vcom_string0[] = {
+ USB_DESC_BYTE(4), /* bLength. */
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
+ USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */
+};
+
+/*
+ * Vendor string.
+ */
+static const uint8_t vcom_string1[] = {
+ USB_DESC_BYTE(38), /* bLength. */
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
+ 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
+ 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
+ 'c', 0, 's', 0
+};
+
+/*
+ * Device Description string.
+ */
+static const uint8_t vcom_string2[] = {
+ USB_DESC_BYTE(56), /* bLength. */
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
+ 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,
+ 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,
+ 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,
+ 'o', 0, 'r', 0, 't', 0
+};
+
+/*
+ * Serial Number string.
+ */
+static const uint8_t vcom_string3[] = {
+ USB_DESC_BYTE(8), /* bLength. */
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
+ '0' + CH_KERNEL_MAJOR, 0,
+ '0' + CH_KERNEL_MINOR, 0,
+ '0' + CH_KERNEL_PATCH, 0
+};
+
+/*
+ * Strings wrappers array.
+ */
+static const USBDescriptor vcom_strings[] = {
+ {sizeof vcom_string0, vcom_string0},
+ {sizeof vcom_string1, vcom_string1},
+ {sizeof vcom_string2, vcom_string2},
+ {sizeof vcom_string3, vcom_string3}
+};
+
+/*
+ * Handles the GET_DESCRIPTOR callback. All required descriptors must be
+ * handled here.
+ */
+static const USBDescriptor *get_descriptor(USBDriver *usbp,
+ uint8_t dtype,
+ uint8_t dindex,
+ uint16_t lang) {
+
+ (void)usbp;
+ (void)lang;
+ switch (dtype) {
+ case USB_DESCRIPTOR_DEVICE:
+ return &vcom_device_descriptor;
+ case USB_DESCRIPTOR_CONFIGURATION:
+ return &vcom_configuration_descriptor;
+ case USB_DESCRIPTOR_STRING:
+ if (dindex < 4)
+ return &vcom_strings[dindex];
+ }
+ return NULL;
+}
+
+/**
+ * @brief IN EP1 state.
+ */
+static USBInEndpointState ep1instate;
+
+/**
+ * @brief OUT EP1 state.
+ */
+static USBOutEndpointState ep1outstate;
+
+/**
+ * @brief EP1 initialization structure (both IN and OUT).
+ */
+static const USBEndpointConfig ep1config = {
+ USB_EP_MODE_TYPE_BULK,
+ NULL,
+ sduDataTransmitted,
+ sduDataReceived,
+ 0x0040,
+ 0x0040,
+ &ep1instate,
+ &ep1outstate,
+ 2,
+ NULL
+};
+
+/**
+ * @brief IN EP2 state.
+ */
+static USBInEndpointState ep2instate;
+
+/**
+ * @brief EP2 initialization structure (IN only).
+ */
+static const USBEndpointConfig ep2config = {
+ USB_EP_MODE_TYPE_INTR,
+ NULL,
+ sduInterruptTransmitted,
+ NULL,
+ 0x0010,
+ 0x0000,
+ &ep2instate,
+ NULL,
+ 1,
+ NULL
+};
+
+/*
+ * Handles the USB driver global events.
+ */
+static void usb_event(USBDriver *usbp, usbevent_t event) {
+ extern SerialUSBDriver SDU1;
+
+ switch (event) {
+ case USB_EVENT_ADDRESS:
+ return;
+ case USB_EVENT_CONFIGURED:
+ chSysLockFromISR();
+
+ /* Enables the endpoints specified into the configuration.
+ Note, this callback is invoked from an ISR so I-Class functions
+ must be used.*/
+ usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);
+ usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);
+
+ /* Resetting the state of the CDC subsystem.*/
+ sduConfigureHookI(&SDU1);
+
+ chSysUnlockFromISR();
+ return;
+ case USB_EVENT_RESET:
+ /* Falls into.*/
+ case USB_EVENT_UNCONFIGURED:
+ /* Falls into.*/
+ case USB_EVENT_SUSPEND:
+ chSysLockFromISR();
+
+ /* Disconnection event on suspend.*/
+ sduSuspendHookI(&SDU1);
+
+ chSysUnlockFromISR();
+ return;
+ case USB_EVENT_WAKEUP:
+ chSysLockFromISR();
+
+ /* Wake up event.*/
+ sduWakeupHookI(&SDU1);
+
+ chSysUnlockFromISR();
+ return;
+ case USB_EVENT_STALLED:
+ return;
+ }
+ return;
+}
+
+/*
+ * Handles the USB driver global events.
+ */
+static void sof_handler(USBDriver *usbp) {
+
+ (void)usbp;
+
+ osalSysLockFromISR();
+ sduSOFHookI(&SDU1);
+ osalSysUnlockFromISR();
+}
+
+/*
+ * USB driver configuration.
+ */
+const USBConfig usbcfg = {
+ usb_event,
+ get_descriptor,
+ sduRequestsHook,
+ sof_handler
+};
+
+/*
+ * Serial over USB driver configuration.
+ */
+const SerialUSBConfig serusbcfg = {
+ &USBD1,
+ USBD1_DATA_REQUEST_EP,
+ USBD1_DATA_AVAILABLE_EP,
+ USBD1_INTERRUPT_REQUEST_EP
+};
+
diff --git a/tracker/software/main.c b/tracker/software/main.c
index 56a87c79..9f986de3 100644
--- a/tracker/software/main.c
+++ b/tracker/software/main.c
@@ -4,7 +4,6 @@
#include "debug.h"
#include "threads.h"
-#include "padc.h"
/**
* Main routine is starting up system, runs the software watchdog (module monitoring), controls LEDs
@@ -18,17 +17,18 @@ int main(void) {
// Init debugging (Serial debug port, LEDs)
DEBUG_INIT();
- // This won't actually display since USB isn't initialized yet.
- TRACE_INFO("MAIN > Startup");
/*
* Setup buffers in CCM if available.
- * Setup IO device arbitration.
+ * Setup packet primary data.
*/
bool pkt = pktSystemInit();
chDbgAssert(pkt == true, "failed to init packet system");
+ /* Start Serial Over USB. */
+ startSDU();
+
/* Start serial channels if selected. */
pktSerialStart();
@@ -39,24 +39,19 @@ int main(void) {
pktEnableEventTrace();
}
- #if ACTIVATE_USB
- startUSB();
- #endif
+ TRACE_INFO("MAIN > Startup");
// Startup threads
start_essential_threads(); // Startup required modules (tracking manager, watchdog)
start_user_threads(); // Startup optional modules (eg. POSITION, LOG, ...)
while(true) {
- #if ACTIVATE_USB
- if(isUSBactive()) {
- manageShell();
- pktTraceEvents();
- continue;
- }
- #endif /* ACTIVATE_USB */
- /* Wait in a loop if nothing to do. */
- chThdSleep(TIME_S2I(1));
+ #if ACTIVATE_USB
+ manageTraceAndShell();
+ pktTraceEvents();
+ #endif /* ACTIVATE_USB */
+ /* Wait in a loop if nothing to do. */
+ chThdSleep(TIME_MS2I(200));
}
}
diff --git a/tracker/software/pkt/channels/rxafsk.c b/tracker/software/pkt/channels/rxafsk.c
index 6935e067..7b99bc4e 100644
--- a/tracker/software/pkt/channels/rxafsk.c
+++ b/tracker/software/pkt/channels/rxafsk.c
@@ -642,11 +642,11 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
/* Activity LED blink rate scaling variable. */
uint16_t led_count = 0;
-#define DECODER_WAIT_TIME 200U /* 200mS. */
-#define DECODER_IDLE_TIME 2000U /* 2000uS. */
+#define DECODER_WAIT_TIME 100U /* 100mS. */
+//#define DECODER_IDLE_TIME 2000U /* 2000uS. */
#define DECODER_POLL_TIME 10U /* 10mS. */
-#define DECODER_LED_RATE_POLL 100U /* 1000uS. */
-#define DECODER_ACTIVE_TIMEOUT 5U /* 5mS. */
+//#define DECODER_LED_RATE_POLL 100U /* 1000uS. */
+//#define DECODER_ACTIVE_TIMEOUT 5U /* 5mS. */
#define DECODER_SUSPEND_TIME 2000U /* 2000uS. */
#define DECODER_LED_RATE_SUSPEND 250U /* Blink at 250mS during suspend. */
@@ -693,6 +693,7 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
/* Something went wrong if we arrive here. */
chSysHalt("ThdExit");
}
+ /* Toggle decoder LED in wait state. */
pktWriteDecoderLED(PAL_TOGGLE);
continue;
}
@@ -723,12 +724,15 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
TIME_MS2I(DECODER_POLL_TIME));
if(fifo_msg != MSG_OK) {
- if(++led_count >= DECODER_LED_RATE_POLL) {
- /* Toggle decoder LED. */
+/* if(++led_count >= DECODER_LED_RATE_POLL) {
+ Toggle decoder LED.
pktWriteDecoderLED(PAL_TOGGLE);
led_count = 0;
- }
- /* No FIFO object posted so loop again. */
+ }*/
+ /*
+ * No FIFO object posted so loop.
+ * Go back through IDLE and check for STOP event.
+ */
myDriver->decoder_state = DECODER_IDLE;
break;
}
@@ -757,8 +761,11 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
TIME_MS2I(100));
if(myPktBuffer == NULL) {
+ /* Decrease ref count on AX25 FIFO and stop PWM. */
+ chFactoryReleaseObjectsFIFO(pkt_fifo);
pktAddEventFlags(myHandler, EVT_AX25_NO_BUFFER);
- myDriver->active_demod_object->status |= EVT_AX25_NO_BUFFER;
+ myDriver->active_demod_object->status |=
+ EVT_AX25_NO_BUFFER | EVT_PWM_QUEUE_LOCK;
myDriver->decoder_state = DECODER_ERROR;
break;
}
@@ -796,8 +803,9 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
byte_packed_pwm_t data;
size_t n = iqReadTimeout(myQueue, data.bytes,
sizeof(packed_pwm_counts_t),
- TIME_MS2I(DECODER_ACTIVE_TIMEOUT));
- /* TODO: Timeout to be calculated from SYMBOL time x (8?). */
+ chTimeUS2I(833 * 8)
+ /*TIME_MS2I(DECODER_ACTIVE_TIMEOUT)*/);
+ /* Timeout calculated as SYMBOL time x 8. */
if(n == sizeof(packed_pwm_counts_t)) {
array_min_pwm_counts_t radio;
@@ -901,7 +909,13 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
radio_cca_fifo_t *myFIFO = myDriver->active_demod_object;
if(myFIFO != NULL) {
- /* Wait for queue object to be released by PWM. */
+ /*
+ * Wait for queue object to be released by PWM.
+ * Normally this is the case.
+ * If can be a forced release by semaphore reset.
+ * TODO: This may happen if the watchdog system forces reset.
+ * TBD.
+ */
(void)chBSemWait(&myFIFO->sem);
#if USE_HEAP_PWM_BUFFER == TRUE
diff --git a/tracker/software/pkt/channels/rxpwm.c b/tracker/software/pkt/channels/rxpwm.c
index b2f94883..66efd720 100644
--- a/tracker/software/pkt/channels/rxpwm.c
+++ b/tracker/software/pkt/channels/rxpwm.c
@@ -206,7 +206,7 @@ void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt, pwm_code_t reason)
icuDisableNotificationsI(myICU);
if(myDemod->active_radio_object != NULL) {
myDemod->active_radio_object->status |= (EVT_PWM_QUEUE_LOCK | evt);
- pktAddEventFlagsI(myHandler, (EVT_PWM_QUEUE_LOCK | evt));
+ pktAddEventFlagsI(myHandler, evt);
input_queue_t *myQueue = &myDemod->active_radio_object->radio_pwm_queue;
/* End of data flag. */
#if USE_12_BIT_PWM == TRUE
diff --git a/tracker/software/pkt/devices/si446x.c b/tracker/software/pkt/devices/si446x.c
index 8f8935bf..5ac39eca 100644
--- a/tracker/software/pkt/devices/si446x.c
+++ b/tracker/software/pkt/devices/si446x.c
@@ -589,7 +589,7 @@ static bool Si446x_transmit(radio_unit_t radio,
}
/* Try to get clear channel. */
- TRACE_INFO( "SI > Wait maximum of %.1f seconds for clear channel on"
+ TRACE_INFO( "SI > Run CCA for %.1f seconds on"
" %d.%03d MHz",
(float32_t)(TIME_I2MS(cca_timeout) / 1000),
op_freq/1000000, (op_freq%1000000)/1000);
@@ -601,7 +601,7 @@ static bool Si446x_transmit(radio_unit_t radio,
chThdSleep(TIME_MS2I(1));
}
/* Clear channel timing. */
- TRACE_INFO( "SI > CCA time = %d milliseconds",
+ TRACE_INFO( "SI > CCA completed in %d milliseconds",
chTimeI2MS(chVTTimeElapsedSinceX(t0)));
}
diff --git a/tracker/software/pkt/managers/pktservice.c b/tracker/software/pkt/managers/pktservice.c
index fbd46557..0d77c7c6 100644
--- a/tracker/software/pkt/managers/pktservice.c
+++ b/tracker/software/pkt/managers/pktservice.c
@@ -661,7 +661,6 @@ eventflags_t pktDispatchReceivedBuffer(pkt_data_object_t *pkt_buffer) {
/* Increase outstanding callback count. */
handler->cb_count++;
}
-
}
return flags;
}
diff --git a/tracker/software/pkt/managers/pktservice.h b/tracker/software/pkt/managers/pktservice.h
index d848e71b..d9eedfb7 100644
--- a/tracker/software/pkt/managers/pktservice.h
+++ b/tracker/software/pkt/managers/pktservice.h
@@ -21,8 +21,8 @@
#define PKT_SEND_BUFFER_SEM_NAME "pbsem"
-#define PKT_CALLBACK_WA_SIZE 8192
-#define PKT_TERMINATOR_WA_SIZE 1024
+#define PKT_CALLBACK_WA_SIZE (1024 * 10)
+#define PKT_TERMINATOR_WA_SIZE (1024 * 1)
/*===========================================================================*/
/* Module data structures and types. */
@@ -477,6 +477,23 @@ static inline bool pktIsBufferValidAX25Frame(pkt_data_object_t *object) {
&& (frame_size >= PKT_MIN_FRAME));
}
+/**
+ * @brief Gets status of frame.
+ * @note This returns validity (size) and CRC result.
+ * @details This function is called from thread level.
+ *
+ * @param[in] object pointer to a @p objects FIFO.
+ *
+ * @return The operation status.
+ * @retval true if the frame is valid and has good CRC.
+ * @retval false if the frame is valid and has bad CRC.
+ *
+ * @api
+ */
+static inline bool pktGetAX25FrameStatus(pkt_data_object_t *object) {
+ chDbgAssert(object != NULL, "no pointer to packet object buffer");
+ return !(object->status & (EVT_PKT_INVALID_FRAME | EVT_AX25_CRC_ERROR));
+}
/**
* @brief Gets service object associated with radio.
diff --git a/tracker/software/pkt/pktconf.h b/tracker/software/pkt/pktconf.h
index 4016bb1b..bcb0ca5c 100644
--- a/tracker/software/pkt/pktconf.h
+++ b/tracker/software/pkt/pktconf.h
@@ -53,7 +53,7 @@
#define EVT_PRIORITY_BASE 0
/*
- * Decoder global system events.
+ * Decoder global system event masks.
* The packet channel object holds the global events.
* Events are broadcast to any listeners.
*/
@@ -98,7 +98,8 @@
#define EVT_PKT_CBK_MGR_FAIL EVENT_MASK(EVT_PRIORITY_BASE + 31)
-/* Decoder thread events (sent from initiator to decoder). */
+/* Decoder thread event IDs (sent from initiator to decoder). */
+/*TODO: These needs to be values and NOT bit shifted masks. */
#define DEC_COMMAND_START EVENT_MASK(EVT_PRIORITY_BASE + 0)
#define DEC_COMMAND_STOP EVENT_MASK(EVT_PRIORITY_BASE + 1)
@@ -107,8 +108,9 @@
#define DEC_SUSPEND_EXIT EVENT_MASK(EVT_PRIORITY_BASE + 4)
-/* Reserved system thread events (in user threads level). */
-#define USB_SHELL_EVT EVENT_MASK(EVT_PRIORITY_BASE + 0)
+/* Reserved system event broadcast IDs (set mask in user threads level). */
+#define USB_SHELL_EVT EVT_PRIORITY_BASE + 0
+#define USB_SDU1_EVT EVT_PRIORITY_BASE + 16
/* Response thread events (from decoder to initiator). */
#define DEC_OPEN_EXEC EVENT_MASK(EVT_PRIORITY_BASE + 15)
diff --git a/tracker/software/pkt/protocols/aprs2/ax25_pad.c b/tracker/software/pkt/protocols/aprs2/ax25_pad.c
index 6c58d40d..fae22e7e 100644
--- a/tracker/software/pkt/protocols/aprs2/ax25_pad.c
+++ b/tracker/software/pkt/protocols/aprs2/ax25_pad.c
@@ -1180,43 +1180,54 @@ void ax25_remove_addr (packet_t this_p, int n)
*
*------------------------------------------------------------------------------*/
-int ax25_get_num_addr (packet_t this_p)
-{
- //unsigned char *pf;
- int a;
- int addr_bytes;
+int ax25_get_num_addr (packet_t this_p) {
+ int a;
+ //int addr_bytes;
- if(this_p->magic1 != MAGIC || this_p->magic2 != MAGIC) {
- TRACE_ERROR("PKT > Buffer overflow");
- return 0;
- }
+ if(this_p->magic1 != MAGIC || this_p->magic2 != MAGIC) {
+ TRACE_ERROR("PKT > Buffer overflow");
+ return 0;
+ }
-/* Use cached value if already set. */
+ /* Use cached value if already set. */
- if (this_p->num_addr >= 0) {
- return (this_p->num_addr);
- }
+ if (this_p->num_addr >= 0) {
+ return (this_p->num_addr);
+ }
-/* Otherwise, determine the number of addresses. */
+ /*
+ * Otherwise, determine the number of addresses.
+ * Start with assumption of zero.
+ */
- this_p->num_addr = 0; /* Number of addresses extracted. */
-
- addr_bytes = 0;
- for (a = 0; a < this_p->frame_len && addr_bytes == 0; a++) {
- if (this_p->frame_data[a] & SSID_LAST_MASK) {
- addr_bytes = a + 1;
- }
- }
+ this_p->num_addr = 0;
- if (addr_bytes % 7 == 0) {
- int addrs = addr_bytes / 7;
- if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) {
- this_p->num_addr = addrs;
- }
- }
-
- return (this_p->num_addr);
+ /* Check that address characters are valid. */
+
+ for(a = 0;
+ a < this_p->frame_len && a < (AX25_MAX_ADDRS * AX25_ADDR_LEN);
+ a++) {
+ /*
+ * Check the call sign characters with isgraph
+ * Could be more strict and accept upper case alpha & numeric only.
+ */
+ if(a % 7 != 6) {
+ if(isgraph(this_p->frame_data[a] >> 1))
+ continue;
+ }
+ if((this_p->frame_data[a] & SSID_LAST_MASK))
+ break;
+ } /* End for. */
+
+ /* Check if last happened on an address boundary. */
+ if (++a % 7 == 0) {
+ int addrs = a / 7;
+ if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) {
+ this_p->num_addr = addrs;
+ }
+ }
+ return (this_p->num_addr);
}
@@ -1401,7 +1412,7 @@ int ax25_get_ssid (packet_t this_p, int n)
}
if (n >= 0 && n < this_p->num_addr) {
- return ((this_p->frame_data[n*7+6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT);
+ return ((this_p->frame_data[n * AX25_ADDR_LEN + 6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT);
}
else {
TRACE_ERROR ("Internal error: ax25_get_ssid(%d), num_addr=%d", n, this_p->num_addr);
@@ -1437,7 +1448,8 @@ void ax25_set_ssid (packet_t this_p, int n, int ssid)
if (n >= 0 && n < this_p->num_addr) {
- this_p->frame_data[n*7+6] = (this_p->frame_data[n*7+6] & ~ SSID_SSID_MASK) |
+ this_p->frame_data[n * AX25_ADDR_LEN + 6] =
+ (this_p->frame_data[n * AX25_ADDR_LEN + 6] & ~ SSID_SSID_MASK) |
((ssid << SSID_SSID_SHIFT) & SSID_SSID_MASK) ;
}
else {
@@ -1476,7 +1488,7 @@ int ax25_get_h (packet_t this_p, int n)
}
if (n >= 0 && n < this_p->num_addr) {
- return ((this_p->frame_data[n*7+6] & SSID_H_MASK) >> SSID_H_SHIFT);
+ return ((this_p->frame_data[n * AX25_ADDR_LEN + 6] & SSID_H_MASK) >> SSID_H_SHIFT);
}
else {
TRACE_ERROR ("PKT > Internal error: ax25_get_h(%d), num_addr=%d", n, this_p->num_addr);
@@ -1511,7 +1523,7 @@ void ax25_set_h (packet_t this_p, int n)
}
if (n >= 0 && n < this_p->num_addr) {
- this_p->frame_data[n*7+6] |= SSID_H_MASK;
+ this_p->frame_data[n * AX25_ADDR_LEN + 6] |= SSID_H_MASK;
}
else {
TRACE_ERROR ("PKT > Internal error: ax25_set_hd(%d), num_addr=%d", n, this_p->num_addr);
@@ -1921,15 +1933,13 @@ void ax25_format_addrs (packet_t this_p, char *result, int8_t size)
}
*result = '\0';
- /* New in 0.9. */
- /* Don't get upset if no addresses. */
- /* This will allow packets that do not comply to AX.25 format. */
+ /* There must be at least two addresses. */
- if (this_p->num_addr == 0) {
+ if (this_p->num_addr < 2) {
return;
}
- /* TODO: Make a safe strcat function. */
+ /* TODO: Refactor this to use a single loop and safe write to buffer. */
ax25_get_addr_with_ssid (this_p, AX25_SOURCE, stemp);
if(size - (strlen(stemp) + 1) < 2)
diff --git a/tracker/software/pkt/protocols/aprs2/ax25_pad.h b/tracker/software/pkt/protocols/aprs2/ax25_pad.h
index 79223614..a8e95999 100644
--- a/tracker/software/pkt/protocols/aprs2/ax25_pad.h
+++ b/tracker/software/pkt/protocols/aprs2/ax25_pad.h
@@ -37,6 +37,9 @@
/* to be safe. */
#define AX25_MAX_ADDR_LEN 12
+/* The length of an address field in an AX25 header. */
+#define AX25_ADDR_LEN 7
+
/* Previously 1 when considering only APRS. */
#define AX25_MIN_INFO_LEN 0U
@@ -61,9 +64,9 @@
/* The more general case. */
/* An AX.25 frame can have a control byte and no protocol. */
-#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
+#define AX25_MIN_PACKET_LEN ( 2 * AX25_ADDR_LEN + 1 )
-#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN)
+#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * AX25_ADDR_LEN + 2 + 3 + AX25_MAX_INFO_LEN)
#define AX25_UI_FRAME 3 /* Control field value. */
diff --git a/tracker/software/portab.c b/tracker/software/portab.c
index e8719894..71c5ad76 100644
--- a/tracker/software/portab.c
+++ b/tracker/software/portab.c
@@ -18,6 +18,7 @@
#include "hal.h"
#include "chprintf.h"
#include "portab.h"
+#include "usb.h"
#include
/*===========================================================================*/
@@ -158,6 +159,10 @@ void sysConfigureCoreIO(void) {
palSetLineMode(LINE_I2C_SCL, PAL_MODE_ALTERNATE(4)
| PAL_STM32_OSPEED_HIGHEST
| PAL_STM32_OTYPE_OPENDRAIN); // SCL
+
+ #if ACTIVATE_USB
+ startUSB();
+ #endif
}
/** @} */
diff --git a/tracker/software/portab.h b/tracker/software/portab.h
index d15c49fe..73c08e2d 100644
--- a/tracker/software/portab.h
+++ b/tracker/software/portab.h
@@ -21,7 +21,7 @@
#define USE_SPI_ATTACHED_RADIO TRUE
-#define DUMP_PACKET_TO_SERIAL TRUE
+#define DUMP_PACKET_TO_SERIAL FALSE
/*
* TODO: Need to use radio unit ID to set assigned GPIO & SPI.
@@ -67,6 +67,7 @@
#endif
//#define LINE_PWM_MIRROR PAL_LINE(GPIOA, 8U)
+#define LINE_GPIO_PIN PAL_LINE(GPIOA, 8U)
/**
* ICU related definitions.
@@ -123,7 +124,7 @@ extern "C" {
void pktSetLineModeICU(void);
void pktSerialStart(void);
void dbgWrite(uint8_t level, uint8_t *buf, uint32_t len);
- int dbgPrintf(uint8_t level, const char *format, ...);
+ int dbgPrintf(uint8_t level, const char *format, ...);
void pktWrite(uint8_t *buf, uint32_t len);
void pktPowerUpRadio(radio_unit_t radio);
void pktPowerDownRadio(radio_unit_t radio);
diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c
index fb824770..c0260398 100644
--- a/tracker/software/protocols/packet/aprs.c
+++ b/tracker/software/protocols/packet/aprs.c
@@ -39,13 +39,12 @@ static uint16_t msg_id;
char alias_re[] = "WIDE[4-7]-[1-7]|CITYD";
char wide_re[] = "WIDE[1-7]-[1-7]";
enum preempt_e preempt = PREEMPT_OFF;
-static heard_t heard_list[20];
+static heard_t heard_list[APRS_HEARD_LIST_SIZE];
static bool dedupe_initialized;
const conf_command_t command_list[] = {
{TYPE_INT, "pos_pri.active", sizeof(conf_sram.pos_pri.thread_conf.active), &conf_sram.pos_pri.thread_conf.active },
{TYPE_TIME, "pos_pri.init_delay", sizeof(conf_sram.pos_pri.thread_conf.init_delay), &conf_sram.pos_pri.thread_conf.init_delay },
-/* {TYPE_TIME, "pos_pri.send_spacing", sizeof(conf_sram.pos_pri.thread_conf.send_spacing), &conf_sram.pos_pri.thread_conf.send_spacing },*/
{TYPE_INT, "pos_pri.sleep_conf.type", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.type), &conf_sram.pos_pri.thread_conf.sleep_conf.type },
{TYPE_INT, "pos_pri.sleep_conf.vbat_thres", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.vbat_thres), &conf_sram.pos_pri.thread_conf.sleep_conf.vbat_thres},
{TYPE_INT, "pos_pri.sleep_conf.vsol_thres", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.vsol_thres), &conf_sram.pos_pri.thread_conf.sleep_conf.vsol_thres},
@@ -54,8 +53,6 @@ const conf_command_t command_list[] = {
{TYPE_INT, "pos_pri.freq", sizeof(conf_sram.pos_pri.radio_conf.freq), &conf_sram.pos_pri.radio_conf.freq },
{TYPE_INT, "pos_pri.mod", sizeof(conf_sram.pos_pri.radio_conf.mod), &conf_sram.pos_pri.radio_conf.mod },
{TYPE_INT, "pos_pri.rssi", sizeof(conf_sram.pos_pri.radio_conf.rssi), &conf_sram.pos_pri.radio_conf.rssi },
-/* {TYPE_INT, "pos_pri.speed", sizeof(conf_sram.pos_pri.radio_conf.speed), &conf_sram.pos_pri.radio_conf.speed },*/
-/* {TYPE_INT, "pos_pri.redundantTx", sizeof(conf_sram.pos_pri.radio_conf.redundantTx), &conf_sram.pos_pri.radio_conf.redundantTx },*/
{TYPE_STR, "pos_pri.call", sizeof(conf_sram.pos_pri.call), &conf_sram.pos_pri.call },
{TYPE_STR, "pos_pri.path", sizeof(conf_sram.pos_pri.path), &conf_sram.pos_pri.path },
{TYPE_INT, "pos_pri.symbol", sizeof(conf_sram.pos_pri.symbol), &conf_sram.pos_pri.symbol },
@@ -64,7 +61,6 @@ const conf_command_t command_list[] = {
{TYPE_INT, "pos_sec.active", sizeof(conf_sram.pos_sec.thread_conf.active), &conf_sram.pos_sec.thread_conf.active },
{TYPE_TIME, "pos_sec.init_delay", sizeof(conf_sram.pos_sec.thread_conf.init_delay), &conf_sram.pos_sec.thread_conf.init_delay },
-/* {TYPE_TIME, "pos_sec.send_spacing", sizeof(conf_sram.pos_sec.thread_conf.send_spacing), &conf_sram.pos_sec.thread_conf.send_spacing },*/
{TYPE_INT, "pos_sec.sleep_conf.type", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.type), &conf_sram.pos_sec.thread_conf.sleep_conf.type },
{TYPE_INT, "pos_sec.sleep_conf.vbat_thres", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.vbat_thres), &conf_sram.pos_sec.thread_conf.sleep_conf.vbat_thres},
{TYPE_INT, "pos_sec.sleep_conf.vsol_thres", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.vsol_thres), &conf_sram.pos_sec.thread_conf.sleep_conf.vsol_thres},
@@ -73,8 +69,6 @@ const conf_command_t command_list[] = {
{TYPE_INT, "pos_sec.freq", sizeof(conf_sram.pos_sec.radio_conf.freq), &conf_sram.pos_sec.radio_conf.freq },
{TYPE_INT, "pos_sec.mod", sizeof(conf_sram.pos_sec.radio_conf.mod), &conf_sram.pos_sec.radio_conf.mod },
{TYPE_INT, "pos_sec.rssi", sizeof(conf_sram.pos_sec.radio_conf.rssi), &conf_sram.pos_sec.radio_conf.rssi },
-/* {TYPE_INT, "pos_sec.speed", sizeof(conf_sram.pos_sec.radio_conf.speed), &conf_sram.pos_sec.radio_conf.speed },*/
-/* {TYPE_INT, "pos_sec.redundantTx", sizeof(conf_sram.pos_sec.radio_conf.redundantTx), &conf_sram.pos_sec.radio_conf.redundantTx },*/
{TYPE_STR, "pos_sec.call", sizeof(conf_sram.pos_sec.call), &conf_sram.pos_sec.call },
{TYPE_STR, "pos_sec.path", sizeof(conf_sram.pos_sec.path), &conf_sram.pos_sec.path },
{TYPE_INT, "pos_sec.symbol", sizeof(conf_sram.pos_sec.symbol), &conf_sram.pos_sec.symbol },
@@ -138,13 +132,7 @@ const conf_command_t command_list[] = {
{TYPE_INT, "aprs.active", sizeof(conf_sram.aprs.thread_conf.active), &conf_sram.aprs.thread_conf.active },
{TYPE_TIME, "aprs.init_delay", sizeof(conf_sram.aprs.thread_conf.init_delay), &conf_sram.aprs.thread_conf.init_delay },
- /*
- {TYPE_TIME, "aprs.packet_spacing", sizeof(conf_sram.aprs.rx.thread_conf.packet_spacing), &conf_sram.aprs.rx.thread_conf.packet_spacing },
- {TYPE_INT, "aprs.sleep_conf.type", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.type), &conf_sram.aprs.rx.thread_conf.sleep_conf.type },
- {TYPE_INT, "aprs.sleep_conf.vbat_thres", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.vbat_thres), &conf_sram.aprs.rx.thread_conf.sleep_conf.vbat_thres},
- {TYPE_INT, "aprs.sleep_conf.vsol_thres", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.vsol_thres), &conf_sram.aprs.rx.thread_conf.sleep_conf.vsol_thres},
- {TYPE_TIME, "aprs.rx.cycle", sizeof(conf_sram.aprs.rx.thread_conf.cycle), &conf_sram.aprs.rx.thread_conf.cycle },
- */
+
{TYPE_INT, "aprs.rx.freq", sizeof(conf_sram.aprs.rx.radio_conf.freq), &conf_sram.aprs.rx.radio_conf.freq },
{TYPE_INT, "aprs.rx.mod", sizeof(conf_sram.aprs.rx.radio_conf.mod), &conf_sram.aprs.rx.radio_conf.mod },
{TYPE_INT, "aprs.rx.speed", sizeof(conf_sram.aprs.rx.radio_conf.speed), &conf_sram.aprs.tx.radio_conf.speed },
@@ -172,6 +160,89 @@ const conf_command_t command_list[] = {
{TYPE_NULL}
};
+/*
+ * Table of commands that can be embedded in a message.
+ */
+const APRSCommand aprs_commands[] = {
+ {"?aprsd", aprs_send_aprsd_message},
+ {"?aprsh", aprs_send_aprsh_message},
+ {"?aprsp", aprs_send_position_beacon},
+ {"?gpio", aprs_execute_gpio_command},
+ {"?gps", aprs_handle_gps_command},
+ {"?reset", aprs_execute_system_reset},
+ {"?save", aprs_execute_config_save},
+ {"?img", aprs_execute_img_command},
+ {"?config", aprs_execute_config_command},
+ {NULL, NULL}
+};
+
+/**
+ * @brief parse arguments from a command string.
+ *
+ * @return pointer to next element in string.
+ * @retval NULL if end.
+ */
+static char *aprs_parse_arguments(char *str, char **saveptr) {
+ char *p;
+
+ if (str != NULL)
+ *saveptr = str;
+
+ p = *saveptr;
+ if (!p) {
+ return NULL;
+ }
+
+ /* Skipping white space.*/
+ p += strspn(p, " \t");
+
+ if (*p == '"') {
+ /* If an argument starts with a double quote then its delimiter is another
+ quote.*/
+ p++;
+ *saveptr = strpbrk(p, "\"");
+ }
+ else {
+ /* The delimiter is white space.*/
+ *saveptr = strpbrk(p, " \t");
+ }
+
+ /* Replacing the delimiter with a zero.*/
+ if (*saveptr != NULL) {
+ *(*saveptr)++ = '\0';
+ }
+
+ return *p != '\0' ? p : NULL;
+}
+
+/**
+ * @brief Execute a command in an APRS message.
+ * @notes Known commands are in APRS command table.
+ * @notes Commands themselves return only MSG_OK or MSG_ERROR.
+ *
+ * @return result of command.
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error in command execution.
+ * @retval MSG_TIMEOUT if the command was not found in known commands.
+ */
+static msg_t aprs_cmd_exec(const APRSCommand *acp,
+ char *name,
+ aprs_identity_t *id,
+ int argc,
+ char *argv[]) {
+
+ while (acp->ac_name != NULL) {
+ if (strcmp(acp->ac_name, name) == 0) {
+ return acp->ac_function(id, argc, argv);
+ }
+ acp++;
+ }
+ return MSG_TIMEOUT;
+}
+
+/**
+ *
+ */
void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len)
{
// Decode packet
@@ -193,19 +264,27 @@ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len)
}
/**
- * Transmit APRS position packet. The comments are filled with:
- * - Static comment (can be set in config.h)
- * - Battery voltage in mV
- * - Solar voltage in mW (if tracker is solar-enabled)
- * - Temperature in Celcius
- * - Air pressure in Pascal
- * - Number of satellites being used
- * - Number of cycles where GPS has been lost (if applicable in cycle)
+ * @brief Transmit APRS position packet.
+ * @notes The comments are filled with:
+ * @notes - Static comment (can be set in config.h)
+ * @notes - Battery voltage in mV
+ * @notes - Solar voltage in mW (if tracker is solar-enabled)
+ * @notes - Temperature in Celcius
+ * @notes - Air pressure in Pascal
+ * @notes - Number of satellites being used
+ * @notes - Number of cycles where GPS has been lost (if applicable in cycle)
+ *
+ * @param[in] callsign origination call sign
+ * @param[in] path path to use
+ * @param[in] symbol symbol for originator
+ * @param[in] dataPoint position data object
+ *
+ * @return encoded packet object pointer
+ * @retval NULL if encoding failed
*/
packet_t aprs_encode_position(const char *callsign,
const char *path, uint16_t symbol,
- dataPoint_t *dataPoint)
-{
+ dataPoint_t *dataPoint) {
// Latitude
uint32_t y = 380926 * (90 - dataPoint->gps_lat/10000000.0);
uint32_t y3 = y / 753571;
@@ -236,7 +315,7 @@ packet_t aprs_encode_position(const char *callsign,
char xmit[256];
uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:!",
- callsign, APRS_DEST_CALLSIGN, path);
+ callsign, APRS_DEVICE_CALLSIGN, path);
xmit[len+0] = (symbol >> 8) & 0xFF;
xmit[len+1] = y3+33;
@@ -289,353 +368,721 @@ packet_t aprs_encode_data_packet(const char *callsign, const char *path,
{
char xmit[256];
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:{{%c%s", callsign,
- APRS_DEST_CALLSIGN, path, packetType, data);
+ APRS_DEVICE_CALLSIGN, path, packetType, data);
return ax25_from_text(xmit, true);
}
/**
- * Transmit message packet
+ * @brief Transmit message packet
+ *
+ * @param[in] originator call sign of originator of this message
+ * @param[in path path for message
+ * @param[in] recipient call sign of recipient
+ * @param[in text text of the message
+ * @param[in] ack true if message acknowledgment requested
*/
-packet_t aprs_encode_message(const char *callsign, const char *path,
- const char *receiver, const char *text,
- const bool noCounter) {
+packet_t aprs_encode_message(const char *originator, const char *path,
+ const char *recipient, const char *text,
+ const bool ack) {
char xmit[256];
if((strlen(text) > AX25_MAX_APRS_MSG_LEN)
|| (strpbrk(text, "|~{") != NULL))
/* Invalid message. */
return NULL;
- if(noCounter)
- chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", callsign,
- APRS_DEST_CALLSIGN, path, receiver, text);
+ if(!ack)
+ chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s",
+ originator,
+ APRS_DEVICE_CALLSIGN,
+ path,
+ recipient,
+ text);
else
- chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", callsign,
- APRS_DEST_CALLSIGN, path, receiver, text, ++msg_id);
+ chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d",
+ originator,
+ APRS_DEVICE_CALLSIGN,
+ path,
+ recipient,
+ text,
+ ++msg_id);
return ax25_from_text(xmit, true);
}
-packet_t aprs_encode_query_answer_aprsd(const char *callsign,
- const char *path,
- const char *receiver)
-{
+/*
+ * @brief Compose an APRSD message
+ * @notes Used by position service.
+ *
+ * @param[in] originator originator of this message
+ * @param[in] path path to use
+ * @param[in] recipient identity that requested the message
+ */
+packet_t aprs_compose_aprsd_message(const char *originator,
+ const char *path,
+ const char *recipient) {
char buf[256] = "Directs=";
- uint32_t out = 8;
- for(uint8_t i=0; i<20; i++) {
+ uint32_t out = strlen(buf);
+ uint32_t empty = out;
+ for(uint8_t i = 0; i < APRS_HEARD_LIST_SIZE; i++) {
if(heard_list[i].time
&& heard_list[i].time + TIME_S2I(600) >= chVTGetSystemTime()
&& heard_list[i].time <= chVTGetSystemTime())
out += chsnprintf(&buf[out], sizeof(buf)-out, "%s ",
heard_list[i].call);
}
- buf[out-1] = 0; // Remove last space
-
- return aprs_encode_message(callsign, path, receiver, buf, true);
-}
-
-static bool aprs_decode_message(packet_t pp) {
- // Get Info field
- char src[127];
- unsigned char *pinfo;
- if(ax25_get_info(pp, &pinfo) == 0)
- return false;
- ax25_format_addrs(pp, src, sizeof(src));
-
- // Decode destination callsign
- char dest[AX25_MAX_ADDR_LEN];
- uint8_t i=0;
-
- while(i < sizeof(dest)-1) {
- if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') {
- dest[i++] = 0;
- break;
- }
- dest[i] = pinfo[i+1];
- i++;
+ if(out == empty) {
+ out += chsnprintf(&buf[out], sizeof(buf)-out, "[none]");
+ } else {
+ buf[out-1] = 0; // Remove last space
}
- // Decode source callsign
- for(uint32_t i=0; i < sizeof(src); i++) {
- if(src[i] == '>') {
- src[i] = 0;
- break;
- }
- }
-
- /* Check if this message is meant for us. */
- bool pos_pri = !strcmp(conf_sram.pos_pri.call, dest)
- && (conf_sram.pos_pri.aprs_msg)
- && (conf_sram.pos_pri.thread_conf.active);
- bool pos_sec = !strcmp(conf_sram.pos_sec.call, dest)
- && (conf_sram.pos_sec.aprs_msg)
- && (conf_sram.pos_sec.thread_conf.active);
- bool aprs_rx = !strcmp(conf_sram.aprs.rx.call, dest)
- && (conf_sram.aprs.thread_conf.active);
- bool aprs_tx = !strcmp(conf_sram.aprs.tx.call, dest)
- && (conf_sram.aprs.thread_conf.active)
- && (conf_sram.aprs.dig_active);
-
- if((pinfo[10] == ':') && (pos_pri || pos_sec || aprs_rx || aprs_tx)) {
- char msg_id_rx[8];
- memset(msg_id_rx, 0, sizeof(msg_id_rx));
-
- // Cut off control chars
- for(uint16_t i = 11; pinfo[i] != 0 && i < AX25_MAX_APRS_MSG_LEN + 11; i++) {
- /* FIXME: Trim trailing spaces before {. */
- if(pinfo[i] == '{') {
- // Copy ACK ID
- memcpy(msg_id_rx, &pinfo[i+1], sizeof(msg_id_rx)-1);
- // Cut off non-printable chars
- for(uint8_t j=0; j 126) {
- msg_id_rx[j] = 0;
- break;
- }
- }
- pinfo[i] = 0; // Mark end of message
- }
- if(pinfo[i] == '\r' || pinfo[i] == '\n') {
- pinfo[i] = 0;
- }
- }
-
- char *command = strlwr((char*)&pinfo[11]);
-
- // Trace
- TRACE_INFO("RX > Received message from %s (ID=%s): %s [%s]",
- src, msg_id_rx, &pinfo[11], command);
-
- // Do control actions
- if(!strcmp(command, "?gpio pa8:1")) { // Switch on pin
-
- TRACE_INFO("RX > Message: GPIO query PA8 HIGH");
- palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
- palSetPad(GPIOA, 8);
-
- } else if(!strcmp(command, "?gpio pa8:0")) { // Switch off pin
-
- TRACE_INFO("RX > Message: GPIO query PA8 LOW");
- palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
- palClearPad(GPIOA, 8);
-
- } else if(!strcmp(command, "?gps fixed")) { // Use fixed gps coordinates
-
- TRACE_INFO("RX > Message: GPS fixed location");
- test_gps_enabled = true;
-
- } else if(!strcmp(command, "?gps normal")) { // Use fixed gps coordinates
-
- TRACE_INFO("RX > Message: GPS acquire location");
- test_gps_enabled = false;
-
- } else if(!strcmp(command, "?aprsp")) { // Transmit position
- /*
- * There is no reply sent to the src (ack can be sent).
- * Just a position sent from the APRS TX identity.
- * The identity could be the same as one of the POS identities.
- */
- TRACE_INFO("RX > Message: Position query");
- dataPoint_t* dataPoint = getLastDataPoint();
- packet_t pp = aprs_encode_position(conf_sram.aprs.tx.call,
- conf_sram.aprs.tx.path,
- conf_sram.aprs.tx.symbol,
- dataPoint);
- if(pp == NULL) {
- TRACE_WARN("RX > No free packet objects");
- return false;
- }
- transmitOnRadio(pp,
- conf_sram.aprs.tx.radio_conf.freq,
- 0,
- 0,
- conf_sram.aprs.tx.radio_conf.pwr,
- conf_sram.aprs.tx.radio_conf.mod,
- conf_sram.aprs.tx.radio_conf.rssi);
-
- } else if(!strcmp(command, "?aprsd")) { // Transmit position
-
- TRACE_INFO("RX > Message: Directs query");
- packet_t pp =
- aprs_encode_query_answer_aprsd(conf_sram.aprs.tx.call,
- conf_sram.aprs.tx.path, src);
- if(pp == NULL) {
- TRACE_WARN("RX > No free packet objects or badly formed message");
- return false;
- }
- transmitOnRadio(pp,
- conf_sram.aprs.tx.radio_conf.freq,
- 0,
- 0,
- conf_sram.aprs.tx.radio_conf.pwr,
- conf_sram.aprs.tx.radio_conf.mod,
- conf_sram.aprs.tx.radio_conf.rssi);
-
- } else if(!strcmp(command, "?reset")) { // Transmit position
-
- TRACE_INFO("RX > Message: System Reset");
- char buf[16];
- chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx);
- packet_t pp = aprs_encode_message(conf_sram.aprs.tx.call,
- conf_sram.aprs.tx.path,
- src, buf, true);
- if(pp == NULL) {
- TRACE_WARN("RX > No free packet objects");
- return false;
- }
- transmitOnRadio(pp,
- conf_sram.aprs.tx.radio_conf.freq,
- 0,
- 0,
- conf_sram.aprs.tx.radio_conf.pwr,
- conf_sram.aprs.tx.radio_conf.mod,
- conf_sram.aprs.tx.radio_conf.rssi);
- chThdSleep(TIME_S2I(5)); // Give some time to send the message
-
- NVIC_SystemReset();
-
- } else if(!strcmp(command, "?save")) { // Transmit position
-
- TRACE_INFO("RX > Message: Save");
- conf_sram.magic = CONFIG_MAGIC_UPDATED;
- flashSectorBegin(flashSectorAt(0x08060000));
- flashErase(0x08060000, 0x20000);
- flashWrite(0x08060000, (char*)&conf_sram, sizeof(conf_t));
- flashSectorEnd(flashSectorAt(0x08060000));
-
- } else if(!strcmp(command, "?img reject pri")) { // Reject image
-
- reject_pri = true;
-
- } else if(!strcmp(command, "?img reject sec")) { // Reject image
-
- reject_sec = true;
-
- } else if(!strncmp(command, "?img ", 5)) { // Repeat packets
-
- TRACE_INFO("RX > Message: Image packet repeat request");
-
- char *pt;
- pt = strtok(&command[5], " ");
- while(pt != NULL) {
- uint32_t req = strtol(pt, NULL, 16);
-
- for(uint8_t i=0; i<16; i++) {
- if(!packetRepeats[i].n_done) {
- packetRepeats[i].image_id = (req >> 16) & 0xFF;
- packetRepeats[i].packet_id = req & 0xFFFF;
- packetRepeats[i].n_done = true;
-
- TRACE_INFO("RX > ... Image %3d Packet %3d",
- packetRepeats[i].image_id,
- packetRepeats[i].packet_id);
- break;
- }
- }
-
- pt = strtok(NULL, " ");
- }
-
- } else if(!strncmp(command, "?conf ", 6)) { // Modify configuration
-
- for(uint8_t i=0; command_list[i].type != TYPE_NULL; i++)
- {
- if(!strncmp(&command[6], command_list[i].name,
- strlen(command_list[i].name))) {
-
- char *value = &command[strlen(command_list[i].name) + 6];
- TRACE_INFO("RX > Message: Configuration Command");
- TRACE_INFO("RX > %s => %s", &command[6], value);
-
- if(command_list[i].type == TYPE_INT
- && command_list[i].size == 1) {
- *((uint8_t*)command_list[i].ptr) = atoi(value);
- } else if(command_list[i].type == TYPE_INT
- && command_list[i].size == 2) {
- *((uint16_t*)command_list[i].ptr) = atoi(value);
- } else if(command_list[i].type == TYPE_INT
- && command_list[i].size == 4) {
- *((uint32_t*)command_list[i].ptr) = atoi(value);
- } else if(command_list[i].type == TYPE_TIME) {
- *((sysinterval_t*)command_list[i].ptr) =
- TIME_MS2I(atoi(value));
- } else if(command_list[i].type == TYPE_STR) {
- strncpy((char*)command_list[i].ptr, value,
- sizeof(command_list[i].size)-1);
- }
- }
- }
-
- } else {
- TRACE_INFO("RX > No command found in message");
- }
-
- if(msg_id_rx[0]) { // Message ID has been sent which has to be acknowledged
- char buf[16];
- chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx);
-
- packet_t pp = aprs_encode_message(conf_sram.aprs.tx.call,
- conf_sram.aprs.tx.path,
- src, buf, true);
- if(pp == NULL) {
- TRACE_WARN("RX > No free packet objects");
- return false;
- }
- transmitOnRadio(pp,
- conf_sram.aprs.tx.radio_conf.freq,
- 0,
- 0,
- conf_sram.aprs.tx.radio_conf.pwr,
- conf_sram.aprs.tx.radio_conf.mod,
- conf_sram.aprs.tx.radio_conf.rssi);
- }
- return false; // Mark that message should not be digipeated
- }
- return true; // Mark that message has to be digipeated
+ return aprs_encode_message(originator, path, recipient, buf, false);
}
/*
+ * @brief Encode and send an APRSD message
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_send_aprsd_message(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)argc;
+ (void)argv;
+
+ packet_t pp = aprs_compose_aprsd_message(id->call, id->path, id->src);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects or badly formed message");
+ return MSG_ERROR;
+ }
+ if(!transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi)) {
+ TRACE_ERROR("RX > Transmit of APRSD failed");
+ return MSG_ERROR;
+ }
+ return MSG_OK;
+}
+
+/*
+ * @brief Encode and send an APRSH message
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_send_aprsh_message(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ if(argc != 1)
+ return MSG_ERROR;
+ char buf[AX25_MAX_APRS_MSG_LEN + 1];
+ uint32_t out = 0;
+ strupr(argv[0]);
+ for(uint8_t i = 0; i < APRS_HEARD_LIST_SIZE; i++) {
+ if(heard_list[i].time && (strncmp(heard_list[i].call, argv[0],
+ strlen(argv[0])) == 0)) {
+ /* Convert time to human readable form. */
+ time_secs_t diff = chTimeI2S(chVTTimeElapsedSinceX(heard_list[i].time));
+ out = chsnprintf(buf, sizeof(buf),
+ "%s heard %02i:%02i ago",
+ heard_list[i].call, diff/60, diff % 60);
+ break;
+ }
+ if(out == 0) {
+ out = chsnprintf(buf, sizeof(buf),
+ "%s not heard", argv[0]);
+ }
+ }
+ packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects or badly formed message");
+ return MSG_ERROR;
+ }
+ if(!transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi)) {
+ TRACE_ERROR("RX > Transmit of APRSH failed");
+ return MSG_ERROR;
+ }
+ return MSG_OK;
+}
+
+/*
+ * @brief Handle GPIO set/clear/query
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_execute_gpio_command(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ if(argc != 1)
+ return MSG_ERROR;
+
+ if(!strcmp(argv[0], "pa8:1")) {
+ TRACE_INFO("RX > Message: GPIO query PA8 HIGH");
+ palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPad(GPIOA, 8);
+ return MSG_OK;
+ }
+
+ if(!strcmp(argv[0], "pa8:0")) {
+ TRACE_INFO("RX > Message: GPIO query PA8 LOW");
+ palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOA, 8);
+ return MSG_OK;
+ }
+
+ if(!strcmp(argv[0], "pa8:?")) {
+ char buf[AX25_MAX_APRS_MSG_LEN + 1];
+ /* TODO: Need to read mode and if not output then report as "input" etc. */
+ chsnprintf(buf, sizeof(buf),
+ "PA8 is %s ",
+ (palReadPad(GPIOA, 8) == PAL_HIGH) ? "HIGH" : "LOW");
+
+ packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects or badly formed message");
+ return MSG_ERROR;
+ }
+ if(!transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi)) {
+ TRACE_ERROR("RX > Transmit of GPIO status failed");
+ return MSG_ERROR;
+ }
+ return MSG_OK;
+ }
+ return MSG_ERROR;
+}
+
+/*
+ * @brief Handle GPS command
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_handle_gps_command(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ if(argc != 1)
+ return MSG_ERROR;
+
+ if(!strcmp(argv[0], "fixed")) {
+ TRACE_INFO("RX > Message: GPS set to fixed location");
+ test_gps_enabled = true;
+ return MSG_OK;
+ }
+
+ if(!strcmp(argv[0], "normal")) {
+ TRACE_INFO("RX > Message: GPS set to normal operation");
+ test_gps_enabled = false;
+ return MSG_OK;
+ }
+
+ if(!strcmp(argv[0], "status")) {
+ char buf[AX25_MAX_APRS_MSG_LEN + 1];
+ /* TODO: Need to read mode and if not output then report as "input" etc. */
+ chsnprintf(buf, sizeof(buf),
+ "GPS is %s",
+ test_gps_enabled ? "fixed" : "normal");
+
+ packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects or badly formed message");
+ return MSG_ERROR;
+ }
+ if(!transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi)) {
+ TRACE_ERROR("RX > Transmit of GPS status failed");
+ return MSG_ERROR;
+ }
+ return MSG_OK;
+ }
+ return MSG_ERROR;
+}
+
+/**
+* @brief Request for position beacon to be sent
+*
+* @param[in] id aprs node identity
+* @param[in] argc number of parameters
+* @param[in] argv array of pointers to parameter strings
+*
+* @return result of command
+* @retval MSG_OK if the command completed.
+* @retval MSG_ERROR if there was an error.
+*/
+msg_t aprs_send_position_beacon(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)argv;
+
+ if(argc != 0)
+ return MSG_ERROR;
+
+ TRACE_INFO("RX > Message: Position query");
+ dataPoint_t* dataPoint = getLastDataPoint();
+ packet_t pp = aprs_encode_position(id->call,
+ id->path,
+ id->symbol,
+ dataPoint);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects or badly formed message");
+ return MSG_ERROR;
+ }
+ if(!transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi)) {
+ TRACE_ERROR("RX > Transmit of APRSD failed");
+ return MSG_ERROR;
+ }
+ return MSG_OK;
+}
+
+/**
+* @brief Request for system reset
+*
+* @param[in] id aprs node identity
+* @param[in] argc number of parameters
+* @param[in] argv array of pointers to parameter strings
+*
+* @return result of command
+* @retval MSG_ERROR if there was an error.
+*/
+msg_t aprs_execute_system_reset(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)argv;
+
+ if(argc != 0)
+ return MSG_ERROR;
+
+ TRACE_INFO("RX > Message: System Reset");
+ char buf[16];
+ chsnprintf(buf, sizeof(buf), "ack%s", id->num);
+ packet_t pp = aprs_encode_message(id->call,
+ id->path,
+ id->src, buf, false);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects");
+ return MSG_ERROR;
+ }
+ transmitOnRadio(pp,
+ id->freq,
+ 0,
+ 0,
+ id->pwr,
+ id->mod,
+ id->rssi);
+
+ chThdSleep(TIME_S2I(10));
+
+ NVIC_SystemReset();
+ /* We don't arrive here. */
+ return MSG_OK;
+}
+
+/*
+ * @brief Handle config command
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_execute_config_command(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)id;
+
+ if(argc != 2)
+ return MSG_ERROR;
+
+ for(uint8_t i=0; command_list[i].type != TYPE_NULL; i++) {
+ if(!strncmp(argv[1], command_list[i].name,
+ strlen(command_list[i].name))) {
+
+ /* Parameter being changed is in argv[0], new value is in argv[1]. */
+ TRACE_INFO("RX > Message: Configuration Command");
+ TRACE_INFO("RX > %s => %s", argv[1], argv[1]);
+
+ if(command_list[i].type == TYPE_INT
+ && command_list[i].size == 1) {
+ *((uint8_t*)command_list[i].ptr) = atoi(argv[1]);
+ } else if(command_list[i].type == TYPE_INT
+ && command_list[i].size == 2) {
+ *((uint16_t*)command_list[i].ptr) = atoi(argv[1]);
+ } else if(command_list[i].type == TYPE_INT
+ && command_list[i].size == 4) {
+ *((uint32_t*)command_list[i].ptr) = atoi(argv[1]);
+ } else if(command_list[i].type == TYPE_TIME) {
+ *((sysinterval_t*)command_list[i].ptr) =
+ TIME_MS2I(atoi(argv[2]));
+ } else if(command_list[i].type == TYPE_STR) {
+ strncpy((char*)command_list[i].ptr, argv[1],
+ sizeof(command_list[i].size)-1);
+ }
+ return MSG_OK;
+ } /* Next parameter. */
+ } /* Parameter not found. */
+ return MSG_ERROR;
+}
+
+/**
+* @brief Request configuration save to flash
+*
+* @param[in] id aprs node identity
+* @param[in] argc number of parameters
+* @param[in] argv array of pointers to parameter strings
+*
+* @return result of command
+* @retval MSG_ERROR if there was an error.
+*/
+msg_t aprs_execute_config_save(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)id;
+ (void)argc;
+ (void)argv;
+
+ TRACE_INFO("RX > Message: Config Save");
+ conf_sram.magic = CONFIG_MAGIC_UPDATED;
+ flashSectorBegin(flashSectorAt(0x08060000));
+ flashErase(0x08060000, 0x20000);
+ flashWrite(0x08060000, (char*)&conf_sram, sizeof(conf_t));
+ flashSectorEnd(flashSectorAt(0x08060000));
+ return MSG_OK;
+}
+
+/*
+ * @brief Handle Image command
+ *
+ * @param[in] id aprs node identity
+ * @param[in] argc number of parameters
+ * @param[in] argv array of pointers to parameter strings
+ *
+ * @return result of command
+ * @retval MSG_OK if the command completed.
+ * @retval MSG_ERROR if there was an error.
+ */
+msg_t aprs_execute_img_command(aprs_identity_t *id,
+ int argc, char *argv[]) {
+ (void)id;
+ if(argc < 2)
+ return MSG_ERROR;
+
+ if(!strcmp(argv[0], "reject") && argc == 2) {
+ if(!strcmp(argv[1], "pri")) {
+ reject_pri = true;
+ TRACE_INFO("RX > Message: Image reject pri");
+ return MSG_OK;
+ }
+ if(!strcmp(argv[1], "sec")) {
+ reject_sec = true;
+ TRACE_INFO("RX > Message: Image reject sec");
+ return MSG_OK;
+ }
+ return MSG_ERROR;
+ }
+
+ if(!strcmp(argv[0], "repeat")) {
+ TRACE_INFO("RX > Message: Image packet repeat request");
+
+ /* Start at arg 2. */
+ int c = 2;
+ while(c <= argc) {
+ uint32_t req = strtol(argv[c++], NULL, 16);
+ for(uint8_t i = 0; i < 16; i++) {
+ /* Find an empty repeat slot. */
+ if(!packetRepeats[i].n_done) {
+ packetRepeats[i].image_id = (req >> 16) & 0xFF;
+ packetRepeats[i].packet_id = req & 0xFFFF;
+ packetRepeats[i].n_done = true;
+
+ TRACE_INFO("RX > ... Image %3d Packet %3d",
+ packetRepeats[i].image_id,
+ packetRepeats[i].packet_id);
+ break;
+ } /* Not an empty slot. */
+ } /* No more slots. */
+ } /* No more image IDs. */
+ return MSG_OK;
+ }
+ /* Unknown parameter. */
+ return MSG_ERROR;
+}
+
+/*
+ * @brief Decode APRS content and check for message
+ *
+ * @param[in] pp an APRS packet object
+ *
+ * @return result of command
+ * @retval true if not a message or not addressed to any node on this device.
+ * in that case the APRS content can be digipeated.
+ * @retval false if this was a message for a node on this device.
+ * in that case the APRS content should not be digipeated.
+ */
+static bool aprs_decode_message(packet_t pp) {
+ // Get Info field
+ char src[127];
+ unsigned char *pinfo;
+ if(ax25_get_info(pp, &pinfo) == 0)
+ return false;
+
+ ax25_format_addrs(pp, src, sizeof(src));
+
+ /* Decode destination call sign. */
+ char dest[AX25_MAX_ADDR_LEN];
+ uint8_t i = 0;
+
+ while(i < sizeof(dest) - 1) {
+ if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') {
+ dest[i++] = 0;
+ break;
+ }
+ dest[i] = pinfo[i+1];
+ i++;
+ }
+ /* Convert destination call sign to upper case. */
+ strupr(dest);
+
+ /* Decode source call sign. */
+ for(uint32_t i = 0; i < sizeof(src); i++) {
+ if(src[i] == '>') {
+ src[i] = 0;
+ break;
+ }
+ }
+ /* Convert source call sign to upper case. */
+ strupr(src);
+
+ /*
+ * Setup default responding node identity.
+ * Default identity id set to tx.
+ */
+
+ aprs_identity_t identity = {0};
+
+ strcpy(identity.src, src);
+ strcpy(identity.call, conf_sram.aprs.tx.call);
+ /* TODO: define a length for path. */
+ strcpy(identity.path, conf_sram.aprs.tx.path);
+ identity.symbol = conf_sram.aprs.tx.symbol;
+ identity.freq = conf_sram.aprs.tx.radio_conf.freq;
+ identity.pwr = conf_sram.aprs.tx.radio_conf.pwr;
+ identity.mod = conf_sram.aprs.tx.radio_conf.mod;
+ identity.rssi = conf_sram.aprs.tx.radio_conf.rssi;
+
+ /* Check which nodes are enabled to accept aprs messages. */
+ bool pos_pri = !strcmp(conf_sram.pos_pri.call, dest)
+ && (conf_sram.pos_pri.aprs_msg)
+ && (conf_sram.pos_pri.thread_conf.active);
+
+ if(pos_pri) {
+ strcpy(identity.call, conf_sram.pos_pri.call);
+ strcpy(identity.path, conf_sram.pos_pri.path);
+ identity.symbol = conf_sram.pos_pri.symbol;
+ }
+
+ bool pos_sec = !strcmp(conf_sram.pos_sec.call, dest)
+ && (conf_sram.pos_sec.aprs_msg)
+ && (conf_sram.pos_sec.thread_conf.active);
+ if(pos_sec) {
+ strcpy(identity.call, conf_sram.pos_sec.call);
+ strcpy(identity.path, conf_sram.pos_sec.path);
+ identity.symbol = conf_sram.pos_sec.symbol;
+ }
+
+ bool aprs_rx = !strcmp(conf_sram.aprs.rx.call, dest)
+ && (conf_sram.aprs.thread_conf.active);
+ if(aprs_rx) {
+ /* Parameters come from tx. */
+ }
+
+ bool aprs_tx = !strcmp(conf_sram.aprs.tx.call, dest)
+ && (conf_sram.aprs.thread_conf.active)
+ && (conf_sram.aprs.dig_active);
+ /* Default already set for tx. */
+
+ /* Check if this is message and address is one of the nodes on this device. */
+ if(!((pinfo[10] == ':') && (pos_pri || pos_sec || aprs_rx || aprs_tx))) {
+ /*
+ * Not a command or not addressed to one of the active nodes on this device.
+ * Flag that message should be digipeated.
+ */
+ return true;
+ }
+
+ /* Proceed with command analysis. */
+ char msg_id_rx[8] = {0};
+ //memset(msg_id_rx, 0, sizeof(msg_id_rx));
+
+ // Cut off control chars
+ for(uint16_t i = 11; pinfo[i] != 0
+ && i < (AX25_MAX_APRS_MSG_LEN + 11); i++) {
+ /* FIXME: Trim trailing spaces before {. */
+ if(pinfo[i] == '{') {
+ // Copy ACK ID
+ memcpy(msg_id_rx, &pinfo[i+1], sizeof(msg_id_rx)-1);
+ // Cut off non-printable chars
+ for(uint8_t j=0; j 126) {
+ msg_id_rx[j] = 0;
+ break;
+ }
+ }
+ pinfo[i] = 0; // Mark end of message
+ }
+ if(pinfo[i] == '\r' || pinfo[i] == '\n') {
+ pinfo[i] = 0;
+ }
+ }
+
+ strcpy(identity.num, msg_id_rx);
+
+ /* Convert command string to lower case. */
+ char *astrng = strlwr((char*)&pinfo[11]);
+
+ // Trace
+ TRACE_INFO("RX > Received message from %s (ID=%s): %s [%s]",
+ src, msg_id_rx, &pinfo[11], astrng);
+
+ /* Parse arguments. */
+ char *lp, *cmd, *tokp;
+ char *args[APRS_MAX_MSG_ARGUMENTS];
+ lp = aprs_parse_arguments(astrng, &tokp);
+ /* The command itself. */
+ cmd = lp;
+ int n = 0;
+ while ((lp = aprs_parse_arguments(NULL, &tokp)) != NULL) {
+ if (n >= APRS_MAX_MSG_ARGUMENTS) {
+ TRACE_INFO("RX > Too many APRS command arguments");
+ cmd = NULL;
+ break;
+ }
+ args[n++] = lp;
+ }
+
+ /* Parse and execute command. */
+ msg_t msg = aprs_cmd_exec(aprs_commands, cmd, &identity, n, args);
+
+ if(msg == MSG_TIMEOUT) {
+ TRACE_INFO("RX > No command found in message");
+ }
+
+ if(msg_id_rx[0]) {
+ /* Incoming message ID exists so an ACK or REJ has to be sent. */
+ char buf[16];
+ chsnprintf(buf, sizeof(buf), "%s%s",
+ (msg == MSG_OK || msg == MSG_TIMEOUT) ?
+ "ack" : "rej", msg_id_rx);
+
+ /*
+ * Use the receiving node identity as sender.
+ * Don't request acknowledgment.
+ */
+ packet_t pp = aprs_encode_message(identity.call,
+ identity.path,
+ identity.src, buf, false);
+ if(pp == NULL) {
+ TRACE_WARN("RX > No free packet objects");
+ return false;
+ }
+ transmitOnRadio(pp,
+ identity.freq,
+ 0,
+ 0,
+ identity.pwr,
+ identity.mod,
+ identity.rssi);
+ }
+ /* Flag that the APRS content should not be digipeated. */
+ return false;
+}
+
+/**
* Transmit failure will release the packet memory.
*/
static void aprs_digipeat(packet_t pp) {
- if(!dedupe_initialized) {
- dedupe_init(TIME_S2I(10));
- dedupe_initialized = true;
- }
+ if(!dedupe_initialized) {
+ dedupe_init(TIME_S2I(10));
+ dedupe_initialized = true;
+ }
- if(!dedupe_check(pp, 0)) { // Last identical packet older than 10 seconds
- packet_t result = digipeat_match(0, pp, conf_sram.aprs.rx.call,
- conf_sram.aprs.tx.call, alias_re,
- wide_re, 0, preempt, NULL);
- if(result != NULL) { // Should be digipeated
- dedupe_remember(result, 0);
- /* If transmit fails the packet is freed. */
- transmitOnRadio(result,
- conf_sram.aprs.tx.radio_conf.freq,
- 0,
- 0,
- conf_sram.aprs.tx.radio_conf.pwr,
- conf_sram.aprs.tx.radio_conf.mod,
- conf_sram.aprs.tx.radio_conf.rssi);
- }
- }
+ if(!dedupe_check(pp, 0)) { // Last identical packet older than 10 seconds
+ packet_t result = digipeat_match(0, pp, conf_sram.aprs.rx.call,
+ conf_sram.aprs.tx.call, alias_re,
+ wide_re, 0, preempt, NULL);
+ if(result != NULL) { // Should be digipeated
+ dedupe_remember(result, 0);
+ /* If transmit fails the packet buffer is released. */
+ if(!transmitOnRadio(result,
+ conf_sram.aprs.tx.radio_conf.freq,
+ 0,
+ 0,
+ conf_sram.aprs.tx.radio_conf.pwr,
+ conf_sram.aprs.tx.radio_conf.mod,
+ conf_sram.aprs.tx.radio_conf.rssi)) {
+ TRACE_INFO("RX > Failed to digipeat packet");
+ } /* TX failed. */
+ } /* Should be digipteated. */
+ } /* Duplicate check. */
}
/**
* Transmit APRS telemetry configuration
*/
packet_t aprs_encode_telemetry_configuration(const char *callsign,
- const char *path, uint8_t type)
-{
- switch(type)
- {
+ const char *path,
+ uint8_t type) {
+ switch(type) {
case 0: return aprs_encode_message(callsign, path, callsign,
- "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", true);
+ "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", false);
case 1: return aprs_encode_message(callsign, path, callsign,
- "UNIT.V,V,W,degC,Pa", true);
+ "UNIT.V,V,W,degC,Pa", false);
case 2: return aprs_encode_message(callsign, path, callsign,
- "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", true);
+ "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", false);
case 3: return aprs_encode_message(callsign, path, callsign,
- "BITS.11111111,", true);
+ "BITS.11111111,", false);
default: return NULL;
}
}
@@ -650,14 +1097,15 @@ void aprs_decode_packet(packet_t pp) {
do {
v++;
ax25_get_addr_with_ssid(pp, ax25_get_heard(pp)-v, call);
- } while(ax25_get_heard(pp) - v >= AX25_SOURCE && (!strncmp("WIDE", call, 4) || !strncmp("TRACE", call, 5)));
+ } while(((ax25_get_heard(pp) - v) >= AX25_SOURCE)
+ && (!strncmp("WIDE", call, 4) || !strncmp("TRACE", call, 5)));
// Fill/Update direct list
sysinterval_t first_time = 0xFFFFFFFF; // Timestamp of oldest heard list entry
uint8_t first_id = 0; // ID of oldest heard list entry
- for(uint8_t i=0; i<=20; i++) {
- if(i < 20) {
+ for(uint8_t i=0; i <= APRS_HEARD_LIST_SIZE; i++) {
+ if(i < APRS_HEARD_LIST_SIZE) {
// Search for callsign in list
if(!strcmp(heard_list[i].call, call)) { // Callsign found in list
heard_list[i].time = chVTGetSystemTime(); // Update time the callsign was last heard
@@ -686,7 +1134,9 @@ void aprs_decode_packet(packet_t pp) {
* Execute any command found in the message.
* If not then digipeat it.
*/
- if(pinfo[0] == ':') digipeat = aprs_decode_message(pp); // ax25_get_dti(pp)
+ if(pinfo[0] == ':') {
+ digipeat = aprs_decode_message(pp); // ax25_get_dti(pp)
+ }
// Digipeat packet
if(conf_sram.aprs.dig_active && digipeat) {
diff --git a/tracker/software/protocols/packet/aprs.h b/tracker/software/protocols/packet/aprs.h
index 7d1a07e9..059666e1 100644
--- a/tracker/software/protocols/packet/aprs.h
+++ b/tracker/software/protocols/packet/aprs.h
@@ -40,8 +40,8 @@
#define ORIGIN_OTHER_TRACKER 0x6
#define ORIGIN_DIGIPEATER_CONVERSION 0x7
-#define APRS_DEST_CALLSIGN "APECAN" // APExxx = Pecan device
-#define APRS_DEST_SSID 0
+#define APRS_DEVICE_CALLSIGN "APECAN" // APExxx = Pecan device
+//#define APRS_DEST_SSID 0
#define SYM_BALLOON 0x2F4F
#define SYM_SMALLAIRCRAFT 0x2F27
@@ -49,18 +49,76 @@
#define SYM_CAR 0x2F3E
#define SYM_SHIP 0x2F73
#define SYM_DIGIPEATER 0x2F23
+#define SYM_ANTENNA 0x2F72
+#define APRS_HEARD_LIST_SIZE 20
+
+#define APRS_MAX_MSG_ARGUMENTS 10
+
+typedef struct APRSIdentity {
+ char num[8];
+ char src[AX25_MAX_ADDR_LEN];
+ char call[AX25_MAX_ADDR_LEN];
+ char path[16];
+ uint16_t symbol;
+ uint32_t freq;
+ uint8_t pwr;
+ mod_t mod;
+ uint8_t rssi;
+} aprs_identity_t;
+
+/**
+ * @brief Command handler function type.
+ */
+typedef msg_t (*aprscmd_t)(aprs_identity_t *id, int argc, char *argv[]);
+
+/**
+ * @brief APRS command entry type.
+ */
+typedef struct {
+ const char *ac_name; /**< @brief Command name. */
+ aprscmd_t ac_function; /**< @brief Command function. */
+} APRSCommand;
+
+/* Temporary. Will be deprecated when fixed station feature is implemented. */
extern bool test_gps_enabled;
-void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len);
-
-packet_t aprs_encode_position(const char *callsign, const char *path, uint16_t symbol, dataPoint_t *dataPoint);
-packet_t aprs_encode_telemetry_configuration(const char *callsign, const char *path, uint8_t type);
-packet_t aprs_encode_message(const char *callsign, const char *path, const char *receiver, const char *text, const bool noCounter);
-packet_t aprs_encode_data_packet(const char *callsign, const char *path, char packetType, uint8_t *data);
-packet_t aprs_encode_query_answer_aprsd(const char *callsign, const char *path, const char *receiver);
-
-void aprs_decode_packet(packet_t pp);
-
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len);
+ packet_t aprs_encode_position(const char *callsign, const char *path,
+ uint16_t symbol, dataPoint_t *dataPoint);
+ packet_t aprs_encode_telemetry_configuration(const char *callsign,
+ const char *path, uint8_t type);
+ packet_t aprs_encode_message(const char *callsign, const char *path,
+ const char *receiver, const char *text,
+ const bool ack);
+ packet_t aprs_encode_data_packet(const char *callsign, const char *path,
+ char packetType, uint8_t *data);
+ packet_t aprs_compose_aprsd_message(const char *callsign, const char *path,
+ const char *receiver);
+ void aprs_decode_packet(packet_t pp);
+ msg_t aprs_send_position_beacon(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_send_aprsd_message(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_send_aprsh_message(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_execute_gpio_command(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_handle_gps_command(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_execute_config_command(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_execute_config_save(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_execute_img_command(aprs_identity_t *id,
+ int argc, char *argv[]);
+ msg_t aprs_execute_system_reset(aprs_identity_t *id,
+ int argc, char *argv[]);
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/tracker/software/threads/collector.c b/tracker/software/threads/collector.c
index 0dc6d8e9..3438d697 100644
--- a/tracker/software/threads/collector.c
+++ b/tracker/software/threads/collector.c
@@ -27,16 +27,14 @@ static bool threadStarted = false;
/**
* Returns most recent data point which is complete.
*/
-dataPoint_t* getLastDataPoint(void)
-{
+dataPoint_t* getLastDataPoint(void) {
return lastDataPoint;
}
/*
*
*/
-void waitForNewDataPoint(void)
-{
+void waitForNewDataPoint(void) {
uint32_t old_id = getLastDataPoint()->id;
while(old_id == getLastDataPoint()->id)
chThdSleep(TIME_S2I(1));
@@ -45,8 +43,8 @@ void waitForNewDataPoint(void)
/*
*
*/
-static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp, sysinterval_t timeout)
-{
+static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp,
+ sysinterval_t timeout) {
sysinterval_t start = chVTGetSystemTime();
gpsFix_t gpsFix;
@@ -62,7 +60,7 @@ static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp, sysinterval_t time
bool status = GPS_Init();
if(status) {
- // Search for lock as long enough power is available
+ // Search for lock as long as enough power is available
do {
batt = stm32_get_vbat();
gps_get_fix(&gpsFix);
@@ -185,7 +183,7 @@ static void getSensors(dataPoint_t* tp)
tp->sen_e1_press = 0;
tp->sen_e1_hum = 0;
tp->sen_e1_temp = 0;
- bme280_error |= 0x2;
+ bme280_error |= 0x4;
}
// External BME280 Sensor 2
@@ -199,8 +197,11 @@ static void getSensors(dataPoint_t* tp)
tp->sen_e2_press = 0;
tp->sen_e2_hum = 0;
tp->sen_e2_temp = 0;
- bme280_error |= 0x4;
+ bme280_error |= 0x10;
}
+#else
+ /* Set status to "not installed". */
+ bme280_error |= 0x28;
#endif
// Measure various temperature sensors
tp->stm32_temp = stm32_get_temp();
@@ -214,7 +215,19 @@ static void getSensors(dataPoint_t* tp)
*
*/
static void setSystemStatus(dataPoint_t* tp) {
- // Set system errors
+
+ /*
+ * Set system errors.
+ *
+ * Bit usage:
+ * - 0:1 I2C status
+ * - 2:2 GPS status
+ * - 3:4 pac1720 status
+ * - 5:7 OV5640 status
+ * - 8:9 BMEi1 status (0 = OK, 1 = Fail, 2 = Not fitted)
+ * - 9:10 BMEe1 status (0 = OK, 1 = Fail, 2 = Not fitted)
+ * - 10:11 BMEe2 status (0 = OK, 1 = Fail, 2 = Not fitted)
+ */
tp->sys_error = 0;
tp->sys_error |= (I2C_hasError() & 0x1) << 0;
@@ -222,7 +235,7 @@ static void setSystemStatus(dataPoint_t* tp) {
tp->sys_error |= (pac1720_hasError() & 0x3) << 3;
tp->sys_error |= (OV5640_hasError() & 0x7) << 5;
- tp->sys_error |= (bme280_error & 0x7) << 8;
+ tp->sys_error |= (bme280_error & 0x3F) << 8;
// Set system time
tp->sys_time = TIME_I2S(chVTGetSystemTime());
@@ -320,7 +333,7 @@ THD_FUNCTION(collectorThread, arg) {
"%s Pos %d.%05d %d.%05d Alt %dm\r\n"
"%s Sats %d TTFF %dsec\r\n"
"%s ADC Vbat=%d.%03dV Vsol=%d.%03dV Pbat=%dmW\r\n"
- "%s AIR p=%6d.%01dPa T=%2d.%02ddegC phi=%2d.%01d%%",
+ "%s AIR p=%d.%01dPa T=%d.%02ddegC phi=%d.%01d%%",
tp->id,
TRACE_TAB, time.year, time.month, time.day, time.hour, time.minute, time.day,
TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*(tp->gps_lat/100)%100000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*(tp->gps_lon/100)%100000, tp->gps_alt,
@@ -345,9 +358,9 @@ THD_FUNCTION(collectorThread, arg) {
*/
void init_data_collector(void)
{
- if(!threadStarted)
- {
- threadStarted = true;
+ if(!threadStarted) {
+
+ threadStarted = true;
TRACE_INFO("COLL > Startup data collector thread");
thread_t *th = chThdCreateFromHeap(NULL,
diff --git a/tracker/software/threads/rxtx/image.c b/tracker/software/threads/rxtx/image.c
index aec2804c..ac9d4fe6 100644
--- a/tracker/software/threads/rxtx/image.c
+++ b/tracker/software/threads/rxtx/image.c
@@ -648,14 +648,14 @@ THD_FUNCTION(imgThread, arg) {
chThdSleep(TIME_S2I(60));
continue;
}
-
+ uint32_t my_image_id = gimage_id++;
/* Create image capture buffer. */
uint8_t *buffer = chHeapAllocAligned(NULL, conf->buf_size,
DMA_FIFO_BURST_ALIGN);
if(buffer == NULL) {
/* Could not get a capture buffer. */
TRACE_WARN("IMG > Unable to get capture buffer for image %i",
- gimage_id);
+ my_image_id);
/* Allow time for other threads. */
chThdSleep(TIME_MS2I(10));
/* Try again at next run time. */
@@ -668,11 +668,11 @@ THD_FUNCTION(imgThread, arg) {
/* Nothing captured? */
if(size_sampled == 0) {
TRACE_INFO("IMG > Encode/Transmit SSDV (camera error) ID=%d",
- gimage_id);
+ my_image_id);
if(!transmit_image_packets(noCameraFound, sizeof(noCameraFound),
- conf, (uint8_t)(gimage_id))) {
+ conf, (uint8_t)(my_image_id))) {
TRACE_ERROR("IMG > Error in encoding dummy image %i"
- " - discarded", gimage_id);
+ " - discarded", my_image_id);
}
/* Return the buffer to the heap. */
chHeapFree(buffer);
@@ -699,7 +699,7 @@ THD_FUNCTION(imgThread, arg) {
chsnprintf(filename, sizeof(filename), "r%02xi%04x.jpg",
getLastDataPoint()->reset % 0xFF,
- (gimage_id - 1) % 0xFFFF);
+ (my_image_id) % 0xFFFF);
writeBufferToFile(filename, &buffer[soi], size_sampled - soi);
} /* End initSD() */
@@ -710,13 +710,12 @@ THD_FUNCTION(imgThread, arg) {
}
/* Encode and transmit picture. */
- TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id);
+ TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", my_image_id);
if(!transmit_image_packets(buffer, size_sampled, conf,
- (uint8_t)(gimage_id))) {
+ (uint8_t)(my_image_id))) {
TRACE_ERROR("IMG > Error in encoding snapshot image"
- " %i - discarded", gimage_id);
+ " %i - discarded", my_image_id);
}
- gimage_id++;
break;
} /* End if SOI in buffer. */
} /* End while soi < size_sampled - 1. */
diff --git a/tracker/software/threads/rxtx/position.c b/tracker/software/threads/rxtx/position.c
index aed5221e..1bdf138e 100644
--- a/tracker/software/threads/rxtx/position.c
+++ b/tracker/software/threads/rxtx/position.c
@@ -61,17 +61,23 @@ THD_FUNCTION(posThread, arg)
chThdSleep(TIME_S2I(5));
}
- // Encode/Transmit APRSD packet
/*
- * This is a tracker originated message (not a reply to an incoming).
- * The message will be sent to the base station if set.
+ * Encode/Transmit APRSD packet.
+ * This is a tracker originated message (not a reply to a request).
+ * The message will be sent to the base station set in path.
*/
if(conf_sram.aprs.base.enabled) {
- packet = aprs_encode_query_answer_aprsd(conf->call, /* from */
- conf_sram.aprs.base.path, /* via */
- conf_sram.aprs.base.call); /* to */
+ /*
+ * Send message from this device.
+ * Use call sign and path as specified in base config.
+ * There is no acknowledgment requested.
+ */
+ packet = aprs_compose_aprsd_message(
+ APRS_DEVICE_CALLSIGN,
+ conf_sram.aprs.base.path,
+ conf_sram.aprs.base.call);
if(packet == NULL) {
- TRACE_WARN("POS > No free packet objects for "
+ TRACE_WARN("POS > No free packet objects "
"or badly formed APRSD message");
} else {
if(!transmitOnRadio(packet,
@@ -87,6 +93,7 @@ THD_FUNCTION(posThread, arg)
chThdSleep(TIME_S2I(5));
}
} else {
+ /* TODO: Implement a fallback destination if no base station set? */
TRACE_INFO("POS > APRSD data not sent - no base station specified");
}
diff --git a/tracker/software/threads/rxtx/radio.c b/tracker/software/threads/rxtx/radio.c
index 61a01b14..403dfddd 100644
--- a/tracker/software/threads/rxtx/radio.c
+++ b/tracker/software/threads/rxtx/radio.c
@@ -69,11 +69,12 @@ void mapCallback(pkt_data_object_t *pkt_buff) {
pktDiagnosticOutput(pkt_buff->handler, pkt_buff);
#endif
*/
-if(pktIsBufferValidAX25Frame(pkt_buff)) {
+if(pktGetAX25FrameStatus(pkt_buff)) {
+
/* Perform the callback. */
processPacket(frame_buffer, frame_size);
} else {
- TRACE_INFO("RX > Invalid frame - dropped");
+ TRACE_INFO("RX > Frame has bad CRC - dropped");
}
}
diff --git a/tracker/software/types.h b/tracker/software/types.h
index f85c1938..1c931f46 100644
--- a/tracker/software/types.h
+++ b/tracker/software/types.h
@@ -128,6 +128,10 @@ typedef struct {
uint16_t symbol;
uint8_t rssi; // Squelch for CCA check
bool enabled;
+ bool fixed;
+ int32_t lat;
+ int32_t lon;
+ int32_t alt;
} thd_tx_conf_t;