ESP32 MicroPython управление на лампа с PIR и Wi-Fi Свързаност

ESP32 Управление на Лампа с MicroPython и Wi-Fi Свързаност

Въведение

Проектът представя интелигентна система за управление на лампа, базирана на ESP32 микроконтролер, използващ езика за програмиране MicroPython. Системата предоставя възможност за контрол чрез бутон, сензор за движение (PIR), и уеб интерфейс, достъпен чрез Wi-Fi връзка. Допълнителните функции включват показване на температура и влажност на въздуха посредством DHT22 сензор и използването на I2C LCD дисплей за визуализация на данните. В тази статия ще разгледаме основните компоненти и функционалности на проекта.

Конфигурация на мрежата и свързаност (boot.py)

Файлът boot.py е отговорен за първоначалната конфигурация на Wi-Fi свързаността на устройството. В него се използва библиотеката network, за да се установи връзка с безжична мрежа. Основните стъпки включват активиране на Wi-Fi модула, сканиране на наличните мрежи и опит за свързване към зададената Wi-Fi мрежа. В случай на успешна връзка, в конзолата се отпечатва IP адресът на устройството, което позволява последващото му използване в мрежата.

Кодът за boot.py:

import network
from time import sleep

import esp
esp.osdebug(None)

sta_if = network.WLAN(network.STA_IF);
sta_if.active(True)
sta_if.scan()                             # Сканиране на налични WiFi мрежи

try:
    sta_if.connect("SSID", "PASSWORD") # Въведете вашето Име и Парола на WiFi
except OSError as error:
    print(error, ": The wifi as already connect")
  
while sta_if.isconnected() == False:
    print("unible to connrct to WiFi")
    sleep(0.5)
  

if sta_if.isconnected() == True:	# Check for successful connection
    print("connect sucsess")
    print("IP address = " , sta_if.ifconfig()[0])
else:
    print("WiFi is not connrct")

import webrepl
webrepl.start()

Основен код и функционалности (main.py)

Файлът main.py съдържа основната логика на устройството и използва множество библиотеки за управление на различните периферии. Кодът е структуриран в две основни части: функция за уеб сървър, която се изпълнява на второ ядро (Core 1), и основната логика на устройството, която се изпълнява на основното ядро (Core 0).

Кодът за main.py:

import _thread
import uasyncio as asyncio
from time import sleep
from machine import Pin, SoftI2C
from ds3231 import DS3231
from lcd_api import LcdApi
from i2c_lcd import I2cLcd
import dht

import network
try:
  import usocket as socket
except:
  import socket

import gc
gc.collect()

#====================================================================

Led_Status_Core_1 = Pin(2, Pin.OUT)
Led_Status_Core_1.off()

button = Pin(15, Pin.IN)
button_push_count = 0

web_switch = 0

PIR = Pin(26, Pin.IN, Pin.PULL_DOWN)
PIR_active = 0
PIR_timer_stop = 10  # задаване на времето в което PIR проверява дали има актовност в стаята!

relay = Pin(13, Pin.OUT)
relay.off()
relay_logic_state = 0

i2c = SoftI2C(sda=Pin(21), scl=Pin(22), freq=400000)

dht_inicialize = dht.DHT22(Pin(25))
temperature = 0.0
humidity = 0.0

I2C_ADDR = 0x27
totalRows = 4	# брой редове на LCD
totalColumns = 20	# дължина на реда на LCD

lcd = I2cLcd(i2c, I2C_ADDR, totalRows, totalColumns)

ds3231_year = ""
ds3231_time = ""

#print("IP address = " , sta_if.ifconfig()[0])

#====================================================================

#-----/ Core 1 function\------
def Core_1_function():
    
    def web_page():
        global button_push_count
        global PIR_active
        global web_switch
        
        if button_push_count == 1:
            gpio_state="Light ON"

        if button_push_count == 0:
            gpio_state="Light OFF"

        if button_push_count == 2:
            gpio_state="PIR activate"
                
        html = """
<html>
   <head>
      <title>Hall Light Control</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta http-equiv="refresh" content="3;url=http://192.168.100.132">
      <meta charset="UTF-8">
      <link rel="icon" href="data:,">
      <style>
         html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
         h1{color: #0F3376; padding: 2vh;}
         p{font-size: 1.5rem;}
         .button{display: inline-block; background-color: cyan; border: none; 
           border-radius: 4px; color: black; padding: 16px 40px;
           text-decoration: none;
           font-size: 30px;
           margin: 2px;
           cursor: pointer;}
      </style>
   </head>
   <body>
      <h1>Light control in the living room</h1>
      <p>Дата : """ + ds3231_year + """ Час : """ + ds3231_time + """</p><br>
      
      <p>
         <a href="/?switch"><button class="button">Switch Function</button></a>
         <br>
         <strong>""" + gpio_state + """</strong>
      </p><hr><br>
      <p>Температура : """ +  str(temperature) + """ *C</p>
      <p>Влажност : """ + str(humidity) + """ %</p>
   </body>
</html>
"""
        return html
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 80))
    s.listen(5)
    
    while True:
        global button_push_count
        global web_switch
        
        conn, addr = s.accept()
        #print('Got a connection from %s' % str(addr))
        request = conn.recv(1024)
        request = str(request)
        #print('Content = %s' % request)
  
        #-----// relay 1 \\------
        switch = request.find('/?switch')
  
        if switch == 6:
            #print('switch')
            web_switch = 1

        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        conn.close()


