#include #include #include #include "pico/stdlib.h" #include "pico/binary_info.h" #include "LoRa-RP2040.h" #include "Config.h" #include "KISS.h" bool startRadio(); bool LoadSettings(); void getPacketData(int packetLength); int compare_strings(uint8_t a[], uint8_t b[]); bool is_message_for_me (uint8_t data[], uint8_t mycall[]); /* declaration for receive functions */ uint16_t decode_packet (); /* declaration for transmit functions */ void ComposeAprsFrame(uint8_t payload[]); bool TransmitRequest = false; void transmit(); const uint PowerSupply24VControl = 6; const uint PowerSupply12VControl = 5; const uint PowerSupply5VControl = 4; const uint RelayOffControl = 2; const uint RelayOnControl = 3; int main() { uint16_t ServerCommand = 0; uint16_t TxDelay = 0; /* Among others, this initializes the USB-serial port at 115200bps 8N1 */ stdio_init_all(); // Buffers memset(rxBuffer, 0, sizeof(rxBuffer)); memset(txBuffer, 0, sizeof(txBuffer)); gpio_init(PowerSupply24VControl); gpio_init(PowerSupply12VControl); gpio_init(PowerSupply5VControl); gpio_init(RelayOffControl); gpio_init(RelayOnControl); gpio_set_dir(PowerSupply24VControl, GPIO_OUT); gpio_set_dir(PowerSupply12VControl, GPIO_OUT); gpio_set_dir(PowerSupply5VControl, GPIO_OUT); gpio_set_dir(RelayOffControl, GPIO_OUT); gpio_set_dir(RelayOnControl, GPIO_OUT); gpio_put(PowerSupply24VControl, 0); Status.PowerSupply24V = OFF; gpio_put(PowerSupply12VControl, 0); Status.PowerSupply12V = OFF; gpio_put(PowerSupply5VControl, 1); Status.PowerSupply5V = OFF; gpio_put(RelayOffControl, 1); sleep_ms(250); gpio_put(RelayOffControl, 0); gpio_put(RelayOnControl, 0); Status.ControlRelay = OFF; sleep_ms(5000); LoadSettings(); startRadio(); while (1) { int packetSize = LoRa.parsePacket(); if (packetSize) { // received a packet printf("Received packet (RSSI = %idBm)\n",LoRa.packetRssi()); getPacketData(packetSize); // Check APRS header: must be 0x3C 0xFF 0x01 if (rxBuffer[0] == 0x3C && rxBuffer[1] == 0xFF && rxBuffer[2] == 0x01 ) { // Shift array three places to left (= remove APRS header) for (int cnt = 3; cnt < packetSize; cnt++) { rxBuffer[cnt-3] = rxBuffer[cnt]; } rxBuffer[packetSize-3] = 0; printf("%s\n", rxBuffer); ServerCommand = decode_packet(); } else { printf("ERROR: No or corrupted APRS frame.\n"); } } if (ServerCommand) { switch(ServerCommand) { case 1 : ComposeAprsFrame(AprsSettings.FirmwareVersion); break; // Send status of output pins case 6 : if (Status.PowerSupply24V == ON) Status.StatusString[4] = '1'; else Status.StatusString[4] = '0'; if (Status.PowerSupply12V == ON) Status.StatusString[3] = '1'; else Status.StatusString[3] = '0'; if (Status.PowerSupply5V == ON) Status.StatusString[2] = '1'; else Status.StatusString[2] = '0'; if (Status.ControlRelay == ON) Status.StatusString[1] = '1'; else Status.StatusString[1] = '0'; ComposeAprsFrame(Status.StatusString); break; // Switch off 24V power supply case 30 : gpio_put(PowerSupply24VControl, 0); Status.PowerSupply24V = OFF; break; // Switch on 24V power supply case 31 : gpio_put(PowerSupply24VControl, 1); Status.PowerSupply24V = ON; break; // Switch off 12V power supply case 32 : gpio_put(PowerSupply12VControl, 0); Status.PowerSupply12V = OFF; break; // Switch on 12V power supply case 33 : gpio_put(PowerSupply12VControl, 1); Status.PowerSupply12V = ON; break; // Switch off 5V power supply case 34 : gpio_put(PowerSupply5VControl, 1); Status.PowerSupply5V = OFF; break; // Switch on 5V power supply case 35 : gpio_put(PowerSupply5VControl, 0); Status.PowerSupply5V = ON; break; // Switch off relay case 36 : gpio_put(RelayOffControl, 1); sleep_ms(250); gpio_put(RelayOffControl, 0); Status.ControlRelay = OFF; break; // Switch on 24V relay case 37 : gpio_put(RelayOnControl, 1); sleep_ms(250); gpio_put(RelayOnControl, 0); Status.ControlRelay = ON; break; default : break; } ServerCommand = 0; } /* A message is ready to be send */ if (TransmitRequest) { if ( TxDelay == 0 ) { // Generate pseudo random value between 0-1024 TxDelay = time_us_32()&0x3FF; } /* If TxDelay times out: send message */ if ( TxDelay-- == 1 ) { transmit(); TransmitRequest = false; } } } return 0; } /* * Load settings from EEPROM */ bool LoadSettings() { printf("APRS settings:\n"); printf("My call: %s\n", AprsSettings.MyCall); printf("Server call: %s\n", AprsSettings.ServerCall); } /* * Initializes the LoRa module with the parameters set in config.h */ bool startRadio() { // override the default CS, reset, and IRQ pins (optional) // LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin printf("LoRa settings:\n"); printf("loraFrequency = %u\n", loraFrequency); printf("loraSpreadingFactor = %i\n", loraSpreadingFactor); printf("loraPreamble = %i\n", loraPreamble); printf("loraCodingRate = %i\n", loraCodingRate); printf("loraTxPower = %i\n", loraTxPower); printf("LoRaPaSelect = %i\n", LoRaPaSelect); printf("loraBandwidth = %u\n", loraBandwidth); printf("Starting LoRa radio"); if (!LoRa.begin(loraFrequency)) { printf(" [ FAILED ]\n"); while(1); } else { LoRa.setPreambleLength(loraPreamble); LoRa.setSignalBandwidth(loraBandwidth); LoRa.setTxPower(loraTxPower,LoRaPaSelect); LoRa.setSpreadingFactor(loraSpreadingFactor); LoRa.setCodingRate4(loraCodingRate); LoRa.enableCrc(); printf(" [ DONE ]\n"); } } void getPacketData(int packetLength) { int position = 0; while (packetLength--) { rxBuffer[position++] = LoRa.read(); } rxBuffer[position] = 0; } /* Encode LoRa APRS frame: extract source call, digipeater path and data field. At the same time, check for data corruption * * If frame is a message from the server it returns the command from this server */ uint16_t decode_packet () { int position = 0; int cnt = 0; int number_of_digipeaters = 0; char valid_apsr_data = false; uint8_t aprs_source_address[10]; uint8_t aprs_digi_path[255]; uint8_t aprs_data_field[255]; uint8_t aprs_message[255]; uint8_t aprs_digis[10][10]; uint8_t aprs_acknowledge_number[255]; bool aprs_acknowledge_request = false; uint16_t aprs_server_command = 0; memset(aprs_source_address, 0, sizeof(aprs_source_address)); memset(aprs_digi_path, 0, sizeof(aprs_digi_path)); memset(aprs_data_field, 0, sizeof(aprs_data_field)); memset(aprs_message, 0, sizeof(aprs_message)); memset(aprs_digis, 0, sizeof(aprs_digis)); memset(aprs_acknowledge_number, 0, sizeof(aprs_acknowledge_number)); // Extract from address cnt = 0; while( rxBuffer[position] != 0 ) { aprs_source_address[cnt++] = rxBuffer[position]; if ( rxBuffer[position] == '>' && position < 10 ) { aprs_source_address[cnt-1] = 0; valid_apsr_data = true; break; } position++; } position++; if (valid_apsr_data == true) { // Extract digi path valid_apsr_data = false; cnt = 0; while( rxBuffer[position] != 0 ) { aprs_digi_path[cnt++] = rxBuffer[position]; if ( rxBuffer[position] == ':' ) { aprs_digi_path[cnt-1] = 0; valid_apsr_data = true; break; } position++; } } position++; if (valid_apsr_data == true) { // Extract data field cnt = 0; while( rxBuffer[position] != 0 ) { aprs_data_field[cnt++] = rxBuffer[position]; position++; } aprs_data_field[cnt] = 0; } if (valid_apsr_data == true) { // Extract digis from digi-path cnt = 0; position = 0; number_of_digipeaters = 0; while( aprs_digi_path[position] != 0 ) { aprs_digis[number_of_digipeaters][cnt++] = aprs_digi_path[position]; if ( aprs_digi_path[position] == ',' && cnt < 10 ) { aprs_digis[number_of_digipeaters][cnt-1] = 0; if (++number_of_digipeaters > 9) { valid_apsr_data = false; break; } //position++; cnt = 0; } position++; } aprs_digis[number_of_digipeaters][cnt] = 0; } if (valid_apsr_data) { // Check if packet comes from our server and if so, check if it is a message for us. if ( !compare_strings(aprs_source_address, AprsSettings.ServerCall) ) { if ( is_message_for_me(aprs_data_field, AprsSettings.MyCall) ) { // Extract aprs message from data field position=11; while( aprs_data_field[position] != 0 ) { aprs_message[position-11] = aprs_data_field[position]; position++; } // Extract command and acknowledge number (if present) cnt = 0; position = 0; while( aprs_message[position] != 0 ) { if ( aprs_message[position] == '{' ) { aprs_acknowledge_number[cnt++] = 'a'; aprs_acknowledge_number[cnt++] = 'c'; aprs_acknowledge_number[cnt++] = 'k'; aprs_acknowledge_request = true; } // Calculate server command if (!aprs_acknowledge_request) { aprs_server_command = 10*aprs_server_command + aprs_message[position]-48; } position++; if (aprs_acknowledge_request) { aprs_acknowledge_number[cnt++] = aprs_message[position]; } } aprs_acknowledge_number[cnt] = 0; } } printf("Source address: %s\nDigipeaters (%u): %s %s %s %s\nData: %s\n", aprs_source_address, number_of_digipeaters+1, aprs_digis[0], aprs_digis[1], aprs_digis[2], aprs_digis[3], aprs_data_field); if (aprs_message[0]) { printf("Message from server: %s (command %u)\n", aprs_message, aprs_server_command); if (aprs_acknowledge_request) { ComposeAprsFrame(aprs_acknowledge_number); printf("Acknowledge request: %s\n", aprs_acknowledge_number); } } } else printf("Error decoding APRS frame."); return (aprs_server_command); } /* * Checks if aprs datafield contains message and if message is for us * * Returns: 0 if datafield contains no message for us * 1 if datafield contains a message for us */ bool is_message_for_me (uint8_t data[], uint8_t mycall[]) { // A variable to iterate through the strings int i=0; if (data[0] == ':' && data[10] == ':') { while( i<9 && mycall[i] != 0 ) { if (data[i+1] != mycall[i]) { return (0); } i++; } return (1); } return (0); } int compare_strings(uint8_t a[], uint8_t b[]) { // A variable to iterate through the strings int i = 0; while (a[i] == b[i]) { // If either of the strings reaches the end // we stop the loop if (a[i] == '\0' || b[i] == '\0') break; i++; } // We check if both the strings have been compared // till the end or not // If the strings are compared till the end they are equal if (a[i] == '\0' && b[i] == '\0') return 0; else { if(a[i] == '\0') return -1*(b[i]); else if(b[i] == '\0') return a[i]; else return (a[i]-b[i]); } } void ComposeAprsFrame(uint8_t payload[]) { uint16_t BufferPosition = 0; uint16_t cnt = 0; memset(txBuffer, 0, sizeof(txBuffer)); // APRS header txBuffer[BufferPosition++] = '<'; txBuffer[BufferPosition++] = 0xff; txBuffer[BufferPosition++] = 0x01; while ( AprsSettings.MyCall[cnt] != 0 && BufferPosition