├── LICENSE ├── README.md ├── lora-bme280-aprs └── lora-bme280-aprs.ino ├── lora-kiss-tnc ├── Config.h ├── KISS.h ├── LoRa.cpp ├── LoRa.h └── lora-kiss-tnc.ino └── lora-kiss ├── README.md └── lora-kiss.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Josef Matondang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APRS on LoRa 2 | 3 | ## Introduction 4 | This is an attempt to create an APRS compatible system on top of LoRa Radios. For the TNC, an Arduino with a LoRa Radio module will be used, the program is based on Sandeep Mistry's Arduino LoRa library which is available from https://github.com/sandeepmistry/arduino-Lora. This repository is compatible with SX1276/77/78/79 based LoRa Radio modules. 5 | The KISS TNC on this repository can be integrated with an i-Gate software such as APRX for Linux or APRSIS-32 for Windows. 6 | 7 | ## Weather Station 8 | One of APRS application is to share Weather Station data on RF, this repository also has a simple sketch on how to send Temperature, Humidity, and Pressure via APRS format using KISS frame on LoRa. 9 | The sensor used in this sketch is a Bosch BME280 sensor that can easily be bought from Adafruit in a form of a breakout board. The library is also available from Adafruit Sensors library. 10 | You can find more information about the library on https://github.com/adafruit/Adafruit_Sensor and https://github.com/adafruit/Adafruit_BME280_Library 11 | 12 | The sensor readings will be sent every 10 minutes (the minimum amount of interval for Wx updates) and will be written in APRS format for Complete Weather Report Format with Lat/Long position, no Timestamp. 13 | 14 | ## KISS TNC 15 | Inspired by markqvist's RNode firmware, I checked his source code and made it work for ATmega328p based board. You can build a KISS TNC for LoRa by assembling a quick breadboard prototype, with e.g. Arduino Pro Mini, a Breakout board for Ai-Thinker Ra-02, and a Bluetooth module (optional). 16 | This code also supports the usage of LoRa32u4 modules by DIYmall, this board uses Ai Thinker Ra-02 module and ATmega32u4 as the microcontroller. 17 | The transmit algorithm used in this repository is copied from markqvist's source code and the receive algorithm is done by API provided by Sandeep Mistry. 18 | 19 | If you manage to build one on breadboard with a Bluetooth on-board, you can connect this KISS TNC via APRS Droid and begin using this KISS TNC as an APRS Tracker. 20 | 21 | If you are interested in a plug and play APRS LoRa, you can head over to markqvist's shop and find a ready and plug-and-play device to use: https://unsigned.io/projects/rnode 22 | -------------------------------------------------------------------------------- /lora-bme280-aprs/lora-bme280-aprs.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Adafruit_BME280 bme; 8 | 9 | unsigned long delayTime = 602000; 10 | 11 | uint8_t headerAprs[17] = {0x00, 0x82, 0xA0, 0x98, 0xA4, 0xAE, 0xB0, 0xE0, 12 | 0xB2, 0x88, 0x60, 0xA6, 0x90, 0xB2, 0x6B, 0x03, 13 | 0xF0}; 14 | //uint8_t headerAprs[17] = {0x00, 0x9C, 0x9E, 0x8E, 0x82, 0xA8, 0x8A, 0xE0, 15 | // 0xB2, 0x88, 0x60, 0xA6, 0x90, 0xB2, 0x6B, 0x03, 16 | // 0xF0}; 17 | //uint8_t posAprs[19] = {0x21, 0x30, 0x36, 0x32, 0x31, 0x2E, 0x35, 0x35, 18 | // 0x53, 0x2F, 0x31, 0x30, 0x36, 0x34, 0x30, 0x2E, 19 | // 0x34, 0x30, 0x45}; 20 | uint8_t posAprs[19] = {0x21, 0x30, 0x36, 0x31, 0x31, 0x2E, 0x37, 0x36, 21 | 0x53, 0x2F, 0x31, 0x30, 0x36, 0x34, 0x37, 0x2E, 22 | 0x32, 0x30, 0x45}; 23 | uint8_t headerCwop[14] = {0x5F, 0x2E, 0x2E, 0x2E, 0x2F, 0x2E, 0x2E, 0x2E, 24 | 0x67, 0x2E, 0x2E, 0x2E, 0x74, 0x30}; 25 | uint8_t rain[13] = {0x72, 0x2E, 0x2E, 0x2E, 0x70, 0x2E, 0x2E, 0x2E, 26 | 0x50, 0x2E, 0x2E, 0x2E, 0x68}; 27 | uint8_t barometric = 0x62; 28 | uint8_t comment[7] = {0x4C, 0x6F, 0x52, 0x61, 0x20, 0x57, 0x58}; 29 | 30 | void sendRadio(uint8_t * temp, uint16_t tempSize, uint8_t * humid, uint16_t humidSize, uint8_t * pres, uint16_t presSize) { 31 | uint8_t myBuffer[256]; 32 | uint16_t offset = 0; 33 | for (uint16_t i = 0; i < sizeof(headerAprs); i++) { 34 | myBuffer[offset++] = headerAprs[i]; 35 | } 36 | 37 | for (uint16_t j = 0; j < sizeof(posAprs); j++) { 38 | myBuffer[offset++] = posAprs[j]; 39 | } 40 | 41 | for (uint16_t k = 0; k < sizeof(headerCwop); k++) { 42 | myBuffer[offset++] = headerCwop[k]; 43 | } 44 | 45 | 46 | for (uint16_t l = 0; l < tempSize; l++) { 47 | myBuffer[offset++] = temp[l]; 48 | } 49 | 50 | for (uint16_t m = 0; m < sizeof(rain); m++) { 51 | myBuffer[offset++] = rain[m]; 52 | } 53 | 54 | for (uint16_t n = 0; n < humidSize; n++) { 55 | myBuffer[offset++] = humid[n]; 56 | } 57 | 58 | myBuffer[offset++] = barometric; 59 | 60 | for (uint16_t o = 0; o < presSize; o++) { 61 | myBuffer[offset++] = pres[o]; 62 | } 63 | 64 | for (uint16_t p = 0; p < sizeof(comment); p++) { 65 | myBuffer[offset++] = comment[p]; 66 | } 67 | Serial.write(myBuffer,offset); 68 | Serial.println(); 69 | LoRa.beginPacket(); 70 | LoRa.write(myBuffer, offset); 71 | LoRa.endPacket(); 72 | } 73 | 74 | void setup() { 75 | // put your setup code here, to run once: 76 | Serial.begin(38400); 77 | Serial.println(F("BME280 test")); 78 | 79 | bool status; 80 | 81 | LoRa.setPins(8, 4, 7); 82 | 83 | if(!LoRa.begin(434E6)) { 84 | Serial.println("LoRa Init Failed"); 85 | while(1); 86 | } 87 | if(!bme.begin()) 88 | Serial.println("Could not find a valid BME280 sensor"); 89 | 90 | Serial.println("-- Default Test --"); 91 | } 92 | 93 | void loop() { 94 | //put your main code here, to run repeatedly: 95 | //printValues(); 96 | char charTempAprs[3]; 97 | char charPresAprs[6]; 98 | char charHumidAprs[3]; 99 | uint16_t sizeTempAprs = sizeof(charTempAprs); 100 | uint16_t sizePresAprs = sizeof(charPresAprs); 101 | uint16_t sizeHumidAprs = sizeof(charHumidAprs); 102 | float tempCelsius = bme.readTemperature(); 103 | float tempFahrenheit = tempCelsius*9/5+32; 104 | int tempAprs = (int)tempFahrenheit; 105 | Serial.println(tempAprs); 106 | float humidPercentage = bme.readHumidity(); 107 | int humidAprs = (int)humidPercentage; 108 | Serial.println(humidAprs); 109 | float presPascal = bme.readPressure(); 110 | float presHectoPascal = presPascal/10; 111 | int presAprs = (int)presHectoPascal; 112 | Serial.println(presAprs); 113 | String stringPresAprs = String(presAprs); 114 | String stringTempAprs = String(tempAprs); 115 | String stringHumidAprs = String(humidAprs); 116 | stringTempAprs.toCharArray(charTempAprs, sizeTempAprs); 117 | stringPresAprs.toCharArray(charPresAprs, sizePresAprs); 118 | stringHumidAprs.toCharArray(charHumidAprs, sizeHumidAprs); 119 | sendRadio(charTempAprs, sizeTempAprs-1, charHumidAprs, sizeHumidAprs-1, charPresAprs, sizePresAprs-1); 120 | delay(delayTime); 121 | } 122 | 123 | //void printValues() { 124 | // Serial.print("Temperature = "); 125 | // Serial.print(bme.readTemperature()); 126 | // Serial.println(" *C"); 127 | // 128 | // Serial.print("Pressure = "); 129 | // Serial.print(bme.readPressure() / 100.0F); 130 | // Serial.println(" hPa"); 131 | // 132 | // Serial.print("Humidity = "); 133 | // Serial.print(bme.readHumidity()); 134 | // Serial.println(" %"); 135 | // 136 | // Serial.println(); 137 | //} 138 | 139 | -------------------------------------------------------------------------------- /lora-kiss-tnc/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define MAJ_VERS 0x01 5 | #define MIN_VERS 0x00 6 | 7 | #define MCU_328P 0x90 8 | #define MCU_32U4 0x91 9 | 10 | #if defined(__AVR_ATmega328P__) 11 | #define MCU_VARIANT MCU_328P 12 | #warning "Firmware is being compiled for ATmega328p based boards" 13 | #elif defined(__AVR_ATmega32U4__) 14 | #define MCU_VARIANT MCU_32U4 15 | #warning "Firmware is being compiled for ATmega32u4 based boards" 16 | #else 17 | #error "The firmware cannot be compiled for the selected MCU variant" 18 | #endif 19 | 20 | #define MTU 255 21 | #define CMD_L 4 22 | int lastRssi = -292; 23 | uint8_t lastRssiRaw = 0x00; 24 | size_t readLength = 0; 25 | 26 | #if MCU_VARIANT == MCU_328P 27 | const int pinNSS = 10; 28 | const int pinNRST = 9; 29 | const int pinDIO0 = 2; 30 | // const int pinLedRx = 5; 31 | // const int pinLedTx = 4; 32 | 33 | #endif 34 | 35 | #if MCU_VARIANT == MCU_32U4 36 | const int pinNSS = 8; 37 | const int pinNRST = 4; 38 | const int pinDIO0 = 7; 39 | #endif 40 | 41 | const long serialBaudRate = 38400; 42 | const int rssiOffset = 292; 43 | 44 | const int loraRxTurnaround = 50; 45 | 46 | // Default LoRa settings 47 | int loraSpreadingFactor = 9; 48 | int loraCodingRate = 5; 49 | int loraTxPower = 17; 50 | uint32_t loraBandwidth = 125E6; 51 | uint32_t loraFrequency = 434E6; 52 | 53 | uint8_t txBuffer[MTU]; 54 | uint8_t rxBuffer[MTU]; 55 | 56 | uint32_t statRx = 0; 57 | uint32_t statTx = 0; 58 | 59 | bool outboundReady = false; 60 | 61 | bool statSignalDetected = false; 62 | bool statSignalSynced = false; 63 | bool statRxOngoing = false; 64 | bool dcd = false; 65 | bool dcdLed = false; 66 | bool dcdWaiting = false; 67 | uint16_t dcdCount = 0; 68 | uint16_t dcdThreshold = 15; 69 | 70 | uint32_t statusIntervalms = 3; 71 | uint32_t lastStatusUpdate = 0; 72 | 73 | // Status flags 74 | const uint8_t SIG_DETECT = 0x01; 75 | const uint8_t SIG_SYNCED = 0x02; 76 | const uint8_t RX_ONGOING = 0x04; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /lora-kiss-tnc/KISS.h: -------------------------------------------------------------------------------- 1 | #ifndef KISS_H 2 | #define KISS_H 3 | 4 | #define FEND 0xC0 5 | #define FESC 0xDB 6 | #define TFEND 0xDC 7 | #define TFESC 0xDD 8 | 9 | #define CMD_UNKNOWN 0xFE 10 | #define CMD_DATA 0x00 11 | #define CMD_HARDWARE 0x06 12 | 13 | #define HW_RSSI 0x21 14 | 15 | #define CMD_ERROR 0x90 16 | #define ERROR_INITRADIO 0x01 17 | #define ERROR_TXFAILED 0x02 18 | #define ERROR_QUEUE_FULL 0x04 19 | 20 | size_t frameLength; 21 | bool inFrame = false; 22 | bool escape = false; 23 | bool SERIAL_READING = false; 24 | uint8_t command = CMD_UNKNOWN; 25 | uint32_t lastSerialRead = 0; 26 | uint32_t serialReadTimeout = 25; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /lora-kiss-tnc/LoRa.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | // Modifications and additions copyright 2018 by Mark Qvist 5 | // Obviously still under the MIT license. 6 | 7 | #include "LoRa.h" 8 | 9 | // Registers 10 | #define REG_FIFO 0x00 11 | #define REG_OP_MODE 0x01 12 | #define REG_FRF_MSB 0x06 13 | #define REG_FRF_MID 0x07 14 | #define REG_FRF_LSB 0x08 15 | #define REG_PA_CONFIG 0x09 16 | #define REG_LNA 0x0c 17 | #define REG_FIFO_ADDR_PTR 0x0d 18 | #define REG_FIFO_TX_BASE_ADDR 0x0e 19 | #define REG_FIFO_RX_BASE_ADDR 0x0f 20 | #define REG_FIFO_RX_CURRENT_ADDR 0x10 21 | #define REG_IRQ_FLAGS 0x12 22 | #define REG_RX_NB_BYTES 0x13 23 | #define REG_MODEM_STAT 0x18 24 | #define REG_PKT_SNR_VALUE 0x19 25 | #define REG_PKT_RSSI_VALUE 0x1a 26 | #define REG_MODEM_CONFIG_1 0x1d 27 | #define REG_MODEM_CONFIG_2 0x1e 28 | #define REG_PREAMBLE_MSB 0x20 29 | #define REG_PREAMBLE_LSB 0x21 30 | #define REG_PAYLOAD_LENGTH 0x22 31 | #define REG_MODEM_CONFIG_3 0x26 32 | #define REG_FREQ_ERROR_MSB 0x28 33 | #define REG_FREQ_ERROR_MID 0x29 34 | #define REG_FREQ_ERROR_LSB 0x2a 35 | #define REG_RSSI_WIDEBAND 0x2c 36 | #define REG_DETECTION_OPTIMIZE 0x31 37 | #define REG_DETECTION_THRESHOLD 0x37 38 | #define REG_SYNC_WORD 0x39 39 | #define REG_DIO_MAPPING_1 0x40 40 | #define REG_VERSION 0x42 41 | 42 | // Modes 43 | #define MODE_LONG_RANGE_MODE 0x80 44 | #define MODE_SLEEP 0x00 45 | #define MODE_STDBY 0x01 46 | #define MODE_TX 0x03 47 | #define MODE_RX_CONTINUOUS 0x05 48 | #define MODE_RX_SINGLE 0x06 49 | 50 | // PA config 51 | #define PA_BOOST 0x80 52 | 53 | // IRQ masks 54 | #define IRQ_TX_DONE_MASK 0x08 55 | #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 56 | #define IRQ_RX_DONE_MASK 0x40 57 | 58 | #define MAX_PKT_LENGTH 255 59 | 60 | LoRaClass::LoRaClass() : 61 | _spiSettings(8E6, MSBFIRST, SPI_MODE0), 62 | _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), 63 | _frequency(0), 64 | _packetIndex(0), 65 | _implicitHeaderMode(0), 66 | _onReceive(NULL) 67 | { 68 | // overide Stream timeout value 69 | setTimeout(0); 70 | } 71 | 72 | int LoRaClass::begin(long frequency) 73 | { 74 | // setup pins 75 | pinMode(_ss, OUTPUT); 76 | // set SS high 77 | digitalWrite(_ss, HIGH); 78 | 79 | if (_reset != -1) { 80 | pinMode(_reset, OUTPUT); 81 | 82 | // perform reset 83 | digitalWrite(_reset, LOW); 84 | delay(10); 85 | digitalWrite(_reset, HIGH); 86 | delay(10); 87 | } 88 | 89 | // start SPI 90 | SPI.begin(); 91 | 92 | // check version 93 | uint8_t version = readRegister(REG_VERSION); 94 | if (version != 0x12) { 95 | return 0; 96 | } 97 | 98 | // put in sleep mode 99 | sleep(); 100 | 101 | // set frequency 102 | setFrequency(frequency); 103 | 104 | // set base addresses 105 | writeRegister(REG_FIFO_TX_BASE_ADDR, 0); 106 | writeRegister(REG_FIFO_RX_BASE_ADDR, 0); 107 | 108 | // set LNA boost 109 | writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); 110 | 111 | // set auto AGC 112 | writeRegister(REG_MODEM_CONFIG_3, 0x04); 113 | 114 | // set output power to 2 dBm 115 | setTxPower(2); 116 | 117 | // put in standby mode 118 | idle(); 119 | 120 | return 1; 121 | } 122 | 123 | void LoRaClass::end() 124 | { 125 | // put in sleep mode 126 | sleep(); 127 | 128 | // stop SPI 129 | SPI.end(); 130 | } 131 | 132 | int LoRaClass::beginPacket(int implicitHeader) 133 | { 134 | // put in standby mode 135 | idle(); 136 | 137 | if (implicitHeader) { 138 | implicitHeaderMode(); 139 | } else { 140 | explicitHeaderMode(); 141 | } 142 | 143 | // reset FIFO address and paload length 144 | writeRegister(REG_FIFO_ADDR_PTR, 0); 145 | writeRegister(REG_PAYLOAD_LENGTH, 0); 146 | 147 | return 1; 148 | } 149 | 150 | int LoRaClass::endPacket() 151 | { 152 | // put in TX mode 153 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); 154 | 155 | // wait for TX done 156 | while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { 157 | yield(); 158 | } 159 | 160 | // clear IRQ's 161 | writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); 162 | 163 | return 1; 164 | } 165 | 166 | int LoRaClass::parsePacket(int size) 167 | { 168 | int packetLength = 0; 169 | int irqFlags = readRegister(REG_IRQ_FLAGS); 170 | 171 | if (size > 0) { 172 | implicitHeaderMode(); 173 | 174 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 175 | } else { 176 | explicitHeaderMode(); 177 | } 178 | 179 | // clear IRQ's 180 | writeRegister(REG_IRQ_FLAGS, irqFlags); 181 | 182 | if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 183 | // received a packet 184 | _packetIndex = 0; 185 | 186 | // read packet length 187 | if (_implicitHeaderMode) { 188 | packetLength = readRegister(REG_PAYLOAD_LENGTH); 189 | } else { 190 | packetLength = readRegister(REG_RX_NB_BYTES); 191 | } 192 | 193 | // set FIFO address to current RX address 194 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 195 | 196 | // put in standby mode 197 | idle(); 198 | } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { 199 | // not currently in RX mode 200 | 201 | // reset FIFO address 202 | writeRegister(REG_FIFO_ADDR_PTR, 0); 203 | 204 | // put in single RX mode 205 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); 206 | } 207 | 208 | return packetLength; 209 | } 210 | 211 | uint8_t LoRaClass::modemStatus() { 212 | return readRegister(REG_MODEM_STAT); 213 | } 214 | 215 | uint8_t LoRaClass::packetRssiRaw() { 216 | uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE); 217 | return pkt_rssi_value; 218 | } 219 | 220 | int LoRaClass::packetRssi() { 221 | int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE); 222 | // TODO: change this to look at the actual model code 223 | if (_frequency < 820E6) pkt_rssi -= 7; 224 | pkt_rssi -= 157; 225 | 226 | return pkt_rssi; 227 | } 228 | 229 | float LoRaClass::packetSnr() 230 | { 231 | return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; 232 | } 233 | 234 | long LoRaClass::packetFrequencyError() 235 | { 236 | int32_t freqError = 0; 237 | freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); 238 | freqError <<= 8L; 239 | freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); 240 | freqError <<= 8L; 241 | freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); 242 | 243 | if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on 244 | freqError -= 524288; // B1000'0000'0000'0000'0000 245 | } 246 | 247 | const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) 248 | const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 249 | 250 | return static_cast(fError); 251 | } 252 | 253 | size_t LoRaClass::write(uint8_t byte) 254 | { 255 | return write(&byte, sizeof(byte)); 256 | } 257 | 258 | size_t LoRaClass::write(const uint8_t *buffer, size_t size) 259 | { 260 | int currentLength = readRegister(REG_PAYLOAD_LENGTH); 261 | 262 | // check size 263 | if ((currentLength + size) > MAX_PKT_LENGTH) { 264 | size = MAX_PKT_LENGTH - currentLength; 265 | } 266 | 267 | // write data 268 | for (size_t i = 0; i < size; i++) { 269 | writeRegister(REG_FIFO, buffer[i]); 270 | } 271 | 272 | // update length 273 | writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); 274 | 275 | return size; 276 | } 277 | 278 | int LoRaClass::available() 279 | { 280 | return (readRegister(REG_RX_NB_BYTES) - _packetIndex); 281 | } 282 | 283 | int LoRaClass::read() 284 | { 285 | if (!available()) { 286 | return -1; 287 | } 288 | 289 | _packetIndex++; 290 | 291 | return readRegister(REG_FIFO); 292 | } 293 | 294 | int LoRaClass::peek() 295 | { 296 | if (!available()) { 297 | return -1; 298 | } 299 | 300 | // store current FIFO address 301 | int currentAddress = readRegister(REG_FIFO_ADDR_PTR); 302 | 303 | // read 304 | uint8_t b = readRegister(REG_FIFO); 305 | 306 | // restore FIFO address 307 | writeRegister(REG_FIFO_ADDR_PTR, currentAddress); 308 | 309 | return b; 310 | } 311 | 312 | void LoRaClass::flush() 313 | { 314 | } 315 | 316 | void LoRaClass::onReceive(void(*callback)(int)) 317 | { 318 | _onReceive = callback; 319 | 320 | if (callback) { 321 | pinMode(_dio0, INPUT); 322 | 323 | writeRegister(REG_DIO_MAPPING_1, 0x00); 324 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 325 | SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 326 | #endif 327 | attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); 328 | } else { 329 | detachInterrupt(digitalPinToInterrupt(_dio0)); 330 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 331 | SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 332 | #endif 333 | } 334 | } 335 | 336 | void LoRaClass::receive(int size) 337 | { 338 | if (size > 0) { 339 | implicitHeaderMode(); 340 | 341 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 342 | } else { 343 | explicitHeaderMode(); 344 | } 345 | 346 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); 347 | } 348 | 349 | void LoRaClass::idle() 350 | { 351 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); 352 | } 353 | 354 | void LoRaClass::sleep() 355 | { 356 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); 357 | } 358 | 359 | void LoRaClass::setTxPower(int level, int outputPin) 360 | { 361 | if (PA_OUTPUT_RFO_PIN == outputPin) { 362 | // RFO 363 | if (level < 0) { 364 | level = 0; 365 | } else if (level > 14) { 366 | level = 14; 367 | } 368 | 369 | writeRegister(REG_PA_CONFIG, 0x70 | level); 370 | } else { 371 | // PA BOOST 372 | if (level < 2) { 373 | level = 2; 374 | } else if (level > 17) { 375 | level = 17; 376 | } 377 | 378 | writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); 379 | } 380 | } 381 | 382 | void LoRaClass::setFrequency(long frequency) { 383 | _frequency = frequency; 384 | 385 | uint32_t frf = ((uint64_t)frequency << 19) / 32000000; 386 | 387 | writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); 388 | writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); 389 | writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); 390 | } 391 | 392 | uint32_t LoRaClass::getFrequency() { 393 | uint8_t msb = readRegister(REG_FRF_MSB); 394 | uint8_t mid = readRegister(REG_FRF_MID); 395 | uint8_t lsb = readRegister(REG_FRF_LSB); 396 | 397 | uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb; 398 | uint64_t frm = (uint64_t)frf*32000000; 399 | uint32_t frequency = (frm >> 19); 400 | 401 | return frequency; 402 | } 403 | 404 | void LoRaClass::setSpreadingFactor(int sf) 405 | { 406 | if (sf < 6) { 407 | sf = 6; 408 | } else if (sf > 12) { 409 | sf = 12; 410 | } 411 | 412 | if (sf == 6) { 413 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); 414 | writeRegister(REG_DETECTION_THRESHOLD, 0x0c); 415 | } else { 416 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); 417 | writeRegister(REG_DETECTION_THRESHOLD, 0x0a); 418 | } 419 | 420 | writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); 421 | } 422 | 423 | long LoRaClass::getSignalBandwidth() 424 | { 425 | byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); 426 | switch (bw) { 427 | case 0: return 7.8E3; 428 | case 1: return 10.4E3; 429 | case 2: return 15.6E3; 430 | case 3: return 20.8E3; 431 | case 4: return 31.25E3; 432 | case 5: return 41.7E3; 433 | case 6: return 62.5E3; 434 | case 7: return 125E3; 435 | case 8: return 250E3; 436 | case 9: return 500E3; 437 | } 438 | } 439 | 440 | void LoRaClass::setSignalBandwidth(long sbw) 441 | { 442 | int bw; 443 | 444 | if (sbw <= 7.8E3) { 445 | bw = 0; 446 | } else if (sbw <= 10.4E3) { 447 | bw = 1; 448 | } else if (sbw <= 15.6E3) { 449 | bw = 2; 450 | } else if (sbw <= 20.8E3) { 451 | bw = 3; 452 | } else if (sbw <= 31.25E3) { 453 | bw = 4; 454 | } else if (sbw <= 41.7E3) { 455 | bw = 5; 456 | } else if (sbw <= 62.5E3) { 457 | bw = 6; 458 | } else if (sbw <= 125E3) { 459 | bw = 7; 460 | } else if (sbw <= 250E3) { 461 | bw = 8; 462 | } else /*if (sbw <= 250E3)*/ { 463 | bw = 9; 464 | } 465 | 466 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); 467 | } 468 | 469 | void LoRaClass::setCodingRate4(int denominator) 470 | { 471 | if (denominator < 5) { 472 | denominator = 5; 473 | } else if (denominator > 8) { 474 | denominator = 8; 475 | } 476 | 477 | int cr = denominator - 4; 478 | 479 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); 480 | } 481 | 482 | void LoRaClass::setPreambleLength(long length) 483 | { 484 | writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); 485 | writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); 486 | } 487 | 488 | void LoRaClass::setSyncWord(int sw) 489 | { 490 | writeRegister(REG_SYNC_WORD, sw); 491 | } 492 | 493 | void LoRaClass::enableCrc() 494 | { 495 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); 496 | } 497 | 498 | void LoRaClass::disableCrc() 499 | { 500 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); 501 | } 502 | 503 | byte LoRaClass::random() 504 | { 505 | return readRegister(REG_RSSI_WIDEBAND); 506 | } 507 | 508 | void LoRaClass::setPins(int ss, int reset, int dio0) 509 | { 510 | _ss = ss; 511 | _reset = reset; 512 | _dio0 = dio0; 513 | } 514 | 515 | void LoRaClass::setSPIFrequency(uint32_t frequency) 516 | { 517 | _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); 518 | } 519 | 520 | void LoRaClass::dumpRegisters(Stream& out) 521 | { 522 | for (int i = 0; i < 128; i++) { 523 | out.print("0x"); 524 | out.print(i, HEX); 525 | out.print(": 0x"); 526 | out.println(readRegister(i), HEX); 527 | } 528 | } 529 | 530 | void LoRaClass::explicitHeaderMode() 531 | { 532 | _implicitHeaderMode = 0; 533 | 534 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); 535 | } 536 | 537 | void LoRaClass::implicitHeaderMode() 538 | { 539 | _implicitHeaderMode = 1; 540 | 541 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); 542 | } 543 | 544 | void LoRaClass::handleDio0Rise() 545 | { 546 | int irqFlags = readRegister(REG_IRQ_FLAGS); 547 | 548 | // clear IRQ's 549 | writeRegister(REG_IRQ_FLAGS, irqFlags); 550 | 551 | if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 552 | // received a packet 553 | _packetIndex = 0; 554 | 555 | // read packet length 556 | int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); 557 | 558 | // set FIFO address to current RX address 559 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 560 | 561 | if (_onReceive) { 562 | _onReceive(packetLength); 563 | } 564 | 565 | // reset FIFO address 566 | writeRegister(REG_FIFO_ADDR_PTR, 0); 567 | } 568 | } 569 | 570 | uint8_t LoRaClass::readRegister(uint8_t address) 571 | { 572 | return singleTransfer(address & 0x7f, 0x00); 573 | } 574 | 575 | void LoRaClass::writeRegister(uint8_t address, uint8_t value) 576 | { 577 | singleTransfer(address | 0x80, value); 578 | } 579 | 580 | uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) 581 | { 582 | uint8_t response; 583 | 584 | digitalWrite(_ss, LOW); 585 | 586 | SPI.beginTransaction(_spiSettings); 587 | SPI.transfer(address); 588 | response = SPI.transfer(value); 589 | SPI.endTransaction(); 590 | 591 | digitalWrite(_ss, HIGH); 592 | 593 | return response; 594 | } 595 | 596 | void LoRaClass::onDio0Rise() 597 | { 598 | LoRa.handleDio0Rise(); 599 | } 600 | 601 | LoRaClass LoRa; 602 | -------------------------------------------------------------------------------- /lora-kiss-tnc/LoRa.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef LORA_H 5 | #define LORA_H 6 | 7 | #include 8 | #include 9 | 10 | #define LORA_DEFAULT_SS_PIN 10 11 | #define LORA_DEFAULT_RESET_PIN 9 12 | #define LORA_DEFAULT_DIO0_PIN 2 13 | 14 | #define PA_OUTPUT_RFO_PIN 0 15 | #define PA_OUTPUT_PA_BOOST_PIN 1 16 | 17 | class LoRaClass : public Stream { 18 | public: 19 | LoRaClass(); 20 | 21 | int begin(long frequency); 22 | void end(); 23 | 24 | int beginPacket(int implicitHeader = false); 25 | int endPacket(); 26 | 27 | int parsePacket(int size = 0); 28 | int packetRssi(); 29 | uint8_t packetRssiRaw(); 30 | float packetSnr(); 31 | long packetFrequencyError(); 32 | 33 | // from Print 34 | virtual size_t write(uint8_t byte); 35 | virtual size_t write(const uint8_t *buffer, size_t size); 36 | 37 | // from Stream 38 | virtual int available(); 39 | virtual int read(); 40 | virtual int peek(); 41 | virtual void flush(); 42 | 43 | void onReceive(void(*callback)(int)); 44 | 45 | void receive(int size = 0); 46 | void idle(); 47 | void sleep(); 48 | 49 | void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); 50 | uint32_t getFrequency(); 51 | void setFrequency(long frequency); 52 | void setSpreadingFactor(int sf); 53 | long getSignalBandwidth(); 54 | void setSignalBandwidth(long sbw); 55 | void setCodingRate4(int denominator); 56 | void setPreambleLength(long length); 57 | void setSyncWord(int sw); 58 | uint8_t modemStatus(); 59 | void enableCrc(); 60 | void disableCrc(); 61 | 62 | // deprecated 63 | void crc() { enableCrc(); } 64 | void noCrc() { disableCrc(); } 65 | 66 | byte random(); 67 | 68 | void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); 69 | void setSPIFrequency(uint32_t frequency); 70 | 71 | void dumpRegisters(Stream& out); 72 | 73 | private: 74 | void explicitHeaderMode(); 75 | void implicitHeaderMode(); 76 | 77 | void handleDio0Rise(); 78 | 79 | uint8_t readRegister(uint8_t address); 80 | void writeRegister(uint8_t address, uint8_t value); 81 | uint8_t singleTransfer(uint8_t address, uint8_t value); 82 | 83 | static void onDio0Rise(); 84 | 85 | private: 86 | SPISettings _spiSettings; 87 | int _ss; 88 | int _reset; 89 | int _dio0; 90 | long _frequency; 91 | int _packetIndex; 92 | int _implicitHeaderMode; 93 | void (*_onReceive)(int); 94 | }; 95 | 96 | extern LoRaClass LoRa; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /lora-kiss-tnc/lora-kiss-tnc.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LoRa.h" 3 | #include "Config.h" 4 | #include "KISS.h" 5 | 6 | 7 | void setup() { 8 | // put your setup code here, to run once: 9 | Serial.begin(serialBaudRate); 10 | while (!Serial); // Waiting until LoRa32u4 is ready 11 | 12 | // Buffers 13 | memset(rxBuffer, 0, sizeof(rxBuffer)); 14 | memset(txBuffer, 0, sizeof(txBuffer)); 15 | 16 | LoRa.setPins(pinNSS, pinNRST, pinDIO0); 17 | 18 | startRadio(); 19 | } 20 | 21 | bool startRadio() { 22 | if (!LoRa.begin(loraFrequency)) { 23 | kissIndicateError(ERROR_INITRADIO); 24 | Serial.println("FAIL"); 25 | while(1); 26 | } 27 | else { 28 | Serial.println("SUCCESS"); 29 | LoRa.setSpreadingFactor(loraSpreadingFactor); 30 | LoRa.setCodingRate4(loraCodingRate); 31 | LoRa.enableCrc(); 32 | LoRa.onReceive(receiveCallback); 33 | LoRa.receive(); 34 | } 35 | } 36 | 37 | void transmit(size_t size) { 38 | size_t written = 0; 39 | 40 | if (size > MTU) { 41 | size = MTU; 42 | } 43 | 44 | LoRa.beginPacket(); 45 | for (size_t i; i < size; i++) { 46 | LoRa.write(txBuffer[i]); 47 | written++; 48 | } 49 | LoRa.endPacket(); 50 | 51 | LoRa.receive(); 52 | } 53 | 54 | void serialCallback(uint8_t txByte) { 55 | if (inFrame && txByte == FEND && command == CMD_DATA) { 56 | inFrame = false; 57 | //Serial.println("FULL_KISS"); 58 | if (outboundReady) { 59 | kissIndicateError(ERROR_QUEUE_FULL); 60 | } 61 | else { 62 | outboundReady = true; 63 | //Serial.println("RDY_OUT"); 64 | } 65 | } 66 | else if (txByte == FEND) { 67 | //Serial.println("KISS_FLAG"); 68 | inFrame = true; 69 | command = CMD_UNKNOWN; 70 | frameLength = 0; 71 | } 72 | else if (inFrame && frameLength < MTU) { 73 | // Get command byte 74 | if (frameLength == 0 && command == CMD_UNKNOWN) { 75 | //Serial.println("ACQ_CMD"); 76 | command = txByte; 77 | } 78 | else if (command == CMD_DATA) { 79 | if (txByte == FESC) { 80 | escape = true; 81 | } 82 | else { 83 | if (escape) { 84 | if (txByte == TFEND) { 85 | txByte = FEND; 86 | } 87 | if (txByte == TFESC) { 88 | txByte = FESC; 89 | } 90 | escape = false; 91 | } 92 | else { 93 | txBuffer[frameLength++] = txByte; 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | void updateModemStatus() { 101 | uint8_t status = LoRa.modemStatus(); 102 | lastStatusUpdate = millis(); 103 | if (status & SIG_DETECT == 0x01) { 104 | statSignalDetected = true; 105 | } 106 | else { 107 | statSignalDetected = false; 108 | } 109 | if (status & SIG_SYNCED == 0x01) { 110 | statSignalSynced = true; 111 | } 112 | else { 113 | statSignalSynced = false; 114 | } 115 | if (status & RX_ONGOING == 0x01) { 116 | statRxOngoing = true; 117 | } 118 | else { 119 | statRxOngoing = false; 120 | } 121 | 122 | if (statSignalDetected || statSignalSynced || statRxOngoing) { 123 | if (dcdCount < dcdThreshold) { 124 | dcdCount++; 125 | dcd = true; 126 | } 127 | else { 128 | dcd = true; 129 | dcdLed = true; 130 | } 131 | } 132 | else { 133 | if (dcdCount > 0) { 134 | dcdCount--; 135 | } 136 | else { 137 | dcdLed = false; 138 | } 139 | dcd = false; 140 | } 141 | } 142 | 143 | bool isOutboundReady() { 144 | return outboundReady; 145 | } 146 | 147 | void checkModemStatus() { 148 | if (millis() - lastStatusUpdate >= statusIntervalms) { 149 | updateModemStatus(); 150 | } 151 | } 152 | void receiveCallback(int packetSize) { 153 | readLength = 0; 154 | lastRssi = LoRa.packetRssi(); 155 | getPacketData(packetSize); 156 | 157 | // Send RSSI 158 | Serial.write(FEND); 159 | Serial.write(HW_RSSI); 160 | Serial.write((uint8_t)(lastRssi-rssiOffset)); 161 | Serial.write(FEND); 162 | 163 | // And then write the entire packet 164 | Serial.write(FEND); 165 | Serial.write((uint8_t)CMD_DATA); 166 | for (int i = 0; i < readLength; i++) { 167 | uint8_t temp = rxBuffer[i]; 168 | if (temp == FEND) { 169 | Serial.write(FESC); 170 | temp = TFEND; 171 | } 172 | if (temp == FESC) { 173 | Serial.write(FESC); 174 | temp = TFESC; 175 | } 176 | Serial.write(temp); 177 | } 178 | Serial.write(FEND); 179 | readLength = 0; 180 | } 181 | 182 | void escapedSerialWrite (uint8_t bufferByte) { 183 | switch(bufferByte) { 184 | case FEND: 185 | Serial.write(FESC); 186 | Serial.write(TFEND); 187 | break; 188 | case FESC: 189 | Serial.write(FESC); 190 | Serial.write(TFESC); 191 | break; 192 | default: 193 | Serial.write(bufferByte); 194 | } 195 | } 196 | 197 | void kissIndicateError(uint8_t errorCode) { 198 | Serial.write(FEND); 199 | Serial.write(CMD_ERROR); 200 | Serial.write(errorCode); 201 | Serial.write(FEND); 202 | } 203 | 204 | void getPacketData(int packetLength) { 205 | while (packetLength--) { 206 | rxBuffer[readLength++] = LoRa.read(); 207 | } 208 | } 209 | 210 | void loop() { 211 | // put your main code here, to run repeatedly: 212 | checkModemStatus(); 213 | if (isOutboundReady() && !SERIAL_READING) { 214 | if (!dcdWaiting) { 215 | updateModemStatus(); 216 | } 217 | if (!dcd && !dcdLed) { 218 | if (dcdWaiting) 219 | delay(loraRxTurnaround); 220 | updateModemStatus(); 221 | if (!dcd) { 222 | dcdWaiting = false; 223 | outboundReady = false; 224 | //Serial.println("CLR_TRANSMIT"); 225 | transmit(frameLength); 226 | } 227 | else { 228 | dcdWaiting = true; 229 | } 230 | } 231 | } 232 | if (Serial.available()) { 233 | SERIAL_READING = true; 234 | char txByte = Serial.read(); 235 | serialCallback(txByte); 236 | lastSerialRead = millis(); 237 | } 238 | 239 | else { 240 | if (SERIAL_READING && millis() - lastSerialRead >= serialReadTimeout) { 241 | SERIAL_READING = false; 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /lora-kiss/README.md: -------------------------------------------------------------------------------- 1 | # LoRa RX-Only KISS TNC 2 | 3 | This sketch is for an Arduino based KISS TNC using a LoRa radio. 4 | This sketch will be compatible with Semtech SX1276/77/78/79 based boards. 5 | 6 | This sketch is based on library made by Sandeep Mistry on LoRa. 7 | You can access the library on https://github.com/sandeepmistry/arduino-LoRa 8 | 9 | ## How to Make Your Own KISS TNC 10 | 11 | To create your own KISS TNC, you will need an Arduino (I am using Pro Mini) and 12 | a LoRa module (I am using Ai Thinker Ra-02 breakout). 13 | 14 | You can connect your LoRa module with your Arduino as follows: 15 | 16 | | LoRa Module | Arduino | 17 | | :---------: | :-----: | 18 | | VCC | GND | 19 | | SCK | SCK (13) | 20 | | MISO | MISO (12) | 21 | | MOSI | MOSI (11) | 22 | | NSS | 10 | 23 | | RST | 9 | 24 | | DIO0 | 2 | 25 | 26 | `NSS`, `RST` and `DIO0` pins can be changed if you don't have them available 27 | 28 | After connecting your module correctly to the Arduino Pro Mini, you now can 29 | compile and upload the code. Make sure you already have sandeepmistry's arduino-LoRa 30 | library available on your Arduino IDE, if not you can refer to his README on how 31 | to install the library. 32 | 33 | ## APRS i-Gate 34 | 35 | You can also use this KISS TNC as an APRS i-Gate, you can connect this to 36 | APRSIS-32 (on Windows) or you can use APRX for Linux. This can also work 37 | with a Raspberry Pi 3. 38 | -------------------------------------------------------------------------------- /lora-kiss/lora-kiss.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define FEND 0xC0 5 | #define FESC 0xDB 6 | #define TFEND 0xDC 7 | #define TFESC 0xDD 8 | 9 | #define maxPacketSize 255 10 | 11 | #define kissNoCrc 0x00 12 | #define kissFlexnet 0x20 13 | #define kissSmack 0x80 14 | 15 | void putSerial(const uint8_t *const txBuffer, const uint16_t txBufferSize) { 16 | Serial.write(txBuffer, txBufferSize); 17 | } 18 | 19 | void putRadio(const uint8_t *const txBuffer, const uint16_t txBufferSize) { 20 | LoRa.beginPacket(); 21 | LoRa.write(txBuffer, txBufferSize); 22 | LoRa.endPacket(); 23 | } 24 | 25 | void putByte(uint8_t *const out, uint16_t *const offset, const uint8_t buffer) { 26 | if (buffer == FEND) { 27 | out[(*offset)++] = FESC; 28 | out[(*offset)++] = TFEND; 29 | } 30 | 31 | else if (buffer == FESC) { 32 | out[(*offset)++] = FESC; 33 | out[(*offset)++] = TFESC; 34 | } 35 | 36 | else { 37 | out[(*offset)++] = buffer; 38 | } 39 | } 40 | 41 | void debug(const char *const debug_string) { 42 | uint8_t myBuffer[128]; 43 | const uint8_t ax25_ident[] = { 0x92, 0x88, 0x8A, 0x9C, 0xA8, 0x40, 0xE0, 0x88, 44 | 0x8A, 0x84, 0xAA, 0x8E, 0x60, 0x63, 0x03, 0xF0}; 45 | 46 | uint16_t offset = 0; 47 | myBuffer[offset++] = 0x00; 48 | 49 | // Adding AX25 Address to the Buffer 50 | for (uint8_t i = 0; i < sizeof(ax25_ident); i++) { 51 | myBuffer[offset++] = ax25_ident[i]; 52 | } 53 | 54 | uint8_t length = strlen(debug_string); 55 | 56 | for (uint8_t i = 0; i < length; i++) { 57 | myBuffer[offset++] = debug_string[i]; 58 | } 59 | 60 | const uint8_t fend = FEND; 61 | putSerial(&fend, 1); 62 | 63 | for (uint8_t i = 0; i < offset; i++) { 64 | uint8_t tinyBuffer[4]; 65 | uint16_t offset1 = 0; 66 | putByte(tinyBuffer, &offset1, myBuffer[i]); 67 | putSerial(tinyBuffer, offset1); 68 | } 69 | 70 | putSerial(&fend, 1); 71 | } 72 | 73 | //void kissEscapeBuffer(uint8_t *const out, uint16_t *const offset, const uint8_t charBuffer) { 74 | // if (charBuffer == FEND) { 75 | // out[(*offset)++] = FESC; 76 | // out[(*offset)++] = TFEND; 77 | // } 78 | // else if (charBuffer == FESC) { 79 | // out[(*offset)++] = FESC; 80 | // out[(*offset)++] = TFESC; 81 | // } 82 | // else { 83 | // out[(*offset)++] = charBuffer; 84 | // } 85 | //} 86 | // 87 | //void debug(const char *const debugString) { 88 | // uint8_t tempBuffer[128]; 89 | // const uint8_t ax25Ident[] = { 0x92, 0x88, 0x8A, 0x9C, 0xA8, 0x40, 0xE0, 0x88, 90 | // 0x8A, 0x84, 0xAA, 0x8E, 0x60, 0x63, 0x03, 0xF0 }; 91 | // uint16_t offset = 0; 92 | // tempBuffer[offset++] = 0x00; 93 | // 94 | // // Adding AX25 Address to the Buffer 95 | // for (uint8_t i = 0; i < sizeof(ax25Ident); i++) { 96 | // tempBuffer[offset++] = ax25Ident[i]; 97 | // } 98 | // 99 | // uint8_t debugStringLen = strlen(debugString); 100 | // 101 | // for (uint8_t i = 0; debugStringLen; i++) { 102 | // tempBuffer[offset++] = debugString[i]; 103 | // } 104 | // 105 | // const uint8_t fend = FEND; 106 | // putSerial(&fend, 1); 107 | // 108 | // for (uint8_t i = 0; i < offset; i++) { 109 | // uint8_t tinyBuffer[4]; 110 | // uint16_t offset1 = 0; 111 | // kissEscapeBuffer(tinyBuffer, &offset1, tempBuffer[i]); 112 | // putSerial(tinyBuffer, offset1); 113 | // } 114 | // 115 | // putSerial(&fend, 1); 116 | //} 117 | 118 | bool getSerial(uint8_t *const rxBuffer, const uint16_t rxBufferSize, const unsigned long int timeOut) { 119 | for (uint16_t i = 0; i < rxBufferSize; i++) { 120 | while(!Serial.available()) { 121 | if (millis() >= timeOut) { 122 | return false; 123 | } 124 | } 125 | rxBuffer[i] = Serial.read(); 126 | } 127 | return true; 128 | } 129 | 130 | void processSerial() { 131 | bool first = true; 132 | bool frameValid = false; 133 | bool escape = false; 134 | uint8_t bufferSmall[maxPacketSize]; 135 | 136 | uint16_t offset = 0; 137 | 138 | const unsigned long int end = millis() + 5000; 139 | 140 | while(millis() < end && frameValid == false && offset < maxPacketSize) { 141 | uint8_t buffer = 0; 142 | 143 | if (!getSerial(&buffer, 1, end)) { 144 | debug("SERIAL TIMEOUT"); 145 | break; 146 | } 147 | 148 | switch(buffer) { 149 | case FEND: 150 | if (first) { 151 | first = false; 152 | } 153 | else { 154 | frameValid = true; 155 | } 156 | break; 157 | case FESC: 158 | escape = true; 159 | default: 160 | if (escape) { 161 | if (buffer == TFEND) { 162 | bufferSmall[offset++] = FEND; 163 | } 164 | else if (buffer == TFESC) { 165 | bufferSmall[offset++] = FESC; 166 | } 167 | else { 168 | debug("ERROR ESCAPE"); 169 | } 170 | 171 | escape = false; 172 | } 173 | else { 174 | bufferSmall[offset++] = buffer; 175 | } 176 | } 177 | } 178 | 179 | if (frameValid) { 180 | if (offset > 1) { 181 | switch(bufferSmall[0]) { 182 | case kissNoCrc: 183 | // debug("Should transmit"); 184 | putRadio(&bufferSmall[0], offset); 185 | break; 186 | case kissFlexnet: 187 | debug("CANNOT ACCEPT FLEXNET"); 188 | break; 189 | case kissSmack: 190 | debug("CANNOT ACCEPT SMACK"); 191 | default: 192 | debug("FRAME UNKNOWN"); 193 | } 194 | } 195 | else { 196 | debug("TOO SMALL FOR A KISS FRAME"); 197 | } 198 | } 199 | } 200 | 201 | void setup() { 202 | // put your setup code here, to run once: 203 | Serial.begin(38400); 204 | while (!Serial); 205 | //for LoRa32u4 206 | LoRa.setPins(8, 4, 7); 207 | if (!LoRa.begin(434E6)) { 208 | debug("LoRa Init Failed"); 209 | } 210 | else { 211 | LoRa.enableCrc(); 212 | LoRa.setCodingRate4(9); 213 | LoRa.onReceive(onReceive); 214 | LoRa.receive(); 215 | debug("LoRa Init Success"); 216 | } 217 | } 218 | 219 | void loop() { 220 | // do nothing 221 | uint16_t serialIn = Serial.available(); 222 | if (serialIn != 0) { 223 | // debug("IT'S SOMETHING"); 224 | processSerial(); 225 | } 226 | } 227 | 228 | void onReceive(int PacketSize) { 229 | uint8_t bufferSmall[maxPacketSize]; 230 | uint8_t bufferBig[maxPacketSize * 2]; 231 | uint16_t offsetSmall = 0; 232 | uint16_t offsetBig = 0; 233 | 234 | while(LoRa.available()) { 235 | bufferSmall[offsetSmall++]=(char)LoRa.read(); 236 | } 237 | 238 | bufferBig[offsetBig++] = FEND; 239 | for (uint16_t i = 0; i < offsetSmall; i++) { 240 | putByte(bufferBig, &offsetBig, bufferSmall[i]); 241 | } 242 | bufferBig[offsetBig++] = FEND; 243 | 244 | putSerial(bufferBig, offsetBig); 245 | } 246 | --------------------------------------------------------------------------------