#-----/ Core 1 START\------
_thread.start_new_thread(Core_1_function, ())        
        
#====================================================================

#-----/ Core 0 \------
while True:
    
    #==================================   СТАТУС Core-0 (prime)
    async def status_led_Core_1():
        while True:
            Led_Status_Core_1.on()
            await asyncio.sleep_ms(500)
            Led_Status_Core_1.off()
            
            await asyncio.sleep_ms(500)
            
    #==================================   READ DHT22 (temp,hum)
    async def dht_read():
        global temperature
        global humidity
        
        while True:
            dht_inicialize.measure()
            
            temperature = dht_inicialize.temperature()
            temperature = round(temperature , 1)
            
            humidity = dht_inicialize.humidity()
            humidity = round(humidity , 1)
    
            #print("temp = " , temperature)
            #print("hum = " , humidity)
            
            await asyncio.sleep(2)
            
    #==================================   DS3231 RTC
    async def read_DS3231():
        while True:
            ds = DS3231(i2c) # изчитане на показанията от DS3231 RTC
            #print(ds.datetime())
            global ds3231_year
            global ds3231_time
            
            ds3231_year = str(ds.datetime()[2]) + ":" + str(ds.datetime()[1]) + ":" + str(ds.datetime()[0])
            """ds3231_time = str(ds.datetime()[4]) + ":" + str(ds.datetime()[5]) + ":" + str(ds.datetime()[6])"""
            ds3231_time = str(ds.datetime()[4]) + ":" + str(ds.datetime()[5])
            #print(ds3231_year, ds3231_time)
            
            await asyncio.sleep_ms(10000)
            
    #==================================   LCD WRITE TIME   
    async def write_time_to_LCD():
        while True:
            lcd.move_to(0 , 0)
            lcd.putstr(ds3231_year + "/" + ds3231_time)
            
            await asyncio.sleep(30)
            lcd.clear_line(0)	# изтрива само първи ред
            
    #==================================   LCD WRITE temperature
    async def write_temp_to_LCD():
        while True:
            lcd.move_to(0 , 2)
            lcd.putstr("Temp = " + str(temperature) + " *C")
            
            await asyncio.sleep(40)
            lcd.clear_line(2)	# изтрива трети ред
            
    #==================================   LCD WRITE humidity
    async def write_hum_to_LCD():
        while True:
            lcd.move_to(0 , 3)
            lcd.putstr("Humi = " + str(humidity) + " %")
            #print(humidity)
            
            await asyncio.sleep(45)
            lcd.clear_line(3)	# изтрива четвърти ред
            
    #==================================   LCD WRITE управление от BUTTON, PIR or RELAY off
    async def write_control_to_LCD():
        
        global relay_logic_state
        global PIR_active
        
        while True:
            
            if relay_logic_state == 0:
                lcd.move_to(0 , 1)
                lcd.putstr("   Lamp OFF")
                
            if relay_logic_state == 1:
                lcd.move_to(0 , 1)
                lcd.putstr("   Lamp ON ")
                
            if PIR_active == 1:
                lcd.move_to(0 , 1)
                lcd.putstr("  PIR-Activ")
            
            await asyncio.sleep(1.7)
            lcd.clear_line(1)  # изтрива втори ред
            
    #==================================   БУТОН on/off    
    async def button_read():
        
        global relay_logic_state
        global PIR_active
        global button_push_count
        global web_switch
            
        while True:
                       
            button_logic_state = button.value()
            
            if button_logic_state == 0 or web_switch == 1:
                button_push_count += 1
                #print("cout = " , button_push_count)
                
                if button_push_count == 1:
                    relay_logic_state = 1
                    PIR_active = 0	# деактивиране на PIR
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
                    
                if button_push_count == 2:
                    # relay_logic_state = 0  # може би ако го деактивирам от тук ще при всеки цикъл ще казва че релето е OFF
                    PIR_active = 1	# активиране на PIR
                    #button_push_count = 0
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
                    
                if button_push_count == 3:
                    relay_logic_state = 0
                    PIR_active = 0	# деактивиране на PIR
                    button_push_count = 0
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
            
            await asyncio.sleep_ms(100)
            
    #==================================   PIR check is on/off
    def check_pir():
        return PIR.value()
            
    #==================================   PIR on/off relay
    async def PIR_control():
        global PIR_active
        global PIR_timer_stop
        global relay_logic_state
        
        while True:

            if PIR_active == 1:
                
                if check_pir() == 1:
                    i = 0
                    
                    while i <= PIR_timer_stop:
                        #print("i = " , i)
                        relay_logic_state = 1
                        
                        if check_pir() == 1:
                            i = 0
                        await asyncio.sleep(1)
                        i += 1
                        
                        if PIR_active == 0:		# терминира цикъла от бутона
                            relay_logic_state = 0
                            break
                    
                if check_pir() == 0:
                    relay_logic_state = 0
                        
            await asyncio.sleep_ms(100)
            
    #==================================   RELAY on/off     
    async def relay_state():
        global relay_logic_state
        
        while True:
            if relay_logic_state == 1:
                relay.on()
                #print("lamp on")
            if relay_logic_state == 0:
                relay.off()
                #print("lamp off")
                
            await asyncio.sleep_ms(500)
            
    #==================================    Стартиране на tasks...        
    async def main():
        task1 = asyncio.create_task(read_DS3231())
        task2 = asyncio.create_task(write_time_to_LCD())
        task3 = asyncio.create_task(status_led_Core_1())
        task4 = asyncio.create_task(button_read())
        task5 = asyncio.create_task(relay_state())
        task6 = asyncio.create_task(dht_read())
        task6 = asyncio.create_task(write_temp_to_LCD())
        task7 = asyncio.create_task(write_hum_to_LCD())
        task8 = asyncio.create_task(PIR_control())
        task9 = asyncio.create_task(write_control_to_LCD())
        
        await asyncio.gather(task1, task2, task3, task4, task5, task6, task7, task8, task9)
    
    asyncio.run(main())

