# Air Quality Monitor **Air Quality Monitor** is a lightweight web interface that displays real-time data from two environmental sensors: **HTU21D** and **BMP180**. The system uses **NGINX** to serve an HTML page that reads live data from JSON files. ## Project Description This project was designed to visualize environmental parameters in a simple, fast, and efficient way using an embedded graphical interface. The data is obtained from the following sensors: - **HTU21D**: Temperature and relative humidity. - **BMP180**: Atmospheric pressure and temperature. The data is stored in two JSON files, updated by the embedded system and displayed through an HTML/JavaScript-based frontend. --- ## Project Structure air-quality-monitor/ │ ├── BMP180.json # Pressure and temperature data from BMP180 ├── HTU21D.json # Temperature and humidity data from HTU21D ├── index.html # Main web interface ├── index.js # JavaScript logic to fetch and display JSON data ├── style.css # Custom CSS styles > The `.json` files are automatically updated by C programs that communicate with the sensors via I2C. --- ## 🚀 Installation & Deployment with NGINX ### Requirements - Linux server with `nginx` installed. - Root access or permission to modify NGINX configuration. ### Steps 1. Install NGINX (if not already installed): ```bash sudo apt update sudo apt install nginx ``` 2. Copy the project files to NGINX’s public directory (e.g., /var/www/html): ``` bash ``` 3. Redirect the configuration file to our repository folder: ```bash sudo vi /etc/nginx/sites-available/default ``` 4. Replace the line: ```bash root /var/www/html; ``` with: ```bash root /home/debian/path/to/your/repository; ``` 5. Apply chages with: ```bash sudo systemctl restart nginx ``` ## User interface This is a simple web page that displays real-time sensor data from two devices: • BMP180: Shows temperature and pressure. • HTU21D: Shows humidity and temperature. It’s styled with a separate CSS file (style.css) and fetches live data using JavaScript (index.js), which likely reads values from two JSON files (BMP180.json and HTU21D.json). ### HTML #### Complete code ```html Sensor Dashboard

Sensor Dashboard

Temperature & Pressure Icon

BMP180

Temperature: -- °C Pressure: -- hPa
Humidity Icon

HTU21D

Humidity: -- % Temperature: -- °C
``` #### Explanation ```html ``` • Declares this document as HTML5. • lang="en" sets the document language to English, which is useful for accessibility and SEO. ##### HEAD ```html Sensor Dashboard ``` • charset="UTF-8" ensures proper text encoding. • viewport makes the layout responsive on mobile devices. • title is what appears in the browser tab. • link imports the external CSS file (style.css) for styling. ##### BODY ```html
...
``` • The background div might be used for a visual effect like a background image or color gradient (defined in your CSS). • container holds the main content (dashboard). • index.js is loaded at the end to ensure the DOM is ready before scripts run. ##### Dashboard Title ```html

Sensor Dashboard

``` • This is the main heading of your web page. ##### Sensor Card: BMP180 ```hmtl
Temperature & Pressure Icon

BMP180

Temperature: -- °C Pressure: -- hPa
``` • This block represents the BMP180 sensor. • The icon (from flaticon.com) visually represents pressure/temperature. • The id attributes (temperature, pressure) are hooks used by JavaScript to insert real values from BMP180.json. ##### Sensor Card: HTU21D ```html
Humidity Icon

HTU21D

