meta data for this page
  •  

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 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

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

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

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h3>Daily passenger count:</h3><!--Motion detector detects how many people used the tram-->
<h3>Tram is moving: [yes/no]</h3> <!--Motion detector can automatically stop tram before collision - activates RGB light in red as brake light, buzzer as warning sound-->
<h3>Button for emergency stop: [on/off]</h3> <!--Button for emergency stop, triggers alert system-->
<h3>Fan: [on/off]</h3> <!--Fan control – fan activates at certain temperature (data also from internet?) BUT turns off if doors open-->
<h3>Lights: [dim/brighter/brightest]</h3> <!--Lights turn on first as dim, then later gradually brighter-->
<h3>Display information: [temperature outside/time until tram arrives at the next stop] </h3> <!--Passenger information system via display- cycling through info e.g. temp, next tramp stop, alert (if sudden stop)-->
<h3></h3> <!--Data sent to website e.g. log of actions, passenger numbers, alerts, temperature on tram-->
<script>
  // Refresh the page every 1000 milliseconds (1 second)
  setInterval(function () {
    location.reload();
  }, 1000);
</script>
</body>
</html>