Импортиране на библитеки

Тази част от кода включва импортирането на различни библиотеки и модули, които осигуряват функционалността, необходима за управление на проекта с ESP32. Ето какво прави всяка от тях:

import _thread
import uasyncio as asyncio
from time import sleep
from machine import Pin, SoftI2C
from ds3231 import DS3231
from lcd_api import LcdApi
from i2c_lcd import I2cLcd
import dht

import network
try:
  import usocket as socket
except:
  import socket

import gc
gc.collect()
  1. _thread: Този модул позволява създаването на нови потоци за паралелно изпълнение на функции. В случая се използва за стартиране на функция на второто ядро на ESP32.
  2. uasyncio като asyncio: Модулът uasyncio е MicroPython версия на популярната библиотека asyncio от Python, която осигурява асинхронно програмиране. Тя позволява изпълнението на задачи едновременно, като същевременно се използва малко памет и процесорни ресурси.
  3. from time import sleep: Функцията sleep от модула time позволява паузирането на изпълнението на програмата за определен период от време.
  4. from machine import Pin, SoftI2C: Модулът machine предоставя функции за работа с хардуерни компоненти. Pin се използва за контрол на GPIO пиновете на ESP32, а SoftI2C е софтуерна имплементация на I2C комуникационния протокол.
  5. from ds3231 import DS3231: Този модул позволява комуникацията с DS3231 – модул за реално време (RTC), който осигурява точни часови данни. Може да свалите боблиотеката от тук.
  6. from lcd_api import LcdApi и from i2c_lcd import I2cLcd: Тези модули осигуряват интерфейс за управление на LCD дисплеи чрез I2C протокол. LcdApi е основният API за управление на дисплея, докато I2cLcd е специфична имплементация за работа с I2C LCD дисплеи. Тези библиотеки може да свалите от тук.
  7. import dht: Този модул се използва за работа със сензори за температура и влажност DHT (например DHT22), които предоставят данни за околната среда.
  8. import network: Модулът network позволява на ESP32 да се свързва с Wi-Fi мрежи и да осъществява мрежова комуникация.
  9. import usocket като socket: usocket е MicroPython версия на стандартния socket модул, който осигурява възможности за мрежова комуникация чрез създаване на TCP/UDP връзки.
  10. import gc и gc.collect(): Модулът gc (garbage collector) се използва за управление на паметта, като функцията collect() се използва за освобождаване на неизползвана памет, за да се предотврати изчерпването ѝ и да се поддържа плавното изпълнение на програмата.

Инициализация и конфигурация на хардуера

