From 2d44f3d5a6c6a4b75ffa5081b011a93407d1026d Mon Sep 17 00:00:00 2001 From: localhorst Date: Wed, 1 Mar 2023 21:45:23 +0100 Subject: [PATCH 01/10] first dummy backend --- README.md | 2 + msv_clubhouse_backend.py | 169 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 msv_clubhouse_backend.py diff --git a/README.md b/README.md index 9ddc370..35af250 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,7 @@ TTN LoRa frequency / region 3. Build with ESP-IDF extension in VSCodium +## MQTT Endpoint ## +`pip3 install paho-mqtt` diff --git a/msv_clubhouse_backend.py b/msv_clubhouse_backend.py new file mode 100644 index 0000000..8d1b86b --- /dev/null +++ b/msv_clubhouse_backend.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com + Date of creation: 2022/03/01 + Date of last modification: 2023/03/01 +""" + +from http.server import BaseHTTPRequestHandler, HTTPServer +import time +import threading +from datetime import datetime +from urllib.parse import urlsplit, parse_qs +from random import randrange +import os, sys, logging, time +import paho.mqtt.client as mqtt +import json +import csv +from datetime import datetime + +hostName = "127.0.0.1" +serverPort = 9101 +exporter_prefix = "msv_clubhouse_" + +User = "APP UUID @ttn" +Password = "API Key" +theRegion = "EU1" # The region you are using + +scrape_healthy = True +startTime = datetime.now() +node_metrics = list() +mutex = threading.Lock() +request_count = 0 + +# MQTT event functions +def on_connect(mqttc, obj, flags, rc): + print("\nConnect: rc = " + str(rc)) + +def on_message(mqttc, obj, msg): + print("\nMessage: " + msg.topic + " " + str(msg.qos)) # + " " + str(msg.payload)) + parsedJSON = json.loads(msg.payload) + print(json.dumps(parsedJSON, indent=4)) # Uncomment this to fill your terminal screen with JSON + uplink_message = parsedJSON["uplink_message"]; + frm_payload = uplink_message["frm_payload"]; + print("MSG: " + frm_payload) + print("MSG: " + str(type(frm_payload))) + +def on_subscribe(mqttc, obj, mid, granted_qos): + print("\nSubscribe: " + str(mid) + " " + str(granted_qos)) + +def on_log(mqttc, obj, level, string): + print("\nLog: "+ string) + logging_level = mqtt.LOGGING_LEVEL[level] + logging.log(logging_level, string) + +class RequestHandler(BaseHTTPRequestHandler): + + def get_metrics(self): + global request_count + global node_metrics + global exporter_prefix + global mutex + mutex.acquire() + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes(exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) + self.wfile.write(bytes(exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) + self.wfile.write(bytes(exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) + + for metric in node_metrics: + #print(metric) + self.wfile.write(bytes(exporter_prefix + metric + "\n", "utf-8")) + + mutex.release() + + def do_GET(self): + global request_count + request_count = request_count + 1 + print("Request: " + self.path) + if (self.path.startswith("/metrics")): + self.get_metrics() + else: + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("MSV Clubhouse exporter", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes('

msv-clubhouse exporter based on data from LoRaWAN TTN node.

', "utf-8")) + self.wfile.write(bytes('

Metrics

