Skip to content

Commit 6972954

Browse files
committed
sw: Reorganize and improve tools
- Rename melexis.py -> amsky01_viewer.py (GUI viewer) - Rename sensor_viewer.py -> amsky01_cli.py (CLI viewer) - Remove debug_sensor.py (obsolete) - Add sw/README.md with tool descriptions - Update amsky01_viewer.py: - Add --debug flag for serial communication logging - Add buffer for handling split thrmap messages - Update to include SQM parameter - Update to include dew point - Rename to - Update CI: Run firmware build only on fw/ changes
1 parent 181c2c3 commit 6972954

File tree

5 files changed

+135
-182
lines changed

5 files changed

+135
-182
lines changed

.github/workflows/platformio-build.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ name: PlatformIO CI
22

33
on:
44
push:
5+
paths:
6+
- 'fw/**'
7+
- '.github/workflows/platformio-build.yml'
58
pull_request:
9+
paths:
10+
- 'fw/**'
11+
- '.github/workflows/platformio-build.yml'
612
release:
713
types: [ created, published ]
814
workflow_dispatch:

sw/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# AMSKY01 Software Tools
2+
3+
Nástroje pro práci se senzory AMSKY01 (MLX90641, SHT4x, TSL2591).
4+
5+
## Nástroje pro real-time monitoring
6+
7+
### `amsky01_viewer.py`
8+
GUI vizualizátor s PyQt/PyQtGraph pro zobrazení dat v reálném čase:
9+
- IR teplotní mapa z MLX90641 (12×16 pixelů)
10+
- Teplota a vlhkost ze SHT4x včetně dew pointu
11+
- Osvětlení z TSL2591 včetně SQM
12+
- Parametry senzorů (Vdd, Ta, rohy IR mapy)
13+
14+
**Použití:**
15+
```bash
16+
python amsky01_viewer.py --port /dev/ttyACM0 --baud 115200
17+
python amsky01_viewer.py --port /dev/ttyACM0 --debug # s výpisem komunikace
18+
```
19+
20+
**Závislosti:** `pyserial`, `numpy`, `pyqtgraph`, `PyQt5`
21+
22+
---
23+
24+
### `amsky01_cli.py`
25+
CLI viewer s curses rozhraním + automatické logování do CSV:
26+
- Real-time zobrazení senzorových dat v terminálu
27+
- Automatické ukládání dat do CSV souborů
28+
- Rotace log souborů každých 10 minut
29+
30+
**Použití:**
31+
```bash
32+
python amsky01_cli.py --port /dev/ttyACM0 --baud 115200
33+
```
34+
35+
**Závislosti:** `pyserial`, `curses`
36+
37+
---
38+
39+
## Nástroje pro analýzu logů
40+
41+
### `plot_logs.py`
42+
Vykreslování grafů z uložených CSV logů:
43+
- Podpora více souborů současně
44+
- Interaktivní režim s auto-refresh
45+
- Export do PNG
46+
47+
**Použití:**
48+
```bash
49+
python plot_logs.py sensor_logs/latest.csv
50+
python plot_logs.py --interactive --refresh 30 sensor_logs/*.csv
51+
python plot_logs.py --output myplot.png sensor_logs/data.csv
52+
```
53+
54+
**Závislosti:** `pandas`, `matplotlib`, `numpy`
55+
56+
---
57+
58+
### `plot_latest.sh`
59+
Bash wrapper pro rychlé vykreslení logů:
60+
```bash
61+
./plot_latest.sh # Vykreslí nejnovější log
62+
./plot_latest.sh --all # Vykreslí všechny logy
63+
./plot_latest.sh specific.csv # Vykreslí konkrétní soubor
64+
```
65+
66+
---
67+
68+
## Formát UART zpráv
69+
70+
Zařízení komunikuje přes UART rychlostí 115200 baud (konfigurovatelné). Zprávy:
71+
72+
- `$thrmap,<192 hodnot>` - IR teplotní mapa (12×16 pixelů)
73+
- `$cloud,<TL>,<TR>,<BL>,<BR>,<CENTER>` - Rohy a střed IR mapy
74+
- `$cloud_meta,<Vdd>,<Ta>` - Napětí a teplota MLX90641
75+
- `$hygro,<temp>,<humidity>,<dew_point>` - Teplota, vlhkost, rosný bod
76+
- `$light,<lux>,<full>,<ir>,<gain>,<int_time>,<sqm>` - Osvětlení a SQM
77+
78+
---
79+
80+
## Instalace závislostí
81+
82+
```bash
83+
pip install pyserial numpy pyqtgraph PyQt5 pandas matplotlib
84+
```
File renamed without changes.