Тази част от кода инициализира и конфигурира различни хардуерни компоненти на ESP32 микроконтролера, които ще се използват в проекта за управление на лампа чрез PIR сензор, бутон и Wi-Fi свързаност. Запознайте се с ESP32 платформата в нашата страница ESP32 Wroom 30/38 pins.

Ето подробно обяснение на всеки ред от кода:

Led_Status_Core_1 = Pin(2, Pin.OUT)
Led_Status_Core_1.off()

button = Pin(15, Pin.IN)
button_push_count = 0

web_switch = 0

PIR = Pin(26, Pin.IN, Pin.PULL_DOWN)
PIR_active = 0
PIR_timer_stop = 10  # задаване на времето в което PIR проверява дали има актовност в стаята!

relay = Pin(13, Pin.OUT)
relay.off()
relay_logic_state = 0

i2c = SoftI2C(sda=Pin(21), scl=Pin(22), freq=400000)

dht_inicialize = dht.DHT22(Pin(25))
temperature = 0.0
humidity = 0.0

I2C_ADDR = 0x27
totalRows = 4	# брой редове на LCD
totalColumns = 20	# дължина на реда на LCD

lcd = I2cLcd(i2c, I2C_ADDR, totalRows, totalColumns)

ds3231_year = ""
ds3231_time = ""
  1. Led_Status_Core_1 = Pin(2, Pin.OUT)
    Този ред създава обект Pin, свързан с GPIO 2 на ESP32, който е настроен като изход (Pin.OUT). Това ще бъде използвано за управление на LED, който показва статуса на Core 1.
  2. Led_Status_Core_1.off()
    Този ред изключва LED-а, като задава ниско ниво на GPIO 2, което спира подаването на напрежение към него.
  3. button = Pin(15, Pin.IN)
    Тук се създава обект Pin, свързан с GPIO 15, който е настроен като вход (Pin.IN). Този пин ще бъде използван за четене на състоянието на бутон, свързан към него.
  4. button_push_count = 0
    Този ред инициализира брояч на натискания на бутона. Започва от нула и ще се увеличава всеки път, когато бутонът бъде натиснат.
  5. web_switch = 0
    Инициализира се променлива web_switch, която ще се използва за управление на лампата чрез уеб интерфейс. Стойността ѝ ще бъде променяна при заявки от уеб страницата.
  6. PIR = Pin(26, Pin.IN, Pin.PULL_DOWN)
    Този ред създава обект Pin, свързан с GPIO 26, който е настроен като вход с пулдаун резистор (Pin.PULL_DOWN). Това означава, че ако няма активност на пина, стойността му ще бъде държана на ниско ниво. Този пин е свързан към PIR (Passive Infrared) сензор, който ще се използва за откриване на движение.
  7. PIR_active = 0
    Тази променлива указва дали PIR сензорът е активен. В началото е инициализирана със стойност 0 (неактивен).
  8. PIR_timer_stop = 10 # задаване на времето в което PIR проверява дали има актовност в стаята!
    Този ред задава времето (в секунди), през което PIR сензорът ще проверява за активност в стаята. Ако няма движение за това време, лампата ще се изключи.
  9. relay = Pin(13, Pin.OUT)
    Тук се създава обект Pin, свързан с GPIO 13, който е настроен като изход (Pin.OUT). Този пин е свързан към релето, което ще управлява включването и изключването на лампата.
  10. relay.off()
    Този ред изключва релето, като задава ниско ниво на GPIO 13, което означава, че лампата е изключена.
  11. relay_logic_state = 0
    Тази променлива съхранява състоянието на релето – дали лампата е включена (1) или изключена (0).
  12. i2c = SoftI2C(sda=Pin(21), scl=Pin(22), freq=400000)
    Този ред инициализира софтуерен I2C интерфейс с използване на GPIO 21 за SDA (Serial Data Line) и GPIO 22 за SCL (Serial Clock Line). Честотата на комуникация е зададена на 400 kHz. Този I2C интерфейс ще бъде използван за комуникация с различни периферни устройства, като DHT сензора и LCD екрана.
  13. dht_inicialize = dht.DHT22(Pin(25))
    Тук се инициализира DHT22 сензор за измерване на температура и влажност, който е свързан към GPIO 25.
  14. temperature = 0.0 и humidity = 0.0
    Тези променливи съхраняват текущите стойности на температурата и влажността, измерени от DHT22 сензора. В началото те са инициализирани на 0.0.
  15. I2C_ADDR = 0x27
    Този ред задава I2C адреса на LCD дисплея, който е свързан към микроконтролера.
  16. totalRows = 4 и totalColumns = 20
    Тези променливи указват броя на редовете и колоните на LCD дисплея. В този случай, дисплеят има 4 реда и 20 колони.
  17. lcd = I2cLcd(i2c, I2C_ADDR, totalRows, totalColumns)
    Тук се създава обект I2cLcd, който управлява LCD дисплея чрез I2C интерфейса. Този обект ще бъде използван за писане на информация на дисплея.
  18. ds3231_year = "" и ds3231_time = ""
    Тези променливи ще съхраняват текущата дата и час, прочетени от DS3231 RTC модула (Real-Time Clock).

