diff --git a/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.cpp b/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.cpp new file mode 100644 index 0000000..210a589 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.cpp @@ -0,0 +1,754 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +// registers +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_OCP 0x0b +#define REG_LNA 0x0c +#define REG_FIFO_ADDR_PTR 0x0d +#define REG_FIFO_TX_BASE_ADDR 0x0e +#define REG_FIFO_RX_BASE_ADDR 0x0f +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_RX_NB_BYTES 0x13 +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PKT_RSSI_VALUE 0x1a +#define REG_RSSI_VALUE 0x1b +#define REG_MODEM_CONFIG_1 0x1d +#define REG_MODEM_CONFIG_2 0x1e +#define REG_PREAMBLE_MSB 0x20 +#define REG_PREAMBLE_LSB 0x21 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_MODEM_CONFIG_3 0x26 +#define REG_FREQ_ERROR_MSB 0x28 +#define REG_FREQ_ERROR_MID 0x29 +#define REG_FREQ_ERROR_LSB 0x2a +#define REG_RSSI_WIDEBAND 0x2c +#define REG_DETECTION_OPTIMIZE 0x31 +#define REG_INVERTIQ 0x33 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_SYNC_WORD 0x39 +#define REG_INVERTIQ2 0x3b +#define REG_DIO_MAPPING_1 0x40 +#define REG_VERSION 0x42 +#define REG_PA_DAC 0x4d + +// modes +#define MODE_LONG_RANGE_MODE 0x80 +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_TX 0x03 +#define MODE_RX_CONTINUOUS 0x05 +#define MODE_RX_SINGLE 0x06 + +// PA config +#define PA_BOOST 0x80 + +// IRQ masks +#define IRQ_TX_DONE_MASK 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 +#define IRQ_RX_DONE_MASK 0x40 + +#define RF_MID_BAND_THRESHOLD 525E6 +#define RSSI_OFFSET_HF_PORT 157 +#define RSSI_OFFSET_LF_PORT 164 + +#define MAX_PKT_LENGTH 255 + +#if (ESP8266 || ESP32) + #define ISR_PREFIX ICACHE_RAM_ATTR +#else + #define ISR_PREFIX +#endif + +LoRaClass::LoRaClass() : + _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), + _spi(&LORA_DEFAULT_SPI), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _implicitHeaderMode(0), + _onReceive(NULL), + _onTxDone(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +int LoRaClass::begin(long frequency) +{ +#if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) + pinMode(LORA_IRQ_DUMB, OUTPUT); + digitalWrite(LORA_IRQ_DUMB, LOW); + + // Hardware reset + pinMode(LORA_BOOT0, OUTPUT); + digitalWrite(LORA_BOOT0, LOW); + + pinMode(LORA_RESET, OUTPUT); + digitalWrite(LORA_RESET, HIGH); + delay(200); + digitalWrite(LORA_RESET, LOW); + delay(200); + digitalWrite(LORA_RESET, HIGH); + delay(50); +#endif + + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + if (_reset != -1) { + pinMode(_reset, OUTPUT); + + // perform reset + digitalWrite(_reset, LOW); + delay(10); + digitalWrite(_reset, HIGH); + delay(10); + } + + // start SPI + _spi->begin(); + + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return 0; + } + + // put in sleep mode + sleep(); + + // set frequency + setFrequency(frequency); + + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR, 0); + + // set LNA boost + writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); + + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + + // set output power to 17 dBm + setTxPower(17); + + // put in standby mode + idle(); + + return 1; +} + +void LoRaClass::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + _spi->end(); +} + +int LoRaClass::beginPacket(int implicitHeader) +{ + if (isTransmitting()) { + return 0; + } + + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + // reset FIFO address and paload length + writeRegister(REG_FIFO_ADDR_PTR, 0); + writeRegister(REG_PAYLOAD_LENGTH, 0); + + return 1; +} + +int LoRaClass::endPacket(bool async) +{ + + if ((async) && (_onTxDone)) + writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE + + // put in TX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); + + if (!async) { + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { + yield(); + } + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return 1; +} + +bool LoRaClass::isTransmitting() +{ + if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) { + return true; + } + + if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) { + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return false; +} + +int LoRaClass::parsePacket(int size) +{ + int packetLength = 0; + int irqFlags = readRegister(REG_IRQ_FLAGS); + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + if (_implicitHeaderMode) { + packetLength = readRegister(REG_PAYLOAD_LENGTH); + } else { + packetLength = readRegister(REG_RX_NB_BYTES); + } + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + // put in standby mode + idle(); + } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { + // not currently in RX mode + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + + // put in single RX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); + } + + return packetLength; +} + +int LoRaClass::packetRssi() +{ + return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT)); +} + +float LoRaClass::packetSnr() +{ + return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; +} + +long LoRaClass::packetFrequencyError() +{ + int32_t freqError = 0; + freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); + + if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on + freqError -= 524288; // B1000'0000'0000'0000'0000 + } + + const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) + const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 + + return static_cast(fError); +} + +int LoRaClass::rssi() +{ + return (readRegister(REG_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT)); +} + +size_t LoRaClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t LoRaClass::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH); + + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO, buffer[i]); + } + + // update length + writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); + + return size; +} + +int LoRaClass::available() +{ + return (readRegister(REG_RX_NB_BYTES) - _packetIndex); +} + +int LoRaClass::read() +{ + if (!available()) { + return -1; + } + + _packetIndex++; + + return readRegister(REG_FIFO); +} + +int LoRaClass::peek() +{ + if (!available()) { + return -1; + } + + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR); + + // read + uint8_t b = readRegister(REG_FIFO); + + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR, currentAddress); + + return b; +} + +void LoRaClass::flush() +{ +} + +#ifndef ARDUINO_SAMD_MKRWAN1300 +void LoRaClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::onTxDone(void(*callback)()) +{ + _onTxDone = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::receive(int size) +{ + + writeRegister(REG_DIO_MAPPING_1, 0x00); // DIO0 => RXDONE + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); +} +#endif + +void LoRaClass::idle() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); +} + +void LoRaClass::sleep() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); +} + +void LoRaClass::setTxPower(int level, int outputPin) +{ + if (PA_OUTPUT_RFO_PIN == outputPin) { + // RFO + if (level < 0) { + level = 0; + } else if (level > 14) { + level = 14; + } + + writeRegister(REG_PA_CONFIG, 0x70 | level); + } else { + // PA BOOST + if (level > 17) { + if (level > 20) { + level = 20; + } + + // subtract 3 from level, so 18 - 20 maps to 15 - 17 + level -= 3; + + // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) + writeRegister(REG_PA_DAC, 0x87); + setOCP(140); + } else { + if (level < 2) { + level = 2; + } + //Default value PA_HF/LF or +17dBm + writeRegister(REG_PA_DAC, 0x84); + setOCP(100); + } + + writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); + } +} + +void LoRaClass::setFrequency(long frequency) +{ + _frequency = frequency; + + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + + writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +int LoRaClass::getSpreadingFactor() +{ + return readRegister(REG_MODEM_CONFIG_2) >> 4; +} + +void LoRaClass::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } else if (sf > 12) { + sf = 12; + } + + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD, 0x0a); + } + + writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); + setLdoFlag(); +} + +long LoRaClass::getSignalBandwidth() +{ + byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); + + switch (bw) { + case 0: return 7.8E3; + case 1: return 10.4E3; + case 2: return 15.6E3; + case 3: return 20.8E3; + case 4: return 31.25E3; + case 5: return 41.7E3; + case 6: return 62.5E3; + case 7: return 125E3; + case 8: return 250E3; + case 9: return 500E3; + } + + return -1; +} + +void LoRaClass::setSignalBandwidth(long sbw) +{ + int bw; + + if (sbw <= 7.8E3) { + bw = 0; + } else if (sbw <= 10.4E3) { + bw = 1; + } else if (sbw <= 15.6E3) { + bw = 2; + } else if (sbw <= 20.8E3) { + bw = 3; + } else if (sbw <= 31.25E3) { + bw = 4; + } else if (sbw <= 41.7E3) { + bw = 5; + } else if (sbw <= 62.5E3) { + bw = 6; + } else if (sbw <= 125E3) { + bw = 7; + } else if (sbw <= 250E3) { + bw = 8; + } else /*if (sbw <= 250E3)*/ { + bw = 9; + } + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); + setLdoFlag(); +} + +void LoRaClass::setLdoFlag() +{ + // Section 4.1.1.5 + long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; + + // Section 4.1.1.6 + boolean ldoOn = symbolDuration > 16; + + uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); + bitWrite(config3, 3, ldoOn); + writeRegister(REG_MODEM_CONFIG_3, config3); +} + +void LoRaClass::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); +} + +void LoRaClass::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); +} + +void LoRaClass::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD, sw); +} + +void LoRaClass::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); +} + +void LoRaClass::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); +} + +void LoRaClass::enableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x66); + writeRegister(REG_INVERTIQ2, 0x19); +} + +void LoRaClass::disableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x27); + writeRegister(REG_INVERTIQ2, 0x1d); +} + +void LoRaClass::setOCP(uint8_t mA) +{ + uint8_t ocpTrim = 27; + + if (mA <= 120) { + ocpTrim = (mA - 45) / 5; + } else if (mA <=240) { + ocpTrim = (mA + 30) / 10; + } + + writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); +} + +void LoRaClass::setGain(uint8_t gain) +{ + // check allowed range + if (gain > 6) { + gain = 6; + } + + // set to standby + idle(); + + // set gain + if (gain == 0) { + // if gain = 0, enable AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + } else { + // disable AGC + writeRegister(REG_MODEM_CONFIG_3, 0x00); + + // clear Gain and set LNA boost + writeRegister(REG_LNA, 0x03); + + // set gain + writeRegister(REG_LNA, readRegister(REG_LNA) | (gain << 5)); + } +} + +byte LoRaClass::random() +{ + return readRegister(REG_RSSI_WIDEBAND); +} + +void LoRaClass::setPins(int ss, int reset, int dio0) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; +} + +void LoRaClass::setSPI(SPIClass& spi) +{ + _spi = &spi; +} + +void LoRaClass::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void LoRaClass::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + out.print("0x"); + out.print(i, HEX); + out.print(": 0x"); + out.println(readRegister(i), HEX); + } +} + +void LoRaClass::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); +} + +void LoRaClass::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); +} + +void LoRaClass::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS); + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + + if ((irqFlags & IRQ_RX_DONE_MASK) != 0) { + // received a packet + _packetIndex = 0; + + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + if (_onReceive) { + _onReceive(packetLength); + } + } + else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) { + if (_onTxDone) { + _onTxDone(); + } + } + } +} + +uint8_t LoRaClass::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void LoRaClass::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + + digitalWrite(_ss, LOW); + + _spi->beginTransaction(_spiSettings); + _spi->transfer(address); + response = _spi->transfer(value); + _spi->endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + +ISR_PREFIX void LoRaClass::onDio0Rise() +{ + LoRa.handleDio0Rise(); +} + +LoRaClass LoRa; diff --git a/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.h b/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.h new file mode 100644 index 0000000..b312db5 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/Library/LoRa/LoRa.h @@ -0,0 +1,130 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LORA_H +#define LORA_H + +#include +#include + +#if defined(ARDUINO_SAMD_MKRWAN1300) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN -1 +#elif defined(ARDUINO_SAMD_MKRWAN1310) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN LORA_IRQ +#else +#define LORA_DEFAULT_SPI SPI +#define LORA_DEFAULT_SPI_FREQUENCY 8E6 +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#endif + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +class LoRaClass : public Stream { +public: + LoRaClass(); + + int begin(long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(bool async = false); + + int parsePacket(int size = 0); + int packetRssi(); + float packetSnr(); + long packetFrequencyError(); + + int rssi(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + +#ifndef ARDUINO_SAMD_MKRWAN1300 + void onReceive(void(*callback)(int)); + void onTxDone(void(*callback)()); + + void receive(int size = 0); +#endif + void idle(); + void sleep(); + + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + void setFrequency(long frequency); + void setSpreadingFactor(int sf); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + void enableCrc(); + void disableCrc(); + void enableInvertIQ(); + void disableInvertIQ(); + + void setOCP(uint8_t mA); // Over Current Protection control + + void setGain(uint8_t gain); // Set LNA gain + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); + void setSPI(SPIClass& spi); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + bool isTransmitting(); + + int getSpreadingFactor(); + long getSignalBandwidth(); + + void setLdoFlag(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + +private: + SPISettings _spiSettings; + SPIClass* _spi; + int _ss; + int _reset; + int _dio0; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; + void (*_onReceive)(int); + void (*_onTxDone)(); +}; + +extern LoRaClass LoRa; + +#endif diff --git a/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver/LoRa_Receiver.ino b/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver/LoRa_Receiver.ino new file mode 100644 index 0000000..bf3a821 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver/LoRa_Receiver.ino @@ -0,0 +1,122 @@ +/** + * ESP32 LoRa Receiver with Status LEDs + * + * Description: + * Implements a LoRa packet receiver using the ESP32's HSPI interface. + * - Initializes LoRa radio module with custom SPI pins + * - Listens for incoming LoRa packets + * - Displays received messages and signal strength via Serial + * - Provides visual feedback using RX/TX LEDs + * + * Hardware Configuration: + * LoRa Module Connections: + * - MOSI: GPIO15 + * - MISO: GPIO16 + * - SCK: GPIO17 + * - NSS: GPIO18 (CS) + * - RST: GPIO5 + * - INT: GPIO4 (DIO0) + * + * LED Indicators: + * - RX LED: GPIO8 (lights when packet received) + * - TX LED: GPIO9 (currently unused in this receive-only implementation) + * + * Radio Settings: + * - Frequency: 433MHz + * - Uses HSPI interface + * + * Functionality: + * 1. Initializes Serial monitor (115200 baud) + * 2. Sets up LED pins and LoRa module + * 3. Continuously checks for incoming LoRa packets + * 4. Prints received messages and RSSI to Serial + * 5. Blinks RX LED on packet reception + * + * Usage: + * 1. Connect LoRa module to specified GPIO pins + * 2. Upload sketch to ESP32 + * 3. Open Serial monitor (115200 baud) + * 4. Monitor incoming LoRa packets and signal strength + * + * Customization: + * - Change frequency by modifying the 'frequency' constant + * - Adjust LED pins by changing LED_RX/LED_TX defines + * - Modify SPI pins if using different GPIOs + * + * Dependencies: + * - LoRa library (Included in Repository) + * - SPI library (built-in) + */ + + +#include // include libraries +#include + +#define LoRa_MOSI 15 +#define LoRa_MISO 16 +#define LoRa_RST 5 +#define LoRa_NSS 18 +#define LoRa_SCK 17 +#define LoRa_INT 4 + +#define LED_RX 8 +#define LED_TX 9 + +const long frequency = 433E6; // LoRa Frequency +SPIClass loRaSPI(HSPI); + +void pinSetup() +{ + Serial.println("Starting pin Initialization"); + pinMode(LED_RX, OUTPUT); + pinMode(LED_TX, OUTPUT); + + digitalWrite(LED_RX, HIGH); + digitalWrite(LED_TX, HIGH); + + Serial.println("Pin Initialized"); +} + + + +void LoRaSetup() +{ + loRaSPI.begin(LoRa_SCK, LoRa_MISO, LoRa_MOSI, LoRa_NSS); // set LoRa SPI pins + LoRa.setSPI(loRaSPI); // set LoRa SPI + LoRa.setPins(LoRa_NSS, LoRa_RST, LoRa_INT); // set CS, reset, IRQ pin + if (!LoRa.begin(frequency)) + { + Serial.println("LoRa init failed. Check your connections."); + while (true) + ; + } + + Serial.println("LoRa init succeeded."); + Serial.println(); +} + +void setup() +{ + Serial.begin(115200); // initialize serial + Serial.println("Serial Initialized"); + pinSetup(); // initialize pins + LoRaSetup(); // initialize LoRa +} + +void loop() +{ + int packetSize = LoRa.parsePacket(); + if (packetSize) + { + digitalWrite(LED_RX, LOW); + Serial.print("Received packet '"); + while (LoRa.available()) + { + Serial.print((char)LoRa.read()); + } + + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + digitalWrite(LED_RX, HIGH); + } +} diff --git a/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver_Interrupt_Callback/LoRa_Receiver_Interrupt_Callback.ino b/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver_Interrupt_Callback/LoRa_Receiver_Interrupt_Callback.ino new file mode 100644 index 0000000..ea72fe6 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/LoRa_Receiver_Interrupt_Callback/LoRa_Receiver_Interrupt_Callback.ino @@ -0,0 +1,141 @@ +/** + * ESP32 LoRa Receiver with Interrupt-Driven Operation + * + * Description: + * Advanced LoRa packet receiver using interrupt-based reception for efficient operation. + * Implements callback-driven architecture to minimize CPU usage while waiting for packets. + * + * Key Features: + * - Interrupt-based packet reception (no polling) + * - Visual feedback via RX LED + * - Detailed packet diagnostics (RSSI) + * - Custom HSPI interface configuration + * - Efficient power management through interrupt handling + * + * Hardware Configuration: + * LoRa Module Connections: + * - MOSI: GPIO15 + * - MISO: GPIO16 + * - SCK: GPIO17 + * - NSS: GPIO18 (CS) + * - RST: GPIO5 + * - INT: GPIO4 (DIO0 - interrupt pin) + * + * LED Indicators: + * - RX LED: GPIO8 (active-low blink on reception) + * - TX LED: GPIO9 (reserved for future transmit functionality) + * + * Radio Settings: + * - Frequency: 433MHz + * - SPI Interface: HSPI + * - Interrupt-driven receive mode + * + * Functionality: + * 1. Initializes hardware peripherals (Serial, LEDs, LoRa) + * 2. Configures LoRa module in receive mode with callback + * 3. Processes incoming packets via interrupt service routine + * 4. Provides visual and serial output for received packets + * + * Usage: + * 1. Connect hardware as per pin definitions + * 2. Upload sketch to ESP32 + * 3. Monitor serial output at 115200 baud + * 4. RX LED will blink on packet reception + * + * Advanced Features: + * - Interrupt-driven architecture reduces CPU load + * - Callback handler for immediate packet processing + * - Packet size tracking for efficient reading + * - Ready for expansion to transmit functionality + * + * Dependencies: + * - LoRa library (included in repository) + * - SPI library (built-in) + */ + +#include // include libraries +#include + +#define LoRa_MOSI 15 +#define LoRa_MISO 16 +#define LoRa_RST 5 +#define LoRa_NSS 18 +#define LoRa_SCK 17 +#define LoRa_INT 4 + +#define LED_RX 8 +#define LED_TX 9 + +const long frequency = 433E6; // LoRa Frequency +bool rxFlag = false; +int packetSize = 0; +SPIClass loRaSPI(HSPI); + +void pinSetup() +{ + Serial.println("Starting pin Initialization"); + pinMode(LED_RX, OUTPUT); + pinMode(LED_TX, OUTPUT); + digitalWrite(LED_RX, HIGH); + digitalWrite(LED_TX, HIGH); + + Serial.println("Pin Initialized"); +} + +void onReceiveCB(int size) +{ + rxFlag = true; + packetSize = size; +} + +void LoRaSetup() +{ + loRaSPI.begin(LoRa_SCK, LoRa_MISO, LoRa_MOSI, LoRa_NSS); // set LoRa SPI pins + LoRa.setSPI(loRaSPI); // set LoRa SPI + LoRa.setPins(LoRa_NSS, LoRa_RST, LoRa_INT); // set CS, reset, IRQ pin + if (!LoRa.begin(frequency)) + { + Serial.println("LoRa init failed. Check your connections."); + while (true) + ; + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + + LoRa.onReceive(onReceiveCB); + LoRa.receive(); +} + +void readPacket() +{ + if (rxFlag == true) + { + digitalWrite(LED_RX, LOW); + Serial.print("Received packet: "); + for (int i = 0; i < packetSize; i++) + { + Serial.print((char)LoRa.read()); + } + + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + + packetSize = 0; + rxFlag = false; + digitalWrite(LED_RX, HIGH); + } +} + +void setup() +{ + Serial.begin(115200); // initialize serial + Serial.println("Serial Initialized"); + pinSetup(); // initialize pins + LoRaSetup(); // initialize LoRa +} + +void loop() +{ + readPacket(); +} \ No newline at end of file diff --git a/bolt-ons/lora-wireless-bolt-on/LoRa_Sender/LoRa_Sender.ino b/bolt-ons/lora-wireless-bolt-on/LoRa_Sender/LoRa_Sender.ino new file mode 100644 index 0000000..9086152 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/LoRa_Sender/LoRa_Sender.ino @@ -0,0 +1,112 @@ +/** + * ESP32 LoRa Transmitter with Packet Counter + * + * Description: + * Implements a LoRa packet transmitter that sends incrementing counter messages. + * Demonstrates basic LoRa communication with periodic message transmission. + * + * Hardware Configuration: + * LoRa Module Connections: + * - MOSI: GPIO15 + * - MISO: GPIO16 + * - SCK: GPIO17 + * - NSS: GPIO18 (CS) + * - RST: GPIO5 + * - INT: GPIO4 (DIO0) + * + * LED Indicators: + * - RX LED: GPIO8 (unused in this transmit-only example) + * - TX LED: GPIO9 (unused in current implementation) + * + * Radio Settings: + * - Frequency: 433MHz + * - Uses HSPI interface + * + * Functionality: + * 1. Initializes Serial monitor (115200 baud) + * 2. Configures LoRa module with custom SPI pins + * 3. Transmits "Hello World" messages with incrementing counter + * 4. Sends packets every 1000ms + * + * Usage: + * 1. Connect LoRa module to specified GPIO pins + * 2. Upload sketch to ESP32 + * 3. Open Serial monitor (115200 baud) for status messages + * 4. Messages will be transmitted automatically every second + * + * Customization: + * - Change transmission interval by modifying the delay() value + * - Adjust message content in LoRa_sendMessage() + * - Modify frequency by changing the 'frequency' constant + * - Enable TX LED blinking by adding digitalWrite() in LoRa_sendMessage() + * + * Dependencies: + * - LoRa library (Included in Repository) + * - SPI library (built-in) + */ + +#include // include libraries +#include + +#define LoRa_MOSI 15 +#define LoRa_MISO 16 +#define LoRa_RST 5 +#define LoRa_NSS 18 +#define LoRa_SCK 17 +#define LoRa_INT 4 + +#define LED_RX 8 +#define LED_TX 9 + +const long frequency = 433E6; // LoRa Frequency +SPIClass loRaSPI(HSPI); +void pinSetup() +{ + Serial.println("Starting pin Initialization"); + pinMode(LED_RX, OUTPUT); + pinMode(LED_TX, OUTPUT); + + digitalWrite(LED_RX, HIGH); + digitalWrite(LED_TX, HIGH); + + Serial.println("Pin Initialized"); +} + +void LoRaSetup() +{ + loRaSPI.begin(LoRa_SCK, LoRa_MISO, LoRa_MOSI, LoRa_NSS); // set LoRa SPI pins + LoRa.setSPI(loRaSPI); // set LoRa SPI + LoRa.setPins(LoRa_NSS, LoRa_RST, LoRa_INT); // set CS, reset, IRQ pin + if (!LoRa.begin(frequency)) + { + Serial.println("LoRa init failed. Check your connections."); + while (true) + ; + } + + Serial.println("LoRa init succeeded."); + Serial.println(); +} + +void LoRa_sendMessage(String message) +{ + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(true); // finish packet and send it +} + +void setup() +{ + Serial.begin(115200); // initialize serial + Serial.println("Serial Initialized"); + pinSetup(); // initialize pins + LoRaSetup(); // initialize LoRa +} + +uint16_t counter = 0; + +void loop() { + LoRa_sendMessage("Hello World." + String(counter)); + counter++; + delay(1000); +} diff --git a/bolt-ons/lora-wireless-bolt-on/README.md b/bolt-ons/lora-wireless-bolt-on/README.md new file mode 100644 index 0000000..ed0d1b8 --- /dev/null +++ b/bolt-ons/lora-wireless-bolt-on/README.md @@ -0,0 +1,52 @@ +# LoRa Communication with LoRa-Wireless-Bolt-On and ESP32-CAN-X2 + +## Overview +Repository includes demonstration codes LoRa communication using the LoRa-Wireless-Bolt-On LoRa module on a custom daughterboard with the ESP32-CAN-X2 development board. The setup utilizes the LoRa Arduino library by Sandeep Mistry (Version 0.8.0) for seamless communication between sender and receiver nodes. + +## Requirements +- ESP32-CAN-X2 Development Board +- LoRa-Wireless-Bolt-On LoRa Module +- Arduino IDE +- LoRa Arduino Library +- Two ESP32 boards (one for sending, one for receiving) + +## Installation +### Adding LoRa Library to Arduino IDE +1. Open Arduino IDE. +2. Navigate to **Sketch** → **Include Library** → **Add .ZIP Library...**. +3. Select the `LoRa` library ZIP file located in the `Library` folder of this repository. +4. Click "Open" to install the library. + +Alternatively, you can manually copy the `LoRa` library folder to your Arduino libraries directory: +``` +Documents/Arduino/libraries/ +``` + +## Sample Codes +This repository includes two sample programs for testing LoRa communication: +1. `LoRa_Sender`: Sends a test message periodically. +2. `LoRa_Receiver`: Listens for incoming messages and prints them to the Serial Monitor. + +### Uploading and Testing +#### LoRa Sender +1. Open `LoRa_Sender.ino` in Arduino IDE. +2. Select the correct ESP32 board and port under **Tools**. +3. Upload the code to your ESP32 sender board. +4. Open the Serial Monitor (115200 baud rate) to observe the transmission logs. + +#### LoRa Receiver +1. Open `LoRa_Receiver.ino` in Arduino IDE. +2. Upload the code to another ESP32 board (receiver). +3. Open the Serial Monitor (115200 baud rate) to view received messages. + +## Notes +- Ensure both sender and receiver use the same frequency settings. +- Antenna must be connected to the RA-02 module to avoid damage. +- LoRa works best in open areas with minimal obstructions. + +## License +This project is open-source and free to use under the MIT License. + +--- +Developed for ESP32-CAN-X2 with LoRa-Wireless-Bolt-On LoRa Module. +