Compare commits

..

13 Commits

Author SHA1 Message Date
ms
b68a950062 Update modb_orno_tester.c
ocena szybkosci oparta tylko na poprawnych ramkach ignorowane ramki z blednym CRC lub timoutem
2026-02-17 14:01:59 +01:00
ms
7a03a794ba Add modb_orno_tester.c
program do testowania szybkości pobierania pomiarów tylko napięć z ORNO i SUN2K aby podjąć decyzję z którego urządzenia odczytywać napięcia
2026-02-17 13:15:46 +01:00
ms
fd8dce7535 Update modb_orno3.c
W trybie zwiększonej fluktuacji przyspieszamy wyłącznie odczyt napięc.
Poprawka wygenerowana przez qwen3_coder
2026-02-17 13:12:52 +01:00
ms
3078bbcf3f poprawka do odczytywania z orno 2026-02-15 23:08:33 +01:00
ms
7220d3e750 zmiana w modb_orno3.c i dodanie skryptu usuniecie_pola_highfluct.sh 2026-02-15 22:22:01 +01:00
ms
bc399e0044 Po upływie HIGH_FREQ_MODE_DURATION sekund od wykrycia fluktuacji napięcia:
- Wysyłany jest komunikat MQTT na topic /energy/orno/fluct z wartością "off"
  - Wysyłany jest zapis do InfluxDB z tagiem highfluct=end i wartością highfluct=0
  - Flaga jest resetowana, aby powiadomienie wysłało się tylko raz
2026-02-15 00:29:54 +01:00
ms
ae444a10fc Mapowanie rejestrów SUN2K do jednolitego formatu ORNO 2026-02-15 00:04:34 +01:00
ms
8f3756b1fe dodane logowanie wysyłanych danych do InfluxDB (przez --log)
wysylanie U_A, U_B, U_C do MQTT w formacie JSON podobnie jak dla innych pomiarów (np. Hz)
wysylanie U_A, U_B, U_C do InfluxDB w batchu razem z innymi pomiarami (nie osobno)
2026-02-14 23:08:04 +01:00
ms
0524e7284d Fix spike detection bug causing stuck values
When a spike was detected in voltage, current, power, energy, or frequency,
the code sent previous values but never updated the prev_* variables.
This caused the system to get stuck reporting old values indefinitely
after any significant measurement change.

Now all prev_* variables are always updated after spike detection,
ensuring values can change again after being filtered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 21:09:30 +01:00
ms
2b16211ef2 Fix sprintf overflows, integer sign handling, and clean up dead code
- Replace all sprintf with snprintf for buffer safety
- Fix rgI32/rgI16 sign extension using int32_t/int16_t casts
- Fix rgU32/rgU16 to use proper uint types and %u format
- Remove unused mbReg_Orno[] array and individual influx_send_* functions
- Fix MQTT topic for voltage fluctuation to match /energy/orno/ convention

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:38:45 +01:00
ms
44aceebae6 jakis commit 2026-02-14 20:20:51 +01:00
ms
912878d6e6 modyfikacje przez claude 2026-02-14 19:40:38 +01:00
ms
aa31532d8c Fix multiple bugs and harden modb_orno3.c
- Fix NULL pointer dereference: move modbus_set_slave after ctx validation (ORNO)
- Fix InfluxDB line protocol: remove stray comma in voltage measurement
- Move InfluxDB token to env variable INFLUX_TOKEN with fallback to default
- Fix spike detection for negative power values using fabs()
- Read and validate HTTP response from InfluxDB after writes
- Replace all sprintf with snprintf to prevent buffer overflows
- Batch ORNO InfluxDB writes into single TCP connection (5→1)
- Fix non-portable absolute include path for modbus-rtu.h

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 19:32:04 +01:00
6 changed files with 1293 additions and 102 deletions

View File

@@ -11,7 +11,12 @@
"Bash(1 echo \"Test wykonany. Analiza wyników:\" echo \"\" echo \"✓ POMYŚLNIE ODCZYTANE REJESTRY:\" grep \"OK.*elapsed\" /tmp/sun2k_test.log)",
"Read(//tmp/**)",
"Bash(for i in 1 2)",
"Bash(ls:*)"
"Bash(ls:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(git -c http.sslVerify=false push)",
"Bash(git config:*)"
]
}
}

464
kimi_analyse.md Normal file
View File