', "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + + +def update_metrics(): + while True: + print("set data from ttn") + global node_metrics + global mutex + global scrape_healthy + mutex.acquire() + scrape_healthy = True + node_metrics.clear() + + try: + + node_metrics.append("target_temperature " + str(int(42))) + node_metrics.append("valve " + str(int(42))) + node_metrics.append("low_battery " + str(int(42))) + node_metrics.append("window_open " + str(int(42))) + except Exception as ex: + print("unable to poll data from TTN! error: " + str(ex)) + scrape_healthy = False + pass + mutex.release() + time.sleep(300) + +def poll_mqtt(mqttc): + while True: + mqttc.loop(10) # seconds timeout / blocking time + print(".", end="", flush=True) # feedback to the user that something is actually happening + + +def main(): + print("start") + + print("Init mqtt client") + mqttc = mqtt.Client() + + print("Assign callbacks") + mqttc.on_connect = on_connect + mqttc.on_subscribe = on_subscribe + mqttc.on_message = on_message + + print("Connect") + # Setup authentication from settings above + mqttc.username_pw_set(User, Password) + + # IMPORTANT - this enables the encryption of messages + mqttc.tls_set() # default certification authority of the system + + mqttc.connect(theRegion.lower() + ".cloud.thethings.network", 8883, 60) + + print("Subscribe") + mqttc.subscribe("#", 0) # all device uplinks + + print("And run forever") + poll_mqtt_thread = threading.Thread(target=poll_mqtt, args=((mqttc,))) + poll_mqtt_thread.start() + + webServer = HTTPServer((hostName, serverPort), RequestHandler) + + print("Server started http://%s:%s" % (hostName, serverPort)) + + update_metrics_thread = threading.Thread(target=update_metrics, args=()) + update_metrics_thread.start() + + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + print("Server stopped.") + update_metrics_thread.join() + poll_mqtt_thread.join() + +if __name__ == "__main__": + main() \ No newline at end of file -- 2.47.1 From e3febdc6ed258357d1da52068e333b7fb8e43308 Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 18:40:44 +0100 Subject: [PATCH 02/10] cleanup --- __pycache__/config.cpython-310.pyc | Bin 0 -> 583 bytes config.py | 14 +++ msv_clubhouse_backend.py | 134 ++++++++++------------------- 3 files changed, 61 insertions(+), 87 deletions(-) create mode 100644 __pycache__/config.cpython-310.pyc create mode 100644 config.py diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5bda93d9aff104816cf7228b3943a26b7fadcfd GIT binary patch literal 583 zcmZuv(Qeu>6m=k!6sg@8Op3IpN^JrOkae0SA`+TXHIxPf@kEi{gSYOwZ*sOtypgcRGK~ zDiKLrRl5~3ER(eyQ5>>_WQUt-ZB19(d}#laZo{0(DM86PiT?B)lB3XQ(L=rym`-o#&W7(-OSwUsWLRI$=G%l{saz36R&qYyT3E6u{WOB`poi& zLucd}@Wyn_>0+e!-QK-#!n!^0xM$6>83c$#K~PFD$(b9bxNPN^K4N-DC@bIKrycj0 z20Mz^$x|uu0|xvOv}{(<1AZ=BRY38EYxf0Ei9g{l`k|;XNwNB>ySho3%5}K-tHVV` l5LP3St&@$idoHDqT+(8TKULo$KjkFW#D*wvtBHan`~;Mbq7(oC literal 0 HcmV?d00001 diff --git a/config.py b/config.py new file mode 100644 index 0000000..77b3c2f --- /dev/null +++ b/config.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com + Date of creation: 2023/03/02 + Date of last modification: 2023/03/02 +""" + +hostName = "127.0.0.1" +serverPort = 9101 +exporter_prefix = "msv_clubhouse_" + +ttn_user = "USERID@ttn" +ttn_key = "API KEY" +ttn_region = "EU1" \ No newline at end of file diff --git a/msv_clubhouse_backend.py b/msv_clubhouse_backend.py index 8d1b86b..546b5c7 100644 --- a/msv_clubhouse_backend.py +++ b/msv_clubhouse_backend.py @@ -2,28 +2,16 @@ # -*- coding: utf-8 -*- """ Author: Hendrik Schutter, mail@hendrikschutter.com Date of creation: 2022/03/01 - Date of last modification: 2023/03/01 + Date of last modification: 2023/03/02 """ from http.server import BaseHTTPRequestHandler, HTTPServer -import time -import threading -from datetime import datetime -from urllib.parse import urlsplit, parse_qs -from random import randrange -import os, sys, logging, time import paho.mqtt.client as mqtt -import json -import csv from datetime import datetime - -hostName = "127.0.0.1" -serverPort = 9101 -exporter_prefix = "msv_clubhouse_" - -User = "APP UUID @ttn" -Password = "API Key" -theRegion = "EU1" # The region you are using +import threading +import time +import json +import config scrape_healthy = True startTime = datetime.now() @@ -31,45 +19,23 @@ node_metrics = list() mutex = threading.Lock() request_count = 0 -# MQTT event functions -def on_connect(mqttc, obj, flags, rc): - print("\nConnect: rc = " + str(rc)) - -def on_message(mqttc, obj, msg): - print("\nMessage: " + msg.topic + " " + str(msg.qos)) # + " " + str(msg.payload)) - parsedJSON = json.loads(msg.payload) - print(json.dumps(parsedJSON, indent=4)) # Uncomment this to fill your terminal screen with JSON - uplink_message = parsedJSON["uplink_message"]; - frm_payload = uplink_message["frm_payload"]; - print("MSG: " + frm_payload) - print("MSG: " + str(type(frm_payload))) - -def on_subscribe(mqttc, obj, mid, granted_qos): - print("\nSubscribe: " + str(mid) + " " + str(granted_qos)) - -def on_log(mqttc, obj, level, string): - print("\nLog: "+ string) - logging_level = mqtt.LOGGING_LEVEL[level] - logging.log(logging_level, string) - class RequestHandler(BaseHTTPRequestHandler): def get_metrics(self): global request_count global node_metrics - global exporter_prefix global mutex mutex.acquire() self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() - self.wfile.write(bytes(exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) - self.wfile.write(bytes(exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) - self.wfile.write(bytes(exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) for metric in node_metrics: #print(metric) - self.wfile.write(bytes(exporter_prefix + metric + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + metric + "\n", "utf-8")) mutex.release() @@ -91,70 +57,65 @@ class RequestHandler(BaseHTTPRequestHandler): self.wfile.write(bytes("", "utf-8")) self.wfile.write(bytes("", "utf-8")) +def update_metrics(payload, metadata): + print("Payload: "+ str(payload)) + print("Metadata: "+ str(metadata)) -def update_metrics(): - while True: - print("set data from ttn") - global node_metrics - global mutex - global scrape_healthy - mutex.acquire() - scrape_healthy = True - node_metrics.clear() + print("set data from ttn") + global node_metrics + global mutex + global scrape_healthy + mutex.acquire() + scrape_healthy = True + node_metrics.clear() - try: - + try: node_metrics.append("target_temperature " + str(int(42))) node_metrics.append("valve " + str(int(42))) node_metrics.append("low_battery " + str(int(42))) node_metrics.append("window_open " + str(int(42))) - except Exception as ex: - print("unable to poll data from TTN! error: " + str(ex)) - scrape_healthy = False - pass - mutex.release() - time.sleep(300) + except Exception as ex: + print("unable to poll data from TTN! error: " + str(ex)) + scrape_healthy = False + pass + mutex.release() + time.sleep(300) + +def on_connect(mqttc, obj, flags, rc): + print("\nConnected to MQTT: rc = " + str(rc)) + +def on_message(mqttc, obj, msg): + #print("\nMessage: " + msg.topic + " " + str(msg.qos)) + parsedJSON = json.loads(msg.payload) + #print(json.dumps(parsedJSON, indent=4)) + uplink_message = parsedJSON["uplink_message"]; + update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"]) + +def on_subscribe(mqttc, obj, mid, granted_qos): + print("\nSubscribed to MQTT: " + str(mid) + " " + str(granted_qos)) def poll_mqtt(mqttc): while True: - mqttc.loop(10) # seconds timeout / blocking time - print(".", end="", flush=True) # feedback to the user that something is actually happening - + mqttc.loop(10) # seconds timeout def main(): - print("start") + print("starting ...") - print("Init mqtt client") mqttc = mqtt.Client() - - print("Assign callbacks") mqttc.on_connect = on_connect mqttc.on_subscribe = on_subscribe mqttc.on_message = on_message - - print("Connect") - # Setup authentication from settings above - mqttc.username_pw_set(User, Password) - - # IMPORTANT - this enables the encryption of messages - mqttc.tls_set() # default certification authority of the system - - mqttc.connect(theRegion.lower() + ".cloud.thethings.network", 8883, 60) - - print("Subscribe") + mqttc.username_pw_set(config.ttn_user, config.ttn_key) + mqttc.tls_set() + mqttc.connect(config.ttn_region.lower() + ".cloud.thethings.network", 8883, 60) mqttc.subscribe("#", 0) # all device uplinks - print("And run forever") + # run mqtt in thread forever poll_mqtt_thread = threading.Thread(target=poll_mqtt, args=((mqttc,))) poll_mqtt_thread.start() - webServer = HTTPServer((hostName, serverPort), RequestHandler) - - print("Server started http://%s:%s" % (hostName, serverPort)) - - update_metrics_thread = threading.Thread(target=update_metrics, args=()) - update_metrics_thread.start() - + webServer = HTTPServer((config.hostName, config.serverPort), RequestHandler) + print("Server started http://%s:%s" % (config.hostName, config.serverPort)) try: webServer.serve_forever() except KeyboardInterrupt: @@ -162,7 +123,6 @@ def main(): webServer.server_close() print("Server stopped.") - update_metrics_thread.join() poll_mqtt_thread.join() if __name__ == "__main__": -- 2.47.1 From 239a2517ba3f891bb699188516f0ff0df1ac6078 Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 18:42:51 +0100 Subject: [PATCH 03/10] update gitignore --- .gitignore | 1 + config.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index b02f414..cfdef90 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,4 @@ dkms.conf *.out *.app +config.py diff --git a/config.py b/config.py index 77b3c2f..b12393a 100644 --- a/config.py +++ b/config.py @@ -9,6 +9,6 @@ hostName = "127.0.0.1" serverPort = 9101 exporter_prefix = "msv_clubhouse_" -ttn_user = "USERID@ttn" -ttn_key = "API KEY" -ttn_region = "EU1" \ No newline at end of file +ttn_user = "689427bb344c937172c04b172@ttn" +ttn_key = "NNSXS.33YDGTEWAM6USZ3YAAXQE2MHSU5HZ7XQ3GKTNIQ.C7DOJEHWYOZCFOS5ITRP7DJSJOE3TDYCCHFS7ZKANAQWF36N5RYA" +ttn_region = "EU1" \ No newline at end of file -- 2.47.1 From 3ba5169e1cea2e0a50bb104d05c04e1f042fe90c Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 18:45:35 +0100 Subject: [PATCH 04/10] delete config --- config.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 config.py diff --git a/config.py b/config.py deleted file mode 100644 index b12393a..0000000 --- a/config.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" Author: Hendrik Schutter, mail@hendrikschutter.com - Date of creation: 2023/03/02 - Date of last modification: 2023/03/02 -""" - -hostName = "127.0.0.1" -serverPort = 9101 -exporter_prefix = "msv_clubhouse_" - -ttn_user = "689427bb344c937172c04b172@ttn" -ttn_key = "NNSXS.33YDGTEWAM6USZ3YAAXQE2MHSU5HZ7XQ3GKTNIQ.C7DOJEHWYOZCFOS5ITRP7DJSJOE3TDYCCHFS7ZKANAQWF36N5RYA" -ttn_region = "EU1" \ No newline at end of file -- 2.47.1 From bbdf520a5e745bb44d3a37892e16cefaa6cb856b Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 18:46:43 +0100 Subject: [PATCH 05/10] add dummy config --- config.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 config.py diff --git a/config.py b/config.py new file mode 100644 index 0000000..77b3c2f --- /dev/null +++ b/config.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com + Date of creation: 2023/03/02 + Date of last modification: 2023/03/02 +""" + +hostName = "127.0.0.1" +serverPort = 9101 +exporter_prefix = "msv_clubhouse_" + +ttn_user = "USERID@ttn" +ttn_key = "API KEY" +ttn_region = "EU1" \ No newline at end of file -- 2.47.1 From dfba150bd67eaafaa976453ca44402187e0d6d09 Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 21:00:29 +0100 Subject: [PATCH 06/10] parse all metrics from payload --- .gitignore | 155 ++++++++++++++++++++++++++++++++++++++- README.md | 75 +++++++++++++++++++ msv_clubhouse_backend.py | 54 ++++++++++---- 3 files changed, 270 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index cfdef90..9fa915b 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,157 @@ dkms.conf *.out *.app -config.py +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/README.md b/README.md index 35af250..3a85b12 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,78 @@ TTN LoRa frequency / region `pip3 install paho-mqtt` + +JS Payload Formatter: + +``` +function Decoder(bytes, port) { + var decoded = {}; + + // temperature + rawTemp = bytes[0] + bytes[1] * 256; + decoded.degreesC = sflt162f(rawTemp) * 100; + + // pressure + rawPressure = bytes[2] + bytes[3] * 256; + decoded.pressure = sflt162f(rawPressure) * 100; + + // windspeed + rawWindspeed = bytes[4] + bytes[5] * 256; + decoded.windspeed = sflt162f(rawWindspeed) * 100; + + // winddirection + rawWinddirection = bytes[6] + bytes[7] * 256; + decoded.winddirection = sflt162f(rawWinddirection) * 100; + + if(bytes[8] === 0){ + decoded.dooropen = false; + }else{ + decoded.dooropen = true; + } + + + return decoded; +} + +function sflt162f(rawSflt16) + { + // rawSflt16 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFFF + // bit 15 is the sign bit + // bits 14..11 are the exponent + // bits 10..0 are the the mantissa. Unlike IEEE format, + // the msb is transmitted; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the open interval (-1.0, 1.0); + // + + // throw away high bits for repeatability. + rawSflt16 &= 0xFFFF; + + // special case minus zero: + if (rawSflt16 == 0x8000) + return -0.0; + + // extract the sign. + var sSign = ((rawSflt16 & 0x8000) !== 0) ? -1 : 1; + + // extract the exponent + var exp1 = (rawSflt16 >> 11) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawSflt16 & 0x7FF) / 2048.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } + ``` \ No newline at end of file diff --git a/msv_clubhouse_backend.py b/msv_clubhouse_backend.py index 546b5c7..fa1ae82 100644 --- a/msv_clubhouse_backend.py +++ b/msv_clubhouse_backend.py @@ -58,10 +58,9 @@ class RequestHandler(BaseHTTPRequestHandler): self.wfile.write(bytes("", "utf-8")) def update_metrics(payload, metadata): - print("Payload: "+ str(payload)) - print("Metadata: "+ str(metadata)) + #print("Payload: "+ str(payload)) + #print("Metadata: "+ str(metadata)) - print("set data from ttn") global node_metrics global mutex global scrape_healthy @@ -69,17 +68,46 @@ def update_metrics(payload, metadata): scrape_healthy = True node_metrics.clear() - try: - node_metrics.append("target_temperature " + str(int(42))) - node_metrics.append("valve " + str(int(42))) - node_metrics.append("low_battery " + str(int(42))) - node_metrics.append("window_open " + str(int(42))) - except Exception as ex: - print("unable to poll data from TTN! error: " + str(ex)) - scrape_healthy = False - pass + if "degreesC" in payload: + print("set degree: " + str(float(payload["degreesC"]))) + node_metrics.append("temperature " + str(float(payload["degreesC"]))) + + if "pressure" in payload: + print("set pressure: " + str(float(payload["pressure"]))) + node_metrics.append("pressure " + str(float(payload["pressure"]))) + + if "winddirection" in payload: + print("set winddirection: " + str(float(payload["winddirection"]))) + node_metrics.append("winddirection " + str(float(payload["winddirection"]))) + + if "windspeed" in payload: + print("set windspeed: " + str(float(payload["windspeed"]))) + node_metrics.append("windspeed " + str(float(payload["windspeed"]))) + + if "dooropen" in payload: + print("set dooropen: " + str(bool(payload["dooropen"]))) + node_metrics.append("dooropen " + str(int(payload["dooropen"]))) + + # if "gateway_id" in metadata[0]["gateway_ids"]: + # print("set gateway_id: " + str(metadata[0]["gateway_ids"]["gateway_id"])) + # node_metrics.append("gateway_id " + str(metadata[0]["gateway_ids"]["gateway_id"])) + + if "rssi" in metadata[0]: + print("set rssi: " + str(int(metadata[0]["rssi"]))) + node_metrics.append("rssi " + str(int(metadata[0]["rssi"]))) + + if "channel_rssi" in metadata[0]: + print("set channel_rssi: " + str(int(metadata[0]["channel_rssi"]))) + node_metrics.append("channel_rssi " + str(int(metadata[0]["channel_rssi"]))) + + if "snr" in metadata[0]: + print("set snr: " + str(float(metadata[0]["snr"]))) + node_metrics.append("snr " + str(float(metadata[0]["snr"]))) + + + #scrape_healthy = False + mutex.release() - time.sleep(300) def on_connect(mqttc, obj, flags, rc): print("\nConnected to MQTT: rc = " + str(rc)) -- 2.47.1 From 5ccf70028a567bee4073dc57ccedf02df749736c Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 2 Mar 2023 21:17:12 +0100 Subject: [PATCH 07/10] added systemd job --- README.md | 8 ++++++++ msv-clubhouse-backend.service | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 msv-clubhouse-backend.service diff --git a/README.md b/README.md index 3a85b12..102d30c 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,14 @@ TTN LoRa frequency / region `pip3 install paho-mqtt` +- `mkdir /opt/msv-clubhouse-backend/` +- `cd /opt/msv-clubhouse-backend/` +- import `msv_clubhouse_backend.py` and `config.py` +- Set the constants in `config.py` +- `chmod +x /opt/msv-clubhouse-backend/msv_clubhouse_backend.py` +- `chown -R prometheus /opt/msv-clubhouse-backend/` +- `nano /etc/systemd/system/msv-clubhouse-backend.service` +- `systemctl daemon-reload && systemctl enable --now msv-clubhouse-backend.service` JS Payload Formatter: diff --git a/msv-clubhouse-backend.service b/msv-clubhouse-backend.service new file mode 100644 index 0000000..a56b568 --- /dev/null +++ b/msv-clubhouse-backend.service @@ -0,0 +1,15 @@ +[Unit] +Description=MSV-Clubhouse-Backend +After=syslog.target +After=network.target + +[Service] +RestartSec=2s +Type=oneshot +User=prometheus +Group=prometheus +WorkingDirectory=/opt/msv-clubhouse-backend/ +ExecStart=/usr/bin/python3 /opt/msv-clubhouse-backend/msv_clubhouse_backend.py + +[Install] +WantedBy=multi-user.target \ No newline at end of file -- 2.47.1 From ec7b4eb16d71c53479a68c712ec4b11141ea8bc5 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Mar 2023 17:39:44 +0100 Subject: [PATCH 08/10] dirty fix for non-payload uplinks --- msv_clubhouse_backend.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/msv_clubhouse_backend.py b/msv_clubhouse_backend.py index fa1ae82..fb8a922 100644 --- a/msv_clubhouse_backend.py +++ b/msv_clubhouse_backend.py @@ -116,8 +116,12 @@ def on_message(mqttc, obj, msg): #print("\nMessage: " + msg.topic + " " + str(msg.qos)) parsedJSON = json.loads(msg.payload) #print(json.dumps(parsedJSON, indent=4)) - uplink_message = parsedJSON["uplink_message"]; - update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"]) + + try: + uplink_message = parsedJSON["uplink_message"]; + update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"]) + except: + print("Unable to parse uplink") def on_subscribe(mqttc, obj, mid, granted_qos): print("\nSubscribed to MQTT: " + str(mid) + " " + str(granted_qos)) -- 2.47.1 From d21004f46722389413b961bcba7b519b6c04f469 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Mar 2023 18:12:04 +0100 Subject: [PATCH 09/10] restructure and cleanup --- .gitignore | 269 --------- .gitmodules | 4 +- __pycache__/config.cpython-310.pyc | Bin 583 -> 0 bytes backend/config.py | 12 + backend/msv-clubhouse-backend.service | 15 + backend/msv_clubhouse_backend.py | 159 ++++++ frontend/grafana_dashboard.json | 766 ++++++++++++++++++++++++++ node/CMakeLists.txt | 4 + node/components/.gitkeep | 0 node/main/CMakeLists.txt | 4 + node/main/Kconfig.projbuild | 13 + node/main/font8x8_basic.h | 174 ++++++ node/main/i2c.cpp | 22 + node/main/i2c.h | 24 + node/main/main.cpp | 90 +++ node/main/main.h | 33 ++ node/main/oled/font8x8_basic.h | 174 ++++++ node/main/oled/ssd1366.cpp | 180 ++++++ node/main/oled/ssd1366.h | 65 +++ node/main/ssd1366.cpp | 204 +++++++ node/main/ssd1366.h | 65 +++ 21 files changed, 2006 insertions(+), 271 deletions(-) delete mode 100644 .gitignore delete mode 100644 __pycache__/config.cpython-310.pyc create mode 100644 backend/config.py create mode 100644 backend/msv-clubhouse-backend.service create mode 100644 backend/msv_clubhouse_backend.py create mode 100644 frontend/grafana_dashboard.json create mode 100644 node/CMakeLists.txt create mode 100644 node/components/.gitkeep create mode 100644 node/main/CMakeLists.txt create mode 100644 node/main/Kconfig.projbuild create mode 100644 node/main/font8x8_basic.h create mode 100644 node/main/i2c.cpp create mode 100644 node/main/i2c.h create mode 100644 node/main/main.cpp create mode 100644 node/main/main.h create mode 100644 node/main/oled/font8x8_basic.h create mode 100644 node/main/oled/ssd1366.cpp create mode 100644 node/main/oled/ssd1366.h create mode 100644 node/main/ssd1366.cpp create mode 100644 node/main/ssd1366.h diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9fa915b..0000000 --- a/.gitignore +++ /dev/null @@ -1,269 +0,0 @@ -# ---> esp-idf -# gitignore template for esp-idf, the official development framework for ESP32 -# https://github.com/espressif/esp-idf - -build/ -sdkconfig -sdkconfig.old - -# ---> VisualStudioCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -# ---> Kate -# Swap Files # -.*.kate-swp -.swp.* - -# ---> C -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -# ---> C++ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ diff --git a/.gitmodules b/.gitmodules index 651e526..67e1810 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "components/ttn-esp32"] - path = components/ttn-esp32 +[submodule "node/components/ttn-esp32"] + path = node/components/ttn-esp32 url = https://git.mosad.xyz/localhorst/ttn-esp32.git diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc deleted file mode 100644 index e5bda93d9aff104816cf7228b3943a26b7fadcfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 583 zcmZuv(Qeu>6m=k!6sg@8Op3IpN^JrOkae0SA`+TXHIxPf@kEi{gSYOwZ*sOtypgcRGK~ zDiKLrRl5~3ER(eyQ5>>_WQUt-ZB19(d}#laZo{0(DM86PiT?B)lB3XQ(L=rym`-o#&W7(-OSwUsWLRI$=G%l{saz36R&qYyT3E6u{WOB`poi& zLucd}@Wyn_>0+e!-QK-#!n!^0xM$6>83c$#K~PFD$(b9bxNPN^K4N-DC@bIKrycj0 z20Mz^$x|uu0|xvOv}{(<1AZ=BRY38EYxf0Ei9g{l`k|;XNwNB>ySho3%5}K-tHVV` l5LP3St&@$idoHDqT+(8TKULo$KjkFW#D*wvtBHan`~;Mbq7(oC diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..087069b --- /dev/null +++ b/backend/config.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com +""" + +hostName = "127.0.0.1" +serverPort = 9101 +exporter_prefix = "msv_clubhouse_" + +ttn_user = "USER@ttn" +ttn_key = "TTN KEY" +ttn_region = "EU1" \ No newline at end of file diff --git a/backend/msv-clubhouse-backend.service b/backend/msv-clubhouse-backend.service new file mode 100644 index 0000000..a56b568 --- /dev/null +++ b/backend/msv-clubhouse-backend.service @@ -0,0 +1,15 @@ +[Unit] +Description=MSV-Clubhouse-Backend +After=syslog.target +After=network.target + +[Service] +RestartSec=2s +Type=oneshot +User=prometheus +Group=prometheus +WorkingDirectory=/opt/msv-clubhouse-backend/ +ExecStart=/usr/bin/python3 /opt/msv-clubhouse-backend/msv_clubhouse_backend.py + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/backend/msv_clubhouse_backend.py b/backend/msv_clubhouse_backend.py new file mode 100644 index 0000000..8e4c433 --- /dev/null +++ b/backend/msv_clubhouse_backend.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com +""" + +from http.server import BaseHTTPRequestHandler, HTTPServer +import paho.mqtt.client as mqtt +from datetime import datetime +import threading +import time +import json +import config + +scrape_healthy = True +startTime = datetime.now() +node_metrics = list() +mutex = threading.Lock() +request_count = 0 + +class RequestHandler(BaseHTTPRequestHandler): + + def get_metrics(self): + global request_count + global node_metrics + global mutex + mutex.acquire() + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes(config.exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) + self.wfile.write(bytes(config.exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) + + for metric in node_metrics: + #print(metric) + self.wfile.write(bytes(config.exporter_prefix + metric + "\n", "utf-8")) + + mutex.release() + + def do_GET(self): + global request_count + request_count = request_count + 1 + print("Request: " + self.path) + if (self.path.startswith("/metrics")): + self.get_metrics() + else: + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("MSV Clubhouse exporter", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes('

