diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..eb3135b --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,6 @@ +ARG VARIANT="3.10-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +RUN apt-get update && apt-get install -y clang-format-11 + +RUN pip3 --disable-pip-version-check --no-cache-dir install platformio diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..dc7411a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Python 3", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "VARIANT": "3.10-bullseye" + } + }, + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", + "clang-format.executable": "clang-format-11" + }, + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "platformio.platformio-ide", + "xaver.clang-format" + ], + "postCreateCommand": "pip3 install --user platformio", + //"remoteUser": "vscode" +} diff --git a/.github/workflows/build_check.yml b/.github/workflows/build_check.yml index 542fe65..ef7e68f 100644 --- a/.github/workflows/build_check.yml +++ b/.github/workflows/build_check.yml @@ -1,6 +1,13 @@ name: Integreation Tests -on: [push, pull_request] +on: + push: + branches: + - '*' + - '!master' + pull_request: + branches: + - master jobs: build: @@ -31,8 +38,6 @@ jobs: - 'lib/BoardFinder' - 'lib/ConfigurationManagement' #- 'lib/Display' - #- 'lib/LoRa' - - 'lib/LoRa_APRS' #- 'lib/NTPClient' - 'lib/PowerManagement' - 'lib/System' @@ -41,7 +46,7 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - name: Run clang-format style check for C/C++ programs. - uses: jidicula/clang-format-action@v3.2.0 + uses: jidicula/clang-format-action@v4.5.0 with: clang-format-version: '11' check-path: ${{ matrix.path }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ccc28a..71a9939 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,46 +1,39 @@ -on: - push: - tags: - - 'v*' +name: Create new release -name: Upload Release Assets +on: + workflow_dispatch: jobs: - build: - name: Upload Release Assets + version_check: + name: Version Check runs-on: ubuntu-latest steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v2 + - run: pip install GitPython + - name: check version + run: ./scripts/check_version.py + + create_release: + needs: version_check + name: Create new release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 - run: sudo apt-get install python3-setuptools python3-wheel - run: pip3 install platformio - run: echo "$HOME/.local/bin" >> $GITHUB_PATH - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - run: platformio run - - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: echo "VERSION=$(./scripts/create_version_tag.py)" >> $GITHUB_ENV + - uses: ncipollo/release-action@v1 with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - # upload firmware bin - - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: .pio/build/lora_board/firmware.bin - asset_name: lora_board.bin - asset_content_type: application/bin - # upload json file - - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: data/is-cfg.json - asset_name: is-cfg.json - asset_content_type: application/json \ No newline at end of file + tag: ${{ env.VERSION }} + commit: master + generateReleaseNotes: true + artifacts: ".pio/build/lora_board/firmware.bin,data/is-cfg.json" + owner: ${{ secrets.OWNER }} + token: ${{ secrets.PAT }} diff --git a/.github/workflows/tweet_release.yml b/.github/workflows/tweet_release.yml new file mode 100644 index 0000000..462b2bc --- /dev/null +++ b/.github/workflows/tweet_release.yml @@ -0,0 +1,19 @@ +name: tweet-release + +on: + release: + types: [published] + +jobs: + tweet: + runs-on: ubuntu-latest + steps: + - uses: Eomm/why-don-t-you-tweet@v1 + if: ${{ !github.event.repository.private }} + with: + tweet-message: "New ${{ github.event.repository.name }} release ${{ github.event.release.tag_name }}! ${{ github.event.release.html_url }} #LoRa #APRS #HAM #hamradio #iGate" + env: + TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }} + TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} + TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} diff --git a/README.md b/README.md index bc8bd22..6766f7e 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ Try it out and be part of the APRS network. * [Manuel Lausmann - Tracker](https://www.youtube.com/watch?v=clIlTEFbWLk) (youtube - german - OLD) 02.11.2020 * [OE1ROT](https://www.aronaut.at/2019/12/lora-aprs-tracker-mit-ttgo-t-beam-433mhz/) (blog post - german) 09.12.2019 -feel free to add yours or create a ticket if you want to be added. +Feel free to add yours or create a ticket if you want to be added. ## Supported boards -You can use one of the Lora32 boards without changings: +You can use one of the Lora32 boards without changes: * Heltec WiFi LoRa 32 V1 (433MHz SX1278) * Heltec WiFi LoRa 32 V2 (433MHz SX1278) @@ -42,7 +42,7 @@ Here are some amazon-de links for some example boards: * [T-Beam V1.0](https://www.amazon.de/dp/B07RT9FKPL) This boards cost around 20 Euros, they are very cheap and perfect for an LoRa iGate. -Keep in minde: you need a 433MHz version! +Keep in mind: you need a 433MHz version! ## Compiling and configuration @@ -63,22 +63,12 @@ The best success is to use PlatformIO (and it is the only platform where I can s * You can find all nessesary settings to change for your configuration in **data/is-cfg.json**. * To upload it to your board you have to do this via **Upload File System image** in PlatformIO! -* To find the 'Upload File System image' click the PlatformIO symbol (the little alien) on the left side, choos your configuration, click on 'Platform' and search for 'Upload File System image'. +* To find the 'Upload File System image' click the PlatformIO symbol (the little alien) on the left side, choose your configuration, click on 'Platform' and search for 'Upload File System image'. ## Branches in this repository and version system This firmware is developed in a rolling release system: everyday a new release could be created. But there are still rules where new pull requests has to go and and how the version system looks like. -### Branches - -There are 2 main branches: -* *master* and -* *develop* - -The *master* branch has all releases and is the most stable one. With the different tags you can jump to different versions or if you take the most current *master* branch you will just get the latest, stable version. There will be no side releases which are branched of from *master*. If there is a bugfix version it will be done directly on the *master* branch and a tag will be generated with a new version. - -The *develop* branch is used for new feature development. It will be also used to stabilize the current development to create a new stable version (pull request to *master* branch). **Again:** all new development (pull requests) has to go directly into the *develop* branch! - ### Version system If the *develop* branch is stable enough for a new release it will be merged with a pull request to the *master* branch and a new version will be generated. @@ -116,13 +106,15 @@ Look at my other project: a [LoRa Tracker](https://github.com/peterus/LoRa_APRS_ ### Here are some peculiarities of the different boards -* TTGO T-Beam V1 +* TTGO T-Beam V1.0 and V1.1 and SSD1306 OLED display -When adding a 0,96" OLED display direct to the board you have to be careful, there are two different pinout +When adding an SSD1306 0,96" OLED display direct to the board you have to be careful, there are two different pinout versions on the market. For direct mount you need a display with this Pinout -> [VCC - GND - SCL - SDA](pics/display-right.jpg). A direct mount of the [other display](pics/display-wrong.jpg) is not possible without damage the display! The 'wrong' display works too but you have to change VCC and GND by wire ! -feel free to add hints! +The [LoRa APRS WiKi Displays](https://github.com/lora-aprs/LoRa_APRS_Tracker/wiki/Displays) page has more details. + +Feel free to add hints! diff --git a/data/is-cfg.json b/data/is-cfg.json index 736d44c..47d6f65 100644 --- a/data/is-cfg.json +++ b/data/is-cfg.json @@ -2,13 +2,20 @@ "callsign": "NOCALL-10", "network": { "DHCP": true, - "staticIP": "192.0.2.100", - "subnet": "255.255.255.0", - "gateway": "192.0.2.1", - "dns1": "192.0.2.1", - "dns2": "192.0.2.2" + "static": { + "ip": "192.168.0.100", + "subnet": "255.255.255.0", + "gateway": "192.168.0.1", + "dns1": "192.168.0.1", + "dns2": "192.168.0.2" + }, + "hostname": { + "overwrite": false, + "name": "NOCALL-10" + } }, "wifi": { + "active": true, "AP": [ { "SSID": "YOURSSID", @@ -22,6 +29,7 @@ "latitude": 0.000000, "longitude": 0.000000 }, + "use_gps": false, "timeout": 15 }, "aprs_is": { @@ -36,11 +44,13 @@ }, "lora": { "frequency_rx": 433775000, + "gain_rx": 0, "frequency_tx": 433775000, "power": 20, "spreading_factor": 12, "signal_bandwidth": 125000, - "coding_rate4": 5 + "coding_rate4": 5, + "tx_enable": false }, "display": { "always_on": true, @@ -57,5 +67,18 @@ } ] }, + "mqtt": { + "active": false, + "server": "", + "port": 1883, + "name": "", + "password": "", + "topic": "LoraAPRS/Data" + }, + "syslog": { + "active": true, + "server": "syslog.lora-aprs.info", + "port": 514 + }, "ntp_server": "pool.ntp.org" } diff --git a/lib/APRS-IS/APRS-IS.cpp b/lib/APRS-IS/APRS-IS.cpp index 6988ac9..7863bbf 100644 --- a/lib/APRS-IS/APRS-IS.cpp +++ b/lib/APRS-IS/APRS-IS.cpp @@ -8,34 +8,32 @@ void APRS_IS::setup(const String &user, const String &passcode, const String &to _version = version; } -bool APRS_IS::connect(const String &server, const int port) { +APRS_IS::ConnectionStatus 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) { +APRS_IS::ConnectionStatus 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) { +APRS_IS::ConnectionStatus APRS_IS::_connect(const String &server, const int port, const String &login_line) { if (!_client.connect(server.c_str(), port)) { - logPrintlnE("Something went wrong on connecting! Is the server reachable?"); - return false; + return ERROR_CONNECTION; } sendMessage(login_line); while (true) { String line = _client.readStringUntil('\n'); if (line.indexOf("logresp") != -1) { if (line.indexOf("unverified") == -1) { - return true; + return SUCCESS; } else { - logPrintlnE("User can not be verified with passcode!"); - return false; + return ERROR_PASSCODE; } } } - return true; + return SUCCESS; } bool APRS_IS::connected() { @@ -76,7 +74,6 @@ std::shared_ptr APRS_IS::getAPRSMessage() { line = _client.readStringUntil('\n'); } if (line.startsWith("#")) { - logPrintlnD(line); return 0; } if (line.length() == 0) { diff --git a/lib/APRS-IS/APRS-IS.h b/lib/APRS-IS/APRS-IS.h index 00d27ca..0aa0691 100644 --- a/lib/APRS-IS/APRS-IS.h +++ b/lib/APRS-IS/APRS-IS.h @@ -9,9 +9,16 @@ class APRS_IS { public: void setup(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 connected(); + enum ConnectionStatus + { + SUCCESS, + ERROR_CONNECTION, + ERROR_PASSCODE, + }; + + ConnectionStatus connect(const String &server, const int port); + ConnectionStatus connect(const String &server, const int port, const String &filter); + bool connected(); bool sendMessage(const String &message); bool sendMessage(const std::shared_ptr message); @@ -28,7 +35,7 @@ private: String _version; WiFiClient _client; - bool _connect(const String &server, const int port, const String &login_line); + ConnectionStatus _connect(const String &server, const int port, const String &login_line); }; #endif diff --git a/lib/BoardFinder/BoardFinder.cpp b/lib/BoardFinder/BoardFinder.cpp index 0e076e0..833008e 100644 --- a/lib/BoardFinder/BoardFinder.cpp +++ b/lib/BoardFinder/BoardFinder.cpp @@ -2,19 +2,21 @@ #include #include -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) { +#define MODULE_NAME "BoardFinder" + +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, uint8_t gpsrx, uint8_t gpstx, uint8_t button, 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), GpsRx(gpsrx), GpsTx(gpstx), Button(button), needCheckPowerChip(needcheckpowerchip), powerCheckStatus(powercheckstatus) { } BoardFinder::BoardFinder(const std::list &boardConfigs) : _boardConfigs(boardConfigs) { } -BoardConfig const *BoardFinder::searchBoardConfig() { - logPrintlnI("looking for a board config."); - logPrintlnI("searching for OLED..."); +BoardConfig const *BoardFinder::searchBoardConfig(logging::Logger &logger) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "looking for a board config."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "searching for OLED..."); for (BoardConfig const *boardconf : _boardConfigs) { - if (boardconf->needCheckPowerChip && checkPowerConfig(boardconf) == boardconf->powerCheckStatus) { + if (boardconf->needCheckPowerChip && checkPowerConfig(boardconf, logger) == boardconf->powerCheckStatus) { PowerManagement powerManagement; Wire.begin(boardconf->OledSda, boardconf->OledScl); powerManagement.begin(Wire); @@ -22,30 +24,28 @@ BoardConfig const *BoardFinder::searchBoardConfig() { } else if (boardconf->needCheckPowerChip) { continue; } - if (checkOledConfig(boardconf)) { - logPrintI("found a board config: "); - logPrintlnI(boardconf->Name); + if (checkOledConfig(boardconf, logger)) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "found a board config: %s", boardconf->Name.c_str()); return boardconf; } } - logPrintlnI("could not find OLED, will search for the modem now..."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "could not find OLED, will search for the modem now..."); for (BoardConfig const *boardconf : _boardConfigs) { - if (boardconf->needCheckPowerChip && checkPowerConfig(boardconf) == boardconf->powerCheckStatus) { + if (boardconf->needCheckPowerChip && checkPowerConfig(boardconf, logger) == boardconf->powerCheckStatus) { PowerManagement powerManagement; Wire.begin(boardconf->OledSda, boardconf->OledScl); powerManagement.begin(Wire); powerManagement.activateLoRa(); } if (checkModemConfig(boardconf)) { - logPrintI("found a board config: "); - logPrintlnI(boardconf->Name); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "found a board config: %s", boardconf->Name.c_str()); return boardconf; } } - logPrintlnE("could not find a board config!"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "could not find a board config!"); return 0; } @@ -60,7 +60,7 @@ BoardConfig const *BoardFinder::getBoardConfig(String name) { return *elem; } -bool BoardFinder::checkOledConfig(BoardConfig const *boardConfig) { +bool BoardFinder::checkOledConfig(BoardConfig const *boardConfig, logging::Logger &logger) { if (boardConfig->OledReset > 0) { pinMode(boardConfig->OledReset, OUTPUT); digitalWrite(boardConfig->OledReset, HIGH); @@ -70,7 +70,7 @@ bool BoardFinder::checkOledConfig(BoardConfig const *boardConfig) { digitalWrite(boardConfig->OledReset, HIGH); } if (!Wire.begin(boardConfig->OledSda, boardConfig->OledScl)) { - logPrintlnW("issue with wire"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, MODULE_NAME, "issue with wire"); return false; } Wire.beginTransmission(boardConfig->OledAddr); @@ -107,9 +107,9 @@ bool BoardFinder::checkModemConfig(BoardConfig const *boardConfig) { return false; } -bool BoardFinder::checkPowerConfig(BoardConfig const *boardConfig) { +bool BoardFinder::checkPowerConfig(BoardConfig const *boardConfig, logging::Logger &logger) { if (!Wire.begin(boardConfig->OledSda, boardConfig->OledScl)) { - logPrintlnW("issue with wire"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, MODULE_NAME, "issue with wire"); return false; } Wire.beginTransmission(0x34); @@ -120,22 +120,22 @@ bool BoardFinder::checkPowerConfig(BoardConfig const *boardConfig) { int response = Wire.read(); Wire.endTransmission(); - logPrintlnD(String(response)); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, MODULE_NAME, "wire response: %d", response); if (response == 0x03) { - logPrintlnD("power chip found!"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, MODULE_NAME, "power chip found!"); return true; } - logPrintlnD("power chip NOT found"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, MODULE_NAME, "power chip NOT found"); return false; } // clang-format off -BoardConfig TTGO_LORA32_V1 ("TTGO_LORA32_V1", eTTGO_LORA32_V1, 4, 15, 0x3C, 0, 5, 19, 27, 18, 14, 26); -BoardConfig TTGO_LORA32_V2 ("TTGO_LORA32_V2", eTTGO_LORA32_V2, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true); -BoardConfig TTGO_T_Beam_V0_7 ("TTGO_T_Beam_V0_7", eTTGO_T_Beam_V0_7, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true); -BoardConfig TTGO_T_Beam_V1_0 ("TTGO_T_Beam_V1_0", eTTGO_T_Beam_V1_0, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, true, true); -BoardConfig ETH_BOARD ("ETH_BOARD", eETH_BOARD, 33, 32, 0x3C, 0, 14, 2, 15, 12, 4, 36); -BoardConfig TRACKERD ("TRACKERD", eTRACKERD, 5, 4, 0x3C, 0, 18, 19, 23, 16, 14, 26); -BoardConfig HELTEC_WIFI_LORA_32_V1("HELTEC_WIFI_LORA_32_V1", eHELTEC_WIFI_LORA_32_V1, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26); -BoardConfig HELTEC_WIFI_LORA_32_V2("HELTEC_WIFI_LORA_32_V2", eHELTEC_WIFI_LORA_32_V2, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26); +BoardConfig TTGO_LORA32_V1 ("TTGO_LORA32_V1", eTTGO_LORA32_V1, 4, 15, 0x3C, 0, 5, 19, 27, 18, 14, 26, 0, 0, 0); +BoardConfig TTGO_LORA32_V2 ("TTGO_LORA32_V2", eTTGO_LORA32_V2, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, 0, 0, 0, true); +BoardConfig TTGO_T_Beam_V0_7 ("TTGO_T_Beam_V0_7", eTTGO_T_Beam_V0_7, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, 15, 12, 38, true); +BoardConfig TTGO_T_Beam_V1_0 ("TTGO_T_Beam_V1_0", eTTGO_T_Beam_V1_0, 21, 22, 0x3C, 0, 5, 19, 27, 18, 14, 26, 12, 34, 38, true, true); +BoardConfig ETH_BOARD ("ETH_BOARD", eETH_BOARD, 33, 32, 0x3C, 0, 14, 2, 15, 12, 4, 36, 0, 0, 0); +BoardConfig TRACKERD ("TRACKERD", eTRACKERD, 5, 4, 0x3C, 0, 18, 19, 23, 16, 14, 26, 0, 0, 0); +BoardConfig HELTEC_WIFI_LORA_32_V1("HELTEC_WIFI_LORA_32_V1", eHELTEC_WIFI_LORA_32_V1, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26, 0, 0, 0); +BoardConfig HELTEC_WIFI_LORA_32_V2("HELTEC_WIFI_LORA_32_V2", eHELTEC_WIFI_LORA_32_V2, 4, 15, 0x3C, 16, 5, 19, 27, 18, 14, 26, 0, 0, 0); // clang-format on diff --git a/lib/BoardFinder/BoardFinder.h b/lib/BoardFinder/BoardFinder.h index 142de6d..73b5ff9 100644 --- a/lib/BoardFinder/BoardFinder.h +++ b/lib/BoardFinder/BoardFinder.h @@ -8,6 +8,8 @@ #include #include +#include + enum BoardType { eHELTEC_WIFI_LORA_32_V1, @@ -22,7 +24,7 @@ enum BoardType class BoardConfig { public: - explicit 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); + explicit 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, uint8_t gpsrx, uint8_t gpstx, uint8_t button, bool needcheckpowerchip = false, bool powercheckstatus = false); String Name; BoardType Type; @@ -38,6 +40,9 @@ public: uint8_t LoraCS; uint8_t LoraReset; uint8_t LoraIRQ; + uint8_t GpsRx; + uint8_t GpsTx; + uint8_t Button; bool needCheckPowerChip; bool powerCheckStatus; @@ -47,16 +52,16 @@ class BoardFinder { public: explicit BoardFinder(const std::list &boardConfigs); - BoardConfig const *searchBoardConfig(); + BoardConfig const *searchBoardConfig(logging::Logger &logger); BoardConfig const *getBoardConfig(String name); private: const std::list &_boardConfigs; - bool checkOledConfig(BoardConfig const *boardConfig); + bool checkOledConfig(BoardConfig const *boardConfig, logging::Logger &logger); bool checkModemConfig(BoardConfig const *boardConfig); - bool checkPowerConfig(BoardConfig const *boardConfig); + bool checkPowerConfig(BoardConfig const *boardConfig, logging::Logger &logger); }; extern BoardConfig TTGO_LORA32_V1; diff --git a/lib/ConfigurationManagement/configuration.cpp b/lib/ConfigurationManagement/configuration.cpp index 5f50550..451fee4 100644 --- a/lib/ConfigurationManagement/configuration.cpp +++ b/lib/ConfigurationManagement/configuration.cpp @@ -2,12 +2,14 @@ #include #include -ConfigurationManagement::ConfigurationManagement(String FilePath) : mFilePath(FilePath) { +#define MODULE_NAME "ConfigurationManagement" + +ConfigurationManagement::ConfigurationManagement(logging::Logger &logger, String FilePath) : mFilePath(FilePath) { if (!SPIFFS.begin(true)) { - logPrintlnI("Mounting SPIFFS was not possible. Trying to format SPIFFS..."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "Mounting SPIFFS was not possible. Trying to format SPIFFS..."); SPIFFS.format(); if (!SPIFFS.begin()) { - logPrintlnE("Formating SPIFFS was not okay!"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "Formating SPIFFS was not okay!"); } } } @@ -15,16 +17,16 @@ ConfigurationManagement::ConfigurationManagement(String FilePath) : mFilePath(Fi ConfigurationManagement::~ConfigurationManagement() { } -void ConfigurationManagement::readConfiguration(Configuration &conf) { +void ConfigurationManagement::readConfiguration(logging::Logger &logger, Configuration &conf) { File file = SPIFFS.open(mFilePath); if (!file) { - logPrintlnE("Failed to open file for reading, using default configuration."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "Failed to open file for reading, using default configuration."); return; } DynamicJsonDocument data(2048); DeserializationError error = deserializeJson(data, file); if (error) { - logPrintlnW("Failed to read file, using default configuration."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, MODULE_NAME, "Failed to read file, using default configuration."); } // serializeJson(data, Serial); // Serial.println(); @@ -33,13 +35,13 @@ void ConfigurationManagement::readConfiguration(Configuration &conf) { readProjectConfiguration(data, conf); // update config in memory to get the new fields: - writeConfiguration(conf); + // writeConfiguration(logger, conf); } -void ConfigurationManagement::writeConfiguration(Configuration &conf) { +void ConfigurationManagement::writeConfiguration(logging::Logger &logger, Configuration &conf) { File file = SPIFFS.open(mFilePath, "w"); if (!file) { - logPrintlnE("Failed to open file for writing..."); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "Failed to open file for writing..."); return; } DynamicJsonDocument data(2048); diff --git a/lib/ConfigurationManagement/configuration.h b/lib/ConfigurationManagement/configuration.h index 6ebcf75..4d988cb 100644 --- a/lib/ConfigurationManagement/configuration.h +++ b/lib/ConfigurationManagement/configuration.h @@ -9,15 +9,17 @@ #include #endif +#include + class Configuration; class ConfigurationManagement { public: - explicit ConfigurationManagement(String FilePath); + explicit ConfigurationManagement(logging::Logger &logger, String FilePath); virtual ~ConfigurationManagement(); - void readConfiguration(Configuration &conf); - void writeConfiguration(Configuration &conf); + void readConfiguration(logging::Logger &logger, Configuration &conf); + void writeConfiguration(logging::Logger &logger, Configuration &conf); private: virtual void readProjectConfiguration(DynamicJsonDocument &data, Configuration &conf) = 0; diff --git a/lib/LoRa/LoRa.cpp b/lib/LoRa/LoRa.cpp deleted file mode 100644 index 59142ec..0000000 --- a/lib/LoRa/LoRa.cpp +++ /dev/null @@ -1,673 +0,0 @@ -// 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/LoRa.h b/lib/LoRa/LoRa.h deleted file mode 100644 index 51e089a..0000000 --- a/lib/LoRa/LoRa.h +++ /dev/null @@ -1,119 +0,0 @@ -// 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 deleted file mode 100644 index ea28f8b..0000000 --- a/lib/LoRa_APRS/LoRa_APRS.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "LoRa_APRS.h" - -LoRa_APRS::LoRa_APRS() : _RxFrequency(433775000), _TxFrequency(433775000) { -} - -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); - return true; -} - -std::shared_ptr LoRa_APRS::getMessage() { - return _LastReceivedMsg; -} - -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); -} - -// cppcheck-suppress unusedFunction -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; -} diff --git a/lib/LoRa_APRS/LoRa_APRS.h b/lib/LoRa_APRS/LoRa_APRS.h deleted file mode 100644 index cacd0fe..0000000 --- a/lib/LoRa_APRS/LoRa_APRS.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LORA_H_ -#define LORA_H_ - -#include - -#include -#include -#include - -class LoRa_APRS : public LoRaClass { -public: - LoRa_APRS(); - - 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; - -private: - std::shared_ptr _LastReceivedMsg; - long _RxFrequency; - long _TxFrequency; -}; - -#endif diff --git a/lib/NTPClient/NTPClient.h b/lib/NTPClient/NTPClient.h index b528023..1c6c9c2 100644 --- a/lib/NTPClient/NTPClient.h +++ b/lib/NTPClient/NTPClient.h @@ -17,7 +17,7 @@ class NTPClient { unsigned int _port = NTP_DEFAULT_LOCAL_PORT; long _timeOffset = 0; - unsigned long _updateInterval = 60000; // In ms + unsigned long _updateInterval = 3600000; // In ms unsigned long _currentEpoc = 0; // In s unsigned long _lastUpdate = 0; // In ms diff --git a/lib/System/System.cpp b/lib/System/System.cpp index e074745..008e3a4 100644 --- a/lib/System/System.cpp +++ b/lib/System/System.cpp @@ -1,7 +1,7 @@ #include "System.h" -System::System() : _boardConfig(0), _userConfig(0), _isWifiEthConnected(false) { +System::System() : _boardConfig(0), _userConfig(0), _isEthConnected(false), _isWifiConnected(false) { } System::~System() { @@ -31,10 +31,18 @@ Display &System::getDisplay() { return _display; } -bool System::isWifiEthConnected() const { - return _isWifiEthConnected; +bool System::isWifiOrEthConnected() const { + return _isEthConnected || _isWifiConnected; } -void System::connectedViaWifiEth(bool status) { - _isWifiEthConnected = status; +void System::connectedViaEth(bool status) { + _isEthConnected = status; +} + +void System::connectedViaWifi(bool status) { + _isWifiConnected = status; +} + +logging::Logger &System::getLogger() { + return _logger; } diff --git a/lib/System/System.h b/lib/System/System.h index 376741b..a6a0d97 100644 --- a/lib/System/System.h +++ b/lib/System/System.h @@ -1,6 +1,7 @@ #ifndef SYSTEM_H_ #define SYSTEM_H_ +#include #include #include "TaskManager.h" @@ -20,15 +21,19 @@ public: Configuration const *const getUserConfig() const; TaskManager & getTaskManager(); Display & getDisplay(); - bool isWifiEthConnected() const; - void connectedViaWifiEth(bool status); + bool isWifiOrEthConnected() const; + void connectedViaEth(bool status); + void connectedViaWifi(bool status); + logging::Logger & getLogger(); private: BoardConfig const * _boardConfig; Configuration const *_userConfig; TaskManager _taskManager; Display _display; - bool _isWifiEthConnected; + bool _isEthConnected; + bool _isWifiConnected; + logging::Logger _logger; }; #endif diff --git a/lib/System/TaskManager.cpp b/lib/System/TaskManager.cpp index d524ad0..b643c1e 100644 --- a/lib/System/TaskManager.cpp +++ b/lib/System/TaskManager.cpp @@ -2,6 +2,8 @@ #include #include +#define MODULE_NAME "TaskManager" + TaskManager::TaskManager() { } @@ -20,15 +22,13 @@ std::list TaskManager::getTasks() { } bool TaskManager::setup(System &system) { - logPrintlnV("will setup all tasks..."); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "will setup all tasks..."); for (Task *elem : _alwaysRunTasks) { - logPrintD("call setup from "); - logPrintlnD(elem->getName()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, MODULE_NAME, "call setup for %s", elem->getName().c_str()); elem->setup(system); } for (Task *elem : _tasks) { - logPrintD("call setup from "); - logPrintlnD(elem->getName()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, MODULE_NAME, "call setup for %s", elem->getName().c_str()); elem->setup(system); } _nextTask = _tasks.begin(); @@ -36,10 +36,7 @@ bool TaskManager::setup(System &system) { } bool TaskManager::loop(System &system) { - // logPrintlnD("will loop all tasks..."); for (Task *elem : _alwaysRunTasks) { - // logPrintD("call loop from "); - // logPrintlnD(elem->getName()); elem->loop(system); } diff --git a/platformio.ini b/platformio.ini index c955992..7c1aa1c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,8 +11,12 @@ lib_deps = bblanchon/ArduinoJson @ 6.17.0 lewisxhe/AXP202X_Library @ 1.1.2 peterus/APRS-Decoder-Lib @ 0.0.6 - peterus/esp-logger @ 0.0.1 + peterus/esp-logger @ 1.0.0 peterus/ESP-FTP-Server-Lib @ 0.9.5 + knolleary/PubSubClient@^2.8 + mikalhart/TinyGPSPlus @ 1.0.2 + shaggydog/OneButton @ 1.5.0 + jgromes/RadioLib @ 5.1.2 check_tool = cppcheck check_flags = cppcheck: --suppress=*:*.pio\* --inline-suppr -DCPPCHECK --force lib -ilib/TimeLib -ilib/LoRa -ilib/NTPClient diff --git a/scripts/check_version.py b/scripts/check_version.py new file mode 100755 index 0000000..57f87ea --- /dev/null +++ b/scripts/check_version.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import git +from datetime import date + +today = date.today() + +current_year = int(str(today.isocalendar()[0])[2:]) +current_week = int(today.isocalendar()[1]) + +version = None +with open("src/LoRa_APRS_iGate.cpp") as f: + for line in f: + if line.startswith("#define VERSION"): + version = line.strip().split(" ")[-1].replace('"', "") + +version_split = version.split(".") +version_year = int(version_split[0]) +version_week = int(version_split[1]) +version_vers = int(version_split[2]) + +print(f"[INFO] firmware version year: {version_year}") +print(f"[INFO] firmware version week: {version_week}") +print(f"[INFO] firmware version version: {version_vers}") +print(f"[INFO] -> {version}") + +print(f"[INFO] current year: {current_year}") +print(f"[INFO] current week: {current_week}") +print(f"[INFO] -> {current_year}.{current_week}.x") + +error = False +if version_year != current_year: + print("[ERROR] firmware version is not current year!") + error = True + +if version_week != current_week: + print("[ERROR] firmware version is not current week!") + error = True + +repo = git.Repo('.') +print(f"[INFO] found {len(repo.tags)} tags in repo") +if f"v{version}" in repo.tags: + print("[ERROR] tag with this version is already existing") + error = True + +if error: + print("[ERROR] check/update VERSION define in src/LoRa_APRS_iGate.cpp to fix this issue") + +exit(error) diff --git a/scripts/create_version_tag.py b/scripts/create_version_tag.py new file mode 100755 index 0000000..5b37c82 --- /dev/null +++ b/scripts/create_version_tag.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from datetime import date + +today = date.today() + +current_year = int(str(today.isocalendar()[0])[2:]) +current_week = int(today.isocalendar()[1]) + +version = None +with open("src/LoRa_APRS_iGate.cpp") as f: + for line in f: + if line.startswith("#define VERSION"): + version = line.strip().split(" ")[-1].replace('"', "") + +version_split = version.split(".") +version_year = int(version_split[0]) +version_week = int(version_split[1]) +version_vers = int(version_split[2]) + +print(f"v{version_year}.{version_week}.{version_vers}") diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp index 05739dd..da05b99 100644 --- a/src/LoRa_APRS_iGate.cpp +++ b/src/LoRa_APRS_iGate.cpp @@ -8,17 +8,20 @@ #include #include "TaskAprsIs.h" +#include "TaskBeacon.h" #include "TaskDisplay.h" #include "TaskEth.h" #include "TaskFTP.h" -#include "TaskModem.h" +#include "TaskMQTT.h" #include "TaskNTP.h" #include "TaskOTA.h" +#include "TaskRadiolib.h" #include "TaskRouter.h" #include "TaskWifi.h" #include "project_configuration.h" -#define VERSION "21.35.0-dev" +#define VERSION "22.20.0" +#define MODULE_NAME "Main" String create_lat_aprs(double lat); String create_long_aprs(double lng); @@ -26,26 +29,31 @@ String create_long_aprs(double lng); TaskQueue> toAprsIs; TaskQueue> fromModem; TaskQueue> toModem; +TaskQueue> toMQTT; System LoRaSystem; Configuration userConfig; DisplayTask displayTask; -ModemTask modemTask(fromModem, toModem); -EthTask ethTask; -WifiTask wifiTask; -OTATask otaTask; -NTPTask ntpTask; -FTPTask ftpTask; -AprsIsTask aprsIsTask(toAprsIs); -RouterTask routerTask(fromModem, toModem, toAprsIs); +// ModemTask modemTask(fromModem, toModem); +RadiolibTask modemTask(fromModem, toModem); +EthTask ethTask; +WifiTask wifiTask; +OTATask otaTask; +NTPTask ntpTask; +FTPTask ftpTask; +MQTTTask mqttTask(toMQTT); +AprsIsTask aprsIsTask(toAprsIs); +RouterTask routerTask(fromModem, toModem, toAprsIs, toMQTT); +BeaconTask beaconTask(toModem, toAprsIs); void setup() { Serial.begin(115200); - Logger::instance().setSerial(&Serial); + LoRaSystem.getLogger().setSerial(&Serial); + setWiFiLogger(&LoRaSystem.getLogger()); delay(500); - logPrintlnI("LoRa APRS iGate by OE5BPA (Peter Buchegger)"); - logPrintlnI("Version: " VERSION); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "LoRa APRS iGate by OE5BPA (Peter Buchegger)"); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "Version: %s", VERSION); std::list boardConfigs; boardConfigs.push_back(&TTGO_LORA32_V1); @@ -57,40 +65,42 @@ void setup() { boardConfigs.push_back(&HELTEC_WIFI_LORA_32_V1); boardConfigs.push_back(&HELTEC_WIFI_LORA_32_V2); - ProjectConfigurationManagement confmg; - confmg.readConfiguration(userConfig); + ProjectConfigurationManagement confmg(LoRaSystem.getLogger()); + confmg.readConfiguration(LoRaSystem.getLogger(), userConfig); BoardFinder finder(boardConfigs); BoardConfig const *boardConfig = finder.getBoardConfig(userConfig.board); if (!boardConfig) { - boardConfig = finder.searchBoardConfig(); + boardConfig = finder.searchBoardConfig(LoRaSystem.getLogger()); if (!boardConfig) { - logPrintlnE("Board config not set and search failed!"); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "Board config not set and search failed!"); while (true) ; } else { userConfig.board = boardConfig->Name; - confmg.writeConfiguration(userConfig); - logPrintlnI("will restart board now!"); + confmg.writeConfiguration(LoRaSystem.getLogger(), userConfig); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "will restart board now!"); ESP.restart(); } } - logPrintI("Board "); - logPrintI(boardConfig->Name); - logPrintlnI(" loaded."); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "Board %s loaded.", boardConfig->Name.c_str()); if (boardConfig->Type == eTTGO_T_Beam_V1_0) { Wire.begin(boardConfig->OledSda, boardConfig->OledScl); PowerManagement powerManagement; if (!powerManagement.begin(Wire)) { - logPrintlnI("AXP192 init done!"); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "AXP192 init done!"); } else { - logPrintlnE("AXP192 init failed!"); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "AXP192 init failed!"); } powerManagement.activateLoRa(); powerManagement.activateOLED(); - powerManagement.deactivateGPS(); + if (userConfig.beacon.use_gps) { + powerManagement.activateGPS(); + } else { + powerManagement.deactivateGPS(); + } } LoRaSystem.setBoardConfig(boardConfig); @@ -98,19 +108,33 @@ void setup() { LoRaSystem.getTaskManager().addTask(&displayTask); LoRaSystem.getTaskManager().addTask(&modemTask); LoRaSystem.getTaskManager().addTask(&routerTask); + LoRaSystem.getTaskManager().addTask(&beaconTask); - if (userConfig.aprs_is.active) { - if (boardConfig->Type == eETH_BOARD) { - LoRaSystem.getTaskManager().addAlwaysRunTask(ðTask); - } else { - LoRaSystem.getTaskManager().addAlwaysRunTask(&wifiTask); - } + bool tcpip = false; + + if (userConfig.wifi.active) { + LoRaSystem.getTaskManager().addAlwaysRunTask(&wifiTask); + tcpip = true; + } + if (boardConfig->Type == eETH_BOARD) { + LoRaSystem.getTaskManager().addAlwaysRunTask(ðTask); + tcpip = true; + } + + if (tcpip) { LoRaSystem.getTaskManager().addTask(&otaTask); LoRaSystem.getTaskManager().addTask(&ntpTask); if (userConfig.ftp.active) { LoRaSystem.getTaskManager().addTask(&ftpTask); } - LoRaSystem.getTaskManager().addTask(&aprsIsTask); + + if (userConfig.aprs_is.active) { + LoRaSystem.getTaskManager().addTask(&aprsIsTask); + } + + if (userConfig.mqtt.active) { + LoRaSystem.getTaskManager().addTask(&mqttTask); + } } LoRaSystem.getTaskManager().setup(LoRaSystem); @@ -118,13 +142,13 @@ void setup() { LoRaSystem.getDisplay().showSpashScreen("LoRa APRS iGate", VERSION); if (userConfig.callsign == "NOCALL-10") { - logPrintlnE("You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "You have to change your settings in 'data/is-cfg.json' and upload it via 'Upload File System image'!"); LoRaSystem.getDisplay().showStatusScreen("ERROR", "You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); while (true) ; } if ((!userConfig.aprs_is.active) && !(userConfig.digi.active)) { - logPrintlnE("No mode selected (iGate or Digi)! You have to activate one of iGate or Digi."); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, MODULE_NAME, "No mode selected (iGate or Digi)! You have to activate one of iGate or Digi."); LoRaSystem.getDisplay().showStatusScreen("ERROR", "No mode selected (iGate or Digi)! You have to activate one of iGate or Digi."); while (true) ; @@ -136,33 +160,16 @@ void setup() { } delay(5000); - logPrintlnI("setup done..."); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "setup done..."); } +volatile bool syslogSet = false; + void loop() { LoRaSystem.getTaskManager().loop(LoRaSystem); -} - -String create_lat_aprs(double lat) { - char str[20]; - char n_s = 'N'; - if (lat < 0) { - n_s = 'S'; + if (LoRaSystem.isWifiOrEthConnected() && LoRaSystem.getUserConfig()->syslog.active && !syslogSet) { + LoRaSystem.getLogger().setSyslogServer(LoRaSystem.getUserConfig()->syslog.server, LoRaSystem.getUserConfig()->syslog.port, LoRaSystem.getUserConfig()->callsign); + LoRaSystem.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, MODULE_NAME, "System connected after a restart to the network, syslog server set"); + syslogSet = true; } - lat = std::abs(lat); - sprintf(str, "%02d%05.2f%c", (int)lat, (lat - (double)((int)lat)) * 60.0, n_s); - String lat_str(str); - return lat_str; -} - -String create_long_aprs(double lng) { - char str[20]; - char e_w = 'E'; - if (lng < 0) { - e_w = 'W'; - } - lng = std::abs(lng); - sprintf(str, "%03d%05.2f%c", (int)lng, (lng - (double)((int)lng)) * 60.0, e_w); - String lng_str(str); - return lng_str; } diff --git a/src/Task.h b/src/Task.h index 4c8163b..a3b3fb9 100644 --- a/src/Task.h +++ b/src/Task.h @@ -7,20 +7,26 @@ enum TaskNames TaskEth, TaskFtp, TaskModem, + TaskRadiolib, TaskNtp, TaskOta, TaskWifi, TaskRouter, - TaskSize, + TaskMQTT, + TaskBeacon, + TaskSize }; -#define TASK_APRS_IS "AprsIsTask" -#define TASK_ETH "EthTask" -#define TASK_FTP "FTPTask" -#define TASK_MODEM "ModemTask" -#define TASK_NTP "NTPTask" -#define TASK_OTA "OTATask" -#define TASK_WIFI "WifiTask" -#define TASK_ROUTER "RouterTask" +#define TASK_APRS_IS "AprsIsTask" +#define TASK_ETH "EthTask" +#define TASK_FTP "FTPTask" +#define TASK_MODEM "ModemTask" +#define TASK_RADIOLIB "RadiolibTask" +#define TASK_NTP "NTPTask" +#define TASK_OTA "OTATask" +#define TASK_WIFI "WifiTask" +#define TASK_ROUTER "RouterTask" +#define TASK_MQTT "MQTTTask" +#define TASK_BEACON "BeaconTask" #endif diff --git a/src/TaskAprsIs.cpp b/src/TaskAprsIs.cpp index b96b06e..3fa3da6 100644 --- a/src/TaskAprsIs.cpp +++ b/src/TaskAprsIs.cpp @@ -16,7 +16,7 @@ bool AprsIsTask::setup(System &system) { } bool AprsIsTask::loop(System &system) { - if (!system.isWifiEthConnected()) { + if (!system.isWifiOrEthConnected()) { return false; } if (!_aprs_is.connected()) { @@ -40,15 +40,18 @@ bool AprsIsTask::loop(System &system) { return true; } -bool AprsIsTask::connect(const System &system) { - logPrintI("connecting to APRS-IS server: "); - logPrintI(system.getUserConfig()->aprs_is.server); - logPrintI(" on port: "); - logPrintlnI(String(system.getUserConfig()->aprs_is.port)); - if (!_aprs_is.connect(system.getUserConfig()->aprs_is.server, system.getUserConfig()->aprs_is.port)) { - logPrintlnE("Connection failed."); +bool AprsIsTask::connect(System &system) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "connecting to APRS-IS server: %s on port: %d", system.getUserConfig()->aprs_is.server.c_str(), system.getUserConfig()->aprs_is.port); + APRS_IS::ConnectionStatus status = _aprs_is.connect(system.getUserConfig()->aprs_is.server, system.getUserConfig()->aprs_is.port); + if (status == APRS_IS::ERROR_CONNECTION) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "Something went wrong on connecting! Is the server reachable?"); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "Connection failed."); + return false; + } else if (status == APRS_IS::ERROR_PASSCODE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "User can not be verified with passcode!"); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "Connection failed."); return false; } - logPrintlnI("Connected to APRS-IS server!"); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Connected to APRS-IS server!"); return true; } diff --git a/src/TaskAprsIs.h b/src/TaskAprsIs.h index de75fec..30679db 100644 --- a/src/TaskAprsIs.h +++ b/src/TaskAprsIs.h @@ -19,7 +19,7 @@ private: TaskQueue> &_toAprsIs; - bool connect(const System &system); + bool connect(System &system); }; #endif diff --git a/src/TaskBeacon.cpp b/src/TaskBeacon.cpp new file mode 100644 index 0000000..e356920 --- /dev/null +++ b/src/TaskBeacon.cpp @@ -0,0 +1,126 @@ +#include + +#include +#include + +#include "Task.h" +#include "TaskBeacon.h" +#include "project_configuration.h" + +BeaconTask::BeaconTask(TaskQueue> &toModem, TaskQueue> &toAprsIs) : Task(TASK_BEACON, TaskBeacon), _toModem(toModem), _toAprsIs(toAprsIs), _ss(1), _useGps(false) { +} + +BeaconTask::~BeaconTask() { +} + +OneButton BeaconTask::_userButton; +bool BeaconTask::_send_update; +uint BeaconTask::_instances; + +void BeaconTask::pushButton() { + _send_update = true; +} + +bool BeaconTask::setup(System &system) { + if (_instances++ == 0 && system.getBoardConfig()->Button > 0) { + _userButton = OneButton(system.getBoardConfig()->Button, true, true); + _userButton.attachClick(pushButton); + _send_update = false; + } + + _useGps = system.getUserConfig()->beacon.use_gps; + + if (_useGps) { + if (system.getBoardConfig()->GpsRx != 0) { + _ss.begin(9600, SERIAL_8N1, system.getBoardConfig()->GpsTx, system.getBoardConfig()->GpsRx); + } else { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "NO GPS found."); + _useGps = false; + } + } + // setup beacon + _beacon_timer.setTimeout(system.getUserConfig()->beacon.timeout * 60 * 1000); + + _beaconMsg = std::shared_ptr(new APRSMessage()); + _beaconMsg->setSource(system.getUserConfig()->callsign); + _beaconMsg->setDestination("APLG01"); + + return true; +} + +bool BeaconTask::loop(System &system) { + if (_useGps) { + while (_ss.available() > 0) { + char c = _ss.read(); + _gps.encode(c); + } + } + + _userButton.tick(); + + // check for beacon + if (_beacon_timer.check() || _send_update) { + if (sendBeacon(system)) { + _send_update = false; + _beacon_timer.start(); + } + } + + uint32_t diff = _beacon_timer.getTriggerTimeInSec(); + _stateInfo = "beacon " + String(uint32_t(diff / 600)) + String(uint32_t(diff / 60) % 10) + ":" + String(uint32_t(diff / 10) % 6) + String(uint32_t(diff % 10)); + + return true; +} + +String create_lat_aprs(double lat) { + char str[20]; + char n_s = 'N'; + if (lat < 0) { + n_s = 'S'; + } + lat = std::abs(lat); + sprintf(str, "%02d%05.2f%c", (int)lat, (lat - (double)((int)lat)) * 60.0, n_s); + String lat_str(str); + return lat_str; +} + +String create_long_aprs(double lng) { + char str[20]; + char e_w = 'E'; + if (lng < 0) { + e_w = 'W'; + } + lng = std::abs(lng); + sprintf(str, "%03d%05.2f%c", (int)lng, (lng - (double)((int)lng)) * 60.0, e_w); + String lng_str(str); + return lng_str; +} + +bool BeaconTask::sendBeacon(System &system) { + double lat = system.getUserConfig()->beacon.positionLatitude; + double lng = system.getUserConfig()->beacon.positionLongitude; + + if (_useGps) { + if (_gps.location.isUpdated()) { + lat = _gps.location.lat(); + lng = _gps.location.lng(); + } else { + return false; + } + } + _beaconMsg->getBody()->setData(String("=") + create_lat_aprs(lat) + "L" + create_long_aprs(lng) + "&" + system.getUserConfig()->beacon.message); + + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "[%s] %s", timeString().c_str(), _beaconMsg->encode().c_str()); + + if (system.getUserConfig()->aprs_is.active) { + _toAprsIs.addElement(_beaconMsg); + } + + if (system.getUserConfig()->digi.beacon) { + _toModem.addElement(_beaconMsg); + } + + system.getDisplay().addFrame(std::shared_ptr(new TextFrame("BEACON", _beaconMsg->toString()))); + + return true; +} diff --git a/src/TaskBeacon.h b/src/TaskBeacon.h new file mode 100644 index 0000000..ba0ff22 --- /dev/null +++ b/src/TaskBeacon.h @@ -0,0 +1,37 @@ +#ifndef TASK_BEACON_H_ +#define TASK_BEACON_H_ + +#include +#include + +#include +#include +#include + +class BeaconTask : public Task { +public: + BeaconTask(TaskQueue> &toModem, TaskQueue> &toAprsIs); + virtual ~BeaconTask(); + + virtual bool setup(System &system) override; + virtual bool loop(System &system) override; + bool sendBeacon(System &system); + +private: + TaskQueue> &_toModem; + TaskQueue> &_toAprsIs; + + std::shared_ptr _beaconMsg; + Timer _beacon_timer; + + HardwareSerial _ss; + TinyGPSPlus _gps; + bool _useGps; + + static uint _instances; + static OneButton _userButton; + static bool _send_update; + static void pushButton(); +}; + +#endif diff --git a/src/TaskEth.cpp b/src/TaskEth.cpp index c25120e..59d4149 100644 --- a/src/TaskEth.cpp +++ b/src/TaskEth.cpp @@ -6,66 +6,62 @@ #include "TaskEth.h" #include "project_configuration.h" -volatile bool eth_connected = false; +#define WIFI_EVENT "WiFiEvent" + +volatile bool eth_connected = false; +logging::Logger *_logger; + +void setWiFiLogger(logging::Logger *logger) { + _logger = logger; +} void WiFiEvent(WiFiEvent_t event) { switch (event) { case SYSTEM_EVENT_STA_START: - logPrintlnI("WiFi Started"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Started"); break; case SYSTEM_EVENT_STA_CONNECTED: - logPrintlnI("WiFi Connected"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Connected"); break; case SYSTEM_EVENT_STA_GOT_IP: - logPrintI("WiFi MAC: "); - logPrintI(WiFi.macAddress()); - logPrintI(", IPv4: "); - logPrintI(WiFi.localIP().toString()); - logPrintI(", Gateway: "); - logPrintI(WiFi.gatewayIP().toString()); - logPrintI(", DNS1: "); - logPrintI(WiFi.dnsIP().toString()); - logPrintI(", DNS2: "); - logPrintlnI(WiFi.dnsIP(1).toString()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi MAC: %s", WiFi.macAddress().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi IPv4: %s", WiFi.localIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Gateway: %s", WiFi.gatewayIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi DNS1: %s", WiFi.dnsIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi DNS2: %s", WiFi.dnsIP(1).toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Hostname: %s", WiFi.getHostname()); break; case SYSTEM_EVENT_STA_DISCONNECTED: - logPrintlnW("WiFi Disconnected"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Disconnected"); break; case SYSTEM_EVENT_STA_STOP: - logPrintlnW("WiFi Stopped"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "WiFi Stopped"); break; case SYSTEM_EVENT_ETH_START: - logPrintlnI("ETH Started"); - ETH.setHostname("esp32-ethernet"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH Started"); break; case SYSTEM_EVENT_ETH_CONNECTED: - logPrintlnI("ETH Connected"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH Connected"); break; case SYSTEM_EVENT_ETH_GOT_IP: - logPrintI("ETH MAC: "); - logPrintI(ETH.macAddress()); - logPrintI(", IPv4: "); - logPrintI(ETH.localIP().toString()); - logPrintI(", Gateway: "); - logPrintI(ETH.gatewayIP().toString()); - logPrintI(", DNS1: "); - logPrintI(ETH.dnsIP().toString()); - logPrintI(", DNS2: "); - logPrintI(ETH.dnsIP(1).toString()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH MAC: %s", ETH.macAddress().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH IPv4: %s", ETH.localIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH Gateway: %s", ETH.gatewayIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH DNS1: %s", ETH.dnsIP().toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH DNS2: %s", ETH.dnsIP(1).toString().c_str()); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH Hostname: %s", ETH.getHostname()); if (ETH.fullDuplex()) { - logPrintI(", FULL_DUPLEX"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH FULL_DUPLEX"); } - logPrintI(", "); - logPrintI(String(ETH.linkSpeed())); - logPrintlnI("Mbps"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_INFO, WIFI_EVENT, "ETH Speed: %dMbps", ETH.linkSpeed()); eth_connected = true; break; case SYSTEM_EVENT_ETH_DISCONNECTED: - logPrintlnW("ETH Disconnected"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_WARN, WIFI_EVENT, "ETH Disconnected"); eth_connected = false; break; case SYSTEM_EVENT_ETH_STOP: - logPrintlnW("ETH Stopped"); + _logger->log(logging::LoggerLevel::LOGGER_LEVEL_WARN, WIFI_EVENT, "ETH Stopped"); eth_connected = false; break; default: @@ -100,22 +96,28 @@ bool EthTask::setup(System &system) { delay(200); digitalWrite(ETH_NRST, 1); + ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK); + if (!system.getUserConfig()->network.DHCP) { - ETH.config(system.getUserConfig()->network.staticIP, system.getUserConfig()->network.gateway, system.getUserConfig()->network.subnet, system.getUserConfig()->network.dns1, system.getUserConfig()->network.dns2); + ETH.config(system.getUserConfig()->network.static_.ip, system.getUserConfig()->network.static_.gateway, system.getUserConfig()->network.static_.subnet, system.getUserConfig()->network.static_.dns1, system.getUserConfig()->network.static_.dns2); + } + if (system.getUserConfig()->network.hostname.overwrite) { + ETH.setHostname(system.getUserConfig()->network.hostname.name.c_str()); + } else { + ETH.setHostname(system.getUserConfig()->callsign.c_str()); } - ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK); return true; } bool EthTask::loop(System &system) { if (!eth_connected) { - system.connectedViaWifiEth(false); + system.connectedViaEth(false); _stateInfo = "Ethernet not connected"; _state = Error; return false; } - system.connectedViaWifiEth(true); + system.connectedViaEth(true); _stateInfo = ETH.localIP().toString(); _state = Okay; return true; diff --git a/src/TaskEth.h b/src/TaskEth.h index 4a63d3f..50f1d63 100644 --- a/src/TaskEth.h +++ b/src/TaskEth.h @@ -3,6 +3,7 @@ #include +void setWiFiLogger(logging::Logger *logger); void WiFiEvent(WiFiEvent_t event); class EthTask : public Task { diff --git a/src/TaskFTP.cpp b/src/TaskFTP.cpp index d9615cf..86e0bef 100644 --- a/src/TaskFTP.cpp +++ b/src/TaskFTP.cpp @@ -14,8 +14,7 @@ FTPTask::~FTPTask() { bool FTPTask::setup(System &system) { for (Configuration::Ftp::User user : system.getUserConfig()->ftp.users) { - logPrintD("Adding user to FTP Server: "); - logPrintlnD(user.name); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "Adding user to FTP Server: %s", user.name.c_str()); _ftpServer.addUser(user.name, user.password); } _ftpServer.addFilesystem("SPIFFS", &SPIFFS); @@ -31,8 +30,7 @@ bool FTPTask::loop(System &system) { _ftpServer.handle(); static bool configWasOpen = false; if (configWasOpen && _ftpServer.countConnections() == 0) { - logPrintlnW("Maybe the config has been changed via FTP, lets restart now to get the new config..."); - logPrintlnW(""); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_WARN, getName(), "Maybe the config has been changed via FTP, lets restart now to get the new config..."); ESP.restart(); } if (_ftpServer.countConnections() > 0) { diff --git a/src/TaskMQTT.cpp b/src/TaskMQTT.cpp new file mode 100644 index 0000000..e065a1d --- /dev/null +++ b/src/TaskMQTT.cpp @@ -0,0 +1,64 @@ +#include + +#include "Task.h" +#include "TaskMQTT.h" +#include "project_configuration.h" + +#include + +MQTTTask::MQTTTask(TaskQueue> &toMQTT) : Task(TASK_MQTT, TaskMQTT), _toMQTT(toMQTT), _MQTT(_client) { +} + +MQTTTask::~MQTTTask() { +} + +bool MQTTTask::setup(System &system) { + _MQTT.setServer(system.getUserConfig()->mqtt.server.c_str(), system.getUserConfig()->mqtt.port); + return true; +} + +bool MQTTTask::loop(System &system) { + if (!system.isWifiOrEthConnected()) { + return false; + } + + if (!_MQTT.connected()) { + connect(system); + } + + if (!_toMQTT.empty()) { + std::shared_ptr msg = _toMQTT.getElement(); + + DynamicJsonDocument data(1024); + data["source"] = msg->getSource(); + data["destination"] = msg->getDestination(); + data["path"] = msg->getPath(); + data["type"] = msg->getType().toString(); + String body = msg->getBody()->encode(); + body.replace("\n", ""); + data["data"] = body; + + String r; + serializeJson(data, r); + + String topic = String(system.getUserConfig()->mqtt.topic); + if (!topic.endsWith("/")) { + topic = topic + "/"; + } + topic = topic + system.getUserConfig()->callsign; + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "Send MQTT with topic: '%s', data: %s", topic.c_str(), r.c_str()); + _MQTT.publish(topic.c_str(), r.c_str()); + } + _MQTT.loop(); + return true; +} + +bool MQTTTask::connect(System &system) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Connecting to MQTT broker: %s on port %d", system.getUserConfig()->mqtt.server.c_str(), system.getUserConfig()->mqtt.port); + if (_MQTT.connect(system.getUserConfig()->callsign.c_str(), system.getUserConfig()->mqtt.name.c_str(), system.getUserConfig()->mqtt.password.c_str())) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Connected to MQTT broker as: %s", system.getUserConfig()->callsign.c_str()); + return true; + } + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Connecting to MQTT broker failed. Try again later."); + return false; +} diff --git a/src/TaskMQTT.h b/src/TaskMQTT.h new file mode 100644 index 0000000..d388075 --- /dev/null +++ b/src/TaskMQTT.h @@ -0,0 +1,26 @@ +#ifndef TASK_MQTT_H_ +#define TASK_MQTT_H_ + +#include +#include +#include +#include + +class MQTTTask : public Task { +public: + MQTTTask(TaskQueue> &toMQTT); + virtual ~MQTTTask(); + + virtual bool setup(System &system) override; + virtual bool loop(System &system) override; + +private: + TaskQueue> &_toMQTT; + + WiFiClient _client; + PubSubClient _MQTT; + + bool connect(System &system); +}; + +#endif diff --git a/src/TaskModem.cpp b/src/TaskModem.cpp deleted file mode 100644 index 7dad353..0000000 --- a/src/TaskModem.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include - -#include - -#include "Task.h" -#include "TaskAprsIs.h" -#include "TaskModem.h" -#include "project_configuration.h" - -ModemTask::ModemTask(TaskQueue> &fromModem, TaskQueue> &toModem) : Task(TASK_MODEM, TaskModem), _lora_aprs(), _fromModem(fromModem), _toModem(toModem) { -} - -ModemTask::~ModemTask() { -} - -bool ModemTask::setup(System &system) { - SPI.begin(system.getBoardConfig()->LoraSck, system.getBoardConfig()->LoraMiso, system.getBoardConfig()->LoraMosi, system.getBoardConfig()->LoraCS); - _lora_aprs.setPins(system.getBoardConfig()->LoraCS, system.getBoardConfig()->LoraReset, system.getBoardConfig()->LoraIRQ); - if (!_lora_aprs.begin(system.getUserConfig()->lora.frequencyRx)) { - logPrintlnE("Starting LoRa failed!"); - _stateInfo = "LoRa-Modem failed"; - _state = Error; - while (true) - ; - } - _lora_aprs.setRxFrequency(system.getUserConfig()->lora.frequencyRx); - _lora_aprs.setTxFrequency(system.getUserConfig()->lora.frequencyTx); - _lora_aprs.setTxPower(system.getUserConfig()->lora.power); - _lora_aprs.setSpreadingFactor(system.getUserConfig()->lora.spreadingFactor); - _lora_aprs.setSignalBandwidth(system.getUserConfig()->lora.signalBandwidth); - _lora_aprs.setCodingRate4(system.getUserConfig()->lora.codingRate4); - _lora_aprs.enableCrc(); - - _stateInfo = ""; - return true; -} - -bool ModemTask::loop(System &system) { - if (_lora_aprs.checkMessage()) { - std::shared_ptr msg = _lora_aprs.getMessage(); - // msg->getAPRSBody()->setData(msg->getAPRSBody()->getData() + " 123"); - logPrintD("[" + timeString() + "] "); - logPrintD("Received packet '"); - logPrintD(msg->toString()); - logPrintD("' with RSSI "); - logPrintD(String(_lora_aprs.packetRssi())); - logPrintD(" and SNR "); - logPrintlnD(String(_lora_aprs.packetSnr())); - - _fromModem.addElement(msg); - system.getDisplay().addFrame(std::shared_ptr(new TextFrame("LoRa", msg->toString()))); - } - - if (!_toModem.empty()) { - std::shared_ptr msg = _toModem.getElement(); - _lora_aprs.sendMessage(msg); - } - - return true; -} diff --git a/src/TaskModem.h b/src/TaskModem.h deleted file mode 100644 index 6087b67..0000000 --- a/src/TaskModem.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TASK_LORA_H_ -#define TASK_LORA_H_ - -#include -#include -#include - -class ModemTask : public Task { -public: - explicit ModemTask(TaskQueue> &fromModem, TaskQueue> &_toModem); - virtual ~ModemTask(); - - virtual bool setup(System &system) override; - virtual bool loop(System &system) override; - -private: - LoRa_APRS _lora_aprs; - - TaskQueue> &_fromModem; - TaskQueue> &_toModem; -}; - -#endif diff --git a/src/TaskNTP.cpp b/src/TaskNTP.cpp index 14b3808..7622032 100644 --- a/src/TaskNTP.cpp +++ b/src/TaskNTP.cpp @@ -18,7 +18,7 @@ bool NTPTask::setup(System &system) { } bool NTPTask::loop(System &system) { - if (!system.isWifiEthConnected()) { + if (!system.isWifiOrEthConnected()) { return false; } if (!_beginCalled) { @@ -27,8 +27,7 @@ bool NTPTask::loop(System &system) { } if (_ntpClient.update()) { setTime(_ntpClient.getEpochTime()); - logPrintI("Current time: "); - logPrintlnI(_ntpClient.getFormattedTime()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Current time: %s", _ntpClient.getFormattedTime().c_str()); } _stateInfo = _ntpClient.getFormattedTime(); _state = Okay; diff --git a/src/TaskOTA.cpp b/src/TaskOTA.cpp index 6b6348c..08f90fa 100644 --- a/src/TaskOTA.cpp +++ b/src/TaskOTA.cpp @@ -13,37 +13,36 @@ OTATask::~OTATask() { bool OTATask::setup(System &system) { _ota.onStart([&]() { String type; - if (_ota.getCommand() == U_FLASH) + if (_ota.getCommand() == U_FLASH) { type = "sketch"; - else // U_SPIFFS + } else { // U_SPIFFS type = "filesystem"; - logPrintlnI("Start updating " + type); + } + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "Start updating %s. please wait, this prozess is taking some time!", type.c_str()); }) - .onEnd([]() { - logPrintlnI(""); - logPrintlnI("OTA End"); + .onEnd([&]() { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "OTA End"); }) - .onProgress([](unsigned int progress, unsigned int total) { - logPrintI("Progress: "); - logPrintI(String(progress / (total / 100))); - logPrintlnI("%"); - }) - .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"); + .onError([&](ota_error_t error) { + String error_str; + if (error == OTA_AUTH_ERROR) { + error_str = "Auth Failed"; + } else if (error == OTA_BEGIN_ERROR) { + error_str = "Begin Failed"; + } else if (error == OTA_CONNECT_ERROR) { + error_str = "Connect Failed"; + } else if (error == OTA_RECEIVE_ERROR) { + error_str = "Receive Failed"; + } else if (error == OTA_END_ERROR) { + error_str = "End Failed"; + } + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "Error[%d]: %s", error, error_str.c_str()); }); - _ota.setHostname(system.getUserConfig()->callsign.c_str()); + if (system.getUserConfig()->network.hostname.overwrite) { + _ota.setHostname(system.getUserConfig()->network.hostname.name.c_str()); + } else { + _ota.setHostname(system.getUserConfig()->callsign.c_str()); + } _stateInfo = ""; return true; } diff --git a/src/TaskRadiolib.cpp b/src/TaskRadiolib.cpp new file mode 100644 index 0000000..295753a --- /dev/null +++ b/src/TaskRadiolib.cpp @@ -0,0 +1,212 @@ +#include +#include +#include + +#include "TaskRadiolib.h" + +RadiolibTask::RadiolibTask(TaskQueue> &fromModem, TaskQueue> &toModem) : Task(TASK_RADIOLIB, TaskRadiolib), _fromModem(fromModem), _toModem(toModem) { +} + +RadiolibTask::~RadiolibTask() { + radio->clearDio0Action(); +} + +volatile bool RadiolibTask::enableInterrupt = true; // Need to catch interrupt or not. +volatile bool RadiolibTask::operationDone = false; // Caught IRQ or not. + +void RadiolibTask::setFlag(void) { + if (!enableInterrupt) { + return; + } + + operationDone = true; +} + +bool RadiolibTask::setup(System &system) { + SPI.begin(system.getBoardConfig()->LoraSck, system.getBoardConfig()->LoraMiso, system.getBoardConfig()->LoraMosi, system.getBoardConfig()->LoraCS); + module = new Module(system.getBoardConfig()->LoraCS, system.getBoardConfig()->LoraIRQ, system.getBoardConfig()->LoraReset); + radio = new SX1278(module); + + config = system.getUserConfig()->lora; + + rxEnable = true; + txEnable = config.tx_enable; + + float freqMHz = (float)config.frequencyRx / 1000000; + float BWkHz = (float)config.signalBandwidth / 1000; + + const uint16_t preambleLength = 8; + + int16_t state = radio->begin(freqMHz, BWkHz, config.spreadingFactor, config.codingRate4, RADIOLIB_SX127X_SYNC_WORD, config.power, preambleLength, config.gainRx); + if (state != RADIOLIB_ERR_NONE) { + switch (state) { + case RADIOLIB_ERR_INVALID_FREQUENCY: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied frequency value (%fMHz) is invalid for this module.", timeString().c_str(), freqMHz); + rxEnable = false; + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_BANDWIDTH: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied bandwidth value (%fkHz) is invalid for this module. Should be 7800, 10400, 15600, 20800, 31250, 41700 ,62500, 125000, 250000, 500000.", timeString().c_str(), BWkHz); + rxEnable = false; + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_SPREADING_FACTOR: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied spreading factor value (%d) is invalid for this module.", timeString().c_str(), config.spreadingFactor); + rxEnable = false; + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_CODING_RATE: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied coding rate value (%d) is invalid for this module.", timeString().c_str(), config.codingRate4); + rxEnable = false; + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_OUTPUT_POWER: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied output power value (%d) is invalid for this module.", timeString().c_str(), config.power); + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied preamble length is invalid.", timeString().c_str()); + txEnable = false; + break; + case RADIOLIB_ERR_INVALID_GAIN: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, The supplied gain value (%d) is invalid.", timeString().c_str(), config.gainRx); + rxEnable = false; + break; + default: + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] SX1278 init failed, code %d", timeString().c_str(), state); + rxEnable = false; + txEnable = false; + } + _stateInfo = "LoRa-Modem failed"; + _state = Error; + } + + state = radio->setCRC(true); + if (state != RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] setCRC failed, code %d", timeString().c_str(), state); + _stateInfo = "LoRa-Modem failed"; + _state = Error; + } + + radio->setDio0Action(setFlag); + + if (rxEnable) { + int state = startRX(RADIOLIB_SX127X_RXCONTINUOUS); + if (state != RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] startRX failed, code %d", timeString().c_str(), state); + rxEnable = false; + _stateInfo = "LoRa-Modem failed"; + _state = Error; + } + } + + preambleDurationMilliSec = ((uint64_t)(preambleLength + 4) << (config.spreadingFactor + 10 /* to milli-sec */)) / config.signalBandwidth; + + _stateInfo = ""; + return true; +} + +int transmissionState = RADIOLIB_ERR_NONE; +bool transmitFlag = false; // Transmitting or not. + +bool RadiolibTask::loop(System &system) { + if (operationDone) { // occurs interrupt. + enableInterrupt = false; + + if (transmitFlag) { // transmitted. + if (transmissionState == RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] TX done", timeString().c_str()); + + } else { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] transmitFlag failed, code %d", timeString().c_str(), transmissionState); + } + operationDone = false; + transmitFlag = false; + + txWaitTimer.setTimeout(preambleDurationMilliSec * 2); + txWaitTimer.start(); + + } else { // received. + String str; + int state = radio->readData(str); + + if (state != RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] readData failed, code %d", timeString().c_str(), state); + } else { + if (str.substring(0, 3) != "<\xff\x01") { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] Unknown packet '%s' with RSSI %.0fdBm, SNR %.2fdB and FreqErr %fHz%s", timeString().c_str(), str.c_str(), radio->getRSSI(), radio->getSNR(), -radio->getFrequencyError()); + } else { + std::shared_ptr msg = std::shared_ptr(new APRSMessage()); + msg->decode(str.substring(3)); + _fromModem.addElement(msg); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] Received packet '%s' with RSSI %.0fdBm, SNR %.2fdB and FreqErr %fHz", timeString().c_str(), msg->toString().c_str(), radio->getRSSI(), radio->getSNR(), -radio->getFrequencyError()); + system.getDisplay().addFrame(std::shared_ptr(new TextFrame("LoRa", msg->toString().c_str()))); + } + } + operationDone = false; + } + + if (rxEnable) { + int state = startRX(RADIOLIB_SX127X_RXCONTINUOUS); + if (state != RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] startRX failed, code %d", timeString().c_str(), state); + rxEnable = false; + } + } + + enableInterrupt = true; + } else { // not interrupt. + if (!txWaitTimer.check()) { + } else { + if (!txEnable) { + // system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] TX is not enabled", timeString().c_str()); + } else { + if (transmitFlag) { + // system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] TX signal detected. Waiting TX", timeString().c_str()); + } else { + if (!_toModem.empty()) { + if (config.frequencyRx == config.frequencyTx && (radio->getModemStatus() & 0x01) == 0x01) { + // system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] RX signal detected. Waiting TX", timeString().c_str()); + } else { + std::shared_ptr msg = _toModem.getElement(); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "[%s] Transmitting packet '%s'", timeString().c_str(), msg->toString().c_str()); + + int16_t state = startTX("<\xff\x01" + msg->encode()); + if (state != RADIOLIB_ERR_NONE) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "[%s] startTX failed, code %d", timeString().c_str(), state); + txEnable = false; + return true; + } + } + } + } + } + } + } + + return true; +} + +int16_t RadiolibTask::startRX(uint8_t mode) { + if (config.frequencyTx != config.frequencyRx) { + int16_t state = radio->setFrequency((float)config.frequencyRx / 1000000); + if (state != RADIOLIB_ERR_NONE) { + return state; + } + } + + return radio->startReceive(0, mode); +} + +int16_t RadiolibTask::startTX(String &str) { + if (config.frequencyTx != config.frequencyRx) { + int16_t state = radio->setFrequency((float)config.frequencyTx / 1000000); + if (state != RADIOLIB_ERR_NONE) { + return state; + } + } + + transmissionState = radio->startTransmit(str); + transmitFlag = true; + return RADIOLIB_ERR_NONE; +} diff --git a/src/TaskRadiolib.h b/src/TaskRadiolib.h new file mode 100644 index 0000000..11e59da --- /dev/null +++ b/src/TaskRadiolib.h @@ -0,0 +1,41 @@ +#ifndef TASK_LORA_H_ +#define TASK_LORA_H_ + +#include "project_configuration.h" +#include +#include +#include +#include + +class RadiolibTask : public Task { +public: + explicit RadiolibTask(TaskQueue> &fromModem, TaskQueue> &_toModem); + virtual ~RadiolibTask(); + + virtual bool setup(System &system) override; + virtual bool loop(System &system) override; + +private: + Module *module; + SX1278 *radio; + + Configuration::LoRa config; + + bool rxEnable, txEnable; + + TaskQueue> &_fromModem; + TaskQueue> &_toModem; + + static volatile bool enableInterrupt; // Need to catch interrupt or not. + static volatile bool operationDone; // Caught IRQ or not. + + static void setFlag(void); + + int16_t startRX(uint8_t mode); + int16_t startTX(String &str); + + uint32_t preambleDurationMilliSec; + Timer txWaitTimer; +}; + +#endif diff --git a/src/TaskRouter.cpp b/src/TaskRouter.cpp index f64f6b0..dc9da8a 100644 --- a/src/TaskRouter.cpp +++ b/src/TaskRouter.cpp @@ -6,34 +6,24 @@ #include "TaskRouter.h" #include "project_configuration.h" -String create_lat_aprs(double lat); -String create_long_aprs(double lng); - -RouterTask::RouterTask(TaskQueue> &fromModem, TaskQueue> &toModem, TaskQueue> &toAprsIs) : Task(TASK_ROUTER, TaskRouter), _fromModem(fromModem), _toModem(toModem), _toAprsIs(toAprsIs) { +RouterTask::RouterTask(TaskQueue> &fromModem, TaskQueue> &toModem, TaskQueue> &toAprsIs, TaskQueue> &toMQTT) : Task(TASK_ROUTER, TaskRouter), _fromModem(fromModem), _toModem(toModem), _toAprsIs(toAprsIs), _toMQTT(toMQTT) { } RouterTask::~RouterTask() { } bool RouterTask::setup(System &system) { - // setup beacon - _beacon_timer.setTimeout(system.getUserConfig()->beacon.timeout * 60 * 1000); - - _beaconMsg = std::shared_ptr(new APRSMessage()); - _beaconMsg->setSource(system.getUserConfig()->callsign); - _beaconMsg->setDestination("APLG01"); - String lat = create_lat_aprs(system.getUserConfig()->beacon.positionLatitude); - String lng = create_long_aprs(system.getUserConfig()->beacon.positionLongitude); - _beaconMsg->getBody()->setData(String("=") + lat + "L" + lng + "&" + system.getUserConfig()->beacon.message); - return true; } bool RouterTask::loop(System &system) { - // do routing if (!_fromModem.empty()) { std::shared_ptr modemMsg = _fromModem.getElement(); + if (system.getUserConfig()->mqtt.active) { + _toMQTT.addElement(modemMsg); + } + if (system.getUserConfig()->aprs_is.active && modemMsg->getSource() != system.getUserConfig()->callsign) { std::shared_ptr aprsIsMsg = std::make_shared(*modemMsg); String path = aprsIsMsg->getPath(); @@ -43,20 +33,21 @@ bool RouterTask::loop(System &system) { path += ","; } - aprsIsMsg->setPath(path + "qAR," + system.getUserConfig()->callsign); + aprsIsMsg->setPath(path + "qAO," + system.getUserConfig()->callsign); - logPrintD("APRS-IS: "); - logPrintlnD(aprsIsMsg->toString()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "APRS-IS: %s", aprsIsMsg->toString().c_str()); _toAprsIs.addElement(aprsIsMsg); } else { - logPrintlnD("APRS-IS: no forward => RFonly"); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "APRS-IS: no forward => RFonly"); } } else { - if (!system.getUserConfig()->aprs_is.active) - logPrintlnD("APRS-IS: disabled"); + if (!system.getUserConfig()->aprs_is.active) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "APRS-IS: disabled"); + } - if (modemMsg->getSource() == system.getUserConfig()->callsign) - logPrintlnD("APRS-IS: no forward => own packet received"); + if (modemMsg->getSource() == system.getUserConfig()->callsign) { + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "APRS-IS: no forward => own packet received"); + } } if (system.getUserConfig()->digi.active && modemMsg->getSource() != system.getUserConfig()->callsign) { @@ -68,33 +59,14 @@ bool RouterTask::loop(System &system) { // fixme digiMsg->setPath(system.getUserConfig()->callsign + "*"); - logPrintD("DIGI: "); - logPrintlnD(digiMsg->toString()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_INFO, getName(), "DIGI: %s", digiMsg->toString().c_str()); _toModem.addElement(digiMsg); } } } - // check for beacon - if (_beacon_timer.check()) { - logPrintD("[" + timeString() + "] "); - logPrintlnD(_beaconMsg->encode()); - - if (system.getUserConfig()->aprs_is.active) - _toAprsIs.addElement(_beaconMsg); - - if (system.getUserConfig()->digi.beacon) { - _toModem.addElement(_beaconMsg); - } - - system.getDisplay().addFrame(std::shared_ptr(new TextFrame("BEACON", _beaconMsg->toString()))); - - _beacon_timer.start(); - } - - uint32_t diff = _beacon_timer.getTriggerTimeInSec(); - _stateInfo = "beacon " + String(uint32_t(diff / 600)) + String(uint32_t(diff / 60) % 10) + ":" + String(uint32_t(diff / 10) % 6) + String(uint32_t(diff % 10)); + _stateInfo = "Router done "; return true; } diff --git a/src/TaskRouter.h b/src/TaskRouter.h index 8d8d1ac..a2e1bbe 100644 --- a/src/TaskRouter.h +++ b/src/TaskRouter.h @@ -2,11 +2,12 @@ #define TASK_ROUTER_H_ #include +#include #include class RouterTask : public Task { public: - RouterTask(TaskQueue> &fromModem, TaskQueue> &toModem, TaskQueue> &toAprsIs); + RouterTask(TaskQueue> &fromModem, TaskQueue> &toModem, TaskQueue> &toAprsIs, TaskQueue> &toMQTT); virtual ~RouterTask(); virtual bool setup(System &system) override; @@ -16,9 +17,7 @@ private: TaskQueue> &_fromModem; TaskQueue> &_toModem; TaskQueue> &_toAprsIs; - - std::shared_ptr _beaconMsg; - Timer _beacon_timer; + TaskQueue> &_toMQTT; }; #endif diff --git a/src/TaskWifi.cpp b/src/TaskWifi.cpp index 6a0b54e..1377ba7 100644 --- a/src/TaskWifi.cpp +++ b/src/TaskWifi.cpp @@ -20,15 +20,18 @@ bool WifiTask::setup(System &system) { WiFi.mode(WIFI_STA); WiFi.onEvent(WiFiEvent); - WiFi.setHostname(system.getUserConfig()->callsign.c_str()); + if (system.getUserConfig()->network.hostname.overwrite) { + WiFi.setHostname(system.getUserConfig()->network.hostname.name.c_str()); + } else { + WiFi.setHostname(system.getUserConfig()->callsign.c_str()); + } if (!system.getUserConfig()->network.DHCP) { - WiFi.config(system.getUserConfig()->network.staticIP, system.getUserConfig()->network.gateway, system.getUserConfig()->network.subnet, system.getUserConfig()->network.dns1, system.getUserConfig()->network.dns2); + WiFi.config(system.getUserConfig()->network.static_.ip, system.getUserConfig()->network.static_.gateway, system.getUserConfig()->network.static_.subnet, system.getUserConfig()->network.static_.dns1, system.getUserConfig()->network.static_.dns2); } for (Configuration::Wifi::AP ap : system.getUserConfig()->wifi.APs) { - logPrintD("Looking for AP: "); - logPrintlnD(ap.SSID); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "Looking for AP: %s", ap.SSID.c_str()); _wiFiMulti.addAP(ap.SSID.c_str(), ap.password.c_str()); } return true; @@ -37,20 +40,19 @@ bool WifiTask::setup(System &system) { bool WifiTask::loop(System &system) { const uint8_t wifi_status = _wiFiMulti.run(); if (wifi_status != WL_CONNECTED) { - system.connectedViaWifiEth(false); - logPrintlnE("WiFi not connected!"); + system.connectedViaWifi(false); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, getName(), "WiFi not connected!"); _oldWifiStatus = wifi_status; _stateInfo = "WiFi not connected"; _state = Error; return false; } else if (wifi_status != _oldWifiStatus) { - logPrintD("IP address: "); - logPrintlnD(WiFi.localIP().toString()); + system.getLogger().log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, getName(), "WiFi IP address: %s", WiFi.localIP().toString().c_str()); _oldWifiStatus = wifi_status; return false; } - system.connectedViaWifiEth(true); - _stateInfo = WiFi.localIP().toString(); + system.connectedViaWifi(true); + _stateInfo = String("IP .") + String(WiFi.localIP()[3]) + String(" @ ") + String(WiFi.RSSI()) + String("dBm"); _state = Okay; return true; } diff --git a/src/project_configuration.cpp b/src/project_configuration.cpp index 656d284..02e5acb 100644 --- a/src/project_configuration.cpp +++ b/src/project_configuration.cpp @@ -8,42 +8,63 @@ void ProjectConfigurationManagement::readProjectConfiguration(DynamicJsonDocumen if (data.containsKey("callsign")) conf.callsign = data["callsign"].as(); - if (data.containsKey("network") && data["network"].containsKey("DHCP")) { - conf.network.DHCP = data["network"]["DHCP"]; - conf.network.staticIP.fromString(data["network"]["staticIP"].as()); - conf.network.subnet.fromString(data["network"]["subnet"].as()); - conf.network.gateway.fromString(data["network"]["gateway"].as()); - conf.network.dns1.fromString(data["network"]["dns1"].as()); - conf.network.dns2.fromString(data["network"]["dns2"].as()); + if (data.containsKey("network")) { + conf.network.DHCP = data["network"]["DHCP"] | false; + if (data["network"].containsKey("static")) { + if (data["network"]["static"].containsKey("ip")) + conf.network.static_.ip.fromString(data["network"]["static"]["ip"].as()); + if (data["network"]["static"].containsKey("subnet")) + conf.network.static_.subnet.fromString(data["network"]["static"]["subnet"].as()); + if (data["network"]["static"].containsKey("gateway")) + conf.network.static_.gateway.fromString(data["network"]["static"]["gateway"].as()); + if (data["network"]["static"].containsKey("dns1")) + conf.network.static_.dns1.fromString(data["network"]["static"]["dns1"].as()); + if (data["network"]["static"].containsKey("dns2")) + conf.network.static_.dns2.fromString(data["network"]["static"]["dns2"].as()); + } + if (data["network"].containsKey("hostname")) { + conf.network.hostname.overwrite = data["network"]["hostname"]["overwrite"] | false; + if (data["network"]["hostname"].containsKey("name")) + conf.network.hostname.name = data["network"]["hostname"]["name"].as(); + } } - JsonArray aps = data["wifi"]["AP"].as(); + conf.wifi.active = data["wifi"]["active"]; + JsonArray aps = data["wifi"]["AP"].as(); for (JsonVariant v : aps) { Configuration::Wifi::AP ap; - ap.SSID = v["SSID"].as(); - ap.password = v["password"].as(); + if (v.containsKey("SSID")) + ap.SSID = v["SSID"].as(); + if (v.containsKey("password")) + ap.password = v["password"].as(); conf.wifi.APs.push_back(ap); } if (data.containsKey("beacon") && data["beacon"].containsKey("message")) 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.beacon.use_gps = data["beacon"]["use_gps"] | false; conf.beacon.timeout = data["beacon"]["timeout"] | 15; conf.aprs_is.active = data["aprs_is"]["active"] | true; + if (data.containsKey("aprs_is") && data["aprs_is"].containsKey("passcode")) conf.aprs_is.passcode = data["aprs_is"]["passcode"].as(); if (data.containsKey("aprs_is") && data["aprs_is"].containsKey("server")) conf.aprs_is.server = data["aprs_is"]["server"].as(); conf.aprs_is.port = data["aprs_is"]["port"] | 14580; - conf.digi.active = data["digi"]["active"] | false; - conf.digi.beacon = data["digi"]["beacon"] | false; + conf.digi.active = data["digi"]["active"] | false; + conf.digi.beacon = data["digi"]["beacon"] | false; + conf.lora.frequencyRx = data["lora"]["frequency_rx"] | 433775000; + conf.lora.gainRx = data["lora"]["gain_rx"] | 0; conf.lora.frequencyTx = data["lora"]["frequency_tx"] | 433775000; conf.lora.power = data["lora"]["power"] | 20; conf.lora.spreadingFactor = data["lora"]["spreading_factor"] | 12; conf.lora.signalBandwidth = data["lora"]["signal_bandwidth"] | 125000; conf.lora.codingRate4 = data["lora"]["coding_rate4"] | 5; + conf.lora.tx_enable = data["lora"]["tx_enable"] | true; + conf.display.alwaysOn = data["display"]["always_on"] | true; conf.display.timeout = data["display"]["timeout"] | 10; conf.display.overwritePin = data["display"]["overwrite_pin"] | 0; @@ -53,8 +74,10 @@ void ProjectConfigurationManagement::readProjectConfiguration(DynamicJsonDocumen JsonArray users = data["ftp"]["user"].as(); for (JsonVariant u : users) { Configuration::Ftp::User us; - us.name = u["name"].as(); - us.password = u["password"].as(); + if (u.containsKey("name")) + us.name = u["name"].as(); + if (u.containsKey("password")) + us.password = u["password"].as(); conf.ftp.users.push_back(us); } if (conf.ftp.users.empty()) { @@ -63,6 +86,23 @@ void ProjectConfigurationManagement::readProjectConfiguration(DynamicJsonDocumen us.password = "ftp"; conf.ftp.users.push_back(us); } + + conf.mqtt.active = data["mqtt"]["active"] | false; + if (data["mqtt"].containsKey("server")) + conf.mqtt.server = data["mqtt"]["server"].as(); + conf.mqtt.port = data["mqtt"]["port"] | 1883; + if (data["mqtt"].containsKey("name")) + conf.mqtt.name = data["mqtt"]["name"].as(); + if (data["mqtt"].containsKey("password")) + conf.mqtt.password = data["mqtt"]["password"].as(); + if (data["mqtt"].containsKey("topic")) + conf.mqtt.topic = data["mqtt"]["topic"].as(); + + conf.syslog.active = data["syslog"]["active"] | true; + if (data["syslog"].containsKey("server")) + conf.syslog.server = data["syslog"]["server"].as(); + conf.syslog.port = data["syslog"]["port"] | 514; + if (data.containsKey("ntp_server")) conf.ntpServer = data["ntp_server"].as(); @@ -74,15 +114,18 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration &co data["callsign"] = conf.callsign; if (!conf.network.DHCP) { - data["network"]["DHCP"] = conf.network.DHCP; - data["network"]["staticIP"] = conf.network.staticIP.toString(); - data["network"]["subnet"] = conf.network.subnet.toString(); - data["network"]["gateway"] = conf.network.gateway.toString(); - data["network"]["dns1"] = conf.network.dns1.toString(); - data["network"]["dns2"] = conf.network.dns2.toString(); + data["network"]["DHCP"] = conf.network.DHCP; + data["network"]["static"]["ip"] = conf.network.static_.ip.toString(); + data["network"]["static"]["subnet"] = conf.network.static_.subnet.toString(); + data["network"]["static"]["gateway"] = conf.network.static_.gateway.toString(); + data["network"]["static"]["dns1"] = conf.network.static_.dns1.toString(); + data["network"]["static"]["dns2"] = conf.network.static_.dns2.toString(); + data["network"]["hostname"]["overwrite"] = conf.network.hostname.overwrite; + data["network"]["hostname"]["name"] = conf.network.hostname.name; } - JsonArray aps = data["wifi"].createNestedArray("AP"); + data["wifi"]["active"] = conf.wifi.active; + JsonArray aps = data["wifi"].createNestedArray("AP"); for (Configuration::Wifi::AP ap : conf.wifi.APs) { JsonObject v = aps.createNestedObject(); v["SSID"] = ap.SSID; @@ -91,6 +134,7 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration &co data["beacon"]["message"] = conf.beacon.message; data["beacon"]["position"]["latitude"] = conf.beacon.positionLatitude; data["beacon"]["position"]["longitude"] = conf.beacon.positionLongitude; + data["beacon"]["use_gps"] = conf.beacon.use_gps; data["beacon"]["timeout"] = conf.beacon.timeout; data["aprs_is"]["active"] = conf.aprs_is.active; data["aprs_is"]["passcode"] = conf.aprs_is.passcode; @@ -99,11 +143,13 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration &co data["digi"]["active"] = conf.digi.active; data["digi"]["beacon"] = conf.digi.beacon; data["lora"]["frequency_rx"] = conf.lora.frequencyRx; + data["lora"]["gain_rx"] = conf.lora.gainRx; data["lora"]["frequency_tx"] = conf.lora.frequencyTx; data["lora"]["power"] = conf.lora.power; data["lora"]["spreading_factor"] = conf.lora.spreadingFactor; data["lora"]["signal_bandwidth"] = conf.lora.signalBandwidth; data["lora"]["coding_rate4"] = conf.lora.codingRate4; + data["lora"]["tx_enable"] = conf.lora.tx_enable; data["display"]["always_on"] = conf.display.alwaysOn; data["display"]["timeout"] = conf.display.timeout; data["display"]["overwrite_pin"] = conf.display.overwritePin; @@ -115,7 +161,16 @@ void ProjectConfigurationManagement::writeProjectConfiguration(Configuration &co v["name"] = u.name; v["password"] = u.password; } - data["ntp_server"] = conf.ntpServer; + data["mqtt"]["active"] = conf.mqtt.active; + data["mqtt"]["server"] = conf.mqtt.server; + data["mqtt"]["port"] = conf.mqtt.port; + data["mqtt"]["name"] = conf.mqtt.name; + data["mqtt"]["password"] = conf.mqtt.password; + data["mqtt"]["topic"] = conf.mqtt.topic; + data["syslog"]["active"] = conf.syslog.active; + data["syslog"]["server"] = conf.syslog.server; + data["syslog"]["port"] = conf.syslog.port; + data["ntp_server"] = conf.ntpServer; data["board"] = conf.board; } diff --git a/src/project_configuration.h b/src/project_configuration.h index b2ba0bc..e32a17a 100644 --- a/src/project_configuration.h +++ b/src/project_configuration.h @@ -6,41 +6,55 @@ class Configuration { public: - class Network { + class Static { public: - Network() : DHCP(true) { - } - - bool DHCP; - IPAddress staticIP; + IPAddress ip; IPAddress subnet; IPAddress gateway; IPAddress dns1; IPAddress dns2; }; + class Hostname { + public: + bool overwrite; + String name; + }; + + class Network { + public: + Network() : DHCP(true) { + } + + bool DHCP; + Static static_; + Hostname hostname; + }; + class Wifi { public: + Wifi() : active(true) { + } + + bool active; class AP { public: String SSID; String password; }; - Wifi() { - } - std::list APs; }; class Beacon { public: - Beacon() : message("LoRa iGATE & Digi, Info: github.com/peterus/LoRa_APRS_iGate"), positionLatitude(0.0), positionLongitude(0.0), timeout(15) { + Beacon() : message("LoRa iGATE & Digi, Info: github.com/peterus/LoRa_APRS_iGate"), positionLatitude(0.0), positionLongitude(0.0), use_gps(false), timeout(15) { } String message; double positionLatitude; double positionLongitude; + bool use_gps; int timeout; }; @@ -66,15 +80,17 @@ public: class LoRa { public: - LoRa() : frequencyRx(433775000), frequencyTx(433775000), power(20), spreadingFactor(12), signalBandwidth(125000), codingRate4(5) { + LoRa() : frequencyRx(433775000), frequencyTx(433775000), power(20), spreadingFactor(12), signalBandwidth(125000), codingRate4(5), tx_enable(true) { } - long frequencyRx; - long frequencyTx; - int power; - int spreadingFactor; - long signalBandwidth; - int codingRate4; + long frequencyRx; + uint8_t gainRx; + long frequencyTx; + int power; + int spreadingFactor; + long signalBandwidth; + int codingRate4; + bool tx_enable; }; class Display { @@ -103,7 +119,31 @@ public: std::list users; }; - Configuration() : callsign("NOCALL-10"), board(""), ntpServer("pool.ntp.org"){}; + class MQTT { + public: + MQTT() : active(false), server(""), port(1883), name(""), password(""), topic("LoraAPRS/Data") { + } + + bool active; + String server; + int port; + String name; + String password; + String topic; + }; + + class Syslog { + public: + Syslog() : active(true), server("syslog.lora-aprs.info"), port(514) { + } + + bool active; + String server; + int port; + }; + + Configuration() : callsign("NOCALL-10"), ntpServer("pool.ntp.org"), board("") { + } String callsign; Network network; @@ -114,13 +154,15 @@ public: LoRa lora; Display display; Ftp ftp; - String board; + MQTT mqtt; + Syslog syslog; String ntpServer; + String board; }; class ProjectConfigurationManagement : public ConfigurationManagement { public: - explicit ProjectConfigurationManagement() : ConfigurationManagement("/is-cfg.json") { + explicit ProjectConfigurationManagement(logging::Logger &logger) : ConfigurationManagement(logger, "/is-cfg.json") { } virtual ~ProjectConfigurationManagement() { }