sw/melexis.py renamed to sw/amsky01_viewer.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
33
Zobrazuje:
44
- IR mapu z MLX90641 ($thrmap)
5-
- Ta/Vdd z MLX90641 ($thr_parameters)
5+
- Ta/Vdd z MLX90641 ($cloud_meta)
66
- rohy/střed z MLX90641 ($cloud)
77
- teplotu a vlhkost ze SHT4x ($hygro)
8-
- osvětlení z TSL2591 ($light)
8+
- osvětlení z TSL2591 ($light) včetně SQM
99
1010
Závislosti:
1111
pip install pyserial numpy pyqtgraph PyQt5
@@ -47,44 +47,46 @@ def parse_thrmap(line: str):
4747

4848

4949
def parse_hygro(line: str):
50-
"""$hygro,<temp>,<humidity>"""
50+
"""$hygro,<temp>,<humidity>,<dew_point>"""
5151
line = line.strip()
5252
if not line.startswith("$hygro,"):
5353
return None
5454
parts = line.split(",")
55-
if len(parts) < 3:
55+
if len(parts) < 4:
5656
return None
5757
try:
5858
temp = float(parts[1])
5959
rh = float(parts[2])
60+
dew_point = float(parts[3])
6061
except ValueError:
6162
return None
62-
return temp, rh
63+
return temp, rh, dew_point
6364

6465

6566
def parse_light(line: str):
66-
"""$light,normalized_lux,full_raw,ir_raw,gain,integration_time"""
67+
"""$light,normalized_lux,full_raw,ir_raw,gain,integration_time,sqm"""
6768
line = line.strip()
6869
if not line.startswith("$light,"):
6970
return None
7071
parts = line.split(",")
71-
if len(parts) < 6:
72+
if len(parts) < 7:
7273
return None
7374
try:
7475
lux = float(parts[1])
7576
full_raw = float(parts[2])
7677
ir_raw = float(parts[3])
7778
gain = parts[4]
7879
itime = parts[5]
80+
sqm = float(parts[6])
7981
except ValueError:
8082
return None
81-
return lux, full_raw, ir_raw, gain, itime
83+
return lux, full_raw, ir_raw, gain, itime, sqm
8284

8385

84-
def parse_thr_parameters(line: str):
85-
"""$thr_parameters,<vdd>,<ta>"""
86+
def parse_cloud_meta(line: str):
87+
"""$cloud_meta,<vdd>,<ta>"""
8688
line = line.strip()
87-
if not line.startswith("$thr_parameters,"):
89+
if not line.startswith("$cloud_meta,"):
8890
return None
8991
parts = line.split(",")
9092
if len(parts) < 3:
@@ -117,11 +119,13 @@ def parse_cloud(line: str):
117119

118120

119121
class MainWindow(QtWidgets.QMainWindow):
120-
def __init__(self, ser: serial.Serial, vmin=None, vmax=None, parent=None):
122+
def __init__(self, ser: serial.Serial, vmin=None, vmax=None, debug=False, parent=None):
121123
super().__init__(parent)
122124
self.ser = ser
123125
self.vmin = vmin
124126
self.vmax = vmax
127+
self.debug = debug
128+
self.line_buffer = "" # Buffer pro neúplné řádky
125129

126130
self.setWindowTitle("AMSKY01 / MLX90641 vizualizátor")
127131

@@ -167,20 +171,24 @@ def make_label():
167171
# Hygro
168172
self.lbl_temp = make_label()
169173
self.lbl_rh = make_label()
174+
self.lbl_dew = make_label()
170175
form.addRow("SHT4x T [°C]", self.lbl_temp)
171176
form.addRow("SHT4x RH [%]", self.lbl_rh)
177+
form.addRow("Dew Point [°C]", self.lbl_dew)
172178