Тази инициализация създава основната структура за работа с хардуерните компоненти, които ще се използват за управление на осветлението в стаята, базирано на входове от бутона и PIR сензора, както и за показване на информация на LCD дисплея.

ESP32 Core 1

Тази част от кода реализира уеб сървър на ESP32, който позволява управление на лампа чрез уеб интерфейс. Функцията Core_1_function се изпълнява на второто ядро (Core 1) на ESP32, което освобождава основното ядро (Core 0) за изпълнение на други задачи, като мониторинг на сензори и управление на хардуерни изходи.

#-----/ Core 1 function\------
def Core_1_function():
    
    def web_page():
        global button_push_count
        global PIR_active
        global web_switch
        
        if button_push_count == 1:
            gpio_state="Light ON"

        if button_push_count == 0:
            gpio_state="Light OFF"

        if button_push_count == 2:
            gpio_state="PIR activate"
                
        html = """
<html>
   <head>
      <title>Hall Light Control</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta http-equiv="refresh" content="3;url=http://192.168.100.132">
      <meta charset="UTF-8">
      <link rel="icon" href="data:,">
      <style>
         html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
         h1{color: #0F3376; padding: 2vh;}
         p{font-size: 1.5rem;}
         .button{display: inline-block; background-color: cyan; border: none; 
           border-radius: 4px; color: black; padding: 16px 40px;
           text-decoration: none;
           font-size: 30px;
           margin: 2px;
           cursor: pointer;}
      </style>
   </head>
   <body>
      <h1>Light control in the living room</h1>
      <p>Дата : """ + ds3231_year + """ Час : """ + ds3231_time + """</p><br>
      
      <p>
         <a href="/?switch"><button class="button">Switch Function</button></a>
         <br>
         <strong>""" + gpio_state + """</strong>
      </p><hr><br>
      <p>Температура : """ +  str(temperature) + """ *C</p>
      <p>Влажност : """ + str(humidity) + """ %</p>
   </body>
</html>
"""
        return html
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 80))
    s.listen(5)
    
    while True:
        global button_push_count
        global web_switch
        
        conn, addr = s.accept()
        #print('Got a connection from %s' % str(addr))
        request = conn.recv(1024)
        request = str(request)
        #print('Content = %s' % request)
  
        #-----// relay 1 \\------
        switch = request.find('/?switch')
    
        if switch == 6:
            #print('switch')
            web_switch = 1

        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        conn.close()


#-----/ Core 1 START\------
_thread.start_new_thread(Core_1_function, ())

1. Функция Core_1_function

Core_1_function е основната функция, която се изпълнява на Core 1 и управлява уеб сървъра, позволявайки дистанционно управление на лампата и показване на данни като температура, влажност и време.

2. Функция web_page

Функцията web_page генерира HTML код за уеб страницата, която се предоставя на клиента (например браузър) при заявка. Страницата показва текущото състояние на лампата, както и показания за температура, влажност и текущо време.

  • Глобални променливи:
    • button_push_count: Определя броя на натисканията на бутона, който контролира лампата. Тази стойност се използва за определяне на състоянието на лампата.
    • PIR_active: Указва дали PIR сензорът е активен.
    • web_switch: Флаг, използван за управление на лампата чрез уеб интерфейса.
  • Определяне на състоянието на лампата: В зависимост от стойността на button_push_count, се определя състоянието на лампата:
    • При button_push_count = 1, лампата е включена (изписва се “Light ON”).
    • При button_push_count = 0, лампата е изключена (изписва се “Light OFF”).
    • При button_push_count = 2, PIR сензорът е активиран (изписва се “PIR activate”).
  • Генериране на HTML страница: HTML кодът, генериран от web_page, включва:
    • Заглавие на страницата и стилове за оформлението.
    • Текущата дата и час, получени от DS3231 RTC модула.
    • Бутон за превключване на състоянието на лампата.
    • Текущото състояние на лампата.
    • Показания за температура и влажност, получени от DHT22 сензора.

3. Настройка на сокет сървър

