- ESP32
- Laptop
- USB wire
- DF Robot Environment Sensor
- Adapter
- Strong wifi
- Basic Python and HTML (optional) language
- Thonny Desktop
- Thingspeak account
-
Download Thonny
-
Create account Thingspeak
-
Thonny setup
- Connect your ESP32 to your laptop through USB or Type C wire
- Open Thonny and choose run and choose “Configure interpreter”
- Choose microPython and port that your laptop connect to ESP32
- Click “Install or update Micropython” to download firmware to ESP32 (so that ESP32 can run Thonny code (Python))
-
Connect DF Robot Environmental sensor to I2C port in shell of ESP32
-
Create a channel
-
Name a channel and choose field for your project (max: 8 fields). Save channel
-
Remember the channel id. Note: The id is different in every channel so check the channel id carefully
-
Sharing the project to public view so everyone can see it.
-
Check and remember the API KEYS for read and write
-
Code
import machine import urequests import ujson as json import time import network # Define the I2C address and communication modes I2C_MODE = 0x01 UART_MODE = 0x02 DEV_ADDRESS = 0x22 HPA = 0x01 KPA = 0x02 TEMP_C = 0x03 TEMP_F = 0x04 HTTP_HEADERS = {'Content-Type': 'application/json'} THINGSPEAK_WRITE_API_KEY = 'CECWK4EDQD9CRCJW' THINGSPEAK_CHANNEL_ID= '2604448' THINGSPEAK_READ_API_KEY = 'UQNBZGODZAF499TP' USER_API_KEY = 'MQHY981MRKM29ZDE' ssid='MakerLab.vn' password='' sta_if=network.WLAN(network.STA_IF) sta_if.active(True) if not sta_if.isconnected(): print('connecting to network...') sta_if.connect(ssid, password) while not sta_if.isconnected(): pass print('network config:', sta_if.ifconfig()) '''url = f'https://api.thingspeak.com/channels/2604448/feeds.json?api_key=MQHY981MRKM29ZDE' response = urequests.delete(url, headers={'Content-Type': 'application/json', 'api_key': USER_API_KEY}) # Check the response print(f'Status Code: {response.status_code}') print(f'Response Text: {response.text}') if response.status_code == 200: print('ThingSpeak data cleared successfully') else: print('Failed to clear ThingSpeak data') response.close()''' field1_request = 'https://api.thingspeak.com/channels/2604448/fields/1/last.json' class DFRobot_Environmental_Sensor(): def __init__(self, i2cbus=None, baud=9600): self.i2cbus = i2cbus self._baud = baud self._uart_i2c = I2C_MODE if i2cbus else UART_MODE self._addr = None def _detect_device_address(self): rbuf = self._read_reg(0x04, 2) if self._uart_i2c == I2C_MODE: data = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: data = rbuf[0] return data def begin(self): if self._detect_device_address() != DEV_ADDRESS: return False return True def _read_reg(self, reg_addr, length): try: if self._uart_i2c == I2C_MODE: rslt = self.i2cbus.readfrom_mem(self._addr, reg_addr, length) elif self._uart_i2c == UART_MODE: # Implement UART read here if needed rslt = [-1] * length except Exception as e: print("Error reading register:", e) rslt = [-1] * length return rslt def get_temperature(self, units): rbuf = self._read_reg(0x14, 2) if self._uart_i2c == I2C_MODE: data = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: data = rbuf[0] temp = (-45) + ((data * 175.00) / 1024.00 / 64.00) if units == TEMP_F: temp = temp * 1.8 + 32 return round(temp, 2) def get_humidity(self): rbuf = self._read_reg(0x16, 2) if self._uart_i2c == I2C_MODE: humidity = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: humidity = rbuf[0] humidity = (humidity / 1024) * 100 / 64 return humidity def get_ultraviolet_intensity(self): version = self._read_reg(0x05, 2) if (version[0] << 8 | version[1]) == 0x1001: rbuf = self._read_reg(0x10, 2) data = rbuf[0] << 8 | rbuf[1] ultraviolet = data / 1800 else: rbuf = self._read_reg(0x10, 2) if self._uart_i2c == I2C_MODE: data = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: data = rbuf[0] outputVoltage = 3.0 * data / 1024 ultraviolet = abs((outputVoltage - 0.99) * (15.0 - 0.0) / (2.9 - 0.99) + 0.0)/25 return round(ultraviolet, 2) def get_luminousintensity(self): rbuf = self._read_reg(0x12, 2) if self._uart_i2c == I2C_MODE: data = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: data = rbuf[0] luminous = data * (1.0023 + data * (8.1488e-5 + data * (-9.3924e-9 + data * 6.0135e-13))) return round(luminous, 2) def get_atmosphere_pressure(self, units): rbuf = self._read_reg(0x18, 2) if self._uart_i2c == I2C_MODE: atmosphere = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: atmosphere = rbuf[0] if units == KPA: atmosphere /= 10 return atmosphere def get_elevation(self): rbuf = self._read_reg(0x18, 2) if self._uart_i2c == I2C_MODE: elevation = rbuf[0] << 8 | rbuf[1] elif self._uart_i2c == UART_MODE: elevation = rbuf[0] elevation = 44330 * (1.0 - pow(elevation / 1015.0, 0.1903)) return round(elevation, 2) class DFRobot_Environmental_Sensor_I2C(DFRobot_Environmental_Sensor): def __init__(self, i2cbus, addr): super().__init__(i2cbus) self._addr = addr def _read_reg(self, reg_addr, length): return self.i2cbus.readfrom_mem(self._addr, reg_addr, length) class DFRobot_Environmental_Sensor_UART(DFRobot_Environmental_Sensor): def __init__(self, baud, addr): super().__init__(None, baud) self._addr = addr # Initialize UART here if needed def _read_reg(self, reg_addr, length): # Implement UART read here if needed return [-1] * length def setup(): i2c = machine.I2C(0, scl=machine.Pin(22), sda=machine.Pin(21)) sensor = DFRobot_Environmental_Sensor_I2C(i2c, DEV_ADDRESS) if not sensor.begin(): print("Sensor initialization failed!!") return print("Sensor initialization success!!") while True: temperature_c = sensor.get_temperature(TEMP_C) temperature_f = sensor.get_temperature(TEMP_F) humidity = sensor.get_humidity() ultraviolet_intensity = sensor.get_ultraviolet_intensity() luminous_intensity = sensor.get_luminousintensity() atmospheric_pressure = sensor.get_atmosphere_pressure(HPA) elevation = sensor.get_elevation() print("-----------------------") print("Temp: " + str(temperature_c) + " °C") print("Temp: " + str(temperature_f) + " °F") print("Humidity: " + str(humidity) + " %") print("Ultraviolet intensity: " + str(ultraviolet_intensity) + " mw/cm2") print("Luminous Intensity: " + str(luminous_intensity) + " lx") print("Atmospheric pressure: " + str(atmospheric_pressure) + " hPa") print("Elevation: " + str(elevation) + " m") print("-----------------------") sensor_reading = {'field1':temperature_c, 'field2':humidity, 'field3':ultraviolet_intensity, 'field4':luminous_intensity, 'field5':atmospheric_pressure, 'field6': elevation} print(sensor_reading) request = urequests.post( 'http://api.thingspeak.com/update?api_key=' + THINGSPEAK_WRITE_API_KEY,json = sensor_reading, headers = HTTP_HEADERS ) request.close() time.sleep(1) if __name__ == "__main__": setup()
-
Explain
-
Imports and Configuration
import machine import urequests import ujson as json import time import network
machine
: A module for interacting with hardware components like pins and I2C buses.urequests
: A library for making HTTP requests, used here to send data to ThingSpeak.ujson
: A module for working with JSON data, used to format the data being sent.time
: Provides time-related functions, used to control timing between sensor readings.network
: Handles network-related tasks, such as connecting to Wi-Fi.
-
Constants and Configuration
I2C_MODE = 0x01 UART_MODE = 0x02 DEV_ADDRESS = 0x22 HPA = 0x01 KPA = 0x02 TEMP_C = 0x03 TEMP_F = 0x04
-
Wi-Fi Connection Setup
ssid='MakerLab.vn' # Wi-Fi network name password='' # Wi-Fi password
sta_if=network.WLAN(network.STA_IF) sta_if.active(True) if not sta_if.isconnected(): print('connecting to network...') sta_if.connect(ssid, password) while not sta_if.isconnected(): pass print('network config:', sta_if.ifconfig())
- This block connects the ESP32 to the specified Wi-Fi network. If not connected, it attempts to connect and waits until successful.
-
Setup function
def setup(): i2c = machine.I2C(0, scl=machine.Pin(22), sda=machine.Pin(21)) sensor = DFRobot_Environmental_Sensor_I2C(i2c, DEV_ADDRESS) if not sensor.begin(): print("Sensor initialization failed!!") return print("Sensor initialization success!!")
-
Data Collection and Uploading
while True: temperature_c = sensor.get_temperature(TEMP_C) temperature_f = sensor.get_temperature(TEMP_F) humidity = sensor.get_humidity() ultraviolet_intensity = sensor.get_ultraviolet_intensity() luminous_intensity = sensor.get_luminousintensity() atmospheric_pressure = sensor.get_atmosphere_pressure(HPA) elevation = sensor.get_elevation()
sensor_reading = {'field1':temperature_c, 'field2':humidity, 'field3':ultraviolet_intensity, 'field4':luminous_intensity, 'field5':atmospheric_pressure, 'field6': elevation} request = urequests.post( 'http://api.thingspeak.com/update?api_key=' + THINGSPEAK_WRITE_API_KEY,json = sensor_reading, headers = HTTP_HEADERS ) request.close() time.sleep(1)
-
To implement real-time weather and air quality updates on a web platform, the OpenWeatherMap API can be utilized effectively. Below is a guide on how to integrate this functionality using a JavaScript script within your web application.
Before integrating the API, ensure your HTML file contains the necessary elements to display the weather data and air quality information. The script provided interacts with elements like .date
, .temp
, .weather-content
, and several pollutant-specific elements. Here is an example structure:
<div class="weather-widget">
<div class="date"></div>
<div class="temp"></div>
<div class="temp-format"></div>
<div class="weather-content"></div>
<div class="aqi-value"></div>
<div class="pollutants">
<div class="co"></div>
<div class="no"></div>
<div class="no2"></div>
<div class="o3"></div>
<div class="so2"></div>
<div class="pm2_5"></div>
<div class="pm10"></div>
<div class="nh3"></div>
</div>
</div>
To access the OpenWeatherMap API, you need an API key. Replace the placeholder in the script with your actual key. Also, specify the city or coordinates for which you want to fetch the weather and air quality data.
const apiKey = 'YOUR_API_KEY';
const city = 'Ho Chi Minh City';
Alternatively, if you want to use geographical coordinates:
const lat = 10.8231; // Latitude for Ho Chi Minh City
const lon = 106.6297; // Longitude for Ho Chi Minh City
The script uses the weather endpoint to retrieve current weather data such as temperature, weather condition, and description. It then updates the respective HTML elements with this data.
function updateWeather() {
fetch(weatherApiUrl)
.then(response => response.json())
.then(data => {
const temperature = data.main.temp;
const tempMax = data.main.temp_max;
const tempMin = data.main.temp_min;
const weatherCondition = data.weather[0].main;
const weatherDescription = data.weather[0].description;
tempElement.innerHTML = `${temperature.toFixed(1)}°C`;
tempFormatElement.innerHTML = `High/Low: ${tempMax.toFixed(1)}°C / ${tempMin.toFixed(1)}°C`;
weatherContentElement.innerHTML = `${weatherCondition} (${weatherDescription.charAt(0).toUpperCase() + weatherDescription.slice(1)})`;
})
.catch(error => {
console.error('Error fetching weather data:', error);
tempElement.innerHTML = 'Temp not available';
tempFormatElement.innerHTML = 'High/Low: N/A / N/A';
weatherContentElement.innerHTML = 'Weather not available';
});
}
The air pollution endpoint provides data on the Air Quality Index (AQI) and concentrations of various pollutants. The script processes this information and updates the respective pollutant elements.
function updateAirQuality() {
fetch(airPollutionApiUrl)
.then(response => response.json())
.then(data => {
const aqi = data.list[0].main.aqi;
const components = data.list[0].components;
aqiValueElement.innerHTML = `AQI Level: <span class="aqi-number">${aqi}</span>`;
document.querySelector('.aqi-number').classList.add(`value-${aqi}`);
pollutantElements.co.innerHTML = `${components.co.toFixed(1)} µg/m³`;
pollutantElements.no.innerHTML = `${components.no.toFixed(1)} µg/m³`;
pollutantElements.no2.innerHTML = `${components.no2.toFixed(1)} µg/m³`;
pollutantElements.o3.innerHTML = `${components.o3.toFixed(1)} µg/m³`;
pollutantElements.so2.innerHTML = `${components.so2.toFixed(1)} µg/m³`;
pollutantElements.pm2_5.innerHTML = `${components.pm2_5.toFixed(1)} µg/m³`;
pollutantElements.pm10.innerHTML = `${components.pm10.toFixed(1)} µg/m³`;
pollutantElements.nh3.innerHTML = `${components.nh3.toFixed(1)} µg/m³`;
})
.catch(error => {
console.error('Error fetching air quality data:', error);
aqiValueElement.innerHTML = 'AQI data not available';
Object.keys(pollutantElements).forEach(key => {
pollutantElements[key].innerHTML = 'N/A';
});
});
}
The data updates every minute using the setInterval function. This ensures that your web application always displays the latest weather and air quality information.
setInterval(updateDate, 60000);
setInterval(updateWeather, 60000);
setInterval(updateAirQuality, 60000);
The script includes error handling to display fallback messages if the API requests fail. This ensures that the user interface remains informative even when the API is inaccessible.