Skip to content

Commit d38414b

Browse files
feat: Read each sensor separately so one failure doesn't kill everything
1 parent 066e165 commit d38414b

File tree

1 file changed

+133
-96
lines changed

1 file changed

+133
-96
lines changed

modules/default/sensehat/python/reader.py

Lines changed: 133 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -5,121 +5,158 @@
55
from datetime import datetime, timezone
66

77
try:
8-
from sense_hat import SenseHat
8+
from sense_hat import SenseHat
99
except Exception as e:
10-
SenseHat = None
10+
SenseHat = None
1111

1212

1313
def iso_timestamp():
14-
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
14+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
15+
16+
17+
def safe_float(value):
18+
try:
19+
return float(value) if value is not None else None
20+
except Exception:
21+
return None
1522

1623

1724
def read_sensors():
18-
if SenseHat is None:
19-
return {"error": "sense_hat library not available"}
20-
try:
21-
sh = SenseHat()
22-
temperature = sh.get_temperature()
23-
humidity = sh.get_humidity()
24-
pressure = sh.get_pressure()
25-
26-
# Orientation
27-
orientation = sh.get_orientation() or {}
28-
pitch = orientation.get("pitch")
29-
roll = orientation.get("roll")
30-
yaw = orientation.get("yaw")
31-
32-
# IMU sensors
33-
accel_raw = sh.get_accelerometer_raw() or {}
34-
gyro_raw = sh.get_gyroscope_raw() or {}
35-
mag_raw = sh.get_compass_raw() or {}
36-
37-
return {
38-
"temperature": float(temperature) if temperature is not None else None,
39-
"humidity": float(humidity) if humidity is not None else None,
40-
"pressure": float(pressure) if pressure is not None else None,
41-
"orientation": {
42-
"pitch": float(pitch) if pitch is not None else None,
43-
"roll": float(roll) if roll is not None else None,
44-
"yaw": float(yaw) if yaw is not None else None,
45-
},
46-
"accelerometer": {
47-
"x": float(accel_raw.get("x")) if accel_raw.get("x") is not None else None,
48-
"y": float(accel_raw.get("y")) if accel_raw.get("y") is not None else None,
49-
"z": float(accel_raw.get("z")) if accel_raw.get("z") is not None else None,
50-
},
51-
"gyroscope": {
52-
"x": float(gyro_raw.get("x")) if gyro_raw.get("x") is not None else None,
53-
"y": float(gyro_raw.get("y")) if gyro_raw.get("y") is not None else None,
54-
"z": float(gyro_raw.get("z")) if gyro_raw.get("z") is not None else None,
55-
},
56-
"magnetometer": {
57-
"x": float(mag_raw.get("x")) if mag_raw.get("x") is not None else None,
58-
"y": float(mag_raw.get("y")) if mag_raw.get("y") is not None else None,
59-
"z": float(mag_raw.get("z")) if mag_raw.get("z") is not None else None,
60-
},
61-
"timestamp": iso_timestamp(),
62-
}
63-
except Exception as e:
64-
return {"error": f"Exception during sensor read: {e}"}
25+
if SenseHat is None:
26+
return {"error": "sense_hat library not available"}
27+
28+
try:
29+
sh = SenseHat()
30+
except Exception as e:
31+
# If we can't even init the SenseHat, we really are stuck
32+
return {"error": f"SenseHat init failed: {e}"}
33+
34+
# Read each sensor separately so one failure doesn't kill everything
35+
try:
36+
temperature = safe_float(sh.get_temperature())
37+
except Exception:
38+
temperature = None
39+
40+
try:
41+
humidity = safe_float(sh.get_humidity())
42+
except Exception:
43+
humidity = None
44+
45+
try:
46+
pressure = safe_float(sh.get_pressure())
47+
except Exception:
48+
pressure = None
49+
50+
# Orientation
51+
try:
52+
orientation = sh.get_orientation() or {}
53+
except Exception:
54+
orientation = {}
55+
56+
pitch = orientation.get("pitch")
57+
roll = orientation.get("roll")
58+
yaw = orientation.get("yaw")
59+
60+
# IMU sensors
61+
try:
62+
accel_raw = sh.get_accelerometer_raw() or {}
63+
except Exception:
64+
accel_raw = {}
65+
66+
try:
67+
gyro_raw = sh.get_gyroscope_raw() or {}
68+
except Exception:
69+
gyro_raw = {}
70+
71+
try:
72+
mag_raw = sh.get_compass_raw() or {}
73+
except Exception:
74+
mag_raw = {}
75+
76+
return {
77+
"temperature": temperature,
78+
"humidity": humidity,
79+
"pressure": pressure,
80+
"orientation": {
81+
"pitch": safe_float(pitch),
82+
"roll": safe_float(roll),
83+
"yaw": safe_float(yaw)
84+
},
85+
"accelerometer": {
86+
"x": safe_float(accel_raw.get("x")),
87+
"y": safe_float(accel_raw.get("y")),
88+
"z": safe_float(accel_raw.get("z"))
89+
},
90+
"gyroscope": {
91+
"x": safe_float(gyro_raw.get("x")),
92+
"y": safe_float(gyro_raw.get("y")),
93+
"z": safe_float(gyro_raw.get("z"))
94+
},
95+
"magnetometer": {
96+
"x": safe_float(mag_raw.get("x")),
97+
"y": safe_float(mag_raw.get("y")),
98+
"z": safe_float(mag_raw.get("z"))
99+
},
100+
"timestamp": iso_timestamp(),
101+
}
65102

