From d5c015f4b981a228180232aeb0dd40b8d50b85b4 Mon Sep 17 00:00:00 2001 From: Peter Buchegger Date: Fri, 1 Jan 2021 23:23:27 +0100 Subject: [PATCH] push a lot of changes... --- .gitmodules | 3 - data/is-cfg.json | 2 - lib/APRS-IS/APRS-IS.cpp | 92 +++ lib/APRS-IS/APRS-IS.h | 37 + lib/BoardFinder/BoardFinder.cpp | 167 +++++ lib/BoardFinder/BoardFinder.h | 68 ++ lib/ConfigurationManagement/configuration.cpp | 65 ++ lib/ConfigurationManagement/configuration.h | 27 + lib/LoRa_APRS/LoRa.cpp | 673 ++++++++++++++++++ lib/LoRa_APRS/LoRa.h | 119 ++++ lib/LoRa_APRS/LoRa_APRS.cpp | 87 +++ lib/LoRa_APRS/LoRa_APRS.h | 40 ++ lib/NTPClient/NTPClient.cpp | 202 ++++++ lib/NTPClient/NTPClient.h | 106 +++ lib/PowerManagement/power_management.cpp | 55 ++ lib/PowerManagement/power_management.h | 26 + lib/SignalSlot/SignalSlot.h | 116 +++ lib/TimeLib/TimeLib.cpp | 329 +++++++++ lib/TimeLib/TimeLib.h | 126 ++++ lib/TimeLib/TimeLibString.cpp | 56 ++ lib/common | 1 - platformio.ini | 4 +- src/LoRa_APRS_iGate.cpp | 463 +++--------- src/connection.cpp | 183 +++++ src/connection.h | 19 + src/display.cpp | 142 ++++ src/display.h | 47 ++ src/project_configuration.cpp | 30 +- src/project_configuration.h | 13 +- 29 files changed, 2907 insertions(+), 391 deletions(-) create mode 100644 lib/APRS-IS/APRS-IS.cpp create mode 100644 lib/APRS-IS/APRS-IS.h create mode 100644 lib/BoardFinder/BoardFinder.cpp create mode 100644 lib/BoardFinder/BoardFinder.h create mode 100644 lib/ConfigurationManagement/configuration.cpp create mode 100644 lib/ConfigurationManagement/configuration.h create mode 100644 lib/LoRa_APRS/LoRa.cpp create mode 100644 lib/LoRa_APRS/LoRa.h create mode 100644 lib/LoRa_APRS/LoRa_APRS.cpp create mode 100644 lib/LoRa_APRS/LoRa_APRS.h create mode 100644 lib/NTPClient/NTPClient.cpp create mode 100644 lib/NTPClient/NTPClient.h create mode 100644 lib/PowerManagement/power_management.cpp create mode 100644 lib/PowerManagement/power_management.h create mode 100644 lib/SignalSlot/SignalSlot.h create mode 100644 lib/TimeLib/TimeLib.cpp create mode 100644 lib/TimeLib/TimeLib.h create mode 100644 lib/TimeLib/TimeLibString.cpp delete mode 160000 lib/common create mode 100644 src/connection.cpp create mode 100644 src/connection.h create mode 100644 src/display.cpp create mode 100644 src/display.h diff --git a/.gitmodules b/.gitmodules index e8683d4..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "lib"] - path = lib/common - url = ../LoRa_APRS_Common diff --git a/data/is-cfg.json b/data/is-cfg.json index 7a24d5e..a9fc508 100644 --- a/data/is-cfg.json +++ b/data/is-cfg.json @@ -2,7 +2,6 @@ "callsign":"NOCALL-10", "wifi": { - "active":false, "AP": [ { "SSID":"YOURSSID", "password":"YOURPASSWORD" } ] @@ -18,7 +17,6 @@ }, "aprs_is": { - "active":false, "password":"", "server":"euro.aprs2.net", "port":14580, diff --git a/lib/APRS-IS/APRS-IS.cpp b/lib/APRS-IS/APRS-IS.cpp new file mode 100644 index 0000000..5b50d94 --- /dev/null +++ b/lib/APRS-IS/APRS-IS.cpp @@ -0,0 +1,92 @@ +#include "APRS-IS.h" + +APRS_IS::APRS_IS(const String & user, const String & passcode, const String & tool_name, const String & version) + : _user(user), _passcode(passcode), _tool_name(tool_name), _version(version) +{ +} + +bool APRS_IS::connect(const String & server, const int port) +{ + const String login = "user " + _user + " pass " + _passcode + " vers " + _tool_name + " " + _version + "\n\r"; + return connect_(server, port, login); +} + +bool APRS_IS::connect(const String & server, const int port, const String & filter) +{ + const String login = "user " + _user + " pass " + _passcode + " vers " + _tool_name + " " + _version + " filter " + filter + "\n\r"; + return connect_(server, port, login); +} + +bool APRS_IS::connect_(const String & server, const int port, const String & login_line) +{ + if(!_client.connect(server.c_str(), port)) + { + return false; + } + sendMessage(login_line); + // TODO: implement check if auth was successfull! + //while(!available()); + return true; +} + +bool APRS_IS::connected() +{ + return _client.connected(); +} + +bool APRS_IS::sendMessage(const String & message) +{ + if(!connected()) + { + return false; + } + _client.println(message); + return true; +} + +bool APRS_IS::sendMessage(const std::shared_ptr message) +{ + if(!connected()) + { + return false; + } + _client.println(message->encode()); + return true; +} + +void APRS_IS::action(std::shared_ptr elem, int rssi, float snr) +{ + sendMessage(elem); +} + +int APRS_IS::available() +{ + return _client.available(); +} + +String APRS_IS::getMessage() +{ + String line; + if (_client.available() > 0) + { + line = _client.readStringUntil('\n'); + } + return line; +} + +std::shared_ptr APRS_IS::getAPRSMessage() +{ + String line; + if (_client.available() > 0) + { + line = _client.readStringUntil('\n'); + } + if(line.length() == 0 || line.startsWith("#")) + { + return 0; + } + std::shared_ptr msg = std::shared_ptr(new APRSMessage()); + msg->decode(line); + emit(msg); + return msg; +} diff --git a/lib/APRS-IS/APRS-IS.h b/lib/APRS-IS/APRS-IS.h new file mode 100644 index 0000000..a7df11f --- /dev/null +++ b/lib/APRS-IS/APRS-IS.h @@ -0,0 +1,37 @@ + +#ifndef APRS_IS_Lib_h_ +#define APRS_IS_Lib_h_ + +#include +#include +#include + +class APRS_IS : public Signal1>, public Slot3, int, float> +{ +public: + APRS_IS(const String & user, const String & passcode, const String & tool_name, const String & version); + + bool connect(const String & server, const int port); + bool connect(const String & server, const int port, const String & filter); + bool connect_(const String & server, const int port, const String & login_line); + bool connected(); + + bool sendMessage(const String & message); + bool sendMessage(const std::shared_ptr message); + void action(std::shared_ptr msg, int rssi, float snr) override; + + int available(); + + String getMessage(); + std::shared_ptr getAPRSMessage(); + +private: + const String _user; + const String _passcode; + const String _tool_name; + const String _version; + WiFiClient _client; +}; + +#endif + diff --git a/lib/BoardFinder/BoardFinder.cpp b/lib/BoardFinder/BoardFinder.cpp new file mode 100644 index 0000000..39e0ca0 --- /dev/null +++ b/lib/BoardFinder/BoardFinder.cpp @@ -0,0 +1,167 @@ +#include +#include +#include "BoardFinder.h" + +BoardConfig::BoardConfig( + String name, BoardType type, + uint8_t oledsda, uint8_t oledscl, uint8_t oledaddr, uint8_t oledreset, + uint8_t lorasck, uint8_t loramiso, uint8_t loramosi, uint8_t loracs, uint8_t lorareset, uint8_t lorairq, + bool needcheckpowerchip, bool powercheckstatus) + : + Name(name), Type(type), + OledSda(oledsda), OledScl(oledscl), OledAddr(oledaddr), OledReset(oledreset), + LoraSck(lorasck), LoraMiso(loramiso), LoraMosi(loramosi), LoraCS(loracs), LoraReset(lorareset), LoraIRQ(lorairq), + needCheckPowerChip(needcheckpowerchip), powerCheckStatus(powercheckstatus) +{ +} + +BoardFinder::BoardFinder(std::list> boardConfigs) +{ + _boardConfigs = boardConfigs; +} + +std::shared_ptr BoardFinder::searchBoardConfig() +{ + logPrintlnI("looking for a board config."); + logPrintlnI("searching for OLED..."); + + for(std::shared_ptr boardconf : _boardConfigs) + { + if(boardconf->needCheckPowerChip && checkPowerConfig(boardconf) == boardconf->powerCheckStatus) + { + PowerManagement powerManagement; + TwoWire wire(0); + wire.begin(boardconf->OledSda, boardconf->OledScl); + powerManagement.begin(wire); + powerManagement.activateOLED(); + } + else if(boardconf->needCheckPowerChip) + { + continue; + } + if(checkOledConfig(boardconf)) + { + logPrintI("found a board config: "); + logPrintlnI(boardconf->Name); + return boardconf; + } + } + + logPrintlnW("could not find OLED, will search for the modem now..."); + + for(std::shared_ptr boardconf : _boardConfigs) + { + if(boardconf->needCheckPowerChip && checkPowerConfig(boardconf) == boardconf->powerCheckStatus) + { + PowerManagement powerManagement; + TwoWire wire(0); + wire.begin(boardconf->OledSda, boardconf->OledScl); + powerManagement.begin(wire); + powerManagement.activateLoRa(); + } + if(checkModemConfig(boardconf)) + { + logPrintI("found a board config: "); + logPrintlnI(boardconf->Name); + return boardconf; + } + } + + logPrintlnW("could not find a board config!"); + + return 0; +} + +std::shared_ptr BoardFinder::getBoardConfig(String name) +{ + std::_List_iterator> elem = std::find_if(_boardConfigs.begin(), _boardConfigs.end(), [&](std::shared_ptr conf) + { + return conf->Name == name; + }); + if(elem == _boardConfigs.end()) + { + return 0; + } + return *elem; +} + +bool BoardFinder::checkOledConfig(std::shared_ptr boardConfig) +{ + if(boardConfig->OledReset > 0) + { + pinMode(boardConfig->OledReset, OUTPUT); + digitalWrite(boardConfig->OledReset, HIGH); + delay(1); + digitalWrite(boardConfig->OledReset, LOW); + delay(10); + digitalWrite(boardConfig->OledReset, HIGH); + } + TwoWire wire(0); + if(!wire.begin(boardConfig->OledSda, boardConfig->OledScl)) + { + logPrintlnW("issue with wire"); + return false; + } + wire.beginTransmission(boardConfig->OledAddr); + if(!wire.endTransmission()) + { + return true; + } + return false; +} + +bool BoardFinder::checkModemConfig(std::shared_ptr boardConfig) +{ + pinMode(boardConfig->LoraReset, OUTPUT); + digitalWrite(boardConfig->LoraReset, LOW); + delay(10); + digitalWrite(boardConfig->LoraReset, HIGH); + delay(10); + + pinMode(boardConfig->LoraCS, OUTPUT); + digitalWrite(boardConfig->LoraCS, HIGH); + + SPIClass spi; + spi.begin(boardConfig->LoraSck, boardConfig->LoraMiso, boardConfig->LoraMosi, boardConfig->LoraCS); + + digitalWrite(boardConfig->LoraCS, LOW); + + spi.beginTransaction(SPISettings(8E6, MSBFIRST, SPI_MODE0)); + spi.transfer(0x42); + uint8_t response = spi.transfer(0x00); + spi.endTransaction(); + + digitalWrite(boardConfig->LoraCS, HIGH); + + if(response == 0x12) + { + return true; + } + return false; +} + +bool BoardFinder::checkPowerConfig(std::shared_ptr boardConfig) +{ + TwoWire wire(0); + if(!wire.begin(boardConfig->OledSda, boardConfig->OledScl)) + { + logPrintlnW("issue with wire"); + return false; + } + wire.beginTransmission(0x34); + wire.write(0x03); + wire.endTransmission(); + + wire.requestFrom(0x34, 1); + int response = wire.read(); + wire.endTransmission(); + + logPrintlnD(String(response)); + if(response == 0x03) + { + logPrintlnD("power chip found!"); + return true; + } + logPrintlnD("power chip NOT found"); + return false; +} diff --git a/lib/BoardFinder/BoardFinder.h b/lib/BoardFinder/BoardFinder.h new file mode 100644 index 0000000..45b9243 --- /dev/null +++ b/lib/BoardFinder/BoardFinder.h @@ -0,0 +1,68 @@ +#ifndef BOARD_FINDER_H_ +#define BOARD_FINDER_H_ + +#include +#include + +#include +#include +#include + +enum BoardType +{ + eHELTEC_WIFI_LORA_32_V1, + eHELTEC_WIFI_LORA_32_V2, + eTTGO_LORA32_V1, + eTTGO_LORA32_V2, + eTTGO_T_Beam_V0_7, + eTTGO_T_Beam_V1_0, + eETH_BOARD, + eTRACKERD +}; + +class BoardConfig +{ +public: + BoardConfig( + String name, BoardType type, + uint8_t oledsda, uint8_t oledscl, uint8_t oledaddr, uint8_t oledreset, + uint8_t lorasck, uint8_t loramiso, uint8_t loramosi, uint8_t loracs, uint8_t lorareset, uint8_t lorairq, + bool needcheckpowerchip = false, bool powercheckstatus = false); + + String Name; + BoardType Type; + + uint8_t OledSda; + uint8_t OledScl; + uint8_t OledAddr; + uint8_t OledReset; + + uint8_t LoraSck; + uint8_t LoraMiso; + uint8_t LoraMosi; + uint8_t LoraCS; + uint8_t LoraReset; + uint8_t LoraIRQ; + + bool needCheckPowerChip; + bool powerCheckStatus; +}; + +class BoardFinder +{ +public: + BoardFinder(std::list> boardConfigs); + + std::shared_ptr searchBoardConfig(); + + std::shared_ptr getBoardConfig(String name); + +private: + std::list> _boardConfigs; + + bool checkOledConfig(std::shared_ptr boardConfig); + bool checkModemConfig(std::shared_ptr boardConfig); + bool checkPowerConfig(std::shared_ptr boardConfig); +}; + +#endif diff --git a/lib/ConfigurationManagement/configuration.cpp b/lib/ConfigurationManagement/configuration.cpp new file mode 100644 index 0000000..b713489 --- /dev/null +++ b/lib/ConfigurationManagement/configuration.cpp @@ -0,0 +1,65 @@ +#include +#include +#include "configuration.h" + +ConfigurationManagement::ConfigurationManagement(String FilePath) + : mFilePath(FilePath) +{ + if(!SPIFFS.begin(true)) + { + logPrintlnE("Mounting SPIFFS was not possible. Trying to format SPIFFS..."); + SPIFFS.format(); + if(!SPIFFS.begin()) + { + logPrintlnE("Formating SPIFFS was not okay!"); + } + } +} + +ConfigurationManagement::~ConfigurationManagement() +{ +} + +std::shared_ptr ConfigurationManagement::readConfiguration() +{ + File file = SPIFFS.open(mFilePath); + if(!file) + { + logPrintlnE("Failed to open file for reading..."); + return 0; + } + DynamicJsonDocument data(2048); + DeserializationError error = deserializeJson(data, file); + if(error) + { + logPrintlnW("Failed to read file, using default configuration."); + } + //serializeJson(data, Serial); + //Serial.println(); + file.close(); + + std::shared_ptr conf = readProjectConfiguration(data); + + // update config in memory to get the new fields: + writeConfiguration(conf); + + return conf; +} + +void ConfigurationManagement::writeConfiguration(std::shared_ptr conf) +{ + File file = SPIFFS.open(mFilePath, "w"); + if(!file) + { + logPrintlnE("Failed to open file for writing..."); + return; + } + DynamicJsonDocument data(2048); + + writeProjectConfiguration(conf, data); + + serializeJson(data, file); + //serializeJson(data, Serial); + //Serial.println(); + file.close(); +} diff --git a/lib/ConfigurationManagement/configuration.h b/lib/ConfigurationManagement/configuration.h new file mode 100644 index 0000000..0e22b10 --- /dev/null +++ b/lib/ConfigurationManagement/configuration.h @@ -0,0 +1,27 @@ +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ + +#include + +#include +#include + +class Configuration; + +class ConfigurationManagement +{ +public: + explicit ConfigurationManagement(String FilePath); + virtual ~ConfigurationManagement(); + + std::shared_ptr readConfiguration(); + void writeConfiguration(std::shared_ptr conf); + +private: + virtual std::shared_ptr readProjectConfiguration(DynamicJsonDocument & data) = 0; + virtual void writeProjectConfiguration(std::shared_ptr conf, DynamicJsonDocument & data) = 0; + + const String mFilePath; +}; + +#endif diff --git a/lib/LoRa_APRS/LoRa.cpp b/lib/LoRa_APRS/LoRa.cpp new file mode 100644 index 0000000..59142ec --- /dev/null +++ b/lib/LoRa_APRS/LoRa.cpp @@ -0,0 +1,673 @@ +// 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) +{ + // 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) +{ + + // 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() +{ +} + +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); +} + +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); +} + +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; +} diff --git a/lib/LoRa_APRS/LoRa.h b/lib/LoRa_APRS/LoRa.h new file mode 100644 index 0000000..51e089a --- /dev/null +++ b/lib/LoRa_APRS/LoRa.h @@ -0,0 +1,119 @@ +// 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(); + + void receive(int size = 0); + + 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(); + + 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); + +private: + SPISettings _spiSettings; + SPIClass* _spi; + int _ss; + int _reset; + int _dio0; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; +}; + +#endif diff --git a/lib/LoRa_APRS/LoRa_APRS.cpp b/lib/LoRa_APRS/LoRa_APRS.cpp new file mode 100644 index 0000000..8f8a5a2 --- /dev/null +++ b/lib/LoRa_APRS/LoRa_APRS.cpp @@ -0,0 +1,87 @@ +#include +#include "LoRa_APRS.h" + +LoRa_APRS::LoRa_APRS(std::shared_ptr boardConfig) + : _LastReceivedMsg(0), _RxFrequency(LORA_RX_FREQUENCY), _TxFrequency(LORA_TX_FREQUENCY) +{ + SPI.begin(boardConfig->LoraSck, boardConfig->LoraMiso, boardConfig->LoraMosi, boardConfig->LoraCS); + setPins(boardConfig->LoraCS, boardConfig->LoraReset, boardConfig->LoraIRQ); +} + +bool LoRa_APRS::checkMessage() +{ + if(!parsePacket()) + { + return false; + } + // read header: + char dummy[4]; + readBytes(dummy, 3); + if(dummy[0] != '<') + { + // is no APRS message, ignore message + while(available()) + { + read(); + } + return false; + } + // read APRS data: + String str; + while(available()) + { + str += (char)read(); + } + _LastReceivedMsg = std::shared_ptr(new APRSMessage()); + _LastReceivedMsg->decode(str); + emit(_LastReceivedMsg, packetRssi(), packetSnr()); + return true; +} + +std::shared_ptr LoRa_APRS::getMessage() +{ + return _LastReceivedMsg; +} + +// cppcheck-suppress unusedFunction +void LoRa_APRS::sendMessage(const std::shared_ptr msg) +{ + setFrequency(_TxFrequency); + String data = msg->encode(); + beginPacket(); + // Header: + write('<'); + write(0xFF); + write(0x01); + // APRS Data: + write((const uint8_t *)data.c_str(), data.length()); + endPacket(); + setFrequency(_RxFrequency); +} + +void LoRa_APRS::setRxFrequency(long frequency) +{ + _RxFrequency = frequency; + setFrequency(_RxFrequency); +} + +long LoRa_APRS::getRxFrequency() const +{ + return _RxFrequency; +} + +void LoRa_APRS::setTxFrequency(long frequency) +{ + _TxFrequency = frequency; +} + +// cppcheck-suppress unusedFunction +long LoRa_APRS::getTxFrequency() const +{ + return _TxFrequency; +} + +void LoRa_APRS::action(const std::shared_ptr elem) +{ + sendMessage(elem); +} diff --git a/lib/LoRa_APRS/LoRa_APRS.h b/lib/LoRa_APRS/LoRa_APRS.h new file mode 100644 index 0000000..71bad11 --- /dev/null +++ b/lib/LoRa_APRS/LoRa_APRS.h @@ -0,0 +1,40 @@ +#ifndef LORA_H_ +#define LORA_H_ + +#include +#include +#include +#include +#include + +#define LORA_RX_FREQUENCY (433775000) +#define LORA_TX_FREQUENCY (433900000) +#define LORA_SPREADING_FACTOR (12) +#define LORA_SIGNAL_BANDWIDTH (125E3) +#define LORA_CODING_RATE4 (5) + +class LoRa_APRS : public LoRaClass, public Slot1>, public Signal3, int, float> +{ +public: + explicit LoRa_APRS(std::shared_ptr boardConfig); + + bool checkMessage(); + std::shared_ptr getMessage(); + + void sendMessage(const std::shared_ptr msg); + + void setRxFrequency(long frequency); + long getRxFrequency() const; + + void setTxFrequency(long frequency); + long getTxFrequency() const; + + void action(const std::shared_ptr elem) override; + +private: + std::shared_ptr _LastReceivedMsg; + long _RxFrequency; + long _TxFrequency; +}; + +#endif diff --git a/lib/NTPClient/NTPClient.cpp b/lib/NTPClient/NTPClient.cpp new file mode 100644 index 0000000..13447f7 --- /dev/null +++ b/lib/NTPClient/NTPClient.cpp @@ -0,0 +1,202 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2015 by Fabrice Weinberg + * + * 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. + */ + +#include "NTPClient.h" + +NTPClient::NTPClient() { +} + +NTPClient::NTPClient(long timeOffset) { + this->_timeOffset = timeOffset; +} + +NTPClient::NTPClient(const char* poolServerName) { + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(IPAddress poolServerIP) { + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; +} + +NTPClient::NTPClient(const char* poolServerName, long timeOffset) { + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(IPAddress poolServerIP, long timeOffset){ + this->_timeOffset = timeOffset; + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; +} + +NTPClient::NTPClient(const char* poolServerName, long timeOffset, unsigned long updateInterval) { + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; + this->_updateInterval = updateInterval; +} + +NTPClient::NTPClient(IPAddress poolServerIP, long timeOffset, unsigned long updateInterval) { + this->_timeOffset = timeOffset; + this->_poolServerIP = poolServerIP; + this->_poolServerName = NULL; + this->_updateInterval = updateInterval; +} + +void NTPClient::begin() { + this->begin(NTP_DEFAULT_LOCAL_PORT); +} + +void NTPClient::begin(unsigned int port) { + this->_port = port; + + this->_udp.begin(this->_port); + + this->_udpSetup = true; +} + +bool NTPClient::forceUpdate() { + #ifdef DEBUG_NTPClient + Serial.println("Update from NTP Server"); + #endif + + // flush any existing packets + while(this->_udp.parsePacket() != 0) + this->_udp.flush(); + + this->sendNTPPacket(); + + // Wait till data is there or timeout... + byte timeout = 0; + int cb = 0; + do { + delay ( 10 ); + cb = this->_udp.parsePacket(); + if (timeout > 100) return false; // timeout after 1000 ms + timeout++; + } while (cb == 0); + + this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + + this->_udp.read(this->_packetBuffer, NTP_PACKET_SIZE); + + unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); + unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + + return true; // return true after successful update +} + +bool NTPClient::update() { + if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval + || this->_lastUpdate == 0) { // Update if there was no update yet. + if (!this->_udpSetup || this->_port != NTP_DEFAULT_LOCAL_PORT) this->begin(this->_port); // setup the UDP client if needed + return this->forceUpdate(); + } + return false; // return false if update does not occur +} + +unsigned long NTPClient::getEpochTime() const { + return this->_timeOffset + // User offset + this->_currentEpoc + // Epoc returned by the NTP server + ((millis() - this->_lastUpdate) / 1000); // Time since last update +} + +int NTPClient::getDay() const { + return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday +} +int NTPClient::getHours() const { + return ((this->getEpochTime() % 86400L) / 3600); +} +int NTPClient::getMinutes() const { + return ((this->getEpochTime() % 3600) / 60); +} +int NTPClient::getSeconds() const { + return (this->getEpochTime() % 60); +} + +String NTPClient::getFormattedTime() const { + unsigned long rawTime = this->getEpochTime(); + unsigned long hours = (rawTime % 86400L) / 3600; + String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); + + unsigned long minutes = (rawTime % 3600) / 60; + String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); + + unsigned long seconds = rawTime % 60; + String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); + + return hoursStr + ":" + minuteStr + ":" + secondStr; +} + +void NTPClient::end() { + this->_udp.stop(); + + this->_udpSetup = false; +} + +void NTPClient::setTimeOffset(int timeOffset) { + this->_timeOffset = timeOffset; +} + +void NTPClient::setUpdateInterval(unsigned long updateInterval) { + this->_updateInterval = updateInterval; +} + +void NTPClient::setPoolServerName(const char* poolServerName) { + this->_poolServerName = poolServerName; +} + +void NTPClient::sendNTPPacket() { + // set all bytes in the buffer to 0 + memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + + this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode + this->_packetBuffer[1] = 0; // Stratum, or type of clock + this->_packetBuffer[2] = 6; // Polling Interval + this->_packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + this->_packetBuffer[12] = 49; + this->_packetBuffer[13] = 0x4E; + this->_packetBuffer[14] = 49; + this->_packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + if (this->_poolServerName) { + this->_udp.beginPacket(this->_poolServerName, 123); + } else { + this->_udp.beginPacket(this->_poolServerIP, 123); + } + this->_udp.write(this->_packetBuffer, NTP_PACKET_SIZE); + this->_udp.endPacket(); +} + +void NTPClient::setRandomPort(unsigned int minValue, unsigned int maxValue) { + randomSeed(analogRead(0)); + this->_port = random(minValue, maxValue); +} \ No newline at end of file diff --git a/lib/NTPClient/NTPClient.h b/lib/NTPClient/NTPClient.h new file mode 100644 index 0000000..1407c68 --- /dev/null +++ b/lib/NTPClient/NTPClient.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include + +#define SEVENZYYEARS 2208988800UL +#define NTP_PACKET_SIZE 48 +#define NTP_DEFAULT_LOCAL_PORT 1337 + +class NTPClient { + private: + WiFiUDP _udp; + bool _udpSetup = false; + + const char* _poolServerName = "pool.ntp.org"; // Default time server + IPAddress _poolServerIP; + unsigned int _port = NTP_DEFAULT_LOCAL_PORT; + long _timeOffset = 0; + + unsigned long _updateInterval = 60000; // In ms + + unsigned long _currentEpoc = 0; // In s + unsigned long _lastUpdate = 0; // In ms + + byte _packetBuffer[NTP_PACKET_SIZE]; + + void sendNTPPacket(); + + public: + NTPClient(); + NTPClient(long timeOffset); + NTPClient(const char* poolServerName); + NTPClient(const char* poolServerName, long timeOffset); + NTPClient(const char* poolServerName, long timeOffset, unsigned long updateInterval); + NTPClient(IPAddress poolServerIP); + NTPClient(IPAddress poolServerIP, long timeOffset); + NTPClient(IPAddress poolServerIP, long timeOffset, unsigned long updateInterval); + + /** + * Set time server name + * + * @param poolServerName + */ + void setPoolServerName(const char* poolServerName); + + /** + * Set random local port + */ + void setRandomPort(unsigned int minValue = 49152, unsigned int maxValue = 65535); + + /** + * Starts the underlying UDP client with the default local port + */ + void begin(); + + /** + * Starts the underlying UDP client with the specified local port + */ + void begin(unsigned int port); + + /** + * This should be called in the main loop of your application. By default an update from the NTP Server is only + * made every 60 seconds. This can be configured in the NTPClient constructor. + * + * @return true on success, false on failure + */ + bool update(); + + /** + * This will force the update from the NTP Server. + * + * @return true on success, false on failure + */ + bool forceUpdate(); + + int getDay() const; + int getHours() const; + int getMinutes() const; + int getSeconds() const; + + /** + * Changes the time offset. Useful for changing timezones dynamically + */ + void setTimeOffset(int timeOffset); + + /** + * Set the update interval to another frequency. E.g. useful when the + * timeOffset should not be set in the constructor + */ + void setUpdateInterval(unsigned long updateInterval); + + /** + * @return time formatted like `hh:mm:ss` + */ + String getFormattedTime() const; + + /** + * @return time in seconds since Jan. 1, 1970 + */ + unsigned long getEpochTime() const; + + /** + * Stops the underlying UDP client + */ + void end(); +}; diff --git a/lib/PowerManagement/power_management.cpp b/lib/PowerManagement/power_management.cpp new file mode 100644 index 0000000..8f017b6 --- /dev/null +++ b/lib/PowerManagement/power_management.cpp @@ -0,0 +1,55 @@ + +#include "power_management.h" + +// cppcheck-suppress uninitMemberVar +PowerManagement::PowerManagement() +{ +} + +// cppcheck-suppress unusedFunction +bool PowerManagement::begin(TwoWire & port) +{ + bool result = axp.begin(port, AXP192_SLAVE_ADDRESS); + if(!result) + { + axp.setDCDC1Voltage(3300); + } + return result; +} + +// cppcheck-suppress unusedFunction +void PowerManagement::activateLoRa() +{ + axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); +} + +// cppcheck-suppress unusedFunction +void PowerManagement::deactivateLoRa() +{ + axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); +} + +// cppcheck-suppress unusedFunction +void PowerManagement::activateGPS() +{ + axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); +} + +// cppcheck-suppress unusedFunction +void PowerManagement::deactivateGPS() +{ + axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); +} + +// cppcheck-suppress unusedFunction +void PowerManagement::activateOLED() +{ + axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); +} + +// cppcheck-suppress unusedFunction +void PowerManagement::decativateOLED() +{ + axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); +} + diff --git a/lib/PowerManagement/power_management.h b/lib/PowerManagement/power_management.h new file mode 100644 index 0000000..ecd6cf6 --- /dev/null +++ b/lib/PowerManagement/power_management.h @@ -0,0 +1,26 @@ +#ifndef POWER_MANAGEMENT_H_ +#define POWER_MANAGEMENT_H_ + +#include +#include + +class PowerManagement +{ +public: + PowerManagement(); + bool begin(TwoWire & port); + + void activateLoRa(); + void deactivateLoRa(); + + void activateGPS(); + void deactivateGPS(); + + void activateOLED(); + void decativateOLED(); + +private: + AXP20X_Class axp; +}; + +#endif diff --git a/lib/SignalSlot/SignalSlot.h b/lib/SignalSlot/SignalSlot.h new file mode 100644 index 0000000..28c92b5 --- /dev/null +++ b/lib/SignalSlot/SignalSlot.h @@ -0,0 +1,116 @@ +#ifndef SIGNAL_SLOT_H_ +#define SIGNAL_SLOT_H_ + +#include + +class Slot0 +{ +public: + virtual void action() = 0; +}; + +template +class Slot1 +{ +public: + virtual void action(T elem1) = 0; +}; + +template +class Slot2 +{ +public: + virtual void action(T elem1, H elem2) = 0; +}; + +template +class Slot3 +{ +public: + virtual void action(T elem1, H elem2, K elem3) = 0; +}; + +class Signal0 +{ +public: + void emit() + { + for(Slot0 * slot: _slots) + { + slot->action(); + } + } + + void connectSlot(Slot0 * slot) + { + _slots.push_back(slot); + } + +private: + std::list _slots; +}; + +template +class Signal1 +{ +public: + void emit(T elem1) + { + for(Slot1 * slot: _slots) + { + slot->action(elem1); + } + } + + void connectSlot(Slot1 * slot) + { + _slots.push_back(slot); + } + +private: + std::list *> _slots; +}; + +template +class Signal2 +{ +public: + void emit(T elem1, H elem2) + { + for(Slot2 * slot: _slots) + { + slot->action(elem1, elem2); + } + } + + void connectSlot(Slot2 * slot) + { + _slots.push_back(slot); + } + +private: + std::list *> _slots; +}; + +template +class Signal3 +{ +public: + void emit(T elem1, H elem2, K elem3) + { + for(Slot3 * slot: _slots) + { + slot->action(elem1, elem2, elem3); + } + } + + void connectSlot(Slot3 * slot) + { + _slots.push_back(slot); + } + +private: + std::list *> _slots; +}; + +#endif diff --git a/lib/TimeLib/TimeLib.cpp b/lib/TimeLib/TimeLib.cpp new file mode 100644 index 0000000..9394574 --- /dev/null +++ b/lib/TimeLib/TimeLib.cpp @@ -0,0 +1,329 @@ +/* + time.c - low level time and date functions + Copyright (c) Michael Margolis 2009-2014 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + 1.0 6 Jan 2010 - initial release + 1.1 12 Feb 2010 - fixed leap year calculation error + 1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this) + 1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update + status, updated examples for Arduino 1.0, fixed ARM + compatibility issues, added TimeArduinoDue and TimeTeensy3 + examples, add error checking and messages to RTC examples, + add examples to DS1307RTC library. + 1.4 5 Sep 2014 - compatibility with Arduino 1.5.7 +*/ + +#include + +#include "TimeLib.h" + +static tmElements_t tm; // a cache of time elements +static time_t cacheTime; // the time the cache was updated +static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds + +void refreshCache(time_t t) { + if (t != cacheTime) { + breakTime(t, tm); + cacheTime = t; + } +} + +int hour() { // the hour now + return hour(now()); +} + +int hour(time_t t) { // the hour for the given time + refreshCache(t); + return tm.Hour; +} + +int hourFormat12() { // the hour now in 12 hour format + return hourFormat12(now()); +} + +int hourFormat12(time_t t) { // the hour for the given time in 12 hour format + refreshCache(t); + if( tm.Hour == 0 ) + return 12; // 12 midnight + else if( tm.Hour > 12) + return tm.Hour - 12 ; + else + return tm.Hour ; +} + +uint8_t isAM() { // returns true if time now is AM + return !isPM(now()); +} + +uint8_t isAM(time_t t) { // returns true if given time is AM + return !isPM(t); +} + +uint8_t isPM() { // returns true if PM + return isPM(now()); +} + +uint8_t isPM(time_t t) { // returns true if PM + return (hour(t) >= 12); +} + +int minute() { + return minute(now()); +} + +int minute(time_t t) { // the minute for the given time + refreshCache(t); + return tm.Minute; +} + +int second() { + return second(now()); +} + +int second(time_t t) { // the second for the given time + refreshCache(t); + return tm.Second; +} + +int day(){ + return(day(now())); +} + +int day(time_t t) { // the day for the given time (0-6) + refreshCache(t); + return tm.Day; +} + +int weekday() { // Sunday is day 1 + return weekday(now()); +} + +int weekday(time_t t) { + refreshCache(t); + return tm.Wday; +} + +int month(){ + return month(now()); +} + +int month(time_t t) { // the month for the given time + refreshCache(t); + return tm.Month; +} + +int year() { // as in Processing, the full four digit year: (2009, 2010 etc) + return year(now()); +} + +int year(time_t t) { // the year for the given time + refreshCache(t); + return tmYearToCalendar(tm.Year); +} + +const String timeString() +{ + return timeString(now()); +} + +const String timeString(time_t t) +{ + char line[30]; + sprintf(line, "%02d:%02d:%02d", hour(t), minute(t), second(t)); + return String(line); +} + +/*============================================================================*/ +/* functions to convert to and from system time */ +/* These are for interfacing with time services and are not normally needed in a sketch */ + +// leap year calculator expects year argument as years offset from 1970 +#define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) ) + +static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0 + +void breakTime(time_t timeInput, tmElements_t &tm){ +// break the given time_t into time components +// this is a more compact version of the C library localtime function +// note that year is offset from 1970 !!! + + uint8_t year; + uint8_t month, monthLength; + uint32_t time; + unsigned long days; + + time = (uint32_t)timeInput; + tm.Second = time % 60; + time /= 60; // now it is minutes + tm.Minute = time % 60; + time /= 60; // now it is hours + tm.Hour = time % 24; + time /= 24; // now it is days + tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 + + year = 0; + days = 0; + while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { + year++; + } + tm.Year = year; // year is offset from 1970 + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; // now it is days in this year, starting at 0 + + days=0; + month=0; + monthLength=0; + for (month=0; month<12; month++) { + if (month==1) { // february + if (LEAP_YEAR(year)) { + monthLength=29; + } else { + monthLength=28; + } + } else { + monthLength = monthDays[month]; + } + + if (time >= monthLength) { + time -= monthLength; + } else { + break; + } + } + tm.Month = month + 1; // jan is month 1 + tm.Day = time + 1; // day of month +} + +time_t makeTime(const tmElements_t &tm){ +// assemble time elements into time_t +// note year argument is offset from 1970 (see macros in time.h to convert to other formats) +// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 + + int i; + uint32_t seconds; + + // seconds from 1970 till 1 jan 00:00:00 of the given year + seconds= tm.Year*(SECS_PER_DAY * 365); + for (i = 0; i < tm.Year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; // add extra days for leap years + } + } + + // add days for this year, months start from 1 + for (i = 1; i < tm.Month; i++) { + if ( (i == 2) && LEAP_YEAR(tm.Year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0 + } + } + seconds+= (tm.Day-1) * SECS_PER_DAY; + seconds+= tm.Hour * SECS_PER_HOUR; + seconds+= tm.Minute * SECS_PER_MIN; + seconds+= tm.Second; + return (time_t)seconds; +} +/*=====================================================*/ +/* Low level system time functions */ + +static uint32_t sysTime = 0; +static uint32_t prevMillis = 0; +static uint32_t nextSyncTime = 0; +static timeStatus_t Status = timeNotSet; + +getExternalTime getTimePtr; // pointer to external sync function +//setExternalTime setTimePtr; // not used in this version + +#ifdef TIME_DRIFT_INFO // define this to get drift data +time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync +#endif + + +time_t now() { + // calculate number of seconds passed since last call to now() + while (millis() - prevMillis >= 1000) { + // millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference + sysTime++; + prevMillis += 1000; +#ifdef TIME_DRIFT_INFO + sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift +#endif + } + if (nextSyncTime <= sysTime) { + if (getTimePtr != 0) { + time_t t = getTimePtr(); + if (t != 0) { + setTime(t); + } else { + nextSyncTime = sysTime + syncInterval; + Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; + } + } + } + return (time_t)sysTime; +} + +void setTime(time_t t) { +#ifdef TIME_DRIFT_INFO + if(sysUnsyncedTime == 0) + sysUnsyncedTime = t; // store the time of the first call to set a valid Time +#endif + + sysTime = (uint32_t)t; + nextSyncTime = (uint32_t)t + syncInterval; + Status = timeSet; + prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) +} + +void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ + // year can be given as full four digit year or two digts (2010 or 10 for 2010); + //it is converted to years since 1970 + if( yr > 99) + yr = yr - 1970; + else + yr += 30; + tm.Year = yr; + tm.Month = mnth; + tm.Day = dy; + tm.Hour = hr; + tm.Minute = min; + tm.Second = sec; + setTime(makeTime(tm)); +} + +void adjustTime(long adjustment) { + sysTime += adjustment; +} + +// indicates if time has been set and recently synchronized +timeStatus_t timeStatus() { + now(); // required to actually update the status + return Status; +} + +void setSyncProvider( getExternalTime getTimeFunction){ + getTimePtr = getTimeFunction; + nextSyncTime = sysTime; + now(); // this will sync the clock +} + +void setSyncInterval(time_t interval){ // set the number of seconds between re-sync + syncInterval = (uint32_t)interval; + nextSyncTime = sysTime + syncInterval; +} diff --git a/lib/TimeLib/TimeLib.h b/lib/TimeLib/TimeLib.h new file mode 100644 index 0000000..b624542 --- /dev/null +++ b/lib/TimeLib/TimeLib.h @@ -0,0 +1,126 @@ +/* + time.h - low level time and date functions +*/ + +/* + July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this) + - fixed daysToTime_t macro (thanks maniacbug) +*/ + +#ifndef _Time_h +#define _Time_h + +#include + +typedef enum {timeNotSet, timeNeedsSync, timeSet +} timeStatus_t ; + +typedef enum { + dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday +} timeDayOfWeek_t; + +typedef enum { + tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields +} tmByteFields; + +typedef struct { + uint8_t Second; + uint8_t Minute; + uint8_t Hour; + uint8_t Wday; // day of week, sunday is day 1 + uint8_t Day; + uint8_t Month; + uint8_t Year; // offset from 1970; +} tmElements_t, TimeElements, *tmElementsPtr_t; + +//convenience macros to convert to and from tm years +#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year +#define CalendarYrToTm(Y) ((Y) - 1970) +#define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000 +#define y2kYearToTm(Y) ((Y) + 30) + +typedef time_t(*getExternalTime)(); +//typedef void (*setExternalTime)(const time_t); // not used in this version + + +/*==============================================================================*/ +/* Useful Constants */ +#define SECS_PER_MIN ((time_t)(60UL)) +#define SECS_PER_HOUR ((time_t)(3600UL)) +#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL)) +#define DAYS_PER_WEEK ((time_t)(7UL)) +#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK)) +#define SECS_PER_YEAR ((time_t)(SECS_PER_DAY * 365UL)) // TODO: ought to handle leap years +#define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k + +/* Useful Macros for getting elapsed time */ +#define numberOfSeconds(_time_) ((_time_) % SECS_PER_MIN) +#define numberOfMinutes(_time_) (((_time_) / SECS_PER_MIN) % SECS_PER_MIN) +#define numberOfHours(_time_) (((_time_) % SECS_PER_DAY) / SECS_PER_HOUR) +#define dayOfWeek(_time_) ((((_time_) / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday +#define elapsedDays(_time_) ((_time_) / SECS_PER_DAY) // this is number of days since Jan 1 1970 +#define elapsedSecsToday(_time_) ((_time_) % SECS_PER_DAY) // the number of seconds since last midnight +// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 +// Always set the correct time before setting alarms +#define previousMidnight(_time_) (((_time_) / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day +#define nextMidnight(_time_) (previousMidnight(_time_) + SECS_PER_DAY) // time at the end of the given day +#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY)) // note that week starts on day 1 +#define previousSunday(_time_) ((_time_) - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time +#define nextSunday(_time_) (previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time + + +/* Useful Macros for converting elapsed time to a time_t */ +#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) +#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) +#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 +#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) + +/*============================================================================*/ +/* time and date functions */ +int hour(); // the hour now +int hour(time_t t); // the hour for the given time +int hourFormat12(); // the hour now in 12 hour format +int hourFormat12(time_t t); // the hour for the given time in 12 hour format +uint8_t isAM(); // returns true if time now is AM +uint8_t isAM(time_t t); // returns true the given time is AM +uint8_t isPM(); // returns true if time now is PM +uint8_t isPM(time_t t); // returns true the given time is PM +int minute(); // the minute now +int minute(time_t t); // the minute for the given time +int second(); // the second now +int second(time_t t); // the second for the given time +int day(); // the day now +int day(time_t t); // the day for the given time +int weekday(); // the weekday now (Sunday is day 1) +int weekday(time_t t); // the weekday for the given time +int month(); // the month now (Jan is month 1) +int month(time_t t); // the month for the given time +int year(); // the full four digit year: (2009, 2010 etc) +int year(time_t t); // the year for the given time + +const String timeString(); +const String timeString(time_t t); + +time_t now(); // return the current time as seconds since Jan 1 1970 +void setTime(time_t t); +void setTime(int hr,int min,int sec,int day, int month, int yr); +void adjustTime(long adjustment); + +/* date strings */ +#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) +const String monthStr(uint8_t month); +const String dayStr(uint8_t day); +const String monthShortStr(uint8_t month); +const String dayShortStr(uint8_t day); + +/* time sync functions */ +timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized +void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider +void setSyncInterval(time_t interval); // set the number of seconds between re-sync + +/* low level functions to convert to and from system time */ +void breakTime(time_t time, tmElements_t &tm); // break time_t into elements +time_t makeTime(const tmElements_t &tm); // convert time elements into time_t + +#endif /* _Time_h */ + diff --git a/lib/TimeLib/TimeLibString.cpp b/lib/TimeLib/TimeLibString.cpp new file mode 100644 index 0000000..49bb0e8 --- /dev/null +++ b/lib/TimeLib/TimeLibString.cpp @@ -0,0 +1,56 @@ +/* DateStrings.cpp + * Definitions for date strings for use with the Time library + * + * Updated for Arduino 1.5.7 18 July 2014 + * + * No memory is consumed in the sketch if your code does not call any of the string methods + * You can change the text of the strings, make sure the short strings are each exactly 3 characters + * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h + * + */ + +#include +#include "TimeLib.h" + +const String monthNames[] = +{ + "Error", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" +}; + +const String monthStr(uint8_t month) +{ + return monthNames[month]; +} + + +const String monthShortNames[] = +{ + "Err", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +const String monthShortStr(uint8_t month) +{ + return monthShortNames[month]; +} + + +const String dayNames[] = +{ + "Err", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +const String dayStr(uint8_t day) +{ + return dayNames[day]; +} + + +const String dayShortNames[] = +{ + "Err", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +const String dayShortStr(uint8_t day) +{ + return dayShortNames[day]; +} diff --git a/lib/common b/lib/common deleted file mode 160000 index 285c641..0000000 --- a/lib/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 285c641c628e803e54d847d65165a7dbbbe271df diff --git a/platformio.ini b/platformio.ini index e3808e9..03fd44c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,14 +4,12 @@ framework = arduino lib_ldf_mode = deep+ monitor_speed = 115200 lib_deps = - arduino-libraries/NTPClient @ 3.1.0 adafruit/Adafruit GFX Library @ 1.7.5 adafruit/Adafruit SSD1306 @ 2.4.0 bblanchon/ArduinoJson @ 6.17.0 lewisxhe/AXP202X_Library @ 1.1.2 - sandeepmistry/LoRa @ 0.7.2 peterus/APRS-Decoder-Lib @ 0.0.5 - peterus/APRS-IS-Lib @ 0.0.7 + peterus/esp-logger @ 0.0.1 peterus/ESP-FTP-Server-Lib @ 0.9.5 check_tool = cppcheck check_flags = diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp index f01ac92..93f20a6 100644 --- a/src/LoRa_APRS_iGate.cpp +++ b/src/LoRa_APRS_iGate.cpp @@ -1,65 +1,37 @@ #include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include "logger.h" +#include +#include "SignalSlot.h" #include "BoardFinder.h" #include "LoRa_APRS.h" -#include "pins.h" #include "display.h" -#include "project_configuration.h" - -#ifdef NO_GLOBAL_INSTANCES -HardwareSerial Serial(0); -ArduinoOTAClass ArduinoOTA; -#endif - #include "power_management.h" -PowerManagement powerManagement; +#include "project_configuration.h" +#include "connection.h" -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -hw_timer_t * timer = NULL; -volatile uint secondsSinceLastAPRSISBeacon = 0; -volatile uint secondsSinceStartup = 0; -volatile uint secondsSinceDisplay = 0; +HardwareSerial Serial(0); -WiFiMulti WiFiMulti; -WiFiUDP ntpUDP; -NTPClient * timeClient; -FTPServer ftpServer; -Configuration * Config; +std::shared_ptr OTA; +std::shared_ptr powerManagement; +std::shared_ptr WiFiMulti; +std::shared_ptr ntpClient; +std::shared_ptr ftpServer; +std::shared_ptr userConfig; std::shared_ptr boardConfig; -APRS_IS * aprs_is = 0; -LoRa_APRS * lora_aprs; +std::shared_ptr aprs_is; +std::shared_ptr lora_aprs; std::shared_ptr BeaconMsg; -volatile bool eth_connected = false; - String create_lat_aprs(double lat); String create_long_aprs(double lng); -void setup_eth(); -void setup_wifi(); +std::shared_ptr setup_lora(); -void load_config(); -void setup_wifi(); -void setup_ota(); -void setup_lora(); -void setup_ntp(); -void setup_aprs_is(); -void setup_timer(); -void setup_ftp(); - -std::map> lastMessages; +PrintMessageToConsole printMessageConsole; +bool ethEnabled = false; // cppcheck-suppress unusedFunction void setup() @@ -69,22 +41,31 @@ void setup() delay(500); ProjectConfigurationManagement confmg; - Config = confmg.readConfiguration(); + userConfig = confmg.readConfiguration(); - BoardFinder finder; - boardConfig = finder.getBoardConfig(Config->board); + std::list> boardConfigs; + boardConfigs.push_back(std::shared_ptr(new BoardConfig("TTGO_LORA32_V1", eTTGO_LORA32_V1, 4, 15, 0x3C, 0, 5, 19, 27, 18, 14, 26))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("TTGO_LORA32_V2", eTTGO_LORA32_V2, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("TTGO_T_Beam_V0_7", eTTGO_T_Beam_V0_7, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("TTGO_T_Beam_V1_0", eTTGO_T_Beam_V1_0, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true, true))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("ETH_BOARD", eETH_BOARD, 33, 32, 0x3C, 0, 14, 2, 15, 12, 4, 36))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("TRACKERD", eTRACKERD, 5, 4, 0x3C, 0, 18, 19, 23, 16, 14, 26))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("HELTEC_WIFI_LORA_32_V1", eHELTEC_WIFI_LORA_32_V1, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26))); + boardConfigs.push_back(std::shared_ptr(new BoardConfig("HELTEC_WIFI_LORA_32_V2", eHELTEC_WIFI_LORA_32_V2, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26))); + + BoardFinder finder(boardConfigs); + boardConfig = finder.getBoardConfig(userConfig->board); if(boardConfig == 0) { boardConfig = finder.searchBoardConfig(); if(boardConfig == 0) { logPrintlnE("Board config not set and search failed!"); - while (true) - { - } + while(true) + {} } - Config->board = boardConfig->Name; - confmg.writeConfiguration(Config); + userConfig->board = boardConfig->Name; + confmg.writeConfiguration(userConfig); logPrintlnI("will restart board now!"); ESP.restart(); } @@ -96,7 +77,8 @@ void setup() { TwoWire wire(0); wire.begin(boardConfig->OledSda, boardConfig->OledScl); - if (!powerManagement.begin(wire)) + std::shared_ptr powerManagement = std::shared_ptr(new PowerManagement); + if (!powerManagement->begin(wire)) { logPrintlnI("AXP192 init done!"); } @@ -104,9 +86,9 @@ void setup() { logPrintlnE("AXP192 init failed!"); } - powerManagement.activateLoRa(); - powerManagement.activateOLED(); - powerManagement.deactivateGPS(); + powerManagement->activateLoRa(); + powerManagement->activateOLED(); + powerManagement->deactivateGPS(); } logPrintlnW("LoRa APRS iGate by OE5BPA (Peter Buchegger)"); @@ -114,107 +96,76 @@ void setup() setup_display(boardConfig); show_display("OE5BPA", "LoRa APRS iGate", "by Peter Buchegger", "20.49.0-dev", 3000); - load_config(); - setup_lora(); - timeClient = new NTPClient(ntpUDP, Config->ntpServer.c_str()); + load_config(boardConfig); + lora_aprs = setup_lora(); if(boardConfig->Type == eETH_BOARD) { setup_eth(); - setup_ota(); - setup_ntp(); - setup_ftp(); - setup_aprs_is(); + ethEnabled = true; } - else - { - if(Config->wifi.active) - { - setup_wifi(); - setup_ota(); - setup_ntp(); - setup_ftp(); - } - else - { - // make sure wifi and bt is off if we don't need it: - WiFi.mode(WIFI_OFF); - btStop(); - } - if(Config->aprs_is.active) setup_aprs_is(); - } - setup_timer(); + WiFiMulti = setup_wifi(userConfig); + OTA = setup_ota(userConfig); + ntpClient = setup_ntp(userConfig); + ftpServer = setup_ftp(userConfig); + aprs_is = std::shared_ptr(new APRS_IS(userConfig->callsign, userConfig->aprs_is.password , "ESP32-APRS-IS", "0.1")); - if(Config->display.overwritePin != 0) + if(userConfig->display.overwritePin != 0) { - pinMode(Config->display.overwritePin, INPUT); - pinMode(Config->display.overwritePin, INPUT_PULLUP); + pinMode(userConfig->display.overwritePin, INPUT); + pinMode(userConfig->display.overwritePin, INPUT_PULLUP); } + logPrintlnD("connect objects..."); + lora_aprs->connectSlot(&printMessageConsole); + lora_aprs->connectSlot(aprs_is.get()); + delay(500); logPrintlnI("setup done..."); - secondsSinceDisplay = 0; } // cppcheck-suppress unusedFunction void loop() { - static bool display_is_on = true; - if(Config->display.overwritePin != 0 && !digitalRead(Config->display.overwritePin)) + static bool beacon_aprs_is = true; + if(userConfig->ftp.active) { - secondsSinceDisplay = 0; - display_is_on = true; - setup_display(boardConfig); - } else - if(!Config->display.alwaysOn && secondsSinceDisplay > Config->display.timeout && display_is_on) - { - turn_off_display(); - display_is_on = false; - } - - static bool beacon_aprs_is = Config->aprs_is.active && Config->aprs_is.beacon; - - if(Config->aprs_is.active && Config->aprs_is.beacon && secondsSinceLastAPRSISBeacon >= (Config->aprs_is.beaconTimeout*60)) - { - portENTER_CRITICAL(&timerMux); - secondsSinceLastAPRSISBeacon -= (Config->aprs_is.beaconTimeout*60); - portEXIT_CRITICAL(&timerMux); - beacon_aprs_is = true; - } - - if(Config->ftp.active) - { - ftpServer.handle(); + ftpServer->handle(); static bool configWasOpen = false; - if(configWasOpen && ftpServer.countConnections() == 0) + if(configWasOpen && ftpServer->countConnections() == 0) { logPrintlnW("Maybe the config has been changed via FTP, lets restart now to get the new config..."); Serial.println(); ESP.restart(); } - if(ftpServer.countConnections() > 0) + if(ftpServer->countConnections() > 0) { configWasOpen = true; } } - if(Config->wifi.active || eth_connected) ArduinoOTA.handle(); - if(Config->wifi.active && WiFiMulti.run() != WL_CONNECTED) + const uint8_t wifi_status = WiFiMulti->run(); + if(!ethEnabled && wifi_status != WL_CONNECTED) { - setup_display(boardConfig); secondsSinceDisplay = 0; display_is_on = true; logPrintlnE("WiFi not connected!"); show_display("ERROR", "WiFi not connected!"); delay(1000); return; } - if((eth_connected && !aprs_is->connected()) || (Config->aprs_is.active && !aprs_is->connected())) + + OTA->handle(); + if(ntpClient->update()) + { + setTime(ntpClient->getEpochTime()); + } + + if(!aprs_is->connected()) { - setup_display(boardConfig); secondsSinceDisplay = 0; display_is_on = true; logPrintI("connecting to APRS-IS server: "); - logPrintI(Config->aprs_is.server); + logPrintI(userConfig->aprs_is.server); logPrintI(" on port: "); - logPrintlnI(String(Config->aprs_is.port)); + logPrintlnI(String(userConfig->aprs_is.port)); show_display("INFO", "Connecting to APRS-IS server"); - if(!aprs_is->connect(Config->aprs_is.server, Config->aprs_is.port)) + if(!aprs_is->connect(userConfig->aprs_is.server, userConfig->aprs_is.port)) { logPrintlnE("Connection failed."); logPrintlnI("Waiting 5 seconds before retrying..."); @@ -224,274 +175,48 @@ void loop() } logPrintlnI("Connected to APRS-IS server!"); } - if(Config->aprs_is.active && aprs_is->available() > 0) - { - String str = aprs_is->getMessage(); - logPrintD("[" + timeClient->getFormattedTime() + "] "); - logPrintlnD(str); - } - if(lora_aprs->hasMessage()) - { - std::shared_ptr msg = lora_aprs->getMessage(); - setup_display(boardConfig); secondsSinceDisplay = 0; display_is_on = true; - show_display(Config->callsign, timeClient->getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs->packetRssi()) + ", SNR: " + String(lora_aprs->packetSnr()), msg->toString()); - logPrintD("[" + timeClient->getFormattedTime() + "] "); - logPrintD(" Received packet '"); - logPrintD(msg->toString()); - logPrintD("' with RSSI "); - logPrintD(String(lora_aprs->packetRssi())); - logPrintD(" and SNR "); - logPrintlnD(String(lora_aprs->packetSnr())); - - if(Config->aprs_is.active) - { - aprs_is->sendMessage(msg->encode()); - } - } - if(beacon_aprs_is) + aprs_is->getAPRSMessage(); + lora_aprs->checkMessage(); + + if(false) //beacon_aprs_is { beacon_aprs_is = false; - setup_display(boardConfig); secondsSinceDisplay = 0; display_is_on = true; - show_display(Config->callsign, "Beacon to APRS-IS Server..."); - logPrintD("[" + timeClient->getFormattedTime() + "] "); + show_display(userConfig->callsign, "Beacon to APRS-IS Server..."); + logPrintD("[" + ntpClient->getFormattedTime() + "] "); logPrintlnD(BeaconMsg->encode()); aprs_is->sendMessage(BeaconMsg); - show_display(Config->callsign, "Standby..."); + show_display(userConfig->callsign, "Standby..."); } } -void load_config() +std::shared_ptr setup_lora() { - ProjectConfigurationManagement confmg; - Config = confmg.readConfiguration(); - if(Config->callsign == "NOCALL-10") - { - logPrintlnE("You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); - show_display("ERROR", "You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); - while (true) - {} - } - - if(boardConfig->Type != eETH_BOARD && Config->aprs_is.active && !Config->wifi.active) - { - logPrintlnE("You have to activate Wifi for APRS IS to work, please check your settings!"); - show_display("ERROR", "You have to activate Wifi for APRS IS to work, please check your settings!"); - while (true) - {} - } - - if(KEY_BUILTIN != 0 && Config->display.overwritePin == 0) - { - Config->display.overwritePin = KEY_BUILTIN; - } - logPrintlnI("Configuration loaded!"); -} - -void WiFiEvent(WiFiEvent_t event) -{ - switch (event) { - case SYSTEM_EVENT_ETH_START: - logPrintlnI("ETH Started"); - ETH.setHostname("esp32-ethernet"); - break; - case SYSTEM_EVENT_ETH_CONNECTED: - logPrintlnI("ETH Connected"); - break; - case SYSTEM_EVENT_ETH_GOT_IP: - logPrintI("ETH MAC: "); - logPrintI(ETH.macAddress()); - logPrintI(", IPv4: "); - logPrintI(ETH.localIP().toString()); - if (ETH.fullDuplex()) { - logPrintI(", FULL_DUPLEX"); - } - logPrintI(", "); - logPrintI(String(ETH.linkSpeed())); - logPrintlnI("Mbps"); - eth_connected = true; - break; - case SYSTEM_EVENT_ETH_DISCONNECTED: - logPrintlnW("ETH Disconnected"); - eth_connected = false; - break; - case SYSTEM_EVENT_ETH_STOP: - logPrintlnW("ETH Stopped"); - eth_connected = false; - break; - default: - break; - } -} - -void setup_eth() -{ - WiFi.onEvent(WiFiEvent); - - #define ETH_POWER_PIN -1 - #define ETH_TYPE ETH_PHY_LAN8720 - #define ETH_ADDR 0 - #define ETH_MDC_PIN 23 - #define ETH_MDIO_PIN 18 - #define ETH_NRST 5 - #define ETH_CLK ETH_CLOCK_GPIO17_OUT // TTGO PoE V1.0 - //#define ETH_CLK ETH_CLOCK_GPIO0_OUT // TTGO PoE V1.2 - - pinMode(ETH_NRST, OUTPUT); - digitalWrite(ETH_NRST, 0); - delay(200); - digitalWrite(ETH_NRST, 1); - delay(200); - digitalWrite(ETH_NRST, 0); - delay(200); - digitalWrite(ETH_NRST, 1); - - ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK); - while(!eth_connected) - { - sleep(1); - } -} - -void setup_wifi() -{ - WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); - WiFi.setHostname(Config->callsign.c_str()); - for(Configuration::Wifi::AP ap : Config->wifi.APs) - { - logPrintD("Looking for AP: "); - logPrintlnD(ap.SSID); - WiFiMulti.addAP(ap.SSID.c_str(), ap.password.c_str()); - } - logPrintlnI("Waiting for WiFi"); - show_display("INFO", "Waiting for WiFi"); - while(WiFiMulti.run() != WL_CONNECTED) - { - show_display("INFO", "Waiting for WiFi", "...."); - delay(500); - } - logPrintlnI("WiFi connected"); - logPrintD("IP address: "); - logPrintlnD(WiFi.localIP().toString()); - show_display("INFO", "WiFi connected", "IP: ", WiFi.localIP().toString(), 2000); -} - -void setup_ota() -{ - ArduinoOTA - .onStart([]() - { - String type; - if (ArduinoOTA.getCommand() == U_FLASH) - type = "sketch"; - else // U_SPIFFS - type = "filesystem"; - Serial.println("Start updating " + type); - show_display("OTA UPDATE", "Start update", type); - }) - .onEnd([]() - { - Serial.println(); - Serial.println("End"); - }) - .onProgress([](unsigned int progress, unsigned int total) - { - Serial.print("Progress: "); - Serial.print(progress / (total / 100)); - Serial.println("%"); - show_display("OTA UPDATE", "Progress: ", String(progress / (total / 100)) + "%"); - }) - .onError([](ota_error_t error) { - Serial.print("Error["); - Serial.print(error); - Serial.print("]: "); - if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); - else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); - else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); - else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); - else if (error == OTA_END_ERROR) Serial.println("End Failed"); - }); - ArduinoOTA.setHostname(Config->callsign.c_str()); - ArduinoOTA.begin(); - logPrintlnI("OTA init done!"); -} - -void setup_lora() -{ - lora_aprs = new LoRa_APRS(boardConfig); - if (!lora_aprs->begin(lora_aprs->getRxFrequency())) + std::shared_ptr lora_aprs = std::shared_ptr(new LoRa_APRS(boardConfig)); + if(!lora_aprs->begin(lora_aprs->getRxFrequency())) { logPrintlnE("Starting LoRa failed!"); show_display("ERROR", "Starting LoRa failed!"); - while (1); + while(true); } - lora_aprs->setRxFrequency(Config->lora.frequencyRx); - lora_aprs->setTxFrequency(Config->lora.frequencyTx); - lora_aprs->setTxPower(Config->lora.power); - lora_aprs->setSpreadingFactor(Config->lora.spreadingFactor); - lora_aprs->setSignalBandwidth(Config->lora.signalBandwidth); - lora_aprs->setCodingRate4(Config->lora.codingRate4); + lora_aprs->setRxFrequency(userConfig->lora.frequencyRx); + lora_aprs->setTxFrequency(userConfig->lora.frequencyTx); + lora_aprs->setTxPower(userConfig->lora.power); + lora_aprs->setSpreadingFactor(userConfig->lora.spreadingFactor); + lora_aprs->setSignalBandwidth(userConfig->lora.signalBandwidth); + lora_aprs->setCodingRate4(userConfig->lora.codingRate4); lora_aprs->enableCrc(); logPrintlnI("LoRa init done!"); show_display("INFO", "LoRa init done!", 2000); BeaconMsg = std::shared_ptr(new APRSMessage()); - BeaconMsg->setSource(Config->callsign); + BeaconMsg->setSource(userConfig->callsign); BeaconMsg->setDestination("APLG0"); - String lat = create_lat_aprs(Config->beacon.positionLatitude); - String lng = create_long_aprs(Config->beacon.positionLongitude); - BeaconMsg->getAPRSBody()->setData(String("=") + lat + "I" + lng + "&" + Config->beacon.message); -} + String lat = create_lat_aprs(userConfig->beacon.positionLatitude); + String lng = create_long_aprs(userConfig->beacon.positionLongitude); + BeaconMsg->getAPRSBody()->setData(String("=") + lat + "I" + lng + "&" + userConfig->beacon.message); -void setup_ntp() -{ - timeClient->begin(); - while(!timeClient->forceUpdate()) - { - logPrintlnW("NTP Client force update issue! Waiting 1 sek..."); - show_display("WARN", "NTP Client force update issue! Waiting 1 sek...", 1000); - } - logPrintlnI("NTP Client init done!"); - show_display("INFO", "NTP Client init done!", 2000); -} - -void setup_aprs_is() -{ - aprs_is = new APRS_IS(Config->callsign, Config->aprs_is.password , "ESP32-APRS-IS", "0.1"); -} - -void IRAM_ATTR onTimer() -{ - portENTER_CRITICAL_ISR(&timerMux); - secondsSinceLastAPRSISBeacon++; - secondsSinceStartup++; - secondsSinceDisplay++; - portEXIT_CRITICAL_ISR(&timerMux); -} - -void setup_timer() -{ - timer = timerBegin(0, 80, true); - timerAlarmWrite(timer, 1000000, true); - timerAttachInterrupt(timer, &onTimer, true); - timerAlarmEnable(timer); -} - -void setup_ftp() -{ - if(!Config->ftp.active) - { - return; - } - for(Configuration::Ftp::User user : Config->ftp.users) - { - logPrintD("Adding user to FTP Server: "); - logPrintlnD(user.name); - ftpServer.addUser(user.name, user.password); - } - ftpServer.addFilesystem("SPIFFS", &SPIFFS); - ftpServer.begin(); - logPrintlnI("FTP Server init done!"); + return lora_aprs; } String create_lat_aprs(double lat) diff --git a/src/connection.cpp b/src/connection.cpp new file mode 100644 index 0000000..f294ee6 --- /dev/null +++ b/src/connection.cpp @@ -0,0 +1,183 @@ + +#include +#include +#include +#include +#include + +#include "connection.h" + +volatile bool eth_connected = false; + +static void WiFiEvent(WiFiEvent_t event) +{ + switch (event) { + case SYSTEM_EVENT_ETH_START: + logPrintlnI("ETH Started"); + ETH.setHostname("esp32-ethernet"); + break; + case SYSTEM_EVENT_ETH_CONNECTED: + logPrintlnI("ETH Connected"); + break; + case SYSTEM_EVENT_ETH_GOT_IP: + logPrintI("ETH MAC: "); + logPrintI(ETH.macAddress()); + logPrintI(", IPv4: "); + logPrintI(ETH.localIP().toString()); + if (ETH.fullDuplex()) { + logPrintI(", FULL_DUPLEX"); + } + logPrintI(", "); + logPrintI(String(ETH.linkSpeed())); + logPrintlnI("Mbps"); + eth_connected = true; + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + logPrintlnW("ETH Disconnected"); + eth_connected = false; + break; + case SYSTEM_EVENT_ETH_STOP: + logPrintlnW("ETH Stopped"); + eth_connected = false; + break; + default: + break; + } +} + +bool isEthConnected() +{ + return eth_connected; +} + +void setup_eth() +{ + WiFi.onEvent(WiFiEvent); + + #define ETH_POWER_PIN -1 + #define ETH_TYPE ETH_PHY_LAN8720 + #define ETH_ADDR 0 + #define ETH_MDC_PIN 23 + #define ETH_MDIO_PIN 18 + #define ETH_NRST 5 + #define ETH_CLK ETH_CLOCK_GPIO17_OUT // TTGO PoE V1.0 + //#define ETH_CLK ETH_CLOCK_GPIO0_OUT // TTGO PoE V1.2 + + pinMode(ETH_NRST, OUTPUT); + digitalWrite(ETH_NRST, 0); + delay(200); + digitalWrite(ETH_NRST, 1); + delay(200); + digitalWrite(ETH_NRST, 0); + delay(200); + digitalWrite(ETH_NRST, 1); + + ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK); + while(!eth_connected) + { + sleep(1); + } +} + +std::shared_ptr setup_wifi(std::shared_ptr config) +{ + WiFi.onEvent(WiFiEvent); + //WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); + WiFi.setHostname(config->callsign.c_str()); + std::shared_ptr wiFiMulti = std::shared_ptr(new WiFiMulti());; + for(Configuration::Wifi::AP ap : config->wifi.APs) + { + logPrintD("Looking for AP: "); + logPrintlnD(ap.SSID); + wiFiMulti->addAP(ap.SSID.c_str(), ap.password.c_str()); + } + logPrintlnI("Waiting for WiFi"); + //show_display("INFO", "Waiting for WiFi"); + while(wiFiMulti->run() != WL_CONNECTED) + { + //show_display("INFO", "Waiting for WiFi", "...."); + delay(500); + } + logPrintlnI("WiFi connected"); + logPrintD("IP address: "); + logPrintlnD(WiFi.localIP().toString()); + //show_display("INFO", "WiFi connected", "IP: ", WiFi.localIP().toString(), 2000); + return wiFiMulti; +} + +std::shared_ptr setup_ota(std::shared_ptr config) +{ + std::shared_ptr ota = std::shared_ptr(new ArduinoOTAClass()); + ota->onStart([&]() + { + String type; + if (ota->getCommand() == U_FLASH) + type = "sketch"; + else // U_SPIFFS + type = "filesystem"; + logPrintlnI("Start updating " + type); + //show_display("OTA UPDATE", "Start update", type); + }) + .onEnd([]() + { + logPrintlnI(""); + logPrintlnI("OTA End"); + }) + .onProgress([](unsigned int progress, unsigned int total) + { + logPrintI("Progress: "); + logPrintI(String(progress / (total / 100))); + logPrintlnI("%"); + //show_display("OTA UPDATE", "Progress: ", String(progress / (total / 100)) + "%"); + }) + .onError([](ota_error_t error) + { + logPrintE("Error["); + logPrintE(String(error)); + logPrintE("]: "); + if (error == OTA_AUTH_ERROR) logPrintlnE("Auth Failed"); + else if (error == OTA_BEGIN_ERROR) logPrintlnE("Begin Failed"); + else if (error == OTA_CONNECT_ERROR) logPrintlnE("Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) logPrintlnE("Receive Failed"); + else if (error == OTA_END_ERROR) logPrintlnE("End Failed"); + }); + ota->setHostname(config->callsign.c_str()); + ota->begin(); + logPrintlnI("OTA init done!"); + return ota; +} + +std::shared_ptr setup_ntp(std::shared_ptr config) +{ + std::shared_ptr ntpClient = std::shared_ptr(new NTPClient(config->ntpServer.c_str())); + ntpClient->begin(); + while(!ntpClient->forceUpdate()) + { + logPrintlnW("NTP Client force update issue! Waiting 1 sek..."); + logPrintlnD(ntpClient->getFormattedTime()); + //show_display("WARN", "NTP Client force update issue! Waiting 1 sek...", 1000); + sleep(1); + } + setTime(ntpClient->getEpochTime()); + logPrintlnI("NTP Client init done!"); + //show_display("INFO", "NTP Client init done!", 2000); + return ntpClient; +} + +std::shared_ptr setup_ftp(std::shared_ptr config) +{ + std::shared_ptr ftpServer = std::shared_ptr(new FTPServer()); + if(config->ftp.active) + { + for(Configuration::Ftp::User user : config->ftp.users) + { + logPrintD("Adding user to FTP Server: "); + logPrintlnD(user.name); + ftpServer->addUser(user.name, user.password); + } + ftpServer->addFilesystem("SPIFFS", &SPIFFS); + ftpServer->begin(); + logPrintlnI("FTP Server init done!"); + } + return ftpServer; +} diff --git a/src/connection.h b/src/connection.h new file mode 100644 index 0000000..2ef9880 --- /dev/null +++ b/src/connection.h @@ -0,0 +1,19 @@ + +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +#include +#include +#include +#include +#include +#include "project_configuration.h" + +bool isEthConnected(); +void setup_eth(); +std::shared_ptr setup_wifi(std::shared_ptr config); +std::shared_ptr setup_ota(std::shared_ptr config); +std::shared_ptr setup_ntp(std::shared_ptr config); +std::shared_ptr setup_ftp(std::shared_ptr config); + +#endif diff --git a/src/display.cpp b/src/display.cpp new file mode 100644 index 0000000..ba54c22 --- /dev/null +++ b/src/display.cpp @@ -0,0 +1,142 @@ + +#include +#include +#include + +#include "BoardFinder.h" +#include "display.h" +#include "logger.h" + +Adafruit_SSD1306 * display = 0; +TwoWire * wire = 0; + +void setup_display(std::shared_ptr boardConfig) +{ + if(display == 0) + { + wire = new TwoWire(0); + wire->begin(boardConfig->OledSda, boardConfig->OledScl); + if(boardConfig->OledReset > 0) + { + display = new Adafruit_SSD1306(128, 64, wire, boardConfig->OledReset); + logPrintlnI("with reset"); + } + else + { + display = new Adafruit_SSD1306(128, 64, wire); + logPrintlnI("with NO reset"); + } + } + if(!display->begin(SSD1306_SWITCHCAPVCC, boardConfig->OledAddr, false, false)) + { + logPrintlnE("SSD1306 allocation failed"); + while (1); + } + logPrintlnI("Display init done!"); +} + +void turn_off_display() +{ + display->ssd1306_command(SSD1306_DISPLAYOFF); +} + +void show_display(String header, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->display(); + delay(wait); +} + +void show_display(String header, String line1, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->setTextSize(1); + display->setCursor(0,16); + display->println(line1); + display->display(); + delay(wait); +} + +void show_display(String header, String line1, String line2, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->setTextSize(1); + display->setCursor(0,16); + display->println(line1); + display->setCursor(0,26); + display->println(line2); + display->display(); + delay(wait); +} + +void show_display(String header, String line1, String line2, String line3, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->setTextSize(1); + display->setCursor(0,16); + display->println(line1); + display->setCursor(0,26); + display->println(line2); + display->setCursor(0,36); + display->println(line3); + display->display(); + delay(wait); +} + +void show_display(String header, String line1, String line2, String line3, String line4, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->setTextSize(1); + display->setCursor(0,16); + display->println(line1); + display->setCursor(0,26); + display->println(line2); + display->setCursor(0,36); + display->println(line3); + display->setCursor(0,46); + display->println(line4); + display->display(); + delay(wait); +} + +void show_display(String header, String line1, String line2, String line3, String line4, String line5, int wait) +{ + display->clearDisplay(); + display->setTextColor(WHITE); + display->setTextSize(2); + display->setCursor(0,0); + display->println(header); + display->setTextSize(1); + display->setCursor(0,16); + display->println(line1); + display->setCursor(0,26); + display->println(line2); + display->setCursor(0,36); + display->println(line3); + display->setCursor(0,46); + display->println(line4); + display->setCursor(0,56); + display->println(line5); + display->display(); + delay(wait); +} diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..f89c431 --- /dev/null +++ b/src/display.h @@ -0,0 +1,47 @@ + +#ifndef DISPLAY_H_ +#define DISPLAY_H_ + +void setup_display(std::shared_ptr boardConfig); +void turn_off_display(); + +void show_display(String header, int wait = 0); +void show_display(String header, String line1, int wait = 0); +void show_display(String header, String line1, String line2, int wait = 0); +void show_display(String header, String line1, String line2, String line3, int wait = 0); +void show_display(String header, String line1, String line2, String line3, String line4, int wait = 0); +void show_display(String header, String line1, String line2, String line3, String line4, String line5, int wait = 0); + +#include +#include "SignalSlot.h" +#include "TimeLib.h" +#include "logger.h" + +class PrintMessageToConsole : public Slot1>, public Slot3, int, float> +{ +public: + void action(std::shared_ptr msg, int rssi, float snr) override + { + //setup_display(boardConfig); secondsSinceDisplay = 0; //display_is_on = true; + //show_display(Config->callsign, timeClient->getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs->packetRssi()) + ", SNR: " + String(lora_aprs->packetSnr()), elem->toString()); + logPrintD("[" + timeString() + "] "); + logPrintD("Received packet '"); + logPrintD(msg->toString()); + logPrintD("' with RSSI "); + logPrintD(String(rssi)); + logPrintD(" and SNR "); + logPrintlnD(String(snr)); + } + + void action(std::shared_ptr msg) override + { + //setup_display(boardConfig); secondsSinceDisplay = 0; //display_is_on = true; + //show_display(Config->callsign, timeClient->getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs->packetRssi()) + ", SNR: " + String(lora_aprs->packetSnr()), elem->toString()); + logPrintD("[" + timeString() + "] "); + logPrintD("Received packet '"); + logPrintD(msg->toString()); + logPrintD("'"); + } +}; + +#endif diff --git a/src/project_configuration.cpp b/src/project_configuration.cpp index dd8be2f..5b1175f 100644 --- a/src/project_configuration.cpp +++ b/src/project_configuration.cpp @@ -3,13 +3,12 @@ #include "project_configuration.h" #include "logger.h" -Configuration * ProjectConfigurationManagement::readProjectConfiguration(DynamicJsonDocument & data) +std::shared_ptr ProjectConfigurationManagement::readProjectConfiguration(DynamicJsonDocument & data) { - Configuration * conf = new Configuration; + std::shared_ptr conf = std::shared_ptr(new Configuration); if(data.containsKey("callsign")) conf->callsign = data["callsign"].as(); - conf->wifi.active = data["wifi"]["active"] | false; JsonArray aps = data["wifi"]["AP"].as(); for(JsonVariant v : aps) { @@ -22,7 +21,6 @@ Configuration * ProjectConfigurationManagement::readProjectConfiguration(Dynamic conf->beacon.message = data["beacon"]["message"].as(); conf->beacon.positionLatitude = data["beacon"]["position"]["latitude"] | 0.0; conf->beacon.positionLongitude = data["beacon"]["position"]["longitude"] | 0.0; - conf->aprs_is.active = data["aprs_is"]["active"] | false; if(data.containsKey("aprs_is") && data["aprs_is"].containsKey("password")) conf->aprs_is.password = data["aprs_is"]["password"].as(); if(data.containsKey("aprs_is") && data["aprs_is"].containsKey("server")) @@ -66,10 +64,9 @@ Configuration * ProjectConfigurationManagement::readProjectConfiguration(Dynamic return conf; } -void ProjectConfigurationManagement::writeProjectConfiguration(Configuration * conf, DynamicJsonDocument & data) +void ProjectConfigurationManagement::writeProjectConfiguration(std::shared_ptr conf, DynamicJsonDocument & data) { data["callsign"] = conf->callsign; - data["wifi"]["active"] = conf->wifi.active; JsonArray aps = data["wifi"].createNestedArray("AP"); for(Configuration::Wifi::AP ap : conf->wifi.APs) { @@ -80,7 +77,6 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration * c data["beacon"]["message"] = conf->beacon.message; data["beacon"]["position"]["latitude"] = conf->beacon.positionLatitude; data["beacon"]["position"]["longitude"] = conf->beacon.positionLongitude; - data["aprs_is"]["active"] = conf->aprs_is.active; data["aprs_is"]["password"] = conf->aprs_is.password; data["aprs_is"]["server"] = conf->aprs_is.server; data["aprs_is"]["port"] = conf->aprs_is.port; @@ -107,3 +103,23 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration * c data["board"] = conf->board; } + +std::shared_ptr load_config(std::shared_ptr boardConfig) +{ + ProjectConfigurationManagement confmg; + std::shared_ptr config = confmg.readConfiguration(); + if(config->callsign == "NOCALL-10") + { + logPrintlnE("You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); + //show_display("ERROR", "You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); + while (true) + {} + } + + /*if(KEY_BUILTIN != 0 && Config->display.overwritePin == 0) + { + Config->display.overwritePin = KEY_BUILTIN; + }*/ + logPrintlnI("Configuration loaded!"); + return config; +} diff --git a/src/project_configuration.h b/src/project_configuration.h index b9c4d5d..42a91d0 100644 --- a/src/project_configuration.h +++ b/src/project_configuration.h @@ -2,6 +2,7 @@ #define PROJECT_CONFIGURATION_H_ #include "configuration.h" +#include "BoardFinder.h" class Configuration { @@ -16,9 +17,8 @@ public: String password; }; - Wifi() : active(false) {} + Wifi() {} - bool active; std::list APs; }; @@ -35,9 +35,8 @@ public: class APRS_IS { public: - APRS_IS() : active(false), server("euro.aprs2.net"), port(14580), beacon(true), beaconTimeout(15) {} + APRS_IS() : server("euro.aprs2.net"), port(14580), beacon(true), beaconTimeout(15) {} - bool active; String password; String server; int port; @@ -104,8 +103,10 @@ public: virtual ~ProjectConfigurationManagement() {} private: - virtual Configuration * readProjectConfiguration(DynamicJsonDocument & data) override; - virtual void writeProjectConfiguration(Configuration * conf, DynamicJsonDocument & data) override; + virtual std::shared_ptr readProjectConfiguration(DynamicJsonDocument & data) override; + virtual void writeProjectConfiguration(std::shared_ptr conf, DynamicJsonDocument & data) override; }; +std::shared_ptr load_config(std::shared_ptr boardConfig); + #endif