Създава се и конфигурира сокет сървър, който слуша за входящи HTTP заявки на порт 80. Това е стандартният порт за уеб сървъри, което позволява на ESP32 да приема и обработва заявки от мрежата.

  • Създаване на сокет:
    • socket.socket(socket.AF_INET, socket.SOCK_STREAM): Създава нов TCP сокет с IPv4.
  • Обвързване и слушане:
    • s.bind(('', 80)): Сокетът се обвързва с всички налични мрежови интерфейси и слуша на порт 80.
    • s.listen(5): Започва да слуша за входящи връзки, като максималният брой чакащи връзки е 5.

4. Основен цикъл за обработка на заявки

В основния цикъл на функцията Core_1_function се обработват входящите връзки от клиенти (браузъри). Всяка заявка се анализира и се проверява дали съдържа команда за превключване на състоянието на лампата. След това се генерира и изпраща HTML страница с актуалната информация за състоянието на системата.

  • Обработка на входящи заявки:
    • conn, addr = s.accept(): Приема нова връзка от клиент и създава обекти conn и addr за връзката.
    • request = conn.recv(1024): Получава HTTP заявка от клиента.
    • switch = request.find('/?switch'): Проверява дали заявката съдържа команда за превключване на лампата.
  • Управление на лампата чрез уеб интерфейса:
    • Ако в заявката се открие /switch, променливата web_switch се задава на 1, което сигнализира за промяна в състоянието на лампата.
  • Изпращане на отговор:
    • Генерираната HTML страница от web_page се изпраща обратно на клиента като отговор на заявката.

5. Стартиране на Core 1 функцията

Функцията Core_1_function се стартира в нова нишка, използвайки _thread.start_new_thread(Core_1_function, ()). Това означава, че тя ще работи паралелно с останалите задачи, изпълнявани на основното ядро на ESP32.

ESP32 Core 0 Основна логика на програмата

Тази част от кода реализира основната логика на Core 0 на ESP32, където се изпълняват различни асинхронни задачи. Тези задачи управляват LED индикатор, четат данни от сензори, показват информация на LCD дисплея, и контролират лампата чрез бутон, PIR сензор и реле. Ето подробно обяснение на всяка част от кода:

