====Thunder Bolts==== === 1. Group Introduction === Team Members: Tauri Torp, Rehan Ali, Haben Yohannes, Mishqat Maqbool, Thomas Curtis Roles utilised throughout: Programmer, architect, electrician, designer === 2. Initial brainstorming ideas/concepts === Problem: Energy waste with lights being on all the time. Solution: Lights switching on and off automatically depending on the time of day or if motion sensor detects someone walking through the doors. Online powerpoint presentation [[https://utufi-my.sharepoint.com/:p:/g/personal/tautor_utu_fi/ERYLEUsP55xEsYmF-mjSuLYBstzINHCUbmxqcY2E-rxbSw?e=xXVdbp|PowerPoint presentation]] Gas Leak Detection and Prevention project using the Keyestudio IoT Smart Home Kit for ESP32. Problem: Gas Leak Detection and Prevention Gas leaks can occur due to faulty appliances, pipelines, or connections, leading to potentially disastrous consequences. A smart gas leak detection system can alert homeowners and automate safety measures to prevent accidents. Components: 1. Gas Sensor (MQ-2/MQ-5/MQ-9): Detects gas leaks and measures gas concentrations. 2. ESP32 Microcontroller: Processes sensor data, sends alerts, and controls safety measures. 3. Wi-Fi Module: Enables internet connectivity for remote monitoring and alerts. 4. Relay Module: Controls external devices like gas valves or ventilation systems. 5. Buzzer/Siren: Provides local alerts in case of gas leaks. 6. Smartphone App/Email: Receives notifications and alerts. Solution: 1. Gas Leak Detection: The gas sensor detects abnormal gas levels and sends data to the ESP32. 2. Data Processing: The ESP32 processes the sensor data and determines if a gas leak is present. 3. Alert System: If a gas leak is detected, the ESP32 sends notifications to the homeowner's smartphone or email. 4. Automated Safety Measures: The ESP32 can trigger the relay module to shut off gas valves or activate ventilation systems. Project Steps: 1. Hardware Setup: Connect the gas sensor, relay module, and buzzer/siren to the ESP32. 2. Software Setup: Write code for the ESP32 to read sensor data, send alerts, and control safety measures. 3. Wi-Fi Configuration: Connect the ESP32 to the internet for remote monitoring. 4. Testing and Calibration: Test the system and calibrate the gas sensor for accurate detection. Benefits: 1. Enhanced Safety: Early detection and alerts prevent accidents. 2. Peace of Mind: Homeowners can monitor their homes remotely. 3. Prevention: Automated safety measures reduce risk. === 3. Day 2 Presentation slides === https://utufi-my.sharepoint.com/:p:/g/personal/tjcurt_utu_fi/EajJ4qdvY-tKmBoB6I-NMPcBsNiMwygQtwIO_VnHju2J7w?e=dEHmlX === 3. Finalised Idea, description & Functions === Solutions completed * Motion detector can automatically stop tram before collision - activates RGB light in red as brake light, buzzer as warning sound * Button for emergency stop, triggers alert system * Passenger information system via display- cycling through info e.g. temp, next tramp stop, alert (if sudden stop) Current solutions being developed: * Fan control – fan activates at certain temperature BUT turns off if doors open * * Mostly implemented * Lights turn on first as dim, then later gradually brighter * * Unimplemented * Data sent to website e.g. log of actions, passenger numbers, alerts, temperature on tram * * Implementation started Possible additional solutions: * Smoking detector warns if any smoke or toxic gas is detected * Motion detector detects how many people used the tram === 4. SUSAF Analysis === See final powerpoint slide on susaf === 5. Power meter measurements === Attempted but incomplete due to time constraints. Did achieve some results === 6. Future Improvements === See final powerpoint slide on future improvements and reflection === 7. Final Day Presentation Slides === https://utufi-my.sharepoint.com/:p:/g/personal/tautor_utu_fi/ERYLEUsP55xEsYmF-mjSuLYBstzINHCUbmxqcY2E-rxbSw?e=M3qavC === 8. Final Code === main.py import time import website_manager as wm import display_manager as dm import combine_btn_motion as bm def test_func(): pass def main(): alert_state = False alert_length = 0 rolling_timer = time.time() alert_timer = time.time() print("Starting main loop") dm.write_message("Tram start!") detections = 0 while True: current_time = time.time() if alert_state == False: print("no alert") if current_time - rolling_timer > 5: print("Display: New rolling message") rolling_timer += 5 dm.rolling_message() #check for danger alert_state, alert_length = bm.detect_alert_state() alert_timer = current_time if alert_state == True: detections += 1 if detections < 3: alert_state = False else: detections = 0 else: print("alert!") if current_time - alert_timer < alert_length: dm.force_message("Emergency stop!") bm.activate_brake_and_warning() bm.clear_brake_light() bm.stop_buzzer() else: alert_state = False wm.force_message("Tram shutdown!") #trigger buzzer #write message on lcd #send message to website def trigger_alert(alert_message : str): wm.alert_website(alert_message) dm.force_message(alert_message) test_func() print() main() display_manager.py from time import sleep_ms, ticks_ms from machine import I2C, Pin from i2c_lcd import I2cLcd DEFAULT_I2C_ADDR = 0x27 i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000) lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16) messages = ["Out temp: 22C\nIn temp: 20C", "Next Stop: Kauppatori C"] message_counter = 0 # Max characters around 40 # Message can be split using \n def write_message(message : str): lcd.clear() lcd.move_to(0,0) if "\n" in message: message_sections = message.split("\n") lcd.putstr(message_sections[0]) lcd.move_to(0,1) lcd.putstr(message_sections[1]) else: lcd.putstr(message) def force_message(message : str): write_message(message) def rolling_message(): global message_counter write_message(messages[message_counter]) message_counter += 1 if message_counter >= len(messages): message_counter = 0 # The following line of code should be tested # using the REPL: # 1. To print a string to the LCD: # lcd.putstr('Hello world') # 2. To clear the display: #lcd.clear() # 3. To control the cursor position: # lcd.move_to(2, 1) # 4. To show the cursor: # lcd.show_cursor() # 5. To hide the cursor: #lcd.hide_cursor() # 6. To set the cursor to blink: #lcd.blink_cursor_on() # 7. To stop the cursor on blinking: #lcd.blink_cursor_off() # 8. To hide the currently displayed character: #lcd.display_off() # 9. To show the currently hidden character: #lcd.display_on() # 10. To turn off the backlight: #lcd.backlight_off() # 11. To turn ON the backlight: #lcd.backlight_on() # 12. To print a single character: #lcd.putchar('x') # 13. To print a custom character: #happy_face = bytearray([0x00, 0x0A, 0x00, 0x04, 0x00, 0x11, 0x0E, 0x00]) #lcd.custom_char(0, happy_face) #lcd.putchar(chr(0)) combine_btn_motion.py from time import sleep_ms, ticks_ms from machine import Pin, PWM import neopixel import time # Initialize PIR sensor (motion detector) PIR = Pin(19, Pin.IN) # Initialize Button (Emergency Stop) on Pin 16 button1 = Pin(16, Pin.IN, Pin.PULL_UP) # Initialize RGB LED (Brake light) pin = Pin(14, Pin.OUT) np = neopixel.NeoPixel(pin, 4) # RGB colors (Red for brake light) brightness = 100 colors = [ [brightness, 0, 0], # Red (Brake light) [0, brightness, 0], # Green [0, 0, brightness], # Blue [brightness, brightness, brightness], # White [0, 0, 0] # Off ] # Initialize Buzzer buzzer = PWM(Pin(25)) # Function to activate brake light (Red) and buzzer def activate_brake_and_warning(): # Turn on red brake light for i in range(4): np[i] = colors[0] # Set all LEDs to red (brake light) np.write() # Activate buzzer warning sound buzzer.duty(1000) buzzer.freq(294) # Start sound time.sleep(0.25) buzzer.freq(440) time.sleep(0.25) buzzer.freq(392) time.sleep(0.25) buzzer.freq(532) time.sleep(0.25) buzzer.duty(0) # Stop sound # Function to clear brake light (turn off LEDs) def clear_brake_light(): for i in range(4): np[i] = colors[4] # Turn off all LEDs np.write() # Initialize counter and last PIR value count = 0 last_value = 0 # Main loop to detect motion or button press and activate brake and buzzer def detect_alert_state(): # Read button state (Emergency Stop) btnVal1 = button1.value() # Button press state # Check for motion detection or button press if PIR.value() == 1 and last_value == 0: # Motion detected (new motion event) print(f"Motion detected! Count: {count + 1}") count += 1 # Increment people count # Activate brake light and buzzer return true, 5 elif btnVal1 == 0: # Emergency stop button pressed (active low) print("Emergency Stop Activated!") # Activate brake light and buzzer return true, 5 else: last_value = PIR.value() # Update PIR last value clear_brake_light() # Turn off brake light if no motion and no button press time.sleep(0.1) # Small delay for debounce light_control.py mport time import math from machine import Pin, PWM PWM_PIN = 12 PWM_FREQ = 10000 # PWM frequency in Hz MIN_BRIGHTNESS = 0 MAX_BRIGHTNESS = 1023 # Time-based irradiance simulation parameters DAWN_HOUR = 6 SUNRISE_HOUR = 7 NOON_HOUR = 12 SUNSET_HOUR = 19 DUSK_HOUR = 20 led_pwm = PWM(Pin(PWM_PIN, Pin.OUT), PWM_FREQ) def get_current_hour(): cycle_minutes = (time.time() % (24 * 60)) current_hour = (cycle_minutes / 60) % 24 return current_hour def simulate_irradiance(): hour = get_current_hour() # Night (before dawn or after dusk) if hour < DAWN_HOUR or hour > DUSK_HOUR: return 0.0 # Dawn (gradual increase from dark to daylight) elif DAWN_HOUR <= hour < SUNRISE_HOUR: dawn_progress = (hour - DAWN_HOUR) / (SUNRISE_HOUR - DAWN_HOUR) return dawn_progress * 0.5 # Morning (increasing to noon) elif SUNRISE_HOUR <= hour < NOON_HOUR: morning_progress = (hour - SUNRISE_HOUR) / (NOON_HOUR - SUNRISE_HOUR) return 0.5 + (morning_progress * 0.5) # Afternoon (decreasing from noon) elif NOON_HOUR <= hour < SUNSET_HOUR: afternoon_progress = (hour - NOON_HOUR) / (SUNSET_HOUR - NOON_HOUR) return 1.0 - (afternoon_progress * 0.5) # Dusk (gradual decrease from daylight to dark) else: dusk_progress = (hour - SUNSET_HOUR) / (DUSK_HOUR - SUNSET_HOUR) return 0.5 - (dusk_progress * 0.5) def calculate_light_brightness(irradiance): # Threshold above which no artificial light is needed DAY_THRESHOLD = 0.7 if irradiance >= DAY_THRESHOLD: return 0 else: brightness_factor = 1.0 - (irradiance / DAY_THRESHOLD) brightness_factor = math.pow(brightness_factor, 0.8) brightness = int(brightness_factor * MAX_BRIGHTNESS) return max(MIN_BRIGHTNESS, min(brightness, MAX_BRIGHTNESS)) def print_debug_info(irradiance, brightness): hour = get_current_hour() print(f"Time: {hour:.2f}h | Irradiance: {irradiance:.2f} | Light brightness: {brightness} ({brightness/MAX_BRIGHTNESS*100:.1f}%)") try: print("Smart Tram Light Control - Demo Mode") print("------------------------------------") print("Light will adjust automatically based on simulated time of day") print("Press Ctrl+C to exit") print() # For demo purposes, we'll speed up time demo_time_factor = 60 # 1 minute = 1 hour last_time = time.time() while True: irradiance = simulate_irradiance() brightness = calculate_light_brightness(irradiance) led_pwm.duty(brightness) print_debug_info(irradiance, brightness) time.sleep(1) current_time = time.time() time_diff = current_time - last_time last_time = current_time time.sleep_ms(int(time_diff * demo_time_factor * 1000)) except KeyboardInterrupt: print("\nExiting program") finally: led_pwm.deinit() print("PWM resource released") fan.py from machine import Pin, PWM import machine import time import dht # Initialize Fan Control Pins INA = PWM(Pin(27, Pin.OUT), 10000) # INA corresponds to IN+ INB = PWM(Pin(18, Pin.OUT), 10000) # INB corresponds to IN- # Initialize Button for door control button1 = Pin(26, Pin.IN, Pin.PULL_UP) # Button for opening/closing the door # Initialize PWM for Servo (Door control) pwm = PWM(Pin(5)) pwm.freq(50) #Associate DHT11 with Pin(17). DHT = dht.DHT11(machine.Pin(17)) # RGB colors (Red for brake light, for indication when door is open) # Fan control functions def activate_fan(): INA.duty(0) # Fan control forward direction INB.duty(700) # Set duty cycle to rotate the fan def deactivate_fan(): INA.duty(0) # Stop fan INB.duty(0) # Stop fan # Function to simulate door control with PWM (servo motor) def control_door(open_door): if open_door: pwm.duty(77) # Door open (90 degrees) print("Door opened.") else: pwm.duty(25) # Door closed (0 degrees) print("Door closed.") # Main loop to toggle door and control fan door_open = False # Initially, door is closed while True: btnVal1 = button1.value() # Read the button value (active low) DHT.measure() if btnVal1 == 0: # Button pressed (active low) time.sleep(0.01) # Delay to debounce the button while btnVal1 == 0: btnVal1 = button1.value() # Wait for button release door_open = not door_open # Toggle door state # Control the door based on the state control_door(door_open) # Control the fan based on door state if door_open: deactivate_fan() # Turn off fan if door is open else: activate_fan() # Turn on fan if door is closed time.sleep(0.1) # Short delay to prevent rapid toggling testwebsite.html