@@ -0,0 +1,464 @@
╭─── Claude Code v2.1.42 ────────────────────────────────────────────────────────────────────────────────╮
│ │ Tips for getting started │
│ Welcome back! │ Run /init to create a CLAUDE.md file with instructions for … │
│ │ ──────────────────────────────────────────────────────────── │
│ │ Recent activity │
│ ▐▛███▜▌ │ No recent activity │
│ ▝▜█████▛▘ │ │
│ ▘▘ ▝▝ │ │
│ kimi-k2.5:cloud · API Usage Billing │ │
│ ~/projekty/modborno3 │ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯
/model to try Opus 4.6
analyse modb_orno3.c
● Read 1 file (ctrl+o to expand)
● Analysis of modb_orno3.c
Overview
This is a C program that reads energy data from two Modbus RTU devices:
- ORNO - 3-phase energy meter (voltage, current, power, energy, frequency)
- SUN2000 - Huawei solar inverter
Data is published to MQTT (using Eclipse Mosquitto) and InfluxDB v2.
---
Architecture
Modbus Communication
- Uses libmodbus for RTU serial communication via /dev/ttyUSB0
- ORNO: slave address 2, even parity ('E'), RTS delay 5ms
- SUN2000: slave address 3, no parity ('N'), RTS delay 5ms
- Timing parameters configurable via #define directives (lines 41-50)
Data Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ ORNO │ │ SUN2000 │ │ Output │
│ (meter) │ │ (inverter) │ │ │
└──────┬──────┘ └──────┬──────┘ ├─> MQTT │
│ │ ├─> InfluxDB │
└───────┬───────────┘ │ (HTTP) │
│ └─────────────┘
modbus_read_registers()
---
Key Features
1. Voltage Fluctuation Detection (lines 546-572)
- Circular buffer of 12 samples tracks voltage history
- Detects fluctuations >5% from average
- Triggers high-frequency mode (300s duration, 10ms polling)
2. Spike Detection (lines 591-649)
- Validates readings against previous values
- Rejects changes >80% (factor of 1.8x) for voltage, current, power
- Rejects energy decreases (line 630)
- Frequency validated within ±5%
3. Range Validation (lines 360-375)
- Checks for NaN/Inf values
- Validates against min/max thresholds (e.g., voltage 150-280V)
---
Register Mappings
ORNO registers (lines 128-140):
- Voltages: 0x0E, 0x10, 0x12 (L1, L2, L3)
- Currents: 0x16 (start)
- Power: 0x1C (total + per-phase)
- Energy: 0x100 (total + per-phase)
- Frequency: 0x14
SUN2000 registers (lines 105-126):
- Startup/shutdown times, device status, alarms
- PV input power, phase voltages, peak/active/reactive power
- Grid frequency, temperature, energy yields
---
Security Concerns
Critical: Line 65 contains a hardcoded InfluxDB token:
#define INFLUX_TOKEN
"BCIZ6kcCAVgpcwSfU0PBS7m0Zb6an93kuOtizbEtlXub-uaoYp4dmIQBQYaJCv8_KE4QYYZ08jxtpaZ3TUWP-Q=="
Other issues:
- Hardcoded MQTT broker (10.1.1.1:1883) - no TLS
- InfluxDB connection uses plaintext HTTP (port 5086)
- No authentication on MQTT (no username/password)
---
Code Quality Observations
Strengths:
- Good error handling for Modbus operations
- Timing instrumentation (modbus_read_timed())
- Previous-value fallback on read failures
- Configurable via compile-time defines
Weaknesses:
- Uses sprintf() without bounds checking (potential buffer overflow)
- No free(buf) in mosq_test() (line 408) - memory leak
- Mixed Polish/English in comments
- mosq_test() declared to return int but has no return statement
- main() uses do-while with exit(1) on errors - connection leaks on ORNO failure
---
Potential Bugs
1. Line 630 - Energy spike detection logic:
if (prev_energy.W_Tot != 0 && (current_energy.W_Tot > prev_energy.W_Tot * 1.8 || current_energy.W_Tot <
prev_energy.W_Tot))
1. This triggers on ANY decrease in energy (not just spikes), which may be intentional for a
non-decreasing counter.
2. Line 565-567 - Division by zero risk if voltage_buffer_items is 0 (protected by check on line 551,
but edge case exists).
3. Line 797 - asctime() returns pointer to static buffer - not thread-safe.
4. Line 303 - strlen(data) with potential unterminated string if influx_send_* functions receive bad
input.
---
Recommendations
1. Move credentials to environment variables or config file
2. Add TLS support for MQTT and InfluxDB
3. Replace sprintf() with snprintf()
4. Add signal handling for clean shutdown
5. Consider using modbus_read_registers() batch reads for SUN2000 to reduce round-trips
✻ Sautéed for 55s
✻ Sautéed for 55s
/model
⎿ Set model to opus (kimi-k2.5:cloud)
analyse modb_orno3.c
● I already have the file content from our previous conversation. Here's a comprehensive analysis:
Code Analysis: modb_orno3.c
Purpose
A Modbus RTU data collector that reads energy measurements from two devices:
- ORNO (3-phase electricity meter) - voltages, currents, power, energy, frequency
- SUN2000 (Huawei solar inverter) - status, alarms, PV power, temperatures, yields
Publishes data to MQTT (Eclipse Mosquitto) and InfluxDB v2.
---
Architecture
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ ORNO │ │ Program │ │ MQTT │
│ (addr 2) │─────>│ modb_orno3 │─────>│ Broker │
│ 9600,E,8,1 │ │ │ │ 10.1.1.1 │
└─────────────┘ │ │ └─────────────┘
│ │
┌─────────────┐ │ │ ┌─────────────┐
│ SUN2000 │ │ │ │ InfluxDB │
│ (addr 3) │─────>│ │─────>│ 10.1.1.1 │
│ 9600,N,8,1 │ └──────────────┘ │ :5086 │
└─────────────┘ └─────────────┘
---
Key Features
┌─────────────────────┬────────────────────────────────────────────────────────────────────────┐
│ Feature │ Implementation │
├─────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Spike Detection │ Rejects values changing >80% (1.8x factor) for U/I/P │
├─────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Voltage Fluctuation │ Circular buffer (12 samples), triggers high-freq mode on >5% deviation │
├─────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Data Validation │ NaN/Inf checks, min/max range validation │
├─────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Retry Logic │ Up to 10 retries per SUN2000 register │
├─────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ Timed Reads │ Logs Modbus operation duration │
└─────────────────────┴────────────────────────────────────────────────────────────────────────┘
---
Critical Security Issues
1. Hardcoded InfluxDB Token (line 65)
#define INFLUX_TOKEN
"BCIZ6kcCAVgpcwSfU0PBS7m0Zb6an93kuOtizbEtlXub-uaoYp4dmIQBQYaJCv8_KE4QYYZ08jxtpaZ3TUWP-Q=="
- Risk: Credential exposure in source control
- Fix: Move to environment variable or config file
2. Unencrypted Communications
- MQTT: Plaintext on port 1883 (no TLS)
- InfluxDB: Plaintext HTTP on port 5086
- No MQTT authentication (no username/password)
3. Hardcoded IP Addresses
#define MQTT_BROKER "10.1.1.1"
#define INFLUX_HOST "10.1.1.1"
---
Code Quality Issues
┌──────────────────────┬───────────────┬──────────────────────────────────────────────┐
│ Issue │ Line │ Problem │
├──────────────────────┼───────────────┼──────────────────────────────────────────────┤
│ Memory leak │ 398-407 │ mosq_test() allocates buf but never frees it │
├──────────────────────┼───────────────┼──────────────────────────────────────────────┤
│ Missing return │ 393-407 │ mosq_test() declared int but has no return │
├──────────────────────┼───────────────┼──────────────────────────────────────────────┤
│ Buffer overflow risk │ 226, 234, etc │ sprintf() without bounds checking │
├──────────────────────┼───────────────┼──────────────────────────────────────────────┤
│ Mixed languages │ 25, 44, etc │ Polish/English comments mixed │
├──────────────────────┼───────────────┼──────────────────────────────────────────────┤
│ Dead code │ 903 │ //mosq_test() commented out │
└──────────────────────┴───────────────┴──────────────────────────────────────────────┘
---
Potential Bugs
1. Energy Spike Detection Logic (line 630)
if (prev_energy.W_Tot != 0 && (current_energy.W_Tot > prev_energy.W_Tot * 1.8 || current_energy.W_Tot <
prev_energy.W_Tot))
This rejects ANY decrease in energy (not just spikes), which may reject valid data if the counter rolls
over or resets.
2. Frequency Threshold Inconsistency
- Line 569: ±5% threshold for spike detection
- But line 641 uses 1.05/0.95 (also 5%)
- Line 688 validates 47-53 Hz (±6% from 50 Hz)
3. InfluxDB Line Protocol Syntax Issues
Line 323 (missing comma between tags and fields):
"orno,device=orno,pomiar=voltage, L1=%.2f,L2=%.2f,L3=%.2f"
Should be:
"orno,device=orno,pomiar=voltage L1=%.2f,L2=%.2f,L3=%.2f"
4. asctime() Buffer Issue (line 797)
sprintf(str_buf, "\"%s\"", asctime(timeinfo));
asctime() returns pointer to static buffer - contains newline which is then manipulated (line 798-799).
Not thread-safe.
5. Inconsistent Scaling in ORNO Registers
Line 132: L3 Volt has scale: 10 while L1/L2 have scale: 1 - potential copy-paste error?
---
Configuration (lines 26-66)
#define READ_LOOP true // true = continuous 5s polling
#define VOLTAGE_FLUCTUATION_THRESHOLD 0.05 // 5%
#define HIGH_FREQ_MODE_DURATION 300 // 300 seconds
#define ORNO_RTS_DELAY 5000 // 5ms
#define SUN2K_RTS_DELAY 5000 // 5ms
#define DELAY_BETWEEN_DEVICES 10000 // 10ms between devices
---
Modbus Register Maps
ORNO (mbReg_Orno[]) - Uses function code 3 (holding registers):
┌───────────┬─────────┬──────┬─────────────────┐
│ Register │ Address │ Type │ Scale │
├───────────┼─────────┼──────┼─────────────────┤
│ L1 Volt │ 0x000E │ U32 │ 1 │
├───────────┼─────────┼──────┼─────────────────┤
│ L2 Volt │ 0x0010 │ U32 │ 1 │
├───────────┼─────────┼──────┼─────────────────┤
│ L3 Volt │ 0x0012 │ U32 │ 10 (suspicious) │
├───────────┼─────────┼──────┼─────────────────┤
│ Tot Power │ 0x001C │ U32 │ 1 │
├───────────┼─────────┼──────┼─────────────────┤
│ ... │ ... │ ... │ ... │
└───────────┴─────────┴──────┴─────────────────┘
SUN2000 (mbReg[]) - Various data types:
- rgData - timestamps (lines 107-109)
- rgI32 - signed 32-bit (power values)
- rgU32 - unsigned 32-bit (energy counters)
- rgI16 - signed 16-bit (temperature)
- rgU16 - unsigned 16-bit (status codes)
---
Recommendations
1. Security: Move credentials to environment variables
2. Add TLS: Configure MQTT and InfluxDB with TLS certificates
3. Fix InfluxDB format: Remove comma between tags/fields in line protocol
4. Buffer safety: Replace sprintf() with snprintf()
5. Memory: Fix leak in mosq_test() or remove unused function
6. Scaling: Verify L3 Volt scale (10 vs 1) for ORNO
7. Energy logic: Consider rollover handling for energy counters
8. Logging: Add structured logging instead of printf
---
Summary
This is functional embedded systems code for energy monitoring with reasonable error handling and data
validation. The main concerns are security (hardcoded credentials) and potential data quality issues
(InfluxDB line protocol format, scaling inconsistencies). The spike detection and voltage fluctuation
features show thoughtful defensive programming.
✻ Sautéed for 52s
--------------------------------------------------------
/model
⎿ Set model to opus (claude-opus-4-6)
analyse modb_orno3.c
⎿ Not logged in · Please run /login
run /login
⎿ Not logged in · Please run /login
run /login
⎿ Not logged in · Please run /login
/login
⎿ Login successful
analyse modb_orno3.c
● Searched for 1 pattern, read 1 file (ctrl+o to expand)
● Analiza modb_orno3.c
Opis ogólny
Program w C do odczytu danych z dwóch urządzeń Modbus RTU przez port szeregowy:
- ORNO (slave 2) — trójfazowy licznik energii
- SUN2000 (slave 3) — falownik Huawei SUN2000
Odczytane dane są wysyłane do MQTT (broker 10.1.1.1:1883) i InfluxDB v2 (10.1.1.1:5086).
---
Architektura
┌──────────────┬──────────────────────────────────────────────────────┐
│ Warstwa │ Opis │
├──────────────┼──────────────────────────────────────────────────────┤
│ Modbus RTU │ libmodbus, port /dev/ttyUSB0, 9600 baud │
├──────────────┼──────────────────────────────────────────────────────┤
│ MQTT │ libmosquitto, topic /energy/orno/* i /energy/SUN2K │
├──────────────┼──────────────────────────────────────────────────────┤
│ InfluxDB │ Surowe HTTP POST na /api/v2/write, line protocol │
├──────────────┼──────────────────────────────────────────────────────┤
│ Pętla główna │ do/while co 5s (normalnie) lub 10ms (tryb high-freq) │
└──────────────┴──────────────────────────────────────────────────────┘
---
Znalezione problemy i uwagi
1. Wyciek tokenu InfluxDB (linia 65)
Token jest zahardkodowany w kodzie źródłowym. Powinien być w zmiennej środowiskowej lub pliku
konfiguracyjnym.
2. Bug: modbus_set_slave przed sprawdzeniem ctx (linia 459-460)
modbus_set_slave(ctx, ORNO_SLAVE); // linia 459 - ctx może być NULL
if (!ctx) { ... } // linia 460 - sprawdzenie za późno
modbus_set_slave zostanie wywołane na NULL pointer zanim nastąpi walidacja. Crash gwarantowany jeśli
modbus_new_rtu zawiedzie.
3. Bug w InfluxDB line protocol - nadmiarowy przecinek (linia 323)
"orno,device=orno,pomiar=voltage, L1=%.2f,L2=%.2f,L3=%.2f"
// ^ spacja+przecinek zamiast spacji
Powinno być pomiar=voltage L1=... (spacja oddziela tagi od pól). Dodatkowy przecinek po voltage powoduje
błąd parsowania w InfluxDB — dane napięcia prawdopodobnie nie są zapisywane.
4. Tryb high-frequency polling — 10ms bez throttlingu (linia 897)
Przy wykryciu fluktuacji napięcia program przełącza się na usleep(10000) (10ms) na 300 sekund. W tym
trybie:
- Program robi ~100 odczytów/s po Modbus RTU (9600 baud to ~1 odczyt/s realnie)
- Każdy odczyt otwiera nowe połączenie TCP do InfluxDB → tysiące krótkich połączeń
- Timeout Modbus to 0.9s — więc realna częstotliwość i tak będzie ~1/s, ale logika jest myląca
5. Brak modbus_set_slave po stronie SUN2K idzie przed ctx check (linia 720 vs 714)
Tutaj jest OK — modbus_set_slave jest po sprawdzeniu ctx. Ale dla ORNO (pkt 2) jest bug.
6. Połączenie TCP do InfluxDB tworzone na każdy pomiar (linia 264-318)
Każde wywołanie influx_send_post otwiera nowy socket, wysyła dane i zamyka. W normalnym trybie to ~6
połączeń co 5s (OK), ale w trybie high-freq to setki na minutę. Lepiej: batch writes lub persistent
connection.
7. Brak odczytu odpowiedzi z InfluxDB (linia 317)
Po wysłaniu danych socket jest zamykany bez czytania odpowiedzi HTTP. Nie wiadomo czy zapis się powiódł
(np. 401 Unauthorized, 400 Bad Request).
8. sprintf bez ograniczenia bufora (linie 227, 235, 243, 251, 259, 401)
Używane sprintf zamiast snprintf — potencjalny buffer overflow przy nieoczekiwanych danych float (np.
bardzo duże wartości).
9. Spike detection nie działa dla ujemnych mocy (linia 619)
current_power.P_Tot > prev_power.P_Tot * 1.8 || current_power.P_Tot < prev_power.P_Tot * 0.2
Jeśli moc jest ujemna (oddawanie do sieci), mnożenie przez 1.8 daje bardziej ujemną wartość, a 0.2 mniej
ujemną — logika się odwraca.
10. Energy spike — asymetryczna walidacja (linia 630)
current_energy.W_Tot > prev_energy.W_Tot * 1.8 || current_energy.W_Tot < prev_energy.W_Tot
Górny próg to 180%, ale dolny to dokładnie prev (jakikolwiek spadek = spike). Energia powinna być
monotoniczne rosnąca, więc logika dolnego progu jest OK, ale wartość 1.8 dla górnego może być za wysoka.
11. mosq_test() — memory leak (linia 398)
malloc(64) bez free. Drobnostka bo funkcja nie jest używana (zakomentowana w main).
12. Różne parametry seryjne ORNO vs SUN2K
- ORNO: 9600, Even parity, 8, 1 (linia 457)
- SUN2K: 9600, No parity, 8, 1 (linia 713)
To jest poprawne (Huawei SUN2000 domyślnie używa N-8-1, ORNO domyślnie E-8-1), ale warto mieć świadomość
że oba urządzenia mają różne konfiguracje.
13. Niestandardowy include (linia 4)
#include </usr/include/modbus/modbus-rtu.h>
Powinno być #include <modbus/modbus-rtu.h> — absolutna ścieżka uniemożliwia przenośność.
---
Podsumowanie priorytetów napraw
┌───────────┬────────────────────────────────────────────────────┐
│ Priorytet │ Problem │
├───────────┼────────────────────────────────────────────────────┤
│ Krytyczny │ Bug NULL pointer dereference ORNO (pkt 2) │
├───────────┼────────────────────────────────────────────────────┤
│ Krytyczny │ InfluxDB line protocol — zły format napięć (pkt 3) │
├───────────┼────────────────────────────────────────────────────┤
│ Wysoki │ Token InfluxDB w kodzie (pkt 1) │
├───────────┼────────────────────────────────────────────────────┤
│ Wysoki │ Spike detection dla ujemnych mocy (pkt 9) │
├───────────┼────────────────────────────────────────────────────┤
│ Średni │ Brak odczytu odpowiedzi InfluxDB (pkt 7) │
├───────────┼────────────────────────────────────────────────────┤
│ Średni │ sprintf → snprintf (pkt 8) │
├───────────┼────────────────────────────────────────────────────┤
│ Niski │ Batching InfluxDB writes (pkt 6) │
├───────────┼────────────────────────────────────────────────────┤
│ Niski │ Niestandardowy include path (pkt 13) │
└───────────┴────────────────────────────────────────────────────┘
Chcesz żebym naprawił którekolwiek z tych problemów?
✻ Cooked for 52s
──────────────────────────────────────────────────────────────────────────────────────────────────────────
napraw krytyczne problemy (pkt 2 i 3
──────────────────────────────────────────────────────────────────────────────────────────────────────
? for shortcuts

68
kimi_analyse2.md Normal file
View File

@@ -0,0 +1,68 @@
frequency of reports of orno measuring is very rare , value not changes. try to analyse this problem
● I'll analyze the code to understand the ORNO measuring report frequency issue. Let me first read the relevant file.
● Read 1 file (ctrl+o to expand)
● I analyzed the code and found two critical bugs that explain the rare reports and stuck values:
Problem 1: Spike Detection Logic Bug (Causes "Values Not Changing")
Lines 618-624, 632-638, 644-650, 656-662, and 668-674 have a logic error. When a spike is detected, the code sends previous
values but never updates the stored previous value. This means once a spike is detected, the system reports old values forever.
Example from voltage check:
if(spike_U) {
send_voltage = prev_voltage; // Send old value
printf("ORNO: Voltage spike detected. Sending previous values.\n");
// BUG: prev_voltage is NOT updated here!
} else {
send_voltage = current_voltage;
prev_voltage = current_voltage; // Only updated when NO spike
}
Impact: If your voltage/current/power changes by more than 80% (or frequency by 5%), the system will get stuck reporting the
old values indefinitely.
Problem 2: SUN2K Retry Loop (Causes "Rare Reports")
Lines 796-799 show SUN2K has a retry loop with up to 10 attempts per register:
do {
num = modbus_read_timed(ctx, mbReg[i].ireg, mbReg[i].num_reg, reg);
} while ((num != mbReg[i].num_reg) && (proba++ < 10));
With a 0.9s timeout (line 766) and ~19 registers to read, if SUN2K is unresponsive this can delay each loop iteration by up to
~3 minutes (19 registers × 10 retries × 0.9s = 171s).
Since ORNO is read before SUN2K in the same loop, ORNO reports become rare when SUN2K has communication issues.
Recommendations
1. Fix spike detection - Update prev_* values even when spikes are detected, or the values will never update after any
significant change:
// Current buggy code:
if(spike_U) {
send_voltage = prev_voltage;
} else {
send_voltage = current_voltage;
prev_voltage = current_voltage; // Missing in spike branch!
}
// Fixed version:
if(spike_U) {
printf("ORNO: Voltage spike detected. Using previous values.\n");
}
send_voltage = current_voltage; // Always use current
prev_voltage = current_voltage; // Always update prev
2. Reduce SUN2K impact - Either:
- Reduce proba limit from 10 to 2-3 attempts
- Reduce response timeout from 900ms to 300ms
- Run SUN2K in a separate thread or process
- Disable SUN2K if not needed (#define READ_SUN2K false)
3. Add debugging - The code already has good logging. Check if you see "spike detected" messages or SUN2K timeout errors in
your logs.
The combination of these bugs explains why values appear stuck (spike detection) and reports are infrequent (SUN2K delays).
✻ Sautéed for 3m 13s

View File

@@ -1,7 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include </usr/include/modbus/modbus-rtu.h>
#include <modbus/modbus-rtu.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
@@ -12,6 +13,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
// sterowanie logiem wysyłanych danych do InfluxDB (przez --log)
int log_influx_data = 0; /* Set to 1 when --log parameter is provided */
/* ============================================ */
/* STRUCTS FOR MEASUREMENTS */
/* ============================================ */
@@ -62,7 +66,7 @@ typedef struct { float Freq; } s_frequency;
#define INFLUX_PORT 5086
#define INFLUX_ORG "sic" /* Zmień na swoją organizację */
#define INFLUX_BUCKET "energydb" /* Zmień na swój bucket */
#define INFLUX_TOKEN "BCIZ6kcCAVgpcwSfU0PBS7m0Zb6an93kuOtizbEtlXub-uaoYp4dmIQBQYaJCv8_KE4QYYZ08jxtpaZ3TUWP-Q==" /* Zmień na swój token */
#define INFLUX_TOKEN_DEFAULT "BCIZ6kcCAVgpcwSfU0PBS7m0Zb6an93kuOtizbEtlXub-uaoYp4dmIQBQYaJCv8_KE4QYYZ08jxtpaZ3TUWP-Q==" /* Fallback - preferuj zmienną INFLUX_TOKEN */
/* ============================================ */
/*
@@ -125,19 +129,6 @@ t_mb_reg mbReg[] = {
};
t_mb_reg mbReg_Orno[] = {
{"L1 Volt", 0x000e, 2, "L1 Volt", 1, rgU32, "V", "info", "hold", ""},
{"L2 Volt", 0x0010, 2, "L2 Volt", 1, rgU32, "V", "info", "hold", ""},
{"L3 Volt", 0x0012, 2, "L3 Volt", 10, rgU32, "V", "info", "hold", ""},
{"Tot Power", 0x001c, 2, "Tot Power", 1, rgU32, "kW", "info", "hold", ""},
{"L1 Tot Power", 0x001e, 2, "L1 Tot Power", 1, rgU32, "kW", "info", "hold", ""},
{"L2 Tot Power", 0x0020, 2, "L2 Tot Power", 1, rgU32, "kW", "info", "hold", ""},
{"L3 Tot Power", 0x0022, 2, "L3 Tot Power", 1, rgU32, "kW", "info", "hold", ""},
{"Tot Energ", 0x0100, 2, "Tot Energ", 1, rgU32, "kWh", "info", "hold", ""},
{"L1 Tot Energ", 0x0102, 2, "L1 Tot Energ", 1, rgU32, "kWh", "info", "hold", ""},
{"L2 Tot Energ", 0x0104, 2, "L2 Tot Energ", 1, rgU32, "kWh", "info", "hold", ""},
{"L3 Tot Energ", 0x0106, 2, "L3 Tot Energ", 1, rgU32, "kWh", "info", "hold", ""}};
void mosq_log_callback(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
@@ -156,6 +147,7 @@ void mosq_log_callback(struct mosquitto *mosq, void *userdata, int level, const
}
}
const char *influx_token = NULL;
struct mosquitto *mosq = NULL;
void mqtt_setup()
{
@@ -224,7 +216,7 @@ int mqtt_send_U(float U1, float U2, float U3)
{
if (DISABLE_MQTT) return 0;
char buf[128];
sprintf(buf, "{\"U1\":%f,\"U2\":%f,\"U3\":%f}", U1, U2, U3);
snprintf(buf, sizeof(buf), "{\"U1\":%f,\"U2\":%f,\"U3\":%f}", U1, U2, U3);
return mosquitto_publish(mosq, NULL, "/energy/orno/U", strlen(buf), buf, 0, 0);
}
@@ -232,7 +224,7 @@ int mqtt_send_P(float P_Tot, float P1, float P2, float P3)
{
if (DISABLE_MQTT) return 0;
char buf[128];
sprintf(buf, "{\"P_Tot\":%f,\"P1\":%f,\"P2\":%f,\"P3\":%f}", P_Tot, P1, P2, P3);
snprintf(buf, sizeof(buf), "{\"P_Tot\":%f,\"P1\":%f,\"P2\":%f,\"P3\":%f}", P_Tot, P1, P2, P3);
return mosquitto_publish(mosq, NULL, "/energy/orno/P", strlen(buf), buf, 0, 0);
}
@@ -240,7 +232,7 @@ int mqtt_send_W(float W_Tot, float W1, float W2, float W3)
{
if (DISABLE_MQTT) return 0;
char buf[128];
sprintf(buf, "{\"W_Tot\":%f,\"W1\":%f,\"W2\":%f,\"W3\":%f}", W_Tot, W1, W2, W3);
snprintf(buf, sizeof(buf), "{\"W_Tot\":%f,\"W1\":%f,\"W2\":%f,\"W3\":%f}", W_Tot, W1, W2, W3);
return mosquitto_publish(mosq, NULL, "/energy/orno/W", strlen(buf), buf, 0, 0);
}
@@ -248,7 +240,7 @@ int mqtt_send_I(float I1, float I2, float I3)
{
if (DISABLE_MQTT) return 0;
char buf[128];
sprintf(buf, "{\"I1\":%f,\"I2\":%f,\"I3\":%f}", I1, I2, I3);
snprintf(buf, sizeof(buf), "{\"I1\":%f,\"I2\":%f,\"I3\":%f}", I1, I2, I3);
return mosquitto_publish(mosq, NULL, "/energy/orno/I", strlen(buf), buf, 0, 0);
}
@@ -256,14 +248,26 @@ int mqtt_send_Hz(float Hz)
{
if (DISABLE_MQTT) return 0;
char buf[64];
sprintf(buf, "%f", Hz);
snprintf(buf, sizeof(buf), "%f", Hz);
return mosquitto_publish(mosq, NULL, "/energy/orno/Hz", strlen(buf), buf, 0, 0);
}
int mqtt_send_SUN2K_U(float U_A, float U_B, float U_C)
{
if (DISABLE_MQTT) return 0;
char buf[128];
snprintf(buf, sizeof(buf), "{\"U_A\":%f,\"U_B\":%f,\"U_C\":%f}", U_A, U_B, U_C);
return mosquitto_publish(mosq, NULL, "/energy/sun2k/U", strlen(buf), buf, 0, 0);
}
/* InfluxDB Functions */
int influx_send_post(char *data)
{
if (DISABLE_INFLUX) return 0;
/* Log data to screen if --log parameter was provided */
if (log_influx_data) {
printf("INFLUX: Sending data: %s\n", data);
}
int sock;
struct sockaddr_in server;
@@ -300,7 +304,7 @@ int influx_send_post(char *data)
"Content-Length: %ld\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"Connection: close\r\n\r\n",
INFLUX_ORG, INFLUX_BUCKET, INFLUX_HOST, INFLUX_PORT, INFLUX_TOKEN, strlen(data));
INFLUX_ORG, INFLUX_BUCKET, INFLUX_HOST, INFLUX_PORT, influx_token, strlen(data));
if (send(sock, header, strlen(header), 0) < 0) {
perror("INFLUX: Send header to InfluxDB failed");
@@ -314,48 +318,174 @@ int influx_send_post(char *data)
return -1;
}
/* Read HTTP response to check for errors */
char resp[512];
int resp_len = recv(sock, resp, sizeof(resp) - 1, 0);
if (resp_len > 0) {
resp[resp_len] = '\0';
/* Check HTTP status code (e.g. "HTTP/1.1 204") */
int http_status = 0;
if (sscanf(resp, "HTTP/%*d.%*d %d", &http_status) == 1) {
if (http_status < 200 || http_status >= 300) {
printf("INFLUX: ERROR: HTTP %d response from InfluxDB\n", http_status);
close(sock);
return -1;
}
}
} else if (resp_len < 0) {
printf("INFLUX: WARNING: No response from InfluxDB (timeout)\n");
}
close(sock);
return 0;
}
int influx_send_U(float U1, float U2, float U3) {
char line[256];
snprintf(line, sizeof(line), "orno,device=orno,pomiar=voltage, L1=%.2f,L2=%.2f,L3=%.2f", U1, U2, U3);
return influx_send_post(line);
int influx_send_orno_batch(s_voltage *v, int v_ok, s_current *i, int i_ok,
s_power *p, int p_ok, s_energy *w, int w_ok,
s_frequency *f, int f_ok) {
if (DISABLE_INFLUX) return 0;
char batch[1024];
int offset = 0;
/* New format: group by phase with U, I, P, W fields */
/* For each phase, build field list based on available data */
// Phase L1
if (v_ok || i_ok || p_ok || w_ok) {
char fields[256] = "";
int foff = 0;
if (v_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "U=%.2f,", v->U1);
if (i_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "I=%.2f,", i->I1);
if (p_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "P=%.2f,", p->P1);
if (w_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "W=%.2f,", w->W1);
if (foff > 0) fields[foff - 1] = '\0'; // remove trailing comma
offset += snprintf(batch + offset, sizeof(batch) - offset,
"energydb,device=orno,phase=L1 %s\n", fields);
}
int influx_send_I(float I1, float I2, float I3) {
char line[256];
snprintf(line, sizeof(line), "orno,device=orno,pomiar=current L1=%.2f,L2=%.2f,L3=%.2f", I1, I2, I3);
return influx_send_post(line);
// Phase L2
if (v_ok || i_ok || p_ok || w_ok) {
char fields[256] = "";
int foff = 0;
if (v_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "U=%.2f,", v->U2);
if (i_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "I=%.2f,", i->I2);
if (p_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "P=%.2f,", p->P2);
if (w_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "W=%.2f,", w->W2);
if (foff > 0) fields[foff - 1] = '\0';
offset += snprintf(batch + offset, sizeof(batch) - offset,
"energydb,device=orno,phase=L2 %s\n", fields);
}
int influx_send_P(float P_Tot, float P1, float P2, float P3) {
char line[256];
snprintf(line, sizeof(line), "orno,device=orno,pomiar=power total=%.3f,L1=%.3f,L2=%.3f,L3=%.3f", P_Tot, P1, P2, P3);
return influx_send_post(line);
// Phase L3
if (v_ok || i_ok || p_ok || w_ok) {
char fields[256] = "";
int foff = 0;
if (v_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "U=%.2f,", v->U3);
if (i_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "I=%.2f,", i->I3);
if (p_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "P=%.2f,", p->P3);
if (w_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "W=%.2f,", w->W3);
if (foff > 0) fields[foff - 1] = '\0';
offset += snprintf(batch + offset, sizeof(batch) - offset,
"energydb,device=orno,phase=L3 %s\n", fields);
}
int influx_send_W(float W_Tot, float W1, float W2, float W3) {
char line[256];
snprintf(line, sizeof(line), "orno,device=orno,pomiar=energy total=%.3f,L1=%.3f,L2=%.3f,L3=%.3f", W_Tot, W1, W2, W3);
return influx_send_post(line);
// Phase total
if (p_ok || w_ok) {
char fields[256] = "";
int foff = 0;
if (p_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "P=%.2f,", p->P_Tot);
if (w_ok) foff += snprintf(fields + foff, sizeof(fields) - foff, "W=%.2f,", w->W_Tot);
if (foff > 0) fields[foff - 1] = '\0';
offset += snprintf(batch + offset, sizeof(batch) - offset,
"energydb,device=orno,phase=total %s\n", fields);
}
int influx_send_Hz(float Hz) {
char line[256];
snprintf(line, sizeof(line), "orno,device=orno,pomiar=frequency value=%.4f", Hz);
return influx_send_post(line);
if (f_ok) {
offset += snprintf(batch + offset, sizeof(batch) - offset,
"energydb,device=orno,frequency=Hz value=%.4f\n", f->Freq);
}
if (offset == 0) return 0;
/* Remove trailing newline */
if (offset > 0 && batch[offset - 1] == '\n') batch[offset - 1] = '\0';
return influx_send_post(batch);
}
int influx_send_SUN2K(t_mb_reg *reg_to_send, char *_str_buf) {
if (!_str_buf || strlen(_str_buf) == 0) return 0;
char line[256];
/* reg_id used as tag, value as field */
snprintf(line, sizeof(line), "sun2k,device=sun2k,pomiar=%s value=%s", reg_to_send->reg_id, _str_buf);
char pomiar_tag[32] = "";
char field_name[32] = "";
/* Mapowanie rejestrów SUN2K do jednolitego formatu ORNO */
if (strcmp(reg_to_send->reg_id, "PV_P") == 0 ||
strcmp(reg_to_send->reg_id, "P_active") == 0 ||
strcmp(reg_to_send->reg_id, "P_peak") == 0) {
/* Moc - mapujemy do pomiar=power, pole=total */
strcpy(pomiar_tag, ",pomiar=power");
strcpy(field_name, "total");
}
else if (strcmp(reg_to_send->reg_id, "P_reactive") == 0) {
/* Moc bierna - osobny pomiar */
strcpy(pomiar_tag, ",pomiar=reactive_power");
strcpy(field_name, "total");
}
else if (strcmp(reg_to_send->reg_id, "Frequency") == 0) {
/* Częstotliwość - mapujemy do pomiar=frequency, pole=frequency */
strcpy(pomiar_tag, ",pomiar=frequency");
strcpy(field_name, "frequency");
}
else if (strcmp(reg_to_send->reg_id, "Temp") == 0) {
/* Temperatura */
strcpy(pomiar_tag, ",pomiar=temperature");
strcpy(field_name, "value");
}
else if (strcmp(reg_to_send->reg_id, "P_accum") == 0) {
/* Energia zgromadzona */
strcpy(pomiar_tag, ",pomiar=energy");
strcpy(field_name, "total");
}
else if (strcmp(reg_to_send->reg_id, "P_daily") == 0) {
/* Energia dzienna - osobny pomiar */
strcpy(pomiar_tag, ",pomiar=daily_energy");
strcpy(field_name, "total");
}
else if (strcmp(reg_to_send->reg_id, "State1") == 0 ||
strcmp(reg_to_send->reg_id, "Alarm1") == 0 ||
strcmp(reg_to_send->reg_id, "Status") == 0 ||
strcmp(reg_to_send->reg_id, "Fault") == 0) {
/* Statusy */
strcpy(pomiar_tag, ",pomiar=status");
strcpy(field_name, reg_to_send->reg_id);
}
else if (strcmp(reg_to_send->reg_id, "Start") == 0 ||
strcmp(reg_to_send->reg_id, "Shutdown") == 0 ||
strcmp(reg_to_send->reg_id, "Time") == 0) {
/* Info/czas */
strcpy(pomiar_tag, ",pomiar=info");
strcpy(field_name, reg_to_send->reg_id);
}
else {
/* Domyślnie - nazwa rejestru jako nazwa pola */
strcpy(field_name, reg_to_send->reg_id);
}
snprintf(line, sizeof(line), "sun2k,device=sun2k%s %s=%s",
pomiar_tag, field_name, _str_buf);
return influx_send_post(line);
}
int influx_send_SUN2K_U_batch(float U_A, float U_B, float U_C, int valid) {
if (DISABLE_INFLUX) return 0;
if (!valid) return 0;
char batch[256];
/* Ujednolicone z ORNO - L1, L2, L3 zamiast U_A, U_B, U_C */
snprintf(batch, sizeof(batch), "sun2k,device=sun2k,pomiar=voltage L1=%.2f,L2=%.2f,L3=%.2f", U_A, U_B, U_C);
return influx_send_post(batch);
}
/* Validate float value - check if not NaN, Inf, or out of reasonable range */
int is_valid_float(float value, float min_val, float max_val)
{
@@ -398,7 +528,7 @@ int mosq_test()
char *buf = malloc(64);
while (k-- > 0)
{
sprintf(buf, "i=%i", i++);
snprintf(buf, 64, "i=%i", i++);
int snd = mqtt_send("/testtopic", buf);
if (snd != 0)
printf("TEST: mqtt_send error=%i\n", snd);
@@ -406,9 +536,41 @@ int mosq_test()
}
}
/* Function to read only voltage from ORNO device */
int read_orno_voltage_only(modbus_t *ctx, s_voltage *voltage) {
uint16_t reg[6];
int num;
// READ ONLY VOLTAGES
num = modbus_read_timed(ctx, 0xe, 6, reg);
if (num == 6) {
voltage->U1 = modbus_get_float_abcd(&reg[0]);
voltage->U2 = modbus_get_float_abcd(&reg[2]);
voltage->U3 = modbus_get_float_abcd(&reg[4]);
return 0; // success
} else {
printf("ORNO: Failed to read voltages: %s\n", modbus_strerror(errno));
return -1; // error
}
}
int main(int argc, char *argv[])
{
/* Użyj wartości z #define */
/* Parse command line arguments */
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--log") == 0) {
log_influx_data = 1;
printf("CFG: InfluxDB data logging enabled (--log)\n");
}
}
/* Użyj wartości z #define, token z env lub fallback */
influx_token = getenv("INFLUX_TOKEN");
if (!influx_token || strlen(influx_token) == 0) {
influx_token = INFLUX_TOKEN_DEFAULT;
printf("CFG: INFLUX_TOKEN env not set, using default from code\n");
} else {
printf("CFG: INFLUX_TOKEN loaded from environment\n");
}
const char *USB_DEV = USB_DEV_DEFAULT;
int ORNO_SLAVE = ORNO_SLAVE_ADR;
int SUN2000_SLAVE = SUN2000_SLAVE_ADR;
@@ -428,6 +590,7 @@ int main(int argc, char *argv[])
int voltage_buffer_index = 0;
int voltage_buffer_items = 0;
time_t high_frequency_mode_end_time = 0;
int high_frequency_mode_active = 0; /* Flag to track if we notified about high freq mode */
time_t rawtime;
struct tm *timeinfo;
@@ -451,17 +614,79 @@ int main(int argc, char *argv[])
do
{
int fast_voltage_mode = (time(NULL) < high_frequency_mode_end_time);
if (do_orno)
{
if (fast_voltage_mode) {
// FAST MODE - ONLY READ VOLTAGE
printf("ORNO: Fast voltage reading mode active\n");
//Create a new RTU context
modbus_t *ctx = modbus_new_rtu(USB_DEV, 9600, 'E', 8, 1);
//Set the Modbus address of the remote slave
modbus_set_slave(ctx, ORNO_SLAVE);
if (!ctx)
{
fprintf(stderr, "ORNO: Failed to create the context: %s\n", modbus_strerror(errno));
exit(1);
}
//Set the Modbus address of the remote slave
modbus_set_slave(ctx, ORNO_SLAVE);
if (modbus_connect(ctx) == -1)
{
fprintf(stderr, "ORNO: Unable to connect: %s\n", modbus_strerror(errno));
modbus_free(ctx);
exit(1);
}
modbus_rtu_set_rts_delay(ctx, ORNO_RTS_DELAY);
modbus_set_response_timeout(ctx, 0, 900000); /* 0.9s */
modbus_set_byte_timeout(ctx, 0, ORNO_BYTE_TIMEOUT);
printf("ORNO: RTS Delay and Timeouts configured.\n");
// Read only voltage in fast mode
s_voltage fast_voltage;
if (read_orno_voltage_only(ctx, &fast_voltage) == 0) {
printf("ORNO: Fast mode voltages: L1=%.1f V, L2=%.1f V, L3=%.1f V\n",
fast_voltage.U1, fast_voltage.U2, fast_voltage.U3);
// Send voltage data immediately
if (is_valid_float(fast_voltage.U1, 150.0, 280.0) &&
is_valid_float(fast_voltage.U2, 150.0, 280.0) &&
is_valid_float(fast_voltage.U3, 150.0, 280.0)) {
mqtt_send_U(fast_voltage.U1, fast_voltage.U2, fast_voltage.U3);
printf("ORNO: MQTT: Published fast voltage readings\n");
// Update voltage buffer for fluctuation detection
voltage_buffer_L1[voltage_buffer_index] = fast_voltage.U1;
voltage_buffer_L2[voltage_buffer_index] = fast_voltage.U2;
voltage_buffer_L3[voltage_buffer_index] = fast_voltage.U3;
voltage_buffer_index = (voltage_buffer_index + 1) % VOLTAGE_BUFFER_SIZE;
if (voltage_buffer_items < VOLTAGE_BUFFER_SIZE) {
voltage_buffer_items++;
}
}
}
modbus_close(ctx);
modbus_free(ctx);
// Small delay before next fast read
usleep(10000); // 10ms
continue; // Skip other readings in fast mode
}
// NORMAL MODE - FULL READING
//Create a new RTU context
modbus_t *ctx = modbus_new_rtu(USB_DEV, 9600, 'E', 8, 1);
if (!ctx)
{
fprintf(stderr, "ORNO: Failed to create the context: %s\n", modbus_strerror(errno));
exit(1);
}
//Set the Modbus address of the remote slave
modbus_set_slave(ctx, ORNO_SLAVE);
if (modbus_connect(ctx) == -1)
{
@@ -566,9 +791,10 @@ int main(int argc, char *argv[])
fabs(current_voltage.U2 - avg_L2) > avg_L2 * VOLTAGE_FLUCTUATION_THRESHOLD ||
fabs(current_voltage.U3 - avg_L3) > avg_L3 * VOLTAGE_FLUCTUATION_THRESHOLD) {
printf("ORNO: Voltage fluctuation detected! Switching to high frequency polling for %d seconds.\n", HIGH_FREQ_MODE_DURATION);
mqtt_send("fluct/start", "on");
influx_send_post("orno,device=orno,highfluct=start value=true");
mqtt_send("/energy/orno/fluct", "on");
influx_send_post("orno,device=orno,highfluct=start highfluct=1");
high_frequency_mode_end_time = time(NULL) + HIGH_FREQ_MODE_DURATION;
high_frequency_mode_active = 1;
}
@@ -589,6 +815,7 @@ int main(int argc, char *argv[])
s_energy send_energy;
s_frequency send_freq;
// VOLTAGE check
int spike_U = 0;
if (prev_voltage.U1 != 0 && (current_voltage.U1 > prev_voltage.U1 * 1.8 || current_voltage.U1 < prev_voltage.U1 * 0.2)) spike_U = 1;
if (prev_voltage.U2 != 0 && (current_voltage.U2 > prev_voltage.U2 * 1.8 || current_voltage.U2 < prev_voltage.U2 * 0.2)) spike_U = 1;
@@ -599,9 +826,11 @@ int main(int argc, char *argv[])
printf("ORNO: Voltage spike detected. Sending previous values.\n");
} else {
send_voltage = current_voltage;
prev_voltage = current_voltage;
}
// Always update prev_voltage to prevent getting stuck on old values
prev_voltage = current_voltage;
// CURRENT check
int spike_I = 0;
if (prev_current.I1 != 0 && (current_current.I1 > prev_current.I1 * 1.8 || current_current.I1 < prev_current.I1 * 0.2)) spike_I = 1;
if (prev_current.I2 != 0 && (current_current.I2 > prev_current.I2 * 1.8 || current_current.I2 < prev_current.I2 * 0.2)) spike_I = 1;
@@ -612,20 +841,24 @@ int main(int argc, char *argv[])
printf("ORNO: Current spike detected. Sending previous values.\n");
} else {
send_current = current_current;
prev_current = current_current;
}
// Always update prev_current to prevent getting stuck on old values
prev_current = current_current;
// POWER check
int spike_P = 0;
if (prev_power.P_Tot != 0 && (current_power.P_Tot > prev_power.P_Tot * 1.8 || current_power.P_Tot < prev_power.P_Tot * 0.2)) spike_P = 1;
if (prev_power.P_Tot != 0 && (fabs(current_power.P_Tot - prev_power.P_Tot) > fabs(prev_power.P_Tot) * 0.8)) spike_P = 1;
if(spike_P) {
send_power = prev_power;
printf("ORNO: Power spike detected. Sending previous values.\n");
} else {
send_power = current_power;
prev_power = current_power;
}
// Always update prev_power to prevent getting stuck on old values
prev_power = current_power;
// ENERGY check (should only increase)
int spike_W = 0;
if (prev_energy.W_Tot != 0 && (current_energy.W_Tot > prev_energy.W_Tot * 1.8 || current_energy.W_Tot < prev_energy.W_Tot)) spike_W = 1;
@@ -637,6 +870,7 @@ int main(int argc, char *argv[])
prev_energy = current_energy;
}
// FREQUENCY check (5% threshold)
int spike_F = 0;
if (prev_freq.Freq != 0 && (current_freq.Freq > prev_freq.Freq * 1.05 || current_freq.Freq < prev_freq.Freq * 0.95)) spike_F = 1; // 5% for frequency
@@ -648,10 +882,14 @@ int main(int argc, char *argv[])
prev_freq = current_freq;
}
// --- Now send the selected values ---
int valid_U = 0, valid_I = 0, valid_P = 0, valid_W = 0, valid_F = 0;
printf("ORNO: Voltages: L1=%.1f V, L2=%.1f V, L3=%.1f V\n", send_voltage.U1, send_voltage.U2, send_voltage.U3);
if (is_valid_float(send_voltage.U1, 150.0, 280.0) && is_valid_float(send_voltage.U2, 150.0, 280.0) && is_valid_float(send_voltage.U3, 150.0, 280.0)) {
mqtt_send_U(send_voltage.U1, send_voltage.U2, send_voltage.U3);
influx_send_U(send_voltage.U1, send_voltage.U2, send_voltage.U3);
valid_U = 1;
printf("ORNO: MQTT: Published voltages\n");
} else {
printf("ORNO: MQTT: Skipping voltages - invalid values\n");
@@ -660,7 +898,7 @@ int main(int argc, char *argv[])
printf("ORNO: Currents: L1=%.2f A, L2=%.2f A, L3=%.2f A\n", send_current.I1, send_current.I2, send_current.I3);
if (is_valid_float(send_current.I1, 0.0, 100.0) && is_valid_float(send_current.I2, 0.0, 100.0) && is_valid_float(send_current.I3, 0.0, 100.0)) {
mqtt_send_I(send_current.I1, send_current.I2, send_current.I3);
influx_send_I(send_current.I1, send_current.I2, send_current.I3);
valid_I = 1;
printf("ORNO: MQTT: Published currents\n");
} else {
printf("ORNO: MQTT: Skipping currents - invalid values\n");
@@ -669,7 +907,7 @@ int main(int argc, char *argv[])
printf("ORNO: Power: Total=%.3f W, L1=%.3f W, L2=%.3f W, L3=%.3f W\n", send_power.P_Tot, send_power.P1, send_power.P2, send_power.P3);
if (is_valid_float(send_power.P_Tot, -25000.0, 25000.0) && is_valid_float(send_power.P1, -10000.0, 10000.0) && is_valid_float(send_power.P2, -10000.0, 10000.0) && is_valid_float(send_power.P3, -10000.0, 10000.0)) {
mqtt_send_P(send_power.P_Tot, send_power.P1, send_power.P2, send_power.P3);
influx_send_P(send_power.P_Tot, send_power.P1, send_power.P2, send_power.P3);
valid_P = 1;
printf("ORNO: MQTT: Published power\n");
} else {
printf("ORNO: MQTT: Skipping power - invalid values\n");
@@ -678,7 +916,7 @@ int main(int argc, char *argv[])
printf("ORNO: Energy: Total=%.3f kWh, L1=%.3f kWh, L2=%.3f kWh, L3=%.3f kWh\n", send_energy.W_Tot, send_energy.W1, send_energy.W2, send_energy.W3);
if (is_valid_float(send_energy.W_Tot, 0.0, 1000000.0) && is_valid_float(send_energy.W1, 0.0, 1000000.0) && is_valid_float(send_energy.W2, 0.0, 1000000.0) && is_valid_float(send_energy.W3, 0.0, 1000000.0)) {
mqtt_send_W(send_energy.W_Tot, send_energy.W1, send_energy.W2, send_energy.W3);
influx_send_W(send_energy.W_Tot, send_energy.W1, send_energy.W2, send_energy.W3);
valid_W = 1;
printf("ORNO: MQTT: Published energy\n");
} else {
printf("ORNO: MQTT: Skipping energy - invalid values\n");
@@ -687,13 +925,21 @@ int main(int argc, char *argv[])
printf("ORNO: Frequency: %.4f Hz\n", send_freq.Freq);
if (is_valid_float(send_freq.Freq, 47.0, 53.0)) {
mqtt_send_Hz(send_freq.Freq);
influx_send_Hz(send_freq.Freq);
valid_F = 1;
printf("ORNO: MQTT: Published frequency\n");
} else {
printf("ORNO: MQTT: Skipping frequency - invalid value\n");
}
/* Send all ORNO data to InfluxDB in one batch (1 TCP connection instead of 5) */
influx_send_orno_batch(
&send_voltage, valid_U, &send_current, valid_I,
&send_power, valid_P, &send_energy, valid_W,
&send_freq, valid_F);
}
}
// Always execute SUN2K reading regardless of fast voltage mode
if (do_sun2k)
{
/* Delay between ORNO and SUN2K as configured */
@@ -754,8 +1000,41 @@ int main(int argc, char *argv[])
int proba = 0;
char str_buf[32];
/* Read SUN2K voltages (U_A, U_B, U_C) in one batch - registers 32069-32071 */
float sun2k_U_A = 0, sun2k_U_B = 0, sun2k_U_C = 0;
int sun2k_voltage_ok = 0;
proba = 0;
do {
num = modbus_read_timed(ctx, 32069, 3, reg);
} while ((num != 3) && (proba++ < 10));
if (num == 3) {
sun2k_U_A = (float)reg[0] / 10.0;
sun2k_U_B = (float)reg[1] / 10.0;
sun2k_U_C = (float)reg[2] / 10.0;
printf("SUN2K: Voltages: U_A=%.1f V, U_B=%.1f V, U_C=%.1f V\n", sun2k_U_A, sun2k_U_B, sun2k_U_C);
if (is_valid_float(sun2k_U_A, 150.0, 280.0) &&
is_valid_float(sun2k_U_B, 150.0, 280.0) &&
is_valid_float(sun2k_U_C, 150.0, 280.0)) {
mqtt_send_SUN2K_U(sun2k_U_A, sun2k_U_B, sun2k_U_C);
sun2k_voltage_ok = 1;
printf("SUN2K: MQTT: Published voltages\n");
} else {
printf("SUN2K: MQTT: Skipping voltages - invalid values\n");
}
influx_send_SUN2K_U_batch(sun2k_U_A, sun2k_U_B, sun2k_U_C, sun2k_voltage_ok);
} else {
printf("SUN2K: Failed to read voltages: %s\n", modbus_strerror(errno));
}
usleep(10000); /* 10ms delay before continuing */
for (int i = 0; i < sizeof(mbReg) / sizeof(t_mb_reg); i++)
{
/* Skip U_A, U_B, U_C - already read and sent in batch */
if (mbReg[i].ireg == 32069 || mbReg[i].ireg == 32070 || mbReg[i].ireg == 32071) {
continue;
}
memset(str_buf, 0, sizeof(str_buf));
proba = 0;
do
@@ -794,81 +1073,77 @@ int main(int argc, char *argv[])
time_t td = reg[0] * 65536 + reg[1];
timeinfo = localtime(&td);
printf("%s", asctime(timeinfo));
sprintf(str_buf, "\"%s\"", asctime(timeinfo));
snprintf(str_buf, sizeof(str_buf), "\"%s\"", asctime(timeinfo));
str_buf[strlen(str_buf) - 2] = '"';
str_buf[strlen(str_buf) - 1] = '\0';
break;
}
case rgI32:
{
int dh = reg[0];
int dl = reg[1];
int d = (dh << 16) + dl;
int32_t d = (int32_t)((uint32_t)reg[0] << 16 | reg[1]);
if (mbReg[i].scale == 1)
{
printf("%i", d);
sprintf(str_buf, "%i", d);
snprintf(str_buf, sizeof(str_buf), "%i", d);
}
else
{
double f = d;
f /= mbReg[i].scale;
printf("%lf", f);
sprintf(str_buf, "%lf", f);
snprintf(str_buf, sizeof(str_buf), "%lf", f);
}
break;
}
case rgI16:
{
int d = reg[0];
int16_t d = (int16_t)reg[0];
if (mbReg[i].scale == 1)
{
printf("%i", d);
sprintf(str_buf, "%i", d);
snprintf(str_buf, sizeof(str_buf), "%i", d);
}
else
{
double f = d;
f /= mbReg[i].scale;
printf("%lf", f);
sprintf(str_buf, "%lf", f);
snprintf(str_buf, sizeof(str_buf), "%lf", f);
}
break;
}
case rgU32:
{
unsigned int dh = reg[0];
unsigned int dl = reg[1];
unsigned int d = (dh << 16) + dl;
uint32_t d = (uint32_t)reg[0] << 16 | reg[1];
if (mbReg[i].scale == 1)
{
printf("%i", d);
sprintf(str_buf, "%i", d);
printf("%u", d);
snprintf(str_buf, sizeof(str_buf), "%u", d);
}
else
{
double f = d;
f /= mbReg[i].scale;
printf("%lf", f);
sprintf(str_buf, "%lf", f);
snprintf(str_buf, sizeof(str_buf), "%lf", f);
}
break;
}
case rgU16:
{
unsigned int d = reg[0];
uint16_t d = reg[0];
if (mbReg[i].scale == 1)
{
printf("%i", d);
sprintf(str_buf, "%i", d);
printf("%u", d);
snprintf(str_buf, sizeof(str_buf), "%u", d);
}
else
{
double f = d;
f /= mbReg[i].scale;
printf("%lf", f);
sprintf(str_buf, "%lf", f);
snprintf(str_buf, sizeof(str_buf), "%lf", f);
}
break;
}
@@ -877,7 +1152,7 @@ int main(int argc, char *argv[])
for (int j = 0; j < mbReg[i].num_reg; j++)
{
printf("%i:", reg[j]);
sprintf(str_buf, "%i", reg[j]);
snprintf(str_buf, sizeof(str_buf), "%i", reg[j]);
}
break;
}
@@ -892,10 +1167,18 @@ int main(int argc, char *argv[])
modbus_close(ctx);
modbus_free(ctx);
}
if (READ_LOOP) {
if (time(NULL) < high_frequency_mode_end_time) {
usleep(10000); // 10ms
} else {
// Check if high frequency mode just ended - send notification
if (high_frequency_mode_active) {
printf("ORNO: High frequency mode ended after %d seconds.\n", HIGH_FREQ_MODE_DURATION);
mqtt_send("/energy/orno/fluct", "off");
influx_send_post("orno,device=orno,highfluct=end highfluct=0");
high_frequency_mode_active = 0;
}
sleep(5); // 5s
}
}

370
modb_orno_tester.c Normal file
View File

@@ -0,0 +1,370 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <modbus/modbus-rtu.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>
#include <limits.h>
/* Konfiguracja */
#define USB_DEV_DEFAULT "/dev/ttyUSB0"
#define ORNO_SLAVE_ADR 2
#define SUN2000_SLAVE_ADR 3
#define NUM_READINGS 10 /* Liczba odczytów do testu */
/* Parametry timing dla ORNO */
#define ORNO_RTS_DELAY 5000
#define ORNO_BYTE_TIMEOUT 2500
/* Parametry timing dla SUN2K */
#define SUN2K_RTS_DELAY 5000
#define SUN2K_BYTE_TIMEOUT 2500
/* Stała dla timeoutu - wartości powyżej tego są uznawane za timeout */
#define TIMEOUT_THRESHOLD_US 100000 /* 100ms */
/* Struktura do przechowywania wyników testu */
typedef struct {
long total_time_us;
long min_time_us;
long max_time_us;
double avg_time_ms;
int successful_reads;
int total_reads;
int crc_errors;
int timeouts;
} test_results_t;
/* Timed wrapper for modbus_read_registers - logs start/stop and duration */
long modbus_read_timed(modbus_t *ctx, int addr, int nb, uint16_t *dest, int *success)
{
struct timeval t0, t1;
gettimeofday(&t0, NULL);
int res = modbus_read_registers(ctx, addr, nb, dest);
gettimeofday(&t1, NULL);
long elapsed_us = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - t0.tv_usec);
if (res < 0) {
const char *error_str = modbus_strerror(errno);
printf("MBUS: modbus_read_registers addr=0x%X nb=%d -> ERR (%s) elapsed=%ldus\n",
addr, nb, error_str, elapsed_us);
if (strstr(error_str, "timeout") || strstr(error_str, "Timed out") || elapsed_us > TIMEOUT_THRESHOLD_US) {
*success = 0; /* Timeout */
return -2;
} else if (strstr(error_str, "CRC") || strstr(error_str, "crc")) {
*success = -1; /* CRC error */
return -1;
} else {
*success = 0; /* Inny błąd */
return 0;
}
} else {
printf("MBUS: modbus_read_registers addr=0x%X nb=%d -> OK (%d) elapsed=%ldus\n",
addr, nb, res, elapsed_us);
*success = 1; /* Sukces */
return elapsed_us;
}
}
/* Test function for ORNO voltage reading */
int test_orno_voltage(modbus_t *ctx, test_results_t *results) {
uint16_t reg[6];
long elapsed_time;
int success_flag;
results->total_time_us = 0;
results->min_time_us = LONG_MAX;
results->max_time_us = 0;
results->successful_reads = 0;
results->total_reads = NUM_READINGS;
results->crc_errors = 0;
results->timeouts = 0;
printf("\n=== TESTING ORNO VOLTAGE READING ===\n");
printf("Performing %d voltage readings...\n", NUM_READINGS);
for (int i = 0; i < NUM_READINGS; i++) {
printf("Reading %d: ", i+1);
elapsed_time = modbus_read_timed(ctx, 0x0e, 6, reg, &success_flag);
if (success_flag == 1) {
/* Poprawny odczyt */
results->total_time_us += elapsed_time;
if (elapsed_time < results->min_time_us) results->min_time_us = elapsed_time;
if (elapsed_time > results->max_time_us) results->max_time_us = elapsed_time;
results->successful_reads++;
float U1 = modbus_get_float_abcd(&reg[0]);
float U2 = modbus_get_float_abcd(&reg[2]);
float U3 = modbus_get_float_abcd(&reg[4]);
printf("Voltages: L1=%.1fV L2=%.1fV L3=%.1fV\n", U1, U2, U3);
} else if (success_flag == -1) {
/* Błąd CRC */
results->crc_errors++;
printf("CRC Error\n");
} else if (success_flag == 0) {
/* Timeout lub inny błąd */
results->timeouts++;
printf("Timeout/Error\n");
}
usleep(10000); // 10ms delay between reads
}
/* Oblicz statystyki tylko dla poprawnych odczytów */
if (results->successful_reads > 0) {
results->avg_time_ms = (double)results->total_time_us / results->successful_reads / 1000.0;
} else {
results->avg_time_ms = 0;
results->min_time_us = 0;
results->max_time_us = 0;
}
return 0;
}
/* Test function for SUN2K voltage reading */
int test_sun2k_voltage(modbus_t *ctx, test_results_t *results) {
uint16_t reg[3];
long elapsed_time;
int success_flag;
results->total_time_us = 0;
results->min_time_us = LONG_MAX;
results->max_time_us = 0;
results->successful_reads = 0;
results->total_reads = NUM_READINGS;
results->crc_errors = 0;
results->timeouts = 0;
printf("\n=== TESTING SUN2K VOLTAGE READING ===\n");
printf("Performing %d voltage readings...\n", NUM_READINGS);
for (int i = 0; i < NUM_READINGS; i++) {
printf("Reading %d: ", i+1);
elapsed_time = modbus_read_timed(ctx, 32069, 3, reg, &success_flag);
if (success_flag == 1) {
/* Poprawny odczyt */
results->total_time_us += elapsed_time;
if (elapsed_time < results->min_time_us) results->min_time_us = elapsed_time;
if (elapsed_time > results->max_time_us) results->max_time_us = elapsed_time;
results->successful_reads++;
float U_A = (float)reg[0] / 10.0;
float U_B = (float)reg[1] / 10.0;
float U_C = (float)reg[2] / 10.0;
printf("Voltages: U_A=%.1fV U_B=%.1fV U_C=%.1fV\n", U_A, U_B, U_C);
} else if (success_flag == -1) {
/* Błąd CRC */
results->crc_errors++;
printf("CRC Error\n");
} else if (success_flag == 0) {
/* Timeout lub inny błąd */
results->timeouts++;
printf("Timeout/Error\n");
}
usleep(10000); // 10ms delay between reads
}
/* Oblicz statystyki tylko dla poprawnych odczytów */
if (results->successful_reads > 0) {
results->avg_time_ms = (double)results->total_time_us / results->successful_reads / 1000.0;
} else {
results->avg_time_ms = 0;
results->min_time_us = 0;
results->max_time_us = 0;
}
return 0;
}
/* Initialize ORNO modbus context */
modbus_t* init_orno_context(const char *device, int slave_addr) {
modbus_t *ctx = modbus_new_rtu(device, 9600, 'E', 8, 1);
if (!ctx) {
fprintf(stderr, "ORNO: Failed to create the context: %s\n", modbus_strerror(errno));
return NULL;
}
modbus_set_slave(ctx, slave_addr);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "ORNO: Unable to connect: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return NULL;
}
modbus_rtu_set_rts_delay(ctx, ORNO_RTS_DELAY);
modbus_set_response_timeout(ctx, 0, 900000); /* 0.9s */
modbus_set_byte_timeout(ctx, 0, ORNO_BYTE_TIMEOUT);
printf("ORNO: Context initialized successfully\n");
return ctx;
}
/* Initialize SUN2K modbus context */
modbus_t* init_sun2k_context(const char *device, int slave_addr) {
modbus_t *ctx = modbus_new_rtu(device, 9600, 'N', 8, 1);
if (!ctx) {
fprintf(stderr, "SUN2K: Failed to create the context: %s\n", modbus_strerror(errno));
return NULL;
}
modbus_set_slave(ctx, slave_addr);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "SUN2K: Unable to connect: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return NULL;
}
modbus_rtu_set_rts_delay(ctx, SUN2K_RTS_DELAY);
modbus_set_response_timeout(ctx, 0, 900000); /* 0.9s */
modbus_set_byte_timeout(ctx, 0, SUN2K_BYTE_TIMEOUT);
printf("SUN2K: Context initialized successfully\n");
return ctx;
}
/* Print test results */
void print_results(const char *device_name, test_results_t *results) {
printf("\n=== %s TEST RESULTS ===\n", device_name);
printf("Total readings: %d\n", results->total_reads);
printf("Successful readings: %d\n", results->successful_reads);
printf("CRC errors: %d\n", results->crc_errors);
printf("Timeouts/Other errors: %d\n", results->timeouts);
printf("Success rate: %.1f%%\n",
(results->total_reads > 0) ? (100.0 * results->successful_reads / results->total_reads) : 0);
if (results->successful_reads > 0) {
printf("Average time per reading: %.2f ms\n", results->avg_time_ms);
printf("Minimum time: %.2f ms\n", results->min_time_us / 1000.0);
printf("Maximum time: %.2f ms\n", results->max_time_us / 1000.0);
printf("Total time for successful readings: %.2f ms\n", results->total_time_us / 1000.0);
} else {
printf("No successful readings!\n");
}
printf("========================\n\n");
}
/* Compare devices and recommend faster one (based only on successful readings) */
void compare_and_recommend(test_results_t *orno_results, test_results_t *sun2k_results) {
printf("=== COMPARISON AND RECOMMENDATION ===\n");
if (orno_results->successful_reads == 0 && sun2k_results->successful_reads == 0) {
printf("ERROR: Neither device could be read successfully!\n");
return;
}
if (orno_results->successful_reads > 0 && sun2k_results->successful_reads > 0) {
printf("Both devices are working. Comparison:\n");
printf("- ORNO average time: %.2f ms (from %d successful readings)\n",
orno_results->avg_time_ms, orno_results->successful_reads);
printf("- SUN2K average time: %.2f ms (from %d successful readings)\n",
sun2k_results->avg_time_ms, sun2k_results->successful_reads);
if (orno_results->avg_time_ms < sun2k_results->avg_time_ms) {
printf("RECOMMENDATION: ORNO is faster by %.2f ms per reading\n",
sun2k_results->avg_time_ms - orno_results->avg_time_ms);
} else if (sun2k_results->avg_time_ms < orno_results->avg_time_ms) {
printf("RECOMMENDATION: SUN2K is faster by %.2f ms per reading\n",
orno_results->avg_time_ms - sun2k_results->avg_time_ms);
} else {
printf("RECOMMENDATION: Both devices have similar performance\n");
}
} else if (orno_results->successful_reads > 0) {
printf("RECOMMENDATION: Only ORNO has successful readings (%d). Use ORNO for fast voltage readings.\n",
orno_results->successful_reads);
} else if (sun2k_results->successful_reads > 0) {
printf("RECOMMENDATION: Only SUN2K has successful readings (%d). Use SUN2K for fast voltage readings.\n",
sun2k_results->successful_reads);
}
/* Reliability analysis */
printf("\nReliability Analysis:\n");
if (orno_results->crc_errors > 0 || orno_results->timeouts > 0) {
printf("- ORNO had %d CRC errors and %d timeouts\n",
orno_results->crc_errors, orno_results->timeouts);
}
if (sun2k_results->crc_errors > 0 || sun2k_results->timeouts > 0) {
printf("- SUN2K had %d CRC errors and %d timeouts\n",
sun2k_results->crc_errors, sun2k_results->timeouts);
}
if ((orno_results->crc_errors + orno_results->timeouts) > (sun2k_results->crc_errors + sun2k_results->timeouts)) {
printf("RELIABILITY: SUN2K is more reliable\n");
} else if ((orno_results->crc_errors + orno_results->timeouts) < (sun2k_results->crc_errors + sun2k_results->timeouts)) {
printf("RELIABILITY: ORNO is more reliable\n");
} else {
printf("RELIABILITY: Both devices show similar reliability\n");
}
printf("=====================================\n");
}
int main(int argc, char *argv[]) {
const char *USB_DEV = USB_DEV_DEFAULT;
int ORNO_SLAVE = ORNO_SLAVE_ADR;
int SUN2000_SLAVE = SUN2000_SLAVE_ADR;
test_results_t orno_results = {0};
test_results_t sun2k_results = {0};
printf("==============================================\n");
printf("Voltage Reading Speed Test\n");
printf("Testing ORNO vs SUN2K voltage reading speed\n");
printf("==============================================\n");
printf("Configuration:\n");
printf(" Device: %s\n", USB_DEV);
printf(" ORNO slave address: %d\n", ORNO_SLAVE);
printf(" SUN2K slave address: %d\n", SUN2000_SLAVE);
printf(" Number of readings per device: %d\n", NUM_READINGS);
printf("==============================================\n\n");
// Test ORNO
modbus_t *orno_ctx = init_orno_context(USB_DEV, ORNO_SLAVE);
if (orno_ctx) {
test_orno_voltage(orno_ctx, &orno_results);
modbus_close(orno_ctx);
modbus_free(orno_ctx);
} else {
printf("ORNO: Could not initialize context. Skipping ORNO tests.\n");
orno_results.successful_reads = 0;
}
usleep(100000); // 100ms delay between devices
// Test SUN2K
modbus_t *sun2k_ctx = init_sun2k_context(USB_DEV, SUN2000_SLAVE);
if (sun2k_ctx) {
test_sun2k_voltage(sun2k_ctx, &sun2k_results);
modbus_close(sun2k_ctx);
modbus_free(sun2k_ctx);
} else {
printf("SUN2K: Could not initialize context. Skipping SUN2K tests.\n");
sun2k_results.successful_reads = 0;
}
// Print results
if (orno_results.total_reads > 0) {
print_results("ORNO", &orno_results);
}
if (sun2k_results.total_reads > 0) {
print_results("SUN2K", &sun2k_results);
}
// Compare and recommend
compare_and_recommend(&orno_results, &sun2k_results);
printf("\nTest completed.\n");
return 0;
}

View File

@@ -0,0 +1 @@
influx delete --host "http://localhost:5086" --org "sic" --bucket "energydb" --token "BCIZ6kcCAVgpcwSfU0PBS7m0Zb6an93kuOtizbEtlXub-uaoYp4dmIQBQYaJCv8_KE4QYYZ08jxtpaZ3TUWP-Q==" --start 1970-01-01T00:00:00Z --stop $(date -u +"%Y-%m-%dT%H:%M:%SZ") --predicate '_fields="highfluct"'