#-----/ Core 0 \------
while True:
    
    #==================================   СТАТУС Core-0 (prime)
    async def status_led_Core_1():
        while True:
            Led_Status_Core_1.on()
            await asyncio.sleep_ms(500)
            Led_Status_Core_1.off()
            
            await asyncio.sleep_ms(500)
            
    #==================================   READ DHT22 (temp,hum)
    async def dht_read():
        global temperature
        global humidity
        
        while True:
            dht_inicialize.measure()
            
            temperature = dht_inicialize.temperature()
            temperature = round(temperature , 1)
            
            humidity = dht_inicialize.humidity()
            humidity = round(humidity , 1)
    
            #print("temp = " , temperature)
            #print("hum = " , humidity)
            
            await asyncio.sleep(2)
            
    #==================================   DS3231 RTC
    async def read_DS3231():
        while True:
            ds = DS3231(i2c) # изчитане на показанията от DS3231 RTC
            #print(ds.datetime())
            global ds3231_year
            global ds3231_time
            
            ds3231_year = str(ds.datetime()[2]) + ":" + str(ds.datetime()[1]) + ":" + str(ds.datetime()[0])
            """ds3231_time = str(ds.datetime()[4]) + ":" + str(ds.datetime()[5]) + ":" + str(ds.datetime()[6])"""
            ds3231_time = str(ds.datetime()[4]) + ":" + str(ds.datetime()[5])
            #print(ds3231_year, ds3231_time)
            
            await asyncio.sleep_ms(10000)
            
    #==================================   LCD WRITE TIME   
    async def write_time_to_LCD():
        while True:
            lcd.move_to(0 , 0)
            lcd.putstr(ds3231_year + "/" + ds3231_time)
            
            await asyncio.sleep(30)
            lcd.clear_line(0)	# изтрива само първи ред
            
    #==================================   LCD WRITE temperature
    async def write_temp_to_LCD():
        while True:
            lcd.move_to(0 , 2)
            lcd.putstr("Temp = " + str(temperature) + " *C")
            
            await asyncio.sleep(40)
            lcd.clear_line(2)	# изтрива трети ред
            
    #==================================   LCD WRITE humidity
    async def write_hum_to_LCD():
        while True:
            lcd.move_to(0 , 3)
            lcd.putstr("Humi = " + str(humidity) + " %")
            #print(humidity)
            
            await asyncio.sleep(45)
            lcd.clear_line(3)	# изтрива четвърти ред
            
    #==================================   LCD WRITE управление от BUTTON, PIR or RELAY off
    async def write_control_to_LCD():
        
        global relay_logic_state
        global PIR_active
        
        while True:
            
            if relay_logic_state == 0:
                lcd.move_to(0 , 1)
                lcd.putstr("   Lamp OFF")
                
            if relay_logic_state == 1:
                lcd.move_to(0 , 1)
                lcd.putstr("   Lamp ON ")
                
            if PIR_active == 1:
                lcd.move_to(0 , 1)
                lcd.putstr("  PIR-Activ")
            
            await asyncio.sleep(1.7)
            lcd.clear_line(1)  # изтрива втори ред
            
    #==================================   БУТОН on/off    
    async def button_read():
        
        global relay_logic_state
        global PIR_active
        global button_push_count
        global web_switch
            
        while True:
                       
            button_logic_state = button.value()
            
            if button_logic_state == 0 or web_switch == 1:
                button_push_count += 1
                #print("cout = " , button_push_count)
                
                if button_push_count == 1:
                    relay_logic_state = 1
                    PIR_active = 0	# деактивиране на PIR
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
                    
                if button_push_count == 2:
                    # relay_logic_state = 0  # може би ако го деактивирам от тук ще при всеки цикъл ще казва че релето е OFF
                    PIR_active = 1	# активиране на PIR
                    #button_push_count = 0
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
                    
                if button_push_count == 3:
                    relay_logic_state = 0
                    PIR_active = 0	# деактивиране на PIR
                    button_push_count = 0
                    web_switch = 0
                    
                    await asyncio.sleep_ms(200)
            
            await asyncio.sleep_ms(100)
            
    #==================================   PIR check is on/off
    def check_pir():
        return PIR.value()
            
    #==================================   PIR on/off relay
    async def PIR_control():
        global PIR_active
        global PIR_timer_stop
        global relay_logic_state
        
        while True:

            if PIR_active == 1:
                
                if check_pir() == 1:
                    i = 0
                    
                    while i <= PIR_timer_stop:
                        #print("i = " , i)
                        relay_logic_state = 1
                        
                        if check_pir() == 1:
                            i = 0
                        await asyncio.sleep(1)
                        i += 1
                        
                        if PIR_active == 0:		# терминира цикъла от бутона
                            relay_logic_state = 0
                            break
                    
                if check_pir() == 0:
                    relay_logic_state = 0
                        
            await asyncio.sleep_ms(100)
            
    #==================================   RELAY on/off     
    async def relay_state():
        global relay_logic_state
        
        while True:
            if relay_logic_state == 1:
                relay.on()
                #print("lamp on")
            if relay_logic_state == 0:
                relay.off()
                #print("lamp off")
                
            await asyncio.sleep_ms(500)
            
    #==================================    Стартиране на tasks...        
    async def main():
        task1 = asyncio.create_task(read_DS3231())
        task2 = asyncio.create_task(write_time_to_LCD())
        task3 = asyncio.create_task(status_led_Core_1())
        task4 = asyncio.create_task(button_read())
        task5 = asyncio.create_task(relay_state())
        task6 = asyncio.create_task(dht_read())
        task6 = asyncio.create_task(write_temp_to_LCD())
        task7 = asyncio.create_task(write_hum_to_LCD())
        task8 = asyncio.create_task(PIR_control())
        task9 = asyncio.create_task(write_control_to_LCD())
        
        await asyncio.gather(task1, task2, task3, task4, task5, task6, task7, task8, task9)
    
    asyncio.run(main())

1. Основен цикъл на Core 0

Основният цикъл на Core 0 започва с непрекъснато изпълнение на различни асинхронни функции, които се стартират в main().

2. status_led_Core_1: Статус на Core 1

Тази асинхронна функция контролира статуса на LED индикатор, свързан към Core 1. LED-ът премигва на всеки 500ms, показвайки, че Core 1 работи нормално.

  • Цикъл на мигане: LED-ът се включва и изключва с интервал от 500ms, използвайки asyncio.sleep_ms(), което позволява продължаване на изпълнението на други задачи.

3. dht_read: Четене на DHT22 сензора

Тази функция периодично чете температурата и влажността от DHT22 сензора.

  • Измерване и закръгляне на стойностите: След всяко измерване, стойностите на температурата и влажността се закръгляват до 1 десетична запетая и се съхраняват в глобалните променливи temperature и humidity.
  • Интервал на обновяване: Данните се обновяват на всеки 2 секунди.

4. read_DS3231: Четене на DS3231 RTC

Функцията read_DS3231 чете текущата дата и час от RTC модула DS3231 и ги форматира като низове, които след това се показват на LCD дисплея.

  • Изчитане и формат на данни: Датата и часът се извличат от модула и се форматират в две променливи ds3231_year и ds3231_time.
  • Интервал на обновяване: Данните се обновяват на всеки 10 секунди.

