Inhaltsverzeichnis
Lesekopf für den Gaszähler BK-G4M
Zur Ermittlung des Gasverbrauchs haben Gaszähler in der Regel einen Magneten im Zählwerk eingebaut. Dieser rotiert auf einer der Walze des Zählwerks mit und kann für Ermitteln des abgegebenen Volumens an Gas herangezogen werden. Aufbau eines Gaszähler / youtube video
Typischer Weise wird dieses mit einem Reed-Kontakt realisiert. Dieser schließt, wenn der Magnet auf der Walze in die Nähe des Kontaktes kommt. Bei manchen Gaszählern ist das Magnetfeld zu schwach, um einen Reedkontakt zu schließen oder einen Hallsensor auszulösen. Der vorliegender Ansatz verwenden einen elektronischen Kompass, der die fallende Flanke eines Magnetfelds zählt und dadurch auf das Gasvolumen schließen lässt.
Als Magnetfeldsensor wird mit dem HSCDTD008A ein Low-Cost-Sensor verwendet, der für die Anwendung völlig ausreicht. Der HSCDTD008A vermisst die Stärke des Magnetfelds in den 3 Raumrichtungen. Für die Anwendung interessiert lediglich die Stärke des Gesamtfeldes. Die Richtung ist nicht wesentlich.
Gegenwärtig hat die Software den Reifestand eines PoC, d.h. er stellt einen funktionalen Versuch dar und der Funktionsumfang ist nicht komplett.
Das Programm misst alle 500ms die 3 Komponenten des Magnetfeldes und errechnet den Betrag des gemessenen Magnetfeldes. Zusammen mit dem vorherigen Messwert kann man dann ermitteln, ob eine fallende Flanke vorliegt: Wenn der alte Messwerte einen Triggerlevel überschreitet, und der nächsten einen zweiten tiefer liegenden Triggerlevel unterschreitet, dann liegt eine fallende Flanke vor, die gezählt wird. Ein weitere Programm, ein Python-Script, wird regelmäßig von vzlogger aufgerufen und transportiert die gezählten Impulse in die Datenbank des Volkszählers.
Benötigte Komponenten
- HSCDTD008A mit BreakoutBox
- Halterung ( Thingiverse)
- HSCDTD008A Library ( HSCDTD008A library )
- Binary (PoC) zum Zählen der Impulse
- Python Script zum Vorbreiten der gezählten Impulse für vzlogger
- Linux Kleinrechner (z.B. Raspberry PI)
Aufbau
- 3D-Druck der Halterung
- Nach dem Drucken der Halterung habe ich, in Ermangelung der Fähigkeit, STL-Files zu editieren, eine Öffnung in die Halterung für die Platine des Sensors hineingeschnitten
- Entfernen der I2C-Widerstände auf der Platine des Sensors, da diese bereits im Raspberry Pi einbaut sind
- Anlöten eines Kabels an den Sensors
- Die Platine des Sensors ist lediglich in die Halterung gesteckt. Das Kabel ist mit Kabelbindern an der Halterung befestigt. Das ist stabil genug. Man könnte den Sensor sicherlich zusätzlich in die Öffnung der Halterung einkleben, um ihn vor Umwelteinflüssen zu schützen
Software
Ein Proof of Concept (PoC).
Binary
/**************************************************************** * Example5_Basics.cpp * HSCDTD008A Library Demo * Tilman Glötzner * Original Creation Date: 2022-09-03 * * * Distributed as-is; no warranty is given. ***************************************************************/ #include "hscdtd008a.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <math.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> //strlen #include <sys/socket.h> #include <arpa/inet.h> //inet_addr #include <signal.h> #include <pthread.h> #include <semaphore.h> #define NUMBABSENTRIES 3 // Create an instance of the sensor. HSCDTD008A geomag; void setup() { hscdtd_status_t status; //geomag.begin(); // If you know the I2C address is different than in the provided // data sheet. Uncomment the line below, and configure the address. geomag.begin(0x0F,(char*)"/dev/i2c-1"); // Initialize the hardware. status = geomag.initialize(); if (status != HSCDTD_STAT_OK) { printf("Failed to initialize sensor. Status:%d. Check wiring.\n", status); // Halt program here. exit(1); } // Compensate for temperature. geomag.temperatureCompensation(); } hscdtd_status_t status; sem_t mutex; float bAbs[NUMBABSENTRIES];; int bAbsLastEntryIndex = 0; float lowLimit = 50; float highLimit = 200; unsigned long counter = 0; unsigned long totalCounter = 0; int stopFlag = 0; int daemonized = 0; int verbose = 2; void* loop(void *ptr) { int k; memset(bAbs, 0, sizeof(float)); // Explicitly start a reading. while (stopFlag == 0) { status = geomag.startMeasurement(); // If the status is OK then we can print the result. if (status == HSCDTD_STAT_OK) { bAbs[bAbsLastEntryIndex] = sqrt(pow(geomag.mag.mag_x, 2) + pow(geomag.mag.mag_y, 2) + pow(geomag.mag.mag_z, 2)); // weed out erroneous readings bAbs = 0 if (bAbs[bAbsLastEntryIndex] == 0) { //erronous measurement if (verbose >=2) { char c; printf("bABS==0;"); printf("X: %f uT, Y: %f uT, Z: %f uT; |B|: %f uT;\n", geomag.mag.mag_x, geomag.mag.mag_y, geomag.mag.mag_z,bAbs[bAbsLastEntryIndex]); for (k = 0 ; k < NUMBABSENTRIES; k++) { if (k == bAbsLastEntryIndex) c = '*'; else c = ' '; printf("%c|B[%d]|: %f uT;",c,k,bAbs[k]); } printf(";Counter: %lu; TotalCounter: %lu\n", counter,totalCounter); } continue; } // trigger on falling flank. // To prevent occassional missreadings (i.e. bAbs = 0) several measurements are used // all new entries but the oldest need to be under the lower limit // while the oldest entriey needs to be above the high limit int trigger = 0; int index = 0; int mod = 0; for (k=0; k < NUMBABSENTRIES; k++) { mod = ((bAbsLastEntryIndex-k) % NUMBABSENTRIES); index = mod >= 0 ? mod : NUMBABSENTRIES + mod ; if (k<NUMBABSENTRIES-1) { if (bAbs[index] < lowLimit) trigger++; } else { if (bAbs[index] > highLimit) trigger++; } if (verbose >=3 ) { printf("trigger: %d; bAbs[%d]: %f\n", trigger, index, bAbs[index] ); } } if (trigger == NUMBABSENTRIES) { sem_wait(&mutex); counter++; sem_post(&mutex); totalCounter++; if (verbose >= 2) { printf("INC: Counter: %lu", counter); printf(";TotalCounter: %lu;", totalCounter); char c; for (k = 0 ; k < NUMBABSENTRIES; k++) { if (k == bAbsLastEntryIndex) c = '*'; else c = ' '; printf("%c|B[%d]|: %f uT;",c,k,bAbs[k]); } printf("\n"); } } bAbsLastEntryIndex = (bAbsLastEntryIndex + 1) % NUMBABSENTRIES; } else { if (verbose >= 1) { printf("Error occurred, unable to read sensor data. Exiting ...\n"); } stopFlag = 1; //exit(1); } // Wait 500ms before reading the next sample. usleep(500000); } return NULL; } int main(int argc, char** argv) { setup(); int socket_desc , new_socket , c; struct sockaddr_in server , client; pthread_t readMeterThread; sem_init(&mutex, 0, 1); int iret1; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) printf("Could not create socket"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) puts("bind failed"); puts("bind done"); iret1 = pthread_create( &readMeterThread, NULL, loop, (void*) NULL); char sendBuff[150]; //Listen listen(socket_desc , 1); puts("Waiting for incoming connections..."); while (stopFlag == 0) { //Accept and incoming connection new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); if (new_socket>=0) { snprintf(sendBuff, sizeof(sendBuff), "{ \"counter\": %lu, \"total\": %lu } ", counter, totalCounter); write(new_socket, sendBuff, strlen(sendBuff)); close(new_socket); sem_wait(&mutex); counter = 0; sem_post(&mutex); } else { perror("accept failed"); } } pthread_join( readMeterThread, NULL); }
Pythonscript
#!/usr/bin/python3 import datetime import socket import json import syslog HOST = "127.0.0.1" # The server's hostname or IP address PORT = 8888 # The port used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", 8888)) string = (s.recv(1024).decode('utf-8')) #print (string) data = json.loads(string) now = str(int( datetime.datetime.now().timestamp() )) print(now + ': Counter = ' + str(data["counter"])) #print(now + ': TotalCounter = ' + str(data["total"])) if data["counter"] != 0: message = now + ': Counter = ' + str(data["counter"]) + '; TotalCounter = ' + str(data["total"]) syslog.syslog(syslog.LOG_DEBUG, message)
vzlogger.conf
.... { // gas counter via i2c daemon and python script "enabled": true, "allowskip": true, "protocol": "exec", "command": "countingmeter.py", "format": "$t: $i = $v", "interval": 60, "channels": [{ "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "identifier": "Counter", "api": "volkszaehler", "middleware": "http://localhost/middleware.php", "aggmode": "none" }] } ....