From d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 10 Oct 2022 21:25:01 +0200 Subject: daemon & client --- .gitignore | 5 ++ hw/platformio.ini | 12 ++++ hw/src/main.cpp | 75 ++++++++++++++++++++++ platformio.ini | 12 ---- src/main.cpp | 75 ---------------------- srv/c.c | 114 ++++++++++++++++++++++++++++++++++ srv/d.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ "\305\241tevec.service" | 13 ++++ 8 files changed, 381 insertions(+), 87 deletions(-) create mode 100644 hw/platformio.ini create mode 100644 hw/src/main.cpp delete mode 100644 platformio.ini delete mode 100644 src/main.cpp create mode 100644 srv/c.c create mode 100644 srv/d.c create mode 100644 "\305\241tevec.service" diff --git a/.gitignore b/.gitignore index 03f4a3c..6fbe107 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ +a.out .pio +db +*.tsv +c +d diff --git a/hw/platformio.ini b/hw/platformio.ini new file mode 100644 index 0000000..5503719 --- /dev/null +++ b/hw/platformio.ini @@ -0,0 +1,12 @@ +; https://docs.platformio.org/page/projectconf.html + +[env:myboard] +platform = espressif8266 +board = nodemcuv2 +monitor_speed = 460800 +upload_speed = 460800 +build_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_PORT=Serial -DIP_FORWARD=1 -DMONITOR_SPEED=460800 +framework = arduino +board_build.filesystem = littlefs +board_build.ldscript = eagle.flash.4m2m.ld +src_filter = -<*> + diff --git a/hw/src/main.cpp b/hw/src/main.cpp new file mode 100644 index 0000000..5b0d7f9 --- /dev/null +++ b/hw/src/main.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +unsigned int packets = 0; +WiFiUDP u; +unsigned int doreport = 0; +uint64_t imp; +unsigned long last_imp = 0; +IRAM_ATTR void isr () { + unsigned long time = millis(); + unsigned long lumax = -1; + if (time < last_imp) { // debouncer + if (lumax - last_imp + time < (unsigned long) 100) // saj ne bo več kot 36 kW porabe + return; + } else + if (last_imp + (unsigned long) 100 > time) + return; + last_imp = time; + if (imp++ % 2) + digitalWrite(D8, HIGH); + else + digitalWrite(D8, LOW); +} +void setup () { + Serial.begin(MONITOR_SPEED); + pinMode(D5, INPUT); + pinMode(D6, OUTPUT); + pinMode(D7, OUTPUT); + pinMode(D8, OUTPUT); + EEPROM.begin(512); + EEPROM.get(0, imp); + WiFi.begin("OpenWrt", NULL); + IPAddress a(10,69,69,35); + IPAddress g(10,69,69,1); + IPAddress n(255,255,255,0); + IPAddress d1(10,69,69,1); + IPAddress d2(89,212,194,154); + WiFi.config(a, g, n, d1, d2); + u.begin(35358); + attachInterrupt(digitalPinToInterrupt(D5), isr, FALLING); +} +void loop () { + if (WiFi.status() == WL_CONNECTED) + digitalWrite(D7, LOW); + else + digitalWrite(D7, HIGH); + if (u.parsePacket()) { + packets++; + if (packets % 2) + digitalWrite(D6, HIGH); + else + digitalWrite(D6, LOW); + Serial.println(); + char p[512]; + u.read(p, 255); + if (!strncmp(p, "reset", 5)) { + imp = 0; + Serial.println("reset"); + } + } + uint64_t imp_stored; + EEPROM.get(0, imp_stored); + if (imp_stored != imp) { + EEPROM.put(0, imp); + EEPROM.commit(); + Serial.println(String(imp)); + IPAddress b(255,255,255,255); + u.beginPacket(b, 35358); + char buf[255]; + sprintf(buf, "%lld\n", imp); + u.write(buf); + u.endPacket(); + } +} diff --git a/platformio.ini b/platformio.ini deleted file mode 100644 index 5503719..0000000 --- a/platformio.ini +++ /dev/null @@ -1,12 +0,0 @@ -; https://docs.platformio.org/page/projectconf.html - -[env:myboard] -platform = espressif8266 -board = nodemcuv2 -monitor_speed = 460800 -upload_speed = 460800 -build_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_PORT=Serial -DIP_FORWARD=1 -DMONITOR_SPEED=460800 -framework = arduino -board_build.filesystem = littlefs -board_build.ldscript = eagle.flash.4m2m.ld -src_filter = -<*> + diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 5b0d7f9..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -unsigned int packets = 0; -WiFiUDP u; -unsigned int doreport = 0; -uint64_t imp; -unsigned long last_imp = 0; -IRAM_ATTR void isr () { - unsigned long time = millis(); - unsigned long lumax = -1; - if (time < last_imp) { // debouncer - if (lumax - last_imp + time < (unsigned long) 100) // saj ne bo več kot 36 kW porabe - return; - } else - if (last_imp + (unsigned long) 100 > time) - return; - last_imp = time; - if (imp++ % 2) - digitalWrite(D8, HIGH); - else - digitalWrite(D8, LOW); -} -void setup () { - Serial.begin(MONITOR_SPEED); - pinMode(D5, INPUT); - pinMode(D6, OUTPUT); - pinMode(D7, OUTPUT); - pinMode(D8, OUTPUT); - EEPROM.begin(512); - EEPROM.get(0, imp); - WiFi.begin("OpenWrt", NULL); - IPAddress a(10,69,69,35); - IPAddress g(10,69,69,1); - IPAddress n(255,255,255,0); - IPAddress d1(10,69,69,1); - IPAddress d2(89,212,194,154); - WiFi.config(a, g, n, d1, d2); - u.begin(35358); - attachInterrupt(digitalPinToInterrupt(D5), isr, FALLING); -} -void loop () { - if (WiFi.status() == WL_CONNECTED) - digitalWrite(D7, LOW); - else - digitalWrite(D7, HIGH); - if (u.parsePacket()) { - packets++; - if (packets % 2) - digitalWrite(D6, HIGH); - else - digitalWrite(D6, LOW); - Serial.println(); - char p[512]; - u.read(p, 255); - if (!strncmp(p, "reset", 5)) { - imp = 0; - Serial.println("reset"); - } - } - uint64_t imp_stored; - EEPROM.get(0, imp_stored); - if (imp_stored != imp) { - EEPROM.put(0, imp); - EEPROM.commit(); - Serial.println(String(imp)); - IPAddress b(255,255,255,255); - u.beginPacket(b, 35358); - char buf[255]; - sprintf(buf, "%lld\n", imp); - u.write(buf); - u.endPacket(); - } -} diff --git a/srv/c.c b/srv/c.c new file mode 100644 index 0000000..eadda7c --- /dev/null +++ b/srv/c.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define S0(x) (x ? x : "") +struct entry { + uint64_t time __attribute__((packed)); + uint64_t value __attribute__((packed)); +} __attribute__((packed)); +int najdi (struct entry * db, int first, int last, long long int čas) { + if (first == last) + return first; + if (abs(first-last) == 1) { + if (labs(čas/1000-first*1000) < labs(čas/1000-last*1000)) + return first; + else + return last; + } + if ((long long int) be64toh(db[(first+last)/2].time/1000) > čas*1000) + return najdi(db, (first+last)/2, last, čas); + else + return najdi(db, first, (first+last)/2-1, čas); +} +int main (int argc, char ** argv) { + int r = 0; + if (argc < 1+2) + error_at_line(1, 0, __FILE__, __LINE__, "uporaba: %s db informacije/preveri/rast/seštevek [natančnost=0] [začetek UNIXsek] [konec UNIXsek]\n\t" +"informacije: pove UNIXus prvega in UNIXus zadnjega zapisa\n\t" +"preveri: pove, če čas ni naraščajoč in kdaj se je števec resetiral\n\t" +"rast: TSV s podatki : najgosteje na [natančnost=0] ms\n\t" +"seštevek: TSV s podatki :", S0(argv[0])); + int fd = -1; + struct entry * db = NULL; + if ((fd = open(argv[1], O_RDONLY | O_CLOEXEC)) == -1) + error_at_line(2, 0, __FILE__, __LINE__, "open"); + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) { + error_at_line(0, errno, __FILE__, __LINE__, "fstat"); + r = 3; + goto r; + } + unsigned entries = statbuf.st_size / sizeof(struct entry); + if (!(db = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0))) { + error_at_line(0, errno, __FILE__, __LINE__, "mmap"); + r = 4; + goto r; + } + if (argv[2][0] == 'i') { + printf("začetek\t%" PRIu64 " UNIXus\t%" PRIu64 " Wh\nkonec\t%" PRIu64 " UNIXus\t%" PRIu64 " Wh\nzapisov: %u\n", be64toh(db[0].time), be64toh(db[0].value), be64toh(db[entries-1].time), be64toh(db[entries-1].value), entries); + goto r; + } + if (argv[2][0] == 'p') { + uint64_t prev_time = 0; + uint64_t prev_value = 0; + for (unsigned i = 0; i < entries; i++) { + if (be64toh(db[0].time) < prev_time) + printf("čas se je zavrtel nazaj: i=%u a=%" PRIu64 " b=%" PRIu64 "\n", i, prev_time, be64toh(db[0].time)); + if (be64toh(db[0].value) < prev_value) + printf("števec se je resetiral: i=%u a=%" PRIu64 " b=%" PRIu64 "\n", i, prev_value, be64toh(db[0].value)); + prev_time = be64toh(db[0].time); + prev_value = be64toh(db[0].value); + } + goto r; + } + unsigned long long natančnost = 0; + if (argv[2][0] == 's') + natančnost = 10e3; + if (argc >= 1+3) + natančnost = atoi(argv[3]); + unsigned začetek = 0; + if (argc >= 1+4) + začetek = najdi(db, 0, entries-1, atoi(argv[4])); + unsigned konec = entries-1; + if (argc >= 1+5) + konec = najdi(db, 0, entries-1, atoi(argv[5])); + if (argv[2][0] == 's') { + uint64_t us = 0; + uint64_t Wh = 0; + unsigned long long prev_time = be64toh(db[začetek].time); + unsigned long long prev_value = be64toh(db[začetek].value); + for (unsigned i = začetek; i <= konec; i++) { + if (us + (be64toh(db[i].time)-prev_time) >= natančnost*1000) { + printf("%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\n", be64toh(db[i].time)-us, Wh, be64toh(db[i].time), us); + us = 0; + Wh = 0; + } + us += (be64toh(db[i].time)-prev_time); + Wh += (be64toh(db[i].value)-prev_value); + prev_time = be64toh(db[i].time); + prev_value = be64toh(db[i].value); + } + } else { + unsigned long long prev_print = 0; + for (unsigned i = začetek; i <= konec; i++) { + if (be64toh(db[i].time)-prev_print > natančnost*1000) { + printf("%" PRIu64 "\t%" PRIu64 "\n", be64toh(db[i].time), be64toh(db[i].value)); + prev_print = be64toh(db[i].time); + } + } + } +r: + if (db != NULL) + if (munmap(db, statbuf.st_size) == -1) + error_at_line(98, errno, __FILE__, __LINE__, "munmap"); + if (fd == -1) + if (close(fd) == -1) + error_at_line(99, errno, __FILE__, __LINE__, "fclose"); + return r; +} diff --git a/srv/d.c b/srv/d.c new file mode 100644 index 0000000..3c9e482 --- /dev/null +++ b/srv/d.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define S0(x) (x ? x : "") +struct entry { + uint64_t time __attribute__((packed)); + uint64_t value __attribute__((packed)); +} __attribute__((packed)); +int samomor = 0; +void handle_me (int s __attribute__((unused))) { + samomor++; +} +int main (int argc, char ** argv) { + if (argc < 1+1) + error_at_line(1, 0, __FILE__, __LINE__, "uporaba: %s db [port=35358] [bind=::] [src=]", S0(argv[0])); + struct sigaction act = { + .sa_handler = handle_me, + .sa_flags = SA_RESTART + }; + if (sigaction(SIGINT, &act, NULL) == -1) + error_at_line(2, errno, __FILE__, __LINE__, "sigaction SIGINT"); + if (sigaction(SIGTERM, &act, NULL) == -1) + error_at_line(3, errno, __FILE__, __LINE__, "sigaction SIGTERM"); +#define DB argv[1] + int port = 35358; + if (argc >= 1+2) + port = atoi(argv[2]); + struct sockaddr_in6 bind_address = { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = in6addr_any + }; + if (argc >= 1+3) + switch (inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)) { + case 0: + error_at_line(4, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)"); + break; + case -1: + error_at_line(5, errno, __FILE__, __LINE__, "inet_pton"); + } + struct sockaddr_in6 src = { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = in6addr_any + }; + int whitelist_src = 0; + if (argc >= 1+4) { + whitelist_src++; + switch (inet_pton(AF_INET6, argv[4], &src.sin6_addr)) { + case 0: + error_at_line(6, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[4], &src.sin6_addr)"); + break; + case -1: + error_at_line(7, errno, __FILE__, __LINE__, "inet_pton"); + } + } + int udp = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (udp == -1) + error_at_line(10, errno, __FILE__, __LINE__, "socket"); + FILE * db = NULL; + int z = 1; + int r = 0; + if (setsockopt(udp, SOL_SOCKET, SO_BROADCAST, &z, sizeof z) == -1) { + error_at_line(0, errno, __FILE__, __LINE__, "setsockopt"); + r = 11; + goto r; + } + if (bind(udp, (struct sockaddr *) &bind_address, sizeof bind_address)) { + error_at_line(0, errno, __FILE__, __LINE__, "bind"); + r = 12; + goto r; + } + if (!(db = fopen(DB, "a"))) { + error_at_line(0, errno, __FILE__, __LINE__, "fopen"); + r = 13; + goto r; + } + while (!samomor) { + struct pollfd pollfd = { + .fd = udp, + .events = POLLIN | POLLERR | POLLHUP | POLLNVAL + }; + if (poll(&pollfd, 1, -1) == -1) { + error_at_line(0, errno, __FILE__, __LINE__, "poll"); + r = 14; + goto r; + } + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) || (pollfd.revents & POLLNVAL)) { + error_at_line(0, 0, __FILE__, __LINE__, "POLLERR || POLLHUP || POLLNVAL"); + r = 15; + goto r; + } + struct sockaddr_storage sender; + socklen_t sender_len = sizeof sender; + char buf[512]; + ssize_t bytes = recvfrom(udp, buf, 256, MSG_DONTWAIT, (struct sockaddr *) &sender, &sender_len); + if (bytes == -1) { + error_at_line(0, errno, __FILE__, __LINE__, "recvfrom"); + r = 16; + goto r; + } + struct timespec current_time; + if (clock_gettime(CLOCK_REALTIME, ¤t_time) == -1) { + error_at_line(0, errno, __FILE__, __LINE__, "clock_gettime"); + r = 17; + goto r; + } + uint64_t current_time_us = current_time.tv_sec*1000000+current_time.tv_nsec/1000; + unsigned long long int current_Wh = strtoull(buf, NULL, 10); + struct entry entry = { + .time = htobe64(current_time_us), + .value = htobe64(current_Wh) + }; + char sender_host[512]; + char sender_port[512]; + int gni = getnameinfo((struct sockaddr *) &sender, sender_len, sender_host, 512, sender_port, 512, NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV); + if (gni) { + error_at_line(0, 0, __FILE__, __LINE__, "getnameinfo: %s", gai_strerror(gni)); + r = 19; + goto r; + } + time_t sedaj = time(NULL); + strcpy(buf, ctime(&sedaj)); + buf[strlen(buf)-1] = '\0'; + if (sender.ss_family != AF_INET6) + if (whitelist_src) { + fprintf(stderr, "[%s] %s:%s\tsender.ss_family != AF_INET6\n", buf, sender_host, sender_port); + continue; + } + if (whitelist_src && memcmp(&((struct sockaddr_in6 *) &sender)->sin6_addr, &src.sin6_addr, sizeof(src.sin6_addr))) { + fprintf(stderr, "[%s] %s:%s\tfailed src whitelist\n", buf, sender_host, sender_port); + continue; + } + fprintf(stderr, "[%s] %s:%s\t%llu Wh\n", buf, sender_host, sender_port, current_Wh); + if (fwrite(&entry, sizeof entry, 1, db) != 1) { + error_at_line(0, 0, __FILE__, __LINE__, "fwrite"); + r = 18; + goto r; + } + } +r: + if (close(udp) == -1) + error_at_line(19, errno, __FILE__, __LINE__, "close(udp)"); + if (db != NULL) + if (fclose(db) == -1) + error_at_line(20, errno, __FILE__, __LINE__, "fclose(db)"); + return r; +} diff --git "a/\305\241tevec.service" "b/\305\241tevec.service" new file mode 100644 index 0000000..795db96 --- /dev/null +++ "b/\305\241tevec.service" @@ -0,0 +1,13 @@ +[Unit] +Description=listens to reports of 1000 imp/kWh power meter counter via UDP +After=network.target + +[Service] +Type=simple +DynamicUser=yes +RuntimeDirectory=števec +ExecStart=/usr/local/bin/števec +Restart=no + +[Install] +WantedBy=multi-user.target -- cgit v1.2.3