5. write_time_to_LCD: Писане на време на LCD

Тази функция показва текущата дата и час на първия ред на LCD дисплея.

  • Писане на LCD дисплея: Текущата дата и час се показват на първия ред, след което редът се изтрива след 30 секунди.

6. write_temp_to_LCD и write_hum_to_LCD: Писане на температура и влажност на LCD

Тези две функции отговарят за показването на температурата и влажността на съответните редове на LCD дисплея.

  • Интервал на обновяване: Температурата се показва и изтрива след 40 секунди, а влажността — след 45 секунди.

7. write_control_to_LCD: Писане на състоянието на лампата на LCD

Тази функция показва текущото състояние на лампата на втория ред на LCD дисплея.

  • Контрол на дисплея: Състоянието на лампата (включена, изключена, или активиран PIR) се показва на LCD дисплея и се изтрива след 1.7 секунди.

8. button_read: Четене на състоянието на бутона

Тази функция следи състоянието на бутона и реагира на всяко натискане, като променя състоянието на лампата и активира/деактивира PIR сензора.

  • Превключване на състоянието на лампата: С натискане на бутона, лампата може да се включва, изключва, или да се активира PIR сензора.
  • Деактивация на PIR сензора: PIR сензорът се деактивира, когато лампата се включи ръчно.

9. check_pir и PIR_control: Управление на PIR сензора

  • check_pir: Тази функция просто връща текущото състояние на PIR сензора (дали е засечено движение).
  • PIR_control: Тази функция управлява лампата въз основа на сигнала от PIR сензора. Ако сензорът засече движение, лампата се включва за определен период от време, зададен в PIR_timer_stop. Ако няма движение, лампата се изключва.

10. relay_state: Управление на релето

Функцията relay_state контролира състоянието на релето, което управлява лампата. В зависимост от състоянието на променливата relay_logic_state, лампата се включва или изключва.

  • Интервал на проверка: Състоянието на релето се проверява на всеки 500ms.

11. main: Стартиране на всички задачи

В main() се създават и стартират всички асинхронни задачи, използвайки asyncio.create_task(). Задачите се изпълняват паралелно, благодарение на асинхронната природа на uasyncio, което позволява на Core 0 да управлява множество задачи едновременно, без да блокира изпълнението им.

  • Събиране на задачите: Всички задачи се събират и стартират едновременно с asyncio.gather().
  • Изпълнение на задачите: Функцията asyncio.run(main()) стартира основния цикъл на задачите.

Схема на ESP32 MicroPython управление на лампа с PIR и Wi-Fi Свързаност

В този параграф ще представя схемното решение на интелигентното устройство за управление на лампа с помощта на ESP32. Схемата включва свързването на различни хардуерни компоненти като PIR сензор, DHT22 сензор за температура и влажност, реле за управление на лампата, бутон за ръчно управление, и LCD дисплей с I2C интерфейс за визуализация на данните. ESP32 микроконтролерът е сърцето на устройството, осигурявайки както безжична свързаност чрез Wi-Fi, така и възможността за паралелно изпълнение на множество задачи. Всеки компонент е внимателно свързан към съответните GPIO пинове на ESP32, като са взети под внимание както захранващите изисквания, така и сигурността на електрическата схема. Тази схема е основата, върху която е изградена цялата система, осигурявайки надеждно и ефективно функциониране на устройството.

Захранваща част

Lamp-control-with-PIR-and-Web-interface-Power-Syplay

Релейна част за включване на лампата

Lamp-control-with-PIR-and-Web-interface-Relay-Part

Процесор ESP32 и сензори LCD16x4, PIR, DHT, DS3231, Бутон

Lamp-control-with-PIR-and-Web-interface-PIR-DHT-RTC

Платка на проекта

ESP32 MicroPython управление на лампа с PIR и Wi-Fi Свързаност PCB

WEB интерфейс

ESP32 MicroPython управление на лампа с PIR и Wi-Fi Свързаност WEB интерфейс

Видео демонстрация ESP32 MicroPython управление на лампа с PIR и Wi-Fi Свързаност

Заключение

Проектът демонстрира как ESP32 може да бъде използван за създаване на смарт устройство за управление на лампи в стая с помощта на MicroPython. Благодарение на използваните технологии като Wi-Fi, асинхронно програмиране и множество сензори, устройството предоставя удобно и ефективно управление на лампата, като същевременно показва полезна информация за околната среда на LCD дисплея.

Този проект може да бъде разширен и адаптиран за други приложения, като добавяне на допълнителни сензори, разширяване на уеб интерфейса и автоматизиране на повече аспекти от домашната среда.