msv-clubhouse exporter based on data from LoRaWAN TTN node.

', "utf-8")) + self.wfile.write(bytes('

Metrics

', "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + +def update_metrics(payload, metadata): + #print("Payload: "+ str(payload)) + #print("Metadata: "+ str(metadata)) + + global node_metrics + global mutex + global scrape_healthy + mutex.acquire() + scrape_healthy = True + node_metrics.clear() + + if "degreesC" in payload: + print("set degree: " + str(float(payload["degreesC"]))) + node_metrics.append("temperature " + str(float(payload["degreesC"]))) + + if "pressure" in payload: + print("set pressure: " + str(float(payload["pressure"]))) + node_metrics.append("pressure " + str(float(payload["pressure"]))) + + if "winddirection" in payload: + print("set winddirection: " + str(float(payload["winddirection"]))) + node_metrics.append("winddirection " + str(float(payload["winddirection"]))) + + if "windspeed" in payload: + print("set windspeed: " + str(float(payload["windspeed"]))) + node_metrics.append("windspeed " + str(float(payload["windspeed"]))) + + if "dooropen" in payload: + print("set dooropen: " + str(bool(payload["dooropen"]))) + node_metrics.append("dooropen " + str(int(payload["dooropen"]))) + + # if "gateway_id" in metadata[0]["gateway_ids"]: + # print("set gateway_id: " + str(metadata[0]["gateway_ids"]["gateway_id"])) + # node_metrics.append("gateway_id " + str(metadata[0]["gateway_ids"]["gateway_id"])) + + if "rssi" in metadata[0]: + print("set rssi: " + str(int(metadata[0]["rssi"]))) + node_metrics.append("rssi " + str(int(metadata[0]["rssi"]))) + + if "channel_rssi" in metadata[0]: + print("set channel_rssi: " + str(int(metadata[0]["channel_rssi"]))) + node_metrics.append("channel_rssi " + str(int(metadata[0]["channel_rssi"]))) + + if "snr" in metadata[0]: + print("set snr: " + str(float(metadata[0]["snr"]))) + node_metrics.append("snr " + str(float(metadata[0]["snr"]))) + + + #scrape_healthy = False + + mutex.release() + +def on_connect(mqttc, obj, flags, rc): + print("\nConnected to MQTT: rc = " + str(rc)) + +def on_message(mqttc, obj, msg): + #print("\nMessage: " + msg.topic + " " + str(msg.qos)) + parsedJSON = json.loads(msg.payload) + #print(json.dumps(parsedJSON, indent=4)) + + try: + uplink_message = parsedJSON["uplink_message"]; + update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"]) + except: + print("Unable to parse uplink") + +def on_subscribe(mqttc, obj, mid, granted_qos): + print("\nSubscribed to MQTT: " + str(mid) + " " + str(granted_qos)) + +def poll_mqtt(mqttc): + while True: + mqttc.loop(10) # seconds timeout + +def main(): + print("starting ...") + + mqttc = mqtt.Client() + mqttc.on_connect = on_connect + mqttc.on_subscribe = on_subscribe + mqttc.on_message = on_message + mqttc.username_pw_set(config.ttn_user, config.ttn_key) + mqttc.tls_set() + mqttc.connect(config.ttn_region.lower() + ".cloud.thethings.network", 8883, 60) + mqttc.subscribe("#", 0) # all device uplinks + + # run mqtt in thread forever + poll_mqtt_thread = threading.Thread(target=poll_mqtt, args=((mqttc,))) + poll_mqtt_thread.start() + + webServer = HTTPServer((config.hostName, config.serverPort), RequestHandler) + print("Server started http://%s:%s" % (config.hostName, config.serverPort)) + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + print("Server stopped.") + poll_mqtt_thread.join() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/frontend/grafana_dashboard.json b/frontend/grafana_dashboard.json new file mode 100644 index 0000000..b34c076 --- /dev/null +++ b/frontend/grafana_dashboard.json @@ -0,0 +1,766 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 16, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "description": "Wenn die Türe offen, dann ist wohl jemand am Platz.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Türe geschlossen" + }, + "1": { + "color": "green", + "index": 0, + "text": "Türe offen" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_dooropen", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Türe zur Vereinshütte", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pressurehpa" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 0 + }, + "id": 5, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_pressure", + "legendFormat": "Luftdruck Außen", + "range": true, + "refId": "A" + } + ], + "title": "Aktueller Luftdruck Außen ", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 10, + "y": 0 + }, + "id": 6, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_temperature", + "legendFormat": "Temperatur Außen", + "range": true, + "refId": "A" + } + ], + "title": "Aktuelle Temperatur Außen ", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "degree" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 15, + "y": 0 + }, + "id": 8, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_winddirection", + "legendFormat": "Windrichtung", + "range": true, + "refId": "A" + } + ], + "title": "Aktuelle Windrichtung", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "velocityms" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 19, + "y": 0 + }, + "id": 9, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_winddirection", + "legendFormat": "Windstärke", + "range": true, + "refId": "A" + } + ], + "title": "Aktuelle Windstärke", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pressurehpa" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_pressure", + "legendFormat": "Luftdruck Außen", + "range": true, + "refId": "A" + } + ], + "title": "Luftdruck Außen", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_temperature", + "legendFormat": "Luftdruck Außen", + "range": true, + "refId": "A" + } + ], + "title": "Tempertur Außen", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dB" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_rssi", + "legendFormat": "RSSI", + "range": true, + "refId": "A" + } + ], + "title": "RSSI", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dB" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "x08D2Hlnz" + }, + "editorMode": "builder", + "expr": "msv_clubhouse_snr", + "legendFormat": "SNR", + "range": true, + "refId": "A" + } + ], + "title": "SNR", + "type": "timeseries" + } + ], + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "MSV Moos Modellflugplatz", + "uid": "nuH5csxVz", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt new file mode 100644 index 0000000..a917f70 --- /dev/null +++ b/node/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(msv_node) diff --git a/node/components/.gitkeep b/node/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/node/main/CMakeLists.txt b/node/main/CMakeLists.txt new file mode 100644 index 0000000..6970ecf --- /dev/null +++ b/node/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "ssd1366.cpp" "main.cpp" + INCLUDE_DIRS "." + REQUIRES ttn-esp32) diff --git a/node/main/Kconfig.projbuild b/node/main/Kconfig.projbuild new file mode 100644 index 0000000..f6aab17 --- /dev/null +++ b/node/main/Kconfig.projbuild @@ -0,0 +1,13 @@ +menu "MSV Node" + + config APPEUI + string "AppEUI" + default "0000000000000000" + config DEVEUI + string "DevEUI" + default "0000000000000000" + config APPKEY + string "AppKey" + default "00000000000000000000000000000000" + +endmenu \ No newline at end of file diff --git a/node/main/font8x8_basic.h b/node/main/font8x8_basic.h new file mode 100644 index 0000000..1d39023 --- /dev/null +++ b/node/main/font8x8_basic.h @@ -0,0 +1,174 @@ +/* + * font8x8_basic.h + * + * Created on: 2017/05/03 + * Author: yanbe + */ + +#ifndef MAIN_FONT8X8_BASIC_H_ +#define MAIN_FONT8X8_BASIC_H_ + +#include "freertos/FreeRTOS.h" + +/* + Constant: font8x8_basic_tr + Contains an 90 digree transposed 8x8 font map for unicode points + U+0000 - U+007F (basic latin) + + To make it easy to use with SSD1306's GDDRAM mapping and API, + this constant is an 90 degree transposed. + The original version written by Marcel Sondaar is availble at: + https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h + Conversion is done via following procedure: + + for (int code = 0; code < 128; code++) { + uint8_t trans[8]; + for (int w = 0; w < 8; w++) { + trans[w] = 0x00; + for (int b = 0; b < 8; b++) { + trans[w] |= ((font8x8_basic[code][b] & (1 << w)) >> w) << b; + } + } + + for (int w = 0; w < 8; w++) { + if (w == 0) { printf(" { "); } + printf("0x%.2X", trans[w]); + if (w < 7) { printf(", "); } + if (w == 7) { printf(" }, // U+00%.2X (%c)\n", code, code); } + } + } +*/ + +uint8_t font8x8_basic_tr[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space) + { 0x00, 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00 }, // U+0021 (!) + { 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00 }, // U+0022 (") + { 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // U+0023 (#) + { 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // U+0024 ($) + { 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // U+0025 (%) + { 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // U+0026 (&) + { 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (') + { 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+0028 (() + { 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // U+0029 ()) + { 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // U+002A (*) + { 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+002B (+) + { 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002C (,) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // U+002D (-) + { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/) + { 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // U+0030 (0) + { 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // U+0031 (1) + { 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // U+0032 (2) + { 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0033 (3) + { 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // U+0034 (4) + { 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // U+0035 (5) + { 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // U+0036 (6) + { 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // U+0037 (7) + { 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0038 (8) + { 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // U+0039 (9) + { 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003A (:) + { 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003B (;) + { 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+003C (<) + { 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // U+003D (=) + { 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // U+003E (>) + { 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // U+003F (?) + { 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // U+0040 (@) + { 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // U+0041 (A) + { 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // U+0042 (B) + { 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // U+0043 (C) + { 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+0044 (D) + { 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // U+0045 (E) + { 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // U+0046 (F) + { 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // U+0047 (G) + { 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // U+0048 (H) + { 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // U+0049 (I) + { 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // U+004A (J) + { 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // U+004B (K) + { 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // U+004C (L) + { 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // U+004D (M) + { 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // U+004E (N) + { 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+004F (O) + { 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // U+0050 (P) + { 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // U+0051 (Q) + { 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // U+0052 (R) + { 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // U+0053 (S) + { 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // U+0054 (T) + { 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // U+0055 (U) + { 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // U+0056 (V) + { 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // U+0057 (W) + { 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // U+0058 (X) + { 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // U+0059 (Y) + { 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // U+005A (Z) + { 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // U+005B ([) + { 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // U+005C (\) + { 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // U+005D (]) + { 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // U+005E (^) + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // U+005F (_) + { 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // U+0060 (`) + { 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // U+0061 (a) + { 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // U+0062 (b) + { 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // U+0063 (c) + { 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // U+0064 (d) + { 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // U+0065 (e) + { 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // U+0066 (f) + { 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // U+0067 (g) + { 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // U+0068 (h) + { 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // U+0069 (i) + { 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // U+006A (j) + { 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+006B (k) + { 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // U+006C (l) + { 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // U+006D (m) + { 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // U+006E (n) + { 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // U+006F (o) + { 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // U+0070 (p) + { 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // U+0071 (q) + { 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // U+0072 (r) + { 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // U+0073 (s) + { 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // U+0074 (t) + { 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // U+0075 (u) + { 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // U+0076 (v) + { 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // U+0077 (w) + { 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+0078 (x) + { 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // U+0079 (y) + { 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // U+007A (z) + { 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // U+007B ({) + { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // U+007C (|) + { 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+007D (}) + { 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 }, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F +}; + +#endif /* MAIN_FONT8X8_BASIC_H_ */ + diff --git a/node/main/i2c.cpp b/node/main/i2c.cpp new file mode 100644 index 0000000..12d5908 --- /dev/null +++ b/node/main/i2c.cpp @@ -0,0 +1,22 @@ + + +#include "i2c.h" + +#include "esp_log.h" + + +void i2c_init() +{ + i2c_config_t i2c_config; + + i2c_config.mode = I2C_MODE_MASTER; + i2c_config.sda_io_num = I2C_SDA_PIN; + i2c_config.scl_io_num = I2C_SCL_PIN; + i2c_config.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.master.clk_speed = I2C_MASTER_FREQ_HZ; + + + ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config)); + ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0)); +} \ No newline at end of file diff --git a/node/main/i2c.h b/node/main/i2c.h new file mode 100644 index 0000000..7d705ec --- /dev/null +++ b/node/main/i2c.h @@ -0,0 +1,24 @@ +#ifndef _I2C_H_ +#define _I2C_H_ + +#define I2C_SDA_PIN GPIO_NUM_4 +#define I2C_SCL_PIN GPIO_NUM_15 + +#define I2C_NUM I2C_NUM_0 +//#define I2C_NUM I2C_NUM + +#define I2C_MASTER_FREQ_HZ 400000U /*!< I2C clock of SSD1306 can run at 400 kHz max. */ + + +class I2C_Driver +{ + + +}; + + + +void i2c_init(); + + +#endif /* _I2C_H_ */ \ No newline at end of file diff --git a/node/main/main.cpp b/node/main/main.cpp new file mode 100644 index 0000000..ffd6f3e --- /dev/null +++ b/node/main/main.cpp @@ -0,0 +1,90 @@ +#include "main.h" + +#include "ssd1366.h" + +void sendMessages(void* pvParameter) +{ + while (1) { + printf("Sending message...\n"); + ssd1306_display_clear(); + const char *pcTask1 = "Sending message...\n"; + xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); + TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1); + + if(res == kTTNSuccessfulTransmission){ + printf("Message sent.\n"); + ssd1306_display_clear(); + const char *pcTask1 = "Message sent.\n"; + xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); + }else{ + printf("Transmission failed.\n"); + ssd1306_display_clear(); + const char *pcTask1 = "Transmission failed.\n"; + xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); + } + + vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000)); + } +} + +void messageReceived(const uint8_t* message, size_t length, ttn_port_t port) +{ + printf("Message of %d bytes received on port %d:", length, port); + for (int i = 0; i < length; i++) + printf(" %02x", message[i]); + printf("\n"); +} + +extern "C" void app_main(void) +{ + esp_err_t err; + // Initialize the GPIO ISR handler service + err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + ESP_ERROR_CHECK(err); + + // Initialize the NVS (non-volatile storage) for saving and restoring the keys + err = nvs_flash_init(); + ESP_ERROR_CHECK(err); + + // Initialize SPI bus + spi_bus_config_t spi_bus_config; + spi_bus_config.miso_io_num = TTN_PIN_SPI_MISO; + spi_bus_config.mosi_io_num = TTN_PIN_SPI_MOSI; + spi_bus_config.sclk_io_num = TTN_PIN_SPI_SCLK; + spi_bus_config.quadwp_io_num = -1; + spi_bus_config.quadhd_io_num = -1; + spi_bus_config.max_transfer_sz = 0; + err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN); + ESP_ERROR_CHECK(err); + + // Configure the SX127x pins + ttn.configurePins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1); + + // The below line can be commented after the first run as the data is saved in NVS + ttn.provision(CONFIG_DEVEUI, CONFIG_APPEUI, CONFIG_APPKEY); + + // Register callback for received messages + ttn.onMessage(messageReceived); + + + i2c_master_init(); + ssd1306_init(); + + printf("Joining...\n"); + ssd1306_display_clear(); + const char *pcTask1 = "Joining...\n"; + xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); + if (ttn.join()) + { + printf("Joined.\n"); + ssd1306_display_clear(); + const char *pcTask1 = "Joined.\n"; + xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); + xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr); + } + else + { + printf("Join failed. Goodbye\n"); + esp_restart(); + } +} diff --git a/node/main/main.h b/node/main/main.h new file mode 100644 index 0000000..6032db7 --- /dev/null +++ b/node/main/main.h @@ -0,0 +1,33 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "freertos/FreeRTOS.h" +#include "esp_event.h" +#include "driver/gpio.h" +#include "nvs_flash.h" +#include "driver/i2c.h" + +#include "TheThingsNetwork.h" + +#define SDA_PIN GPIO_NUM_4 +#define SCL_PIN GPIO_NUM_15 + +// Pins and other resources +#define TTN_SPI_HOST HSPI_HOST +#define TTN_SPI_DMA_CHAN 1 +#define TTN_PIN_SPI_SCLK 5 +#define TTN_PIN_SPI_MOSI 27 +#define TTN_PIN_SPI_MISO 19 +#define TTN_PIN_NSS 18 +#define TTN_PIN_RXTX TTN_NOT_CONNECTED +#define TTN_PIN_RST 14 +#define TTN_PIN_DIO0 26 +#define TTN_PIN_DIO1 35 + +static TheThingsNetwork ttn; + +const unsigned TX_INTERVAL = 5; +static uint8_t msgData[] = "0xBADC0DED"; + + +#endif /* MAIN_H */ \ No newline at end of file diff --git a/node/main/oled/font8x8_basic.h b/node/main/oled/font8x8_basic.h new file mode 100644 index 0000000..1d39023 --- /dev/null +++ b/node/main/oled/font8x8_basic.h @@ -0,0 +1,174 @@ +/* + * font8x8_basic.h + * + * Created on: 2017/05/03 + * Author: yanbe + */ + +#ifndef MAIN_FONT8X8_BASIC_H_ +#define MAIN_FONT8X8_BASIC_H_ + +#include "freertos/FreeRTOS.h" + +/* + Constant: font8x8_basic_tr + Contains an 90 digree transposed 8x8 font map for unicode points + U+0000 - U+007F (basic latin) + + To make it easy to use with SSD1306's GDDRAM mapping and API, + this constant is an 90 degree transposed. + The original version written by Marcel Sondaar is availble at: + https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h + Conversion is done via following procedure: + + for (int code = 0; code < 128; code++) { + uint8_t trans[8]; + for (int w = 0; w < 8; w++) { + trans[w] = 0x00; + for (int b = 0; b < 8; b++) { + trans[w] |= ((font8x8_basic[code][b] & (1 << w)) >> w) << b; + } + } + + for (int w = 0; w < 8; w++) { + if (w == 0) { printf(" { "); } + printf("0x%.2X", trans[w]); + if (w < 7) { printf(", "); } + if (w == 7) { printf(" }, // U+00%.2X (%c)\n", code, code); } + } + } +*/ + +uint8_t font8x8_basic_tr[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space) + { 0x00, 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00 }, // U+0021 (!) + { 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00 }, // U+0022 (") + { 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // U+0023 (#) + { 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // U+0024 ($) + { 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // U+0025 (%) + { 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // U+0026 (&) + { 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (') + { 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+0028 (() + { 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // U+0029 ()) + { 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // U+002A (*) + { 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+002B (+) + { 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002C (,) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // U+002D (-) + { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/) + { 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // U+0030 (0) + { 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // U+0031 (1) + { 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // U+0032 (2) + { 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0033 (3) + { 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // U+0034 (4) + { 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // U+0035 (5) + { 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // U+0036 (6) + { 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // U+0037 (7) + { 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0038 (8) + { 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // U+0039 (9) + { 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003A (:) + { 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003B (;) + { 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+003C (<) + { 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // U+003D (=) + { 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // U+003E (>) + { 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // U+003F (?) + { 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // U+0040 (@) + { 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // U+0041 (A) + { 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // U+0042 (B) + { 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // U+0043 (C) + { 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+0044 (D) + { 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // U+0045 (E) + { 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // U+0046 (F) + { 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // U+0047 (G) + { 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // U+0048 (H) + { 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // U+0049 (I) + { 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // U+004A (J) + { 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // U+004B (K) + { 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // U+004C (L) + { 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // U+004D (M) + { 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // U+004E (N) + { 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+004F (O) + { 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // U+0050 (P) + { 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // U+0051 (Q) + { 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // U+0052 (R) + { 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // U+0053 (S) + { 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // U+0054 (T) + { 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // U+0055 (U) + { 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // U+0056 (V) + { 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // U+0057 (W) + { 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // U+0058 (X) + { 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // U+0059 (Y) + { 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // U+005A (Z) + { 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // U+005B ([) + { 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // U+005C (\) + { 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // U+005D (]) + { 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // U+005E (^) + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // U+005F (_) + { 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // U+0060 (`) + { 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // U+0061 (a) + { 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // U+0062 (b) + { 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // U+0063 (c) + { 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // U+0064 (d) + { 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // U+0065 (e) + { 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // U+0066 (f) + { 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // U+0067 (g) + { 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // U+0068 (h) + { 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // U+0069 (i) + { 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // U+006A (j) + { 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+006B (k) + { 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // U+006C (l) + { 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // U+006D (m) + { 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // U+006E (n) + { 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // U+006F (o) + { 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // U+0070 (p) + { 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // U+0071 (q) + { 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // U+0072 (r) + { 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // U+0073 (s) + { 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // U+0074 (t) + { 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // U+0075 (u) + { 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // U+0076 (v) + { 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // U+0077 (w) + { 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+0078 (x) + { 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // U+0079 (y) + { 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // U+007A (z) + { 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // U+007B ({) + { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // U+007C (|) + { 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+007D (}) + { 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 }, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F +}; + +#endif /* MAIN_FONT8X8_BASIC_H_ */ + diff --git a/node/main/oled/ssd1366.cpp b/node/main/oled/ssd1366.cpp new file mode 100644 index 0000000..e0318e6 --- /dev/null +++ b/node/main/oled/ssd1366.cpp @@ -0,0 +1,180 @@ + +#include "ssd1366.h" +#include "font8x8_basic.h" +#include +#include "driver/gpio.h" + + +#define SSD1366_RST_PIN GPIO_NUM_16 +#define SSD1366_VEXT_PIN GPIO_NUM_21 + +#define I2C_NUM I2C_NUM_0 + +static const char *tag = "oled"; + +void ssd1306_init() +{ + esp_err_t espRc; + gpio_set_direction(SSD1366_RST_PIN, GPIO_MODE_OUTPUT); + gpio_set_direction(SSD1366_VEXT_PIN, GPIO_MODE_OUTPUT); + gpio_set_level(SSD1366_VEXT_PIN, 0); //enable power to oled + gpio_set_level(SSD1366_RST_PIN, 0); + vTaskDelay(50 / portTICK_PERIOD_MS); + gpio_set_level(SSD1366_RST_PIN, 1); + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_OFF, true); // AE + i2c_master_write_byte(cmd, OLED_CMD_SET_MUX_RATIO, true); // A8 + i2c_master_write_byte(cmd, 0x3F, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_OFFSET, true); // D3 + i2c_master_write_byte(cmd, 0x00, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_START_LINE, true); // 40 + + i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); // reverse left-right mapping + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE, true); // reverse up-bottom mapping + + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_CLK_DIV, true); // D5 + i2c_master_write_byte(cmd, 0x80, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_PIN_MAP, true); // DA + i2c_master_write_byte(cmd, 0x12, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 + i2c_master_write_byte(cmd, 0xFF, true); + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_RAM, true); // A4 + i2c_master_write_byte(cmd, OLED_CMD_SET_VCOMH_DESELCT, true); // DB + i2c_master_write_byte(cmd, 0x40, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_MEMORY_ADDR_MODE, true); // 20 + //i2c_master_write_byte(cmd, OLED_CMD_SET_HORI_ADDR_MODE, true); // 00 + i2c_master_write_byte(cmd, OLED_CMD_SET_PAGE_ADDR_MODE, true); // 02 + // Set Lower Column Start Address for Page Addressing Mode + i2c_master_write_byte(cmd, 0x00, true); + // Set Higher Column Start Address for Page Addressing Mode + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); // 8D + i2c_master_write_byte(cmd, 0x14, true); + i2c_master_write_byte(cmd, OLED_CMD_DEACTIVE_SCROLL, true); // 2E + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_NORMAL, true); // A6 + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); + + i2c_master_stop(cmd); + + espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + if (espRc == ESP_OK) + { + ESP_LOGI(tag, "OLED configured successfully"); + } + else + { + ESP_LOGE(tag, "OLED configuration failed. code: 0x%.2X", espRc); + } + i2c_cmd_link_delete(cmd); +} + +void i2c_contrast( int contrast) { + i2c_cmd_handle_t cmd; + int _contrast = contrast; + if (contrast < 0x0) _contrast = 0; + if (contrast > 0xFF) _contrast = 0xFF; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 + i2c_master_write_byte(cmd, _contrast, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); +} + +void ssd1306_display_clear(void) { + i2c_cmd_handle_t cmd; + uint8_t zero[128] = {0}; + for (uint8_t i = 0; i < 8; i++) { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_SINGLE, true); + i2c_master_write_byte(cmd, 0xB0 | i, true); // Set GDDRAM page start address + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, zero, 128, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } +} + +void ssd1306_display_text(char* text, uint16_t text_len) +{ + + i2c_cmd_handle_t cmd; + + uint8_t cur_page = 0; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | cur_page, true); // reset page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + for (uint8_t i = 0; i < text_len; i++) + { + if (text[i] == '\n') + { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | ++cur_page, true); // increment page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } + else + { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, font8x8_basic_tr[(uint8_t)text[i]], 8, true); + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } + } +} + + +void task_ssd1306_display_text(void *pvParameters) +{ + char *pcTaskName; + pcTaskName = (char *) pvParameters; + + //char text[13] = "Hello world!"; + //uint8_t text_len = strlen(text); + + ssd1306_display_text(pcTaskName, strlen(pcTaskName)); + + vTaskDelete(NULL); +} + + + + diff --git a/node/main/oled/ssd1366.h b/node/main/oled/ssd1366.h new file mode 100644 index 0000000..13fa6d0 --- /dev/null +++ b/node/main/oled/ssd1366.h @@ -0,0 +1,65 @@ +#ifndef MAIN_SSD1366_H_ +#define MAIN_SSD1366_H_ + +#include "freertos/FreeRTOS.h" +#include "driver/gpio.h" +#include "driver/i2c.h" +#include "esp_log.h" + +// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000) +#define OLED_I2C_ADDRESS 0x3C + +// Control byte +#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80 +#define OLED_CONTROL_BYTE_CMD_STREAM 0x00 +#define OLED_CONTROL_BYTE_DATA_STREAM 0x40 + +// Fundamental commands (pg.28) +#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F +#define OLED_CMD_DISPLAY_RAM 0xA4 +#define OLED_CMD_DISPLAY_ALLON 0xA5 +#define OLED_CMD_DISPLAY_NORMAL 0xA6 +#define OLED_CMD_DISPLAY_INVERTED 0xA7 +#define OLED_CMD_DISPLAY_OFF 0xAE +#define OLED_CMD_DISPLAY_ON 0xAF + +// Addressing Command Table (pg.30) +#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD +#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127 +#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7 +#define OLED_CMD_SET_PAGE_ADDR_MODE 0x02 // Page Addressing Mode + +// Hardware Config (pg.31) +#define OLED_CMD_SET_DISPLAY_START_LINE 0x40 +#define OLED_CMD_SET_SEGMENT_REMAP 0xA1 +#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX +#define OLED_CMD_SET_COM_SCAN_MODE 0xC8 +#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00 +#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12 +#define OLED_CMD_NOP 0xE3 // NOP + +// Timing and Driving Scheme (pg.32) +#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80 +#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1 +#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30 + +// Charge Pump (pg.62) +#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14 + +// Scrolling Command +#define OLED_CMD_HORIZONTAL_RIGHT 0x26 +#define OLED_CMD_HORIZONTAL_LEFT 0x27 +#define OLED_CMD_CONTINUOUS_SCROLL 0x29 +#define OLED_CMD_DEACTIVE_SCROLL 0x2E +#define OLED_CMD_ACTIVE_SCROLL 0x2F +#define OLED_CMD_VERTICAL 0xA3 + + +void i2c_master_init(); +void ssd1306_init(); +void task_ssd1306_display_text(void *pvParameters); +void i2c_contrast( int contrast); +void ssd1306_display_clear(void); +void ssd1306_display_text(char* text, uint16_t text_len); + +#endif /* MAIN_SSD1366_H_ */ \ No newline at end of file diff --git a/node/main/ssd1366.cpp b/node/main/ssd1366.cpp new file mode 100644 index 0000000..ba86219 --- /dev/null +++ b/node/main/ssd1366.cpp @@ -0,0 +1,204 @@ + +#include "ssd1366.h" +#include "font8x8_basic.h" +#include +#include "driver/gpio.h" + +#define SSD1366_SDA_PIN GPIO_NUM_4 +#define SSD1366_SCL_PIN GPIO_NUM_15 +#define SSD1366_RST_PIN GPIO_NUM_16 +#define SSD1366_VEXT_PIN GPIO_NUM_21 + + + + +#define I2C_NUM I2C_NUM_0 +//#define I2C_NUM I2C_NUM + +#define I2C_MASTER_FREQ_HZ 400000U /*!< I2C clock of SSD1306 can run at 400 kHz max. */ + + +#define tag "SSD1306" + +void i2c_master_init() +{ + i2c_config_t i2c_config; + + i2c_config.mode = I2C_MODE_MASTER; + i2c_config.sda_io_num = SSD1366_SDA_PIN; + i2c_config.scl_io_num = SSD1366_SCL_PIN; + i2c_config.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.master.clk_speed = I2C_MASTER_FREQ_HZ; + + + ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config)); + ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0)); +} + +void ssd1306_init() +{ + esp_err_t espRc; + gpio_set_direction(SSD1366_RST_PIN, GPIO_MODE_OUTPUT); + gpio_set_direction(SSD1366_VEXT_PIN, GPIO_MODE_OUTPUT); + gpio_set_level(SSD1366_VEXT_PIN, 0); //enable power to oled + gpio_set_level(SSD1366_RST_PIN, 0); + vTaskDelay(50 / portTICK_PERIOD_MS); + gpio_set_level(SSD1366_RST_PIN, 1); + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_OFF, true); // AE + i2c_master_write_byte(cmd, OLED_CMD_SET_MUX_RATIO, true); // A8 + i2c_master_write_byte(cmd, 0x3F, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_OFFSET, true); // D3 + i2c_master_write_byte(cmd, 0x00, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_START_LINE, true); // 40 + + i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); // reverse left-right mapping + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE, true); // reverse up-bottom mapping + + i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_CLK_DIV, true); // D5 + i2c_master_write_byte(cmd, 0x80, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_PIN_MAP, true); // DA + i2c_master_write_byte(cmd, 0x12, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 + i2c_master_write_byte(cmd, 0xFF, true); + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_RAM, true); // A4 + i2c_master_write_byte(cmd, OLED_CMD_SET_VCOMH_DESELCT, true); // DB + i2c_master_write_byte(cmd, 0x40, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_MEMORY_ADDR_MODE, true); // 20 + //i2c_master_write_byte(cmd, OLED_CMD_SET_HORI_ADDR_MODE, true); // 00 + i2c_master_write_byte(cmd, OLED_CMD_SET_PAGE_ADDR_MODE, true); // 02 + // Set Lower Column Start Address for Page Addressing Mode + i2c_master_write_byte(cmd, 0x00, true); + // Set Higher Column Start Address for Page Addressing Mode + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); // 8D + i2c_master_write_byte(cmd, 0x14, true); + i2c_master_write_byte(cmd, OLED_CMD_DEACTIVE_SCROLL, true); // 2E + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_NORMAL, true); // A6 + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); + + i2c_master_stop(cmd); + + espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + if (espRc == ESP_OK) + { + ESP_LOGI(tag, "OLED configured successfully"); + } + else + { + ESP_LOGE(tag, "OLED configuration failed. code: 0x%.2X", espRc); + } + i2c_cmd_link_delete(cmd); +} + +void i2c_contrast( int contrast) { + i2c_cmd_handle_t cmd; + int _contrast = contrast; + if (contrast < 0x0) _contrast = 0; + if (contrast > 0xFF) _contrast = 0xFF; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 + i2c_master_write_byte(cmd, _contrast, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); +} + +void ssd1306_display_clear(void) { + i2c_cmd_handle_t cmd; + uint8_t zero[128] = {0}; + for (uint8_t i = 0; i < 8; i++) { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_SINGLE, true); + i2c_master_write_byte(cmd, 0xB0 | i, true); // Set GDDRAM page start address + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, zero, 128, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } +} + +void ssd1306_display_text(char* text, uint16_t text_len) +{ + + i2c_cmd_handle_t cmd; + + uint8_t cur_page = 0; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | cur_page, true); // reset page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + for (uint8_t i = 0; i < text_len; i++) + { + if (text[i] == '\n') + { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | ++cur_page, true); // increment page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } + else + { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, font8x8_basic_tr[(uint8_t)text[i]], 8, true); + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } + } +} + + +void task_ssd1306_display_text(void *pvParameters) +{ + char *pcTaskName; + pcTaskName = (char *) pvParameters; + + //char text[13] = "Hello world!"; + //uint8_t text_len = strlen(text); + + ssd1306_display_text(pcTaskName, strlen(pcTaskName)); + + vTaskDelete(NULL); +} + + + + diff --git a/node/main/ssd1366.h b/node/main/ssd1366.h new file mode 100644 index 0000000..13fa6d0 --- /dev/null +++ b/node/main/ssd1366.h @@ -0,0 +1,65 @@ +#ifndef MAIN_SSD1366_H_ +#define MAIN_SSD1366_H_ + +#include "freertos/FreeRTOS.h" +#include "driver/gpio.h" +#include "driver/i2c.h" +#include "esp_log.h" + +// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000) +#define OLED_I2C_ADDRESS 0x3C + +// Control byte +#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80 +#define OLED_CONTROL_BYTE_CMD_STREAM 0x00 +#define OLED_CONTROL_BYTE_DATA_STREAM 0x40 + +// Fundamental commands (pg.28) +#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F +#define OLED_CMD_DISPLAY_RAM 0xA4 +#define OLED_CMD_DISPLAY_ALLON 0xA5 +#define OLED_CMD_DISPLAY_NORMAL 0xA6 +#define OLED_CMD_DISPLAY_INVERTED 0xA7 +#define OLED_CMD_DISPLAY_OFF 0xAE +#define OLED_CMD_DISPLAY_ON 0xAF + +// Addressing Command Table (pg.30) +#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD +#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127 +#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7 +#define OLED_CMD_SET_PAGE_ADDR_MODE 0x02 // Page Addressing Mode + +// Hardware Config (pg.31) +#define OLED_CMD_SET_DISPLAY_START_LINE 0x40 +#define OLED_CMD_SET_SEGMENT_REMAP 0xA1 +#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX +#define OLED_CMD_SET_COM_SCAN_MODE 0xC8 +#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00 +#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12 +#define OLED_CMD_NOP 0xE3 // NOP + +// Timing and Driving Scheme (pg.32) +#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80 +#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1 +#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30 + +// Charge Pump (pg.62) +#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14 + +// Scrolling Command +#define OLED_CMD_HORIZONTAL_RIGHT 0x26 +#define OLED_CMD_HORIZONTAL_LEFT 0x27 +#define OLED_CMD_CONTINUOUS_SCROLL 0x29 +#define OLED_CMD_DEACTIVE_SCROLL 0x2E +#define OLED_CMD_ACTIVE_SCROLL 0x2F +#define OLED_CMD_VERTICAL 0xA3 + + +void i2c_master_init(); +void ssd1306_init(); +void task_ssd1306_display_text(void *pvParameters); +void i2c_contrast( int contrast); +void ssd1306_display_clear(void); +void ssd1306_display_text(char* text, uint16_t text_len); + +#endif /* MAIN_SSD1366_H_ */ \ No newline at end of file -- 2.47.1 From 0b32ce5e4d390e87cd99622b46819a8da60e5946 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Mar 2023 18:14:27 +0100 Subject: [PATCH 10/10] delete old folders --- CMakeLists.txt | 4 - components/ttn-esp32 | 1 - config.py | 14 --- main/CMakeLists.txt | 4 - main/Kconfig.projbuild | 13 --- main/font8x8_basic.h | 174 ----------------------------- main/main.cpp | 90 --------------- main/main.h | 33 ------ main/ssd1366.cpp | 204 ---------------------------------- main/ssd1366.h | 65 ----------- msv-clubhouse-backend.service | 15 --- msv_clubhouse_backend.py | 161 --------------------------- 12 files changed, 778 deletions(-) delete mode 100644 CMakeLists.txt delete mode 160000 components/ttn-esp32 delete mode 100644 config.py delete mode 100644 main/CMakeLists.txt delete mode 100644 main/Kconfig.projbuild delete mode 100644 main/font8x8_basic.h delete mode 100644 main/main.cpp delete mode 100644 main/main.h delete mode 100644 main/ssd1366.cpp delete mode 100644 main/ssd1366.h delete mode 100644 msv-clubhouse-backend.service delete mode 100644 msv_clubhouse_backend.py diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a917f70..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -include($ENV{IDF_PATH}/tools/cmake/project.cmake) - -project(msv_node) diff --git a/components/ttn-esp32 b/components/ttn-esp32 deleted file mode 160000 index 8777846..0000000 --- a/components/ttn-esp32 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 877784640d7976aab84398de6a5b376033fd56d1 diff --git a/config.py b/config.py deleted file mode 100644 index 77b3c2f..0000000 --- a/config.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" Author: Hendrik Schutter, mail@hendrikschutter.com - Date of creation: 2023/03/02 - Date of last modification: 2023/03/02 -""" - -hostName = "127.0.0.1" -serverPort = 9101 -exporter_prefix = "msv_clubhouse_" - -ttn_user = "USERID@ttn" -ttn_key = "API KEY" -ttn_region = "EU1" \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt deleted file mode 100644 index 6970ecf..0000000 --- a/main/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -idf_component_register( - SRCS "ssd1366.cpp" "main.cpp" - INCLUDE_DIRS "." - REQUIRES ttn-esp32) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild deleted file mode 100644 index f6aab17..0000000 --- a/main/Kconfig.projbuild +++ /dev/null @@ -1,13 +0,0 @@ -menu "MSV Node" - - config APPEUI - string "AppEUI" - default "0000000000000000" - config DEVEUI - string "DevEUI" - default "0000000000000000" - config APPKEY - string "AppKey" - default "00000000000000000000000000000000" - -endmenu \ No newline at end of file diff --git a/main/font8x8_basic.h b/main/font8x8_basic.h deleted file mode 100644 index 1d39023..0000000 --- a/main/font8x8_basic.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * font8x8_basic.h - * - * Created on: 2017/05/03 - * Author: yanbe - */ - -#ifndef MAIN_FONT8X8_BASIC_H_ -#define MAIN_FONT8X8_BASIC_H_ - -#include "freertos/FreeRTOS.h" - -/* - Constant: font8x8_basic_tr - Contains an 90 digree transposed 8x8 font map for unicode points - U+0000 - U+007F (basic latin) - - To make it easy to use with SSD1306's GDDRAM mapping and API, - this constant is an 90 degree transposed. - The original version written by Marcel Sondaar is availble at: - https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h - Conversion is done via following procedure: - - for (int code = 0; code < 128; code++) { - uint8_t trans[8]; - for (int w = 0; w < 8; w++) { - trans[w] = 0x00; - for (int b = 0; b < 8; b++) { - trans[w] |= ((font8x8_basic[code][b] & (1 << w)) >> w) << b; - } - } - - for (int w = 0; w < 8; w++) { - if (w == 0) { printf(" { "); } - printf("0x%.2X", trans[w]); - if (w < 7) { printf(", "); } - if (w == 7) { printf(" }, // U+00%.2X (%c)\n", code, code); } - } - } -*/ - -uint8_t font8x8_basic_tr[128][8] = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019 - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space) - { 0x00, 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00 }, // U+0021 (!) - { 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00 }, // U+0022 (") - { 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // U+0023 (#) - { 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // U+0024 ($) - { 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // U+0025 (%) - { 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // U+0026 (&) - { 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (') - { 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+0028 (() - { 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // U+0029 ()) - { 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // U+002A (*) - { 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+002B (+) - { 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002C (,) - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // U+002D (-) - { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002E (.) - { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/) - { 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // U+0030 (0) - { 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // U+0031 (1) - { 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // U+0032 (2) - { 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0033 (3) - { 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // U+0034 (4) - { 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // U+0035 (5) - { 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // U+0036 (6) - { 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // U+0037 (7) - { 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0038 (8) - { 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // U+0039 (9) - { 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003A (:) - { 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003B (;) - { 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+003C (<) - { 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // U+003D (=) - { 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // U+003E (>) - { 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // U+003F (?) - { 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // U+0040 (@) - { 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // U+0041 (A) - { 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // U+0042 (B) - { 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // U+0043 (C) - { 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+0044 (D) - { 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // U+0045 (E) - { 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // U+0046 (F) - { 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // U+0047 (G) - { 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // U+0048 (H) - { 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // U+0049 (I) - { 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // U+004A (J) - { 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // U+004B (K) - { 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // U+004C (L) - { 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // U+004D (M) - { 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // U+004E (N) - { 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+004F (O) - { 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // U+0050 (P) - { 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // U+0051 (Q) - { 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // U+0052 (R) - { 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // U+0053 (S) - { 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // U+0054 (T) - { 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // U+0055 (U) - { 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // U+0056 (V) - { 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // U+0057 (W) - { 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // U+0058 (X) - { 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // U+0059 (Y) - { 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // U+005A (Z) - { 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // U+005B ([) - { 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // U+005C (\) - { 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // U+005D (]) - { 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // U+005E (^) - { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // U+005F (_) - { 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // U+0060 (`) - { 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // U+0061 (a) - { 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // U+0062 (b) - { 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // U+0063 (c) - { 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // U+0064 (d) - { 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // U+0065 (e) - { 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // U+0066 (f) - { 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // U+0067 (g) - { 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // U+0068 (h) - { 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // U+0069 (i) - { 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // U+006A (j) - { 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+006B (k) - { 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // U+006C (l) - { 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // U+006D (m) - { 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // U+006E (n) - { 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // U+006F (o) - { 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // U+0070 (p) - { 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // U+0071 (q) - { 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // U+0072 (r) - { 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // U+0073 (s) - { 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // U+0074 (t) - { 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // U+0075 (u) - { 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // U+0076 (v) - { 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // U+0077 (w) - { 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+0078 (x) - { 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // U+0079 (y) - { 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // U+007A (z) - { 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // U+007B ({) - { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // U+007C (|) - { 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+007D (}) - { 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 }, // U+007E (~) - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F -}; - -#endif /* MAIN_FONT8X8_BASIC_H_ */ - diff --git a/main/main.cpp b/main/main.cpp deleted file mode 100644 index ffd6f3e..0000000 --- a/main/main.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "main.h" - -#include "ssd1366.h" - -void sendMessages(void* pvParameter) -{ - while (1) { - printf("Sending message...\n"); - ssd1306_display_clear(); - const char *pcTask1 = "Sending message...\n"; - xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); - TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1); - - if(res == kTTNSuccessfulTransmission){ - printf("Message sent.\n"); - ssd1306_display_clear(); - const char *pcTask1 = "Message sent.\n"; - xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); - }else{ - printf("Transmission failed.\n"); - ssd1306_display_clear(); - const char *pcTask1 = "Transmission failed.\n"; - xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); - } - - vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000)); - } -} - -void messageReceived(const uint8_t* message, size_t length, ttn_port_t port) -{ - printf("Message of %d bytes received on port %d:", length, port); - for (int i = 0; i < length; i++) - printf(" %02x", message[i]); - printf("\n"); -} - -extern "C" void app_main(void) -{ - esp_err_t err; - // Initialize the GPIO ISR handler service - err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); - ESP_ERROR_CHECK(err); - - // Initialize the NVS (non-volatile storage) for saving and restoring the keys - err = nvs_flash_init(); - ESP_ERROR_CHECK(err); - - // Initialize SPI bus - spi_bus_config_t spi_bus_config; - spi_bus_config.miso_io_num = TTN_PIN_SPI_MISO; - spi_bus_config.mosi_io_num = TTN_PIN_SPI_MOSI; - spi_bus_config.sclk_io_num = TTN_PIN_SPI_SCLK; - spi_bus_config.quadwp_io_num = -1; - spi_bus_config.quadhd_io_num = -1; - spi_bus_config.max_transfer_sz = 0; - err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN); - ESP_ERROR_CHECK(err); - - // Configure the SX127x pins - ttn.configurePins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1); - - // The below line can be commented after the first run as the data is saved in NVS - ttn.provision(CONFIG_DEVEUI, CONFIG_APPEUI, CONFIG_APPKEY); - - // Register callback for received messages - ttn.onMessage(messageReceived); - - - i2c_master_init(); - ssd1306_init(); - - printf("Joining...\n"); - ssd1306_display_clear(); - const char *pcTask1 = "Joining...\n"; - xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); - if (ttn.join()) - { - printf("Joined.\n"); - ssd1306_display_clear(); - const char *pcTask1 = "Joined.\n"; - xTaskCreate(&task_ssd1306_display_text, "task_ssd1306_display_text", 4096, (void *) pcTask1, 5, NULL); - xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr); - } - else - { - printf("Join failed. Goodbye\n"); - esp_restart(); - } -} diff --git a/main/main.h b/main/main.h deleted file mode 100644 index 6032db7..0000000 --- a/main/main.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -#include "freertos/FreeRTOS.h" -#include "esp_event.h" -#include "driver/gpio.h" -#include "nvs_flash.h" -#include "driver/i2c.h" - -#include "TheThingsNetwork.h" - -#define SDA_PIN GPIO_NUM_4 -#define SCL_PIN GPIO_NUM_15 - -// Pins and other resources -#define TTN_SPI_HOST HSPI_HOST -#define TTN_SPI_DMA_CHAN 1 -#define TTN_PIN_SPI_SCLK 5 -#define TTN_PIN_SPI_MOSI 27 -#define TTN_PIN_SPI_MISO 19 -#define TTN_PIN_NSS 18 -#define TTN_PIN_RXTX TTN_NOT_CONNECTED -#define TTN_PIN_RST 14 -#define TTN_PIN_DIO0 26 -#define TTN_PIN_DIO1 35 - -static TheThingsNetwork ttn; - -const unsigned TX_INTERVAL = 5; -static uint8_t msgData[] = "0xBADC0DED"; - - -#endif /* MAIN_H */ \ No newline at end of file diff --git a/main/ssd1366.cpp b/main/ssd1366.cpp deleted file mode 100644 index ba86219..0000000 --- a/main/ssd1366.cpp +++ /dev/null @@ -1,204 +0,0 @@ - -#include "ssd1366.h" -#include "font8x8_basic.h" -#include -#include "driver/gpio.h" - -#define SSD1366_SDA_PIN GPIO_NUM_4 -#define SSD1366_SCL_PIN GPIO_NUM_15 -#define SSD1366_RST_PIN GPIO_NUM_16 -#define SSD1366_VEXT_PIN GPIO_NUM_21 - - - - -#define I2C_NUM I2C_NUM_0 -//#define I2C_NUM I2C_NUM - -#define I2C_MASTER_FREQ_HZ 400000U /*!< I2C clock of SSD1306 can run at 400 kHz max. */ - - -#define tag "SSD1306" - -void i2c_master_init() -{ - i2c_config_t i2c_config; - - i2c_config.mode = I2C_MODE_MASTER; - i2c_config.sda_io_num = SSD1366_SDA_PIN; - i2c_config.scl_io_num = SSD1366_SCL_PIN; - i2c_config.sda_pullup_en = GPIO_PULLUP_ENABLE; - i2c_config.scl_pullup_en = GPIO_PULLUP_ENABLE; - i2c_config.master.clk_speed = I2C_MASTER_FREQ_HZ; - - - ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config)); - ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0)); -} - -void ssd1306_init() -{ - esp_err_t espRc; - gpio_set_direction(SSD1366_RST_PIN, GPIO_MODE_OUTPUT); - gpio_set_direction(SSD1366_VEXT_PIN, GPIO_MODE_OUTPUT); - gpio_set_level(SSD1366_VEXT_PIN, 0); //enable power to oled - gpio_set_level(SSD1366_RST_PIN, 0); - vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(SSD1366_RST_PIN, 1); - - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); - - i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_OFF, true); // AE - i2c_master_write_byte(cmd, OLED_CMD_SET_MUX_RATIO, true); // A8 - i2c_master_write_byte(cmd, 0x3F, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_OFFSET, true); // D3 - i2c_master_write_byte(cmd, 0x00, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_START_LINE, true); // 40 - - i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); // reverse left-right mapping - i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE, true); // reverse up-bottom mapping - - i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_CLK_DIV, true); // D5 - i2c_master_write_byte(cmd, 0x80, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_COM_PIN_MAP, true); // DA - i2c_master_write_byte(cmd, 0x12, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 - i2c_master_write_byte(cmd, 0xFF, true); - i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_RAM, true); // A4 - i2c_master_write_byte(cmd, OLED_CMD_SET_VCOMH_DESELCT, true); // DB - i2c_master_write_byte(cmd, 0x40, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_MEMORY_ADDR_MODE, true); // 20 - //i2c_master_write_byte(cmd, OLED_CMD_SET_HORI_ADDR_MODE, true); // 00 - i2c_master_write_byte(cmd, OLED_CMD_SET_PAGE_ADDR_MODE, true); // 02 - // Set Lower Column Start Address for Page Addressing Mode - i2c_master_write_byte(cmd, 0x00, true); - // Set Higher Column Start Address for Page Addressing Mode - i2c_master_write_byte(cmd, 0x10, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); // 8D - i2c_master_write_byte(cmd, 0x14, true); - i2c_master_write_byte(cmd, OLED_CMD_DEACTIVE_SCROLL, true); // 2E - i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_NORMAL, true); // A6 - i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); - - i2c_master_stop(cmd); - - espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); - if (espRc == ESP_OK) - { - ESP_LOGI(tag, "OLED configured successfully"); - } - else - { - ESP_LOGE(tag, "OLED configuration failed. code: 0x%.2X", espRc); - } - i2c_cmd_link_delete(cmd); -} - -void i2c_contrast( int contrast) { - i2c_cmd_handle_t cmd; - int _contrast = contrast; - if (contrast < 0x0) _contrast = 0; - if (contrast > 0xFF) _contrast = 0xFF; - - cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); - i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81 - i2c_master_write_byte(cmd, _contrast, true); - i2c_master_stop(cmd); - i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); -} - -void ssd1306_display_clear(void) { - i2c_cmd_handle_t cmd; - uint8_t zero[128] = {0}; - for (uint8_t i = 0; i < 8; i++) { - cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_SINGLE, true); - i2c_master_write_byte(cmd, 0xB0 | i, true); // Set GDDRAM page start address - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); - i2c_master_write(cmd, zero, 128, true); - i2c_master_stop(cmd); - i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - } -} - -void ssd1306_display_text(char* text, uint16_t text_len) -{ - - i2c_cmd_handle_t cmd; - - uint8_t cur_page = 0; - - cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); - i2c_master_write_byte(cmd, 0x00, true); // reset column - i2c_master_write_byte(cmd, 0x10, true); - i2c_master_write_byte(cmd, 0xB0 | cur_page, true); // reset page - - i2c_master_stop(cmd); - i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - - for (uint8_t i = 0; i < text_len; i++) - { - if (text[i] == '\n') - { - cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); - i2c_master_write_byte(cmd, 0x00, true); // reset column - i2c_master_write_byte(cmd, 0x10, true); - i2c_master_write_byte(cmd, 0xB0 | ++cur_page, true); // increment page - - i2c_master_stop(cmd); - i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - } - else - { - cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - - i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); - i2c_master_write(cmd, font8x8_basic_tr[(uint8_t)text[i]], 8, true); - - i2c_master_stop(cmd); - i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - } - } -} - - -void task_ssd1306_display_text(void *pvParameters) -{ - char *pcTaskName; - pcTaskName = (char *) pvParameters; - - //char text[13] = "Hello world!"; - //uint8_t text_len = strlen(text); - - ssd1306_display_text(pcTaskName, strlen(pcTaskName)); - - vTaskDelete(NULL); -} - - - - diff --git a/main/ssd1366.h b/main/ssd1366.h deleted file mode 100644 index 13fa6d0..0000000 --- a/main/ssd1366.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef MAIN_SSD1366_H_ -#define MAIN_SSD1366_H_ - -#include "freertos/FreeRTOS.h" -#include "driver/gpio.h" -#include "driver/i2c.h" -#include "esp_log.h" - -// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000) -#define OLED_I2C_ADDRESS 0x3C - -// Control byte -#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80 -#define OLED_CONTROL_BYTE_CMD_STREAM 0x00 -#define OLED_CONTROL_BYTE_DATA_STREAM 0x40 - -// Fundamental commands (pg.28) -#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F -#define OLED_CMD_DISPLAY_RAM 0xA4 -#define OLED_CMD_DISPLAY_ALLON 0xA5 -#define OLED_CMD_DISPLAY_NORMAL 0xA6 -#define OLED_CMD_DISPLAY_INVERTED 0xA7 -#define OLED_CMD_DISPLAY_OFF 0xAE -#define OLED_CMD_DISPLAY_ON 0xAF - -// Addressing Command Table (pg.30) -#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD -#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127 -#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7 -#define OLED_CMD_SET_PAGE_ADDR_MODE 0x02 // Page Addressing Mode - -// Hardware Config (pg.31) -#define OLED_CMD_SET_DISPLAY_START_LINE 0x40 -#define OLED_CMD_SET_SEGMENT_REMAP 0xA1 -#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX -#define OLED_CMD_SET_COM_SCAN_MODE 0xC8 -#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00 -#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12 -#define OLED_CMD_NOP 0xE3 // NOP - -// Timing and Driving Scheme (pg.32) -#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80 -#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1 -#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30 - -// Charge Pump (pg.62) -#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14 - -// Scrolling Command -#define OLED_CMD_HORIZONTAL_RIGHT 0x26 -#define OLED_CMD_HORIZONTAL_LEFT 0x27 -#define OLED_CMD_CONTINUOUS_SCROLL 0x29 -#define OLED_CMD_DEACTIVE_SCROLL 0x2E -#define OLED_CMD_ACTIVE_SCROLL 0x2F -#define OLED_CMD_VERTICAL 0xA3 - - -void i2c_master_init(); -void ssd1306_init(); -void task_ssd1306_display_text(void *pvParameters); -void i2c_contrast( int contrast); -void ssd1306_display_clear(void); -void ssd1306_display_text(char* text, uint16_t text_len); - -#endif /* MAIN_SSD1366_H_ */ \ No newline at end of file diff --git a/msv-clubhouse-backend.service b/msv-clubhouse-backend.service deleted file mode 100644 index a56b568..0000000 --- a/msv-clubhouse-backend.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=MSV-Clubhouse-Backend -After=syslog.target -After=network.target - -[Service] -RestartSec=2s -Type=oneshot -User=prometheus -Group=prometheus -WorkingDirectory=/opt/msv-clubhouse-backend/ -ExecStart=/usr/bin/python3 /opt/msv-clubhouse-backend/msv_clubhouse_backend.py - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/msv_clubhouse_backend.py b/msv_clubhouse_backend.py deleted file mode 100644 index fb8a922..0000000 --- a/msv_clubhouse_backend.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" Author: Hendrik Schutter, mail@hendrikschutter.com - Date of creation: 2022/03/01 - Date of last modification: 2023/03/02 -""" - -from http.server import BaseHTTPRequestHandler, HTTPServer -import paho.mqtt.client as mqtt -from datetime import datetime -import threading -import time -import json -import config - -scrape_healthy = True -startTime = datetime.now() -node_metrics = list() -mutex = threading.Lock() -request_count = 0 - -class RequestHandler(BaseHTTPRequestHandler): - - def get_metrics(self): - global request_count - global node_metrics - global mutex - mutex.acquire() - self.send_response(200) - self.send_header("Content-type", "text/html") - self.end_headers() - self.wfile.write(bytes(config.exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) - self.wfile.write(bytes(config.exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) - self.wfile.write(bytes(config.exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) - - for metric in node_metrics: - #print(metric) - self.wfile.write(bytes(config.exporter_prefix + metric + "\n", "utf-8")) - - mutex.release() - - def do_GET(self): - global request_count - request_count = request_count + 1 - print("Request: " + self.path) - if (self.path.startswith("/metrics")): - self.get_metrics() - else: - self.send_response(200) - self.send_header("Content-type", "text/html") - self.end_headers() - self.wfile.write(bytes("", "utf-8")) - self.wfile.write(bytes("MSV Clubhouse exporter", "utf-8")) - self.wfile.write(bytes("", "utf-8")) - self.wfile.write(bytes('

