Arturs Mikelsons
Published © GPL3+

IoT Mailbox

A mailbox that would take away the hassle of checking it physically every time. Now you can just check your phone and know if there is mail.

AdvancedFull instructions providedOver 7 days250
IoT Mailbox

Things used in this project

Hardware components

Raspberry Pi Pico W
Raspberry Pi Pico W
Main microcontroller used in this project
×1
IR-08H
×1
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1
Buzzer
Buzzer
×1
Gravity:Digital Push Button (Yellow)
DFRobot Gravity:Digital Push Button (Yellow)
×1
Breadboard (generic)
Breadboard (generic)
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
×20
Double sided tape
×1
Mail Box
×1
USB-A to Mini-USB Cable
USB-A to Mini-USB Cable
×1

Software apps and online services

Blynk
Blynk
Thonny
Telegram

Hand tools and fabrication machines

Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Schematics

The main schema

Code

The main python file

Python
import machine
import time
import network
import urequests as requests
import ujson as json
import os
from BlynkLib import Blynk

# Define constants
SSID = '***'
PASSWORD = '***'
TELEGRAM_BOT_TOKEN = '***'
TELEGRAM_CHAT_ID = '***'
# Your Blynk Auth Token
BLYNK_AUTH = '***'

RED_LED_PIN = 16  # GPIO 16 for Red LED
GREEN_LED_PIN = 17  # GPIO 17 for Green LED
BUTTON_PIN = 18    # GPIO 18 for Reset Button
IR_SENSOR_PIN = 1  # GPIO 1 for IR Sensor
DEBOUNCE_DELAY = 0.05  # 50 ms debounce delay
MEASUREMENT_INTERVAL = 0.05  # seconds, increased polling frequency
COOLDOWN_PERIOD = 1  # seconds
DATA_FILE = 'mail_log.json'

# Initialize Wi-Fi
def connect_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    
    max_attempts = 10
    attempt = 0
    while not wlan.isconnected() and attempt < max_attempts:
        attempt += 1
        print('Attempting to connect to Wi-Fi...')
        time.sleep(1)
    
    if wlan.isconnected():
        print('Connected to Wi-Fi')
        return True
    else:
        print('Failed to connect to Wi-Fi')
        return False

# Connect to Wi-Fi
if not connect_wifi(SSID, PASSWORD):
    raise RuntimeError("Failed to connect to Wi-Fi")

# Initialize pins
red_led = machine.Pin(RED_LED_PIN, machine.Pin.OUT)
green_led = machine.Pin(GREEN_LED_PIN, machine.Pin.OUT)
button = machine.Pin(BUTTON_PIN, machine.Pin.IN, machine.Pin.PULL_UP)
ir_sensor = machine.Pin(IR_SENSOR_PIN, machine.Pin.IN)

# Initialize Blynk
blynk = Blynk(BLYNK_AUTH)

# Function to send message to Telegram
def send_to_telegram(message):
    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    headers = {'Content-Type': 'application/json'}
    payload = {
        'chat_id': TELEGRAM_CHAT_ID,
        'text': message
    }
    try:
        response = requests.post(url, headers=headers, data=json.dumps(payload))
        if response.status_code == 200:
            print('Message sent to Telegram successfully')
        else:
            print(f'Failed to send message to Telegram. Status code: {response.status_code}')
    except Exception as e:
        print(f'Exception while sending message to Telegram: {e}')

# Initialize JSON file
def init_json():
    if not DATA_FILE in os.listdir():
        with open(DATA_FILE, 'w') as f:
            json.dump([], f)

# Log mail data to the JSON file
def log_to_json(timestamp):
    with open(DATA_FILE, 'r+') as f:
        data = json.load(f)
        data.append(timestamp)
        f.seek(0)
        json.dump(data, f)

# Retrieve data from the JSON file
def retrieve_data():
    if DATA_FILE in os.listdir():
        with open(DATA_FILE, 'r') as f:
            data = json.load(f)
            return data
    else:
        return []

# Initialize JSON file
init_json()

# Initialize variables
previous_mail_status = False
last_mail_time = 0
mail_count = 0  # Initialize mail count

# Function to update LED status
def update_leds(mail_detected):
    if mail_detected:
        red_led.value(0)  # Turn off red LED
        green_led.value(1)  # Turn on green LED
    else:
        red_led.value(1)  # Turn on red LED
        green_led.value(0)  # Turn off green LED

# Function to get status from IR sensor with debouncing
def get_mail_status():
    mail_detected = ir_sensor.value() == 0  # Initial read
    time.sleep(DEBOUNCE_DELAY)  # Wait for debounce delay
    return mail_detected and (ir_sensor.value() == 0)  # Confirm detection

# Function to reset the mail counter
def reset_mail_count(pin):
    global mail_count
    mail_count = 0
    blynk.virtual_write(4, mail_count)  # Update mail count on Blynk (V4)
    print("Mail counter reset.")

# Attach interrupt to the button pin for rising edge (button release)
button.irq(trigger=machine.Pin.IRQ_RISING, handler=reset_mail_count)

# Function to fetch current time from API
def get_current_time():
    try:
        response = requests.get('http://worldtimeapi.org/api/ip')
        if response.status_code == 200:
            data = response.json()
            return data['datetime']
        else:
            print('Failed to get time from API')
            return None
    except Exception as e:
        print('Exception while getting time:', e)
        return None

# Function to log the mail count with timestamp
def log_mail():
    global mail_count
    timestamp = get_current_time()
    if timestamp:
        print(f"Mail detected at {timestamp}")
        send_to_telegram(f"Mail detected at {timestamp}")
        log_to_json(timestamp)
        mail_count += 1

        blynk.virtual_write(4, mail_count)  # Update mail count on Blynk (V4)
        # Use virtual pin to indicate new mail (V3)
        blynk.virtual_write(3, f"New mail detected: {mail_count} at {timestamp}")

# Function to send mail count to Blynk every minute
def send_mail_count():
    blynk.virtual_write(5, mail_count)  # Update mail count on Blynk (V5)
    print(f"Mail count sent to Blynk: {mail_count}")

while True:
    try:
        blynk.run()  # Run Blynk to keep connection alive
        
        mail_detected = get_mail_status()  # Get mail status from IR sensor
        current_time = time.time()
        
        if mail_detected and not previous_mail_status:
            print(f"Mail detected at {current_time}")
            if current_time - last_mail_time >= COOLDOWN_PERIOD:
                log_mail()
                last_mail_time = current_time
            else:
                print(f"Cooldown period active. Time since last mail: {current_time - last_mail_time} seconds")
        
        update_leds(mail_detected)
        previous_mail_status = mail_detected
        
        # Send mail count to Blynk every minute
        if int(current_time) % 60 == 0:
            send_mail_count()
        
        time.sleep(MEASUREMENT_INTERVAL)
    except Exception as e:
        print('Exception in main loop:', e)
        time.sleep(1)  # Delay before retrying

Credits

Arturs Mikelsons

Arturs Mikelsons

1 project • 0 followers

Comments