├── .github
├── FUNDING.yml
└── stale.yml
├── .gitignore
├── .travis.yml
├── .vscode
├── extensions.json
├── settings.json
└── templates
│ ├── cpp.lict
│ └── h.lict
├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── append_version_to_progname.py
├── boards
└── hb-rf-eth.json
├── build
└── CMakeFiles
│ └── cmake.check_cache
├── build_webui.py
├── include
├── README
├── dcf.h
├── ethernet.h
├── gps.h
├── hmframe.h
├── led.h
├── linereader.h
├── mdnsserver.h
├── ntpclient.h
├── ntpserver.h
├── pins.h
├── pushbuttonhandler.h
├── radiomoduleconnector.h
├── radiomoduledetector.h
├── radiomoduledetector_utils.h
├── rawuartudplistener.h
├── rtc.h
├── settings.h
├── streamparser.h
├── sysinfo.h
├── systemclock.h
├── udphelper.h
├── updatecheck.h
└── webui.h
├── lib
└── README
├── partitions.csv
├── platformio.ini
├── sdkconfig.hb-rf-eth
├── src
├── CMakeLists.txt
├── dcf.cpp
├── ethernet.cpp
├── gps.cpp
├── hmframe.cpp
├── led.cpp
├── linereader.cpp
├── main.cpp
├── mdnsserver.cpp
├── ntpclient.cpp
├── ntpserver.cpp
├── pushbuttonhandler.cpp
├── radiomoduleconnector.cpp
├── radiomoduledetector.cpp
├── rawuartudplistener.cpp
├── rtc.cpp
├── settings.cpp
├── streamparser.cpp
├── sysinfo.cpp
├── systemclock.cpp
├── updatecheck.cpp
└── webui.cpp
├── test
└── README
├── version.txt
└── webui
├── .babelrc
├── .compressrc
├── favicon.ico
├── index.htm
├── package-lock.json
├── package.json
└── src
├── about.vue
├── app.vue
├── firmwareupdate.vue
├── header.vue
├── home.vue
├── login.vue
├── main.js
├── settings.vue
└── sysinfo.vue
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [alexreinert]
2 | custom: ['https://www.amazon.de/gp/registry/wishlist/3NNUQIQO20AAP/ref=nav_wishlist_lists_1', 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WUC7QU84EU7DA']
3 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: stale
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had recent activity.
14 | It will be closed if no further activity occurs. Thank you for your contributions.
15 | # Comment to post when closing a stale issue. Set to `false` to disable
16 | closeComment: false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 | src/webui/*.gzip
7 | webui/.cache
8 | webui/dist
9 | webui/node_modules
10 |
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Continuous Integration (CI) is the practice, in software
2 | # engineering, of merging all developer working copies with a shared mainline
3 | # several times a day < https://docs.platformio.org/page/ci/index.html >
4 | #
5 | # Documentation:
6 | #
7 | # * Travis CI Embedded Builds with PlatformIO
8 | # < https://docs.travis-ci.com/user/integration/platformio/ >
9 | #
10 | # * PlatformIO integration with Travis CI
11 | # < https://docs.platformio.org/page/ci/travis.html >
12 | #
13 | # * User Guide for `platformio ci` command
14 | # < https://docs.platformio.org/page/userguide/cmd_ci.html >
15 | #
16 | #
17 | # Please choose one of the following templates (proposed below) and uncomment
18 | # it (remove "# " before each line) or use own configuration according to the
19 | # Travis CI documentation (see above).
20 | #
21 |
22 |
23 | #
24 | # Template #1: General project. Test it using existing `platformio.ini`.
25 | #
26 |
27 | # language: python
28 | # python:
29 | # - "2.7"
30 | #
31 | # sudo: false
32 | # cache:
33 | # directories:
34 | # - "~/.platformio"
35 | #
36 | # install:
37 | # - pip install -U platformio
38 | # - platformio update
39 | #
40 | # script:
41 | # - platformio run
42 |
43 |
44 | #
45 | # Template #2: The project is intended to be used as a library with examples.
46 | #
47 |
48 | # language: python
49 | # python:
50 | # - "2.7"
51 | #
52 | # sudo: false
53 | # cache:
54 | # directories:
55 | # - "~/.platformio"
56 | #
57 | # env:
58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c
59 | # - PLATFORMIO_CI_SRC=examples/file.ino
60 | # - PLATFORMIO_CI_SRC=path/to/test/directory
61 | #
62 | # install:
63 | # - pip install -U platformio
64 | # - platformio update
65 | #
66 | # script:
67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
68 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "typeinfo": "cpp",
4 | "unordered_map": "cpp",
5 | "initializer_list": "cpp",
6 | "atomic": "cpp",
7 | "array": "cpp",
8 | "*.tcc": "cpp",
9 | "cctype": "cpp",
10 | "chrono": "cpp",
11 | "clocale": "cpp",
12 | "cmath": "cpp",
13 | "codecvt": "cpp",
14 | "condition_variable": "cpp",
15 | "csignal": "cpp",
16 | "cstdarg": "cpp",
17 | "cstddef": "cpp",
18 | "cstdint": "cpp",
19 | "cstdio": "cpp",
20 | "cstdlib": "cpp",
21 | "cstring": "cpp",
22 | "ctime": "cpp",
23 | "cwchar": "cpp",
24 | "cwctype": "cpp",
25 | "deque": "cpp",
26 | "list": "cpp",
27 | "vector": "cpp",
28 | "exception": "cpp",
29 | "algorithm": "cpp",
30 | "functional": "cpp",
31 | "iterator": "cpp",
32 | "map": "cpp",
33 | "memory": "cpp",
34 | "memory_resource": "cpp",
35 | "numeric": "cpp",
36 | "optional": "cpp",
37 | "random": "cpp",
38 | "ratio": "cpp",
39 | "set": "cpp",
40 | "string": "cpp",
41 | "string_view": "cpp",
42 | "system_error": "cpp",
43 | "tuple": "cpp",
44 | "type_traits": "cpp",
45 | "utility": "cpp",
46 | "fstream": "cpp",
47 | "future": "cpp",
48 | "iosfwd": "cpp",
49 | "istream": "cpp",
50 | "limits": "cpp",
51 | "mutex": "cpp",
52 | "new": "cpp",
53 | "ostream": "cpp",
54 | "sstream": "cpp",
55 | "stdexcept": "cpp",
56 | "streambuf": "cpp",
57 | "thread": "cpp",
58 | "cinttypes": "cpp",
59 | "*.ipp": "cpp",
60 | "esp_transport.h": "c"
61 | },
62 | "license-header-manager.excludeExtensions": [
63 | ".js",
64 | ".css",
65 | ".py",
66 | ".vue"
67 | ]
68 | }
--------------------------------------------------------------------------------
/.vscode/templates/cpp.lict:
--------------------------------------------------------------------------------
1 | %(File) is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
2 |
3 | Copyright %(CurrentYear) Alexander Reinert
4 |
5 | The HB-RF-ETH firmware is licensed under a
6 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
7 |
8 | You should have received a copy of the license along with this
9 | work. If not, see .
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
--------------------------------------------------------------------------------
/.vscode/templates/h.lict:
--------------------------------------------------------------------------------
1 | %(File) is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
2 |
3 | Copyright %(CurrentYear) Alexander Reinert
4 |
5 | The HB-RF-ETH firmware is licensed under a
6 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
7 |
8 | You should have received a copy of the license along with this
9 | work. If not, see .
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.16.0)
2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake)
3 | project(HB-RF-ETH)
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HB-RF-ETH Firmware
2 |
3 | ### Unterstützung [
](https://ko-fi.com/alexreinert) [
](https://www.paypal.com/donate/?cmd=_s-xclick&hosted_button_id=4PW43VJ2DZ7R2)
4 | Meine Entwicklungen im Homematic Umfeld sind sehr kostenintensiv, z.B. werden viele verschiedene Testgeräte oder auch diverse Prototypen von Platinen benötigt. Allerdings erhält meine Projekt keine Unterstützung durch kommerzielle Anbieter. Ich freue mich daher durch eine Unterstützung mit einer Spende via [Ko-fi](https://ko-fi.com/alexreinert), [PayPal](https://www.paypal.com/donate/?cmd=_s-xclick&hosted_button_id=4PW43VJ2DZ7R2) oder durch eine Aufmerksamkeit auf meinem [Amazon Wunschzettel](https://www.amazon.de/gp/registry/wishlist/3NNUQIQO20AAP/ref=nav_wishlist_lists_1).
5 |
6 | ### Worum es geht
7 | Dieses Repository enhält die Firmware für die HB-RF-ETH Platine, welches es ermöglicht, ein Homematic Funkmodul HM-MOD-RPI-PCB oder RPI-RF-MOD per Netzwerk an eine debmatic oder piVCCU3 Installation anzubinden.
8 |
9 | Hierbei gilt, dass bei einer debmatic oder piVCCU3 Installation immer nur ein Funkmodul angebunden werden kann, egal ob die Anbindung direkt per GPIO Leiste, USB mittels HB-RF-USB(-2) Platine oder per HB-RF-ETH Platine erfolgt.
10 |
11 | ### Was kann die Firmware
12 | * Bereitstellung des Funkmoduls RPI-RF-MOD oder HM-MOD-RPI-PCB per UDP als raw-uart Gerät inkl. Ansteuerung der LEDs des RPI-RF-MODs
13 | * (S)NTP Server für die Verteilung der Zeit im lokalen Netzwerk
14 | * Unterstützung der RTC des RPI-RF-MODs oder eines [DS3231 Aufsteckmoduls](https://www.amazon.de/ANGEEK-DS3231-Precision-Arduino-Raspberry/dp/B07WJSQ6M2)
15 | * Verschiedene mögliche Zeitquellen
16 | * (S)NTP Client
17 | * DCF77 Empfänger (aka Funkuhr) mittels [optionalem Moduls](https://de.elv.com/elv-gehaeuse-fuer-externe-dcf-antenne-dcf-et1-komplettbausatz-ohne-dcf-modul-142883):
18 | * Konnektor J5
19 | * Pin 1: VCC
20 | * Pin 2: DCF Signal
21 | * Pin 3: Gnd
22 | * GPS Empfänger mittels [optionalem Moduls](https://www.amazon.de/AZDelivery-NEO-6M-GPS-baugleich-u-blox/dp/B01N38EMBF):
23 | * Konnektor J5
24 | * Pin 1: VCC
25 | * Pin 2: TX
26 | * Pin 3: Gnd
27 | * MDNS Server um Platine im Netzwerk bekannt zu machen
28 | * Netzwerkeinsellungen per DHCP oder statisch konfigurierbar
29 | * WebUI zur Konfiguration
30 | * Intialpasswort: admin
31 | * Firmware Update per Webinterface
32 | * Erkennung des Funkmoduls und Ausgabe von Typ, Seriennummer, Funkadresse und SGTIN in der WebUI
33 | * Regelmäßige Prüfung auf Firmwareupdates
34 | * Werksreset per Taster
35 |
36 | ### Bekannte Einschränkungen
37 | * Nach einem Neustart der Platine (z.B. bei Stromausfall) findet kein automatischer Reconnect statt, in diesem Fall muss die CCU Software daher neu gestartet werden.
38 | * Die Stromversorgung mittels des Funkmoduls RPI-RF-MOD darf nur erfolgen, wenn keine andere Stromversorgung (USB oder PoE) angeschlossen ist.
39 |
40 | ### Werksreset
41 | Die Firmware kann per Taster auf Werkseinstellungen zurückgesetzt werden:
42 | 1. Platine vom Strom trennen
43 | 2. Taster drücken und gedrückt halten
44 | 3. Stromversorgung wiederherstellen
45 | 4. Nach ca. 4 Sekunden fängt die rote Status LED schnell zu blinken an und die grüne Power LED hört auf zu leuchten
46 | 5. Taster kurz loslassen und wieder drücken und gedrückt halten
47 | 6. Nach ca. 4 Sekunden leuchten die grüne Power LED und die rote Status LED für eine Sekunde
48 | 7. Danach ist der Werkreset abgeschlossen und es folgt der normale Bootvorgang
49 |
50 | ### Blinkcodes der LEDs
51 | #### RPI-RF-MOD
52 | Siehe Hilfe zum RPI-RF-MOD
53 |
54 | #### Grüne Power LED und rote Status LED
55 | * Blinken abwechselnd mit grüner Power LED: System bootet
56 | * Schnelles Blinken der roten Status LED, grüne Power LED leuchtet nicht: Siehe Werksreset
57 | * Schnelles Blinken der roten Status LED, grüne Power LED leuchtet dauerhaft: Firmware Update wird eingespielt
58 | * Langsames Blinken der roten Status LED, grüne Power LED leuchtet dauerhaft: Es ist ein Firmware Update verfügbar
59 | * Dauerhaftes Leuchten der grünen Power LED: Sytem ist gestartet
60 |
61 | ### Firmware Updates
62 | Firmware Updates sind fertig kompiliert und Releases zu finden und können per Webinterface eingespielt werden. Zum Übernehmen der Firmware muss die Platine neu gestartet werden (mittel Power-On Reset).
63 |
64 | ### Einbindung in piVCCU3 und debmatic
65 | Die Unterstützung für die Platine HB-RF-ETH ist in piVCCU3 ab Version 3.51.6-41 und in debmatic ab Version 3.51.6-46 eingebaut. Die Installation der Platine erfolgt über das Paket "hb-rf-eth". Weiteres Details findet man in der Installationsanleitung von piVCCU3 bzw. debmatic.
66 |
67 | ### Roadmap
68 | Folgende Punkte sind angedacht für zukünftige Releases. Die Sortierung ist als zufällig anzusehen und es ist nicht garantiert, dass alle Punkte auch umgesetzt werden.
69 |
70 | * Transportverschlüsselung raw-uart
71 | * LED Fading
72 | * SNMP
73 | * CheckMK Agent
74 | * LAN GW Modus
75 | * AskSin Analyzer Light
76 |
77 | ### Lizenz
78 | Die Firmware steht unter Creative Commons Attribution-NonCommercial-ShareAlike 4.0 Lizenz.
79 |
--------------------------------------------------------------------------------
/append_version_to_progname.py:
--------------------------------------------------------------------------------
1 | Import("env")
2 |
3 | with open("version.txt") as fp:
4 | version = fp.readline()
5 | env.Replace(PROGNAME="firmware_%s" % version.replace(".", "_"))
6 |
--------------------------------------------------------------------------------
/boards/hb-rf-eth.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "core": "esp32",
4 | "f_cpu": "240000000L",
5 | "f_flash": "40000000L",
6 | "flash_mode": "qio",
7 | "mcu": "esp32",
8 | "variant": "hb-rf-eth",
9 | "hwids": [
10 | [ "0x1A86", "0x7523" ] ]
11 | },
12 | "connectivity": [
13 | "wifi",
14 | "bluetooth",
15 | "ethernet"
16 | ],
17 | "debug": {
18 | "openocd_board": "esp-wroom-32.cfg"
19 | },
20 | "frameworks": [
21 | "espidf"
22 | ],
23 | "name": "HB-RF-ETH",
24 | "upload": {
25 | "flash_size": "4MB",
26 | "maximum_ram_size": 327680,
27 | "maximum_size": 4194304,
28 | "require_upload_port": true,
29 | "speed": 921600
30 | },
31 | "url": "https://github.com/alexreinert/PCB",
32 | "vendor": "Alexander Reinert"
33 | }
34 |
--------------------------------------------------------------------------------
/build/CMakeFiles/cmake.check_cache:
--------------------------------------------------------------------------------
1 | # This file is generated by cmake for dependency checking of the CMakeCache.txt file
2 |
--------------------------------------------------------------------------------
/build_webui.py:
--------------------------------------------------------------------------------
1 | from shutil import copyfile
2 | from subprocess import check_output, CalledProcessError
3 | import sys
4 | import os
5 | import platform
6 | import subprocess
7 |
8 | Import("env")
9 |
10 | def is_tool(name):
11 | cmd = "where" if platform.system() == "Windows" else "which"
12 | try:
13 | check_output([cmd, name])
14 | return True
15 | except:
16 | return False
17 |
18 | def build_web():
19 | if is_tool("npm"):
20 | os.chdir("webui")
21 | print("Attempting to build webpage...")
22 | try:
23 | if platform.system() == "Windows":
24 | print(check_output(["npm.cmd", "run", "build"]))
25 | else:
26 | print(check_output(["npm", "run", "build"]))
27 | finally:
28 | os.chdir("..");
29 |
30 | build_web()
31 |
--------------------------------------------------------------------------------
/include/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for project header files.
3 |
4 | A header file is a file containing C declarations and macro definitions
5 | to be shared between several project source files. You request the use of a
6 | header file in your project source file (C, C++, etc) located in `src` folder
7 | by including it, with the C preprocessing directive `#include'.
8 |
9 | ```src/main.c
10 |
11 | #include "header.h"
12 |
13 | int main (void)
14 | {
15 | ...
16 | }
17 | ```
18 |
19 | Including a header file produces the same results as copying the header file
20 | into each source file that needs it. Such copying would be time-consuming
21 | and error-prone. With a header file, the related declarations appear
22 | in only one place. If they need to be changed, they can be changed in one
23 | place, and programs that include the header file will automatically use the
24 | new version when next recompiled. The header file eliminates the labor of
25 | finding and changing all the copies as well as the risk that a failure to
26 | find one copy will result in inconsistencies within a program.
27 |
28 | In C, the usual convention is to give header files names that end with `.h'.
29 | It is most portable to use only letters, digits, dashes, and underscores in
30 | header file names, and at most one dot.
31 |
32 | Read more about using header files in official GCC documentation:
33 |
34 | * Include Syntax
35 | * Include Operation
36 | * Once-Only Headers
37 | * Computed Includes
38 |
39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
40 |
--------------------------------------------------------------------------------
/include/dcf.h:
--------------------------------------------------------------------------------
1 | /*
2 | * dcf.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "systemclock.h"
21 | #include "settings.h"
22 |
23 | class DCF
24 | {
25 | public:
26 | DCF(Settings *settings, SystemClock *clk);
27 |
28 | void start(void);
29 | void stop(void);
30 | };
31 |
--------------------------------------------------------------------------------
/include/ethernet.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ethernet.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 | #include "freertos/FreeRTOS.h"
25 | #include "freertos/task.h"
26 | #include "esp_netif.h"
27 | #include "esp_eth.h"
28 | #include "esp_event.h"
29 | #include "esp_log.h"
30 | #include "driver/gpio.h"
31 | #include "sdkconfig.h"
32 | #include "settings.h"
33 |
34 | class Ethernet
35 | {
36 | private:
37 | esp_netif_config_t _netif_cfg;
38 | esp_netif_t *_eth_netif;
39 | esp_eth_handle_t _eth_handle;
40 | eth_mac_config_t _mac_config;
41 | eth_phy_config_t _phy_config;
42 | esp_eth_mac_t *_mac;
43 | esp_eth_phy_t *_phy;
44 | esp_eth_config_t _eth_config;
45 |
46 | Settings *_settings;
47 | bool _isConnected;
48 |
49 | public:
50 | Ethernet(Settings *settings);
51 |
52 | void start();
53 | void stop();
54 |
55 | void getNetworkSettings(ip4_addr_t *ip, ip4_addr_t *netmask, ip4_addr_t *gateway, ip4_addr_t *dns1, ip4_addr_t *dns2);
56 |
57 | void _handleETHEvent(esp_event_base_t event_base, int32_t event_id, void *event_data);
58 | void _handleIPEvent(esp_event_base_t event_base, int32_t event_id, void *event_data);
59 | };
--------------------------------------------------------------------------------
/include/gps.h:
--------------------------------------------------------------------------------
1 | /*
2 | * gps.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "freertos/FreeRTOS.h"
21 | #include "freertos/task.h"
22 | #include "freertos/queue.h"
23 | #include "driver/uart.h"
24 | #include "systemclock.h"
25 | #include "settings.h"
26 | #include "linereader.h"
27 |
28 | class GPS
29 | {
30 | private:
31 | Settings *_settings;
32 | SystemClock *_clk;
33 | TaskHandle_t _tHandle = NULL;
34 | QueueHandle_t _uart_queue;
35 | LineReader *_lineReader;
36 | uint64_t _nextSync = 0;
37 |
38 | public:
39 | GPS(Settings *settings, SystemClock *clk);
40 |
41 | void start(void);
42 | void stop(void);
43 |
44 | void _gpsSerialQueueHandler();
45 | void _handleLine(unsigned char *buffer, uint16_t len);
46 | };
47 |
--------------------------------------------------------------------------------
/include/hmframe.h:
--------------------------------------------------------------------------------
1 | /*
2 | * hmframe.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 |
24 | class HMFrame
25 | {
26 | public:
27 | static bool TryParse(unsigned char *buffer, uint16_t len, HMFrame *frame);
28 | static uint16_t crc(unsigned char *buffer, uint16_t len);
29 |
30 | HMFrame();
31 | uint8_t counter;
32 | uint8_t destination;
33 | uint8_t command;
34 | unsigned char *data;
35 | uint16_t data_len;
36 |
37 | uint16_t encode(unsigned char *buffer, uint16_t len, bool escaped);
38 | };
39 |
40 | typedef enum
41 | {
42 | HM_DST_HMSYSTEM = 0x00,
43 | HM_DST_TRX = 0x01,
44 | HM_DST_HMIP = 0x02,
45 | HM_DST_LLMAC = 0x03,
46 | HM_DST_COMMON = 0xfe,
47 | } hm_dst_t;
48 |
49 | typedef enum
50 | {
51 | HM_CMD_HMSYSTEM_IDENTIFY = 0x00,
52 | HM_CMD_HMSYSTEM_GET_VERSION = 0x02,
53 | HM_CMD_HMSYSTEM_CHANGE_APP = 0x03,
54 | HM_CMD_HMSYSTEM_ACK = 0x04,
55 | HM_CMD_HMSYSTEM_GET_SERIAL = 0x0b,
56 | } hm_cmd_hmsystem_t;
57 |
58 | typedef enum
59 | {
60 | HM_CMD_TRX_GET_VERSION = 0x02,
61 | HM_CMD_TRX_ACK = 0x04,
62 | HM_CMD_TRX_GET_MCU_TYPE = 0x09,
63 | HM_CMD_TRX_GET_DEFAULT_RF_ADDR = 0x10,
64 | } hm_cmd_trx_t;
65 |
66 | typedef enum
67 | {
68 | HM_CMD_HMIP_GET_DEFAULT_RF_ADDR = 0x01,
69 | HM_CMD_HMIP_ACK = 0x06,
70 | } hm_cmd_hmip_t;
71 |
72 | typedef enum
73 | {
74 | HM_CMD_LLMAC_ACK = 0x01,
75 | HM_CMD_LLMAC_GET_SERIAL = 0x07,
76 | HM_CMD_LLMAC_GET_DEFAULT_RF_ADDR = 0x08,
77 | } hm_cmd_llmac_t;
78 |
79 | typedef enum
80 | {
81 | HM_CMD_COMMON_IDENTIFY = 0x01,
82 | HM_CMD_COMMON_START_BL = 0x02,
83 | HM_CMD_COMMON_START_APP = 0x03,
84 | HM_CMD_COMMON_GET_SGTIN = 0x04,
85 | HM_CMD_COMMON_ACK = 0x05,
86 | } hm_cmd_common_t;
87 |
--------------------------------------------------------------------------------
/include/led.h:
--------------------------------------------------------------------------------
1 | /*
2 | * led.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include "driver/gpio.h"
24 | #include "driver/ledc.h"
25 | #include "settings.h"
26 |
27 | #define MAX_LED_COUNT 5
28 |
29 | typedef enum
30 | {
31 | LED_STATE_OFF = 0,
32 | LED_STATE_ON = 1,
33 | LED_STATE_BLINK = 2,
34 | LED_STATE_BLINK_INV = 3,
35 | LED_STATE_BLINK_FAST = 4,
36 | LED_STATE_BLINK_SLOW = 5,
37 | } led_state_t;
38 |
39 | class LED
40 | {
41 | private:
42 | uint8_t _state;
43 | ledc_channel_config_t _channel_conf;
44 | void _setPinState(bool enabled);
45 |
46 | public:
47 | static void start(Settings *settings);
48 | static void stop();
49 |
50 | LED(gpio_num_t pin);
51 | void setState(led_state_t state);
52 | void updatePinState();
53 | };
--------------------------------------------------------------------------------
/include/linereader.h:
--------------------------------------------------------------------------------
1 | /*
2 | * linereader.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | class LineReader
26 | {
27 | private:
28 | unsigned char _buffer[1024];
29 | std::function _processor;
30 | uint16_t _buffer_pos;
31 |
32 | public:
33 | LineReader(std::function processor);
34 |
35 | void Append(unsigned char chr);
36 | void Append(unsigned char *buffer, uint16_t len);
37 | void Flush();
38 | };
39 |
--------------------------------------------------------------------------------
/include/mdnsserver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * mdnsserver.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "settings.h"
23 |
24 | class MDns
25 | {
26 | public:
27 | void start(Settings* settings);
28 | void stop();
29 | };
--------------------------------------------------------------------------------
/include/ntpclient.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ntpclient.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "settings.h"
23 | #include "systemclock.h"
24 |
25 | class NtpClient
26 | {
27 | public:
28 | NtpClient(Settings *settings, SystemClock *clk);
29 | void start();
30 | void stop();
31 | };
32 |
--------------------------------------------------------------------------------
/include/ntpserver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ntpserver.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "lwip/opt.h"
23 | #include "lwip/inet.h"
24 | #include "lwip/udp.h"
25 | #include "lwip/priv/tcpip_priv.h"
26 | #include "systemclock.h"
27 |
28 | typedef unsigned long long tstamp;
29 |
30 | typedef struct ntp_packet
31 | {
32 | uint8_t flags;
33 | uint8_t stratum;
34 | uint8_t poll;
35 | int8_t precision;
36 | uint32_t delay;
37 | uint32_t dispersion;
38 | char ref_id[4];
39 | tstamp ref_time;
40 | tstamp orig_time;
41 | tstamp recv_time;
42 | tstamp trns_time;
43 | } ntp_packet_t;
44 |
45 | class NtpServer {
46 | private:
47 | SystemClock* _clk;
48 | udp_pcb* _pcb;
49 | QueueHandle_t _udp_queue;
50 | TaskHandle_t _tHandle = NULL;
51 |
52 | void handlePacket(pbuf *pb, ip4_addr_t addr, uint16_t port);
53 |
54 | public:
55 | NtpServer(SystemClock* clk);
56 |
57 | void start();
58 | void stop();
59 |
60 | void _udpQueueHandler();
61 | bool _udpReceivePacket(pbuf *pb, const ip_addr_t *addr, uint16_t port);
62 | };
63 |
--------------------------------------------------------------------------------
/include/pins.h:
--------------------------------------------------------------------------------
1 | /*
2 | * pins.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "driver/gpio.h"
23 | #include "soc/adc_channel.h"
24 |
25 | #define HM_RX_PIN GPIO_NUM_35
26 | #define HM_TX_PIN GPIO_NUM_2
27 |
28 | #define HM_SDA_PIN GPIO_NUM_18
29 | #define HM_SCL_PIN GPIO_NUM_5
30 |
31 | #define HM_RST_PIN GPIO_NUM_23
32 | #define HM_BTN_PIN GPIO_NUM_34
33 | #define HM_RED_PIN GPIO_NUM_15
34 | #define HM_GREEN_PIN GPIO_NUM_14
35 | #define HM_BLUE_PIN GPIO_NUM_12
36 |
37 | #define LED_STATUS_PIN GPIO_NUM_4
38 | #define LED_PWR_PIN GPIO_NUM_16
39 |
40 | #define DCF_PIN GPIO_NUM_39
41 |
42 | #define BOARD_REV_SENSE_CHANNEL ((adc_channel_t)ADC1_GPIO36_CHANNEL)
43 | #define BOARD_REV_SENSE_UNIT ADC_UNIT_1
44 |
45 | #define ETH_PHY_ADDR 0
46 | #define ETH_POWER_PIN GPIO_NUM_13
47 | #define ETH_MDC_PIN GPIO_NUM_32
48 | #define ETH_MDIO_PIN GPIO_NUM_33
49 |
--------------------------------------------------------------------------------
/include/pushbuttonhandler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * pushbuttonhandler.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "esp_log.h"
23 | #include "led.h"
24 | #include "settings.h"
25 | #include "pins.h"
26 |
27 | class PushButtonHandler {
28 | public:
29 | PushButtonHandler();
30 | void handleStartupFactoryReset(LED *powerLED, LED *statusLED, Settings *settings);
31 | };
32 |
--------------------------------------------------------------------------------
/include/radiomoduleconnector.h:
--------------------------------------------------------------------------------
1 | /*
2 | * radiomoduleconnector.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "freertos/FreeRTOS.h"
23 | #include "freertos/task.h"
24 | #include "freertos/queue.h"
25 | #include "driver/uart.h"
26 | #include "led.h"
27 | #include "streamparser.h"
28 | #include
29 | #define _Atomic(X) std::atomic
30 |
31 | class FrameHandler
32 | {
33 | public:
34 | virtual void handleFrame(unsigned char *buffer, uint16_t len) = 0;
35 | };
36 |
37 | class RadioModuleConnector
38 | {
39 | private:
40 | LED *_redLED;
41 | LED *_greenLED;
42 | LED *_blueLED;
43 | StreamParser *_streamParser;
44 | std::atomic _frameHandler = ATOMIC_VAR_INIT(0);
45 | QueueHandle_t _uart_queue;
46 | TaskHandle_t _tHandle = NULL;
47 |
48 | void _handleFrame(unsigned char *buffer, uint16_t len);
49 |
50 | public:
51 | RadioModuleConnector(LED *redLED, LED *greenLed, LED *blueLed);
52 |
53 | void start();
54 | void stop();
55 |
56 | void setLED(bool red, bool green, bool blue);
57 |
58 | void setFrameHandler(FrameHandler *handler, bool decodeEscaped);
59 |
60 | void resetModule();
61 |
62 | void sendFrame(unsigned char *buffer, uint16_t len);
63 |
64 | void _serialQueueHandler();
65 | };
66 |
--------------------------------------------------------------------------------
/include/radiomoduledetector.h:
--------------------------------------------------------------------------------
1 | /*
2 | * radiomoduledetector.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "radiomoduleconnector.h"
23 | #include "radiomoduledetector_utils.h"
24 |
25 | typedef enum
26 | {
27 | RADIO_MODULE_NONE = 0,
28 | RADIO_MODULE_HMIP_RFUSB = 1,
29 | RADIO_MODULE_HM_MOD_RPI_PCB = 3,
30 | RADIO_MODULE_RPI_RF_MOD = 4,
31 | } radio_module_type_t;
32 |
33 | typedef enum
34 | {
35 | DETECT_STATE_START_BL = 0,
36 | DETECT_STATE_START_APP = 10,
37 |
38 | DETECT_STATE_GET_MCU_TYPE = 20,
39 | DETECT_STATE_GET_VERSION = 30,
40 | DETECT_STATE_GET_HMIP_RF_ADDRESS = 40,
41 | DETECT_STATE_GET_SGTIN = 50,
42 | DETECT_STATE_GET_BIDCOS_RF_ADDRESS = 60,
43 | DETECT_STATE_GET_SERIAL = 70,
44 |
45 | DETECT_STATE_LEGACY_GET_VERSION = 31,
46 | DETECT_STATE_LEGACY_GET_BIDCOS_RF_ADDRESS = 61,
47 | DETECT_STATE_LEGACY_GET_SERIAL = 71,
48 |
49 | DETECT_STATE_FINISHED = 255,
50 | } detect_radio_module_state_t;
51 |
52 | class RadioModuleDetector : private FrameHandler
53 | {
54 | private:
55 | void handleFrame(unsigned char *buffer, uint16_t len);
56 | void sendFrame(uint8_t counter, uint8_t destination, uint8_t command, unsigned char *data, uint data_len);
57 |
58 | char _serial[11] = {0};
59 | uint32_t _bidCosRadioMAC = 0;
60 | uint32_t _hmIPRadioMAC = 0;
61 | char _sgtin[25] = {0};
62 | uint8_t _firmwareVersion[3] = {0};
63 | radio_module_type_t _radioModuleType = RADIO_MODULE_NONE;
64 |
65 | int _detectState;
66 | int _detectRetryCount;
67 | int _detectMsgCounter;
68 | SemaphoreHandle_t _detectWaitFrameDataSemaphore;
69 | RadioModuleConnector *_radioModuleConnector;
70 |
71 | public:
72 | void detectRadioModule(RadioModuleConnector *radioModuleConnector);
73 | const char *getSerial();
74 | uint32_t getBidCosRadioMAC();
75 | uint32_t getHmIPRadioMAC();
76 | const char *getSGTIN();
77 | const uint8_t *getFirmwareVersion();
78 | radio_module_type_t getRadioModuleType();
79 | };
--------------------------------------------------------------------------------
/include/radiomoduledetector_utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * radiomoduledetector_utils.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "esp_log.h"
21 |
22 | #define sem_take(__sem, __timeout) (xSemaphoreTake(__sem, __timeout * 1000 / portTICK_PERIOD_MS) == pdTRUE)
23 | #define sem_give(__sem) xSemaphoreGive(__sem)
24 | #define sem_init(__sem) __sem = xSemaphoreCreateBinary();
25 |
26 | #define log_frame(__text, __buffer, __len) \
27 | ESP_LOGD(TAG, __text); \
28 | ESP_LOG_BUFFER_HEX_LEVEL(TAG, __buffer, __len, ESP_LOG_DEBUG);
29 |
30 |
--------------------------------------------------------------------------------
/include/rawuartudplistener.h:
--------------------------------------------------------------------------------
1 | /*
2 | * rawuartudplistener.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "lwip/opt.h"
23 | #include "lwip/inet.h"
24 | #include "lwip/udp.h"
25 | #include "lwip/priv/tcpip_priv.h"
26 | #include
27 | #define _Atomic(X) std::atomic
28 | #include "radiomoduleconnector.h"
29 |
30 | class RawUartUdpListener : FrameHandler
31 | {
32 | private:
33 | RadioModuleConnector *_radioModuleConnector;
34 | std::atomic _remoteAddress;
35 | std::atomic _remotePort;
36 | std::atomic _connectionStarted;
37 | std::atomic _counter;
38 | std::atomic _endpointConnectionIdentifier;
39 | uint64_t _lastReceivedKeepAlive;
40 | udp_pcb *_pcb;
41 | QueueHandle_t _udp_queue;
42 | TaskHandle_t _tHandle = NULL;
43 |
44 | void handlePacket(pbuf *pb, ip4_addr_t addr, uint16_t port);
45 | void sendMessage(unsigned char command, unsigned char *buffer, size_t len);
46 |
47 | public:
48 | RawUartUdpListener(RadioModuleConnector *radioModuleConnector);
49 |
50 | void handleFrame(unsigned char *buffer, uint16_t len);
51 | void handleEvent();
52 |
53 | ip4_addr_t getConnectedRemoteAddress();
54 |
55 | void start();
56 | void stop();
57 |
58 | void _udpQueueHandler();
59 | bool _udpReceivePacket(pbuf *pb, const ip_addr_t *addr, uint16_t port);
60 | };
61 |
--------------------------------------------------------------------------------
/include/rtc.h:
--------------------------------------------------------------------------------
1 | /*
2 | * rtc.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 | #include "driver/i2c.h"
25 | #include "pins.h"
26 |
27 | class Rtc
28 | {
29 | protected:
30 | Rtc(uint8_t address, uint8_t reg_start);
31 | i2c_port_t _i2c_port;
32 | const uint8_t _address;
33 | const uint8_t _reg_start;
34 |
35 | public:
36 | static Rtc* detect();
37 |
38 | virtual bool begin();
39 | virtual ~Rtc();
40 | struct timeval GetTime();
41 | void SetTime(struct timeval now);
42 | };
43 |
44 | class RtcDS3231 : public Rtc
45 | {
46 | public:
47 | RtcDS3231();
48 | };
49 |
50 | class RtcRX8130 : public Rtc
51 | {
52 | public:
53 | RtcRX8130();
54 | bool begin();
55 | };
56 |
--------------------------------------------------------------------------------
/include/settings.h:
--------------------------------------------------------------------------------
1 | /*
2 | * settings.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | typedef enum
26 | {
27 | TIMESOURCE_NTP = 0,
28 | TIMESOURCE_DCF = 1,
29 | TIMESOURCE_GPS = 2
30 | } timesource_t;
31 |
32 | class Settings
33 | {
34 | private:
35 | char _adminPassword[33] = {0};
36 |
37 | char _hostname[33] = {0};
38 | bool _useDHCP;
39 | ip4_addr_t _localIP;
40 | ip4_addr_t _netmask;
41 | ip4_addr_t _gateway;
42 | ip4_addr_t _dns1;
43 | ip4_addr_t _dns2;
44 |
45 | int _timesource;
46 |
47 | int _dcfOffset;
48 |
49 | int _gpsBaudrate;
50 |
51 | char _ntpServer[65] = {0};
52 |
53 | int _ledBrightness;
54 |
55 | public:
56 | Settings();
57 | void load();
58 | void save();
59 | void clear();
60 |
61 | char *getAdminPassword();
62 | void setAdminPassword(char* password);
63 |
64 | char *getHostname();
65 | bool getUseDHCP();
66 | ip4_addr_t getLocalIP();
67 | ip4_addr_t getNetmask();
68 | ip4_addr_t getGateway();
69 | ip4_addr_t getDns1();
70 | ip4_addr_t getDns2();
71 |
72 | void setNetworkSettings(char *hostname, bool useDHCP, ip4_addr_t localIP, ip4_addr_t netmask, ip4_addr_t gateway, ip4_addr_t dns1, ip4_addr_t dns2);
73 |
74 | timesource_t getTimesource();
75 | void setTimesource(timesource_t timesource);
76 |
77 | int getDcfOffset();
78 | void setDcfOffset(int offset);
79 |
80 | int getGpsBaudrate();
81 | void setGpsBaudrate(int baudrate);
82 |
83 | char *getNtpServer();
84 | void setNtpServer(char *ntpServer);
85 |
86 | int getLEDBrightness();
87 | void setLEDBrightness(int brightness);
88 | };
--------------------------------------------------------------------------------
/include/streamparser.h:
--------------------------------------------------------------------------------
1 | /*
2 | * streamparser.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | typedef enum
26 | {
27 | NO_DATA,
28 | RECEIVE_LENGTH_HIGH_BYTE,
29 | RECEIVE_LENGTH_LOW_BYTE,
30 | RECEIVE_FRAME_DATA,
31 | FRAME_COMPLETE
32 | } state_t;
33 |
34 | class StreamParser
35 | {
36 | private:
37 | unsigned char _buffer[2048];
38 | uint16_t _bufferPos;
39 | uint16_t _framePos;
40 | uint16_t _frameLength;
41 | state_t _state;
42 | bool _isEscaped;
43 | bool _decodeEscaped;
44 | std::function _processor;
45 |
46 | public:
47 | StreamParser(bool decodeEscaped, std::function processor);
48 |
49 | void append(unsigned char chr);
50 | void append(unsigned char *buffer, uint16_t len);
51 | void flush();
52 |
53 | bool getDecodeEscaped();
54 | void setDecodeEscaped(bool decodeEscaped);
55 | };
56 |
--------------------------------------------------------------------------------
/include/sysinfo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * sysinfo.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | typedef enum
23 | {
24 | BOARD_TYPE_REV_1_8_PUB = 0,
25 | BOARD_TYPE_REV_1_8_SK = 1,
26 | BOARD_TYPE_REV_1_10_PUB = 2,
27 | BOARD_TYPE_REV_1_10_SK = 3,
28 | BOARD_TYPE_UNKNOWN = 255
29 | } board_type_t;
30 |
31 | class SysInfo
32 | {
33 | public:
34 | SysInfo();
35 | double getCpuUsage();
36 | double getMemoryUsage();
37 | const char* getCurrentVersion();
38 | const char *getSerialNumber();
39 | board_type_t getBoardType();
40 | };
41 |
--------------------------------------------------------------------------------
/include/systemclock.h:
--------------------------------------------------------------------------------
1 | /*
2 | * systemclock.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "freertos/FreeRTOS.h"
23 | #include "freertos/task.h"
24 | #include "rtc.h"
25 |
26 | class SystemClock
27 | {
28 | private:
29 | Rtc *_rtc;
30 | struct timeval _lastSyncTime = { .tv_sec = 0, .tv_usec = 0 };
31 | TaskHandle_t _tHandle = NULL;
32 |
33 | public:
34 | SystemClock(Rtc *rtc);
35 |
36 | void start();
37 | void stop();
38 |
39 | void setTime(struct timeval *tv);
40 | struct timeval getTime();
41 | struct timeval getLastSyncTime();
42 | struct tm getLocalTime();
43 | };
44 |
--------------------------------------------------------------------------------
/include/udphelper.h:
--------------------------------------------------------------------------------
1 | /*
2 | * udphelper.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "lwip/opt.h"
23 | #include "lwip/inet.h"
24 | #include "lwip/udp.h"
25 | #include "lwip/priv/tcpip_priv.h"
26 |
27 | typedef struct
28 | {
29 | struct tcpip_api_call_data call;
30 | udp_pcb *pcb;
31 | const ip_addr_t *addr;
32 | uint16_t port;
33 | struct pbuf *pb;
34 | err_t err;
35 | } udp_api_call_t;
36 |
37 | typedef struct
38 | {
39 | pbuf *pb;
40 | ip4_addr_t addr;
41 | uint16_t port;
42 | } udp_event_t;
43 |
44 | static err_t _udp_remove_api(struct tcpip_api_call_data *api_call_msg)
45 | {
46 | udp_api_call_t *msg = (udp_api_call_t *)api_call_msg;
47 | msg->err = 0;
48 | udp_remove(msg->pcb);
49 | return msg->err;
50 | }
51 |
52 | static void _udp_remove(struct udp_pcb *pcb)
53 | {
54 | udp_api_call_t msg;
55 | msg.pcb = pcb;
56 | tcpip_api_call(_udp_remove_api, (struct tcpip_api_call_data *)&msg);
57 | }
58 |
59 | static err_t _udp_bind_api(struct tcpip_api_call_data *api_call_msg)
60 | {
61 | udp_api_call_t *msg = (udp_api_call_t *)api_call_msg;
62 | msg->err = udp_bind(msg->pcb, msg->addr, msg->port);
63 | return msg->err;
64 | }
65 |
66 | static err_t _udp_bind(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port)
67 | {
68 | udp_api_call_t msg;
69 | msg.pcb = pcb;
70 | msg.addr = addr;
71 | msg.port = port;
72 | tcpip_api_call(_udp_bind_api, (struct tcpip_api_call_data *)&msg);
73 | return msg.err;
74 | }
75 |
76 | static err_t _udp_disconnect_api(struct tcpip_api_call_data *api_call_msg)
77 | {
78 | udp_api_call_t *msg = (udp_api_call_t *)api_call_msg;
79 | msg->err = 0;
80 | udp_disconnect(msg->pcb);
81 | return msg->err;
82 | }
83 |
84 | static void _udp_disconnect(struct udp_pcb *pcb)
85 | {
86 | udp_api_call_t msg;
87 | msg.pcb = pcb;
88 | tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call_data *)&msg);
89 | }
90 |
91 | static err_t _udp_sendto_api(struct tcpip_api_call_data *api_call_msg)
92 | {
93 | udp_api_call_t *msg = (udp_api_call_t *)api_call_msg;
94 | msg->err = udp_sendto(msg->pcb, msg->pb, msg->addr, msg->port);
95 | return msg->err;
96 | }
97 |
98 | static err_t _udp_sendto(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *addr, u16_t port)
99 | {
100 | udp_api_call_t msg;
101 | msg.pcb = pcb;
102 | msg.addr = addr;
103 | msg.port = port;
104 | msg.pb = pb;
105 | tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call_data *)&msg);
106 | return msg.err;
107 | }
108 |
--------------------------------------------------------------------------------
/include/updatecheck.h:
--------------------------------------------------------------------------------
1 | /*
2 | * updatecheck.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 |
22 | #include "freertos/FreeRTOS.h"
23 | #include "freertos/task.h"
24 | #include "sysinfo.h"
25 | #include "led.h"
26 |
27 | class UpdateCheck
28 | {
29 | private:
30 | SysInfo* _sysInfo;
31 | LED *_statusLED;
32 | TaskHandle_t _tHandle = NULL;
33 | void _updateLatestVersion();
34 | char _latestVersion[33] = "n/a";
35 |
36 | public:
37 | UpdateCheck(SysInfo* sysInfo, LED *statusLED);
38 | void start();
39 | void stop();
40 |
41 | const char* getLatestVersion();
42 |
43 | void _taskFunc();
44 | };
--------------------------------------------------------------------------------
/include/webui.h:
--------------------------------------------------------------------------------
1 | /*
2 | * webui.h is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #pragma once
21 | #include
22 | #include "settings.h"
23 | #include "led.h"
24 | #include "updatecheck.h"
25 | #include "radiomoduleconnector.h"
26 | #include "radiomoduledetector.h"
27 | #include "rawuartudplistener.h"
28 | #include "ethernet.h"
29 | #include "esp_http_server.h"
30 |
31 | class WebUI
32 | {
33 | private:
34 | httpd_handle_t _httpd_handle;
35 |
36 | public:
37 | WebUI(Settings *settings, LED *statusLED, SysInfo *sysInfo, UpdateCheck *updateCheck, Ethernet *ethernet, RawUartUdpListener *rawUartUdpListener, RadioModuleConnector *radioModuleConnector, RadioModuleDetector *radioModuleDetector);
38 | void start();
39 | void stop();
40 | };
--------------------------------------------------------------------------------
/lib/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for project specific (private) libraries.
3 | PlatformIO will compile them to static libraries and link into executable file.
4 |
5 | The source code of each library should be placed in a an own separate directory
6 | ("lib/your_library_name/[here are source files]").
7 |
8 | For example, see a structure of the following two libraries `Foo` and `Bar`:
9 |
10 | |--lib
11 | | |
12 | | |--Bar
13 | | | |--docs
14 | | | |--examples
15 | | | |--src
16 | | | |- Bar.c
17 | | | |- Bar.h
18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19 | | |
20 | | |--Foo
21 | | | |- Foo.c
22 | | | |- Foo.h
23 | | |
24 | | |- README --> THIS FILE
25 | |
26 | |- platformio.ini
27 | |--src
28 | |- main.c
29 |
30 | and a contents of `src/main.c`:
31 | ```
32 | #include
33 | #include
34 |
35 | int main (void)
36 | {
37 | ...
38 | }
39 |
40 | ```
41 |
42 | PlatformIO Library Dependency Finder will find automatically dependent
43 | libraries scanning project source files.
44 |
45 | More information about PlatformIO Library Dependency Finder
46 | - https://docs.platformio.org/page/librarymanager/ldf.html
47 |
--------------------------------------------------------------------------------
/partitions.csv:
--------------------------------------------------------------------------------
1 | # ESP-IDF Partition Table
2 | # Name, Type, SubType, Offset, Size, Flags
3 | nvs, data, nvs, , 0x4000,
4 | otadata, data, ota, , 0x2000,
5 | phy_init, data, phy, , 0x1000,
6 | ota_0, app, ota_0, , 0x1d0000,
7 | ota_1, app, ota_1, , 0x1d0000,
8 | spiffs, data, spiffs, , 0x50000
9 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env]
12 |
13 | [env:hb-rf-eth]
14 | platform = espressif32
15 | board = hb-rf-eth
16 | framework = espidf
17 | monitor_speed = 115200
18 | monitor_filters = esp32_exception_decoder
19 | board_build.embed_files =
20 | webui/dist/main.1e43358e.css.gz
21 | webui/dist/main.1e43358e.js.gz
22 | webui/dist/index.html.gz
23 | webui/dist/favicon.26242483.ico.gz
24 | board_build.partitions = partitions.csv
25 | extra_scripts =
26 | pre:append_version_to_progname.py
27 | pre:build_webui.py
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file was automatically generated for projects
2 | # without default 'CMakeLists.txt' file.
3 |
4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
5 |
6 | idf_component_register(SRCS ${app_sources})
7 |
8 | target_add_binary_data(${COMPONENT_TARGET} "../webui/dist/main.1e43358e.css.gz" BINARY)
9 | target_add_binary_data(${COMPONENT_TARGET} "../webui/dist/main.1e43358e.js.gz" BINARY)
10 | target_add_binary_data(${COMPONENT_TARGET} "../webui/dist/index.html.gz" BINARY)
11 | target_add_binary_data(${COMPONENT_TARGET} "../webui/dist/favicon.26242483.ico.gz" BINARY)
12 |
--------------------------------------------------------------------------------
/src/dcf.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * dcf.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "dcf.h"
21 | #include
22 | #include "esp_log.h"
23 | #include "freertos/FreeRTOS.h"
24 | #include "freertos/task.h"
25 | #include "freertos/queue.h"
26 | #include "driver/gpio.h"
27 | #include "pins.h"
28 |
29 | static SystemClock *_clk;
30 | static Settings *_settings;
31 |
32 | static uint64_t _buffer = 0;
33 | static uint8_t _bufferPos = 0;
34 |
35 | static int64_t _previousSecondMark = 0;
36 | static int64_t _secondMark = 0;
37 |
38 | static QueueHandle_t _flank_queue;
39 | static TaskHandle_t _queueHandlerTask;
40 |
41 | static const char *TAG = "DCF";
42 |
43 | typedef struct
44 | {
45 | int64_t flankTime;
46 | int state;
47 | } flank_event_t;
48 |
49 | inline uint8_t bcd2bin(uint8_t val)
50 | {
51 | return val - 6 * (val >> 4);
52 | }
53 |
54 | DCF::DCF(Settings *settings, SystemClock *clk)
55 | {
56 | _settings = settings;
57 | _clk = clk;
58 | }
59 |
60 | bool checkParity(uint8_t start, uint8_t end)
61 | {
62 | int parity = 0;
63 |
64 | for (int pos = start; pos <= end; pos++)
65 | {
66 | parity ^= (int)((_buffer >> pos) & 1);
67 | }
68 |
69 | return parity == 0;
70 | }
71 |
72 | int is_leap(unsigned int y)
73 | {
74 | return (y % 4) == 0 && ((y % 100) != 0 || ((y + 1900) % 400) == 0);
75 | }
76 |
77 | time_t dcf2epoch(struct tm *dcf_tm, uint8_t tz)
78 | {
79 | static const unsigned ndays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
80 |
81 | time_t res = 0;
82 | int i;
83 |
84 | for (i = 70; i < dcf_tm->tm_year; ++i)
85 | {
86 | res += is_leap(i) ? 366 : 365;
87 | }
88 |
89 | for (i = 0; i < dcf_tm->tm_mon; ++i)
90 | {
91 | res += ndays[i];
92 | if (i == 1 && is_leap(dcf_tm->tm_year))
93 | {
94 | res++;
95 | }
96 | }
97 |
98 | res += dcf_tm->tm_mday - 1;
99 | res *= 24;
100 | res += dcf_tm->tm_hour;
101 | res -= tz ^ 3;
102 | res *= 60;
103 | res += dcf_tm->tm_min;
104 | res *= 60;
105 |
106 | return res;
107 | }
108 |
109 | static void IRAM_ATTR onPinChange(void *arg)
110 | {
111 | int64_t flankTime = esp_timer_get_time();
112 | int state = gpio_get_level(DCF_PIN);
113 |
114 | flank_event_t event;
115 | event.flankTime = flankTime;
116 | event.state = state;
117 |
118 | BaseType_t xHigherPriorityTaskWokenByPost = pdFALSE;
119 | xQueueSendFromISR(_flank_queue, &event, &xHigherPriorityTaskWokenByPost);
120 |
121 | if (xHigherPriorityTaskWokenByPost)
122 | {
123 | portYIELD_FROM_ISR();
124 | }
125 | }
126 |
127 | static void handlePinChange(int64_t flankTime, int state)
128 | {
129 | if (state)
130 | {
131 | // end of pulse
132 | int64_t secondLength = _secondMark - _previousSecondMark;
133 | int64_t pulseLength = flankTime - _secondMark;
134 |
135 | uint64_t pulseValue;
136 | if (pulseLength > 80000 && pulseLength < 135000)
137 | {
138 | pulseValue = 0;
139 | }
140 | else if (pulseLength > 180000 && pulseLength < 235000)
141 | {
142 | pulseValue = 1;
143 | }
144 | else
145 | {
146 | ESP_LOGE(TAG, "Invalid pulse: %llu %llu", secondLength / 1000ULL, pulseLength / 1000ULL);
147 | return; // invalid pulse length
148 | }
149 |
150 | if (secondLength > 970000 && secondLength < 1035000)
151 | {
152 | if (_bufferPos < 59)
153 | {
154 | _bufferPos++;
155 | _buffer |= (pulseValue << _bufferPos);
156 | }
157 | }
158 | else if (secondLength > 1970000 && secondLength < 2035000)
159 | {
160 | uint8_t timezone = (uint8_t)((_buffer >> 17) & 3);
161 |
162 | if (_bufferPos < 58 || _bufferPos > 59 || (int)(_buffer & 1) != 0 || (int)((_buffer >> 20) & 1) != 1 || timezone == 0 || timezone == 3 || !checkParity(21, 28) || !checkParity(29, 35) || !checkParity(36, 58))
163 | {
164 | ESP_LOGE(TAG, "Received invalid frame");
165 | }
166 | else
167 | {
168 | struct tm dcf_tm;
169 | dcf_tm.tm_year = 100 + bcd2bin((int)((_buffer >> 50) & 0xff));
170 | dcf_tm.tm_mon = bcd2bin((int)((_buffer >> 45) & 0x1f)) - 1;
171 | dcf_tm.tm_mday = bcd2bin((int)((_buffer >> 36) & 0x3f));
172 | dcf_tm.tm_hour = bcd2bin((int)((_buffer >> 29) & 0x3f));
173 | dcf_tm.tm_min = bcd2bin((int)((_buffer >> 21) & 0x7f));
174 | dcf_tm.tm_sec = 0;
175 |
176 | struct timeval tv;
177 | tv.tv_sec = dcf2epoch(&dcf_tm, timezone);
178 | tv.tv_usec = esp_timer_get_time() - _secondMark + _settings->getDcfOffset();
179 |
180 | ESP_LOGI(TAG, "Updated time to %02d-%02d-%02d %02d:%02d:%02d.%06ld %s", dcf_tm.tm_year + 1900, dcf_tm.tm_mon + 1, dcf_tm.tm_mday, dcf_tm.tm_hour, dcf_tm.tm_min, dcf_tm.tm_sec, tv.tv_usec, timezone == 2 ? "CET" : "CEST");
181 | _clk->setTime(&tv);
182 | }
183 |
184 | _buffer = pulseValue;
185 | _bufferPos = 0;
186 | }
187 | else
188 | {
189 | ESP_LOGE(TAG, "Invalid second: %llu %llu", secondLength / 1000ULL, pulseLength / 1000ULL);
190 | return; // invalid second length
191 | }
192 | }
193 | else
194 | {
195 | // start of pulse
196 | _previousSecondMark = _secondMark;
197 | _secondMark = flankTime;
198 | }
199 | }
200 |
201 | static void flankEventQueueHandler(void *arg)
202 | {
203 | flank_event_t event;
204 |
205 | for (;;)
206 | {
207 | if (xQueueReceive(_flank_queue, &event, portMAX_DELAY) == pdTRUE)
208 | {
209 | handlePinChange(event.flankTime, event.state);
210 | }
211 | }
212 |
213 | vTaskDelete(NULL);
214 | }
215 |
216 | void DCF::start()
217 | {
218 | _flank_queue = xQueueCreate(8, sizeof(flank_event_t));
219 | xTaskCreate(flankEventQueueHandler, "DFC_FlankEvent_QueueHandler", 4096, NULL, 17, &_queueHandlerTask);
220 |
221 | gpio_config_t io_conf;
222 | io_conf.intr_type = GPIO_INTR_ANYEDGE;
223 | io_conf.mode = GPIO_MODE_INPUT;
224 | io_conf.pin_bit_mask = 1ULL << DCF_PIN;
225 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
226 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
227 | gpio_config(&io_conf);
228 |
229 | gpio_install_isr_service(0);
230 | gpio_isr_handler_add(DCF_PIN, onPinChange, NULL);
231 | }
232 |
233 | void DCF::stop()
234 | {
235 | gpio_isr_handler_remove(DCF_PIN);
236 | xQueueReset(_flank_queue);
237 | vTaskDelete(_queueHandlerTask);
238 | }
239 |
--------------------------------------------------------------------------------
/src/ethernet.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * ethernet.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "ethernet.h"
21 | #include "pins.h"
22 |
23 | static const char *TAG = "Ethernet";
24 |
25 | void _handleETHEvent(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
26 | {
27 | reinterpret_cast(arg)->_handleETHEvent(event_base, event_id, event_data);
28 | }
29 |
30 | void _handleIPEvent(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
31 | {
32 | reinterpret_cast(arg)->_handleIPEvent(event_base, event_id, event_data);
33 | }
34 |
35 | Ethernet::Ethernet(Settings *settings) : _settings(settings), _isConnected(false)
36 | {
37 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_init());
38 |
39 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_loop_create_default());
40 |
41 | _netif_cfg = ESP_NETIF_DEFAULT_ETH();
42 | _eth_netif = esp_netif_new(&_netif_cfg);
43 |
44 | if (settings->getUseDHCP())
45 | {
46 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_start(_eth_netif));
47 | }
48 | else
49 | {
50 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcpc_stop(_eth_netif));
51 |
52 | esp_netif_ip_info_t ipInfo;
53 | ipInfo.ip.addr = settings->getLocalIP().addr;
54 | ipInfo.netmask.addr = settings->getNetmask().addr;
55 | ipInfo.gw.addr = settings->getGateway().addr;
56 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_ip_info(_eth_netif, &ipInfo));
57 |
58 | esp_netif_dns_info_t dnsInfo;
59 | dnsInfo.ip.type = ESP_IPADDR_TYPE_V4;
60 | dnsInfo.ip.u_addr.ip4.addr = settings->getDns1().addr;
61 | if (dnsInfo.ip.u_addr.ip4.addr != IPADDR_ANY && dnsInfo.ip.u_addr.ip4.addr != IPADDR_NONE)
62 | {
63 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_dns_info(_eth_netif, ESP_NETIF_DNS_MAIN, &dnsInfo));
64 | }
65 |
66 | dnsInfo.ip.u_addr.ip4.addr = settings->getDns2().addr;
67 | if (dnsInfo.ip.u_addr.ip4.addr != IPADDR_ANY && dnsInfo.ip.u_addr.ip4.addr != IPADDR_NONE)
68 | {
69 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_dns_info(_eth_netif, ESP_NETIF_DNS_BACKUP, &dnsInfo));
70 | }
71 | }
72 |
73 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_eth_set_default_handlers(_eth_netif));
74 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &::_handleETHEvent, (void *)this));
75 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &::_handleIPEvent, (void *)this));
76 |
77 | _phy_config = ETH_PHY_DEFAULT_CONFIG();
78 | _phy_config.phy_addr = ETH_PHY_ADDR;
79 | _phy_config.reset_gpio_num = ETH_POWER_PIN;
80 | _phy = esp_eth_phy_new_lan8720(&_phy_config);
81 |
82 | _mac_config = ETH_MAC_DEFAULT_CONFIG();
83 | _mac_config.smi_mdc_gpio_num = ETH_MDC_PIN;
84 | _mac_config.smi_mdio_gpio_num = ETH_MDIO_PIN;
85 | _mac = esp_eth_mac_new_esp32(&_mac_config);
86 |
87 | _eth_config = ETH_DEFAULT_CONFIG(_mac, _phy);
88 | _eth_handle = NULL;
89 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_eth_driver_install(&_eth_config, &_eth_handle));
90 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_attach(_eth_netif, esp_eth_new_netif_glue(_eth_handle)));
91 | }
92 |
93 | void Ethernet::start()
94 | {
95 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_eth_start(_eth_handle));
96 | }
97 |
98 | void Ethernet::stop()
99 | {
100 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_eth_stop(_eth_handle));
101 | }
102 |
103 | void Ethernet::getNetworkSettings(ip4_addr_t *ip, ip4_addr_t *netmask, ip4_addr_t *gateway, ip4_addr_t *dns1, ip4_addr_t *dns2)
104 | {
105 | if (_isConnected)
106 | {
107 | esp_netif_ip_info_t ipInfo;
108 | esp_netif_get_ip_info(_eth_netif, &ipInfo);
109 | ip->addr = ipInfo.ip.addr;
110 | netmask->addr = ipInfo.netmask.addr;
111 | gateway->addr = ipInfo.gw.addr;
112 |
113 | esp_netif_dns_info_t dnsInfo;
114 | esp_netif_get_dns_info(_eth_netif, ESP_NETIF_DNS_MAIN, &dnsInfo);
115 | dns1->addr = dnsInfo.ip.u_addr.ip4.addr;
116 | esp_netif_get_dns_info(_eth_netif, ESP_NETIF_DNS_BACKUP, &dnsInfo);
117 | dns2->addr = dnsInfo.ip.u_addr.ip4.addr;
118 | }
119 | else
120 | {
121 | ip->addr = 0;
122 | netmask->addr = 0;
123 | gateway->addr = 0;
124 | dns1->addr = 0;
125 | dns2->addr = 0;
126 | }
127 | }
128 |
129 | void Ethernet::_handleETHEvent(esp_event_base_t event_base, int32_t event_id, void *event_data)
130 | {
131 | esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
132 | uint8_t mac_addr[6] = {0};
133 |
134 | switch (event_id)
135 | {
136 | case ETHERNET_EVENT_CONNECTED:
137 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr));
138 | ESP_LOGI(TAG, "Link Up");
139 | ESP_LOGI(TAG, "HW Addr %02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
140 | break;
141 | case ETHERNET_EVENT_DISCONNECTED:
142 | _isConnected = false;
143 | ESP_LOGI(TAG, "Link Down");
144 | break;
145 | case ETHERNET_EVENT_START:
146 | ESP_LOGI(TAG, "Started");
147 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_hostname(_eth_netif, _settings->getHostname()));
148 | break;
149 | case ETHERNET_EVENT_STOP:
150 | _isConnected = false;
151 | ESP_LOGI(TAG, "Stopped");
152 | break;
153 | default:
154 | break;
155 | }
156 | }
157 |
158 | void Ethernet::_handleIPEvent(esp_event_base_t event_base, int32_t event_id, void *event_data)
159 | {
160 | ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
161 | const esp_netif_ip_info_t *ip_info = &event->ip_info;
162 |
163 | _isConnected = true;
164 | ESP_LOGI(TAG, "IPv4: " IPSTR, IP2STR(&ip_info->ip));
165 | }
166 |
--------------------------------------------------------------------------------
/src/gps.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * gps.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "GPS.h"
21 | #include "pins.h"
22 | #include "esp_log.h"
23 | #include "string.h"
24 |
25 | void gpsSerialQueueHandlerTask(void *parameter)
26 | {
27 | ((GPS *)parameter)->_gpsSerialQueueHandler();
28 | }
29 |
30 | GPS::GPS(Settings *settings, SystemClock *clk) : _settings(settings), _clk(clk)
31 | {
32 | uart_config_t uart_config = {
33 | .baud_rate = _settings->getGpsBaudrate(),
34 | .data_bits = UART_DATA_8_BITS,
35 | .parity = UART_PARITY_DISABLE,
36 | .stop_bits = UART_STOP_BITS_1,
37 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
38 | .rx_flow_ctrl_thresh = 0,
39 | .use_ref_tick = false};
40 | uart_param_config(UART_NUM_2, &uart_config);
41 | uart_set_pin(UART_NUM_2, GPIO_NUM_0, DCF_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
42 |
43 | using namespace std::placeholders;
44 | _lineReader = new LineReader(std::bind(&GPS::_handleLine, this, _1, _2));
45 | }
46 |
47 | void GPS::start()
48 | {
49 | uart_driver_install(UART_NUM_2, UART_FIFO_LEN * 2, 0, 20, &_uart_queue, 0);
50 | xTaskCreate(gpsSerialQueueHandlerTask, "GPS_UART_QueueHandler", 4096, this, 15, &_tHandle);
51 | }
52 |
53 | void GPS::stop()
54 | {
55 | uart_driver_delete(UART_NUM_2);
56 | vTaskDelete(_tHandle);
57 | }
58 |
59 | void GPS::_gpsSerialQueueHandler()
60 | {
61 | uart_event_t event;
62 | uint8_t *buffer = (uint8_t *)malloc(UART_FIFO_LEN);
63 |
64 | uart_flush_input(UART_NUM_2);
65 |
66 | for (;;)
67 | {
68 | if (xQueueReceive(_uart_queue, (void *)&event, (portTickType)portMAX_DELAY))
69 | {
70 | switch (event.type)
71 | {
72 | case UART_DATA:
73 | uart_read_bytes(UART_NUM_2, buffer, event.size, portMAX_DELAY);
74 | _lineReader->Append(buffer, event.size);
75 | break;
76 | case UART_FIFO_OVF:
77 | case UART_BUFFER_FULL:
78 | uart_flush_input(UART_NUM_2);
79 | xQueueReset(_uart_queue);
80 | _lineReader->Flush();
81 | break;
82 | case UART_BREAK:
83 | case UART_PARITY_ERR:
84 | case UART_FRAME_ERR:
85 | _lineReader->Flush();
86 | break;
87 | default:
88 | break;
89 | }
90 | }
91 | }
92 |
93 | free(buffer);
94 | buffer = NULL;
95 | vTaskDelete(NULL);
96 | }
97 |
98 | bool parseRMCTime(unsigned char *buffer, uint16_t len, timeval *tv)
99 | {
100 | int fieldIndex = 0;
101 | int fieldStart = 0;
102 |
103 | int fieldLength;
104 |
105 | struct tm time = {};
106 | time.tm_isdst = 0;
107 |
108 | for (int i = 0; i < len; i++)
109 | {
110 | if (buffer[i] == ',')
111 | {
112 | fieldLength = i - fieldStart;
113 |
114 | if (fieldIndex == 1)
115 | {
116 | if (fieldLength != 9)
117 | return false;
118 |
119 | time.tm_sec = ((buffer[fieldStart + 4] - '0') * 10) + (buffer[fieldStart + 5] - '0');
120 | time.tm_min = ((buffer[fieldStart + 2] - '0') * 10) + (buffer[fieldStart + 3] - '0');
121 | time.tm_hour = ((buffer[fieldStart + 0] - '0') * 10) + (buffer[fieldStart + 1] - '0');
122 |
123 | tv->tv_usec = ((buffer[fieldStart + 4] - '7') * 100000) + ((buffer[fieldStart + 8] - '0') * 10000);
124 | }
125 | else if (fieldIndex == 9)
126 | {
127 | if (fieldLength != 6)
128 | return false;
129 |
130 | time.tm_year = ((buffer[fieldStart + 4] - '0') * 10) + (buffer[fieldStart + 5] - '0') + 100;
131 | time.tm_mon = ((buffer[fieldStart + 2] - '0') * 10) + (buffer[fieldStart + 3] - '0') - 1;
132 | time.tm_mday = ((buffer[fieldStart + 0] - '0') * 10) + (buffer[fieldStart + 1] - '0');
133 | }
134 |
135 | fieldIndex++;
136 | fieldStart = i + 1;
137 | }
138 | }
139 |
140 | if (fieldIndex != 12)
141 | return false;
142 |
143 | tv->tv_sec = mktime(&time);
144 |
145 | return true;
146 | }
147 |
148 | void GPS::_handleLine(unsigned char *buffer, uint16_t len)
149 | {
150 | uint64_t startTime = esp_timer_get_time();
151 | if ((len > 6) && (strncmp((char *)buffer, "$GPRMC", 6) == 0))
152 | {
153 | timeval tv;
154 | if (parseRMCTime(buffer, len, &tv))
155 | {
156 | if (_nextSync < startTime)
157 | {
158 | _nextSync = startTime + 300 * 1000 * 1000; // every 5 minutes
159 |
160 | tv.tv_usec += esp_timer_get_time() - startTime;
161 | _clk->setTime(&tv);
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/hmframe.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * hmframe.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "hmframe.h"
21 | #include
22 |
23 | uint16_t HMFrame::crc(unsigned char *buffer, uint16_t len)
24 | {
25 | uint16_t crc = 0xd77f;
26 | int i;
27 |
28 | while (len--)
29 | {
30 | crc ^= *buffer++ << 8;
31 | for (i = 0; i < 8; i++)
32 | {
33 | if (crc & 0x8000)
34 | {
35 | crc <<= 1;
36 | crc ^= 0x8005;
37 | }
38 | else
39 | {
40 | crc <<= 1;
41 | }
42 | }
43 | }
44 |
45 | return crc;
46 | }
47 |
48 | bool HMFrame::TryParse(unsigned char *buffer, uint16_t len, HMFrame *frame)
49 | {
50 | uint16_t crc;
51 |
52 | if (len < 8)
53 | return false;
54 |
55 | if (buffer[0] != 0xfd)
56 | return false;
57 |
58 | frame->data_len = ((buffer[1] << 8) | buffer[2]) - 3;
59 | if (frame->data_len + 8 != len)
60 | return false;
61 |
62 | crc = (buffer[len - 2] << 8) | buffer[len - 1];
63 | if (crc != HMFrame::crc(buffer, len - 2))
64 | return false;
65 |
66 | frame->destination = buffer[3];
67 | frame->counter = buffer[4];
68 | frame->command = buffer[5];
69 | frame->data = &buffer[6];
70 |
71 | return true;
72 | }
73 |
74 | HMFrame::HMFrame() : data_len(0)
75 | {
76 | }
77 |
78 | uint16_t HMFrame::encode(unsigned char *buffer, uint16_t len, bool escaped)
79 | {
80 | uint16_t crc;
81 |
82 | if (data_len + 8 > len)
83 | return 0;
84 |
85 | buffer[0] = 0xfd;
86 | buffer[1] = ((data_len + 3) >> 8) & 0xff;
87 | buffer[2] = (data_len + 3) & 0xff;
88 | buffer[3] = destination;
89 | buffer[4] = counter;
90 | buffer[5] = command;
91 | if (data_len > 0)
92 | memcpy(&(buffer[6]), data, data_len);
93 |
94 | crc = HMFrame::crc(buffer, data_len + 6);
95 | buffer[data_len + 6] = (crc >> 8) & 0xff;
96 | buffer[data_len + 7] = crc & 0xff;
97 |
98 | uint16_t res = data_len + 8;
99 |
100 | if (escaped)
101 | {
102 | for (uint16_t i = 1; i < res; i++)
103 | {
104 | if (buffer[i] == 0xfc || buffer[i] == 0xfd)
105 | {
106 | memmove(buffer + i + 1, buffer + i, res - i);
107 | buffer[i++] = 0xfc;
108 | buffer[i] &= 0x7f;
109 | res++;
110 | }
111 | }
112 | }
113 |
114 | return res;
115 | }
116 |
--------------------------------------------------------------------------------
/src/led.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * led.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "led.h"
21 | #include "freertos/FreeRTOS.h"
22 | #include "freertos/task.h"
23 | #include "driver/gpio.h"
24 |
25 | static uint8_t _blinkState = 0;
26 | static LED *_leds[MAX_LED_COUNT] = {0};
27 | static TaskHandle_t _switchTaskHandle = NULL;
28 | static int _highDuty;
29 |
30 | void ledSwitcherTask(void *parameter)
31 | {
32 | for (;;)
33 | {
34 | _blinkState = (_blinkState + 1) % 24;
35 |
36 | for (uint8_t i = 0; i < MAX_LED_COUNT; i++)
37 | {
38 | if (_leds[i] == 0)
39 | {
40 | break;
41 | }
42 | _leds[i]->updatePinState();
43 | }
44 | vTaskDelay(125 / portTICK_PERIOD_MS);
45 | }
46 |
47 | vTaskDelete(NULL);
48 | }
49 |
50 | void LED::start(Settings *settings)
51 | {
52 | ledc_timer_config_t ledc_timer = {
53 | .speed_mode = LEDC_HIGH_SPEED_MODE,
54 | .duty_resolution = LEDC_TIMER_11_BIT,
55 | .timer_num = LEDC_TIMER_0,
56 | .freq_hz = 5000,
57 | .clk_cfg = LEDC_AUTO_CLK,
58 | };
59 |
60 | _highDuty = settings->getLEDBrightness() * (1 << ledc_timer.duty_resolution) / 100;
61 |
62 | ledc_timer_config(&ledc_timer);
63 |
64 | if (!_switchTaskHandle)
65 | {
66 | xTaskCreate(ledSwitcherTask, "LED_Switcher", 4096, NULL, 10, &_switchTaskHandle);
67 | }
68 | }
69 |
70 | void LED::stop()
71 | {
72 | if (_switchTaskHandle)
73 | {
74 | vTaskDelete(_switchTaskHandle);
75 | _switchTaskHandle = NULL;
76 | }
77 | }
78 |
79 | LED::LED(gpio_num_t pin)
80 | {
81 | _channel_conf = {
82 | .gpio_num = pin,
83 | .speed_mode = LEDC_HIGH_SPEED_MODE,
84 | .channel = LEDC_CHANNEL_MAX,
85 | .intr_type = LEDC_INTR_DISABLE,
86 | .timer_sel = LEDC_TIMER_0,
87 | .duty = 0,
88 | .hpoint = 0,
89 | };
90 |
91 | for (uint8_t i = 0; i < MAX_LED_COUNT; i++)
92 | {
93 | if (_leds[i] == 0)
94 | {
95 | _channel_conf.channel = (ledc_channel_t)i;
96 | break;
97 | }
98 | }
99 |
100 | ledc_channel_config(&_channel_conf);
101 |
102 | _leds[_channel_conf.channel] = this;
103 | }
104 |
105 | void LED::setState(led_state_t state)
106 | {
107 | _state = state;
108 | updatePinState();
109 | }
110 |
111 | void LED::_setPinState(bool enabled) {
112 | ledc_set_duty(_channel_conf.speed_mode, _channel_conf.channel, enabled ? _highDuty : 0);
113 | ledc_update_duty(_channel_conf.speed_mode, _channel_conf.channel);
114 | }
115 |
116 | void LED::updatePinState()
117 | {
118 | switch (_state)
119 | {
120 | case LED_STATE_OFF:
121 | _setPinState(false);
122 | break;
123 | case LED_STATE_ON:
124 | _setPinState(true);
125 | break;
126 | case LED_STATE_BLINK:
127 | _setPinState((_blinkState % 8) < 4);
128 | break;
129 | case LED_STATE_BLINK_INV:
130 | _setPinState((_blinkState % 8) >= 4);
131 | break;
132 | case LED_STATE_BLINK_FAST:
133 | _setPinState((_blinkState % 2) == 0);
134 | break;
135 | case LED_STATE_BLINK_SLOW:
136 | _setPinState(_blinkState < 12);
137 | break;
138 | }
139 | }
--------------------------------------------------------------------------------
/src/linereader.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * linereader.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "linereader.h"
21 | #include
22 |
23 | LineReader::LineReader(std::function processor) : _processor(processor), _buffer_pos(0)
24 | {
25 | }
26 |
27 | void LineReader::Append(unsigned char chr)
28 | {
29 | switch (chr)
30 | {
31 | case '\r':
32 | return;
33 |
34 | case '\n':
35 | _buffer[_buffer_pos++] = 0;
36 | _processor(_buffer, _buffer_pos);
37 | _buffer_pos = 0;
38 | break;
39 |
40 | default:
41 | _buffer[_buffer_pos++] = chr;
42 | break;
43 | }
44 | }
45 |
46 | void LineReader::Append(unsigned char *buffer, uint16_t len)
47 | {
48 | int i;
49 | for (i = 0; i < len; i++)
50 | {
51 | Append(buffer[i]);
52 | }
53 | }
54 |
55 | void LineReader::Flush()
56 | {
57 | _buffer_pos = 0;
58 | }
59 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * main.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include
21 | #include "freertos/FreeRTOS.h"
22 | #include "freertos/task.h"
23 | #include "esp_system.h"
24 | #include "esp_spi_flash.h"
25 |
26 | #include "pins.h"
27 | #include "led.h"
28 | #include "settings.h"
29 | #include "rtc.h"
30 | #include "systemclock.h"
31 | #include "dcf.h"
32 | #include "ntpclient.h"
33 | #include "gps.h"
34 | #include "ethernet.h"
35 | #include "pushbuttonhandler.h"
36 | #include "radiomoduleconnector.h"
37 | #include "radiomoduledetector.h"
38 | #include "rawuartudplistener.h"
39 | #include "webui.h"
40 | #include "mdnsserver.h"
41 | #include "ntpserver.h"
42 | #include "esp_ota_ops.h"
43 | #include "updatecheck.h"
44 |
45 | static const char *TAG = "HB-RF-ETH";
46 |
47 | extern "C"
48 | {
49 | void app_main(void);
50 | }
51 |
52 | void app_main()
53 | {
54 | uart_config_t uart_config = {
55 | .baud_rate = 115200,
56 | .data_bits = UART_DATA_8_BITS,
57 | .parity = UART_PARITY_DISABLE,
58 | .stop_bits = UART_STOP_BITS_1,
59 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
60 | .rx_flow_ctrl_thresh = 0,
61 | .source_clk = UART_SCLK_APB};
62 | uart_param_config(UART_NUM_0, &uart_config);
63 | uart_set_pin(UART_NUM_0, GPIO_NUM_1, GPIO_NUM_3, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
64 |
65 | Settings settings;
66 |
67 | LED powerLED(LED_PWR_PIN);
68 | LED statusLED(LED_STATUS_PIN);
69 |
70 | LED redLED(HM_RED_PIN);
71 | LED greenLED(HM_GREEN_PIN);
72 | LED blueLED(HM_BLUE_PIN);
73 |
74 | LED::start(&settings);
75 |
76 | powerLED.setState(LED_STATE_BLINK);
77 | statusLED.setState(LED_STATE_BLINK_INV);
78 |
79 | redLED.setState(LED_STATE_OFF);
80 | greenLED.setState(LED_STATE_OFF);
81 | blueLED.setState(LED_STATE_OFF);
82 |
83 | SysInfo sysInfo;
84 |
85 | PushButtonHandler pushButton;
86 | pushButton.handleStartupFactoryReset(&powerLED, &statusLED, &settings);
87 |
88 | RadioModuleConnector radioModuleConnector(&redLED, &greenLED, &blueLED);
89 | radioModuleConnector.start();
90 |
91 | Ethernet ethernet(&settings);
92 | ethernet.start();
93 |
94 | setenv("TZ", "UTC0", 1);
95 | tzset();
96 |
97 | Rtc *rtc = NULL;
98 | rtc = Rtc::detect();
99 |
100 | SystemClock clk(rtc);
101 | clk.start();
102 |
103 | DCF dcf(&settings, &clk);
104 | NtpClient ntpClient(&settings, &clk);
105 | GPS gps(&settings, &clk);
106 |
107 | switch (settings.getTimesource())
108 | {
109 | case TIMESOURCE_NTP:
110 | ntpClient.start();
111 | break;
112 | case TIMESOURCE_GPS:
113 | gps.start();
114 | break;
115 | case TIMESOURCE_DCF:
116 | dcf.start();
117 | break;
118 | }
119 |
120 | MDns mdns;
121 | mdns.start(&settings);
122 |
123 | NtpServer ntpServer(&clk);
124 | ntpServer.start();
125 |
126 | RadioModuleDetector radioModuleDetector;
127 | radioModuleDetector.detectRadioModule(&radioModuleConnector);
128 |
129 | radio_module_type_t radioModuleType = radioModuleDetector.getRadioModuleType();
130 | if (radioModuleType != RADIO_MODULE_NONE)
131 | {
132 | switch (radioModuleType)
133 | {
134 | case RADIO_MODULE_HM_MOD_RPI_PCB:
135 | ESP_LOGI(TAG, "Detected HM-MOD-RPI-PCB:");
136 | break;
137 | case RADIO_MODULE_RPI_RF_MOD:
138 | ESP_LOGI(TAG, "Detected RPI-RF-MOD:");
139 | break;
140 | default:
141 | ESP_LOGI(TAG, "Detected unknown radio module:");
142 | break;
143 | }
144 |
145 | ESP_LOGI(TAG, " Serial: %s", radioModuleDetector.getSerial());
146 | ESP_LOGI(TAG, " SGTIN: %s", radioModuleDetector.getSGTIN());
147 | ESP_LOGI(TAG, " BidCos Radio MAC: 0x%06X", radioModuleDetector.getBidCosRadioMAC());
148 | ESP_LOGI(TAG, " HmIP Radio MAC: 0x%06X", radioModuleDetector.getHmIPRadioMAC());
149 |
150 | const uint8_t *firmwareVersion = radioModuleDetector.getFirmwareVersion();
151 | ESP_LOGI(TAG, " Firmware Version: %d.%d.%d", *firmwareVersion, *(firmwareVersion + 1), *(firmwareVersion + 2));
152 | }
153 | else
154 | {
155 | ESP_LOGW(TAG, "Radio module could not be detected.");
156 | }
157 |
158 | radioModuleConnector.resetModule();
159 |
160 | RawUartUdpListener rawUartUdpLister(&radioModuleConnector);
161 | rawUartUdpLister.start();
162 |
163 | UpdateCheck updateCheck(&sysInfo, &statusLED);
164 | updateCheck.start();
165 |
166 | WebUI webUI(&settings, &statusLED, &sysInfo, &updateCheck, ðernet, &rawUartUdpLister, &radioModuleConnector, &radioModuleDetector);
167 | webUI.start();
168 |
169 | powerLED.setState(LED_STATE_ON);
170 | statusLED.setState(LED_STATE_OFF);
171 |
172 | esp_ota_mark_app_valid_cancel_rollback();
173 |
174 | vTaskSuspend(NULL);
175 | }
176 |
--------------------------------------------------------------------------------
/src/mdnsserver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * mdnsserver.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "mdnsserver.h"
21 | #include "mdns.h"
22 | #include "esp_system.h"
23 |
24 | void MDns::start(Settings* settings)
25 | {
26 | ESP_ERROR_CHECK_WITHOUT_ABORT(mdns_init());
27 | ESP_ERROR_CHECK_WITHOUT_ABORT(mdns_hostname_set(settings->getHostname()));
28 |
29 | ESP_ERROR_CHECK_WITHOUT_ABORT(mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0));
30 | ESP_ERROR_CHECK_WITHOUT_ABORT(mdns_service_add(NULL, "_raw-uart", "_udp", 3008, NULL, 0));
31 | ESP_ERROR_CHECK_WITHOUT_ABORT(mdns_service_add(NULL, "_ntp", "_udp", 123, NULL, 0));
32 | }
33 |
34 | void MDns::stop()
35 | {
36 | mdns_free();
37 | }
38 |
--------------------------------------------------------------------------------
/src/ntpclient.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * ntpclient.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "ntpclient.h"
21 | #include "esp_sntp.h"
22 |
23 | static Settings *_settings;
24 | static SystemClock *_clk;
25 |
26 | static void _time_sync_notification_cb(struct timeval *tv)
27 | {
28 | _clk->setTime(tv);
29 | }
30 |
31 | NtpClient::NtpClient(Settings *settings, SystemClock *clk)
32 | {
33 | _settings = settings;
34 | _clk = clk;
35 | }
36 |
37 | void NtpClient::start()
38 | {
39 | sntp_setoperatingmode(SNTP_OPMODE_POLL);
40 | sntp_setservername(0, _settings->getNtpServer());
41 | sntp_set_time_sync_notification_cb(_time_sync_notification_cb);
42 | sntp_init();
43 | }
44 |
45 | void NtpClient::stop()
46 | {
47 | sntp_stop();
48 | }
--------------------------------------------------------------------------------
/src/ntpserver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * ntpserver.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "ntpserver.h"
21 | #include
22 | #include "esp_log.h"
23 | #include "udphelper.h"
24 |
25 | static const char *TAG = "NtpServer";
26 |
27 | void _ntp_udpQueueHandlerTask(void *parameter)
28 | {
29 | ((NtpServer *)parameter)->_udpQueueHandler();
30 | }
31 |
32 | void _ntp_udpReceivePaket(void *arg, udp_pcb *pcb, pbuf *pb, const ip_addr_t *addr, uint16_t port)
33 | {
34 | while (pb != NULL)
35 | {
36 | pbuf *this_pb = pb;
37 | pb = pb->next;
38 | this_pb->next = NULL;
39 | if (!((NtpServer *)arg)->_udpReceivePacket(this_pb, addr, port))
40 | {
41 | pbuf_free(this_pb);
42 | }
43 | }
44 | }
45 |
46 | NtpServer::NtpServer(SystemClock *clk) : _clk(clk)
47 | {
48 | }
49 |
50 | inline tstamp convertToNtp(struct timeval *tv)
51 | {
52 | tv->tv_sec = htonl(tv->tv_sec + 2208988800L);
53 | tv->tv_usec = htonl(tv->tv_usec / 1e6 * 4294967296.);
54 |
55 | return (unsigned long long)tv->tv_sec + (((unsigned long long)tv->tv_usec) << 32);
56 | }
57 |
58 | void NtpServer::handlePacket(pbuf *pb, ip4_addr_t addr, uint16_t port)
59 | {
60 | struct timeval tv = _clk->getTime();
61 | tstamp recv = convertToNtp(&tv);
62 |
63 | struct timeval lastSync = _clk->getLastSyncTime();
64 | if (lastSync.tv_sec < 1577836800l) // 2020-01-01 00:00:00 GMT
65 | {
66 | ESP_LOGE(TAG, "Ignoring ntp request because local time is not set");
67 | return;
68 | }
69 |
70 | if (pb->len != sizeof(ntp_packet_t))
71 | {
72 | ESP_LOGE(TAG, "Ignoring packet with bad length %d (should be %d)", pb->len, sizeof(ntp_packet_t));
73 | return;
74 | }
75 |
76 | ntp_packet_t ntp;
77 | memcpy(&ntp, pb->payload, sizeof(ntp));
78 |
79 | pbuf *resp_pb = pbuf_alloc_reference(&ntp, sizeof(ntp_packet_t), PBUF_REF);
80 |
81 | ip_addr_t resp_addr;
82 | resp_addr.type = IPADDR_TYPE_V4;
83 | resp_addr.u_addr.ip4 = addr;
84 |
85 | ntp.flags = 4 << 3 | 4; // version 4, mode server
86 | ntp.stratum = 15; // Prefer other ntp server over us
87 | ntp.poll = 8;
88 | ntp.precision = 0; // precision 2^0=1sec because of RTC values;
89 | ntp.delay = htonl(1 << 16); // 1sec
90 | ntp.dispersion = htonl(1 << 16); // 1sec
91 | memset(ntp.ref_id, 0, sizeof(ntp.ref_id));
92 | ntp.orig_time = ntp.trns_time;
93 | ntp.recv_time = recv;
94 | ntp.ref_time = convertToNtp(&lastSync);
95 | tv = _clk->getTime();
96 | ntp.trns_time = convertToNtp(&tv);
97 |
98 | _udp_sendto(_pcb, resp_pb, &resp_addr, port);
99 | pbuf_free(resp_pb);
100 | }
101 |
102 | void NtpServer::start()
103 | {
104 | _udp_queue = xQueueCreate(32, sizeof(udp_event_t *));
105 | xTaskCreate(_ntp_udpQueueHandlerTask, "NTPServer_UDP_QueueHandler", 4096, this, 10, &_tHandle);
106 |
107 | _pcb = udp_new();
108 | udp_recv(_pcb, &_ntp_udpReceivePaket, (void *)this);
109 |
110 | _udp_bind(_pcb, IP4_ADDR_ANY, 123);
111 | }
112 |
113 | void NtpServer::stop()
114 | {
115 | _udp_disconnect(_pcb);
116 | udp_recv(_pcb, NULL, NULL);
117 | _udp_remove(_pcb);
118 | _pcb = NULL;
119 |
120 | vTaskDelete(_tHandle);
121 | }
122 |
123 | void NtpServer::_udpQueueHandler()
124 | {
125 | udp_event_t *event = NULL;
126 |
127 | for (;;)
128 | {
129 | if (xQueueReceive(_udp_queue, &event, portMAX_DELAY) == pdTRUE)
130 | {
131 | handlePacket(event->pb, event->addr, event->port);
132 | pbuf_free(event->pb);
133 | free(event);
134 | }
135 | }
136 |
137 | vTaskDelete(NULL);
138 | }
139 |
140 | bool NtpServer::_udpReceivePacket(pbuf *pb, const ip_addr_t *addr, uint16_t port)
141 | {
142 | udp_event_t *e = (udp_event_t *)malloc(sizeof(udp_event_t));
143 | if (!e)
144 | {
145 | return false;
146 | }
147 |
148 | e->pb = pb;
149 |
150 | #pragma GCC diagnostic push
151 | #pragma GCC diagnostic ignored "-Wpointer-arith"
152 |
153 | ip_hdr *iphdr = reinterpret_cast(pb->payload - UDP_HLEN - IP_HLEN);
154 | e->addr.addr = iphdr->src.addr;
155 |
156 | udp_hdr *udphdr = reinterpret_cast(pb->payload - UDP_HLEN);
157 | e->port = ntohs(udphdr->src);
158 |
159 | #pragma GCC diagnostic pop
160 |
161 | if (xQueueSend(_udp_queue, &e, portMAX_DELAY) != pdPASS)
162 | {
163 | free((void *)(e));
164 | return false;
165 | }
166 | return true;
167 | }
--------------------------------------------------------------------------------
/src/pushbuttonhandler.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * pushbuttonhandler.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "pushbuttonhandler.h"
21 |
22 | static const char *TAG = "PushButtonHandler";
23 |
24 | PushButtonHandler::PushButtonHandler()
25 | {
26 | gpio_config_t io_conf;
27 | io_conf.intr_type = GPIO_INTR_DISABLE;
28 | io_conf.mode = GPIO_MODE_INPUT;
29 | io_conf.pin_bit_mask = 1ULL << HM_BTN_PIN;
30 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
31 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
32 | gpio_config(&io_conf);
33 | }
34 |
35 | void PushButtonHandler::handleStartupFactoryReset(LED *powerLED, LED *statusLED, Settings *settings)
36 | {
37 | if (gpio_get_level(HM_BTN_PIN) == 0)
38 | {
39 | // reset request start if button is pressed at least for 4sec
40 | for (int i = 0; i < 40; i++)
41 | {
42 | if (gpio_get_level(HM_BTN_PIN) == 1)
43 | {
44 | return;
45 | }
46 | vTaskDelay(100 / portTICK_PERIOD_MS);
47 | }
48 |
49 | powerLED->setState(LED_STATE_OFF);
50 | statusLED->setState(LED_STATE_BLINK_FAST);
51 |
52 | ESP_LOGI(TAG, "Factory Reset mode started.");
53 |
54 | // wait for release of button
55 | while (gpio_get_level(HM_BTN_PIN) == 0)
56 | {
57 | vTaskDelay(100 / portTICK_PERIOD_MS);
58 | }
59 |
60 | // wait to be pressed again or timeout
61 | for (int i = 0; i < 40; i++)
62 | {
63 | if (gpio_get_level(HM_BTN_PIN) == 0)
64 | {
65 | break;
66 | }
67 | vTaskDelay(100 / portTICK_PERIOD_MS);
68 | }
69 |
70 | // reset request start if button is pressed at least for 4sec
71 | for (int i = 0; i < 40; i++)
72 | {
73 | if (gpio_get_level(HM_BTN_PIN) == 1)
74 | {
75 | statusLED->setState(LED_STATE_ON);
76 | vTaskDelay(1000 / portTICK_PERIOD_MS);
77 | powerLED->setState(LED_STATE_ON);
78 | statusLED->setState(LED_STATE_OFF);
79 | ESP_LOGI(TAG, "Factory Reset mode timeout.");
80 | return;
81 | }
82 | vTaskDelay(100 / portTICK_PERIOD_MS);
83 | }
84 |
85 | statusLED->setState(LED_STATE_OFF);
86 | vTaskDelay(100 / portTICK_PERIOD_MS);
87 |
88 | settings->clear();
89 | ESP_LOGI(TAG, "Factory Reset done.");
90 |
91 | powerLED->setState(LED_STATE_ON);
92 | statusLED->setState(LED_STATE_ON);
93 | vTaskDelay(1000 / portTICK_PERIOD_MS);
94 | powerLED->setState(LED_STATE_BLINK);
95 | statusLED->setState(LED_STATE_BLINK_INV);
96 | }
97 | }
--------------------------------------------------------------------------------
/src/radiomoduleconnector.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * radiomoduleconnector.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include "radiomoduleconnector.h"
24 | #include "hmframe.h"
25 | #include "driver/gpio.h"
26 | #include "pins.h"
27 | #include "esp_log.h"
28 |
29 | static const char *TAG = "RadioModuleConnector";
30 |
31 | void serialQueueHandlerTask(void *parameter)
32 | {
33 | ((RadioModuleConnector *)parameter)->_serialQueueHandler();
34 | }
35 |
36 | RadioModuleConnector::RadioModuleConnector(LED *redLED, LED *greenLed, LED *blueLed) : _redLED(redLED), _greenLED(greenLed), _blueLED(blueLed)
37 | {
38 | gpio_config_t io_conf;
39 | io_conf.intr_type = GPIO_INTR_DISABLE;
40 | io_conf.mode = GPIO_MODE_OUTPUT;
41 | io_conf.pin_bit_mask = 1ULL << HM_RST_PIN;
42 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
43 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
44 | gpio_config(&io_conf);
45 |
46 | uart_config_t uart_config = {
47 | .baud_rate = 115200,
48 | .data_bits = UART_DATA_8_BITS,
49 | .parity = UART_PARITY_DISABLE,
50 | .stop_bits = UART_STOP_BITS_1,
51 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
52 | .rx_flow_ctrl_thresh = 0,
53 | .source_clk = UART_SCLK_APB};
54 | uart_param_config(UART_NUM_1, &uart_config);
55 | uart_set_pin(UART_NUM_1, HM_TX_PIN, HM_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
56 |
57 | using namespace std::placeholders;
58 | _streamParser = new StreamParser(false, std::bind(&RadioModuleConnector::_handleFrame, this, _1, _2));
59 | }
60 |
61 | void RadioModuleConnector::start()
62 | {
63 | setLED(false, false, false);
64 |
65 | uart_driver_install(UART_NUM_1, UART_FIFO_LEN * 2, 0, 20, &_uart_queue, 0);
66 |
67 | xTaskCreate(serialQueueHandlerTask, "RadioModuleConnector_UART_QueueHandler", 4096, this, 15, &_tHandle);
68 | resetModule();
69 | }
70 |
71 | void RadioModuleConnector::stop()
72 | {
73 | resetModule();
74 | uart_driver_delete(UART_NUM_1);
75 | vTaskDelete(_tHandle);
76 | }
77 |
78 | void RadioModuleConnector::setFrameHandler(FrameHandler *frameHandler, bool decodeEscaped)
79 | {
80 | atomic_store(&_frameHandler, frameHandler);
81 | _streamParser->setDecodeEscaped(decodeEscaped);
82 | }
83 |
84 | void RadioModuleConnector::setLED(bool red, bool green, bool blue)
85 | {
86 | _redLED->setState(red ? LED_STATE_ON : LED_STATE_OFF);
87 | _greenLED->setState(green ? LED_STATE_ON : LED_STATE_OFF);
88 | _blueLED->setState(blue ? LED_STATE_ON : LED_STATE_OFF);
89 | }
90 |
91 | void RadioModuleConnector::resetModule()
92 | {
93 | gpio_set_level(HM_RST_PIN, 1);
94 | vTaskDelay(50 / portTICK_PERIOD_MS);
95 | gpio_set_level(HM_RST_PIN, 0);
96 | vTaskDelay(50 / portTICK_PERIOD_MS);
97 | }
98 |
99 | void RadioModuleConnector::sendFrame(unsigned char *buffer, uint16_t len)
100 | {
101 | uart_write_bytes(UART_NUM_1, (const char *)buffer, len);
102 | }
103 |
104 | void RadioModuleConnector::_serialQueueHandler()
105 | {
106 | uart_event_t event;
107 | uint8_t *buffer = (uint8_t *)malloc(UART_FIFO_LEN);
108 |
109 | uart_flush_input(UART_NUM_1);
110 |
111 | for (;;)
112 | {
113 | if (xQueueReceive(_uart_queue, (void *)&event, (portTickType)portMAX_DELAY))
114 | {
115 | switch (event.type)
116 | {
117 | case UART_DATA:
118 | uart_read_bytes(UART_NUM_1, buffer, event.size, portMAX_DELAY);
119 | _streamParser->append(buffer, event.size);
120 | break;
121 | case UART_FIFO_OVF:
122 | case UART_BUFFER_FULL:
123 | uart_flush_input(UART_NUM_1);
124 | xQueueReset(_uart_queue);
125 | _streamParser->flush();
126 | break;
127 | case UART_BREAK:
128 | case UART_PARITY_ERR:
129 | case UART_FRAME_ERR:
130 | _streamParser->flush();
131 | break;
132 | default:
133 | break;
134 | }
135 | }
136 | }
137 |
138 | free(buffer);
139 | buffer = NULL;
140 | vTaskDelete(NULL);
141 | }
142 |
143 | void RadioModuleConnector::_handleFrame(unsigned char *buffer, uint16_t len)
144 | {
145 | FrameHandler *frameHandler = (FrameHandler *)atomic_load(&_frameHandler);
146 |
147 | if (frameHandler)
148 | {
149 | frameHandler->handleFrame(buffer, len);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/radiomoduledetector.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * radiomoduledetector.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include "radiomoduledetector.h"
24 | #include "hmframe.h"
25 |
26 | static const char *TAG = "RadioModuleConnector";
27 |
28 | void RadioModuleDetector::detectRadioModule(RadioModuleConnector *radioModuleConnector)
29 | {
30 | _radioModuleConnector = radioModuleConnector;
31 |
32 | _detectState = DETECT_STATE_START_BL;
33 | _detectRetryCount = 0;
34 | _detectMsgCounter = 0;
35 |
36 | _radioModuleType = RADIO_MODULE_NONE;
37 |
38 | sem_init(_detectWaitFrameDataSemaphore);
39 |
40 | _radioModuleConnector->setFrameHandler(this, true);
41 |
42 | while (_detectState == DETECT_STATE_START_BL && _detectRetryCount < 3)
43 | {
44 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_IDENTIFY, NULL, 0);
45 | if (!sem_take(_detectWaitFrameDataSemaphore, 3))
46 | {
47 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_IDENTIFY, NULL, 0);
48 | if (!sem_take(_detectWaitFrameDataSemaphore, 3))
49 | {
50 | _detectRetryCount++;
51 | }
52 | }
53 | }
54 |
55 | _detectRetryCount = 0;
56 | while (_detectState == DETECT_STATE_START_APP && _detectRetryCount < 3)
57 | {
58 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_IDENTIFY, NULL, 0);
59 | if (!sem_take(_detectWaitFrameDataSemaphore, 3))
60 | {
61 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_IDENTIFY, NULL, 0);
62 | if (!sem_take(_detectWaitFrameDataSemaphore, 3))
63 | {
64 | _detectRetryCount++;
65 | }
66 | }
67 | }
68 |
69 | while (true)
70 | {
71 | switch (_detectState)
72 | {
73 | case DETECT_STATE_START_BL:
74 | case DETECT_STATE_START_APP:
75 | _detectState = DETECT_STATE_FINISHED;
76 | break;
77 |
78 | case DETECT_STATE_GET_MCU_TYPE:
79 | sendFrame(_detectMsgCounter++, HM_DST_TRX, HM_CMD_TRX_GET_MCU_TYPE, NULL, 0);
80 | break;
81 |
82 | case DETECT_STATE_GET_VERSION:
83 | sendFrame(_detectMsgCounter++, HM_DST_TRX, HM_CMD_TRX_GET_VERSION, NULL, 0);
84 | break;
85 |
86 | case DETECT_STATE_GET_HMIP_RF_ADDRESS:
87 | sendFrame(_detectMsgCounter++, HM_DST_HMIP, HM_CMD_HMIP_GET_DEFAULT_RF_ADDR, NULL, 0);
88 | break;
89 |
90 | case DETECT_STATE_GET_SGTIN:
91 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_GET_SGTIN, NULL, 0);
92 | break;
93 |
94 | case DETECT_STATE_GET_BIDCOS_RF_ADDRESS:
95 | sendFrame(_detectMsgCounter++, HM_DST_LLMAC, HM_CMD_LLMAC_GET_DEFAULT_RF_ADDR, NULL, 0);
96 | break;
97 |
98 | case DETECT_STATE_GET_SERIAL:
99 | sendFrame(_detectMsgCounter++, HM_DST_LLMAC, HM_CMD_LLMAC_GET_SERIAL, NULL, 0);
100 | break;
101 |
102 | case DETECT_STATE_LEGACY_GET_VERSION:
103 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_GET_VERSION, NULL, 0);
104 | break;
105 |
106 | case DETECT_STATE_LEGACY_GET_BIDCOS_RF_ADDRESS:
107 | sendFrame(_detectMsgCounter++, HM_DST_TRX, HM_CMD_TRX_GET_DEFAULT_RF_ADDR, NULL, 0);
108 | break;
109 |
110 | case DETECT_STATE_LEGACY_GET_SERIAL:
111 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_GET_SERIAL, NULL, 0);
112 | break;
113 |
114 | case DETECT_STATE_FINISHED:
115 | break;
116 | }
117 |
118 | if (_detectState == DETECT_STATE_FINISHED || !sem_take(_detectWaitFrameDataSemaphore, 3))
119 | {
120 | break;
121 | }
122 | }
123 |
124 | _radioModuleConnector->setFrameHandler(NULL, false);
125 | }
126 |
127 | void RadioModuleDetector::handleFrame(unsigned char *buffer, uint16_t len)
128 | {
129 | log_frame("Received HM frame:", buffer, len);
130 |
131 | HMFrame frame;
132 | if (!HMFrame::TryParse(buffer, len, &frame))
133 | {
134 | return;
135 | }
136 |
137 | switch (_detectState)
138 | {
139 | case DETECT_STATE_START_BL:
140 | if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 12 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "HMIP_TRX_Bl", 11) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 11 && strncmp((char *)(frame.data), "HMIP_TRX_Bl", 11) == 0))
141 | {
142 | // TRX CoPro in bootloader
143 | _detectState = DETECT_STATE_START_APP;
144 | sem_give(_detectWaitFrameDataSemaphore);
145 | }
146 | else if ((frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 10 && frame.data[0] == 2 && strncmp((char *)(frame.data + 1), "Co_CPU_BL", 9) == 0) || (frame.destination == HM_DST_HMSYSTEM && frame.command == 0 && frame.data_len == 9 && strncmp((char *)frame.data, "Co_CPU_BL", 9) == 0))
147 | {
148 | // Legacy CoPro in bootloader
149 | _detectState = DETECT_STATE_START_APP;
150 | sem_give(_detectWaitFrameDataSemaphore);
151 | }
152 | else if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 14 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "DualCoPro_App", 13) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 13 && strncmp((char *)frame.data, "DualCoPro_App", 13) == 0))
153 | {
154 | // Dual CoPro in app --> start bootloader
155 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_START_BL, NULL, 0);
156 | }
157 | else if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 13 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "HMIP_TRX_App", 12) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 12 && strncmp((char *)frame.data, "HMIP_TRX_App", 12) == 0))
158 | {
159 | // HmIP only in app --> start bootloader
160 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_START_BL, NULL, 0);
161 | }
162 | else if ((frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 11 && frame.data[0] == 2 && strncmp((char *)(frame.data + 1), "Co_CPU_App", 10) == 0) || (frame.destination == HM_DST_HMSYSTEM && frame.command == 0 && frame.data_len == 10 && strncmp((char *)frame.data, "Co_CPU_App", 10) == 0))
163 | {
164 | // Legacy CoPro in app --> start bootloader
165 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_CHANGE_APP, NULL, 0);
166 | }
167 | break;
168 |
169 | case DETECT_STATE_START_APP:
170 | if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 12 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "HMIP_TRX_Bl", 11) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 11 && strncmp((char *)(frame.data), "HMIP_TRX_Bl", 11) == 0))
171 | {
172 | // TRX CoPro in bootloader --> start app
173 | sendFrame(_detectMsgCounter++, HM_DST_COMMON, HM_CMD_COMMON_START_APP, NULL, 0);
174 | }
175 | else if ((frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 10 && frame.data[0] == 2 && strncmp((char *)(frame.data + 1), "Co_CPU_BL", 9) == 0) || (frame.destination == HM_DST_HMSYSTEM && frame.command == 0 && frame.data_len == 9 && strncmp((char *)frame.data, "Co_CPU_BL", 9) == 0))
176 | {
177 | // Legacy CoPro in bootloader --> start app
178 | sendFrame(_detectMsgCounter++, HM_DST_HMSYSTEM, HM_CMD_HMSYSTEM_CHANGE_APP, NULL, 0);
179 | }
180 | else if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 14 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "DualCoPro_App", 13) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 13 && strncmp((char *)frame.data, "DualCoPro_App", 13) == 0))
181 | {
182 | // Dual CoPro in app
183 | _detectState = DETECT_STATE_GET_MCU_TYPE;
184 | sem_give(_detectWaitFrameDataSemaphore);
185 | }
186 | else if ((frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 13 && frame.data[0] == 1 && strncmp((char *)(frame.data + 1), "HMIP_TRX_App", 12) == 0) || (frame.destination == HM_DST_COMMON && frame.command == 0 && frame.data_len == 12 && strncmp((char *)frame.data, "HMIP_TRX_App", 12) == 0))
187 | {
188 | // HmIP only in app
189 | _detectState = DETECT_STATE_GET_MCU_TYPE;
190 | sem_give(_detectWaitFrameDataSemaphore);
191 | }
192 | else if ((frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 11 && frame.data[0] == 2 && strncmp((char *)(frame.data + 1), "Co_CPU_App", 10) == 0) || (frame.destination == HM_DST_HMSYSTEM && frame.command == 0 && frame.data_len == 10 && strncmp((char *)frame.data, "Co_CPU_App", 10) == 0))
193 | {
194 | // Legacy CoPro in app
195 | _detectState = DETECT_STATE_LEGACY_GET_VERSION;
196 | sprintf(_sgtin, "n/a");
197 | _hmIPRadioMAC = 0;
198 | _radioModuleType = RADIO_MODULE_HM_MOD_RPI_PCB;
199 | sem_give(_detectWaitFrameDataSemaphore);
200 | }
201 | break;
202 |
203 | case DETECT_STATE_GET_MCU_TYPE:
204 | if (frame.destination == HM_DST_TRX && frame.command == HM_CMD_TRX_ACK && frame.data_len == 2 && frame.data[0] == 1)
205 | {
206 | _radioModuleType = (radio_module_type_t)frame.data[1];
207 | _detectState = DETECT_STATE_GET_VERSION;
208 | sem_give(_detectWaitFrameDataSemaphore);
209 | }
210 | break;
211 |
212 | case DETECT_STATE_GET_VERSION:
213 | if (frame.destination == HM_DST_TRX && frame.command == HM_CMD_TRX_ACK && frame.data_len == 10 && frame.data[0] == 1)
214 | {
215 | memcpy(_firmwareVersion, frame.data + 1, 3);
216 | _detectState = DETECT_STATE_GET_HMIP_RF_ADDRESS;
217 | sem_give(_detectWaitFrameDataSemaphore);
218 | }
219 | break;
220 |
221 | case DETECT_STATE_GET_HMIP_RF_ADDRESS:
222 | if (frame.destination == HM_DST_HMIP && frame.command == HM_CMD_HMIP_ACK && frame.data_len == 4 && frame.data[0] == 1)
223 | {
224 | _hmIPRadioMAC = (frame.data[1] << 16) | (frame.data[2] << 8) | frame.data[3];
225 | _detectState = DETECT_STATE_GET_SGTIN;
226 | sem_give(_detectWaitFrameDataSemaphore);
227 | }
228 | break;
229 |
230 | case DETECT_STATE_GET_SGTIN:
231 | if (frame.destination == HM_DST_COMMON && frame.command == HM_CMD_COMMON_ACK && frame.data_len == 13 && frame.data[0] == 1)
232 | {
233 | sprintf(_sgtin, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", frame.data[1], frame.data[2], frame.data[3], frame.data[4], frame.data[5], frame.data[6], frame.data[7], frame.data[8], frame.data[9], frame.data[10], frame.data[11], frame.data[12]);
234 |
235 | switch (_radioModuleType)
236 | {
237 | case RADIO_MODULE_RPI_RF_MOD:
238 | _bidCosRadioMAC = 0xff0000 | (frame.data[11] << 8) | frame.data[12];
239 | if (_bidCosRadioMAC == 0xffffff)
240 | _bidCosRadioMAC = 0xfffffe;
241 | sprintf(_serial, "%02X%02X%02X%02X%02X", frame.data[8], frame.data[9], frame.data[10], frame.data[11], frame.data[12]);
242 | _detectState = DETECT_STATE_GET_BIDCOS_RF_ADDRESS;
243 | break;
244 |
245 | case RADIO_MODULE_HMIP_RFUSB:
246 | _bidCosRadioMAC = 0;
247 | sprintf(_serial, "%02X%02X%02X%02X%02X", frame.data[8], frame.data[9], frame.data[10], frame.data[11], frame.data[12]);
248 | _detectState = DETECT_STATE_FINISHED;
249 | break;
250 |
251 | default:
252 | _detectState = DETECT_STATE_GET_BIDCOS_RF_ADDRESS;
253 | break;
254 | }
255 | sem_give(_detectWaitFrameDataSemaphore);
256 | }
257 | break;
258 |
259 | case DETECT_STATE_GET_BIDCOS_RF_ADDRESS:
260 | if (frame.destination == HM_DST_LLMAC && frame.command == HM_CMD_LLMAC_ACK && frame.data_len == 4 && frame.data[0] == 1)
261 | {
262 | uint32_t radioMac = (frame.data[1] << 16) | (frame.data[2] << 8) | frame.data[3];
263 | if (radioMac != 0 && (radioMac & 0xffff) != 0xffff)
264 | _bidCosRadioMAC = radioMac;
265 | _detectState = _radioModuleType == RADIO_MODULE_RPI_RF_MOD ? DETECT_STATE_FINISHED : DETECT_STATE_GET_SERIAL;
266 | sem_give(_detectWaitFrameDataSemaphore);
267 | }
268 | else if (frame.destination == HM_DST_LLMAC && frame.command == HM_CMD_LLMAC_ACK && frame.data_len == 1 && frame.data[0] == 0)
269 | {
270 | if (_radioModuleType == RADIO_MODULE_RPI_RF_MOD)
271 | _detectState = DETECT_STATE_FINISHED;
272 | sem_give(_detectWaitFrameDataSemaphore);
273 | }
274 | break;
275 |
276 | case DETECT_STATE_GET_SERIAL:
277 | if (frame.destination == HM_DST_LLMAC && frame.command == HM_CMD_LLMAC_ACK && frame.data_len == 11 && frame.data[0] == 1)
278 | {
279 | memcpy(_serial, frame.data + 1, 10);
280 | _detectState = DETECT_STATE_FINISHED;
281 | sem_give(_detectWaitFrameDataSemaphore);
282 | }
283 | break;
284 |
285 | case DETECT_STATE_LEGACY_GET_VERSION:
286 | if (frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 7 && frame.data[0] == 2)
287 | {
288 | memcpy(_firmwareVersion, frame.data + 4, 3);
289 | _detectState = DETECT_STATE_LEGACY_GET_BIDCOS_RF_ADDRESS;
290 | sem_give(_detectWaitFrameDataSemaphore);
291 | }
292 | break;
293 |
294 | case DETECT_STATE_LEGACY_GET_BIDCOS_RF_ADDRESS:
295 | if (frame.destination == HM_DST_TRX && frame.command == HM_CMD_TRX_ACK && frame.data_len == 6)
296 | {
297 | _bidCosRadioMAC = (frame.data[3] << 16) | (frame.data[4] << 8) | frame.data[5];
298 | _detectState = DETECT_STATE_LEGACY_GET_SERIAL;
299 | sem_give(_detectWaitFrameDataSemaphore);
300 | }
301 | break;
302 |
303 | case DETECT_STATE_LEGACY_GET_SERIAL:
304 | if (frame.destination == HM_DST_HMSYSTEM && frame.command == HM_CMD_HMSYSTEM_ACK && frame.data_len == 11 && frame.data[0] == 2)
305 | {
306 | memcpy(_serial, frame.data + 1, 10);
307 | _detectState = DETECT_STATE_FINISHED;
308 | sem_give(_detectWaitFrameDataSemaphore);
309 | }
310 | break;
311 | }
312 | }
313 |
314 | const char *RadioModuleDetector::getSerial()
315 | {
316 | return _serial;
317 | }
318 |
319 | uint32_t RadioModuleDetector::getBidCosRadioMAC()
320 | {
321 | return _bidCosRadioMAC;
322 | }
323 |
324 | uint32_t RadioModuleDetector::getHmIPRadioMAC()
325 | {
326 | return _hmIPRadioMAC;
327 | }
328 |
329 | const char *RadioModuleDetector::getSGTIN()
330 | {
331 | return _sgtin;
332 | }
333 |
334 | const uint8_t *RadioModuleDetector::getFirmwareVersion()
335 | {
336 | return _firmwareVersion;
337 | }
338 |
339 | radio_module_type_t RadioModuleDetector::getRadioModuleType()
340 | {
341 | return _radioModuleType;
342 | }
343 |
344 | void RadioModuleDetector::sendFrame(uint8_t counter, uint8_t destination, uint8_t command, unsigned char *data, uint data_len)
345 | {
346 | HMFrame frame;
347 | unsigned char sendBuffer[8 + data_len + 10];
348 |
349 | frame.counter = counter;
350 | frame.destination = destination;
351 | frame.command = command;
352 | frame.data = data;
353 | frame.data_len = data_len;
354 | uint16_t len = frame.encode(sendBuffer, sizeof(sendBuffer), true);
355 |
356 | log_frame("Sending HM frame:", sendBuffer, len);
357 |
358 | _radioModuleConnector->sendFrame(sendBuffer, len);
359 | }
360 |
--------------------------------------------------------------------------------
/src/rawuartudplistener.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * rawuartudplistener.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "rawuartudplistener.h"
21 | #include "hmframe.h"
22 | #include "esp_log.h"
23 | #include
24 | #include "udphelper.h"
25 |
26 | static const char *TAG = "RawUartUdpListener";
27 |
28 | void _raw_uart_udpQueueHandlerTask(void *parameter)
29 | {
30 | ((RawUartUdpListener *)parameter)->_udpQueueHandler();
31 | }
32 |
33 | void _raw_uart_udpReceivePaket(void *arg, udp_pcb *pcb, pbuf *pb, const ip_addr_t *addr, uint16_t port)
34 | {
35 | while (pb != NULL)
36 | {
37 | pbuf *this_pb = pb;
38 | pb = pb->next;
39 | this_pb->next = NULL;
40 | if (!((RawUartUdpListener *)arg)->_udpReceivePacket(this_pb, addr, port))
41 | {
42 | pbuf_free(this_pb);
43 | }
44 | }
45 | }
46 |
47 | RawUartUdpListener::RawUartUdpListener(RadioModuleConnector *radioModuleConnector) : _radioModuleConnector(radioModuleConnector)
48 | {
49 | atomic_init(&_connectionStarted, false);
50 | atomic_init(&_remotePort, (ushort)0);
51 | atomic_init(&_remoteAddress, 0u);
52 | atomic_init(&_counter, 0);
53 | atomic_init(&_endpointConnectionIdentifier, 1);
54 | }
55 |
56 | void RawUartUdpListener::handlePacket(pbuf *pb, ip4_addr_t addr, uint16_t port)
57 | {
58 | size_t length = pb->len;
59 | unsigned char *data = (unsigned char *)(pb->payload);
60 | unsigned char response_buffer[3];
61 |
62 | if (length < 4)
63 | {
64 | ESP_LOGE(TAG, "Received invalid raw-uart packet, length %d", length);
65 | return;
66 | }
67 |
68 | if (data[0] != 0 && (addr.addr != atomic_load(&_remoteAddress) || port != atomic_load(&_remotePort)))
69 | {
70 | ESP_LOGE(TAG, "Received raw-uart packet from invalid address.");
71 | return;
72 | }
73 |
74 | if (*((uint16_t *)(data + length - 2)) != htons(HMFrame::crc(data, length - 2)))
75 | {
76 | ESP_LOGE(TAG, "Received raw-uart packet with invalid crc.");
77 | return;
78 | }
79 |
80 | _lastReceivedKeepAlive = esp_timer_get_time();
81 |
82 | switch (data[0])
83 | {
84 | case 0: // connect
85 | if (length == 5 && data[2] == 1)
86 | { // protocol version 1
87 | atomic_fetch_add(&_endpointConnectionIdentifier, 2);
88 | atomic_store(&_remotePort, (ushort)0);
89 | atomic_store(&_connectionStarted, false);
90 | atomic_store(&_remoteAddress, addr.addr);
91 | atomic_store(&_remotePort, port);
92 | _radioModuleConnector->setLED(true, true, false);
93 | response_buffer[0] = 1;
94 | response_buffer[1] = data[1];
95 | sendMessage(0, response_buffer, 2);
96 | }
97 | else if (length == 6 && data[2] == 2) {
98 | int endpointConnectionIdentifier = atomic_load(&_endpointConnectionIdentifier);
99 |
100 | if (data[3] == 0)
101 | {
102 | endpointConnectionIdentifier += 2;
103 | atomic_store(&_endpointConnectionIdentifier, endpointConnectionIdentifier);
104 | atomic_store(&_connectionStarted, false);
105 | }
106 | else if (data[3] != (endpointConnectionIdentifier & 0xff))
107 | {
108 | ESP_LOGE(TAG, "Received raw-uart reconnect packet with invalid endpoint identifier %d, should be %d", data[3], endpointConnectionIdentifier);
109 | return;
110 | }
111 |
112 | atomic_store(&_remotePort, (ushort)0);
113 | atomic_store(&_remoteAddress, addr.addr);
114 | atomic_store(&_remotePort, port);
115 | _radioModuleConnector->setLED(true, true, false);
116 | response_buffer[0] = 2;
117 | response_buffer[1] = data[1];
118 | response_buffer[2] = endpointConnectionIdentifier;
119 | sendMessage(0, response_buffer, 3);
120 | }
121 | else {
122 | ESP_LOGE(TAG, "Received invalid raw-uart connect packet, length %d", length);
123 | return;
124 | }
125 | break;
126 |
127 | case 1: // disconnect
128 | atomic_store(&_remotePort, (ushort)0);
129 | atomic_store(&_connectionStarted, false);
130 | atomic_store(&_remoteAddress, 0u);
131 | _radioModuleConnector->setLED(false, false, false);
132 | break;
133 |
134 | case 2: // keep alive
135 | break;
136 |
137 | case 3: // LED
138 | if (length != 5)
139 | {
140 | ESP_LOGE(TAG, "Received invalid raw-uart LED packet, length %d", length);
141 | return;
142 | }
143 |
144 | _radioModuleConnector->setLED(data[2] & 1, data[2] & 2, data[2] & 4);
145 | break;
146 |
147 | case 4: // Reset
148 | if (length != 4)
149 | {
150 | ESP_LOGE(TAG, "Received invalid raw-uart reset packet, length %d", length);
151 | return;
152 | }
153 |
154 | _radioModuleConnector->resetModule();
155 | break;
156 |
157 | case 5: // Start connection
158 | if (length != 4)
159 | {
160 | ESP_LOGE(TAG, "Received invalid raw-uart startconn packet, length %d", length);
161 | return;
162 | }
163 |
164 | atomic_store(&_connectionStarted, true);
165 | break;
166 |
167 | case 6: // End connection
168 | if (length != 4)
169 | {
170 | ESP_LOGE(TAG, "Received invalid raw-uart endconn packet, length %d", length);
171 | return;
172 | }
173 |
174 | atomic_store(&_connectionStarted, false);
175 | break;
176 |
177 | case 7: // Frame
178 | if (length < 5)
179 | {
180 | ESP_LOGE(TAG, "Received invalid raw-uart frame packet, length %d", length);
181 | return;
182 | }
183 |
184 | _radioModuleConnector->sendFrame(&data[2], length - 4);
185 | break;
186 |
187 | default:
188 | ESP_LOGE(TAG, "Received invalid raw-uart packet with unknown type %d", data[0]);
189 | break;
190 | }
191 | }
192 |
193 | ip4_addr_t RawUartUdpListener::getConnectedRemoteAddress()
194 | {
195 | uint16_t port = atomic_load(&_remotePort);
196 | uint32_t address = atomic_load(&_remoteAddress);
197 |
198 | if (port)
199 | {
200 | ip4_addr_t res{ .addr = address };
201 | return res;
202 | }
203 | else
204 | {
205 | return IP4_ADDR_ANY->u_addr.ip4;
206 | }
207 | }
208 |
209 | void RawUartUdpListener::sendMessage(unsigned char command, unsigned char *buffer, size_t len)
210 | {
211 | uint16_t port = atomic_load(&_remotePort);
212 | uint32_t address = atomic_load(&_remoteAddress);
213 |
214 | pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, len + 4, PBUF_RAM);
215 | unsigned char *sendBuffer = (unsigned char *)pb->payload;
216 |
217 | ip_addr_t addr;
218 | addr.type = IPADDR_TYPE_V4;
219 | addr.u_addr.ip4.addr = address;
220 |
221 | if (!port)
222 | return;
223 |
224 | sendBuffer[0] = command;
225 | sendBuffer[1] = (unsigned char)atomic_fetch_add(&_counter, 1);
226 |
227 | if (len)
228 | memcpy(sendBuffer + 2, buffer, len);
229 |
230 | *((uint16_t *)(sendBuffer + len + 2)) = htons(HMFrame::crc(sendBuffer, len + 2));
231 |
232 | _udp_sendto(_pcb, pb, &addr, port);
233 | pbuf_free(pb);
234 | }
235 |
236 | void RawUartUdpListener::handleFrame(unsigned char *buffer, uint16_t len)
237 | {
238 | if (!atomic_load(&_connectionStarted))
239 | return;
240 |
241 | if (len > (1500 - 28 - 4))
242 | {
243 | ESP_LOGE(TAG, "Received oversized frame from radio module, length %d", len);
244 | return;
245 | }
246 |
247 | sendMessage(7, buffer, len);
248 | }
249 |
250 | void RawUartUdpListener::start()
251 | {
252 | _udp_queue = xQueueCreate(32, sizeof(udp_event_t *));
253 | xTaskCreate(_raw_uart_udpQueueHandlerTask, "RawUartUdpListener_UDP_QueueHandler", 4096, this, 15, &_tHandle);
254 |
255 | _pcb = udp_new();
256 | udp_recv(_pcb, &_raw_uart_udpReceivePaket, (void *)this);
257 |
258 | _udp_bind(_pcb, IP4_ADDR_ANY, 3008);
259 |
260 | _radioModuleConnector->setFrameHandler(this, false);
261 | }
262 |
263 | void RawUartUdpListener::stop()
264 | {
265 | _udp_disconnect(_pcb);
266 | udp_recv(_pcb, NULL, NULL);
267 | _udp_remove(_pcb);
268 | _pcb = NULL;
269 |
270 | _radioModuleConnector->setFrameHandler(NULL, false);
271 | vTaskDelete(_tHandle);
272 | }
273 |
274 | void RawUartUdpListener::_udpQueueHandler()
275 | {
276 | udp_event_t *event = NULL;
277 | int64_t nextKeepAliveSentOut = esp_timer_get_time();
278 |
279 | for (;;)
280 | {
281 | if (xQueueReceive(_udp_queue, &event, (portTickType)(100 / portTICK_PERIOD_MS)) == pdTRUE)
282 | {
283 | handlePacket(event->pb, event->addr, event->port);
284 | pbuf_free(event->pb);
285 | free(event);
286 | }
287 |
288 | if (atomic_load(&_remotePort) != 0)
289 | {
290 | int64_t now = esp_timer_get_time();
291 |
292 | if (now > _lastReceivedKeepAlive + 5000000)
293 | { // 5 sec
294 | atomic_store(&_remotePort, (ushort)0);
295 | atomic_store(&_remoteAddress, 0u);
296 | _radioModuleConnector->setLED(true, false, false);
297 | ESP_LOGE(TAG, "Connection timed out");
298 | }
299 |
300 | if (now > nextKeepAliveSentOut)
301 | {
302 | nextKeepAliveSentOut = now + 1000000; // 1sec
303 | sendMessage(2, NULL, 0);
304 | }
305 | }
306 | }
307 |
308 | vTaskDelete(NULL);
309 | }
310 |
311 | bool RawUartUdpListener::_udpReceivePacket(pbuf *pb, const ip_addr_t *addr, uint16_t port)
312 | {
313 | udp_event_t *e = (udp_event_t *)malloc(sizeof(udp_event_t));
314 | if (!e)
315 | {
316 | return false;
317 | }
318 |
319 | e->pb = pb;
320 |
321 | #pragma GCC diagnostic push
322 | #pragma GCC diagnostic ignored "-Wpointer-arith"
323 |
324 | ip_hdr *iphdr = reinterpret_cast(pb->payload - UDP_HLEN - IP_HLEN);
325 | e->addr.addr = iphdr->src.addr;
326 |
327 | udp_hdr *udphdr = reinterpret_cast(pb->payload - UDP_HLEN);
328 | e->port = ntohs(udphdr->src);
329 |
330 | #pragma GCC diagnostic pop
331 |
332 | if (xQueueSend(_udp_queue, &e, portMAX_DELAY) != pdPASS)
333 | {
334 | free((void *)(e));
335 | return false;
336 | }
337 | return true;
338 | }
339 |
340 | /*
341 | Index 0 - Type: 0-Connect, 1-Disconnect, 2-KeepAlive, 3-LED, 4-StartConn, 5-StopConn, 6-Reset, 7-Frame
342 | Index 1 - Counter
343 | Index 2..n-2 - Payload
344 | Index n-2,n-1 - CRC16
345 |
346 | Payload:
347 | Keepalive: Empty
348 | Connect: 1 Byte: Protocol version
349 | LED: 1 Byte: Bit 0 R, Bit 1 G, Bit 2 B
350 | Reset: Empty
351 | Frame: Frame-Data
352 | */
353 |
--------------------------------------------------------------------------------
/src/rtc.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * rtc.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include
21 | #include "rtc.h"
22 | #include "esp_log.h"
23 |
24 | static const char *TAG = "RTC";
25 |
26 | static uint8_t bcd2bin(uint8_t val)
27 | {
28 | return val - 6 * (val >> 4);
29 | }
30 |
31 | static uint8_t bin2bcd(uint8_t val)
32 | {
33 | return val + 6 * (val / 10);
34 | }
35 |
36 | const uint8_t daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
37 |
38 | static bool _isDriverInstalled = false;
39 |
40 | static void i2c_master_init()
41 | {
42 | if (_isDriverInstalled)
43 | return;
44 |
45 | i2c_config_t i2c_config = {
46 | .mode = I2C_MODE_MASTER,
47 | .sda_io_num = HM_SDA_PIN,
48 | .scl_io_num = HM_SCL_PIN,
49 | .sda_pullup_en = GPIO_PULLUP_ENABLE,
50 | .scl_pullup_en = GPIO_PULLUP_ENABLE,
51 | .master{.clk_speed = 100000}};
52 | i2c_param_config(I2C_NUM_0, &i2c_config);
53 | i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
54 | _isDriverInstalled = true;
55 | }
56 |
57 | Rtc *Rtc::detect()
58 | {
59 | RtcDS3231 *ds3231 = new RtcDS3231();
60 | if (ds3231->begin())
61 | {
62 | ESP_LOGI(TAG, "DS3231 RTC found and initialized.");
63 | return ds3231;
64 | }
65 | else
66 | {
67 | delete ds3231;
68 | }
69 |
70 | RtcRX8130 *rx9130 = new RtcRX8130();
71 | if (rx9130->begin())
72 | {
73 | ESP_LOGI(TAG, "RX9130 RTC found and initialized.");
74 | return rx9130;
75 | }
76 | else
77 | {
78 | delete rx9130;
79 | }
80 |
81 | ESP_LOGE(TAG, "No RTC found.");
82 | return NULL;
83 | }
84 |
85 | Rtc::Rtc(uint8_t address, uint8_t reg_start) : _address(address), _reg_start(reg_start)
86 | {
87 | i2c_master_init();
88 | }
89 |
90 | Rtc::~Rtc()
91 | {
92 | }
93 |
94 | bool Rtc::begin()
95 | {
96 | i2c_cmd_handle_t cmd = i2c_cmd_link_create();
97 | i2c_master_start(cmd);
98 | i2c_master_write_byte(cmd, (_address << 1) | I2C_MASTER_WRITE, true);
99 | i2c_master_stop(cmd);
100 | esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_RATE_MS);
101 | i2c_cmd_link_delete(cmd);
102 |
103 | return ret == ESP_OK;
104 | }
105 |
106 | struct timeval Rtc::GetTime()
107 | {
108 | struct timeval res = {};
109 |
110 | uint8_t rawData[7] = {0};
111 |
112 | i2c_cmd_handle_t cmd = i2c_cmd_link_create();
113 | i2c_master_start(cmd);
114 | i2c_master_write_byte(cmd, _address << 1 | I2C_MASTER_WRITE, true);
115 | i2c_master_write_byte(cmd, _reg_start, true);
116 | i2c_master_start(cmd);
117 | i2c_master_write_byte(cmd, _address << 1 | I2C_MASTER_READ, true);
118 | i2c_master_read(cmd, rawData, sizeof(rawData) - 1, I2C_MASTER_ACK);
119 | i2c_master_read_byte(cmd, rawData + sizeof(rawData) - 1, I2C_MASTER_NACK);
120 | i2c_master_stop(cmd);
121 | esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_RATE_MS);
122 | i2c_cmd_link_delete(cmd);
123 |
124 | if (ret != ESP_OK)
125 | {
126 | ESP_LOGE(TAG, "Could not read time from RTC");
127 | res.tv_sec = 0;
128 | res.tv_usec = 0;
129 | return res;
130 | }
131 |
132 | res.tv_sec = bcd2bin(rawData[0]); // seconds
133 | res.tv_sec += bcd2bin(rawData[1]) * 60; // minutes
134 | res.tv_sec += bcd2bin(rawData[2]) * 3600; // hours
135 |
136 | uint16_t days = bcd2bin(rawData[4]);
137 | uint8_t month = bcd2bin(rawData[5]);
138 | uint8_t year = bcd2bin(rawData[6]);
139 |
140 | for (uint8_t i = 1; i < month; ++i)
141 | {
142 | days += daysInMonth[i - 1];
143 | }
144 |
145 | if (month > 2 && year % 4 == 0)
146 | days++;
147 |
148 | days += 365 * year + (year + 3) / 4 - 1;
149 |
150 | res.tv_sec += days * 86400;
151 |
152 | res.tv_sec += 10957 * 86400; // epoch diff 1970 vs. 2000
153 |
154 | return res;
155 | }
156 |
157 | void Rtc::SetTime(struct timeval now)
158 | {
159 | now.tv_sec -= 10957 * 86400; // epoch diff 1970 vs. 2000
160 |
161 | uint8_t seconds = now.tv_sec % 60;
162 | now.tv_sec /= 60;
163 | uint8_t minutes = now.tv_sec % 60;
164 | now.tv_sec /= 60;
165 | uint8_t hours = now.tv_sec % 24;
166 |
167 | uint16_t days = now.tv_sec / 24;
168 |
169 | uint8_t leap;
170 | uint8_t year;
171 |
172 | for (year = 0;; year++)
173 | {
174 | leap = year % 4 == 0;
175 | if (days < 365 + leap)
176 | break;
177 | days -= 365 + leap;
178 | }
179 |
180 | uint8_t month;
181 | for (month = 1;; month++)
182 | {
183 | uint8_t daysPerMonth = daysInMonth[month - 1];
184 | if (leap && month == 2)
185 | ++daysPerMonth;
186 | if (days < daysPerMonth)
187 | break;
188 | days -= daysPerMonth;
189 | }
190 | days++;
191 |
192 | i2c_cmd_handle_t cmd = i2c_cmd_link_create();
193 | i2c_master_start(cmd);
194 | i2c_master_write_byte(cmd, _address << 1 | I2C_MASTER_WRITE, true);
195 | i2c_master_write_byte(cmd, _reg_start, true);
196 | i2c_master_write_byte(cmd, bin2bcd(seconds), true);
197 | i2c_master_write_byte(cmd, bin2bcd(minutes), true);
198 | i2c_master_write_byte(cmd, bin2bcd(hours), true);
199 | i2c_master_write_byte(cmd, 0, true);
200 | i2c_master_write_byte(cmd, bin2bcd(days), true);
201 | i2c_master_write_byte(cmd, bin2bcd(month), true);
202 | i2c_master_write_byte(cmd, bin2bcd(year), true);
203 | i2c_master_stop(cmd);
204 | esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_RATE_MS);
205 | i2c_cmd_link_delete(cmd);
206 |
207 | if (ret != ESP_OK)
208 | {
209 | ESP_LOGE(TAG, "Could not write time to RTC");
210 | }
211 | }
212 |
213 | RtcDS3231::RtcDS3231() : Rtc::Rtc(0x68, 0)
214 | {
215 | }
216 |
217 | RtcRX8130::RtcRX8130() : Rtc::Rtc(0x32, 0x10)
218 | {
219 | }
220 |
221 | bool RtcRX8130::begin()
222 | {
223 | if (Rtc::begin())
224 | {
225 | i2c_cmd_handle_t cmd = i2c_cmd_link_create();
226 | i2c_master_start(cmd);
227 | i2c_master_write_byte(cmd, _address << 1 | I2C_MASTER_WRITE, true);
228 | i2c_master_write_byte(cmd, 0x1f, true);
229 | i2c_master_write_byte(cmd, 0x31, true);
230 | i2c_master_stop(cmd);
231 | esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_RATE_MS);
232 | i2c_cmd_link_delete(cmd);
233 |
234 | return ret == ESP_OK;
235 | }
236 | else
237 | {
238 | return false;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/settings.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * settings.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "settings.h"
21 | #include "nvs.h"
22 | #include "nvs_flash.h"
23 | #include
24 |
25 | Settings::Settings()
26 | {
27 | load();
28 | }
29 |
30 | static const char *TAG = "Settings";
31 | static const char *NVS_NAMESPACE = "HB-RF-ETH";
32 |
33 | #define GET_IP_ADDR(handle, name, var, defaultValue) \
34 | if (nvs_get_u32(handle, name, &var.addr) != ESP_OK) \
35 | { \
36 | var.addr = defaultValue; \
37 | }
38 |
39 | #define GET_INT(handle, name, var, defaultValue) \
40 | if (nvs_get_i32(handle, name, &var) != ESP_OK) \
41 | { \
42 | var = defaultValue; \
43 | }
44 |
45 | #define GET_BOOL(handle, name, var, defaultValue) \
46 | int8_t __##var##_temp; \
47 | if (nvs_get_i8(handle, name, &__##var##_temp) != ESP_OK) \
48 | { \
49 | var = defaultValue; \
50 | } \
51 | else \
52 | { \
53 | var = (__##var##_temp != 0); \
54 | }
55 |
56 | #define SET_IP_ADDR(handle, name, var) ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_set_u32(handle, name, var.addr));
57 | #define SET_INT(handle, name, var) ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_set_i32(handle, name, var));
58 | #define SET_STR(handle, name, var) ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_set_str(handle, name, var));
59 | #define SET_BOOL(handle, name, var) ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_set_i8(handle, name, var ? 1 : 0));
60 |
61 | void Settings::load()
62 | {
63 | uint32_t handle;
64 |
65 | esp_err_t err = nvs_flash_init();
66 | if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
67 | {
68 | ESP_ERROR_CHECK(nvs_flash_erase());
69 | err = nvs_flash_init();
70 | }
71 | ESP_ERROR_CHECK(err);
72 |
73 | ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle));
74 |
75 | size_t adminPasswordLength = sizeof(_adminPassword);
76 | if (nvs_get_str(handle, "adminPassword", _adminPassword, &adminPasswordLength) != ESP_OK)
77 | {
78 | strncpy(_adminPassword, "admin", sizeof(_adminPassword) - 1);
79 | }
80 |
81 | size_t hostnameLength = sizeof(_hostname);
82 | if (nvs_get_str(handle, "hostname", _hostname, &hostnameLength) != ESP_OK)
83 | {
84 | uint8_t baseMac[6];
85 | esp_read_mac(baseMac, ESP_MAC_ETH);
86 | snprintf(_hostname, sizeof(_hostname) - 1, "HB-RF-ETH-%02X%02X%02X", baseMac[3], baseMac[4], baseMac[5]);
87 | }
88 |
89 | GET_BOOL(handle, "useDHCP", _useDHCP, true);
90 | GET_IP_ADDR(handle, "localIP", _localIP, IPADDR_ANY);
91 | GET_IP_ADDR(handle, "netmask", _netmask, IPADDR_ANY);
92 | GET_IP_ADDR(handle, "gateway", _gateway, IPADDR_ANY);
93 | GET_IP_ADDR(handle, "dns1", _dns1, IPADDR_ANY);
94 | GET_IP_ADDR(handle, "dns2", _dns2, IPADDR_ANY);
95 |
96 | GET_INT(handle, "timesource", _timesource, TIMESOURCE_NTP);
97 |
98 | GET_INT(handle, "dcfOffset", _dcfOffset, 40000);
99 |
100 | GET_INT(handle, "gpsBaudrate", _gpsBaudrate, 9600);
101 |
102 | size_t ntpServerLength = sizeof(_ntpServer);
103 | if (nvs_get_str(handle, "ntpServer", _ntpServer, &ntpServerLength) != ESP_OK)
104 | {
105 | strncpy(_ntpServer, "pool.ntp.org", sizeof(_ntpServer) - 1);
106 | }
107 |
108 | GET_INT(handle, "ledBrightness", _ledBrightness, 100);
109 |
110 | nvs_close(handle);
111 | }
112 |
113 | void Settings::save()
114 | {
115 | uint32_t handle;
116 |
117 | ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle));
118 |
119 | SET_STR(handle, "adminPassword", _adminPassword);
120 |
121 | SET_STR(handle, "hostname", _hostname);
122 | SET_BOOL(handle, "useDHCP", _useDHCP);
123 | SET_IP_ADDR(handle, "localIP", _localIP);
124 | SET_IP_ADDR(handle, "netmask", _netmask);
125 | SET_IP_ADDR(handle, "gateway", _gateway);
126 | SET_IP_ADDR(handle, "dns1", _dns1);
127 | SET_IP_ADDR(handle, "dns2", _dns2);
128 |
129 | SET_INT(handle, "timesource", _timesource);
130 |
131 | SET_INT(handle, "dcfOffset", _dcfOffset);
132 |
133 | SET_INT(handle, "gpsBaudrate", _gpsBaudrate);
134 |
135 | SET_STR(handle, "ntpServer", _ntpServer);
136 |
137 | SET_INT(handle, "ledBrightness", _ledBrightness);
138 |
139 | nvs_close(handle);
140 | }
141 |
142 | void Settings::clear()
143 | {
144 | uint32_t handle;
145 |
146 | ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle));
147 | ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_erase_all(handle));
148 | ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_commit(handle));
149 | nvs_close(handle);
150 |
151 | load();
152 | }
153 |
154 | char *Settings::getAdminPassword()
155 | {
156 | return _adminPassword;
157 | }
158 |
159 | void Settings::setAdminPassword(char *adminPassword)
160 | {
161 | strncpy(_adminPassword, adminPassword, sizeof(_adminPassword) - 1);
162 | }
163 |
164 | char *Settings::getHostname()
165 | {
166 | return _hostname;
167 | }
168 |
169 | bool Settings::getUseDHCP()
170 | {
171 | return _useDHCP;
172 | }
173 |
174 | ip4_addr_t Settings::getLocalIP()
175 | {
176 | return _localIP;
177 | }
178 |
179 | ip4_addr_t Settings::getNetmask()
180 | {
181 | return _netmask;
182 | }
183 |
184 | ip4_addr_t Settings::getGateway()
185 | {
186 | return _gateway;
187 | }
188 |
189 | ip4_addr_t Settings::getDns1()
190 | {
191 | return _dns1;
192 | }
193 |
194 | ip4_addr_t Settings::getDns2()
195 | {
196 | return _dns2;
197 | }
198 |
199 | void Settings::setNetworkSettings(char *hostname, bool useDHCP, ip4_addr_t localIP, ip4_addr_t netmask, ip4_addr_t gateway, ip4_addr_t dns1, ip4_addr_t dns2)
200 | {
201 | strncpy(_hostname, hostname, sizeof(_hostname) - 1);
202 | _useDHCP = useDHCP;
203 | _localIP = localIP;
204 | _netmask = netmask;
205 | _gateway = gateway;
206 | _dns1 = dns1;
207 | _dns2 = dns2;
208 | }
209 |
210 | int Settings::getDcfOffset()
211 | {
212 | return _dcfOffset;
213 | }
214 |
215 | void Settings::setDcfOffset(int dcfOffset)
216 | {
217 | _dcfOffset = dcfOffset;
218 | }
219 |
220 | int Settings::getGpsBaudrate()
221 | {
222 | return _gpsBaudrate;
223 | }
224 |
225 | void Settings::setGpsBaudrate(int gpsBaudrate)
226 | {
227 | _gpsBaudrate = gpsBaudrate;
228 | }
229 |
230 | timesource_t Settings::getTimesource()
231 | {
232 | return (timesource_t)_timesource;
233 | }
234 |
235 | void Settings::setTimesource(timesource_t timesource)
236 | {
237 | _timesource = timesource;
238 | }
239 |
240 | char *Settings::getNtpServer()
241 | {
242 | return _ntpServer;
243 | }
244 |
245 | void Settings::setNtpServer(char *ntpServer)
246 | {
247 | strncpy(_ntpServer, ntpServer, sizeof(_ntpServer) - 1);
248 | }
249 |
250 | int Settings::getLEDBrightness()
251 | {
252 | return _ledBrightness;
253 | }
254 |
255 | void Settings::setLEDBrightness(int ledBrightness)
256 | {
257 | _ledBrightness = ledBrightness;
258 | }
259 |
--------------------------------------------------------------------------------
/src/streamparser.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * streamparser.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "streamparser.h"
21 | #include
22 |
23 | StreamParser::StreamParser(bool decodeEscaped, std::function processor) : _bufferPos(0), _state(NO_DATA), _isEscaped(false), _decodeEscaped(decodeEscaped), _processor(processor)
24 | {
25 | }
26 |
27 | void StreamParser::append(unsigned char chr)
28 | {
29 | switch (chr)
30 | {
31 | case 0xfd:
32 | _bufferPos = 0;
33 | _isEscaped = false;
34 | _state = RECEIVE_LENGTH_HIGH_BYTE;
35 | break;
36 |
37 | case 0xfc:
38 | _isEscaped = true;
39 | if (_decodeEscaped)
40 | return;
41 | break;
42 |
43 | default:
44 | if (_isEscaped && _decodeEscaped)
45 | chr |= 0x80;
46 |
47 | switch (_state)
48 | {
49 | case NO_DATA:
50 | case FRAME_COMPLETE:
51 | return; // Do nothing until the first frame prefix occurs
52 |
53 | case RECEIVE_LENGTH_HIGH_BYTE:
54 | _frameLength = (_isEscaped ? chr | 0x80 : chr) << 8;
55 | _state = RECEIVE_LENGTH_LOW_BYTE;
56 | break;
57 |
58 | case RECEIVE_LENGTH_LOW_BYTE:
59 | _frameLength |= (_isEscaped ? chr | 0x80 : chr);
60 | _frameLength += 2; // handle crc as frame data
61 | _framePos = 0;
62 | _state = RECEIVE_FRAME_DATA;
63 | break;
64 |
65 | case RECEIVE_FRAME_DATA:
66 | _framePos++;
67 | _state = (_framePos == _frameLength) ? FRAME_COMPLETE : RECEIVE_FRAME_DATA;
68 | break;
69 | }
70 | _isEscaped = false;
71 | }
72 |
73 | _buffer[_bufferPos++] = chr;
74 |
75 | if (_bufferPos == sizeof(_buffer))
76 | _state = FRAME_COMPLETE;
77 |
78 | if (_state == FRAME_COMPLETE)
79 | {
80 | _processor(_buffer, _bufferPos);
81 | _state = NO_DATA;
82 | }
83 | }
84 |
85 | void StreamParser::append(unsigned char *buffer, uint16_t len)
86 | {
87 | int i;
88 | for (i = 0; i < len; i++)
89 | {
90 | append(buffer[i]);
91 | }
92 | }
93 |
94 | void StreamParser::flush()
95 | {
96 | _state = NO_DATA;
97 | _bufferPos = 0;
98 | _isEscaped = false;
99 | }
100 |
101 | bool StreamParser::getDecodeEscaped()
102 | {
103 | return _decodeEscaped;
104 | }
105 | void StreamParser::setDecodeEscaped(bool decodeEscaped)
106 | {
107 | _decodeEscaped = decodeEscaped;
108 | }
109 |
--------------------------------------------------------------------------------
/src/sysinfo.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * sysinfo.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "sysinfo.h"
21 | #include "freertos/FreeRTOS.h"
22 | #include "freertos/task.h"
23 | #include "esp_ota_ops.h"
24 | #include "esp_log.h"
25 | #include "esp_adc_cal.h"
26 | #include "pins.h"
27 |
28 | #define DEFAULT_VREF 1100
29 |
30 | static const char *TAG = "SysInfo";
31 |
32 | static double _cpuUsage;
33 | static char _serial[13];
34 | static const char *_currentVersion;
35 | static board_type_t _board;
36 |
37 | void updateCPUUsageTask(void *arg)
38 | {
39 | TaskStatus_t *taskStatus = (TaskStatus_t *)malloc(25 * sizeof(TaskStatus_t));
40 |
41 | TaskHandle_t idle0Task = xTaskGetIdleTaskHandleForCPU(0);
42 | TaskHandle_t idle1Task = xTaskGetIdleTaskHandleForCPU(1);
43 |
44 | uint32_t totalRunTime = 0, idleRunTime = 0, lastTotalRunTime = 0, lastIdleRunTime = 0;
45 |
46 | for (;;)
47 | {
48 | vTaskDelay(1000 / portTICK_PERIOD_MS);
49 |
50 | UBaseType_t taskCount = uxTaskGetSystemState(taskStatus, 25, &totalRunTime);
51 |
52 | idleRunTime = 0;
53 |
54 | if (totalRunTime > 0)
55 | {
56 | for (int i = 0; i < taskCount; i++)
57 | {
58 | TaskStatus_t ts = taskStatus[i];
59 |
60 | if (ts.xHandle == idle0Task || ts.xHandle == idle1Task)
61 | {
62 | idleRunTime += ts.ulRunTimeCounter;
63 | }
64 | }
65 | }
66 |
67 | _cpuUsage = 100.0 - ((idleRunTime - lastIdleRunTime) * 100.0 / ((totalRunTime - lastTotalRunTime) * 2));
68 |
69 | lastIdleRunTime = idleRunTime;
70 | lastTotalRunTime = totalRunTime;
71 | }
72 |
73 | free(taskStatus);
74 | vTaskDelete(NULL);
75 | }
76 |
77 | uint32_t get_voltage(adc_unit_t adc_unit, adc_channel_t adc_channel, adc_bits_width_t adc_width, adc_atten_t adc_atten)
78 | {
79 | esp_adc_cal_characteristics_t *adc_chars = reinterpret_cast(calloc(1, sizeof(esp_adc_cal_characteristics_t)));
80 | esp_adc_cal_characterize(adc_unit, adc_atten, adc_width, DEFAULT_VREF, adc_chars);
81 |
82 | adc_gpio_init(adc_unit, adc_channel);
83 |
84 | if (adc_unit == ADC_UNIT_1)
85 | {
86 | adc1_config_width(adc_width);
87 | adc1_config_channel_atten((adc1_channel_t)adc_channel, adc_atten);
88 | }
89 | else
90 | {
91 | adc2_config_channel_atten((adc2_channel_t)adc_channel, adc_atten);
92 | }
93 |
94 | uint32_t voltage;
95 | esp_adc_cal_get_voltage(adc_channel, adc_chars, &voltage);
96 |
97 | free(adc_chars);
98 |
99 | return voltage;
100 | }
101 |
102 | board_type_t detectBoard()
103 | {
104 | uint32_t voltage = get_voltage(BOARD_REV_SENSE_UNIT, BOARD_REV_SENSE_CHANNEL, ADC_WIDTH_BIT_10, ADC_ATTEN_DB_11);
105 |
106 | switch (voltage) // R31/R32
107 | {
108 | case 400 ... 700: // 10K/2K
109 | return BOARD_TYPE_REV_1_10_PUB;
110 |
111 | case 1500 ... 1800: // 10K/10K
112 | return BOARD_TYPE_REV_1_8_SK;
113 |
114 | case 2600 ... 2900: // 2K/10K
115 | return BOARD_TYPE_REV_1_8_PUB;
116 |
117 | case 2901 ... 3200: // 1K/12K
118 | return BOARD_TYPE_REV_1_10_SK;
119 |
120 | default:
121 | ESP_LOGW(TAG, "Could not determine board, voltage: %u", voltage);
122 | return BOARD_TYPE_UNKNOWN;
123 | }
124 | }
125 |
126 | SysInfo::SysInfo()
127 | {
128 | xTaskCreate(updateCPUUsageTask, "UpdateCPUUsage", 4096, NULL, 3, NULL);
129 |
130 | uint8_t baseMac[6];
131 | esp_read_mac(baseMac, ESP_MAC_ETH);
132 | snprintf(_serial, sizeof(_serial), "%02X%02X%02X%02X%02X%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);
133 |
134 | _currentVersion = esp_ota_get_app_description()->version;
135 |
136 | _board = detectBoard();
137 | }
138 |
139 | double SysInfo::getCpuUsage()
140 | {
141 | return _cpuUsage;
142 | }
143 |
144 | double SysInfo::getMemoryUsage()
145 | {
146 | multi_heap_info_t info;
147 | heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
148 |
149 | return 100.0 - (info.total_free_bytes * 100.0 / (info.total_free_bytes + info.total_allocated_bytes));
150 | }
151 |
152 | const char *SysInfo::getSerialNumber()
153 | {
154 | return _serial;
155 | }
156 |
157 | board_type_t SysInfo::getBoardType()
158 | {
159 | return _board;
160 | }
161 |
162 | const char *SysInfo::getCurrentVersion()
163 | {
164 | return _currentVersion;
165 | }
166 |
--------------------------------------------------------------------------------
/src/systemclock.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * systemclock.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "systemclock.h"
21 | #include
22 | #include "esp_log.h"
23 |
24 | static const char *TAG = "SystemClock";
25 |
26 | #define get_tzname(isdst) isdst > 0 ? *(tzname + 1) : *tzname
27 |
28 | void updateRtcTask(void *parameter)
29 | {
30 | Rtc *_rtc = (Rtc *)parameter;
31 |
32 | for (;;)
33 | {
34 | if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY) != 1)
35 | continue;
36 |
37 | struct timeval tv;
38 | gettimeofday(&tv, NULL);
39 | _rtc->SetTime(tv);
40 |
41 | struct tm now;
42 | localtime_r(&tv.tv_sec, &now);
43 |
44 | ESP_LOGI(TAG, "Updated RTC to %02d-%02d-%02d %02d:%02d:%02d %s", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, get_tzname(now.tm_isdst));
45 | }
46 |
47 | vTaskDelete(NULL);
48 | }
49 |
50 | SystemClock::SystemClock(Rtc *rtc) : _rtc(rtc)
51 | {
52 | }
53 |
54 | void SystemClock::start(void)
55 | {
56 | if (_rtc)
57 | {
58 | struct timeval tv = _rtc->GetTime();
59 | settimeofday(&tv, NULL);
60 | _lastSyncTime = tv;
61 |
62 | time_t nowtime = tv.tv_sec;
63 | struct tm *now = localtime(&nowtime);
64 |
65 | ESP_LOGI(TAG, "Updated time from RTC to %02d-%02d-%02d %02d:%02d:%02d %s", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, get_tzname(now->tm_isdst));
66 |
67 | xTaskCreate(updateRtcTask, "SystemClock_RtcUpdateTask", 4096, _rtc, 10, &_tHandle);
68 | }
69 | }
70 |
71 | void SystemClock::stop(void)
72 | {
73 | if (_tHandle != NULL)
74 | {
75 | vTaskDelete(_tHandle);
76 | _tHandle = NULL;
77 | }
78 | }
79 |
80 | void SystemClock::setTime(struct timeval *tv)
81 | {
82 | settimeofday(tv, NULL);
83 | _lastSyncTime = *tv;
84 |
85 | if (_tHandle != NULL)
86 | {
87 | xTaskNotifyGive(_tHandle);
88 | }
89 | }
90 |
91 | struct timeval SystemClock::getTime()
92 | {
93 | struct timeval tv;
94 | gettimeofday(&tv, NULL);
95 | return tv;
96 | }
97 |
98 | struct timeval SystemClock::getLastSyncTime()
99 | {
100 | return _lastSyncTime;
101 | }
102 |
103 | struct tm SystemClock::getLocalTime(void)
104 | {
105 | time_t now;
106 | time(&now);
107 |
108 | struct tm info;
109 | localtime_r(&now, &info);
110 |
111 | return info;
112 | }
113 |
--------------------------------------------------------------------------------
/src/updatecheck.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * updatecheck.cpp is part of the HB-RF-ETH firmware - https://github.com/alexreinert/HB-RF-ETH
3 | *
4 | * Copyright 2022 Alexander Reinert
5 | *
6 | * The HB-RF-ETH firmware is licensed under a
7 | * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
8 | *
9 | * You should have received a copy of the license along with this
10 | * work. If not, see .
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 |
20 | #include "updatecheck.h"
21 | #include "esp_http_client.h"
22 | #include "esp_log.h"
23 | #include "string.h"
24 | #include "cJSON.h"
25 |
26 | static const char *TAG = "UpdateCheck";
27 |
28 | void _update_check_task_func(void *parameter)
29 | {
30 | {
31 | ((UpdateCheck *)parameter)->_taskFunc();
32 | }
33 | }
34 |
35 | UpdateCheck::UpdateCheck(SysInfo* sysInfo, LED *statusLED) : _sysInfo(sysInfo), _statusLED(statusLED)
36 | {
37 | }
38 |
39 | void UpdateCheck::start()
40 | {
41 | xTaskCreate(_update_check_task_func, "UpdateCheck", 4096, this, 3, &_tHandle);
42 | }
43 |
44 | void UpdateCheck::stop()
45 | {
46 | vTaskDelete(_tHandle);
47 | }
48 |
49 | const char *UpdateCheck::getLatestVersion()
50 | {
51 | return _latestVersion;
52 | }
53 |
54 | void UpdateCheck::_updateLatestVersion()
55 | {
56 | char url[128];
57 | snprintf(url, sizeof(url), "https://www.debmatic.de/hb-rf-eth/latestVersion.json?board=%d&serial=%s&version=%s", _sysInfo->getBoardType(), _sysInfo->getSerialNumber(), _sysInfo->getCurrentVersion());
58 |
59 | esp_http_client_config_t config = {};
60 | config.url = url;
61 |
62 | esp_http_client_handle_t client = esp_http_client_init(&config);
63 |
64 | if (esp_http_client_perform(client) == ESP_OK)
65 | {
66 | char buffer[128];
67 | int len = esp_http_client_read(client, buffer, sizeof(buffer) - 1);
68 |
69 | if (len > 0)
70 | {
71 | buffer[len] = 0;
72 | cJSON *json = cJSON_Parse(buffer);
73 |
74 | char *latestVersion = cJSON_GetStringValue(cJSON_GetObjectItem(json, "version"));
75 | if (latestVersion != NULL)
76 | {
77 | strcpy(_latestVersion, latestVersion);
78 | }
79 | cJSON_Delete(json);
80 | }
81 | }
82 |
83 | esp_http_client_cleanup(client);
84 | }
85 |
86 | void UpdateCheck::_taskFunc()
87 | {
88 | // some time for initial network connection
89 | vTaskDelay(30000 / portTICK_PERIOD_MS);
90 |
91 | for (;;)
92 | {
93 | ESP_LOGI(TAG, "Start checking for the latest available firmware.");
94 | _updateLatestVersion();
95 |
96 | if (strcmp(_latestVersion, "n/a") != 0 && strcmp(_sysInfo->getCurrentVersion(), _latestVersion) < 0)
97 | {
98 | ESP_LOGW(TAG, "An updated firmware with version %s is available.", _latestVersion);
99 | _statusLED->setState(LED_STATE_BLINK_SLOW);
100 | }
101 | else
102 | {
103 | ESP_LOGI(TAG, "There is no newer firmware available.");
104 | }
105 |
106 | vTaskDelay((8 * 60 * 60000) / portTICK_PERIOD_MS); // 8h
107 | }
108 |
109 | vTaskDelete(NULL);
110 | }
--------------------------------------------------------------------------------
/test/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for PIO Unit Testing and project tests.
3 |
4 | Unit Testing is a software testing method by which individual units of
5 | source code, sets of one or more MCU program modules together with associated
6 | control data, usage procedures, and operating procedures, are tested to
7 | determine whether they are fit for use. Unit testing finds problems early
8 | in the development cycle.
9 |
10 | More information about PIO Unit Testing:
11 | - https://docs.platformio.org/page/plus/unit-testing.html
12 |
--------------------------------------------------------------------------------
/version.txt:
--------------------------------------------------------------------------------
1 | 1.3.0
--------------------------------------------------------------------------------
/webui/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env"
4 | ]
5 | }
--------------------------------------------------------------------------------
/webui/.compressrc:
--------------------------------------------------------------------------------
1 | {
2 | "test": ".",
3 | "threshold": undefined,
4 | concurrency: 2,
5 | "gzip": {
6 | "enabled": true,
7 | "numiterations": 15,
8 | "blocksplitting": true,
9 | "blocksplittinglast": false,
10 | "blocksplittingmax": 15,
11 | "zlib": true,
12 | "zlibLevel": 9,
13 | "zlibMemLevel": 9
14 | },
15 | "brotli": {
16 | "enabled": false
17 | },
18 | compressOutput: false
19 | }
--------------------------------------------------------------------------------
/webui/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexreinert/HB-RF-ETH/5af00aa4438fe4aed33a2963763b5fa6c1f679f8/webui/favicon.ico
--------------------------------------------------------------------------------
/webui/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HB-RF-ETH
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/webui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webui",
3 | "version": "1.3.0",
4 | "description": "WebUI of HB-RF-ETH firmware",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "parcel index.htm",
8 | "build": "parcel build index.htm --no-source-maps --no-content-hash"
9 | },
10 | "keywords": [],
11 | "author": "Alexander Reinert",
12 | "license": "CC-BY-NC-SA-4.0",
13 | "dependencies": {
14 | "axios": "^0.21.1",
15 | "bootstrap": "^4.6.0",
16 | "bootstrap-vue": "^2.21.2",
17 | "vue": "^2.6.14",
18 | "vue-hot-reload-api": "^2.3.4",
19 | "vue-i18n": "^8.24.5",
20 | "vue-router": "^3.5.2",
21 | "vuelidate": "^0.7.6",
22 | "vuex": "^3.6.2"
23 | },
24 | "devDependencies": {
25 | "@vue/component-compiler-utils": "^3.2.2",
26 | "babel-core": "^6.26.3",
27 | "babel-preset-env": "^1.7.0",
28 | "parcel-bundler": "^1.3.1",
29 | "parcel-plugin-compress": "^2.0.2",
30 | "vue-template-compiler": "^2.6.14"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/webui/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/webui/src/firmwareupdate.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 | {{ latestVersion }}
20 |
21 |
22 | Github
27 |
28 |
29 |
30 |
31 |
37 |
38 |
45 | {{ $t("uploadSuccess") }}
52 | {{ $t("uploadError") }}
59 |
60 | {{ $t('upload') }}
66 |
67 |
68 |
69 |
70 |
71 |
165 |
166 |
--------------------------------------------------------------------------------
/webui/src/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | HB-RF-ETH
4 |
5 |
6 |
7 | {{ $t('home') }}
8 | {{ $t('settings') }}
9 | {{ $t('firmware') }}
10 | {{ $t('about') }}
11 |
12 |
13 |
14 | {{ $t('logout') }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/webui/src/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
24 |
25 |
--------------------------------------------------------------------------------
/webui/src/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
16 |
17 | {{ $t('loginError') }}
24 |
25 | {{ $t('login') }}
31 |
32 |
33 |
34 |
35 |
36 |
118 |
119 |
--------------------------------------------------------------------------------
/webui/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | import Vuex from "vuex";
4 | Vue.use(Vuex);
5 |
6 | import VueRouter from 'vue-router'
7 | Vue.use(VueRouter)
8 |
9 | import Axios from 'axios'
10 |
11 | import 'bootstrap/dist/css/bootstrap.css'
12 | import 'bootstrap-vue/dist/bootstrap-vue.css'
13 |
14 | import App from './app.vue'
15 | import Home from './home.vue'
16 | import Settings from "./settings.vue";
17 | import FirmwareUpdate from "./firmwareupdate.vue";
18 | import Login from './login.vue'
19 | import About from './about.vue'
20 |
21 | const moduleLogin = {
22 | namespaced: true,
23 | state: () => ({
24 | isLoggedIn: localStorage.getItem("hb-rf-eth-pw") != null,
25 | token: localStorage.getItem("hb-rf-eth-pw")
26 | }),
27 | mutations: {
28 | login(state, token) {
29 | localStorage.setItem("hb-rf-eth-pw", token);
30 | state.token = token;
31 | state.isLoggedIn = true;
32 | },
33 | logout(state) {
34 | state.isLoggedIn = false;
35 | localStorage.removeItem("hb-rf-eth-pw");
36 | state.token = "";
37 | },
38 | },
39 | actions: {
40 | tryLogin(context, password) {
41 | return new Promise((resolve, reject) => {
42 | Axios
43 | .post("/login.json", { password: password })
44 | .then(
45 | response => {
46 | if (response.data.isAuthenticated) {
47 | context.commit("login", response.data.token);
48 |
49 | resolve();
50 | } else {
51 | reject();
52 | }
53 | },
54 | () => {
55 | reject();
56 | })
57 | });
58 | },
59 | logout(context) {
60 | context.commit("logout");
61 | }
62 | }
63 | };
64 |
65 | const moduleSysInfo = {
66 | namespaced: true,
67 | state: () => ({
68 | serial: "",
69 | currentVersion: "",
70 | latestVersion: "",
71 | rawUartRemoteAddress: "",
72 | memoryUsage: 0.0,
73 | cpuUsage: 0.0,
74 | radioModuleType: "",
75 | radioModuleSerial: "",
76 | radioModuleBidCosRadioMAC: "",
77 | radioModuleHmIPRadioMAC: "",
78 | radioModuleSGTIN: ""
79 | }),
80 | mutations: {
81 | sysInfo(state, newState) {
82 | state.serial = newState.serial;
83 | state.currentVersion = newState.currentVersion;
84 | state.latestVersion = newState.latestVersion;
85 | state.rawUartRemoteAddress = newState.rawUartRemoteAddress;
86 | state.memoryUsage = newState.memoryUsage;
87 | state.cpuUsage = newState.cpuUsage;
88 | state.radioModuleType = newState.radioModuleType;
89 | state.radioModuleSerial = newState.radioModuleSerial;
90 | state.radioModuleBidCosRadioMAC = newState.radioModuleBidCosRadioMAC;
91 | state.radioModuleHmIPRadioMAC = newState.radioModuleHmIPRadioMAC;
92 | state.radioModuleSGTIN = newState.radioModuleSGTIN;
93 | },
94 | },
95 | actions: {
96 | update(context) {
97 | return new Promise((resolve, reject) => {
98 | Axios
99 | .get("/sysinfo.json")
100 | .then(
101 | response => {
102 | context.commit("sysInfo", response.data.sysInfo);
103 | resolve();
104 | }
105 | ,
106 | () => {
107 | reject();
108 | });
109 | });
110 | }
111 | }
112 | };
113 |
114 | const moduleSettings = {
115 | namespaced: true,
116 | state: () => ({
117 | hostname: "",
118 | useDHCP: true,
119 | localIP: "",
120 | netmask: "",
121 | gateway: "",
122 | dns1: "",
123 | dns2: "",
124 | timesource: 0,
125 | dcfOffset: 0,
126 | gpsBaudrate: 9600,
127 | ntpServer: "",
128 | ledBrightness: 100,
129 | }),
130 | mutations: {
131 | settings(state, value) {
132 | state.hostname = value.hostname;
133 | state.useDHCP = value.useDHCP;
134 | state.localIP = value.localIP;
135 | state.netmask = value.netmask;
136 | state.gateway = value.gateway;
137 | state.dns1 = value.dns1;
138 | state.dns2 = value.dns2;
139 | state.timesource = value.timesource;
140 | state.dcfOffset = value.dcfOffset;
141 | state.gpsBaudrate = value.gpsBaudrate;
142 | state.ntpServer = value.ntpServer;
143 | state.ledBrightness = value.ledBrightness;
144 | },
145 | },
146 | actions: {
147 | load(context) {
148 | return new Promise((resolve, reject) => {
149 | Axios
150 | .get("/settings.json")
151 | .then(
152 | response => {
153 | context.commit("settings", response.data.settings);
154 | resolve();
155 | },
156 | () => { reject(); });
157 | });
158 | },
159 | save(context, settings) {
160 | return new Promise((resolve, reject) => {
161 | Axios
162 | .post("/settings.json", settings)
163 | .then(
164 | response => {
165 | context.commit("settings", response.data.settings);
166 | resolve();
167 | },
168 | () => { reject(); });
169 | });
170 | }
171 | }
172 | };
173 |
174 | const moduleFirmwareUpdate = {
175 | namespaced: true,
176 | state: () => ({
177 | progress: 0,
178 | }),
179 | mutations: {
180 | progress(state, value) {
181 | state.progress = value;
182 | },
183 | },
184 | actions: {
185 | update(context, file) {
186 | return new Promise((resolve, reject) => {
187 | context.commit("progress", 0);
188 |
189 | var form = new FormData();
190 | form.append("file", file, file.name);
191 | form.append("upload_file", true);
192 |
193 | Axios
194 | .post("/ota_update", form, {
195 | headers: {
196 | 'Content-Type': 'multipart/form-data'
197 | },
198 | onUploadProgress: event => {
199 | if (event.lengthComputable) {
200 | context.commit("progress", Math.ceil((event.loaded || event.position) / event.total * 100));
201 | }
202 | }
203 | })
204 | .then(
205 | response => {
206 | context.commit("progress", 0);
207 | resolve();
208 | },
209 | (err) => {
210 | reject();
211 | });
212 | });
213 | }
214 | }
215 | };
216 |
217 | const store = new Vuex.Store({
218 | modules: {
219 | login: moduleLogin,
220 | sysInfo: moduleSysInfo,
221 | settings: moduleSettings,
222 | firmwareUpdate: moduleFirmwareUpdate,
223 | }
224 | });
225 |
226 | const router = new VueRouter({
227 | mode: "history",
228 | routes: [
229 | { path: '/', component: Home },
230 | { path: '/settings', component: Settings, meta: { requiresAuth: true } },
231 | { path: '/firmware', component: FirmwareUpdate, meta: { requiresAuth: true } },
232 | { path: '/about', component: About },
233 | { path: '/login', component: Login },
234 | ]
235 | });
236 |
237 | router.beforeEach((to, from, next) => {
238 | if (to.matched.some(r => r.meta.requiresAuth)) {
239 | if (!store.state.login.isLoggedIn) {
240 | next({
241 | path: '/login',
242 | query: { redirect: to.fullPath }
243 | })
244 | return;
245 | }
246 | }
247 |
248 | next()
249 | });
250 |
251 | Axios.interceptors.request.use(
252 | request => {
253 | if (store.state.login.isLoggedIn) {
254 | request.headers['Authorization'] = 'Token ' + store.state.login.token;
255 | }
256 | return request;
257 | },
258 | error => { Promise.reject(error) }
259 | );
260 |
261 | Axios.interceptors.response.use(
262 | response => { return response; },
263 | error => {
264 | if (error.response.status == 401) {
265 | store.dispatch("login/logout");
266 | router.go();
267 | }
268 |
269 | return Promise.reject(error);
270 | }
271 | );
272 |
273 | new Vue({
274 | el: '#app',
275 | store,
276 | router,
277 | render: h => h(App)
278 | });
279 |
--------------------------------------------------------------------------------
/webui/src/settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 | {{ $t('enabled') }}
36 | {{ $t('disabled') }}
37 |
38 |
39 |
40 |
46 |
47 |
48 |
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {{ $t('ntp') }}
73 | {{ $t('dcf') }}
74 | {{ $t('gps') }}
75 |
76 |
77 |
78 |
84 |
85 |
86 |
87 |
93 |
94 |
95 |
96 |
97 | 4800
98 | 9600
99 | 19200
100 | 38400
101 | 57600
102 | 115200
103 |
104 |
105 |
106 |
107 |
108 |
109 | 0
110 | 5
111 | 10
112 | 25
113 | 50
114 | 75
115 | 100
116 |
117 |
118 |
119 |
120 | {{ $t("saveSuccess") }}
127 | {{ $t("saveError") }}
134 |
135 |
136 | {{ $t('save') }}
142 |
143 |
144 |
145 |
146 |
147 |
416 |
417 |
--------------------------------------------------------------------------------
/webui/src/sysinfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
121 |
122 |
--------------------------------------------------------------------------------