-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_oled_standalone.py
More file actions
422 lines (348 loc) · 13.9 KB
/
test_oled_standalone.py
File metadata and controls
422 lines (348 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#!/usr/bin/env python3
# test_oled_standalone.py
# Standalone OLED test without dependencies
import sys
import os
import time
import threading
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont
try:
import netifaces
except ImportError:
print("⚠️ netifaces not installed, network info will be limited")
netifaces = None
try:
import psutil
except ImportError:
print("⚠️ psutil not installed, system metrics will be limited")
psutil = None
# Mock hardware if not available
try:
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from oled_manager.oled_hw import OledHardware, pil_to_ssd1306_buffer
HAS_HARDWARE = True
except ImportError:
print("⚠️ Hardware OLED module not available, simulation only")
HAS_HARDWARE = False
def pil_to_ssd1306_buffer(img):
"""Dummy function for simulation"""
return []
# Mock state
state = {
"di": [0, 1, 0, 1],
"do": [1, 0, 1, 0],
"simulation": False,
"simulation_oled": "--sim" in sys.argv
}
class OLEDAutoDisplay:
"""Standalone OLED auto-display"""
def __init__(self, simulation=False):
self.simulation = simulation
self.oled = None
if not simulation and HAS_HARDWARE:
try:
self.oled = OledHardware()
print("✅ Hardware OLED initialized")
except Exception as e:
print(f"❌ Hardware OLED failed: {e}")
print(" Falling back to simulation mode")
self.simulation = True
else:
self.simulation = True
print("📱 Running in simulation mode")
# Display state
self.current_screen = 0
self.total_screens = 4
self.rotation_interval = 5
self.running = False
self.thread = None
# Fonts
try:
self.font_large = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14
)
self.font_medium = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12
)
self.font_small = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 10
)
except:
self.font_large = ImageFont.load_default()
self.font_medium = ImageFont.load_default()
self.font_small = ImageFont.load_default()
def _get_ip_address(self, interface):
"""Get IP address for interface"""
if not netifaces:
return "N/A"
try:
# First check if the specific interface exists
if interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface)
if netifaces.AF_INET in addrs:
return addrs[netifaces.AF_INET][0]['addr']
# If eth0 not found, try common alternatives
if interface == 'eth0':
alternatives = ['enp1s0', 'end0', 'enp0s3', 'ens33']
for alt in alternatives:
if alt in netifaces.interfaces():
addrs = netifaces.ifaddresses(alt)
if netifaces.AF_INET in addrs:
print(f"📡 Found {interface} as {alt}: {addrs[netifaces.AF_INET][0]['addr']}")
return addrs[netifaces.AF_INET][0]['addr']
# If eth1 not found, return None (single ethernet board)
if interface == 'eth1':
return None
except Exception as e:
print(f"❌ Error getting IP for {interface}: {e}")
pass
return None
def _detect_network_interface(self):
"""Detect available network interfaces"""
if not netifaces:
return None, None
try:
interfaces = netifaces.interfaces()
print(f"🔍 Available interfaces: {interfaces}")
# Find first active interface with IP (excluding loopback)
for iface in interfaces:
if iface == 'lo':
continue
try:
addrs = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addrs:
ip = addrs[netifaces.AF_INET][0]['addr']
print(f"✅ Active interface: {iface} = {ip}")
return iface, ip
except:
continue
except Exception as e:
print(f"❌ Error detecting interfaces: {e}")
return None, None
def _get_temperature(self):
"""Get system temperature"""
try:
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
return int(f.read().strip()) / 1000.0
except:
return 45.0
def _get_uptime_string(self):
"""Get uptime string"""
if not psutil:
return "N/A"
try:
uptime_seconds = time.time() - psutil.boot_time()
days = int(uptime_seconds // 86400)
hours = int((uptime_seconds % 86400) // 3600)
if days > 0:
return f"{days}d {hours}h"
else:
return f"{hours}h {int((uptime_seconds % 3600) // 60)}m"
except:
return "N/A"
def _draw_screen_indicator(self, draw, current):
"""Draw screen indicator dots"""
x_start = 128 - (self.total_screens * 8)
y = 58
for i in range(self.total_screens):
x = x_start + (i * 8)
if i == current:
draw.ellipse([(x, y), (x+4, y+4)], fill=1)
else:
draw.ellipse([(x, y), (x+4, y+4)], outline=1)
def draw_network_screen(self):
"""Screen 1: Network Status"""
img = Image.new("1", (128, 64), 0)
draw = ImageDraw.Draw(img)
draw.text((2, 0), "EdgeForce-1000", 1, font=self.font_large)
draw.line([(0, 16), (128, 16)], fill=1)
# Try to get WAN IP (eth0 or alternatives)
wan_ip = self._get_ip_address('eth0')
# If still None, try to auto-detect primary interface
if wan_ip is None:
detected_iface, detected_ip = self._detect_network_interface()
if detected_ip:
wan_ip = detected_ip
# Get LAN IP (may not exist on single ethernet boards)
lan_ip = self._get_ip_address('eth1')
# Display WAN
if wan_ip:
draw.text((2, 20), f"WAN: {wan_ip}", 1, font=self.font_small)
else:
draw.text((2, 20), "WAN: N/A", 1, font=self.font_small)
# Display LAN (or skip if not available)
if lan_ip:
draw.text((2, 32), f"LAN: {lan_ip}", 1, font=self.font_small)
else:
draw.text((2, 32), "LAN: -", 1, font=self.font_small)
# Connection status
if wan_ip:
status = "Connected"
draw.text((2, 44), f"Status: {status}", 1, font=self.font_small)
else:
draw.text((2, 44), "Status: No Network", 1, font=self.font_small)
self._draw_screen_indicator(draw, 0)
return img
def draw_io_screen(self):
"""Screen 2: I/O Status"""
img = Image.new("1", (128, 64), 0)
draw = ImageDraw.Draw(img)
draw.text((2, 0), "I/O Status", 1, font=self.font_large)
draw.line([(0, 16), (128, 16)], fill=1)
di_str = "DI: "
for val in state["di"]:
di_str += ("■ " if val else "□ ")
draw.text((2, 20), di_str, 1, font=self.font_medium)
do_str = "DO: "
for val in state["do"]:
do_str += ("■ " if val else "□ ")
draw.text((2, 35), do_str, 1, font=self.font_medium)
draw.text((2, 50), "Update: 10Hz", 1, font=self.font_small)
self._draw_screen_indicator(draw, 1)
return img
def draw_system_screen(self):
"""Screen 3: System Metrics"""
img = Image.new("1", (128, 64), 0)
draw = ImageDraw.Draw(img)
draw.text((2, 0), "System Metrics", 1, font=self.font_large)
draw.line([(0, 16), (128, 16)], fill=1)
if psutil:
cpu_percent = psutil.cpu_percent(interval=0.1)
memory = psutil.virtual_memory()
else:
cpu_percent = 0
memory = type('obj', (object,), {'percent': 0})()
temp = self._get_temperature()
uptime = self._get_uptime_string()
draw.text((2, 20), f"CPU: {int(cpu_percent)}%", 1, font=self.font_medium)
draw.text((65, 20), f"RAM: {int(memory.percent)}%", 1, font=self.font_medium)
draw.text((2, 35), f"Temp: {int(temp)}°C", 1, font=self.font_medium)
draw.text((2, 50), f"Up: {uptime}", 1, font=self.font_small)
self._draw_screen_indicator(draw, 2)
return img
def draw_expansion_screen(self):
"""Screen 4: Expansion Modules"""
img = Image.new("1", (128, 64), 0)
draw = ImageDraw.Draw(img)
draw.text((2, 0), "Expansion", 1, font=self.font_large)
draw.line([(0, 16), (128, 16)], fill=1)
# Mock modules for testing
modules = [
{"id": 1, "type": "DIO-16", "status": "OK"},
{"id": 2, "type": "AIO-8", "status": "OK"}
]
if modules:
y = 20
for mod in modules[:3]:
status = "✓" if mod["status"] == "OK" else "✗"
text = f"M{mod['id']}: {mod['type']} {status}"
draw.text((2, y), text, 1, font=self.font_small)
y += 12
draw.text((2, 50), f"Total: {len(modules)} modules", 1, font=self.font_small)
else:
draw.text((2, 25), "No expansion", 1, font=self.font_medium)
draw.text((2, 40), "modules found", 1, font=self.font_medium)
self._draw_screen_indicator(draw, 3)
return img
def show_screen(self, screen_num):
"""Display specific screen"""
if screen_num == 0:
img = self.draw_network_screen()
elif screen_num == 1:
img = self.draw_io_screen()
elif screen_num == 2:
img = self.draw_system_screen()
elif screen_num == 3:
img = self.draw_expansion_screen()
else:
return
if self.simulation:
img.save("/tmp/oled_display.png")
print(f"📺 Screen {screen_num + 1}/{self.total_screens} → /tmp/oled_display.png")
else:
buffer = pil_to_ssd1306_buffer(img)
self.oled.draw_buffer(buffer)
print(f"📺 Screen {screen_num + 1}/{self.total_screens} displayed on hardware")
def next_screen(self):
"""Go to next screen"""
self.current_screen = (self.current_screen + 1) % self.total_screens
self.show_screen(self.current_screen)
def _rotation_loop(self):
"""Background rotation thread"""
while self.running:
time.sleep(self.rotation_interval)
self.next_screen()
def start(self):
"""Start auto-rotation"""
if self.running:
return
print("🔄 OLED Auto-Display: Starting...")
self.running = True
# Show initial screen
self.show_screen(self.current_screen)
# Start rotation thread
self.thread = threading.Thread(target=self._rotation_loop, daemon=True)
self.thread.start()
print("✅ OLED Auto-Display: Running")
def stop(self):
"""Stop auto-rotation"""
print("🛑 OLED Auto-Display: Stopping...")
self.running = False
if self.thread:
self.thread.join(timeout=2)
if self.oled and not self.simulation:
self.oled.clear()
print("✅ OLED Auto-Display: Stopped")
if __name__ == "__main__":
print("=" * 50)
print("OLED Auto-Display Standalone Test")
print("=" * 50)
# Check for dependencies
missing = []
if not netifaces:
missing.append("netifaces")
if not psutil:
missing.append("psutil")
if missing:
print(f"\n⚠️ Missing packages: {', '.join(missing)}")
print(" Install with: pip3 install " + " ".join(missing))
print(" Continuing with limited functionality...\n")
# Show network detection info
if netifaces:
print("🔍 Network Interface Detection:")
try:
interfaces = netifaces.interfaces()
print(f" Available interfaces: {interfaces}")
for iface in interfaces:
if iface == 'lo':
continue
try:
addrs = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addrs:
ip = addrs[netifaces.AF_INET][0]['addr']
print(f" ✅ {iface}: {ip}")
except:
pass
except Exception as e:
print(f" ❌ Error: {e}")
print()
# Detect mode
simulation = "--sim" in sys.argv
print(f"Mode: {'Simulation' if simulation else 'Hardware'}")
print("=" * 50 + "\n")
# Create and start display
display = OLEDAutoDisplay(simulation=simulation)
display.start()
try:
print("⏳ Running for 30 seconds...")
print(" Press Ctrl+C to stop\n")
if simulation:
print("💡 View output: display /tmp/oled_display.png")
print(" Or copy: scp radxa@192.168.5.103:/tmp/oled_display.png .\n")
time.sleep(30)
except KeyboardInterrupt:
print("\n⚠️ Interrupted by user")
finally:
display.stop()
print("\n✅ Test complete")