141 lines
3.4 KiB
Markdown
141 lines
3.4 KiB
Markdown
# msv-clubhouse-node
|
|
- Work in progress !
|
|
- Technical demo
|
|
|
|
## Overview
|
|
|
|
![architecture diagram](doc/architecture.png)
|
|
|
|
## Hardware ##
|
|
|
|
- [ESP32 LoRa 868MHz OLED DevBoard](https://de.aliexpress.com/item/33018609928.html)
|
|
- [Bosch BMP280](https://de.aliexpress.com/item/1005003406725433.html)
|
|
- [Reed Switch](https://de.aliexpress.com/item/1005004549803034.html)
|
|
- [Wind speed sensor](https://de.aliexpress.com/item/1005001330214766.html)
|
|
- [Wind direction sensor](https://de.aliexpress.com/item/4000590284491.html)
|
|
|
|
|
|
## Register Device at TTN
|
|
|
|
0. Register manually a device.
|
|
1. Frequency plan Europe 863-870 MHz (SF9 for RX2 - recommended)
|
|
2. LoRaWAN version LoRaWAN Specification 1.0.2
|
|
3. Regional Parameters version RP001 Regional Parameters 1.0.2
|
|
4. JoinEUI all zeros
|
|
5. Generate keys
|
|
|
|
## Build Node Firmware
|
|
|
|
0. Clone repo
|
|
1. Init GIT-submodules
|
|
|
|
```
|
|
git submodule init
|
|
git submodule update
|
|
```
|
|
|
|
2. Set following defines via menuconfig:
|
|
|
|
```
|
|
AppEUI
|
|
DevEUI
|
|
AppKey
|
|
TTN LoRa frequency / region
|
|
```
|
|
|
|
3. Build with ESP-IDF extension in VSCodium
|
|
|
|
|
|
## MQTT Endpoint ##
|
|
|
|
`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
|
|
|
|
```
|
|
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;
|
|
}
|
|
```
|
|
|
|
## Grafana Dashboard
|
|
|
|
see `frontend/`
|
|
|
|
![screenshot of grafana-dashboard](doc/screenshot_dashboard.png) |