Humidity: -- % Temperature: -- °C
``` • This block represents the HTU21D sensor. • The image is a humidity icon. • The id attributes (humidity, tempHTU) are also updated by JavaScript using HTU21D.json. ##### JavaScript Integration ```hmtl ``` • This line loads your script, which is responsible for: • Fetching the JSON files. • Parsing their content. • Replacing the placeholder values (-- °C, -- hPa, etc.) with live sensor data. ### Index.js The script fetches data from two local JSON files: • BMP180.json — contains temperature and pressure. • HTU21D.json — contains temperature and humidity. Then it updates the HTML every 3 seconds so the page always shows live sensor values. #### Full code ```js function updateBMP180() { fetch('BMP180.json') .then(res => res.json()) .then(data => { document.getElementById('temperature').textContent = `Temperature: ${data.temperature.toFixed(2)} °C`; document.getElementById('pressure').textContent = `Pressure: ${data.pressure.toFixed(2)} hPa`; }) .catch(err => console.error("Error BMP180:", err)); } function updateHTU21D() { fetch('HTU21D.json') .then(res => res.json()) .then(data => { document.getElementById('humidity').textContent = `Humidity: ${data.humidity.toFixed(1)} %`; document.getElementById('tempHTU').textContent = `Temperature: ${data.temperature.toFixed(1)} °C`; }) .catch(err => console.error("Error HTU21D:", err)); } setInterval(() => { updateBMP180(); updateHTU21D(); }, 3000); // Carga inicial updateBMP180(); updateHTU21D(); ``` ### Code explanation #### Function: updateBMP180 ```js function updateBMP180() { fetch('BMP180.json') .then(res => res.json()) .then(data => { document.getElementById('temperature').textContent = `Temperature: ${data.temperature.toFixed(2)} °C`; document.getElementById('pressure').textContent = `Pressure: ${data.pressure.toFixed(2)} hPa`; }) .catch(err => console.error("Error BMP180:", err)); } ``` - This defines a new function named updateBMP180. You’ll call this function when you want to update the BMP180 sensor data on the page. - fetch('BMP180.json'): Loads the JSON file from the same directory. - .then(res =\>json()): Parses the response as JSON - .then(data +\> {}): Accesses the data inside the file. - document.getElementById(...) updates the corresponding elements in your HTML. #### Function: updateHTU21D ```js function updateHTU21D() { fetch('HTU21D.json') .then(res => res.json()) .then(data => { document.getElementById('humidity').textContent = `Humidity: ${data.humidity.toFixed(1)} %`; document.getElementById('tempHTU').textContent = `Temperature: ${data.temperature.toFixed(1)} °C`; }) .catch(err => console.error("Error HTU21D:", err)); } ``` Works the same way as updateBMP180(), but: - Fetches from HTU21D.json. - Displays humidity and temperature. - Uses .toFixed(1) for 1 decimal place (common for humidity values). #### Auto-update every 3 seconds ```js setInterval(() => { updateBMP180(); updateHTU21D(); }, 3000); ``` - Calls both update functions every 3,000 milliseconds (3 seconds). - Keeps the UI in sync with new sensor readings, assuming the .json files are being updated continuously. #### Initial load ```js updateBMP180(); updateHTU21D(); ``` - Ensures the data is shown immediately on page load, before the 3-second interval kicks in. ### CSS #### Full code ```css * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Segoe UI", sans-serif; } body, html { height: 100%; background-color: #0d1117; color: #ffffff; position: relative; overflow: hidden; } .background { background-image: url('https://wallpapers.com/images/hd/blue-circuit-board-traces-zn0xezd4t8axj9r6.webp'); background-size: cover; background-position: center; opacity: 0.1; filter: blur(3px); position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .container { max-width: 800px; margin: 40px auto; background-color: #161b22; padding: 30px; border-radius: 20px; box-shadow: 0 0 30px rgba(0, 255, 255, 0.2); animation: fadeIn 1s ease-in; } h1 { text-align: center; margin-bottom: 30px; font-size: 2.5rem; color: #58a6ff; } .sensor-card { background-color: #1f2937; padding: 20px; border-radius: 15px; margin-bottom: 20px; box-shadow: 0 0 20px rgba(100, 255, 255, 0.1); animation: slideIn 1s ease; display: flex; flex-direction: column; align-items: center; text-align: center; } .sensor-card img { width: 64px; height: 64px; margin-bottom: 10px; } .sensor-card h2 { margin-bottom: 10px; color: #90cdf4; } .data span { display: block; margin: 5px 0; font-size: 1.2rem; color: #ffffff; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } } ``` #### Explaining Global Styling - *: Resets default browser spacing (margin, padding) and sets a consistent font and box model across the entire page. - body, html: - Sets full height layout. - Applies a dark background (#0d1117) with white text. - Hides overflow and enables positioning for internal layers. ⸻ Background Layer - .background: - Adds a faint, blurred circuit board image as the background. - Uses opacity: 0.1 and filter: blur(3px) to give it a soft tech feel. - Positioned absolutely behind everything (z-index: -1). ⸻ Main Container - .container: - A centered, card-like section with: - A dark background (#161b22) - Rounded corners and soft glowing shadow. - Padding and a fade-in animation on load. ⸻ Title - h1: - Large, centered header in light blue (#58a6ff). - Styled to stand out at the top of the dashboard. ⸻ Sensor Cards - .sensor-card: - Styled boxes for each sensor. - Darker background (#1f2937) with a soft shadow. - Rounded corners, padding, and a slide-in animation when they load. - Contents are centered vertically and horizontally. - .sensor-card img: - Sensor icons sized to 64×64 pixels with margin for spacing. - .sensor-card h2: - Sub-headers for each sensor card in a soft blue (#90cdf4). ⸻ Data Text - .data span: - Each line of sensor data (e.g., temperature, humidity). - Displayed as blocks with spacing and larger font for visibility. ⸻ Animations - @keyframes fadeIn: - Smooth slide-down + fade-in for the container. - @keyframes slideIn: - Subtle zoom-in effect for each sensor card. ## Sensor library ### HTU21D library The library contains 3 files, HTU21D.c, htu21d.h and main.c #### htu21d.h ```h #ifndef HTU21D_H #define HTU21D_H // HTU21D i2c address #define HTU21D_ADDR 0x40 //commands for readings #define HTU21D_TEMP 0xE3 #define HTU21D_HUM 0xE5 #define HTU21D_RESET 0xFE //funtion declarations //Temp: int getTemp(int fd, double *temp); //HUM int getHum(int fd, double *hum); //RESET int getReset(int fd); #endif ``` - HTU21D_ADDR: The I2C address of the sensor (0x40). - HTU21D_TEMP: Command to read temperature (0xE3). - HTU21D_HUM: Command to read humidity (0xE5). - HTU21D_RESET: Command to reset the sensor (0xFE). - Declares three functions: 1. getTemp() – for reading temperature, 2. getHum() – for reading humidity, 3. getReset() – for resetting the sensor.