This shows you the differences between two versions of the page.
iothings:proiecte:2023:configurableindoorairqualitymonitoringsystem [2024/01/15 00:44] andrei.enescu0512 [4.1 MCU code] |
iothings:proiecte:2023:configurableindoorairqualitymonitoringsystem [2024/06/30 00:59] (current) andrei.enescu0512 [Configurable Indoor Air Quality Monitoring System] |
||
---|---|---|---|
Line 1: | Line 1: | ||
======Configurable Indoor Air Quality Monitoring System====== | ======Configurable Indoor Air Quality Monitoring System====== | ||
* Author: Andrei-George-Lucian ENESCU | * Author: Andrei-George-Lucian ENESCU | ||
- | * Email: <andrei.enescu0512@stud.acs.upb.ro> | + | * Email: <enescu.andrei23@gmail.com> |
- | * Master: AAC | + | * Master : AAC |
- | * Academic year: 2023-2024 | + | * Academic year : 2023-2024 |
- | * Source code: FIXME | + | * Source files : {{:iothings:proiecte:2023:enescu_andrei_iot_design_files.rar|download_design_files}} |
- | * Video: FIXME | + | * Project presentation : {{:iothings:proiecte:2023:enescu_andrei_iot_project_presentation.pdf|download_project_presentation}} |
+ | * Video : [[https://drive.google.com/file/d/12pxGG4WmBNYgOO-bVUk5fnq6-0USTvlY/view?usp=sharing|view_demo_video]] | ||
=====1. Introduction===== | =====1. Introduction===== | ||
- | The aim of this project is to create the infrastructure for an configurable indoor air quality monitoring system. The principle behind this idea is to have multiple devices that can be placed anywhere in an enclosed area. Depending on the user’s needs, the devices can be reconfigured accordingly to meet any demand, without reprogramming them. | + | The aim of this project is to create the infrastructure for a configurable indoor air quality monitoring system. The principle behind this idea is to have multiple devices that can be placed anywhere in an enclosed area. Depending on the user’s needs, the devices can be reconfigured accordingly to meet any demand, without reprogramming them. |
Each device is separated into 2 boards, the first one which has multiple sensors for monitoring the environment, and the second one which’s purpose is to acquire the data read by the previous board and send it to a server. | Each device is separated into 2 boards, the first one which has multiple sensors for monitoring the environment, and the second one which’s purpose is to acquire the data read by the previous board and send it to a server. | ||
The communication between the two boards will be done via a serial protocol. Being small and maneuverable, the device can be easily placed in | The communication between the two boards will be done via a serial protocol. Being small and maneuverable, the device can be easily placed in | ||
- | different parts of the room, and thus, it must be powered up by a battery. | + | different parts of the room, and thus, it must be powered up by a battery. The data acquired will be monitored using a custom web page. |
+ | |||
+ | Project structure: | ||
+ | |||
+ | |||
+ | {{:iothings:proiecte:2023:project_structure.jpeg?400|}} | ||
Line 188: | Line 194: | ||
===4.1.1 ESP32 code=== | ===4.1.1 ESP32 code=== | ||
+ | |||
+ | The WiFi SSID and password, and the MQTT server IP are required to establish a connection to the MQTT broker | ||
+ | <code C> | ||
+ | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WIFI SETUP | ||
+ | #include <WiFi.h> | ||
+ | #include <PubSubClient.h> | ||
+ | #include <Wire.h> | ||
+ | |||
+ | const char* ssid = "TO_BE_REPLACED"; // WiFi SSID | ||
+ | const char* password = "TO_BE_REPLACED"; // WiFi password | ||
+ | const char* mqtt_server = "TO_BE_REPLACED"; // MQTT server | ||
+ | |||
+ | WiFiClient espClient; | ||
+ | PubSubClient client( espClient ); | ||
+ | </code> | ||
+ | |||
+ | The functions used for WiFi: | ||
+ | <code C> | ||
+ | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WIFI FUNCTIONS | ||
+ | // This functions connects your ESP8266 to your router | ||
+ | void setup_wifi() { | ||
+ | delay( 10 ); | ||
+ | Serial.println(); | ||
+ | Serial.print("Connecting to "); | ||
+ | Serial.println( ssid ); | ||
+ | Serial.print("Password used: "); | ||
+ | Serial.println( password ); | ||
+ | WiFi.mode( WIFI_STA ); | ||
+ | WiFi.begin( ssid, password ); | ||
+ | while( WiFi.status() != WL_CONNECTED ) | ||
+ | { | ||
+ | delay( 500 ); | ||
+ | Serial.print( "." ); | ||
+ | } | ||
+ | Serial.println(""); | ||
+ | Serial.print( "WiFi connected - ESP IP address: " ); | ||
+ | Serial.println( WiFi.localIP() ); | ||
+ | } | ||
+ | |||
+ | // This function is executed when some device publishes a message to a topic that your ESP8266 is subscribed to | ||
+ | // Change the function below to add logic to your program, so when a device publishes a message to a topic that | ||
+ | // your ESP8266 is subscribed you can actually do something | ||
+ | void callback( String topic, byte* message, unsigned int length ) | ||
+ | { | ||
+ | Serial.print( "Message arrived on topic: " ); | ||
+ | Serial.println( topic ); | ||
+ | } | ||
+ | |||
+ | // This functions reconnects your ESP8266 to your MQTT broker | ||
+ | // Change the function below if you want to subscribe to more topics with your ESP8266 | ||
+ | void reconnect() | ||
+ | { | ||
+ | while( !client.connected() ) | ||
+ | { | ||
+ | Serial.print( "Attempting MQTT connection..." ); | ||
+ | if( client.connect( "ESP8266Client" ) ) | ||
+ | { | ||
+ | Serial.println( "connected" ); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Serial.print( "failed, rc=" ); | ||
+ | Serial.print( client.state() ); | ||
+ | Serial.println( " try again in 5 seconds" ); | ||
+ | delay( 5000 ); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | The serial communication can be configured by modifying the macros below: | ||
+ | <code C> | ||
+ | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SERIAL COMM SETUP | ||
+ | // general macros | ||
+ | #define TRUE true | ||
+ | #define FALSE false | ||
+ | |||
+ | #define SERIAL_1_BAUDRATE 115200 | ||
+ | #define SERIAL_2_BAUDRATE 115200 | ||
+ | |||
+ | #define ESP_MASTER_RX_PIN 15 | ||
+ | #define ESP_MASTER_TX_PIN 2 | ||
+ | |||
+ | #define UART_CHECK_COUNTER_MAX 10 | ||
+ | #define UART_TIMEOUT_COUNTER_MAX 50 | ||
+ | |||
+ | #define UART_END_BYTE 0x55 | ||
+ | |||
+ | // command list | ||
+ | #define UART_CMD_READ_SENSORS_STATUS 0x10 | ||
+ | #define UART_CMD_READ_SENSORS_DATA 0x20 | ||
+ | |||
+ | // flags pos | ||
+ | #define SENSOR_TEMP_SUPPORTED 0x01 | ||
+ | #define SENSOR_TVOC_SUPPORTED 0x02 | ||
+ | #define SENSOR_SOUND_SUPPORTED 0x04 | ||
+ | |||
+ | #define UART_REQUEST_CMD_MAX_LENGTH 2 | ||
+ | #define UART_RESPONSE_READ_DATA_MAX_LENGTH 8 | ||
+ | #define UART_RESPONSE_INIT_CMD_MAX_LENGTH 4 | ||
+ | |||
+ | #define UART_READ_BUFFER_LENGTH 10 | ||
+ | #define UART_WRITE_BUFFER_LENGTH 5 | ||
+ | |||
+ | // sensor flag position | ||
+ | #define SENSOR_TEMP_SUPPORTED 0x01 | ||
+ | #define SENSOR_TVOC_SUPPORTED 0x02 | ||
+ | #define SENSOR_SOUND_SUPPORTED 0x04 | ||
+ | |||
+ | // UART flags structure | ||
+ | typedef union | ||
+ | { | ||
+ | struct{ | ||
+ | uint8_t is_busy :1, | ||
+ | sensor_temp_supported :1, | ||
+ | sensor_tvoc_supported :1, | ||
+ | sensor_sound_supported :1, | ||
+ | :4; | ||
+ | }; | ||
+ | |||
+ | uint8_t word8; | ||
+ | }UART_COM_FLAGS; | ||
+ | |||
+ | // UART main structure | ||
+ | typedef struct | ||
+ | { | ||
+ | uint8_t write_buffer[UART_WRITE_BUFFER_LENGTH]; | ||
+ | uint8_t read_buffer[UART_READ_BUFFER_LENGTH]; | ||
+ | uint8_t buffer_counter; | ||
+ | |||
+ | uint8_t check_counter; | ||
+ | uint8_t timeout_counter; | ||
+ | |||
+ | UART_COM_FLAGS flags; | ||
+ | }UART_COM_STRUCT; | ||
+ | |||
+ | UART_COM_STRUCT UART_com; | ||
+ | </code> | ||
+ | |||
+ | The starting configuration of the serial protocol is described below. When the function is called, it will write the starting command **UART_CMD_READ_SENSORS_STATUS** to read the slave capabilities. If no response is received, the master board will stop working. | ||
<code C> | <code C> | ||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SETUP FUNCTION | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SETUP FUNCTION | ||
+ | void init_com_protocol_UART() | ||
+ | { | ||
+ | UART_com.flags.word8 = 0x00; | ||
+ | UART_com.flags.is_busy = TRUE; | ||
+ | UART_com.timeout_counter = UART_TIMEOUT_COUNTER_MAX; | ||
+ | UART_com.check_counter = UART_CHECK_COUNTER_MAX; | ||
+ | UART_com.buffer_counter = 0; | ||
+ | UART_com.write_buffer[0] = UART_CMD_READ_SENSORS_STATUS; | ||
+ | UART_com.write_buffer[1] = UART_END_BYTE; | ||
+ | Serial2.write( UART_com.write_buffer, 2 ); | ||
+ | } | ||
</code> | </code> | ||
+ | |||
+ | The WiFi and the serial communications are initialized in the setup functions | ||
<code C> | <code C> | ||
- | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LOOP FUNCTION | + | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SETUP FUNCTION |
+ | void setup() { | ||
+ | // setup the serial comms | ||
+ | Serial.begin( SERIAL_1_BAUDRATE ); | ||
+ | Serial2.begin( SERIAL_2_BAUDRATE, SERIAL_8N1, ESP_MASTER_RX_PIN, ESP_MASTER_TX_PIN ); | ||
+ | delay( 4000 ); | ||
+ | |||
+ | // prepare wifi config | ||
+ | setup_wifi(); | ||
+ | client.setServer( mqtt_server, 1883 ); | ||
+ | client.setCallback( callback ); | ||
+ | |||
+ | // init the serial config | ||
+ | init_com_protocol_UART(); | ||
+ | |||
+ | Serial.println( "data sent" ); | ||
+ | } | ||
</code> | </code> | ||
+ | The master will send periodically the **UART_CMD_READ_SENSORS_DATA** command, and after the response is received, it will send the data to the MQTT broker, depending on the slave's configuration. | ||
+ | <code C> | ||
+ | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LOOP FUNCTION | ||
+ | void loop() | ||
+ | { | ||
+ | uint8_t data_byte_read; | ||
+ | int8_t flags_processed; | ||
+ | // reconnect to MQTT | ||
+ | if( !client.connected() ) | ||
+ | { | ||
+ | reconnect(); | ||
+ | } | ||
+ | if( !client.loop() ) | ||
+ | { | ||
+ | client.connect( "ESP8266Client" ); | ||
+ | } | ||
+ | // check for timeouts | ||
+ | if( UART_com.flags.is_busy == TRUE ) | ||
+ | { | ||
+ | UART_com.timeout_counter--; | ||
+ | if( UART_com.timeout_counter == 0 ) | ||
+ | { | ||
+ | UART_com.flags.word8 = 0x00; | ||
+ | Serial.println( "Communication has been reset and blocked!" ); | ||
+ | while( 1 ); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | UART_com.check_counter--; | ||
+ | | ||
+ | // read the sensors again | ||
+ | if( UART_com.check_counter == 0 ) | ||
+ | { | ||
+ | UART_com.flags.is_busy = TRUE; | ||
+ | UART_com.check_counter = UART_CHECK_COUNTER_MAX; | ||
+ | UART_com.timeout_counter = UART_TIMEOUT_COUNTER_MAX; | ||
+ | | ||
+ | UART_com.write_buffer[0] = UART_CMD_READ_SENSORS_DATA; | ||
+ | UART_com.write_buffer[1] = UART_END_BYTE; | ||
+ | Serial2.write( UART_com.write_buffer, 2 ); | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | // read every byte available from the receiver's buffer | ||
+ | while( Serial2.available() ) | ||
+ | { | ||
+ | data_byte_read = Serial2.read(); | ||
+ | |||
+ | if( UART_com.flags.is_busy == TRUE ) | ||
+ | { | ||
+ | UART_com.timeout_counter = UART_TIMEOUT_COUNTER_MAX; | ||
+ | UART_com.check_counter = UART_CHECK_COUNTER_MAX; | ||
+ | UART_com.read_buffer[UART_com.buffer_counter] = data_byte_read; | ||
+ | UART_com.buffer_counter++; | ||
+ | |||
+ | if( ( ( UART_com.buffer_counter == UART_RESPONSE_INIT_CMD_MAX_LENGTH ) && ( UART_com.write_buffer[0] == UART_CMD_READ_SENSORS_STATUS ) ) || | ||
+ | ( ( UART_com.buffer_counter == UART_RESPONSE_READ_DATA_MAX_LENGTH ) && ( UART_com.write_buffer[0] == UART_CMD_READ_SENSORS_DATA ) ) ) | ||
+ | { | ||
+ | UART_com.flags.is_busy = FALSE; | ||
+ | UART_com.buffer_counter = 0; | ||
+ | |||
+ | switch( UART_com.read_buffer[0] ) | ||
+ | { | ||
+ | case UART_CMD_READ_SENSORS_STATUS: | ||
+ | Serial.println( "Config read ..." ); | ||
+ | |||
+ | flags_processed = ( int8_t )( ( ( ( uint16_t )UART_com.read_buffer[2] ) << 8 ) | UART_com.read_buffer[1] ); | ||
+ | |||
+ | UART_com.flags.sensor_temp_supported = ( ( flags_processed & SENSOR_TEMP_SUPPORTED ) != 0x00 ) ? TRUE : FALSE; | ||
+ | UART_com.flags.sensor_tvoc_supported = ( ( flags_processed & SENSOR_TVOC_SUPPORTED ) != 0x00 ) ? TRUE : FALSE; | ||
+ | UART_com.flags.sensor_sound_supported = ( ( flags_processed & SENSOR_SOUND_SUPPORTED ) != 0x00 ) ? TRUE : FALSE; | ||
+ | break; | ||
+ | |||
+ | case UART_CMD_READ_SENSORS_DATA: | ||
+ | Serial.println( "Data read ..." ); | ||
+ | |||
+ | if( UART_com.flags.sensor_temp_supported == TRUE ) { client.publish("ESP32/temp", String( ( float )( ( ( uint16_t )UART_com.read_buffer[2] << 8 ) | UART_com.read_buffer[1] ) / 10 ).c_str() ); } | ||
+ | if( UART_com.flags.sensor_tvoc_supported == TRUE ) { client.publish("ESP32/tvoc", String( ( int16_t )( ( ( uint16_t )UART_com.read_buffer[4] << 8 ) | UART_com.read_buffer[3] ) ).c_str() ); } | ||
+ | if( UART_com.flags.sensor_sound_supported == TRUE ) { client.publish("ESP32/sound", String( ( int16_t )( ( ( uint16_t )UART_com.read_buffer[6] << 8 ) | UART_com.read_buffer[5] ) ).c_str() ); } | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | delay( 100 ); | ||
+ | } | ||
+ | </code> | ||
Line 459: | Line 724: | ||
The components of the MQTT protocol are the following: | The components of the MQTT protocol are the following: | ||
* **MQTT client** -> multiple clients can connect to network; in our case there are 2 clients, the website and the master board | * **MQTT client** -> multiple clients can connect to network; in our case there are 2 clients, the website and the master board | ||
- | * **MQTT broker** -> it handles the communication between the MQTT clients; only 1 broker can exist, and the protocol cannot function without it; the broker is hosted locally on laptop | + | * **MQTT broker** -> it handles the communication between the MQTT clients; the protocol cannot function without it; the broker is hosted locally on laptop |
+ | |||
+ | MQTT architecture: | ||
+ | |||
+ | {{:iothings:proiecte:2023:MQTT_structure.jpeg?600|}} | ||
The MQTT is a lightweight protocol designed for IOT devices, since the clients do not need to connect or keep tracking other clients, they only need to exchange data with the broker, using topics. A client can publish ( post data ) or subscribe ( receive data ) on a topic. | The MQTT is a lightweight protocol designed for IOT devices, since the clients do not need to connect or keep tracking other clients, they only need to exchange data with the broker, using topics. A client can publish ( post data ) or subscribe ( receive data ) on a topic. | ||
Line 477: | Line 746: | ||
=====5. Conclusion===== | =====5. Conclusion===== | ||
- | In conclusion, this project’s aim was to create the infrastructure for an configurable indoor air quality monitoring system, which was successfully obtained. The advantages of using this infrastructure are that any device can be added with ease to the Dashboard, and the MQTT can support a large number ( even reach up to 1000 clients ), and the configuration of a device can be changed, requiring a minimum amount of effort. | + | In conclusion, this project’s aim was to create the infrastructure for a configurable indoor air quality monitoring system, which was successfully obtained. The advantages of using this infrastructure are that any device can be added with ease to the Dashboard, and the MQTT can support a large number ( even reach up to 1000 clients ), and the configuration of a device can be changed, requiring a minimum amount of effort. |
=====Resources===== | =====Resources===== | ||