173179
# Light
174180
self.lbl_lux = make_label()
175181
self.lbl_full = make_label()
176182
self.lbl_ir = make_label()
177183
self.lbl_gain = make_label()
178184
self.lbl_itime = make_label()
185+
self.lbl_sqm = make_label()
179186
form.addRow("Lux", self.lbl_lux)
180187
form.addRow("TSL full", self.lbl_full)
181188
form.addRow("TSL IR", self.lbl_ir)
182189
form.addRow("TSL gain", self.lbl_gain)
183190
form.addRow("TSL int", self.lbl_itime)
191+
form.addRow("SQM [mag/arcsec²]", self.lbl_sqm)
184192

185193
# IR senzor parametry
186194
self.lbl_vdd = make_label()
@@ -210,12 +218,21 @@ def make_label():
210218

211219
def poll_serial(self):
212220
try:
213-
# Čti všechny dostupné řádky
221+
# Čti všechny dostupné data
214222
while self.ser.in_waiting:
215-
line = self.ser.readline().decode(errors="ignore")
216-
if not line:
223+
chunk = self.ser.read(self.ser.in_waiting).decode(errors="ignore")
224+
if not chunk:
217225
break
218-
self.process_line(line)
226+
227+
# Přidej do bufferu
228+
self.line_buffer += chunk
229+
230+
# Zpracuj kompletní řádky
231+
while "\n" in self.line_buffer:
232+
line, self.line_buffer = self.line_buffer.split("\n", 1)
233+
if self.debug:
234+
print(f"< {line}")
235+
self.process_line(line + "\n")
219236
except serial.SerialException as e:
220237
print(f"Serial error: {e}")
221238

@@ -233,24 +250,26 @@ def process_line(self, line: str):
233250
if s.startswith("$hygro,"):
234251
v = parse_hygro(s)
235252
if v is not None:
236-
t, rh = v
253+
t, rh, dew = v
237254
self.lbl_temp.setText(f"{t:.2f}")
238255
self.lbl_rh.setText(f"{rh:.2f}")
256+
self.lbl_dew.setText(f"{dew:.2f}")
239257
return
240258

241259
if s.startswith("$light,"):
242260
v = parse_light(s)
243261
if v is not None:
244-
lux, full_raw, ir_raw, gain, itime = v
262+
lux, full_raw, ir_raw, gain, itime, sqm = v
245263
self.lbl_lux.setText(f"{lux:.2f}")
246264
self.lbl_full.setText(f"{full_raw:.0f}")
247265
self.lbl_ir.setText(f"{ir_raw:.0f}")
248266
self.lbl_gain.setText(gain)
249267
self.lbl_itime.setText(itime)
268+
self.lbl_sqm.setText(f"{sqm:.2f}")
250269
return
251270

252-
if s.startswith("$thr_parameters,"):
253-
v = parse_thr_parameters(s)
271+
if s.startswith("$cloud_meta,"):
272+
v = parse_cloud_meta(s)
254273
if v is not None:
255274
vdd, ta = v
256275
self.lbl_vdd.setText(f"{vdd:.3f}")
@@ -301,6 +320,7 @@ def main():
301320
parser.add_argument("--baud", type=int, default=115200, help="baudrate (default 115200)")
302321
parser.add_argument("--vmin", type=float, default=None, help="min. teplota pro barevnou škálu")
303322
parser.add_argument("--vmax", type=float, default=None, help="max. teplota pro barevnou škálu")
323+
parser.add_argument("--debug", action="store_true", help="zobraz všechny zprávy na seriové lince")
304324

305325
args = parser.parse_args()
306326

@@ -312,12 +332,15 @@ def main():
312332

313333
# Po startu automaticky zapni režim streamování heatmapy
314334
try:
315-
ser.write(b"thrmap_on\n")
335+
cmd = b"thrmap_on\n"
336+
ser.write(cmd)
337+
if args.debug:
338+
print(f"> {cmd.decode().rstrip()}")
316339
except serial.SerialException:
317340
pass
318341

319342
app = QtWidgets.QApplication(sys.argv)
320-
win = MainWindow(ser, vmin=args.vmin, vmax=args.vmax)
343+
win = MainWindow(ser, vmin=args.vmin, vmax=args.vmax, debug=args.debug)
321344
win.resize(900, 500)
322345
win.show()
323346

0 commit comments

Comments
 (0)