msv-clubhouse exporter based on data from LoRaWAN TTN node.

', "utf-8")) - self.wfile.write(bytes('

Metrics

', "utf-8")) - self.wfile.write(bytes("", "utf-8")) - self.wfile.write(bytes("", "utf-8")) - -def update_metrics(payload, metadata): - #print("Payload: "+ str(payload)) - #print("Metadata: "+ str(metadata)) - - global node_metrics - global mutex - global scrape_healthy - mutex.acquire() - scrape_healthy = True - node_metrics.clear() - - if "degreesC" in payload: - print("set degree: " + str(float(payload["degreesC"]))) - node_metrics.append("temperature " + str(float(payload["degreesC"]))) - - if "pressure" in payload: - print("set pressure: " + str(float(payload["pressure"]))) - node_metrics.append("pressure " + str(float(payload["pressure"]))) - - if "winddirection" in payload: - print("set winddirection: " + str(float(payload["winddirection"]))) - node_metrics.append("winddirection " + str(float(payload["winddirection"]))) - - if "windspeed" in payload: - print("set windspeed: " + str(float(payload["windspeed"]))) - node_metrics.append("windspeed " + str(float(payload["windspeed"]))) - - if "dooropen" in payload: - print("set dooropen: " + str(bool(payload["dooropen"]))) - node_metrics.append("dooropen " + str(int(payload["dooropen"]))) - - # if "gateway_id" in metadata[0]["gateway_ids"]: - # print("set gateway_id: " + str(metadata[0]["gateway_ids"]["gateway_id"])) - # node_metrics.append("gateway_id " + str(metadata[0]["gateway_ids"]["gateway_id"])) - - if "rssi" in metadata[0]: - print("set rssi: " + str(int(metadata[0]["rssi"]))) - node_metrics.append("rssi " + str(int(metadata[0]["rssi"]))) - - if "channel_rssi" in metadata[0]: - print("set channel_rssi: " + str(int(metadata[0]["channel_rssi"]))) - node_metrics.append("channel_rssi " + str(int(metadata[0]["channel_rssi"]))) - - if "snr" in metadata[0]: - print("set snr: " + str(float(metadata[0]["snr"]))) - node_metrics.append("snr " + str(float(metadata[0]["snr"]))) - - - #scrape_healthy = False - - mutex.release() - -def on_connect(mqttc, obj, flags, rc): - print("\nConnected to MQTT: rc = " + str(rc)) - -def on_message(mqttc, obj, msg): - #print("\nMessage: " + msg.topic + " " + str(msg.qos)) - parsedJSON = json.loads(msg.payload) - #print(json.dumps(parsedJSON, indent=4)) - - try: - uplink_message = parsedJSON["uplink_message"]; - update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"]) - except: - print("Unable to parse uplink") - -def on_subscribe(mqttc, obj, mid, granted_qos): - print("\nSubscribed to MQTT: " + str(mid) + " " + str(granted_qos)) - -def poll_mqtt(mqttc): - while True: - mqttc.loop(10) # seconds timeout - -def main(): - print("starting ...") - - mqttc = mqtt.Client() - mqttc.on_connect = on_connect - mqttc.on_subscribe = on_subscribe - mqttc.on_message = on_message - mqttc.username_pw_set(config.ttn_user, config.ttn_key) - mqttc.tls_set() - mqttc.connect(config.ttn_region.lower() + ".cloud.thethings.network", 8883, 60) - mqttc.subscribe("#", 0) # all device uplinks - - # run mqtt in thread forever - poll_mqtt_thread = threading.Thread(target=poll_mqtt, args=((mqttc,))) - poll_mqtt_thread.start() - - webServer = HTTPServer((config.hostName, config.serverPort), RequestHandler) - print("Server started http://%s:%s" % (config.hostName, config.serverPort)) - try: - webServer.serve_forever() - except KeyboardInterrupt: - pass - - webServer.server_close() - print("Server stopped.") - poll_mqtt_thread.join() - -if __name__ == "__main__": - main() \ No newline at end of file -- 2.47.1