-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSensorShareButton.py
More file actions
executable file
·731 lines (638 loc) · 28.9 KB
/
Copy pathSensorShareButton.py
File metadata and controls
executable file
·731 lines (638 loc) · 28.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
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
#!/usr/bin/env python3
"""
SensorShare - MQTT Sensor Publisher
Runs on Rotator-PI to read sensor data and publish via MQTT
Includes LEGO SPIKE force sensors
"""
import paho.mqtt.client as mqtt
import time
import json
from datetime import datetime
from smbus2 import SMBus, i2c_msg
from buildhat import ForceSensor, Motor
import subprocess
import os
import sys
import RPi.GPIO as GPIO
import board
import neopixel
# MQTT Configuration
MQTT_BROKER = "localhost" # Local MQTT broker
MQTT_PORT = 1883
BASE_TOPIC = "sensors/rotator"
# I2C Configuration for light sensors
I2C_BUS = 1
BH1750_OUTSIDE_ADDR = 0x23 # Outside dome light sensor
BH1750_INSIDE_ADDR = 0x5C # Inside dome light sensor
BH1750_POWER_ON = 0x01
BH1750_CONTINUOUS_HIGH_RES_MODE = 0x10
# VEML6075 UVB sensor configuration
VEML6075_ADDR = 0x10
REG_CONF = 0x00
REG_UVB = 0x09
VEML6075_CONF_100MS = 0x00
# Force sensor configuration
FORCE_SENSOR_A = 'A'
# Motor configuration
MOTOR_PORT_B = 'B'
MOTOR_PORT_C = 'C'
LIGHT_THRESHOLD_DAY_MODE = 4000 # lux threshold for motor activation in day mode
LIGHT_THRESHOLD_LOW_DAY_MODE = 1400 # lux threshold for counter-clockwise motor in day mode
# NeoPixel LED Configuration
LED_PIN = board.D18 # GPIO18 (pin 12) -> your level shifter -> 330–500Ω -> DIN
NUM_LEDS = 120 # Number of LEDs in your strip
LED_ORDER = neopixel.GRB # most WS2812B are GRB, change to RGB if colors look off
LED_BRIGHTNESS = 0.20 # keep modest while testing
LIGHT_THRESHOLD_LED = 1250 # lux threshold for LED control
class LightSensor:
def __init__(self, bus, address, sensor_name):
self.bus = bus
self.address = address
self.sensor_name = sensor_name
self.power_on()
def power_on(self):
"""Power on the sensor"""
try:
self.bus.write_byte(self.address, BH1750_POWER_ON)
time.sleep(0.1)
except Exception as e:
print(f"Error powering on {self.sensor_name}: {e}")
def set_mode(self):
"""Set continuous high resolution mode"""
try:
self.bus.write_byte(self.address, BH1750_CONTINUOUS_HIGH_RES_MODE)
time.sleep(0.1)
except Exception as e:
print(f"Error setting mode for {self.sensor_name}: {e}")
def read_light_level(self):
"""Read light level in lux"""
try:
# Read 2 bytes
data = self.bus.read_i2c_block_data(self.address, BH1750_CONTINUOUS_HIGH_RES_MODE, 2)
# Convert to lux
light_level = (data[0] << 8 | data[1]) / 1.2
return round(light_level, 2)
except Exception as e:
print(f"Error reading {self.sensor_name}: {e}")
return None
class UVBSensor:
def __init__(self, bus, address, sensor_name):
self.bus = bus
self.address = address
self.sensor_name = sensor_name
self.working = False
self.initialize()
def initialize(self):
"""Initialize VEML6075 sensor"""
try:
# Set configuration for 100ms integration time
self.bus.write_word_data(self.address, REG_CONF, VEML6075_CONF_100MS)
time.sleep(0.2)
# Verify configuration
conf = self.bus.read_word_data(self.address, REG_CONF)
if conf == VEML6075_CONF_100MS:
self.working = True
print(f"✅ {self.sensor_name} UVB sensor initialized")
else:
print(f"❌ {self.sensor_name} UVB sensor configuration failed")
except Exception as e:
print(f"❌ Failed to initialize {self.sensor_name} UVB sensor: {e}")
def read_uvb(self):
"""Read UVB level"""
if not self.working:
return None
try:
# Read UVB data (16-bit)
val = self.bus.read_word_data(self.address, REG_UVB)
# Swap bytes (little endian to big endian)
uvb = ((val & 0xFF) << 8) | (val >> 8)
return uvb
except Exception as e:
print(f"Error reading {self.sensor_name} UVB: {e}")
return None
def get_uv_index(self, uvb_value):
"""Convert UVB counts to UV Index (approximate)"""
if uvb_value is None:
return None
# This is an approximate conversion - actual conversion depends on sensor calibration
# VEML6075 typically needs calibration for accurate UV Index
uv_index = uvb_value / 100.0 # Rough approximation
return round(uv_index, 2)
class ForceSensorHandler:
def __init__(self, port, sensor_name, publisher, motor_controller_c=None, led_controller=None):
self.port = port
self.sensor_name = sensor_name
self.publisher = publisher
self.sensor = None
self.TimeCycle = False # Boolean variable to track state
self.day_mode = False # Track day/night mode (True = day, False = night)
self.motor_controller_c = motor_controller_c # Motor C controller for night mode
self.led_controller = led_controller # LED controller for mode change effects
self.initialize()
def initialize(self):
"""Initialize force sensor"""
try:
self.sensor = ForceSensor(self.port)
self.sensor.when_pressed = self.handle_pressed
self.sensor.when_released = self.handle_released
print(f"✅ Force sensor {self.sensor_name} initialized on port {self.port}")
except Exception as e:
print(f"❌ Failed to initialize force sensor {self.sensor_name}: {e}")
def handle_pressed(self, force):
"""Handle force sensor press event - toggle TimeCycle and cycle day/night mode"""
self.TimeCycle = not self.TimeCycle # Toggle between True and False
self.day_mode = not self.day_mode # Toggle between day and night mode
timestamp = datetime.now().isoformat()
self.publisher.publish_sensor_data("timecycle", self.sensor_name, self.TimeCycle, "boolean", timestamp)
mode_str = "day" if self.day_mode else "night"
self.publisher.publish_sensor_data("mode", self.sensor_name, mode_str, "string", timestamp)
print(f"🔘 {self.sensor_name} pressed: TimeCycle = {self.TimeCycle}, Mode = {mode_str}")
# Trigger LED mode change effect
if self.led_controller:
self.led_controller.mode_change_effect(self.day_mode)
# Control motor C based on mode
if self.motor_controller_c:
if not self.day_mode: # Night mode just started
print(f"🌙 Night mode started - Starting motor C continuously")
self.motor_controller_c.start_continuous(100)
else: # Day mode just started
print(f"☀️ Day mode started - Stopping motor C")
self.motor_controller_c.stop()
def handle_released(self, force):
"""Handle force sensor release event - no publishing"""
# No publishing on release, only on press
pass
def get_timecycle_state(self):
"""Get current TimeCycle state"""
return self.TimeCycle
def get_day_mode(self):
"""Get current day/night mode state"""
return self.day_mode
class MotorController:
def __init__(self, port, motor_name):
self.port = port
self.motor_name = motor_name
self.motor = None
self.initialize()
def initialize(self):
"""Initialize motor"""
try:
self.motor = Motor(self.port)
print(f"✅ Motor {self.motor_name} initialized on port {self.port}")
except Exception as e:
print(f"❌ Failed to initialize motor {self.motor_name}: {e}")
def run_for_degrees(self, degrees, speed=100):
"""Run motor for specified degrees at given speed"""
if self.motor:
try:
self.motor.run_for_degrees(degrees, speed)
print(f"🔄 Motor {self.motor_name} running for {degrees} degrees at speed {speed}")
except Exception as e:
print(f"❌ Error running motor {self.motor_name}: {e}")
def run_for_degrees_counterclockwise(self, degrees, speed=100):
"""Run motor counter-clockwise for specified degrees at given speed"""
if self.motor:
try:
self.motor.run_for_degrees(-degrees, speed) # Negative degrees for counter-clockwise
print(f"🔄 Motor {self.motor_name} running counter-clockwise for {degrees} degrees at speed {speed}")
except Exception as e:
print(f"❌ Error running motor {self.motor_name} counter-clockwise: {e}")
def start_continuous(self, speed=100):
"""Start motor continuously at given speed"""
if self.motor:
try:
self.motor.start(speed)
print(f"🔄 Motor {self.motor_name} running continuously at speed {speed}")
except Exception as e:
print(f"❌ Error starting motor {self.motor_name} continuously: {e}")
def stop(self):
"""Stop the motor"""
if self.motor:
try:
self.motor.stop()
print(f"🛑 Motor {self.motor_name} stopped")
except Exception as e:
print(f"❌ Error stopping motor {self.motor_name}: {e}")
def run_for_seconds(self, seconds, speed=100):
"""Run motor for specified seconds at given speed"""
if self.motor:
try:
self.motor.start(speed)
print(f"🔄 Motor {self.motor_name} running for {seconds} seconds at speed {speed}")
# Schedule stop after specified seconds
import threading
def stop_after_delay():
time.sleep(seconds)
self.stop()
threading.Thread(target=stop_after_delay, daemon=True).start()
except Exception as e:
print(f"❌ Error running motor {self.motor_name} for {seconds} seconds: {e}")
class MQTTPublisher:
def __init__(self, broker, port):
self.client = mqtt.Client()
self.broker = broker
self.port = port
self.connected = False
# Set up callbacks
self.client.on_connect = self.on_connect
self.client.on_disconnect = self.on_disconnect
def on_connect(self, client, userdata, flags, rc):
if rc == 0:
print(f"✅ Connected to MQTT broker at {self.broker}:{self.port}")
self.connected = True
else:
print(f"❌ Failed to connect to MQTT broker, return code: {rc}")
def on_disconnect(self, client, userdata, rc):
print("🔌 Disconnected from MQTT broker")
self.connected = False
def connect(self):
"""Connect to MQTT broker"""
try:
self.client.connect(self.broker, self.port, 60)
self.client.loop_start()
except Exception as e:
print(f"❌ Error connecting to MQTT broker: {e}")
def publish_sensor_data(self, sensor_type, sensor_id, value, unit, timestamp):
"""Publish sensor data to MQTT topic"""
if not self.connected:
print("⚠️ Not connected to MQTT broker")
return
topic = f"{BASE_TOPIC}/{sensor_type}/{sensor_id}"
data = {
"sensor_id": sensor_id,
"sensor_type": sensor_type,
"value": value,
"unit": unit,
"timestamp": timestamp
}
message = json.dumps(data)
result = self.client.publish(topic, message)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
# For boolean values, show just True/False without the unit
if unit == "boolean":
print(f"📤 Published: {topic} = {value}")
else:
print(f"📤 Published: {topic} = {value} {unit}")
else:
print(f"❌ Failed to publish to {topic}")
def publish_summary(self, sensor_data, timestamp):
"""Publish summary of all sensor data"""
if not self.connected:
return
summary_data = {
"sensor_count": len(sensor_data),
"sensors": sensor_data,
"timestamp": timestamp
}
summary_topic = f"{BASE_TOPIC}/summary"
message = json.dumps(summary_data)
result = self.client.publish(summary_topic, message)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print(f"📊 Published summary: {len(sensor_data)} sensors")
else:
print(f"❌ Failed to publish summary")
def disconnect(self):
"""Disconnect from MQTT broker"""
self.client.loop_stop()
self.client.disconnect()
class LEDController:
def __init__(self, pin, num_leds, sensor_name="NeoPixel LED"):
self.pin = pin
self.num_leds = num_leds
self.sensor_name = sensor_name
self.current_state = False # Track current LED state
self.initialized = False # Track if NeoPixels were initialized successfully
self.pixels = None
self.initialize()
def initialize(self):
"""Initialize NeoPixels for LED control"""
try:
self.pixels = neopixel.NeoPixel(
self.pin,
self.num_leds,
brightness=LED_BRIGHTNESS,
auto_write=False,
pixel_order=LED_ORDER,
)
# Start with all LEDs off
self.pixels.fill((0, 0, 0))
self.pixels.show()
print(f"✅ NeoPixel LED controller initialized on {self.pin} with {self.num_leds} LEDs")
self.initialized = True
except Exception as e:
print(f"❌ Failed to initialize NeoPixel LED controller: {e}")
self.initialized = False
def wheel(self, pos):
"""Input 0..255 -> (r,g,b) - Color wheel function from whitelight.py"""
if pos < 85:
return (pos * 3, 255 - pos * 3, 0)
if pos < 170:
pos -= 85
return (255 - pos * 3, 0, pos * 3)
pos -= 170
return (0, pos * 3, 255 - pos * 3)
def fade_to_white(self, delay=0.01, steps=50):
"""Gradually fade from 0 to maximum brightness white across all LEDs"""
if not self.initialized:
return
try:
for step in range(steps + 1):
# Calculate brightness from 0 to 1
brightness = step / steps
# Set all LEDs to white with current brightness
white_color = (int(255 * brightness), int(255 * brightness), int(255 * brightness))
self.pixels.fill(white_color)
self.pixels.show()
time.sleep(delay)
except Exception as e:
print(f"❌ Error in fade_to_white: {e}")
def fade_to_off(self, delay=0.01, steps=50):
"""Gradually fade from current brightness to off across all LEDs"""
if not self.initialized:
return
try:
for step in range(steps, -1, -1):
# Calculate brightness from 1 to 0
brightness = step / steps
# Set all LEDs to white with current brightness
white_color = (int(255 * brightness), int(255 * brightness), int(255 * brightness))
self.pixels.fill(white_color)
self.pixels.show()
time.sleep(delay)
except Exception as e:
print(f"❌ Error in fade_to_off: {e}")
def turn_on(self):
"""Turn on the LED strip with smooth fade-in"""
if not self.initialized:
return
try:
if not self.current_state:
print(f"💡 {self.sensor_name} turning ON with fade-in effect")
self.fade_to_white(delay=0.02, steps=50)
self.current_state = True
except Exception as e:
print(f"❌ Error turning on NeoPixel LEDs: {e}")
def turn_off(self):
"""Turn off the LED strip with smooth fade-out"""
if not self.initialized:
return
try:
if self.current_state:
print(f"💡 {self.sensor_name} turning OFF with fade-out effect")
self.fade_to_off(delay=0.02, steps=50)
self.current_state = False
except Exception as e:
print(f"❌ Error turning off NeoPixel LEDs: {e}")
def set_color(self, color, fade=True):
"""Set all LEDs to a specific color with optional fade effect"""
if not self.initialized:
return
try:
if fade:
# Fade to the target color
current_color = self.pixels[0] if self.current_state else (0, 0, 0)
steps = 50
for step in range(steps + 1):
progress = step / steps
r = int(current_color[0] + (color[0] - current_color[0]) * progress)
g = int(current_color[1] + (color[1] - current_color[1]) * progress)
b = int(current_color[2] + (color[2] - current_color[2]) * progress)
self.pixels.fill((r, g, b))
self.pixels.show()
time.sleep(0.01)
else:
# Set color immediately
self.pixels.fill(color)
self.pixels.show()
self.current_state = any(color) # True if any color component is non-zero
except Exception as e:
print(f"❌ Error setting NeoPixel color: {e}")
def rainbow_effect(self, cycles=1, delay=0.05):
"""Create a rainbow effect across all LEDs"""
if not self.initialized:
return
try:
for cycle in range(cycles):
for i in range(self.num_leds):
# Calculate color based on position
color_index = (i * 256 // self.num_leds + cycle * 256 // cycles) % 256
self.pixels[i] = self.wheel(color_index)
self.pixels.show()
time.sleep(delay)
except Exception as e:
print(f"❌ Error in rainbow effect: {e}")
def mode_change_effect(self, is_day_mode):
"""Special effect when switching between day/night modes"""
if not self.initialized:
return
try:
if is_day_mode:
# Day mode: warm yellow/orange effect
print("🌅 Day mode activated - Warm lighting effect")
self.set_color((255, 200, 100), fade=True) # Warm yellow
time.sleep(1)
# Return to normal white if needed
self.set_color((255, 255, 255), fade=True)
else:
# Night mode: cool blue effect then fade to off
print("🌙 Night mode activated - Cool lighting effect")
self.set_color((100, 150, 255), fade=True) # Cool blue
time.sleep(1)
self.fade_to_off(delay=0.02, steps=50)
except Exception as e:
print(f"❌ Error in mode change effect: {e}")
def update_led_state(self, day_mode, light_level):
"""Update LED state based on day mode and light level"""
if not self.initialized:
return
if day_mode:
# Day mode: turn on when light < 1250 lux, turn off when light >= 1250 lux
if light_level is not None:
if light_level < LIGHT_THRESHOLD_LED:
if not self.current_state:
self.turn_on()
else:
if self.current_state:
self.turn_off()
else:
# Night mode: always turn off
if self.current_state:
self.turn_off()
def cleanup(self):
"""Clean up NeoPixels"""
if not self.initialized:
return
try:
# Turn off all LEDs
self.pixels.fill((0, 0, 0))
self.pixels.show()
print(f"🧹 NeoPixel LED controller cleaned up")
except Exception as e:
print(f"❌ Error cleaning up NeoPixel LED controller: {e}")
def main():
print("🚀 Starting SensorShare MQTT Publisher...")
# Clean up any existing GPIO state
try:
GPIO.cleanup()
print("🧹 GPIO cleanup completed")
except Exception as e:
print(f"⚠️ GPIO cleanup warning: {e}")
# Initialize I2C bus
try:
bus = SMBus(I2C_BUS)
print(f"✅ I2C bus {I2C_BUS} initialized")
except Exception as e:
print(f"❌ Failed to initialize I2C bus: {e}")
return
# Initialize light sensors
sensors = {}
try:
sensors['light_outside'] = LightSensor(bus, BH1750_OUTSIDE_ADDR, "Outside Dome")
sensors['light_outside'].set_mode()
print(f"✅ Outside dome light sensor initialized at address 0x{BH1750_OUTSIDE_ADDR:02X}")
sensors['light_inside'] = LightSensor(bus, BH1750_INSIDE_ADDR, "Inside Dome")
sensors['light_inside'].set_mode()
print(f"✅ Inside dome light sensor initialized at address 0x{BH1750_INSIDE_ADDR:02X}")
except Exception as e:
print(f"❌ Failed to initialize light sensors: {e}")
return
# Initialize MQTT publisher
publisher = MQTTPublisher(MQTT_BROKER, MQTT_PORT)
publisher.connect()
# Wait for connection
time.sleep(2)
if not publisher.connected:
print("❌ Could not connect to MQTT broker. Exiting.")
return
# Initialize Motor Controllers
motor_controller = None
motor_controller_c = None
try:
motor_controller = MotorController(MOTOR_PORT_B, "motor_b")
print(f"✅ Motor Controller B initialized")
except Exception as e:
print(f"❌ Failed to initialize Motor Controller B: {e}")
print("⚠️ Motor B functionality will be disabled")
try:
motor_controller_c = MotorController(MOTOR_PORT_C, "motor_c")
print(f"✅ Motor Controller C initialized")
except Exception as e:
print(f"❌ Failed to initialize Motor Controller C: {e}")
print("⚠️ Motor C functionality will be disabled")
# Initialize UVB sensors
try:
sensors['uvb_outside'] = UVBSensor(bus, VEML6075_ADDR, "Outside Dome")
sensors['uvb_inside'] = UVBSensor(bus, VEML6075_ADDR, "Inside Dome")
except Exception as e:
print(f"❌ Failed to initialize UVB sensors: {e}")
# Initialize LED Controller first (before force sensor)
try:
led_controller = LEDController(LED_PIN, NUM_LEDS)
except Exception as e:
print(f"❌ Failed to initialize LED Controller: {e}")
led_controller = None
# Initialize Force Sensor (after LED controller)
try:
sensors['force_a'] = ForceSensorHandler(FORCE_SENSOR_A, "force_a", publisher, motor_controller_c, led_controller)
except Exception as e:
print(f"❌ Failed to initialize Force Sensor: {e}")
sensors['force_a'] = None # Set to None if initialization fails
print("📡 Starting sensor data publishing...")
print("Press Ctrl+C to stop")
# Motor state tracking variable
motor_b_state = False # Track motor B state (True = clockwise activated, False = counterclockwise activated)
try:
while True:
timestamp = datetime.now().isoformat()
sensor_data = {}
# Read light sensors
light_outside = sensors['light_outside'].read_light_level()
if light_outside is not None:
publisher.publish_sensor_data("light", "outside_dome", light_outside, "lux", timestamp)
sensor_data['light_outside'] = light_outside
light_inside = sensors['light_inside'].read_light_level()
if light_inside is not None:
publisher.publish_sensor_data("light", "inside_dome", light_inside, "lux", timestamp)
sensor_data['light_inside'] = light_inside
# Read UVB sensors and convert to UV Index
uvb_outside_raw = sensors['uvb_outside'].read_uvb()
if uvb_outside_raw is not None:
uv_index_outside = sensors['uvb_outside'].get_uv_index(uvb_outside_raw)
publisher.publish_sensor_data("uvb", "outside_dome", uv_index_outside, "UVI", timestamp)
sensor_data['uvb_outside'] = uv_index_outside
uvb_inside_raw = sensors['uvb_inside'].read_uvb()
if uvb_inside_raw is not None:
uv_index_inside = sensors['uvb_inside'].get_uv_index(uvb_inside_raw)
publisher.publish_sensor_data("uvb", "inside_dome", uv_index_inside, "UVI", timestamp)
sensor_data['uvb_inside'] = uv_index_inside
# Get current TimeCycle state and day mode (for summary only)
if sensors.get('force_a') is not None:
timecycle_a = sensors['force_a'].get_timecycle_state()
day_mode = sensors['force_a'].get_day_mode()
sensor_data['force_a_timecycle'] = timecycle_a
sensor_data['force_a_day_mode'] = day_mode
else:
# Default values if force sensor is not available
timecycle_a = False
day_mode = True # Default to day mode
sensor_data['force_a_timecycle'] = timecycle_a
sensor_data['force_a_day_mode'] = day_mode
# Update LED state
if led_controller:
led_controller.update_led_state(day_mode, light_outside)
# Motor control logic for day and night modes
if motor_controller and light_outside is not None:
if day_mode:
if light_outside >= LIGHT_THRESHOLD_DAY_MODE and not motor_b_state:
# Counter-clockwise motor when light >= 4000 lux in day mode
motor_controller.run_for_degrees_counterclockwise(1000, 20) # Rotate 1000 degrees counter-clockwise at 20% speed
motor_b_state = True
print(f"☀️ Day mode: Light level {light_outside} lux >= {LIGHT_THRESHOLD_DAY_MODE} lux - Motor counter-clockwise (ACTIVATED)")
elif light_outside <= LIGHT_THRESHOLD_LOW_DAY_MODE and motor_b_state:
# Clockwise motor when light <= 1400 lux in day mode
motor_controller.run_for_degrees(1000, 20) # Rotate 1000 degrees clockwise at 20% speed
motor_b_state = False
print(f"☀️ Day mode: Light level {light_outside} lux <= {LIGHT_THRESHOLD_LOW_DAY_MODE} lux - Motor clockwise (ACTIVATED)")
else:
# Night mode motor control
if light_outside >= LIGHT_THRESHOLD_DAY_MODE and not motor_b_state:
# Counter-clockwise motor when light >= 19000 lux in night mode
motor_controller.run_for_degrees_counterclockwise(1000, 20) # Rotate 1000 degrees counter-clockwise at 20% speed
motor_b_state = True
print(f"🌙 Night mode: Light level {light_outside} lux >= {LIGHT_THRESHOLD_DAY_MODE} lux - Motor counter-clockwise (ACTIVATED)")
elif light_outside <= LIGHT_THRESHOLD_LOW_DAY_MODE and motor_b_state:
# Clockwise motor when light <= 2000 lux in night mode
motor_controller.run_for_degrees(1000, 20) # Rotate 1000 degrees clockwise at 20% speed
motor_b_state = False
print(f"🌙 Night mode: Light level {light_outside} lux <= {LIGHT_THRESHOLD_LOW_DAY_MODE} lux - Motor clockwise (ACTIVATED)")
# Publish summary
if sensor_data:
publisher.publish_summary(sensor_data, timestamp)
else:
print("⚠️ No sensor data available")
# Wait before next reading
time.sleep(5) # Read every 5 seconds
except KeyboardInterrupt:
print("\n🛑 Stopping SensorShare...")
except Exception as e:
print(f"❌ Error in main loop: {e}")
finally:
# Stop motors before exiting
if motor_controller:
motor_controller.stop()
if motor_controller_c:
motor_controller_c.stop()
# Clean up LED controller
if led_controller:
led_controller.cleanup()
# Final GPIO cleanup
try:
GPIO.cleanup()
print("🧹 Final GPIO cleanup completed")
except Exception as e:
print(f"⚠️ Final GPIO cleanup warning: {e}")
publisher.disconnect()
bus.close()
print("👋 SensorShare stopped")
if __name__ == "__main__":
main()