{{ :elektronik:lora_ferrariszaehler:header.jpg?nolink&3000 |}}
====== LoRa-Ferraris-Zähler ======
Auch ein Projekt, dass ich in mehreren Iterationen aufgegriffen habe (und auch etwas Nerven gekostet hat 😅). Ein Sensor, der die guten, alten Ferraris-Stromzähler abgreifen kann. Die mit der Zählscheibe.
Der Stromverbrauch des Hauses sollte digital auf dem PC oder Smartphone als Diagramm angezeigt werden können.
Das sagt Wikipedia dazu:
{{wp:de>Ferraris-Zähler}}
===== Sensor =====
Der Sensor besteht aus einer grünen 5 mm LED und einer 3 mm Fotodiode.
Unter grünem Licht wird die rote Markierung der Zählerscheibe dunkel, sie emmitiert also kaum Licht. Ansonsten reflektiert die metallene Scheibe das grüne Licht sehr gut. Also ist grünes Licht sehr gut geeignet für die Abtastung der Scheibe.
So ist das Prinzip:
{{:elektronik:lora_ferrariszaehler:scheibe_skizze.svg?200|}}
Wichtig noch, dass Fotodiode und LED angewinkelt sind (EiInfallswinkel ist gleich Ausfallswinkel), damit das gut funktioniert.
Ansonsten habe ich ein schwarzes, nicht störendes Gehäuse gedruckt. Mit einem Schlitz in der Mitte, damit der Sensor exakt über der Scheibe ausgerichtet werden kann.
===== Schaltung =====
Für die Schaltung habe ich mir ein 5 V Schaltnetzteil im Zählerschrank eingebaut (Achtung: Netzspannung; Man sollte wissen, was man da tut...). Am Netzteil kann man die Spannung bis 4 V heruntersetzen (da das LoRa Modul ja offiziell keine 5 V unterstützt).
Verwendet wird ein Arduino-Nano, welcher über seinen USB-Port vom Netzteil versorgt wird.
Hier die Schaltung. Dieses mal in ASCII-Art. 😁
+---------------------------------------------------+
| +-----+ +-----------+ |
| +-+ | | LED | |
| | | | | grün +-+ |
| 1MOhm| | | +-----+ | | |
| +-+ | + + +-> | | 50Ohm|
| +-----------+ | +-+ +--> +-+ |
| | Photo | | +---+ | |
| | Diode +-----+ | | | |
| | +-> + + | | | |
| | +--> +-+ +-----+ | |
| | +---+ | | |
+---------------------------------------------------+
| | | |
| | | |
+ + + +
ADC0 VCC GND VCC
===== Berechnung =====
Die Intervalle zwischen den Zählimpulsen können wie folgt in Watt umgerechnet werden:
$\frac{3600000}{75,0 \frac{u}{kwh} \cdot \frac{\color{red}{x} ms}{1000.0}} = \color{red}{y} W$
Bei meinem Zähler entsprechen 75 Umdrehungen einer Kilowattstunde.
===== Software =====
Die Software wurde in diesem Fall mit der Arduino-IDE erstellt. Als Library für LoRa ist Tinylora im Einsatz.
Prinzipiell ist die Software nichts besonderes.
Der Analogwert des Sensors wird regelmäßig eingelesen. Dann wird ein gleitender Mittelwert über ca. 4 min gebildet. Als Stricherkennung dient dann der Unterschied zum Mittelwert. Da der Spannungsunterschied bei mir nicht sonderlich groß ist, habe ich da fix "4" als Konstante hinterlegt. Der benötigte Wert wurde empirisch mit Messgerät ermittelt.
++++ ttn_stromzaehler.ino |
#include
#include
// Network Session Key (MSB)
uint8_t NwkSkey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// Application Session Key (MSB)
uint8_t AppSkey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// Device Address (MSB)
uint8_t DevAddr[4] = { 0x00, 0x00, 0x00, 0x00 };
unsigned char loraData[5];
TinyLoRa lora = TinyLoRa(2, 10);
void setup()
{
delay(100);
Serial.begin(9600);
while (!Serial);
Serial.print("Starting LoRa...");
lora.setChannel(CH2);
lora.setDatarate(SF7BW125);
if(!lora.begin())
{
Serial.println("Failed");
Serial.println("Check your radio");
while(true);
}
Serial.println("OK");
pinMode(8, OUTPUT);
digitalWrite(8, true);
delayMicroseconds(300);
}
unsigned long time_last;
unsigned long time_debounce_last;
int history[50];
int ct1 = 0;
int ct2 = 0;
bool started = false;
void loop()
{
int val = analogRead(A0);
//gleitender Mittelwert
ct1++;
if(ct1==100) {
ct1=0;
ct2++;
if(ct2==50) {
ct2=0;
started=true;
}
history[ct2] = val;
}
unsigned long sum = 0;
for(int i = 0; i < 50; i++) {
sum += history[i];
}
unsigned long mean = sum / 50;
//Verarbeitung
int delta = mean - val;
unsigned long time = millis();
unsigned long time_delta = time - time_last;
if(delta>4 && started) {
if(time - time_debounce_last > 2000) {
union {
unsigned long val;
unsigned char bytes[4];
} tmp;
tmp.val = time_delta;
loraData[0] = 1; //Gerätetypkennzeichner für TTN decoder
loraData[1] = tmp.bytes[0];
loraData[2] = tmp.bytes[1];
loraData[3] = tmp.bytes[2];
loraData[4] = tmp.bytes[3];
Serial.println("Sending LoRa Data...");
lora.begin();
lora.sendData(loraData, sizeof(loraData), lora.frameCounter);
Serial.print("Frame Counter: ");Serial.println(lora.frameCounter);
lora.frameCounter++;
time_last = time;
}
time_debounce_last = time;
}
delay(50);
}
++++
Hier noch die Berechnung in ioBroker, inklusive dem Anlegen der States:
++++ ioBroker Script |
//Zählerkasten
createState("custom.ttn.stromzaehler.leistung", false, {
name: "Leistung",
read: true,
write: false,
desc: "",
def: 0,
unit: "W",
role: "value.power.consumption",
type: "number"
});
on({id: 'mqtt.1.sensornetwork.devices.zaehlerkasten.up', change: "ne"}, function (obj) {
obj = JSON.parse(getState("mqtt.1.sensornetwork.devices.zaehlerkasten.up").val);
var tmp = parseFloat(obj["payload_fields"]["interval"].toFixed(2));
tmp = 3600000.0/(75.0 * (tmp/1000.0))
setState("custom.ttn.stromzaehler.leistung", parseFloat(tmp.toFixed(2)), true);
});
++++
Zuletzt noch der TTN-Decoder:
++++ TTN-Decoder |
function Bytes2Float32(bytes) {
var sign = (bytes & 0x80000000) ? -1 : 1;
var exponent = ((bytes >> 23) & 0xFF) - 127;
var significand = (bytes & ~(-1 << 23));
if (exponent == 128)
return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
if (exponent == -127) {
if (significand === 0) return sign * 0.0 ;
exponent = -126;
significand /= (1 << 22);
} else significand = (significand | (1 << 23)) / (1 << 23);
return sign * significand * Math.pow(2, exponent);
}
function Decoder(bytes, port) {
if (port === 1) {
if(bytes[0] == 0) {
return {
temperature: Bytes2Float32(bytes[3+1]<<24 | bytes[2+1]<<16 | bytes[1+1]<<8 | bytes[0+1]),
humidity: Bytes2Float32(bytes[3+4+1]<<24 | bytes[2+4+1]<<16 | bytes[1+4+1]<<8 | bytes[0+4+1]),
pressure: Bytes2Float32(bytes[3+8+1]<<24 | bytes[2+8+1]<<16 | bytes[1+8+1]<<8 | bytes[0+8+1]),
battery: bytes[1+12+1]<<8 | bytes[0+12+1]
};
}
if(bytes.length==5 && bytes[0] == 1) {
return {
interval: (bytes[4] << 24) | (bytes[3] << 16) | (bytes[2] << 8) | bytes[1]
};
}
}
}
++++
===== Historie =====
Meine ersten Versuche waren batteriebetrieben und daher sehr auf Energieverbrauch optimiert.
Allerdings: Egal was ich gemacht hatte, ich bin nie über die Batterielebensdauer von einigen Wochen gekommen. Daher habe ich mich mit der LoRa-Variante dazu entschlossen auf ein Netzteil zurückzugreifen.
==== 1. Versuch: MySensors ====
Der erste Versuch war mit [[https://www.mysensors.org/|MySensors]]. Ich habe das aber zum damaligen Zeitpunkt nie wirklich stabil hinbekommen.
Vermutlich war das Funktsignal im Zählerkasten zu instabil.
=== Source ===
Achtung: Keine Garantie auf Funktionstüchtigkeit!
++++ program.ino |
//#define MY_DEBUG
#define MY_RADIO_NRF24
#include
#include
//config
#define LED_PIN 6
#define SENSOR_PIN 0
#define N_PER_KW 75.0
#define SLEEP_TIME 300
#define SLEEP_OVERHEAD 43 //ermittelt durch digitalWrite vor und nach Sleep und Oszi-Gegenmessung
#define LED_ON_TIME 3
#define MEAN_VALUES_CT 200
#define MEAN_VALUE_INTERVAL 2
#define HYSTERESE_HIGH 130
#define HYSTERESE_LOW 40
void setup() {
pinMode(LED_PIN, OUTPUT);
//Serial.begin(115200);
}
#define CHILD_ID_POW 0
MyMessage msgPow(CHILD_ID_POW, V_WATT);
void presentation()
{
sendSketchInfo("OpticalPower", "1.0");
present(CHILD_ID_POW, S_POWER);
}
int values[MEAN_VALUES_CT];
int ct = 0;
int ct2 = 0;
long sleep_ct;
bool sig = false;
bool sig_last = false;
long millis_last = 0;
void loop() {
//Messung
digitalWrite(LED_PIN, HIGH);
delay(LED_ON_TIME);
int val = analogRead(SENSOR_PIN);
digitalWrite(LED_PIN, LOW);
//Mittelwertbildung
if(ctHYSTERESE_HIGH && sig == false) sig = true;
if(mean-val
++++
==== 2. Versuch: ESP32 ====
Die zweite Variante nutzte einen ESP32 (ohne "USB-Board") und seinen stromsparenden ULP-Prozessor.
Daten werden über MQTT und WIFI versendet. Die Einstellungen (inklusive WLAN) werden über einen WIFI-Manager gemacht.
Die LED wird über den ULP angesteuert. Und auch der ADC und der Vergleich wird über den ULP gemacht. Erst, wenn der Strich auf der Scheibe kommt startet der "große" Prozessor und sendet das Ergebnis per WLAN.
Hat sich aber dennoch nicht durchgesetzt, da hiermit auch nur eine Laufzeit von wenigen Wochen möglich war und das WLAN-Signal im Schaltschrank nicht sonderlich gut ist, weshelb mit WLAN öfter Pakete verloren gingen.
War aber dennoch sehr interessant die Programmierung des ULPs im ESP32 kennen zu lernen.
=== Source ===
Achtung: Keine Garantie auf Funktionstüchtigkeit!
++++ program.ino |
#include //must be first
#include "SPIFFS.h"
#include "esp32/ulp.h"
#include "esp_deep_sleep.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "driver/adc.h"
#include
#include
#include
#define ULP_DATA_OFFSET 100 //muss größer als ULP Programmlänge sein -> auch in Wifimanager anpassen
#define SARADC1 0 //ADC1
#pragma region "Parameter handling"
char c_brokeraddress[100] = "192.168.2.100";
char c_brokerport[5] = "1883";
char c_brokeruser[50] = "none";
char c_brokerpassword[50] = "none";
char c_topic[200] = "sensor/";
char c_clientid[100] = "ESP8266Client-XXXX";
char c_interval[10] = "250000";
char c_led_on[10] = "3000";
char c_max_freerun[10] = "240";
char c_rpkwh[10] = "75.0";
char c_hysterese[10] = "20";
char c_threshold[10] = "512";
WiFiManagerParameter* custom_brokeraddress;
WiFiManagerParameter* custom_brokerport;
WiFiManagerParameter* custom_brokeruser;
WiFiManagerParameter* custom_brokerpassword;
WiFiManagerParameter* custom_topic;
WiFiManagerParameter* custom_clientid;
WiFiManagerParameter* custom_interval;
WiFiManagerParameter* custom_led_on;
WiFiManagerParameter* custom_max_freerun;
WiFiManagerParameter* custom_rpkwh;
WiFiManagerParameter* custom_hysterese;
WiFiManagerParameter* custom_threshold;
WiFiManagerParameter* custom_text;
bool paramShouldSave = false;
void saveConfigCallback () {
paramShouldSave = true;
}
void add_parameter(WiFiManager &w) {
custom_brokeraddress = new WiFiManagerParameter("brokeraddress", "Broker-Address", c_brokeraddress, 100);
custom_brokerport = new WiFiManagerParameter("brokerport", "Broker-Port", c_brokerport, 5);
custom_brokeruser = new WiFiManagerParameter("brokeruser", "Broker-User", c_brokeruser, 50);
custom_brokerpassword = new WiFiManagerParameter("brokerpassword", "Broker-Password", c_brokerpassword, 50);
custom_topic = new WiFiManagerParameter("topic", "Topic", c_topic, 200);
custom_clientid = new WiFiManagerParameter("clientid", "Client-ID", c_clientid, 100);
custom_interval = new WiFiManagerParameter("interval", "Interval (us)", c_interval, 10);
custom_led_on = new WiFiManagerParameter("led_on", "LED on time (ulp-cycles)", c_led_on, 10);
custom_max_freerun = new WiFiManagerParameter("max_freerun", "Max freerun (s)", c_max_freerun, 10);
custom_rpkwh = new WiFiManagerParameter("rpkwh", "Rotations per kWh", c_rpkwh, 10);
custom_hysterese = new WiFiManagerParameter("hysterese", "Hysterese", c_hysterese, 10);
custom_threshold = new WiFiManagerParameter("threshold", "Threshold (-val = falling edge)", c_threshold, 10);
custom_text = new WiFiManagerParameter("actual value: ");
w.addParameter(custom_brokeraddress);
w.addParameter(custom_brokerport);
w.addParameter(custom_brokeruser);
w.addParameter(custom_brokerpassword);
w.addParameter(custom_topic);
w.addParameter(custom_clientid);
w.addParameter(custom_interval);
w.addParameter(custom_led_on);
w.addParameter(custom_max_freerun);
w.addParameter(custom_rpkwh);
w.addParameter(custom_hysterese);
w.addParameter(custom_threshold);
w.addParameter(custom_text);
}
void read_parameter() {
//read Variables from Flash
if (SPIFFS.begin(true)) {
if (SPIFFS.exists("/config.json")) {
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
size_t size = configFile.size();
std::unique_ptr buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer(2048);
JsonObject& json = jsonBuffer.parseObject(buf.get());
if (json.success()) {
strcpy(c_brokeraddress, json["brokeraddress"]);
strcpy(c_brokerport, json["brokerport"]);
strcpy(c_brokeruser, json["brokeruser"]);
strcpy(c_brokerpassword, json["brokerpassword"]);
strcpy(c_topic, json["topic"]);
strcpy(c_clientid, json["clientid"]);
strcpy(c_interval, json["interval"]);
strcpy(c_led_on, json["led_on"]);
strcpy(c_max_freerun, json["max_freerun"]);
strcpy(c_rpkwh, json["rpkwh"]);
strcpy(c_hysterese, json["hysterese"]);
strcpy(c_threshold, json["threshold"]);
}
}
}
}
}
void write_parameter() {
if(paramShouldSave) {
DynamicJsonBuffer jsonBuffer(2048);
JsonObject& json = jsonBuffer.createObject();
json["brokeraddress"] = (*custom_brokeraddress).getValue();
json["brokerport"] = (*custom_brokerport).getValue();
json["brokeruser"] = (*custom_brokeruser).getValue();
json["brokerpassword"] = (*custom_brokerpassword).getValue();
json["topic"] = (*custom_topic).getValue();
json["clientid"] = (*custom_clientid).getValue();
json["interval"] = (*custom_interval).getValue();
json["led_on"] = (*custom_led_on).getValue();
json["max_freerun"] = (*custom_max_freerun).getValue();
json["rpkwh"] = (*custom_rpkwh).getValue();
json["hysterese"] = (*custom_hysterese).getValue();
json["threshold"] = (*custom_threshold).getValue();
File configFile = SPIFFS.open("/config.json", "w");
json.printTo(configFile);
configFile.close();
delay(10);
}
}
#pragma endregion
/*
+---------------------------------------------------+
| +-----+ +-----------+ |
| +-+ | | LED | |
| | | | | grün +-+ |
| 1MOhm| | | +-----+ | | |
| +-+ | + + +-> | | 50Ohm|
| +-----------+ | +-+ +--> +-+ |
| | Photo | | +---+ | |
| | Diode +-----+ | | | |
| | +-> + + | | | |
| | +--> +-+ +-----+ | |
| | +---+ | | |
+---------------------------------------------------+
| | | |
| | | |
+ + + +
IO34 3V3 GND IO25
*/
void init_ulp() {
RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] = 0;
//RTC-Memory:
//0: count
//1: adc
//2: signal
//3: signal_last
//4: adc_bat
ulp_insn_t program[] = {
//R3-> Immer Speicher-Addresse!!!
I_MOVI(R3, ULP_DATA_OFFSET),
//Wert laden und inkrementieren
I_LD(R0, R3, 0),
I_ADDI(R0, R0, 1),
I_ST(R0, R3, 0),
//test
//I_MOVI(R0, abs(String(c_threshold).toInt())),
//I_ST(R0, R3, 4),
//LED an
I_WR_REG_BIT(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + RTCIO_GPIO25_CHANNEL, 1), // LED an
I_DELAY(String(c_led_on).toInt()), //30000 ~ 3,x ms -> 3000 ~ 3xx uS
//ADC (Bat) lesen
I_ADC(R0, SARADC1, ADC1_CHANNEL_5),
I_ST(R0, R3, 4),
//ADC lesen
I_ADC(R0, SARADC1, ADC1_CHANNEL_6),
I_WR_REG_BIT(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + RTCIO_GPIO25_CHANNEL, 0), // LED aus
I_ST(R0, R3, 1),
//Signal mit Hysterese - Achtung: benötigt passenden R0 (ADC von LED)
M_BGE(1, abs(String(c_threshold).toInt())+(String(c_hysterese).toInt()/2)),
M_BL(2, abs(String(c_threshold).toInt())-(String(c_hysterese).toInt()/2)),
M_BX(3),
M_LABEL(1), //größer
I_MOVI(R0, 1),
I_ST(R0, R3, 2),
M_BX(3),
M_LABEL(2), //kleiner
I_MOVI(R0, 0),
I_ST(R0, R3, 2),
M_BX(3),
M_LABEL(3), //ende
//Flankenerkennung
I_LD(R1, R3, 2),
I_LD(R2, R3, 3),
I_SUBR(R0, R1, R2),
M_BXZ(11), //ueberspringe wenn keine Aenderung
I_SUBI(R0, R1, (((String(c_threshold).toInt())>0) ? 1 : 0)), //pos/neg Flanke
M_BXZ(10), //wenn richtige flanke
M_BX(11), //sonst überspringe
M_LABEL(10), //richtige flanke
I_WAKE(), //wecken
M_LABEL(11), //ende
//Signal_last update
I_LD(R0, R3, 2),
I_ST(R0, R3, 3),
I_HALT()
};
size_t load_addr = 0;
size_t size = sizeof(program)/sizeof(ulp_insn_t);
adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_DB_11); //GPIO33
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); //GPIO34
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_ulp_enable();
rtc_gpio_init(GPIO_NUM_25);
rtc_gpio_set_direction(GPIO_NUM_25, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(GPIO_NUM_25, 0);
ulp_set_wakeup_period(0, String(c_interval).toInt());
ulp_process_macros_and_load(load_addr, program, &size);
ulp_run(load_addr);
//energie sparen durch disconnect der Pins
rtc_gpio_isolate(GPIO_NUM_12);
rtc_gpio_isolate(GPIO_NUM_15);
// ? //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
}
void setup() {
Serial.begin(115200);
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
read_parameter();
Serial.println("rst-cause " + (String)wakeup_reason);
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) // ESP_DEEP_SLEEP_WAKEUP_ULP (5) -> ULP-Wake
init_ulp();
int count = RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] & 0xffff; //Maskieren, da nur 16-bit
RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] = 0;
strncpy(c_clientid,((String)"ESP8266Client-" + String(random(0xffff), HEX)).c_str(),strlen(c_clientid));
WiFiManager wifimgr;
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) wifimgr.resetSettings();
wifimgr.setSaveConfigCallback(saveConfigCallback);
add_parameter(wifimgr);
wifimgr.autoConnect("WIFI-CONFIG");
write_parameter();
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) init_ulp();
if(wakeup_reason == ESP_DEEP_SLEEP_WAKEUP_ULP) {
int adc = RTC_SLOW_MEM[ULP_DATA_OFFSET + 1] & 0xffff; //Maskieren, da nur 16-bit
int signal = RTC_SLOW_MEM[ULP_DATA_OFFSET + 2] & 0xffff;
int signal_last = RTC_SLOW_MEM[ULP_DATA_OFFSET + 3] & 0xffff;
int adc_bat = RTC_SLOW_MEM[ULP_DATA_OFFSET + 4] & 0xffff;
//int signal_test = RTC_SLOW_MEM[ULP_DATA_OFFSET + 4] & 0xffff;
//Serial.println(signal_test);
//int led_delay = (int)((double)String(c_led_on).toInt()*(1000000.0/(double)RTC_FAST_CLK)); aktuell unberücksichtigt...
int timediff = ((count-1)*String(c_interval).toInt())/1000; //in ms
double kwh = 1000.0/String(c_rpkwh).toFloat()/((float)timediff/3600000.0); //3.6e+6ms = 1h
Serial.println("values " + (String)count + " " + (String)adc/* + " - " + (String)analogRead(34)*/ + " " + (String)signal + " " + (String)timediff + " " + (String)kwh);
//
// Send Values
//
char* user = c_brokeruser;
char* password = c_brokerpassword;
if((String)user=="none") user = NULL;
if((String)password=="none") password = NULL;
WiFiClient espClient;
espClient.setNoDelay(true);
PubSubClient client(espClient);
client.setServer(c_brokeraddress, String(c_brokerport).toInt());
if (client.connect(c_clientid, user, password)) {
client.publish((c_topic + (String)"kwh").c_str(), String(kwh).c_str());
client.publish((c_topic + (String)"timediff").c_str(), String((double)timediff).c_str());
client.publish((c_topic + (String)"powerlevel").c_str(), String(adc_bat).c_str());
client.disconnect();
}
//rtc_gpio_set_level(GPIO_NUM_25, 1);
//delay(500);
//rtc_gpio_set_level(GPIO_NUM_25, 0);
//Serial.println(rtc_gpio_is_valid_gpio(GPIO_NUM_25));
}
Serial.flush();
delay(20);
esp_deep_sleep_enable_ulp_wakeup();
esp_deep_sleep_start();
}
void loop() {
delay(1);
}
++++
==== 3. Versuch: LoRa ====
Der dritte Versuch war dann letztendlich mit LoRa. Die Gebäudedurchdringung (und Schaltschrankdurchdringung 😁) von LoRa ist deutlich besser als von den beiden anderen Varianten.
Ich betreibe mein eigenes Gateway (auf dem anderen Ende des Grundstücks), welches absolut problemlos die Funksignale aufgreifen kann. Die Daten werden über das [[https://www.thethingsnetwork.org/|TTN]] gesendet.
Das ist auch der Aufbau, den ich hier beschreibe.
===== Sicherheit =====
Daten über das TTN sind prinzipiell verschlüsselt. Aber da in diesem Fall (bei dem aktuellen Programm) ja die Sendeintervalle und der Sender mehr relevant, als die eigentlichen Daten sind, bringt das hier natürlich relativ wenig.
Man kann aufgrund ein paar Zusatzinformationen auf den Stromverbrauch schließen. Man braucht quasi nur den Umrechnungsfaktor (kann man ggf. auch schätzen) und die Device-ID. Damit kann man auch von außen die Daten abgreifen.
Das sollte einem bewusst sein. Ggf. die Sendeleistung so verringern, dass die Pakete nur noch vom eigenen Gateway empfangen werden können.
===== Bilder =====
{{:elektronik:lora_ferrariszaehler:sensor_aufbau.jpg?direct&300|}}
\\ Sensoraufbau\\ Bestehend aus Widerständen,\\ einer Fotodiode und einer LED
{{:elektronik:lora_ferrariszaehler:verteilerkasten_abtastung.jpg?direct&300|}}
\\ Stromzähler mit montierten Sensor
{{:elektronik:lora_ferrariszaehler:verteilerkasten_netzteil.jpg?direct&300|}}
\\ Stromversorgung
===== Screenshots =====
{{:elektronik:lora_ferrariszaehler:lovelace_leistung.png?direct&300|}}
\\ Darstellung in der ioBroker-\\ Lovelace-UI (webbasierend)
{{:elektronik:lora_ferrariszaehler:grafana_diagram.png?direct&300|}}
\\ Test-Diagram in Grafana
{{:elektronik:lora_ferrariszaehler:grafana_heatmap.png?direct&300|}}
\\ Test-Heatmap in Grafana
===== 3D-Modell =====
{{:elektronik:lora_ferrariszaehler:gehaeuse_sensor.stl?h=200&w=300&bgcolor=#ffffff|Sensor}}
{{tag>[mysensors esp32 arduino nano strom leistung zähler ulp mikrocontroller lora ttn]}}
\\ ~~DISQUS~~