66103

67104
def parse_color(s):
68-
parts = [p.strip() for p in s.split(",")]
69-
if len(parts) != 3:
70-
raise ValueError("Color must be r,g,b")
71-
rgb = []
72-
for p in parts:
73-
v = int(p)
74-
if v < 0:
75-
v = 0
76-
if v > 255:
77-
v = 255
78-
rgb.append(v)
79-
return tuple(rgb)
105+
parts = [p.strip() for p in s.split(",")]
106+
if len(parts) != 3:
107+
raise ValueError("Color must be r,g,b")
108+
rgb = []
109+
for p in parts:
110+
v = int(p)
111+
if v < 0:
112+
v = 0
113+
if v > 255:
114+
v = 255
115+
rgb.append(v)
116+
return tuple(rgb)
80117

81118

82119
def apply_led(args):
83-
if SenseHat is None:
84-
print("sense_hat library not available", file=sys.stderr)
85-
return 1
86-
try:
87-
sh = SenseHat()
88-
if args.clear:
89-
sh.clear()
90-
return 0
91-
if args.mode == "text":
92-
color = parse_color(args.color) if args.color else (255, 255, 255)
93-
sh.clear()
94-
sh.show_message(args.text or "", text_colour=color, scroll_speed=0.07)
95-
return 0
96-
# default status mode
97-
color = parse_color(args.color) if args.color else (0, 255, 0)
98-
sh.clear(color)
99-
return 0
100-
except Exception as e:
101-
print(f"LED command failed: {e}", file=sys.stderr)
102-
return 2
120+
if SenseHat is None:
121+
print("sense_hat library not available", file=sys.stderr)
122+
return 1
123+
try:
124+
sh = SenseHat()
125+
if args.clear:
126+
sh.clear()
127+
return 0
128+
if args.mode == "text":
129+
color = parse_color(args.color) if args.color else (255, 255, 255)
130+
sh.clear()
131+
sh.show_message(args.text or "", text_colour=color, scroll_speed=0.07)
132+
return 0
133+
# default status mode
134+
color = parse_color(args.color) if args.color else (0, 255, 0)
135+
sh.clear(color)
136+
return 0
137+
except Exception as e:
138+
print(f"LED command failed: {e}", file=sys.stderr)
139+
return 2
103140

104141

105142
def main():
106-
parser = argparse.ArgumentParser(description="Sense HAT reader and LED controller")
107-
parser.add_argument("--read", action="store_true", help="Read sensors and output JSON")
108-
parser.add_argument("--mode", choices=["status", "text"], help="LED mode")
109-
parser.add_argument("--text", help="Text to display when mode=text")
110-
parser.add_argument("--color", help="Color as r,g,b")
111-
parser.add_argument("--clear", action="store_true", help="Clear LED matrix")
143+
parser = argparse.ArgumentParser(description="Sense HAT reader and LED controller")
144+
parser.add_argument("--read", action="store_true", help="Read sensors and output JSON")
145+
parser.add_argument("--mode", choices=["status", "text"], help="LED mode")
146+
parser.add_argument("--text", help="Text to display when mode=text")
147+
parser.add_argument("--color", help="Color as r,g,b")
148+
parser.add_argument("--clear", action="store_true", help="Clear LED matrix")
112149

113-
args = parser.parse_args()
150+
args = parser.parse_args()
114151

115-
if args.read or (not args.mode and not args.clear):
116-
data = read_sensors()
117-
print(json.dumps(data))
118-
return 0 if "error" not in data else 3
152+
if args.read or (not args.mode and not args.clear):
153+
data = read_sensors()
154+
print(json.dumps(data))
155+
return 0 if "error" not in data else 3
119156

120-
# Otherwise LED control
121-
return apply_led(args)
157+
# Otherwise LED control
158+
return apply_led(args)
122159

123160

124161
if __name__ == "__main__":
125-
sys.exit(main())
162+
sys.exit(main())

0 commit comments

Comments
 (0)