diff --git a/examples/api_test/api_test.ino b/examples/api_test/api_test.ino new file mode 100644 index 00000000..f8cf9167 --- /dev/null +++ b/examples/api_test/api_test.ino @@ -0,0 +1,56 @@ +#include "KiteLib.h" + +ESP8266 wifi = Kite.ModuleA; +//SX1278 lora = Kite.ModuleB; +//HC05 bluetooth = Kite.ModuleB; + +//Packet pack("01:23:45:67:89:AB:CD:EF", "Hello World! (by LoRa)"); + +void setup() { + Serial.begin(9600); + + Serial.print("[ESP8266] Connecting ... "); + uint8_t state = wifi.begin(9600); + if(state == ERR_NONE) { + Serial.println("success!"); + } else { + Serial.print("failed, code "); + Serial.println(state, HEX); + } + + Serial.print("[ESP8266] Joining AP ... "); + state = wifi.join("Tenda", "Student20-X13"); + if(state == ERR_NONE) { + Serial.println("success!"); + } else { + Serial.print("failed, code "); + Serial.println(state, HEX); + } + + Serial.print("[ESP8266] Sending GET request ... "); + String response; + uint16_t http_code = wifi.HttpGet("http://www.httpbin.org/ip", response); + if(http_code == 200) { + Serial.println("success!"); + Serial.println("[ESP8266] Response:\n"); + Serial.println(response); + } else { + Serial.print("failed, code "); + Serial.println(http_code, HEX); + } + + //lora.begin(); + + //bluetooth.begin(9600); +} + +void loop() { + //wifi.send("GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); + + //lora.transmit(pack); + + //bluetooth.println("Hello World! (by Blueooth)"); + + delay(1000); +} + diff --git a/examples/uart_test/uart_test.ino b/examples/uart_test/uart_test.ino new file mode 100644 index 00000000..7666e3a9 --- /dev/null +++ b/examples/uart_test/uart_test.ino @@ -0,0 +1,24 @@ +#include + +const int RX_A = 9; +const int TX_A = 8; +const int RX_B = 7; +const int TX_B = 6; + +SoftwareSerial module(RX_A, TX_A); // Rx, Tx, Module A +//SoftwareSerial module(RX_B, TX_B); // Rx, Tx, Module B + +void setup() { + Serial.begin(9600); + module.begin(9600); +} + +void loop() { + while(Serial.available() > 0) { + module.write(Serial.read()); + } + + while(module.available() > 0) { + Serial.write(module.read()); + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 00000000..5f51c0f1 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,93 @@ +####################################### +# Syntax Coloring Map For LoRaLib +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +KiteLib KEYWORD1 +Kite KEYWORD1 +Packet KEYWORD1 +ESP8266 KEYWORD1 +SX1278 KEYWORD1 +SX1272 KEYWORD1 +HC05 KEYWORD1 + +Bandwidth KEYWORD1 +SpreadingFactor KEYWORD1 +CodingRate KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +# KiteLib +ModuleA KEYWORD2 +ModuleB KEYWORD2 + +# Packet +source KEYWORD2 +destination KEYWORD2 +data KEYWORD2 +length KEYWORD2 +getSourceStr KEYWORD2 +getDestinationStr KEYWORD2 +setSourceStr KEYWORD2 +setDestinationStr KEYWORD2 +copyInto KEYWORD2 + +# SX1278/SX1272 +dataRate KEYWORD2 +lastPacketRSSI KEYWORD2 +begin KEYWORD2 +transmit KEYWORD2 +receive KEYWORD2 +sleep KEYWORD2 +standby KEYWORD2 +setBandwidth KEYWORD2 +setSpreadingFactor KEYWORD2 +setCodingRate KEYWORD2 + +# ESP8266 +join KEYWORD2 +createTCP KEYWORD2 +send KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +BW_7_80_KHZ LITERAL1 +BW_10_40_KHZ LITERAL1 +BW_15_60_KHZ LITERAL1 +BW_20_80_KHZ LITERAL1 +BW_31_25_KHZ LITERAL1 +BW_41_70_KHZ LITERAL1 +BW_62_50_KHZ LITERAL1 +BW_125_00_KHZ LITERAL1 +BW_250_00_KHZ LITERAL1 +BW_500_00_KHZ LITERAL1 + +SF_6 LITERAL1 +SF_7 LITERAL1 +SF_8 LITERAL1 +SF_9 LITERAL1 +SF_10 LITERAL1 +SF_11 LITERAL1 +SF_12 LITERAL1 + +CR_4_5 LITERAL1 +CR_4_6 LITERAL1 +CR_4_7 LITERAL1 +CR_4_8 LITERAL1 + +ERR_NONE LITERAL1 +ERR_CHIP_NOT_FOUND LITERAL1 +ERR_PACKET_TOO_LONG LITERAL1 +ERR_RX_TIMEOUT LITERAL1 +ERR_CRC_MISMATCH LITERAL1 +ERR_INVALID_BANDWIDTH LITERAL1 +ERR_INVALID_SPREADING_FACTOR LITERAL1 +ERR_INVALID_CODING_RATE LITERAL1 +ERR_INVALID_BIT_RANGE LITERAL1 diff --git a/library.properties b/library.properties new file mode 100644 index 00000000..121918ab --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=KiteLib +version=1.0.0 +author=Jan Gromes +maintainer=Jan Gromes +sentence=Arduino library for the KITE shield +paragraph=Enables user-friendly control of the KITE shield and various wireless modules. +category=Communication +url=https://github.com/jgromes/KiteLib +architecture=* +includes=KiteLib.h diff --git a/license.txt b/license.txt new file mode 100644 index 00000000..23f7e2be --- /dev/null +++ b/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Jan Gromeš + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/ESP8266.cpp b/src/ESP8266.cpp new file mode 100644 index 00000000..76d5cfa2 --- /dev/null +++ b/src/ESP8266.cpp @@ -0,0 +1,172 @@ +#include "ESP8266.h" + +ESP8266::ESP8266(Module* module) { + _mod = module; +} + +uint8_t ESP8266::begin(long speed) { + _mod->baudrate = speed; + _mod->init(USE_UART, INT_NONE); + _mod->ATemptyBuffer(); + if(!_mod->ATsendCommand("AT")) { + return(ERR_UNKNOWN); + } + return(ERR_NONE); +} + +uint8_t ESP8266::restart() { + // send the restart command + if(!_mod->ATsendCommand("AT+RST")) { + return(ERR_UNKNOWN); + } + + // wait for the module to start + delay(2000); + + // test AT setup + uint32_t start = millis(); + while (millis() - start < 3000) { + if(!_mod->ATsendCommand("AT")) { + delay(100); + } else { + return(ERR_NONE); + } + } + + return(ERR_UNKNOWN); +} + +uint8_t ESP8266::join(const char* ssid, const char* password) { + // set mode to station + soft AP + if(!_mod->ATsendCommand("AT+CWMODE_CUR=3")) { + return(ERR_UNKNOWN); + } + + // restart module + uint8_t state = restart(); + if(state != ERR_NONE) { + return(state); + } + + // join AP + String cmd = "AT+CWJAP_CUR=\""; + cmd += ssid; + cmd += "\",\""; + cmd += password; + cmd += "\""; + if(!_mod->ATsendCommand(cmd)) { + return(ERR_UNKNOWN); + } + + // disable multiple connection mode + if(!_mod->ATsendCommand("AT+CIPMUX=0")) { + return(ERR_UNKNOWN); + } + + return(ERR_NONE); +} + +uint16_t ESP8266::HttpGet(const char* url, String& data, uint16_t port) { + String urlString(url); + + // get the host address and endpoint + int32_t resourceIndex = urlString.indexOf("/", 7); + if(resourceIndex == -1) { + return(ERR_URL_MALFORMED); + } + String host = urlString.substring(7, resourceIndex); + String endpoint = urlString.substring(resourceIndex); + + // create TCP connection + uint8_t state = startTCP(host.c_str(), 80); + if(state != ERR_NONE) { + return(state); + } + + // build the GET request + String request = "GET "; + request += endpoint; + request += " HTTP/1.1\r\nHost: "; + request += host; + request += "\r\n\r\n"; + + // send the GET request + state = send(request); + if(state != ERR_NONE) { + return(state); + } + + // read the response + String raw = receive(); + + // close the TCP connection + state = closeTCP(); + if(state != ERR_NONE) { + return(state); + } + + // parse the response + int32_t numBytesIndex = raw.indexOf(":"); + if(numBytesIndex == -1) { + return(ERR_RESPONSE_MALFORMED_AT); + } + data = raw.substring(numBytesIndex + 1); + + // return the HTTP status code + int32_t spaceIndex = data.indexOf(" "); + if(spaceIndex == -1) { + return(ERR_RESPONSE_MALFORMED); + } + String statusString = data.substring(spaceIndex + 1, spaceIndex + 4); + return(statusString.toInt()); +} + +uint8_t ESP8266::startTCP(const char* host, uint16_t port) { + String cmd = "AT+CIPSTART=\"TCP\",\""; + cmd += host; + cmd += "\","; + cmd += port; + if(!_mod->ATsendCommand(cmd)) { + return(ERR_UNKNOWN); + } + return(ERR_NONE); +} + +uint8_t ESP8266::closeTCP() { + if(!_mod->ATsendCommand("AT+CIPCLOSE")) { + return(ERR_UNKNOWN); + } + return(ERR_NONE); +} + +uint8_t ESP8266::send(String data) { + // send data length in bytes + String cmd = "AT+CIPSEND="; + cmd += data.length(); + if(!_mod->ATsendCommand(cmd)) { + return(ERR_UNKNOWN); + } + + // send data + if(!_mod->ATsendCommand(data)) { + return(ERR_UNKNOWN); + } + + return(ERR_NONE); +} + +String ESP8266::receive(uint32_t timeout) { + String data; + uint32_t start = millis(); + while(millis() - start < timeout) { + while(_mod->ModuleSerial->available() > 0) { + char c = _mod->ModuleSerial->read(); + #ifdef DEBUG + Serial.print(c); + #endif + data += c; + } + } + return(data); +} + diff --git a/src/ESP8266.h b/src/ESP8266.h new file mode 100644 index 00000000..f926bf1f --- /dev/null +++ b/src/ESP8266.h @@ -0,0 +1,24 @@ +#ifndef _KITELIB_ESP8266_H +#define _KITELIB_ESP8266_H + +#include "Module.h" + +class ESP8266 { + public: + ESP8266(Module* module); + + uint8_t begin(long speed); + uint8_t restart(); + uint8_t join(const char* ssid, const char* password); + uint16_t HttpGet(const char* url, String& data, uint16_t port = 80); + + uint8_t startTCP(const char* host, uint16_t port); + uint8_t closeTCP(); + uint8_t send(String data); + String receive(uint32_t timeout = 10000); + + private: + Module* _mod; +}; + +#endif diff --git a/src/HC05.cpp b/src/HC05.cpp new file mode 100644 index 00000000..1c981fe2 --- /dev/null +++ b/src/HC05.cpp @@ -0,0 +1,142 @@ +#include "HC05.h" + +HC05::HC05(Module* module) { + _mod = module; +} + +void HC05::begin(long speed) { + _mod->baudrate = speed; + _mod->init(USE_UART, INT_NONE); +} + +bool HC05::listen() { + return(_mod->ModuleSerial->listen()); +} + +void HC05::end() { + _mod->ModuleSerial->end(); +} + +bool HC05::isListening() { + return(_mod->ModuleSerial->isListening()); +} + +bool HC05::stopListening() { + return(_mod->ModuleSerial->stopListening()); +} + +bool HC05::overflow() { + return(_mod->ModuleSerial->overflow()); +} + +int HC05::peek() { + return(_mod->ModuleSerial->peek()); +} + +size_t HC05::write(uint8_t b) { + return(_mod->ModuleSerial->write(b)); +} + +int HC05::read() { + return(_mod->ModuleSerial->read()); +} + +int HC05::available() { + return(_mod->ModuleSerial->available()); +} + +void HC05::flush() { + _mod->ModuleSerial->flush(); +} + +size_t HC05::print(const __FlashStringHelper *ifsh) { + return(_mod->ModuleSerial->print(ifsh)); +} + +size_t HC05::print(const String &s) { + return(_mod->ModuleSerial->print(s)); +} + +size_t HC05::print(const char str[]) { + return(_mod->ModuleSerial->print(str)); +} + +size_t HC05::print(char c) { + return(_mod->ModuleSerial->print(c)); +} + +size_t HC05::print(unsigned char b, int base) { + return(_mod->ModuleSerial->print(b, base)); +} + +size_t HC05::print(int n, int base) { + return(_mod->ModuleSerial->print(n, base)); +} + +size_t HC05::print(unsigned int n, int base) { + return(_mod->ModuleSerial->print(n, base)); +} + +size_t HC05::print(long n, int base) { + return(_mod->ModuleSerial->print(n, base)); +} + +size_t HC05::print(unsigned long n, int base) { + return(_mod->ModuleSerial->print(n, base)); +} + +size_t HC05::print(double n, int digits) { + return(_mod->ModuleSerial->print(n, digits)); +} + +size_t HC05::print(const Printable& x) { + return(_mod->ModuleSerial->print(x)); +} + +size_t HC05::println(const __FlashStringHelper *ifsh) { + return(_mod->ModuleSerial->println(ifsh)); +} + +size_t HC05::println(const String &s) { + return(_mod->ModuleSerial->println(s)); +} + +size_t HC05::println(const char str[]) { + return(_mod->ModuleSerial->println(str)); +} + +size_t HC05::println(char c) { + return(_mod->ModuleSerial->println(c)); +} + +size_t HC05::println(unsigned char b, int base) { + return(_mod->ModuleSerial->println(b, base)); +} + +size_t HC05::println(int n, int base) { + return(_mod->ModuleSerial->println(n, base)); +} + +size_t HC05::println(unsigned int n, int base) { + return(_mod->ModuleSerial->println(n, base)); +} + +size_t HC05::println(long n, int base) { + return(_mod->ModuleSerial->println(n, base)); +} + +size_t HC05::println(unsigned long n, int base) { + return(_mod->ModuleSerial->println(n, base)); +} + +size_t HC05::println(double n, int digits) { + return(_mod->ModuleSerial->println(n, digits)); +} + +size_t HC05::println(const Printable& x) { + return(_mod->ModuleSerial->println(x)); +} + +size_t HC05::println(void) { + return(_mod->ModuleSerial->println()); +} diff --git a/src/HC05.h b/src/HC05.h new file mode 100644 index 00000000..a21dafff --- /dev/null +++ b/src/HC05.h @@ -0,0 +1,56 @@ +#ifndef _KITELIB_HC05_H +#define _KITELIB_HC05_H + +#include "Module.h" + +#include + +#include "WString.h" +#include "Printable.h" + +class HC05 { + public: + HC05(Module* module); + + void begin(long speed); + bool listen(); + void end(); + bool isListening(); + bool stopListening(); + bool overflow(); + int peek(); + size_t write(uint8_t); + int read(); + int available(); + void flush(); + + size_t print(const __FlashStringHelper *); + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); + + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(void); + + private: + Module* _mod; +}; + +#endif diff --git a/src/KiteLib.cpp b/src/KiteLib.cpp new file mode 100644 index 00000000..162fffce --- /dev/null +++ b/src/KiteLib.cpp @@ -0,0 +1,19 @@ +#include "KiteLib.h" + +/*Kite::Kite(ModuleType moduleA, ModuleType moduleB) { + switch(moduleA) { + case ModuleType::ESP8266: + ESP8266 = new class ESP8266(); + break; + case ModuleType::SX1278: + SX1278 = new class SX1278(); + break; + }; +}*/ + +KiteShield::KiteShield() { + ModuleA = new Module(KITE_CS_A, KITE_TX_A, KITE_RX_A, KITE_INT_0, KITE_INT_1); + ModuleB = new Module(KITE_CS_B, KITE_TX_B, KITE_RX_B, KITE_INT_0, KITE_INT_1); +} + +KiteShield Kite; diff --git a/src/KiteLib.h b/src/KiteLib.h new file mode 100644 index 00000000..491f8a39 --- /dev/null +++ b/src/KiteLib.h @@ -0,0 +1,35 @@ +#ifndef _KITELIB_H +#define _KITELIB_H + +#include "TypeDef.h" +#include "Module.h" + +#include "ESP8266.h" +#include "SX1278.h" +#include "SX1272.h" +#include "HC05.h" + +#define KITE_CS_A 10 +#define KITE_TX_A 9 +#define KITE_RX_A 8 +#define KITE_CS_B 5 +#define KITE_TX_B 7 +#define KITE_RX_B 6 +#define KITE_INT_0 2 +#define KITE_INT_1 3 + +class KiteShield { + public: + KiteShield(); + + Module* ModuleA; + Module* ModuleB; + + private: + + +}; + +extern KiteShield Kite; + +#endif diff --git a/src/Module.cpp b/src/Module.cpp new file mode 100644 index 00000000..f205e9ae --- /dev/null +++ b/src/Module.cpp @@ -0,0 +1,183 @@ +#include "Module.h" + +Module::Module() { + +} + +Module::Module(int cs, int tx, int rx, int int0, int int1) { + _cs = cs; + _tx = tx; + _rx = rx; + _int0 = int0; + _int1 = int1; + + ModuleSerial = new SoftwareSerial(_tx, _rx); +} + +uint8_t Module::init(uint8_t interface, uint8_t gpio) { + #ifdef DEBUG + Serial.begin(9600); + Serial.println(); + #endif + + switch(interface) { + case USE_SPI: + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); + SPI.begin(); + break; + case USE_UART: + ModuleSerial->begin(baudrate); + break; + case USE_I2C: + break; + } + + switch(gpio) { + case INT_NONE: + break; + case INT_0: + pinMode(_int0, INPUT); + break; + case INT_1: + pinMode(_int1, INPUT); + break; + case INT_BOTH: + pinMode(_int0, INPUT); + pinMode(_int1, INPUT); + break; + } +} + +void Module::ATemptyBuffer() { + while(ModuleSerial->available() > 0) { + ModuleSerial->read(); + } +} + +bool Module::ATsendCommand(String& cmd) { + return(ATsendCommand(cmd.c_str())); +} + +bool Module::ATsendCommand(const char* cmd) { + ATemptyBuffer(); + ModuleSerial->print(cmd); + ModuleSerial->print("\r\n"); + return(ATgetResponse()); +} + +bool Module::ATgetResponse() { + String data; + uint32_t start = millis(); + while (millis() - start < _ATtimeout) { + while(ModuleSerial->available() > 0) { + char c = ModuleSerial->read(); + #ifdef DEBUG + Serial.print(c); + #endif + data += c; + } + if(data.indexOf("OK") != -1) { + #ifdef DEBUG + Serial.println(); + #endif + return(true); + } else if (data.indexOf("ERROR") != -1) { + #ifdef DEBUG + Serial.println(); + #endif + return(false); + } + } + #ifdef DEBUG + Serial.println(); + #endif + return(false); +} + +uint8_t Module::SPIgetRegValue(uint8_t reg, uint8_t msb, uint8_t lsb) { + if((msb > 7) || (lsb > 7) || (lsb > msb)) { + return(ERR_INVALID_BIT_RANGE); + } + + uint8_t rawValue = SPIreadRegister(reg); + uint8_t maskedValue = rawValue & ((0b11111111 << lsb) & (0b11111111 >> (7 - msb))); + return(maskedValue); +} + +uint8_t Module::SPIreadRegisterBurst(uint8_t reg, uint8_t numBytes, uint8_t* inBytes) { + digitalWrite(_cs, LOW); + SPI.transfer(reg | SPI_READ); + for(uint8_t i = 0; i < numBytes; i++) { + inBytes[i] = SPI.transfer(reg); + } + digitalWrite(_cs, HIGH); + return(ERR_NONE); +} + +uint8_t Module::SPIreadRegisterBurstStr(uint8_t reg, uint8_t numBytes, char* inBytes) { + digitalWrite(_cs, LOW); + SPI.transfer(reg | SPI_READ); + for(uint8_t i = 0; i < numBytes; i++) { + inBytes[i] = SPI.transfer(reg); + } + digitalWrite(_cs, HIGH); + return(ERR_NONE); +} + +uint8_t Module::SPIreadRegister(uint8_t reg) { + uint8_t inByte; + digitalWrite(_cs, LOW); + SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); + SPI.transfer(reg | SPI_READ); + SPI.endTransaction(); + inByte = SPI.transfer(0x00); + digitalWrite(_cs, HIGH); + return(inByte); +} + +uint8_t Module::SPIsetRegValue(uint8_t reg, uint8_t value, uint8_t msb, uint8_t lsb) { + if((msb > 7) || (lsb > 7) || (lsb > msb)) { + return(ERR_INVALID_BIT_RANGE); + } + + uint8_t currentValue = SPIreadRegister(reg); + uint8_t newValue = currentValue & ((0b11111111 << (msb + 1)) & (0b11111111 >> (8 - lsb))); + SPIwriteRegister(reg, newValue | value); + return(ERR_NONE); +} + +void Module::SPIwriteRegisterBurst(uint8_t reg, uint8_t* data, uint8_t numBytes) { + digitalWrite(_cs, LOW); + SPI.transfer(reg | SPI_WRITE); + for(uint8_t i = 0; i < numBytes; i++) { + SPI.transfer(data[i]); + } + digitalWrite(_cs, HIGH); +} + +void Module::SPIwriteRegisterBurstStr(uint8_t reg, const char* data, uint8_t numBytes) { + digitalWrite(_cs, LOW); + SPI.transfer(reg | SPI_WRITE); + for(uint8_t i = 0; i < numBytes; i++) { + SPI.transfer(data[i]); + } + digitalWrite(_cs, HIGH); +} + +void Module::SPIwriteRegister(uint8_t reg, uint8_t data) { + digitalWrite(_cs, LOW); + SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); + SPI.transfer(reg | SPI_WRITE); + SPI.transfer(data); + SPI.endTransaction(); + digitalWrite(_cs, HIGH); +} + +bool Module::getInt0State() { + return(digitalRead(_int0)); +} + +bool Module::getInt1State() { + return(digitalRead(_int1)); +} diff --git a/src/Module.h b/src/Module.h new file mode 100644 index 00000000..37b2f1a8 --- /dev/null +++ b/src/Module.h @@ -0,0 +1,52 @@ +#ifndef _KITELIB_MODULE_H +#define _KITELIB_MODULE_H + +#include +#include +#include + +#include "TypeDef.h" + +#define SPI_READ 0b00000000 +#define SPI_WRITE 0b10000000 + +class Module { + public: + Module(); + Module(int cs, int tx, int rx, int int0, int int1); + + SoftwareSerial* ModuleSerial; + + uint32_t baudrate = 9600; + + uint8_t init(uint8_t interface, uint8_t gpio); + + void ATemptyBuffer(); + bool ATgetResponse(); + bool ATsendCommand(String& cmd); + bool ATsendCommand(const char* cmd); + + uint8_t SPIgetRegValue(uint8_t reg, uint8_t msb = 7, uint8_t lsb = 0); + uint8_t SPIreadRegisterBurst(uint8_t reg, uint8_t numBytes, uint8_t* inBytes); + uint8_t SPIreadRegisterBurstStr(uint8_t reg, uint8_t numBytes, char* str); + uint8_t SPIreadRegister(uint8_t reg); + + uint8_t SPIsetRegValue(uint8_t reg, uint8_t value, uint8_t msb = 7, uint8_t lsb = 0); + void SPIwriteRegisterBurst(uint8_t reg, uint8_t* data, uint8_t numBytes); + void SPIwriteRegisterBurstStr(uint8_t reg, const char* data, uint8_t numBytes); + void SPIwriteRegister(uint8_t reg, uint8_t data); + + bool getInt0State(); + bool getInt1State(); + + private: + int _cs; + int _tx; + int _rx; + int _int0; + int _int1; + + uint32_t _ATtimeout = 15000; +}; + +#endif diff --git a/src/Packet.cpp b/src/Packet.cpp new file mode 100644 index 00000000..3c461ec2 --- /dev/null +++ b/src/Packet.cpp @@ -0,0 +1,178 @@ +#include "Packet.h" + +Packet::Packet(void) { + uint8_t src[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + getLoRaAddress(src); + + uint8_t dest[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t dat[240]; + uint8_t len = 0; + + init(src, dest, dat, len); +} + +Packet::Packet(const char dest[24], const char dat[240]) { + uint8_t src[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + getLoRaAddress(src); + + uint8_t destTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + destTmp[i] = (parseByte(dest[3*i]) << 4) | parseByte(dest[3*i + 1]); + } + + init(src, destTmp, dat); +} + +Packet::Packet(uint8_t dest[8], const char dat[240]) { + uint8_t src[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + getLoRaAddress(src); + + init(src, dest, dat); +} + +Packet::Packet(const char src[24], const char dest[24], const char dat[240]) { + uint8_t srcTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + srcTmp[i] = (parseByte(src[3*i]) << 4) | parseByte(src[3*i + 1]); + } + + uint8_t destTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + destTmp[i] = (parseByte(dest[3*i]) << 4) | parseByte(dest[3*i + 1]); + } + + init(srcTmp, destTmp, dat); +} + +Packet::Packet(uint8_t src[8], uint8_t dest[8], const char dat[240]) { + init(src, dest, dat); +} + +Packet::Packet(const char dest[24], uint8_t* dat, uint8_t len) { + uint8_t src[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + getLoRaAddress(src); + + uint8_t destTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + destTmp[i] = (parseByte(dest[3*i]) << 4) | parseByte(dest[3*i + 1]); + } + + init(src, destTmp, dat, len); +} + +Packet::Packet(uint8_t dest[8], uint8_t* dat, uint8_t len) { + uint8_t src[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + getLoRaAddress(src); + + init(src, dest, dat, len); +} + +Packet::Packet(const char src[24], const char dest[24], uint8_t* dat, uint8_t len) { + uint8_t srcTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + srcTmp[i] = (parseByte(src[3*i]) << 4) | parseByte(src[3*i + 1]); + } + + uint8_t destTmp[8]; + for(uint8_t i = 0; i < 8; i++) { + destTmp[i] = (parseByte(dest[3*i]) << 4) | parseByte(dest[3*i + 1]); + } + + init(srcTmp, destTmp, dat, len); +} + +Packet::Packet(uint8_t src[8], uint8_t dest[8], uint8_t* dat, uint8_t len) { + init(src, dest, dat, len); +} + +void Packet::init(uint8_t src[8], uint8_t dest[8], const char dat[240]) { + for(uint8_t i = 0; i < 8; i++) { + source[i] = src[i]; + destination[i] = dest[i]; + } + + length = 0; + for(uint8_t i = 0; i < 240; i++) { + data[i] = dat[i]; + if(data[i] == '\0') { + length = i + 16; + break; + } + } +} + +void Packet::init(uint8_t src[8], uint8_t dest[8], uint8_t* dat, uint8_t len) { + length = len + 16; + for(uint8_t i = 0; i < 8; i++) { + source[i] = src[i]; + destination[i] = dest[i]; + } + + for(uint8_t i = 0; i < length; i++) { + data[i] = dat[i]; + } +} + +void Packet::getSourceStr(char src[24]) { + for(uint8_t i = 0; i < 8; i++) { + src[3*i] = reparseChar(source[i] >> 4); + src[3*i+1] = reparseChar(source[i] & 0x0F); + src[3*i+2] = ':'; + } + src[23] = '\0'; +} + +void Packet::getDestinationStr(char dest[24]) { + for(uint8_t i = 0; i < 8; i++) { + dest[3*i] = reparseChar(destination[i] >> 4); + dest[3*i+1] = reparseChar(destination[i] & 0x0F); + dest[3*i+2] = ':'; + } + dest[23] = '\0'; +} + +void Packet::setSourceStr(const char src[24]) { + for(uint8_t i = 0; i < 8; i++) { + source[i] = (parseByte(src[3*i]) << 4) | parseByte(src[3*i + 1]); + } +} + +void Packet::setDestinationStr(const char dest[24]) { + for(uint8_t i = 0; i < 8; i++) { + destination[i] = (parseByte(dest[3*i]) << 4) | parseByte(dest[3*i + 1]); + } +} + +void Packet::copyInto(Packet& pack) { + for(uint8_t i = 0; i < 8; i++) { + pack.source[i] = source[i]; + pack.destination[i] = destination[i]; + } + strcpy(pack.data, data); +} + +void Packet::getLoRaAddress(uint8_t addr[8]) { + for(uint8_t i = 0; i < 8; i++) { + addr[i] = EEPROM.read(i); + } +} + +uint8_t Packet::parseByte(char c) { + if((c >= 48) && (c <= 57)) { + return(c - 48); + } else if((c >= 65) && (c <= 70)) { + return(c - 55); + } else if((c >= 97) && (c <= 102)) { + return(c - 87); + } + return(0); +} + +char Packet::reparseChar(uint8_t b) { + if(b <= 9) { + return(b + 48); + } else if((b >= 10) && (b <= 16)) { + return(b + 55); + } + return(0); +} diff --git a/src/Packet.h b/src/Packet.h new file mode 100644 index 00000000..f4cb1f7c --- /dev/null +++ b/src/Packet.h @@ -0,0 +1,47 @@ +#ifndef _LORALIB_PACKET_H +#define _LORALIB_PACKET_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include + +class Packet { + public: + Packet(void); + Packet(const char dest[24], const char dat[240]); + Packet(uint8_t dest[8], const char dat[240]); + Packet(const char src[24], const char dest[24], const char dat[240]); + Packet(uint8_t src[8], uint8_t dest[8], const char dat[240]); + + Packet(const char dest[24], uint8_t* dat, uint8_t len); + Packet(uint8_t dest[8], uint8_t* dat, uint8_t len); + Packet(const char src[24], const char dest[24], uint8_t* dat, uint8_t len); + Packet(uint8_t src[8], uint8_t dest[8], uint8_t* dat, uint8_t len); + + uint8_t source[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t destination[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + char data[240]; + uint8_t length = 0; + + void getSourceStr(char src[24]); + void getDestinationStr(char dest[24]); + + void setSourceStr(const char src[24]); + void setDestinationStr(const char dest[24]); + + void copyInto(Packet& pack); + + private: + void init(uint8_t src[8], uint8_t dest[8], const char dat[240]); + void init(uint8_t src[8], uint8_t dest[8], uint8_t* dat, uint8_t len); + void getLoRaAddress(uint8_t addr[8]); + + uint8_t parseByte(char c); + char reparseChar(uint8_t b); +}; + +#endif diff --git a/src/SX1272.cpp b/src/SX1272.cpp new file mode 100644 index 00000000..761df096 --- /dev/null +++ b/src/SX1272.cpp @@ -0,0 +1,371 @@ +#include "SX1272.h" + +SX1272::SX1272(Module* module) { + _mod = module; +} + +uint8_t SX1272::begin(Bandwidth bw, SpreadingFactor sf, CodingRate cr, uint16_t addrEeprom) { + switch(bw) { + case BW_125_00_KHZ: + _bw = SX1272_BW_125_00_KHZ; + break; + case BW_250_00_KHZ: + _bw = SX1272_BW_250_00_KHZ; + break; + case BW_500_00_KHZ: + _bw = SX1272_BW_500_00_KHZ; + break; + default: + _bw = SX1272_BW_250_00_KHZ; + break; + } + + switch(sf) { + case SF_6: + _sf = SX1272_SF_6; + break; + case SF_7: + _sf = SX1272_SF_7; + break; + case SF_8: + _sf = SX1272_SF_8; + break; + case SF_9: + _sf = SX1272_SF_9; + break; + case SF_10: + _sf = SX1272_SF_10; + break; + case SF_11: + _sf = SX1272_SF_11; + break; + case SF_12: + _sf = SX1272_SF_12; + break; + default: + _sf = SX1272_SF_12; + break; + } + + switch(cr) { + case CR_4_5: + _cr = SX1272_CR_4_5; + break; + case CR_4_6: + _cr = SX1272_CR_4_6; + break; + case CR_4_7: + _cr = SX1272_CR_4_7; + break; + case CR_4_8: + _cr = SX1272_CR_4_8; + break; + default: + _cr = SX1272_CR_4_5; + break; + } + + #ifdef ESP32 + if(!EEPROM.begin(9)) { + #ifdef DEBUG + Serial.println("Unable to initialize EEPROM"); + #endif + return(ERR_EEPROM_NOT_INITIALIZED); + } + #endif + + _addrEeprom = addrEeprom; + + bool hasAddress = false; + for(uint16_t i = 0; i < 8; i++) { + if(EEPROM.read(_addrEeprom + i) != 255) { + hasAddress = true; + break; + } + } + + if(!hasAddress) { + randomSeed(analogRead(5)); + generateLoRaAdress(); + } + + #ifdef DEBUG + Serial.print("LoRa node address string: "); + #endif + for(uint8_t i = 0; i < 8; i++) { + _address[i] = EEPROM.read(i); + #ifdef DEBUG + Serial.print(_address[i], HEX); + if(i < 7) { + Serial.print(":"); + } else { + Serial.println(); + } + #endif + } + + _mod->init(USE_SPI, INT_BOTH); + + uint8_t i = 0; + bool flagFound = false; + while((i < 10) && !flagFound) { + uint8_t version = _mod->SPIreadRegister(SX1272_REG_VERSION); + if(version == 0x12) { + flagFound = true; + } else { + #ifdef DEBUG + Serial.print("SX1272 not found! ("); + Serial.print(i + 1); + Serial.print(" of 10 tries) SX1272_REG_VERSION == "); + + char buffHex[5]; + sprintf(buffHex, "0x%02X", version); + Serial.print(buffHex); + Serial.println(); + #endif + delay(1000); + i++; + } + } + + if(!flagFound) { + #ifdef DEBUG + Serial.println("No SX1272 found!"); + #endif + SPI.end(); + return(ERR_CHIP_NOT_FOUND); + } + #ifdef DEBUG + else { + Serial.println("Found SX1272! (match by SX1272_REG_VERSION == 0x12)"); + } + #endif + + return(config(_bw, _sf, _cr)); +} + +uint8_t SX1272::transmit(Packet& pack) { + char buffer[256]; + + for(uint8_t i = 0; i < 8; i++) { + buffer[i] = pack.source[i]; + buffer[i+8] = pack.destination[i]; + } + + for(uint8_t i = 0; i < pack.length; i++) { + buffer[i+16] = pack.data[i]; + } + + setMode(SX1272_STANDBY); + + _mod->SPIsetRegValue(SX1272_REG_DIO_MAPPING_1, SX1272_DIO0_TX_DONE, 7, 6); + clearIRQFlags(); + + if(pack.length > 256) { + return(ERR_PACKET_TOO_LONG); + } + + _mod->SPIsetRegValue(SX1272_REG_PAYLOAD_LENGTH, pack.length); + _mod->SPIsetRegValue(SX1272_REG_FIFO_TX_BASE_ADDR, SX1272_FIFO_TX_BASE_ADDR_MAX); + _mod->SPIsetRegValue(SX1272_REG_FIFO_ADDR_PTR, SX1272_FIFO_TX_BASE_ADDR_MAX); + + _mod->SPIwriteRegisterBurstStr(SX1272_REG_FIFO, buffer, pack.length); + + setMode(SX1272_TX); + + unsigned long start = millis(); + while(!_mod->getInt0State()) { + #ifdef DEBUG + Serial.print('.'); + #endif + } + + clearIRQFlags(); + + return(ERR_NONE); +} + +uint8_t SX1272::receive(Packet& pack) { + char buffer[256]; + uint32_t startTime = millis(); + + setMode(SX1272_STANDBY); + + _mod->SPIsetRegValue(SX1272_REG_DIO_MAPPING_1, SX1272_DIO0_RX_DONE | SX1272_DIO1_RX_TIMEOUT, 7, 4); + clearIRQFlags(); + + _mod->SPIsetRegValue(SX1272_REG_FIFO_RX_BASE_ADDR, SX1272_FIFO_RX_BASE_ADDR_MAX); + _mod->SPIsetRegValue(SX1272_REG_FIFO_ADDR_PTR, SX1272_FIFO_RX_BASE_ADDR_MAX); + + setMode(SX1272_RXSINGLE); + + while(!_mod->getInt0State()) { + if(_mod->getInt1State()) { + clearIRQFlags(); + return(ERR_RX_TIMEOUT); + } + } + + if(_mod->SPIgetRegValue(SX1272_REG_IRQ_FLAGS, 5, 5) == SX1272_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR) { + return(ERR_CRC_MISMATCH); + } + + uint8_t headerMode = _mod->SPIgetRegValue(SX1272_REG_MODEM_CONFIG_1, 0, 0); + if(headerMode == SX1272_HEADER_EXPL_MODE) { + pack.length = _mod->SPIgetRegValue(SX1272_REG_RX_NB_BYTES); + } + + _mod->SPIreadRegisterBurstStr(SX1272_REG_FIFO, pack.length, buffer); + + clearIRQFlags(); + + for(uint8_t i = 0; i < 8; i++) { + pack.source[i] = buffer[i]; + pack.destination[i] = buffer[i+8]; + } + + for(uint8_t i = 16; i < pack.length; i++) { + pack.data[i-16] = buffer[i]; + } + pack.data[pack.length-16] = 0; + + uint32_t elapsedTime = millis() - startTime; + dataRate = (pack.length*8.0)/((float)elapsedTime/1000.0); + lastPacketRSSI = getLastPacketRSSI(); + + return(ERR_NONE); +} + +uint8_t SX1272::sleep() { + return(setMode(0b00000000)); +} + +uint8_t SX1272::standby() { + return(setMode(0b00000001)); +} + +uint8_t SX1272::setBandwidth(Bandwidth bw) { + return(config(bw, _sf, _cr)); +} + +uint8_t SX1272::setSpreadingFactor(SpreadingFactor sf) { + return(config(_bw, sf, _cr)); +} + +uint8_t SX1272::setCodingRate(CodingRate cr) { + return(config(_bw, _sf, cr)); +} + +void SX1272::generateLoRaAdress() { + for(uint8_t i = _addrEeprom; i < (_addrEeprom + 8); i++) { + EEPROM.write(i, (uint8_t)random(0, 256)); + } +} + +uint8_t SX1272::config(uint8_t bw, uint8_t sf, uint8_t cr) { + uint8_t status = ERR_NONE; + + //check the supplied bw, cr and sf values + if((bw != SX1272_BW_125_00_KHZ) && + (bw != SX1272_BW_250_00_KHZ) && + (bw != SX1272_BW_500_00_KHZ)) { + return(ERR_INVALID_BANDWIDTH); + } + + if((sf != SX1272_SF_6) && + (sf != SX1272_SF_7) && + (sf != SX1272_SF_8) && + (sf != SX1272_SF_9) && + (sf != SX1272_SF_10) && + (sf != SX1272_SF_11) && + (sf != SX1272_SF_12)) { + return(ERR_INVALID_SPREADING_FACTOR); + } + + if((cr != SX1272_CR_4_5) && + (cr != SX1272_CR_4_6) && + (cr != SX1272_CR_4_7) && + (cr != SX1272_CR_4_8)) { + return(ERR_INVALID_CODING_RATE); + } + + // set mode to SLEEP + status = setMode(SX1272_SLEEP); + if(status != ERR_NONE) { + return(status); + } + + // set LoRa mode + status = _mod->SPIsetRegValue(SX1272_REG_OP_MODE, SX1272_LORA, 7, 7); + if(status != ERR_NONE) { + return(status); + } + + // set carrier frequency + status = _mod->SPIsetRegValue(SX1272_REG_FRF_MSB, SX1272_FRF_MSB); + status = _mod->SPIsetRegValue(SX1272_REG_FRF_MID, SX1272_FRF_MID); + status = _mod->SPIsetRegValue(SX1272_REG_FRF_LSB, SX1272_FRF_LSB); + if(status != ERR_NONE) { + return(status); + } + + // output power configuration + status = _mod->SPIsetRegValue(SX1272_REG_PA_CONFIG, SX1272_PA_SELECT_BOOST | SX1272_OUTPUT_POWER); + status = _mod->SPIsetRegValue(SX1272_REG_OCP, SX1272_OCP_ON | SX1272_OCP_TRIM, 5, 0); + status = _mod->SPIsetRegValue(SX1272_REG_LNA, SX1272_LNA_GAIN_1 | SX1272_LNA_BOOST_ON); + status = _mod->SPIsetRegValue(SX1272_REG_PA_DAC, SX1272_PA_BOOST_ON, 2, 0); + if(status != ERR_NONE) { + return(status); + } + + // turn off frequency hopping + status = _mod->SPIsetRegValue(SX1272_REG_HOP_PERIOD, SX1272_HOP_PERIOD_OFF); + if(status != ERR_NONE) { + return(status); + } + + // basic setting (bw, cr, sf, header mode and CRC) + if(sf == SX1272_SF_6) { + status = _mod->SPIsetRegValue(SX1272_REG_MODEM_CONFIG_2, SX1272_SF_6 | SX1272_TX_MODE_SINGLE | SX1272_RX_CRC_MODE_OFF, 7, 2); + status = _mod->SPIsetRegValue(SX1272_REG_MODEM_CONFIG_1, bw | cr | SX1272_HEADER_IMPL_MODE); + status = _mod->SPIsetRegValue(SX1272_REG_DETECT_OPTIMIZE, SX1272_DETECT_OPTIMIZE_SF_6, 2, 0); + status = _mod->SPIsetRegValue(SX1272_REG_DETECTION_THRESHOLD, SX1272_DETECTION_THRESHOLD_SF_6); + } else { + status = _mod->SPIsetRegValue(SX1272_REG_MODEM_CONFIG_2, sf | SX1272_TX_MODE_SINGLE | SX1272_RX_CRC_MODE_ON, 7, 2); + status = _mod->SPIsetRegValue(SX1272_REG_MODEM_CONFIG_1, bw | cr | SX1272_HEADER_EXPL_MODE); + status = _mod->SPIsetRegValue(SX1272_REG_DETECT_OPTIMIZE, SX1272_DETECT_OPTIMIZE_SF_7_12, 2, 0); + status = _mod->SPIsetRegValue(SX1272_REG_DETECTION_THRESHOLD, SX1272_DETECTION_THRESHOLD_SF_7_12); + } + + if(status != ERR_NONE) { + return(status); + } + + // set default preamble length + status = _mod->SPIsetRegValue(SX1272_REG_PREAMBLE_MSB, SX1272_PREAMBLE_LENGTH_MSB); + status = _mod->SPIsetRegValue(SX1272_REG_PREAMBLE_LSB, SX1272_PREAMBLE_LENGTH_LSB); + if(status != ERR_NONE) { + return(status); + } + + // set mode to STANDBY + status = setMode(SX1272_STANDBY); + if(status != ERR_NONE) { + return(status); + } + + return(ERR_NONE); +} + +uint8_t SX1272::setMode(uint8_t mode) { + _mod->SPIsetRegValue(SX1272_REG_OP_MODE, mode, 2, 0); + return(ERR_NONE); +} + +void SX1272::clearIRQFlags() { + _mod->SPIwriteRegister(SX1272_REG_IRQ_FLAGS, 0b11111111); +} + +int8_t SX1272::getLastPacketRSSI() { + return(-164 + _mod->SPIgetRegValue(SX1272_REG_PKT_RSSI_VALUE)); +} diff --git a/src/SX1272.h b/src/SX1272.h new file mode 100644 index 00000000..1b656cc6 --- /dev/null +++ b/src/SX1272.h @@ -0,0 +1,229 @@ +#ifndef _KITELIB_SX1272_H +#define _KITELIB_SX1272_H + +#include + +#include "TypeDef.h" +#include "Module.h" +#include "Packet.h" + +//SX1272 register map +#define SX1272_REG_FIFO 0x00 +#define SX1272_REG_OP_MODE 0x01 +#define SX1272_REG_FRF_MSB 0x06 +#define SX1272_REG_FRF_MID 0x07 +#define SX1272_REG_FRF_LSB 0x08 +#define SX1272_REG_PA_CONFIG 0x09 +#define SX1272_REG_PA_RAMP 0x0A +#define SX1272_REG_OCP 0x0B +#define SX1272_REG_LNA 0x0C +#define SX1272_REG_FIFO_ADDR_PTR 0x0D +#define SX1272_REG_FIFO_TX_BASE_ADDR 0x0E +#define SX1272_REG_FIFO_RX_BASE_ADDR 0x0F +#define SX1272_REG_FIFO_RX_CURRENT_ADDR 0x10 +#define SX1272_REG_IRQ_FLAGS_MASK 0x11 +#define SX1272_REG_IRQ_FLAGS 0x12 +#define SX1272_REG_RX_NB_BYTES 0x13 +#define SX1272_REG_RX_HEADER_CNT_VALUE_MSB 0x14 +#define SX1272_REG_RX_HEADER_CNT_VALUE_LSB 0x15 +#define SX1272_REG_RX_PACKET_CNT_VALUE_MSB 0x16 +#define SX1272_REG_RX_PACKET_CNT_VALUE_LSB 0x17 +#define SX1272_REG_MODEM_STAT 0x18 +#define SX1272_REG_PKT_SNR_VALUE 0x19 +#define SX1272_REG_PKT_RSSI_VALUE 0x1A +#define SX1272_REG_RSSI_VALUE 0x1B +#define SX1272_REG_HOP_CHANNEL 0x1C +#define SX1272_REG_MODEM_CONFIG_1 0x1D +#define SX1272_REG_MODEM_CONFIG_2 0x1E +#define SX1272_REG_SYMB_TIMEOUT_LSB 0x1F +#define SX1272_REG_PREAMBLE_MSB 0x20 +#define SX1272_REG_PREAMBLE_LSB 0x21 +#define SX1272_REG_PAYLOAD_LENGTH 0x22 +#define SX1272_REG_MAX_PAYLOAD_LENGTH 0x23 +#define SX1272_REG_HOP_PERIOD 0x24 +#define SX1272_REG_FIFO_RX_BYTE_ADDR 0x25 +#define SX1272_REG_FEI_MSB 0x28 +#define SX1272_REG_FEI_MID 0x29 +#define SX1272_REG_FEI_LSB 0x2A +#define SX1272_REG_RSSI_WIDEBAND 0x2C +#define SX1272_REG_DETECT_OPTIMIZE 0x31 +#define SX1272_REG_INVERT_IQ 0x33 +#define SX1272_REG_DETECTION_THRESHOLD 0x37 +#define SX1272_REG_SYNC_WORD 0x39 +#define SX1272_REG_DIO_MAPPING_1 0x40 +#define SX1272_REG_DIO_MAPPING_2 0x41 +#define SX1272_REG_VERSION 0x42 +#define SX1272_REG_AGC_REF 0x43 +#define SX1272_REG_AGC_THRESH_1 0x44 +#define SX1272_REG_AGC_THRESH_2 0x45 +#define SX1272_REG_AGC_THRESH_3 0x46 +#define SX1272_REG_PLL_HOP 0x4B +#define SX1272_REG_TCXO 0x58 +#define SX1272_REG_PA_DAC 0x5A +#define SX1272_REG_PLL 0x5C +#define SX1272_REG_PLL_LOW_PN 0x5E +#define SX1272_REG_FORMER_TEMP 0x6C +#define SX1272_REG_BIT_RATE_FRAC 0x70 + +//SX1272 LoRa modem settings +//SX1272_REG_OP_MODE MSB LSB DESCRIPTION +#define SX1272_FSK_OOK 0b00000000 // 7 7 FSK/OOK mode +#define SX1272_LORA 0b10000000 // 7 7 LoRa mode +#define SX1272_ACCESS_SHARED_REG_OFF 0b00000000 // 6 6 access LoRa registers (0x0D:0x3F) in LoRa mode +#define SX1272_ACCESS_SHARED_REG_ON 0b01000000 // 6 6 access FSK registers (0x0D:0x3F) in LoRa mode +#define SX1272_SLEEP 0b00000000 // 2 0 sleep +#define SX1272_STANDBY 0b00000001 // 2 0 standby +#define SX1272_FSTX 0b00000010 // 2 0 frequency synthesis TX +#define SX1272_TX 0b00000011 // 2 0 transmit +#define SX1272_FSRX 0b00000100 // 2 0 frequency synthesis RX +#define SX1272_RXCONTINUOUS 0b00000101 // 2 0 receive continuous +#define SX1272_RXSINGLE 0b00000110 // 2 0 receive single +#define SX1272_CAD 0b00000111 // 2 0 channel activity detection + +//SX1272_REG_FRF_MSB + REG_FRF_MID + REG_FRF_LSB +#define SX1272_FRF_MSB 0xE4 // 7 0 carrier frequency setting: f_RF = (F(XOSC) * FRF)/2^19 +#define SX1272_FRF_MID 0xC0 // 7 0 where F(XOSC) = 32 MHz +#define SX1272_FRF_LSB 0x00 // 7 0 FRF = 3 byte value of FRF registers + +//SX1272_REG_PA_CONFIG +#define SX1272_PA_SELECT_RFO 0b00000000 // 7 7 RFIO pin output, power limited to +13 dBm +#define SX1272_PA_SELECT_BOOST 0b10000000 // 7 7 PA_BOOST pin output, power limited to +20 dBm +#define SX1272_OUTPUT_POWER 0b00001111 // 3 0 output power: P_out = -1 + OUTPUT_POWER [dBm] for PA_SELECT_BOOST + +//SX1272_REG_OCP +#define SX1272_OCP_OFF 0b00000000 // 5 5 PA overload current protection disabled +#define SX1272_OCP_ON 0b00100000 // 5 5 PA overload current protection enabled +#define SX1272_OCP_TRIM 0b00001011 // 4 0 OCP current: I_max(OCP_TRIM = 0b1011) = 100 mA + +//SX1272_REG_LNA +#define SX1272_LNA_GAIN_0 0b00000000 // 7 5 LNA gain setting: not used +#define SX1272_LNA_GAIN_1 0b00100000 // 7 5 max gain +#define SX1272_LNA_GAIN_2 0b01000000 // 7 5 . +#define SX1272_LNA_GAIN_3 0b01100000 // 7 5 . +#define SX1272_LNA_GAIN_4 0b10000000 // 7 5 . +#define SX1272_LNA_GAIN_5 0b10100000 // 7 5 . +#define SX1272_LNA_GAIN_6 0b11000000 // 7 5 min gain +#define SX1272_LNA_GAIN_7 0b11100000 // 7 5 not used +#define SX1272_LNA_BOOST_OFF 0b00000000 // 1 0 default LNA current +#define SX1272_LNA_BOOST_ON 0b00000011 // 1 0 150% LNA current + +//SX1272_REG_MODEM_CONFIG_1 +#define SX1272_BW_125_00_KHZ 0b00000000 // 7 6 bandwidth: 125 kHz +#define SX1272_BW_250_00_KHZ 0b01000000 // 7 6 250 kHz +#define SX1272_BW_500_00_KHZ 0b10000000 // 7 6 500 kHz +#define SX1272_CR_4_5 0b00001000 // 5 3 error coding rate: 4/5 +#define SX1272_CR_4_6 0b00010000 // 5 3 4/6 +#define SX1272_CR_4_7 0b00011000 // 5 3 4/7 +#define SX1272_CR_4_8 0b00100000 // 5 3 4/8 +#define SX1272_HEADER_EXPL_MODE 0b00000000 // 2 2 explicit header mode +#define SX1272_HEADER_IMPL_MODE 0b00000100 // 2 2 implicit header mode +#define SX1272_RX_CRC_MODE_OFF 0b00000000 // 1 1 CRC disabled +#define SX1272_RX_CRC_MODE_ON 0b00000010 // 1 1 CRC enabled +#define SX1272_LOW_DATA_RATE_OPT_OFF 0b00000000 // 0 0 low data rate optimization disabled +#define SX1272_LOW_DATA_RATE_OPT_ON 0b00000001 // 0 0 low data rate optimization enabled, mandatory for SF 11 and 12 with BW 125 kHz + +//SX1272_REG_MODEM_CONFIG_2 +#define SX1272_SF_6 0b01100000 // 7 4 spreading factor: 64 chips/bit +#define SX1272_SF_7 0b01110000 // 7 4 128 chips/bit +#define SX1272_SF_8 0b10000000 // 7 4 256 chips/bit +#define SX1272_SF_9 0b10010000 // 7 4 512 chips/bit +#define SX1272_SF_10 0b10100000 // 7 4 1024 chips/bit +#define SX1272_SF_11 0b10110000 // 7 4 2048 chips/bit +#define SX1272_SF_12 0b11000000 // 7 4 4096 chips/bit +#define SX1272_TX_MODE_SINGLE 0b00000000 // 3 3 single TX +#define SX1272_TX_MODE_CONT 0b00001000 // 3 3 continuous TX +#define SX1272_AGC_AUTO_OFF 0b00000000 // 2 2 LNA gain set by REG_LNA +#define SX1272_AGC_AUTO_ON 0b00000100 // 2 2 LNA gain set by internal AGC loop +#define SX1272_RX_TIMEOUT_MSB 0b00000000 // 1 0 + +//SX1272_REG_SYMB_TIMEOUT_LSB +#define SX1272_RX_TIMEOUT_LSB 0b01100100 // 7 0 10 bit RX operation timeout + +//SX1272_REG_PREAMBLE_MSB + REG_PREAMBLE_LSB +#define SX1272_PREAMBLE_LENGTH_MSB 0b00000000 // 7 0 2 byte preamble length setting: l_P = PREAMBLE_LENGTH + 4.25 +#define SX1272_PREAMBLE_LENGTH_LSB 0b00001000 // 7 0 where l_p = preamble length + +//SX1272_REG_DETECT_OPTIMIZE +#define SX1272_DETECT_OPTIMIZE_SF_6 0b00000101 // 2 0 SF6 detection optimization +#define SX1272_DETECT_OPTIMIZE_SF_7_12 0b00000011 // 2 0 SF7 to SF12 detection optimization + +//SX1272_REG_DETECTION_THRESHOLD +#define SX1272_DETECTION_THRESHOLD_SF_6 0b00001100 // 7 0 SF6 detection threshold +#define SX1272_DETECTION_THRESHOLD_SF_7_12 0b00001010 // 7 0 SF7 to SF12 detection threshold + +//SX1272_REG_PA_DAC +#define SX1272_PA_BOOST_OFF 0b00000100 // 2 0 PA_BOOST disabled +#define SX1272_PA_BOOST_ON 0b00000111 // 2 0 +20 dBm on PA_BOOST when OUTPUT_POWER = 0b1111 + +//SX1272_REG_HOP_PERIOD +#define SX1272_HOP_PERIOD_OFF 0b00000000 // 7 0 number of periods between frequency hops; 0 = disabled +#define SX1272_HOP_PERIOD_MAX 0b11111111 // 7 0 + +//SX1272_REG_DIO_MAPPING_1 +#define SX1272_DIO0_RX_DONE 0b00000000 // 7 6 +#define SX1272_DIO0_TX_DONE 0b01000000 // 7 6 +#define SX1272_DIO0_CAD_DONE 0b10000000 // 7 6 +#define SX1272_DIO1_RX_TIMEOUT 0b00000000 // 5 4 +#define SX1272_DIO1_FHSS_CHANGE_CHANNEL 0b00010000 // 5 4 +#define SX1272_DIO1_CAD_DETECTED 0b00100000 // 5 4 + +//SX1272_REG_IRQ_FLAGS +#define SX1272_CLEAR_IRQ_FLAG_RX_TIMEOUT 0b10000000 // 7 7 timeout +#define SX1272_CLEAR_IRQ_FLAG_RX_DONE 0b01000000 // 6 6 packet reception complete +#define SX1272_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR 0b00100000 // 5 5 payload CRC error +#define SX1272_CLEAR_IRQ_FLAG_VALID_HEADER 0b00010000 // 4 4 valid header received +#define SX1272_CLEAR_IRQ_FLAG_TX_DONE 0b00001000 // 3 3 payload transmission complete +#define SX1272_CLEAR_IRQ_FLAG_CAD_DONE 0b00000100 // 2 2 CAD complete +#define SX1272_CLEAR_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b00000010 // 1 1 FHSS change channel +#define SX1272_CLEAR_IRQ_FLAG_CAD_DETECTED 0b00000001 // 0 0 valid LoRa signal detected during CAD operation + +//SX1272_REG_IRQ_FLAGS_MASK +#define SX1272_MASK_IRQ_FLAG_RX_TIMEOUT 0b01111111 // 7 7 timeout +#define SX1272_MASK_IRQ_FLAG_RX_DONE 0b10111111 // 6 6 packet reception complete +#define SX1272_MASK_IRQ_FLAG_PAYLOAD_CRC_ERROR 0b11011111 // 5 5 payload CRC error +#define SX1272_MASK_IRQ_FLAG_VALID_HEADER 0b11101111 // 4 4 valid header received +#define SX1272_MASK_IRQ_FLAG_TX_DONE 0b11110111 // 3 3 payload transmission complete +#define SX1272_MASK_IRQ_FLAG_CAD_DONE 0b11111011 // 2 2 CAD complete +#define SX1272_MASK_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b11111101 // 1 1 FHSS change channel +#define SX1272_MASK_IRQ_FLAG_CAD_DETECTED 0b11111110 // 0 0 valid LoRa signal detected during CAD operation + +//SX1272_REG_FIFO_TX_BASE_ADDR +#define SX1272_FIFO_TX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for TX only + +//SX1272_REG_FIFO_RX_BASE_ADDR +#define SX1272_FIFO_RX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for RX only + + +class SX1272 { + public: + SX1272(Module* module); + + float dataRate; + int8_t lastPacketRSSI; + + uint8_t begin(Bandwidth bw = BW_125_00_KHZ, SpreadingFactor sf = SF_9, CodingRate cr = CR_4_7, uint16_t addrEeprom = 0); + uint8_t transmit(Packet& pack); + uint8_t receive(Packet& pack); + + uint8_t sleep(); + uint8_t standby(); + + uint8_t setBandwidth(Bandwidth bw); + uint8_t setSpreadingFactor(SpreadingFactor sf); + uint8_t setCodingRate(CodingRate cr); + + private: + Module* _mod; + uint8_t _bw, _sf, _cr; + + uint8_t _address[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint16_t _addrEeprom; + + void generateLoRaAdress(); + uint8_t config(uint8_t bw, uint8_t sf, uint8_t cr); + uint8_t setMode(uint8_t mode); + void clearIRQFlags(); + int8_t getLastPacketRSSI(); +}; + +#endif diff --git a/src/SX1278.cpp b/src/SX1278.cpp new file mode 100644 index 00000000..353cc1c1 --- /dev/null +++ b/src/SX1278.cpp @@ -0,0 +1,399 @@ +#include "SX1278.h" + +SX1278::SX1278(Module* module) { + _mod = module; +} + +uint8_t SX1278::begin(Bandwidth bw, SpreadingFactor sf, CodingRate cr, uint16_t addrEeprom) { + switch(bw) { + case BW_7_80_KHZ: + _bw = SX1278_BW_7_80_KHZ; + break; + case BW_10_40_KHZ: + _bw = SX1278_BW_10_40_KHZ; + break; + case BW_15_60_KHZ: + _bw = SX1278_BW_15_60_KHZ; + break; + case BW_20_80_KHZ: + _bw = SX1278_BW_20_80_KHZ; + break; + case BW_31_25_KHZ: + _bw = SX1278_BW_31_25_KHZ; + break; + case BW_41_70_KHZ: + _bw = SX1278_BW_41_70_KHZ; + break; + case BW_62_50_KHZ: + _bw = SX1278_BW_62_50_KHZ; + break; + case BW_125_00_KHZ: + _bw = SX1278_BW_125_00_KHZ; + break; + case BW_250_00_KHZ: + _bw = SX1278_BW_250_00_KHZ; + break; + case BW_500_00_KHZ: + _bw = SX1278_BW_500_00_KHZ; + break; + default: + _bw = SX1278_BW_250_00_KHZ; + break; + } + + switch(sf) { + case SF_6: + _sf = SX1278_SF_6; + break; + case SF_7: + _sf = SX1278_SF_7; + break; + case SF_8: + _sf = SX1278_SF_8; + break; + case SF_9: + _sf = SX1278_SF_9; + break; + case SF_10: + _sf = SX1278_SF_10; + break; + case SF_11: + _sf = SX1278_SF_11; + break; + case SF_12: + _sf = SX1278_SF_12; + break; + default: + _sf = SX1278_SF_12; + break; + } + + switch(cr) { + case CR_4_5: + _cr = SX1278_CR_4_5; + break; + case CR_4_6: + _cr = SX1278_CR_4_6; + break; + case CR_4_7: + _cr = SX1278_CR_4_7; + break; + case CR_4_8: + _cr = SX1278_CR_4_8; + break; + default: + _cr = SX1278_CR_4_5; + break; + } + + #ifdef ESP32 + if(!EEPROM.begin(9)) { + #ifdef DEBUG + Serial.println("Unable to initialize EEPROM"); + #endif + return(ERR_EEPROM_NOT_INITIALIZED); + } + #endif + + _addrEeprom = addrEeprom; + + bool hasAddress = false; + for(uint16_t i = 0; i < 8; i++) { + if(EEPROM.read(_addrEeprom + i) != 255) { + hasAddress = true; + break; + } + } + + if(!hasAddress) { + randomSeed(analogRead(5)); + generateLoRaAdress(); + } + + #ifdef DEBUG + Serial.print("LoRa node address string: "); + #endif + for(uint8_t i = 0; i < 8; i++) { + _address[i] = EEPROM.read(i); + #ifdef DEBUG + Serial.print(_address[i], HEX); + if(i < 7) { + Serial.print(":"); + } else { + Serial.println(); + } + #endif + } + + _mod->init(USE_SPI, INT_BOTH); + + uint8_t i = 0; + bool flagFound = false; + while((i < 10) && !flagFound) { + uint8_t version = _mod->SPIreadRegister(SX1278_REG_VERSION); + if(version == 0x12) { + flagFound = true; + } else { + #ifdef DEBUG + Serial.print("SX1278 not found! ("); + Serial.print(i + 1); + Serial.print(" of 10 tries) SX1278_REG_VERSION == "); + + char buffHex[5]; + sprintf(buffHex, "0x%02X", version); + Serial.print(buffHex); + Serial.println(); + #endif + delay(1000); + i++; + } + } + + if(!flagFound) { + #ifdef DEBUG + Serial.println("No SX1278 found!"); + #endif + SPI.end(); + return(ERR_CHIP_NOT_FOUND); + } + #ifdef DEBUG + else { + Serial.println("Found SX1278! (match by SX1278_REG_VERSION == 0x12)"); + } + #endif + + return(config(_bw, _sf, _cr)); +} + +uint8_t SX1278::transmit(Packet& pack) { + char buffer[256]; + + for(uint8_t i = 0; i < 8; i++) { + buffer[i] = pack.source[i]; + buffer[i+8] = pack.destination[i]; + } + + for(uint8_t i = 0; i < pack.length; i++) { + buffer[i+16] = pack.data[i]; + } + + setMode(SX1278_STANDBY); + + _mod->SPIsetRegValue(SX1278_REG_DIO_MAPPING_1, SX1278_DIO0_TX_DONE, 7, 6); + clearIRQFlags(); + + if(pack.length > 256) { + return(ERR_PACKET_TOO_LONG); + } + + _mod->SPIsetRegValue(SX1278_REG_PAYLOAD_LENGTH, pack.length); + _mod->SPIsetRegValue(SX1278_REG_FIFO_TX_BASE_ADDR, SX1278_FIFO_TX_BASE_ADDR_MAX); + _mod->SPIsetRegValue(SX1278_REG_FIFO_ADDR_PTR, SX1278_FIFO_TX_BASE_ADDR_MAX); + + _mod->SPIwriteRegisterBurstStr(SX1278_REG_FIFO, buffer, pack.length); + + setMode(SX1278_TX); + + unsigned long start = millis(); + while(!_mod->getInt0State()) { + #ifdef DEBUG + Serial.print('.'); + #endif + } + + clearIRQFlags(); + + return(ERR_NONE); +} + +uint8_t SX1278::receive(Packet& pack) { + char buffer[256]; + uint32_t startTime = millis(); + + setMode(SX1278_STANDBY); + + _mod->SPIsetRegValue(SX1278_REG_DIO_MAPPING_1, SX1278_DIO0_RX_DONE | SX1278_DIO1_RX_TIMEOUT, 7, 4); + clearIRQFlags(); + + _mod->SPIsetRegValue(SX1278_REG_FIFO_RX_BASE_ADDR, SX1278_FIFO_RX_BASE_ADDR_MAX); + _mod->SPIsetRegValue(SX1278_REG_FIFO_ADDR_PTR, SX1278_FIFO_RX_BASE_ADDR_MAX); + + setMode(SX1278_RXSINGLE); + + while(!_mod->getInt0State()) { + if(_mod->getInt1State()) { + clearIRQFlags(); + return(ERR_RX_TIMEOUT); + } + } + + if(_mod->SPIgetRegValue(SX1278_REG_IRQ_FLAGS, 5, 5) == SX1278_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR) { + return(ERR_CRC_MISMATCH); + } + + uint8_t headerMode = _mod->SPIgetRegValue(SX1278_REG_MODEM_CONFIG_1, 0, 0); + if(headerMode == SX1278_HEADER_EXPL_MODE) { + pack.length = _mod->SPIgetRegValue(SX1278_REG_RX_NB_BYTES); + } + + _mod->SPIreadRegisterBurstStr(SX1278_REG_FIFO, pack.length, buffer); + + clearIRQFlags(); + + for(uint8_t i = 0; i < 8; i++) { + pack.source[i] = buffer[i]; + pack.destination[i] = buffer[i+8]; + } + + for(uint8_t i = 16; i < pack.length; i++) { + pack.data[i-16] = buffer[i]; + } + pack.data[pack.length-16] = 0; + + uint32_t elapsedTime = millis() - startTime; + dataRate = (pack.length*8.0)/((float)elapsedTime/1000.0); + lastPacketRSSI = getLastPacketRSSI(); + + return(ERR_NONE); +} + +uint8_t SX1278::sleep() { + return(setMode(0b00000000)); +} + +uint8_t SX1278::standby() { + return(setMode(0b00000001)); +} + +uint8_t SX1278::setBandwidth(Bandwidth bw) { + return(config(bw, _sf, _cr)); +} + +uint8_t SX1278::setSpreadingFactor(SpreadingFactor sf) { + return(config(_bw, sf, _cr)); +} + +uint8_t SX1278::setCodingRate(CodingRate cr) { + return(config(_bw, _sf, cr)); +} + +void SX1278::generateLoRaAdress() { + for(uint8_t i = _addrEeprom; i < (_addrEeprom + 8); i++) { + EEPROM.write(i, (uint8_t)random(0, 256)); + } +} + +uint8_t SX1278::config(uint8_t bw, uint8_t sf, uint8_t cr) { + uint8_t status = ERR_NONE; + + //check the supplied bw, cr and sf values + if((bw != SX1278_BW_7_80_KHZ) && + (bw != SX1278_BW_10_40_KHZ) && + (bw != SX1278_BW_15_60_KHZ) && + (bw != SX1278_BW_20_80_KHZ) && + (bw != SX1278_BW_31_25_KHZ) && + (bw != SX1278_BW_41_70_KHZ) && + (bw != SX1278_BW_62_50_KHZ) && + (bw != SX1278_BW_125_00_KHZ) && + (bw != SX1278_BW_250_00_KHZ) && + (bw != SX1278_BW_500_00_KHZ)) { + return(ERR_INVALID_BANDWIDTH); + } + + if((sf != SX1278_SF_6) && + (sf != SX1278_SF_7) && + (sf != SX1278_SF_8) && + (sf != SX1278_SF_9) && + (sf != SX1278_SF_10) && + (sf != SX1278_SF_11) && + (sf != SX1278_SF_12)) { + return(ERR_INVALID_SPREADING_FACTOR); + } + + if((cr != SX1278_CR_4_5) && + (cr != SX1278_CR_4_6) && + (cr != SX1278_CR_4_7) && + (cr != SX1278_CR_4_8)) { + return(ERR_INVALID_CODING_RATE); + } + + // set mode to SLEEP + status = setMode(SX1278_SLEEP); + if(status != ERR_NONE) { + return(status); + } + + // set LoRa mode + status = _mod->SPIsetRegValue(SX1278_REG_OP_MODE, SX1278_LORA, 7, 7); + if(status != ERR_NONE) { + return(status); + } + + // set carrier frequency + status = _mod->SPIsetRegValue(SX1278_REG_FRF_MSB, SX1278_FRF_MSB); + status = _mod->SPIsetRegValue(SX1278_REG_FRF_MID, SX1278_FRF_MID); + status = _mod->SPIsetRegValue(SX1278_REG_FRF_LSB, SX1278_FRF_LSB); + if(status != ERR_NONE) { + return(status); + } + + // output power configuration + status = _mod->SPIsetRegValue(SX1278_REG_PA_CONFIG, SX1278_PA_SELECT_BOOST | SX1278_MAX_POWER | SX1278_OUTPUT_POWER); + status = _mod->SPIsetRegValue(SX1278_REG_OCP, SX1278_OCP_ON | SX1278_OCP_TRIM, 5, 0); + status = _mod->SPIsetRegValue(SX1278_REG_LNA, SX1278_LNA_GAIN_1 | SX1278_LNA_BOOST_HF_ON); + status = _mod->SPIsetRegValue(SX1278_REG_PA_DAC, SX1278_PA_BOOST_ON, 2, 0); + if(status != ERR_NONE) { + return(status); + } + + // turn off frequency hopping + status = _mod->SPIsetRegValue(SX1278_REG_HOP_PERIOD, SX1278_HOP_PERIOD_OFF); + if(status != ERR_NONE) { + return(status); + } + + // basic setting (bw, cr, sf, header mode and CRC) + if(sf == SX1278_SF_6) { + status = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_2, SX1278_SF_6 | SX1278_TX_MODE_SINGLE | SX1278_RX_CRC_MODE_OFF, 7, 2); + status = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_1, bw | cr | SX1278_HEADER_IMPL_MODE); + status = _mod->SPIsetRegValue(SX1278_REG_DETECT_OPTIMIZE, SX1278_DETECT_OPTIMIZE_SF_6, 2, 0); + status = _mod->SPIsetRegValue(SX1278_REG_DETECTION_THRESHOLD, SX1278_DETECTION_THRESHOLD_SF_6); + } else { + status = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_2, sf | SX1278_TX_MODE_SINGLE | SX1278_RX_CRC_MODE_ON, 7, 2); + status = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_1, bw | cr | SX1278_HEADER_EXPL_MODE); + status = _mod->SPIsetRegValue(SX1278_REG_DETECT_OPTIMIZE, SX1278_DETECT_OPTIMIZE_SF_7_12, 2, 0); + status = _mod->SPIsetRegValue(SX1278_REG_DETECTION_THRESHOLD, SX1278_DETECTION_THRESHOLD_SF_7_12); + } + + if(status != ERR_NONE) { + return(status); + } + + // set default preamble length + status = _mod->SPIsetRegValue(SX1278_REG_PREAMBLE_MSB, SX1278_PREAMBLE_LENGTH_MSB); + status = _mod->SPIsetRegValue(SX1278_REG_PREAMBLE_LSB, SX1278_PREAMBLE_LENGTH_LSB); + if(status != ERR_NONE) { + return(status); + } + + // set mode to STANDBY + status = setMode(SX1278_STANDBY); + if(status != ERR_NONE) { + return(status); + } + + return(ERR_NONE); +} + +uint8_t SX1278::setMode(uint8_t mode) { + _mod->SPIsetRegValue(SX1278_REG_OP_MODE, mode, 2, 0); + return(ERR_NONE); +} + +void SX1278::clearIRQFlags() { + _mod->SPIwriteRegister(SX1278_REG_IRQ_FLAGS, 0b11111111); +} + +int8_t SX1278::getLastPacketRSSI() { + return(-164 + _mod->SPIgetRegValue(SX1278_REG_PKT_RSSI_VALUE)); +} diff --git a/src/SX1278.h b/src/SX1278.h new file mode 100644 index 00000000..0e8f8c5a --- /dev/null +++ b/src/SX1278.h @@ -0,0 +1,239 @@ +#ifndef _KITELIB_SX1278_H +#define _KITELIB_SX1278_H + +#include + +#include "TypeDef.h" +#include "Module.h" +#include "Packet.h" + +//SX1278 register map +#define SX1278_REG_FIFO 0x00 +#define SX1278_REG_OP_MODE 0x01 +#define SX1278_REG_FRF_MSB 0x06 +#define SX1278_REG_FRF_MID 0x07 +#define SX1278_REG_FRF_LSB 0x08 +#define SX1278_REG_PA_CONFIG 0x09 +#define SX1278_REG_PA_RAMP 0x0A +#define SX1278_REG_OCP 0x0B +#define SX1278_REG_LNA 0x0C +#define SX1278_REG_FIFO_ADDR_PTR 0x0D +#define SX1278_REG_FIFO_TX_BASE_ADDR 0x0E +#define SX1278_REG_FIFO_RX_BASE_ADDR 0x0F +#define SX1278_REG_FIFO_RX_CURRENT_ADDR 0x10 +#define SX1278_REG_IRQ_FLAGS_MASK 0x11 +#define SX1278_REG_IRQ_FLAGS 0x12 +#define SX1278_REG_RX_NB_BYTES 0x13 +#define SX1278_REG_RX_HEADER_CNT_VALUE_MSB 0x14 +#define SX1278_REG_RX_HEADER_CNT_VALUE_LSB 0x15 +#define SX1278_REG_RX_PACKET_CNT_VALUE_MSB 0x16 +#define SX1278_REG_RX_PACKET_CNT_VALUE_LSB 0x17 +#define SX1278_REG_MODEM_STAT 0x18 +#define SX1278_REG_PKT_SNR_VALUE 0x19 +#define SX1278_REG_PKT_RSSI_VALUE 0x1A +#define SX1278_REG_RSSI_VALUE 0x1B +#define SX1278_REG_HOP_CHANNEL 0x1C +#define SX1278_REG_MODEM_CONFIG_1 0x1D +#define SX1278_REG_MODEM_CONFIG_2 0x1E +#define SX1278_REG_SYMB_TIMEOUT_LSB 0x1F +#define SX1278_REG_PREAMBLE_MSB 0x20 +#define SX1278_REG_PREAMBLE_LSB 0x21 +#define SX1278_REG_PAYLOAD_LENGTH 0x22 +#define SX1278_REG_MAX_PAYLOAD_LENGTH 0x23 +#define SX1278_REG_HOP_PERIOD 0x24 +#define SX1278_REG_FIFO_RX_BYTE_ADDR 0x25 +#define SX1278_REG_MODEM_CONFIG_3 0x26 +#define SX1278_REG_FEI_MSB 0x28 +#define SX1278_REG_FEI_MID 0x29 +#define SX1278_REG_FEI_LSB 0x2A +#define SX1278_REG_RSSI_WIDEBAND 0x2C +#define SX1278_REG_DETECT_OPTIMIZE 0x31 +#define SX1278_REG_INVERT_IQ 0x33 +#define SX1278_REG_DETECTION_THRESHOLD 0x37 +#define SX1278_REG_SYNC_WORD 0x39 +#define SX1278_REG_DIO_MAPPING_1 0x40 +#define SX1278_REG_DIO_MAPPING_2 0x41 +#define SX1278_REG_VERSION 0x42 +#define SX1278_REG_TCXO 0x4B +#define SX1278_REG_PA_DAC 0x4D +#define SX1278_REG_FORMER_TEMP 0x5D +#define SX1278_REG_AGC_REF 0x61 +#define SX1278_REG_AGC_THRESH_1 0x62 +#define SX1278_REG_AGC_THRESH_2 0x63 +#define SX1278_REG_AGC_THRESH_3 0x64 +#define SX1278_REG_PLL 0x70 + +//SX1278 LoRa modem settings +//SX1278_REG_OP_MODE MSB LSB DESCRIPTION +#define SX1278_FSK_OOK 0b00000000 // 7 7 FSK/OOK mode +#define SX1278_LORA 0b10000000 // 7 7 LoRa mode +#define SX1278_ACCESS_SHARED_REG_OFF 0b00000000 // 6 6 access LoRa registers (0x0D:0x3F) in LoRa mode +#define SX1278_ACCESS_SHARED_REG_ON 0b01000000 // 6 6 access FSK registers (0x0D:0x3F) in LoRa mode +#define SX1278_HIGH_FREQ 0b00000000 // 3 3 access HF test registers +#define SX1278_LOW_FREQ 0b00001000 // 3 3 access LF test registers +#define SX1278_SLEEP 0b00000000 // 2 0 sleep +#define SX1278_STANDBY 0b00000001 // 2 0 standby +#define SX1278_FSTX 0b00000010 // 2 0 frequency synthesis TX +#define SX1278_TX 0b00000011 // 2 0 transmit +#define SX1278_FSRX 0b00000100 // 2 0 frequency synthesis RX +#define SX1278_RXCONTINUOUS 0b00000101 // 2 0 receive continuous +#define SX1278_RXSINGLE 0b00000110 // 2 0 receive single +#define SX1278_CAD 0b00000111 // 2 0 channel activity detection + +//SX1278_REG_FRF_MSB + REG_FRF_MID + REG_FRF_LSB +#define SX1278_FRF_MSB 0x6C // 7 0 carrier frequency setting: f_RF = (F(XOSC) * FRF)/2^19 +#define SX1278_FRF_MID 0x80 // 7 0 where F(XOSC) = 32 MHz +#define SX1278_FRF_LSB 0x00 // 7 0 FRF = 3 byte value of FRF registers + +//SX1278_REG_PA_CONFIG +#define SX1278_PA_SELECT_RFO 0b00000000 // 7 7 RFO pin output, power limited to +14 dBm +#define SX1278_PA_SELECT_BOOST 0b10000000 // 7 7 PA_BOOST pin output, power limited to +20 dBm +#define SX1278_MAX_POWER 0b01110000 // 6 4 max power: P_max = 10.8 + 0.6*MAX_POWER [dBm]; P_max(MAX_POWER = 0b111) = 15 dBm +#define SX1278_OUTPUT_POWER 0b00001111 // 3 0 output power: P_out = 17 - (15 - OUTPUT_POWER) [dBm] for PA_SELECT_BOOST + +//SX1278_REG_OCP +#define SX1278_OCP_OFF 0b00000000 // 5 5 PA overload current protection disabled +#define SX1278_OCP_ON 0b00100000 // 5 5 PA overload current protection enabled +#define SX1278_OCP_TRIM 0b00001011 // 4 0 OCP current: I_max(OCP_TRIM = 0b1011) = 100 mA + +//SX1278_REG_LNA +#define SX1278_LNA_GAIN_0 0b00000000 // 7 5 LNA gain setting: not used +#define SX1278_LNA_GAIN_1 0b00100000 // 7 5 max gain +#define SX1278_LNA_GAIN_2 0b01000000 // 7 5 . +#define SX1278_LNA_GAIN_3 0b01100000 // 7 5 . +#define SX1278_LNA_GAIN_4 0b10000000 // 7 5 . +#define SX1278_LNA_GAIN_5 0b10100000 // 7 5 . +#define SX1278_LNA_GAIN_6 0b11000000 // 7 5 min gain +#define SX1278_LNA_GAIN_7 0b11100000 // 7 5 not used +#define SX1278_LNA_BOOST_LF_OFF 0b00000000 // 4 3 default LNA current +#define SX1278_LNA_BOOST_HF_OFF 0b00000000 // 1 0 default LNA current +#define SX1278_LNA_BOOST_HF_ON 0b00000011 // 1 0 150% LNA current + +//SX1278_REG_MODEM_CONFIG_1 +#define SX1278_BW_7_80_KHZ 0b00000000 // 7 4 bandwidth: 7.80 kHz +#define SX1278_BW_10_40_KHZ 0b00010000 // 7 4 10.40 kHz +#define SX1278_BW_15_60_KHZ 0b00100000 // 7 4 15.60 kHz +#define SX1278_BW_20_80_KHZ 0b00110000 // 7 4 20.80 kHz +#define SX1278_BW_31_25_KHZ 0b01000000 // 7 4 31.25 kHz +#define SX1278_BW_41_70_KHZ 0b01010000 // 7 4 41.70 kHz +#define SX1278_BW_62_50_KHZ 0b01100000 // 7 4 62.50 kHz +#define SX1278_BW_125_00_KHZ 0b01110000 // 7 4 125.00 kHz +#define SX1278_BW_250_00_KHZ 0b10000000 // 7 4 250.00 kHz +#define SX1278_BW_500_00_KHZ 0b10010000 // 7 4 500.00 kHz +#define SX1278_CR_4_5 0b00000010 // 3 1 error coding rate: 4/5 +#define SX1278_CR_4_6 0b00000100 // 3 1 4/6 +#define SX1278_CR_4_7 0b00000110 // 3 1 4/7 +#define SX1278_CR_4_8 0b00001000 // 3 1 4/8 +#define SX1278_HEADER_EXPL_MODE 0b00000000 // 0 0 explicit header mode +#define SX1278_HEADER_IMPL_MODE 0b00000001 // 0 0 implicit header mode + +//SX1278_REG_MODEM_CONFIG_2 +#define SX1278_SF_6 0b01100000 // 7 4 spreading factor: 64 chips/bit +#define SX1278_SF_7 0b01110000 // 7 4 128 chips/bit +#define SX1278_SF_8 0b10000000 // 7 4 256 chips/bit +#define SX1278_SF_9 0b10010000 // 7 4 512 chips/bit +#define SX1278_SF_10 0b10100000 // 7 4 1024 chips/bit +#define SX1278_SF_11 0b10110000 // 7 4 2048 chips/bit +#define SX1278_SF_12 0b11000000 // 7 4 4096 chips/bit +#define SX1278_TX_MODE_SINGLE 0b00000000 // 3 3 single TX +#define SX1278_TX_MODE_CONT 0b00001000 // 3 3 continuous TX +#define SX1278_RX_CRC_MODE_OFF 0b00000000 // 2 2 CRC disabled +#define SX1278_RX_CRC_MODE_ON 0b00000100 // 2 2 CRC enabled +#define SX1278_RX_TIMEOUT_MSB 0b00000000 // 1 0 + +//SX1278_REG_SYMB_TIMEOUT_LSB +#define SX1278_RX_TIMEOUT_LSB 0b01100100 // 7 0 10 bit RX operation timeout + +//SX1278_REG_PREAMBLE_MSB + REG_PREAMBLE_LSB +#define SX1278_PREAMBLE_LENGTH_MSB 0b00000000 // 7 0 2 byte preamble length setting: l_P = PREAMBLE_LENGTH + 4.25 +#define SX1278_PREAMBLE_LENGTH_LSB 0b00001000 // 7 0 where l_p = preamble length + +//SX1278_REG_MODEM_CONFIG_3 +#define SX1278_LOW_DATA_RATE_OPT_OFF 0b00000000 // 3 3 low data rate optimization disabled +#define SX1278_LOW_DATA_RATE_OPT_ON 0b00001000 // 3 3 low data rate optimization enabled +#define SX1278_AGC_AUTO_OFF 0b00000000 // 2 2 LNA gain set by REG_LNA +#define SX1278_AGC_AUTO_ON 0b00000100 // 2 2 LNA gain set by internal AGC loop + +//SX1278_REG_DETECT_OPTIMIZE +#define SX1278_DETECT_OPTIMIZE_SF_6 0b00000101 // 2 0 SF6 detection optimization +#define SX1278_DETECT_OPTIMIZE_SF_7_12 0b00000011 // 2 0 SF7 to SF12 detection optimization + +//SX1278_REG_DETECTION_THRESHOLD +#define SX1278_DETECTION_THRESHOLD_SF_6 0b00001100 // 7 0 SF6 detection threshold +#define SX1278_DETECTION_THRESHOLD_SF_7_12 0b00001010 // 7 0 SF7 to SF12 detection threshold + +//SX1278_REG_PA_DAC +#define SX1278_PA_BOOST_OFF 0b00000100 // 2 0 PA_BOOST disabled +#define SX1278_PA_BOOST_ON 0b00000111 // 2 0 +20 dBm on PA_BOOST when OUTPUT_POWER = 0b1111 + +//SX1278_REG_HOP_PERIOD +#define SX1278_HOP_PERIOD_OFF 0b00000000 // 7 0 number of periods between frequency hops; 0 = disabled +#define SX1278_HOP_PERIOD_MAX 0b11111111 // 7 0 + +//SX1278_REG_DIO_MAPPING_1 +#define SX1278_DIO0_RX_DONE 0b00000000 // 7 6 +#define SX1278_DIO0_TX_DONE 0b01000000 // 7 6 +#define SX1278_DIO0_CAD_DONE 0b10000000 // 7 6 +#define SX1278_DIO1_RX_TIMEOUT 0b00000000 // 5 4 +#define SX1278_DIO1_FHSS_CHANGE_CHANNEL 0b00010000 // 5 4 +#define SX1278_DIO1_CAD_DETECTED 0b00100000 // 5 4 + +//SX1278_REG_IRQ_FLAGS +#define SX1278_CLEAR_IRQ_FLAG_RX_TIMEOUT 0b10000000 // 7 7 timeout +#define SX1278_CLEAR_IRQ_FLAG_RX_DONE 0b01000000 // 6 6 packet reception complete +#define SX1278_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR 0b00100000 // 5 5 payload CRC error +#define SX1278_CLEAR_IRQ_FLAG_VALID_HEADER 0b00010000 // 4 4 valid header received +#define SX1278_CLEAR_IRQ_FLAG_TX_DONE 0b00001000 // 3 3 payload transmission complete +#define SX1278_CLEAR_IRQ_FLAG_CAD_DONE 0b00000100 // 2 2 CAD complete +#define SX1278_CLEAR_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b00000010 // 1 1 FHSS change channel +#define SX1278_CLEAR_IRQ_FLAG_CAD_DETECTED 0b00000001 // 0 0 valid LoRa signal detected during CAD operation + +//SX1278_REG_IRQ_FLAGS_MASK +#define SX1278_MASK_IRQ_FLAG_RX_TIMEOUT 0b01111111 // 7 7 timeout +#define SX1278_MASK_IRQ_FLAG_RX_DONE 0b10111111 // 6 6 packet reception complete +#define SX1278_MASK_IRQ_FLAG_PAYLOAD_CRC_ERROR 0b11011111 // 5 5 payload CRC error +#define SX1278_MASK_IRQ_FLAG_VALID_HEADER 0b11101111 // 4 4 valid header received +#define SX1278_MASK_IRQ_FLAG_TX_DONE 0b11110111 // 3 3 payload transmission complete +#define SX1278_MASK_IRQ_FLAG_CAD_DONE 0b11111011 // 2 2 CAD complete +#define SX1278_MASK_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b11111101 // 1 1 FHSS change channel +#define SX1278_MASK_IRQ_FLAG_CAD_DETECTED 0b11111110 // 0 0 valid LoRa signal detected during CAD operation + +//SX1278_REG_FIFO_TX_BASE_ADDR +#define SX1278_FIFO_TX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for TX only + +//SX1278_REG_FIFO_RX_BASE_ADDR +#define SX1278_FIFO_RX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for RX only + +class SX1278 { + public: + SX1278(Module* module); + + float dataRate; + int8_t lastPacketRSSI; + + uint8_t begin(Bandwidth bw = BW_125_00_KHZ, SpreadingFactor sf = SF_9, CodingRate cr = CR_4_7, uint16_t addrEeprom = 0); + uint8_t transmit(Packet& pack); + uint8_t receive(Packet& pack); + + uint8_t sleep(); + uint8_t standby(); + + uint8_t setBandwidth(Bandwidth bw); + uint8_t setSpreadingFactor(SpreadingFactor sf); + uint8_t setCodingRate(CodingRate cr); + + private: + Module* _mod; + uint8_t _bw, _sf, _cr; + + uint16_t _addrEeprom; + uint8_t _address[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + void generateLoRaAdress(); + uint8_t config(uint8_t bw, uint8_t sf, uint8_t cr); + uint8_t setMode(uint8_t mode); + void clearIRQFlags(); + int8_t getLastPacketRSSI(); +}; + +#endif diff --git a/src/TypeDef.h b/src/TypeDef.h new file mode 100644 index 00000000..6761aebf --- /dev/null +++ b/src/TypeDef.h @@ -0,0 +1,60 @@ +#ifndef _KITELIB_TYPES_H +#define _KITELIB_TYPES_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +//#define DEBUG + +// Shield configuration +#define USE_SPI 0x00 +#define USE_UART 0x01 +#define USE_I2C 0x02 +#define INT_NONE 0x00 +#define INT_0 0x01 +#define INT_1 0x02 +#define INT_BOTH 0x03 + +// UART configuration +#define UART_STOPBIT_1 0x01 +#define UART_STOPBIT_1_5 0x02 +#define UART_STOPBIT_2 0x03 +#define UART_PARITY_NONE 0x00 +#define UART_PARITY_ODD 0x01 +#define UART_PARITY_EVEN 0x02 +#define UART_FLOW_NONE 0x00 +#define UART_FLOW_RTS 0x01 +#define UART_FLOW_CTS 0x02 +#define UART_FLOW_BOTH 0x03 + +// Common error codes +#define ERR_NONE 0x00 +#define ERR_UNKNOWN 0x63 // maximum error code value is 99, so that it does not interfere with HTTP status codes + +// SX1278/SX1272 error codes +#define ERR_CHIP_NOT_FOUND 0x01 +#define ERR_EEPROM_NOT_INITIALIZED 0x02 +#define ERR_PACKET_TOO_LONG 0x10 +#define ERR_RX_TIMEOUT 0x20 +#define ERR_CRC_MISMATCH 0x21 +#define ERR_INVALID_BANDWIDTH 0x30 +#define ERR_INVALID_SPREADING_FACTOR 0x31 +#define ERR_INVALID_CODING_RATE 0x32 +#define ERR_INVALID_BIT_RANGE 0x40 + +// ESP8266 error codes +#define ERR_URL_MALFORMED 0x01 +#define ERR_RESPONSE_MALFORMED_AT 0x02 +#define ERR_RESPONSE_MALFORMED 0x03 + + +enum Slot {SlotA, SlotB}; + +enum Bandwidth {BW_7_80_KHZ, BW_10_40_KHZ, BW_15_60_KHZ, BW_20_80_KHZ, BW_31_25_KHZ, BW_41_70_KHZ, BW_62_50_KHZ, BW_125_00_KHZ, BW_250_00_KHZ, BW_500_00_KHZ, }; +enum SpreadingFactor {SF_6, SF_7, SF_8, SF_9, SF_10, SF_11, SF_12}; +enum CodingRate {CR_4_5, CR_4_6, CR_4_7, CR_4_8}; + +#endif