Fully functioning KISS TNC
This commit is contained in:
@@ -28,8 +28,12 @@ First (more or less) working version.
|
||||
### Added
|
||||
- All settings (LoRa and APRS) can now be saved to FLASH.
|
||||
- Command added for restarting LoRa radio when settings are alterred: "restart lora"
|
||||
- Received LoRa frames can now be converted to propper AX25 frames (needed for KISS TNC functionality)
|
||||
- Received LoRa frames can now be converted to proper AX25 frames (needed for KISS TNC functionality)
|
||||
- KISS mode works for receiving (LoRa -> USB port)
|
||||
- KISS mode for transmitting in early phase of development
|
||||
- Can enter KISS mode with command "kiss 1" and exit KISS mode with command "sudo kissparms -p ax0 -x"
|
||||
|
||||
## [1.0.4] - 2022-05-13
|
||||
|
||||
### Added
|
||||
- Fully functioning KISS TNC, but repeater flag could not be tested because LoRa APRS software in general is a mess. It appears that nobody reads the AX.25 standard anymore. This software probably is also not up to spec. But at least I tried! You should too: [http://www.ax25.net/AX25.2.2-Jul%2098-2.pdf](http://www.ax25.net/AX25.2.2-Jul%2098-2.pdf)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
52465
build/src/main.dis
52465
build/src/main.dis
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
8548
build/src/main.hex
8548
build/src/main.hex
File diff suppressed because it is too large
Load Diff
Binary file not shown.
232
src/kiss.cpp
232
src/kiss.cpp
@@ -4,7 +4,7 @@
|
||||
* KISS encoder uses two steps:
|
||||
*
|
||||
* Step 1: encodes the LoRa APRS frame into an AX.25 frame. DONE
|
||||
* Step 2: encapsulates the AX.25 frame into a KISS frame. TODO
|
||||
* Step 2: encapsulates the AX.25 frame into a KISS frame. DONE
|
||||
*
|
||||
* struct aprs_frame {
|
||||
* uint8_t source_address[10];
|
||||
@@ -27,6 +27,9 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
uint16_t cnt=0;
|
||||
uint16_t position = 0;
|
||||
uint8_t digi_cnt = 0;
|
||||
uint8_t escaped_string_cnt=0;
|
||||
|
||||
//printf("Encode KISS");
|
||||
|
||||
// Destination call
|
||||
//printf("Destination: ");
|
||||
@@ -35,7 +38,7 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
while (cnt < 7)
|
||||
{
|
||||
ax25frame->complete[position] = *(encoded_call+cnt);
|
||||
////printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", ax25frame->complete[position]);
|
||||
cnt++;
|
||||
position++;
|
||||
@@ -49,7 +52,7 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
while (cnt < 7)
|
||||
{
|
||||
ax25frame->complete[position] = *(encoded_call+cnt);
|
||||
////printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", ax25frame->complete[position]);
|
||||
cnt++;
|
||||
position++;
|
||||
@@ -73,7 +76,7 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
encoded_call = EncodeCall(aprsframe->digis[digi_cnt]);
|
||||
while (cnt < 7) {
|
||||
ax25frame->complete[position] = *(encoded_call+cnt);
|
||||
////printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", *(encoded_call+cnt));
|
||||
//printf("0x%X ", ax25frame->complete[position]);
|
||||
cnt++;
|
||||
position++;
|
||||
@@ -109,6 +112,9 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
// Encapsulate AX.25 frame in KISS frame (including escaping FEND codes)
|
||||
putchar(FEND);
|
||||
putchar(CMD_DATA);
|
||||
escaped_string_cnt=0;
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND;
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = CMD_DATA;
|
||||
cnt=0;
|
||||
position=ax25frame->lenght;
|
||||
while (position-- != 0)
|
||||
@@ -117,15 +123,21 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
if (ax25frame->complete[cnt] == FEND) {
|
||||
putchar(FESC);
|
||||
putchar(TFEND);
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC;
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFEND;
|
||||
} else if (ax25frame->complete[cnt] == FESC) {
|
||||
putchar(FESC);
|
||||
putchar(TFESC);
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC;
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFESC;
|
||||
} else {
|
||||
putchar(ax25frame->complete[cnt]);
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = ax25frame->complete[cnt];
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
putchar(FEND);
|
||||
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND;
|
||||
//printf("\n");
|
||||
|
||||
|
||||
@@ -136,13 +148,21 @@ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame
|
||||
* Decodes a KISS frame from the usb serial port
|
||||
*
|
||||
* Input : string starting with FEND and ending with FEND
|
||||
* Output: ax25frame->decoded_kiss_frame terminated with NULL
|
||||
* Output: filled struct AprsFrame
|
||||
* Return: 0 = OK, 1 = ERROR, 2 = EXIT KISS MODE
|
||||
*/
|
||||
uint16_t KissClass::DecodeFrame(uint8_t string[], struct ax25_frame *ax25frame)
|
||||
uint16_t KissClass::DecodeFrame(uint8_t string[], struct kiss_tx_frame *kisstxframe)
|
||||
{
|
||||
uint16_t position =0 ;
|
||||
uint16_t loop_counter = 0;
|
||||
uint16_t cnt =0 ;
|
||||
uint16_t ssid = 0;
|
||||
uint16_t last_digi = 0;
|
||||
uint16_t digi_cnt = 0;
|
||||
|
||||
//printf("Decode KISS");
|
||||
|
||||
kisstxframe->valid_data = false;
|
||||
|
||||
// Not a valid frame
|
||||
if (string[position] != FEND)
|
||||
@@ -182,6 +202,176 @@ uint16_t KissClass::DecodeFrame(uint8_t string[], struct ax25_frame *ax25frame)
|
||||
}
|
||||
|
||||
// If we are here we have a valid AX.25 frame in 'string' which is decapsulated from its KISS frame
|
||||
/*
|
||||
0-5 destination
|
||||
6 destination ssid
|
||||
7-12 source
|
||||
13 desitination sidd
|
||||
|
||||
vanaf byte 13 LAST FLAG zoeken
|
||||
|
||||
03
|
||||
f0
|
||||
payload tot aan NULL
|
||||
*/
|
||||
|
||||
// Decode destination call
|
||||
position = 0;
|
||||
cnt = 0;
|
||||
loop_counter = 0;
|
||||
while (loop_counter < 6)
|
||||
{
|
||||
kisstxframe->digis[0][cnt] = string[position] >> 1;
|
||||
// Remove space
|
||||
if (kisstxframe->digis[0][cnt] == ' ')
|
||||
cnt--;
|
||||
position++;
|
||||
loop_counter++;
|
||||
cnt++;
|
||||
//printf("%u ", position);
|
||||
}
|
||||
|
||||
// Extract SSID
|
||||
//printf("position: %u\n", position);
|
||||
ssid = (string[position] & 0b00011110) >> 1;
|
||||
//printf("DIGI SSID: %u (%u)\n", ssid, position);
|
||||
|
||||
// Place SSID behind call or not if it is zero
|
||||
if (ssid != 0) {
|
||||
kisstxframe->digis[0][cnt++] = '-';
|
||||
if (ssid < 10)
|
||||
{
|
||||
kisstxframe->digis[0][cnt++] = ssid + 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
kisstxframe->digis[0][cnt++] = '1';
|
||||
kisstxframe->digis[0][cnt++] = (ssid%10)+48;
|
||||
}
|
||||
}
|
||||
// Is has-been-repeated flag set?
|
||||
if(string[position] & 0b10000000) {
|
||||
kisstxframe->digis[0][cnt] = '*';
|
||||
cnt++;
|
||||
}
|
||||
kisstxframe->digis[0][cnt] = 0; //terminate string
|
||||
position++;
|
||||
|
||||
// Decode source call
|
||||
cnt = 0;
|
||||
loop_counter=0;
|
||||
while (loop_counter < 6)
|
||||
{
|
||||
kisstxframe->source_address[cnt] = string[position] >> 1;
|
||||
// Remove space
|
||||
if (kisstxframe->source_address[cnt] == ' ')
|
||||
cnt--;
|
||||
position++;
|
||||
loop_counter++;
|
||||
cnt++;
|
||||
//printf("%u ", position);
|
||||
}
|
||||
|
||||
// Extract SSID
|
||||
ssid = (string[position] & 0b00011110) >> 1;
|
||||
//printf("sources SSID: %u (%u)\n", ssid, position);
|
||||
|
||||
// Place SSID behind call or not if it is zero
|
||||
if (ssid != 0) {
|
||||
kisstxframe->source_address[cnt++] = '-';
|
||||
if (ssid < 10)
|
||||
{
|
||||
kisstxframe->source_address[cnt++] = ssid + 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
kisstxframe->source_address[cnt++] = '1';
|
||||
kisstxframe->source_address[cnt++] = (ssid%10)+48;
|
||||
}
|
||||
}
|
||||
// Is has-been-repeated flag set?
|
||||
if(string[position] & 0b10000000) {
|
||||
kisstxframe->source_address[cnt] = '*';
|
||||
cnt++;
|
||||
}
|
||||
kisstxframe->source_address[cnt] = 0; //terminate string
|
||||
|
||||
// Check LAST flag
|
||||
if ( (string[position] & 0b00000001))
|
||||
last_digi = 1;
|
||||
|
||||
position++;
|
||||
|
||||
// Decode digi path
|
||||
digi_cnt=1;
|
||||
while (last_digi == 0) {
|
||||
|
||||
cnt = 0;
|
||||
loop_counter=0;
|
||||
while (loop_counter < 6)
|
||||
{
|
||||
kisstxframe->digis[digi_cnt][cnt] = string[position] >> 1;
|
||||
// Remove space
|
||||
if (kisstxframe->digis[digi_cnt][cnt] == ' ')
|
||||
cnt--;
|
||||
position++;
|
||||
loop_counter++;
|
||||
cnt++;
|
||||
//printf("%u ", position);
|
||||
}
|
||||
|
||||
// Extract SSID
|
||||
ssid = (string[position] & 0b00011110) >> 1;
|
||||
//printf("DIGI SSID: %u (%u)\n", ssid, position);
|
||||
|
||||
// Place SSID behind call or not if it is zero
|
||||
if (ssid != 0) {
|
||||
kisstxframe->digis[digi_cnt][cnt++] = '-';
|
||||
if (ssid < 10)
|
||||
{
|
||||
kisstxframe->digis[digi_cnt][cnt++] = ssid + 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
kisstxframe->digis[digi_cnt][cnt++] = '1';
|
||||
kisstxframe->digis[digi_cnt][cnt++] = (ssid%10) + 48;
|
||||
}
|
||||
}
|
||||
|
||||
// Is has-been-repeated flag set?
|
||||
if(string[position] & 0b10000000) {
|
||||
kisstxframe->digis[digi_cnt][cnt] = '*';
|
||||
cnt++;
|
||||
}
|
||||
kisstxframe->digis[digi_cnt][cnt] = 0; //terminate string
|
||||
|
||||
// Check LAST flag (also ends when more than 10 digis)
|
||||
if ( (string[position] & 0b00000001) || digi_cnt > 8)
|
||||
last_digi = 1;
|
||||
|
||||
digi_cnt++;
|
||||
position++;
|
||||
}
|
||||
kisstxframe->number_of_digipeaters = digi_cnt-1;
|
||||
//printf("Digipeaters: %u \n",kisstxframe->number_of_digipeaters );
|
||||
|
||||
//Skip the two control fields
|
||||
position++;
|
||||
position++;
|
||||
|
||||
//Rest of string up to NULL is payload
|
||||
cnt = 0;
|
||||
kisstxframe->data_field[cnt++] = ':';
|
||||
while (string[position] != 0 && position < 512)
|
||||
{
|
||||
kisstxframe->data_field[cnt] = string[position];
|
||||
position++;
|
||||
cnt++;
|
||||
}
|
||||
kisstxframe->data_field[cnt] = 0; //terminate string
|
||||
|
||||
kisstxframe->valid_data = true;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -197,10 +387,26 @@ uint8_t * KissClass::EncodeCall(uint8_t string[])
|
||||
{
|
||||
uint8_t position = 0;
|
||||
uint8_t cnt = 0;
|
||||
uint8_t repeat_flag = 0;
|
||||
|
||||
static uint8_t call[7] = { 0,0,0,0,0,0,0} ;
|
||||
uint8_t ssid = 0;
|
||||
|
||||
// if asterix is pressent in string, than the message has been repeated, so set the 'has been repeated' flag in the ssid register
|
||||
// At the same time delete the asterix and place a NULL (string terminator) at its place. As the asterix should be the last character in the string, this should work.
|
||||
position = 0;
|
||||
while( string[position] != 0)
|
||||
{
|
||||
if (string[position] == '*') {
|
||||
repeat_flag = 1;
|
||||
string[position] == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
position = 0;
|
||||
// extract call
|
||||
while( string[position] != 0 || cnt < 6)
|
||||
{
|
||||
@@ -229,17 +435,11 @@ uint8_t * KissClass::EncodeCall(uint8_t string[])
|
||||
}
|
||||
}
|
||||
|
||||
// if asterix is pressent in string, than the message has been repeated, so set the 'has been repeated' flag in the ssid register
|
||||
position = 0;
|
||||
while( string[position] != 0)
|
||||
{
|
||||
if (string[position] == '*')
|
||||
ssid = 0b10000000;
|
||||
|
||||
position++;
|
||||
}
|
||||
ssid = ssid << 1;
|
||||
ssid |= 0b01100000;
|
||||
|
||||
ssid = (ssid << 1) | 0b01100000;
|
||||
if (repeat_flag == 1)
|
||||
ssid |= 0b10000000;
|
||||
|
||||
// add encoded ssid to encoded call array
|
||||
call[6] = ssid;
|
||||
|
14
src/kiss.h
14
src/kiss.h
@@ -39,15 +39,27 @@
|
||||
struct ax25_frame {
|
||||
uint8_t complete[512];
|
||||
uint16_t lenght = 0;
|
||||
uint8_t encoded_kiss_frame[512];
|
||||
uint8_t decoded_kiss_frame[512];
|
||||
uint16_t kiss_length = 0;
|
||||
};
|
||||
|
||||
struct kiss_tx_frame {
|
||||
uint8_t source_address[10];
|
||||
uint8_t data_field[512];
|
||||
uint8_t digis[10][10];
|
||||
uint16_t number_of_digipeaters = 0;
|
||||
|
||||
uint8_t lora_string[512];
|
||||
|
||||
uint8_t valid_data = false;
|
||||
};
|
||||
|
||||
class KissClass
|
||||
{
|
||||
public:
|
||||
uint16_t EncodeFrame(struct aprs_frame *frame, struct ax25_frame *ax25frame);
|
||||
uint16_t DecodeFrame(uint8_t string[], struct ax25_frame *ax25frame);
|
||||
uint16_t DecodeFrame(uint8_t string[], struct kiss_tx_frame *kisstxframe);
|
||||
private:
|
||||
uint8_t * EncodeCall(uint8_t string[]);
|
||||
};
|
||||
|
78
src/main.cpp
78
src/main.cpp
@@ -11,7 +11,9 @@
|
||||
#include "hardware/claim.h"
|
||||
|
||||
KissClass Kiss;
|
||||
struct ax25_frame AX25Frame; //defined in kiss.h
|
||||
struct ax25_frame AX25Frame; //defined in kiss.h
|
||||
struct aprs_frame AprsFrame; //defined in kiss.h
|
||||
struct kiss_tx_frame KissTxFrame; //defined in kiss.h
|
||||
|
||||
bool startRadio();
|
||||
void getPacketData(int packetLength);
|
||||
@@ -23,6 +25,7 @@ uint16_t decode_packet ();
|
||||
|
||||
/* declaration for transmit functions */
|
||||
void ComposeAprsFrame(uint8_t payload[]);
|
||||
void ComposeAprsFrameFromKiss();
|
||||
bool TransmitRequest = false;
|
||||
void transmit();
|
||||
|
||||
@@ -433,6 +436,7 @@ void ReadUSBSerial(void)
|
||||
static char strg[512];
|
||||
int chr;
|
||||
static int lp = 0;
|
||||
uint16_t tmp;
|
||||
|
||||
if (Status.KissMode == OFF) {
|
||||
// Read serial port (USB) - non-blocking!
|
||||
@@ -472,15 +476,21 @@ void ReadUSBSerial(void)
|
||||
// Received FEND (=begin or end frame)
|
||||
if(chr == FEND)
|
||||
{
|
||||
// Valid FISS frame received
|
||||
// Valid KISS frame received
|
||||
if (strg[0] == FEND && lp > 1)
|
||||
{
|
||||
if (Kiss.DecodeFrame((uint8_t *) strg, &AX25Frame) == 2)
|
||||
tmp = Kiss.DecodeFrame((uint8_t *) strg, &KissTxFrame);
|
||||
if ( tmp == 2)
|
||||
{
|
||||
//exit KISS MODE
|
||||
stdio_set_translate_crlf(&stdio_usb, true);
|
||||
Status.KissMode = OFF;
|
||||
}
|
||||
// Valid KISS data frame, so lets send it
|
||||
else if (tmp == 0)
|
||||
{
|
||||
ComposeAprsFrameFromKiss();
|
||||
}
|
||||
lp = 0; //reset string buffer pointer
|
||||
}
|
||||
// We received a FEND byte,so we are probably between two KISS frames. Let's assume the latest FEND is the beginning of a new frame
|
||||
@@ -682,8 +692,6 @@ uint16_t decode_packet ()
|
||||
int position = 0;
|
||||
int cnt = 0;
|
||||
|
||||
struct aprs_frame AprsFrame; //defined in kiss.h
|
||||
|
||||
memset(AprsFrame.source_address, 0, sizeof(AprsFrame.source_address));
|
||||
memset(AprsFrame.digi_path, 0, sizeof(AprsFrame.digi_path));
|
||||
memset(AprsFrame.data_field, 0, sizeof(AprsFrame.data_field));
|
||||
@@ -809,8 +817,11 @@ uint16_t decode_packet ()
|
||||
log_out("Source address: %s\nDigipeaters (%u): %s %s %s %s\nData: %s\n", AprsFrame.source_address, AprsFrame.number_of_digipeaters+1, AprsFrame.digis[0], AprsFrame.digis[1], AprsFrame.digis[2], AprsFrame.digis[3], AprsFrame.data_field);
|
||||
|
||||
// If in KISS mode the struct AprsFrame is handed over to the KISS encoder
|
||||
if (Status.KissMode == ON)
|
||||
if (Status.KissMode == ON) {
|
||||
Kiss.EncodeFrame(&AprsFrame, &AX25Frame);
|
||||
//if (Kiss.DecodeFrame(AX25Frame.encoded_kiss_frame, &KissTxFrame) == 0)
|
||||
//ComposeAprsFrameFromKiss();
|
||||
}
|
||||
|
||||
if (AprsFrame.message[0])
|
||||
{
|
||||
@@ -960,6 +971,61 @@ void ComposeAprsFrame(uint8_t payload[])
|
||||
log_out("%s\n", txBuffer);
|
||||
}
|
||||
|
||||
void ComposeAprsFrameFromKiss()
|
||||
{
|
||||
uint16_t BufferPosition = 0;
|
||||
uint16_t cnt = 0;
|
||||
uint8_t digi_cnt=0;
|
||||
|
||||
log_out( "Compose APRS from KISS frame");
|
||||
|
||||
memset(txBuffer, 0, sizeof(txBuffer));
|
||||
|
||||
// APRS header
|
||||
txBuffer[BufferPosition++] = '<';
|
||||
txBuffer[BufferPosition++] = 0xff;
|
||||
txBuffer[BufferPosition++] = 0x01;
|
||||
|
||||
while ( KissTxFrame.source_address[cnt] != 0 && BufferPosition<MTU )
|
||||
{
|
||||
txBuffer[BufferPosition] = KissTxFrame.source_address[cnt];
|
||||
BufferPosition++;
|
||||
cnt++;
|
||||
}
|
||||
txBuffer[BufferPosition++] = '>';
|
||||
|
||||
digi_cnt=0;
|
||||
while(digi_cnt <= KissTxFrame.number_of_digipeaters) {
|
||||
cnt=0;
|
||||
|
||||
while ( KissTxFrame.digis[digi_cnt][cnt] != 0 && BufferPosition<MTU )
|
||||
{
|
||||
txBuffer[BufferPosition++] = KissTxFrame.digis[digi_cnt][cnt];
|
||||
cnt++;
|
||||
}
|
||||
txBuffer[BufferPosition++] = ',';
|
||||
digi_cnt++;
|
||||
}
|
||||
|
||||
// Overwrite last command with colon
|
||||
txBuffer[--BufferPosition] = ':';
|
||||
|
||||
cnt=0;
|
||||
while ( KissTxFrame.data_field[cnt] != 0 && BufferPosition<MTU )
|
||||
{
|
||||
txBuffer[BufferPosition++] = KissTxFrame.data_field[cnt];
|
||||
cnt++;
|
||||
}
|
||||
|
||||
// Set variable to indicate a send request
|
||||
TransmitRequest = true;
|
||||
|
||||
// Ready for next input KISS frame.
|
||||
KissTxFrame.valid_data = false;
|
||||
|
||||
log_out("%s\n", txBuffer);
|
||||
}
|
||||
|
||||
void transmit() {
|
||||
uint16_t position = 0;
|
||||
|
||||
|
Reference in New Issue
Block a user