Compare commits
13 Commits
886cecf523
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b68a950062 | |||
| 7a03a794ba | |||
| fd8dce7535 | |||
| 3078bbcf3f | |||
| 7220d3e750 | |||
| bc399e0044 | |||
| ae444a10fc | |||
| 8f3756b1fe | |||
| 0524e7284d | |||
| 2b16211ef2 | |||
| 44aceebae6 | |||
| 912878d6e6 | |||
|
|
aa31532d8c |
@@ -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
464
kimi_analyse.md
Normal 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
68
kimi_analyse2.md
Normal 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
|
||||
439
modb_orno3.c
439
modb_orno3.c
@@ -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(®[0]);
|
||||
voltage->U2 = modbus_get_float_abcd(®[2]);
|
||||
voltage->U3 = modbus_get_float_abcd(®[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
370
modb_orno_tester.c
Normal 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(®[0]);
|
||||
float U2 = modbus_get_float_abcd(®[2]);
|
||||
float U3 = modbus_get_float_abcd(®[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;
|
||||
}
|
||||
|
||||
1
usuniecie_pola_highfluct.sh
Normal file
1
usuniecie_pola_highfluct.sh
Normal 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"'
|
||||
Reference in New Issue
Block a user