The ESP32-C3 Super Mini is a compact powerhouse for IoT projects. This guide will walk you through creating an advanced DC load control system, perfect for home automation enthusiasts and IoT professionals. We’ll explore everything from basic setup to advanced features like OTA updates and smart home integration.
Key Features
- Dual Activation Times: Set two different times for load activation.
- Adjustable Duration: Customize how long the load stays on.
- Web Interface: Control and configure the system remotely.
- NTP Synchronization: Ensure accurate timekeeping.
- Persistent Settings: Save configurations to EEPROM.
- Wi-Fi Optimization: Maximize Wi-Fi transmit power for better connectivity.
Hardware Setup
For this ESP32-C3 Super Mini project, you’ll need:
-
- ESP32-C3 Super Mini (Affiliate) – Buy on AliExpress or Buy on AliExpress
- Power Module for DIY Kit, AMS1117 – (5v->3.3v) (Affiliate) – Buy on AliExpress
- Relay Module 1 Channel 3.3V (Affiliate) – Buy on AliExpress
- Breadboard and jumper wires (Affiliate) –Buy on AliExpress
ESP32-C3 Super Mini Board
The important pins for this project are:
- GPIO0 – Responsible of controlling the relay board and in turning the load on and off.
Important Steps
- First upload the code to the Esp32-C3 by it self
- When it is programmed, open the Serial Monitor and you will see the IP Address. Store it in a *.txt file if you want. If you donยดt see it, reset the board.
- Now place the Esp32-C3 Super Mini on the breadboard and make the connections to power the load using the Diagram.
- Power everything and use the browser to initiate the Webserver using the IP Address.
Diagram
Software Implementation
Let’s break down the key components of our ESP32-C3 Super Mini DC Load Control system:
Wi-Fi Connection
We start by connecting to Wi-Fi, crucial for web interface access and time synchronization:
const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASSWORD"; WiFi.mode(WIFI_STA); esp_wifi_set_max_tx_power(80); // Maximize transmit power WiFi.begin(ssid, password);
Setting Maximum Transmit Power
When using the ESP32-C3 Super Mini, you might want to adjust the Wi-Fi transmit power to optimize your wireless communication. The line of code you mentioned, esp_wifi_set_max_tx_power()
, is used for this purpose.
Understanding Transmit Power
- Function Purpose:
- The function
esp_wifi_set_max_tx_power(int power)
allows you to set the maximum transmit power for the Wi-Fi radio on the ESP32-C3. This is important for controlling the range and reliability of your Wi-Fi signal.
- The function
- Parameter Range:
- The parameter value should be specified in 0.25 dBm increments. For example, if you want to set the power to 20 dBm (which is a common maximum for Wi-Fi), you would use:
esp_wifi_set_max_tx_power(80); // 20 dBm = 80 (since 20 * 4 = 80)
- Valid Values:
- The valid range for transmit power on the ESP32-C3 is typically from -128 (minimum) to 78 (maximum), which corresponds to approximately 0 dBm to about 20 dBm.
NTP Time Synchronization
Accurate timekeeping is essential for scheduled operations:
NTPClient timeClient(ntpUDP, "pool.ntp.org"); timeClient.begin(); timeClient.setTimeOffset(0); // Adjust for your timezone configTime(0, 0, "pool.ntp.org"); setenv("TZ", "WET0WEST,M3.5.0/1,M10.5.0", 1); // Set timezone for Portugal tzset();
The easiest way to find your timezone is to use the website https://www.timeanddate.com/time/map/. This interactive map allows you to:
- Search for your city
- Place a pin on the location
- See the exact timezone name
- Verify current UTC offset
- Check Daylight Saving Time status
For example, if you type “Lisbon” or “New York”, you’ll immediately see the precise timezone like “Europe/Lisbon” or “America/New_York” that you can directly use in your code.
To change the timezone to New York in the provided code, you need to modify the following lines in the setup() function:
// Initialize NTP Client timeClient.begin(); timeClient.setTimeOffset(-18000); // New York is UTC-5 (winter time) // Set timezone for New York configTime(-18000, 0, "pool.ntp.org"); setenv("TZ", "EST5EDT,M3.2.0,M11.1.0", 1); tzset();
Here’s what each change does:
timeClient.setTimeOffset(-18000);
sets the offset to -5 hours (-5 * 3600 seconds) for Eastern Standard Time.configTime(-18000, 0, "pool.ntp.org");
configures the time with the correct offset for New York.setenv("TZ", "EST5EDT,M3.2.0,M11.1.0", 1);
sets the timezone environment variable for New York, including Daylight Saving Time rules
Remember that New York observes Daylight Saving Time, so the actual offset may vary between UTC-5 (winter) and UTC-4 (summer). The timezone string “EST5EDT,M3.2.0,M11.1.0” automatically handles this transition.
Load Control Logic
The system allows for two daily activation times:
void checkAndActivateLoad() { // Get current time struct tm timeinfo; getLocalTime(&timeinfo); // Check for activation times and control the load if (currentHour == startHour1 && currentMinute == startMinute1 && currentSecond == 0 && !loadActivated1) { digitalWrite(loadPin, HIGH); loadActivated1 = true; loadStartTime = millis(); } // Similar check for second activation time // Turn off load after specified duration if ((loadActivated1 || loadActivated2) && (millis() - loadStartTime >= loadOnTime * 1000)) { digitalWrite(loadPin, LOW); loadActivated1 = false; loadActivated2 = false; } }
Web Interface
A user-friendly web interface allows remote control and configuration:
void handleClient(WiFiClient client) { // Handle incoming client requests // Implement load control and time setting logic // Generate HTML response with current status and form for settings }
- Knowing the IP Address, we can switch the load in real time:
- Or we can set times for the load to activate for a period of time throughout the day:
EEPROM Storage
We use EEPROM to persist settings across power cycles:
void saveTimesToEEPROM() { EEPROM.write(ADDR_HOUR1, startHour1); EEPROM.write(ADDR_MINUTE1, startMinute1); // Write other settings EEPROM.commit(); } void loadTimesFromEEPROM() { startHour1 = EEPROM.read(ADDR_HOUR1); startMinute1 = EEPROM.read(ADDR_MINUTE1); // Read other settings }
Complete Code
#include <WiFi.h> #include <NTPClient.h> #include <WiFiUdp.h> #include <EEPROM.h> #include <esp_wifi.h> const char* ssid = "YOUR SSID"; const char* password = "YOUR NETWORK PASSWORD"; const int loadPin = 0; // GPIO0 for the load WiFiServer server(80); // NTP Client setup WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org"); // Time settings (stored in EEPROM) int startHour1 = 0, startMinute1 = 0; int startHour2 = 0, startMinute2 = 0; int loadOnTime = 60; // Default load on time in seconds // EEPROM addresses const int EEPROM_SIZE = 20; const int ADDR_HOUR1 = 0; const int ADDR_MINUTE1 = 1; const int ADDR_HOUR2 = 2; const int ADDR_MINUTE2 = 3; const int ADDR_LOAD_ON_TIME = 4; // Variables to track load activation bool loadActivated1 = false; bool loadActivated2 = false; unsigned long loadStartTime = 0; void setup() { Serial.begin(115200); delay(1000); pinMode(loadPin, OUTPUT); // Initialize EEPROM EEPROM.begin(EEPROM_SIZE); // Load times from EEPROM loadTimesFromEEPROM(); // Connect to WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); // Set WiFi mode and adjust transmit power WiFi.mode(WIFI_STA); esp_wifi_set_max_tx_power(80); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); // Start the server server.begin(); Serial.println("Server started"); // Print the IP address Serial.print("Use this URL to connect: "); Serial.print("http://"); Serial.print(WiFi.localIP()); Serial.println("/"); // Initialize NTP Client timeClient.begin(); timeClient.setTimeOffset(0); // Portugal is UTC+0 in winter, UTC+1 in summer // Set timezone for Portugal configTime(0, 0, "pool.ntp.org"); setenv("TZ", "WET0WEST,M3.5.0/1,M10.5.0", 1); tzset(); // Now set GPIO0 as OUTPUT and ensure it's LOW (load off) digitalWrite(loadPin, LOW); } void loop() { timeClient.update(); // Check if it's time to activate the load checkAndActivateLoad(); // Handle web server clients WiFiClient client = server.available(); if (client) { handleClient(client); } } void checkAndActivateLoad() { time_t now; struct tm timeinfo; if(!getLocalTime(&timeinfo)){ Serial.println("Failed to obtain time"); return; } int currentHour = timeinfo.tm_hour; int currentMinute = timeinfo.tm_min; int currentSecond = timeinfo.tm_sec; // Check for first activation time if (currentHour == startHour1 && currentMinute == startMinute1 && currentSecond == 0 && !loadActivated1) { digitalWrite(loadPin, HIGH); // Turn on the load loadActivated1 = true; loadStartTime = millis(); Serial.println("Load activated at time 1"); } // Check for second activation time if (currentHour == startHour2 && currentMinute == startMinute2 && currentSecond == 0 && !loadActivated2) { digitalWrite(loadPin, HIGH); // Turn on the load loadActivated2 = true; loadStartTime = millis(); Serial.println("Load activated at time 2"); } // Check if loadOnTime seconds have passed since load activation if ((loadActivated1 || loadActivated2) && (millis() - loadStartTime >= loadOnTime * 1000)) { digitalWrite(loadPin, LOW); // Turn off the load loadActivated1 = false; loadActivated2 = false; Serial.println("Load deactivated after set time"); } // Reset activation flags at the start of a new day if (currentHour == 0 && currentMinute == 0 && currentSecond == 0) { loadActivated1 = false; loadActivated2 = false; digitalWrite(loadPin, LOW); // Ensure the load is off at midnight Serial.println("Flags reset for a new day"); } } void handleClient(WiFiClient client) { String request = client.readStringUntil('\r'); client.flush(); // Handle load control via web requests if (request.indexOf("/LOAD=ON") != -1) { digitalWrite(loadPin, HIGH); // Turn on the load } if (request.indexOf("/LOAD=OFF") != -1) { digitalWrite(loadPin, LOW); // Turn off the load } // Handle time setting if (request.indexOf("/setTimes") != -1) { int pos = request.indexOf("?"); if (pos != -1) { String params = request.substring(pos + 1); updateTimesFromParams(params); } } // Get current time struct tm timeinfo; if(!getLocalTime(&timeinfo)){ Serial.println("Failed to obtain time"); return; } // Prepare the response String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n"; s += "<h1>ESP32-C3 DC Load Control</h1>"; s += "<p>Current time: "; s += String(timeinfo.tm_hour) + ":" + String(timeinfo.tm_min) + ":" + String(timeinfo.tm_sec); s += "</p>"; s += "<p>Load is currently: "; s += (digitalRead(loadPin) == HIGH) ? "ON" : "OFF"; s += "</p>"; s += "<a href=\"/LOAD=ON\">Turn On Load</a><br>"; s += "<a href=\"/LOAD=OFF\">Turn Off Load</a><br><br>"; s += "<form action='/setTimes'>"; s += "Time 1: <input type='number' name='h1' min='0' max='23' value='" + String(startHour1) + "'> : "; s += "<input type='number' name='m1' min='0' max='59' value='" + String(startMinute1) + "'><br>"; s += "Time 2: <input type='number' name='h2' min='0' max='23' value='" + String(startHour2) + "'> : "; s += "<input type='number' name='m2' min='0' max='59' value='" + String(startMinute2) + "'><br>"; s += "Load On Time (seconds): <input type='number' name='ontime' min='1' max='3600' value='" + String(loadOnTime) + "'><br>"; s += "<input type='submit' value='Set Times'>"; s += "</form>"; s += "</html>\n"; client.print(s); delay(1); } void updateTimesFromParams(String params) { int h1 = params.indexOf("h1="); int m1 = params.indexOf("m1="); int h2 = params.indexOf("h2="); int m2 = params.indexOf("m2="); int ontime = params.indexOf("ontime="); if (h1 != -1) startHour1 = params.substring(h1 + 3, params.indexOf("&", h1)).toInt(); if (m1 != -1) startMinute1 = params.substring(m1 + 3, params.indexOf("&", m1)).toInt(); if (h2 != -1) startHour2 = params.substring(h2 + 3, params.indexOf("&", h2)).toInt(); if (m2 != -1) startMinute2 = params.substring(m2 + 3, params.indexOf("&", m2)).toInt(); if (ontime != -1) loadOnTime = params.substring(ontime + 7, params.indexOf("&", ontime)).toInt(); // Ensure values are within valid ranges startHour1 = constrain(startHour1, 0, 23); startMinute1 = constrain(startMinute1, 0, 59); startHour2 = constrain(startHour2, 0, 23); startMinute2 = constrain(startMinute2, 0, 59); loadOnTime = constrain(loadOnTime, 1, 3600); // 1 second to 1 hour saveTimesToEEPROM(); } void saveTimesToEEPROM() { EEPROM.write(ADDR_HOUR1, startHour1); EEPROM.write(ADDR_MINUTE1, startMinute1); EEPROM.write(ADDR_HOUR2, startHour2); EEPROM.write(ADDR_MINUTE2, startMinute2); EEPROM.write(ADDR_LOAD_ON_TIME, loadOnTime); EEPROM.commit(); } void loadTimesFromEEPROM() { startHour1 = EEPROM.read(ADDR_HOUR1); startMinute1 = EEPROM.read(ADDR_MINUTE1); startHour2 = EEPROM.read(ADDR_HOUR2); startMinute2 = EEPROM.read(ADDR_MINUTE2); loadOnTime = EEPROM.read(ADDR_LOAD_ON_TIME); // Ensure loaded values are within valid ranges startHour1 = constrain(startHour1, 0, 23); startMinute1 = constrain(startMinute1, 0, 59); startHour2 = constrain(startHour2, 0, 23); startMinute2 = constrain(startMinute2, 0, 59); loadOnTime = constrain(loadOnTime, 1, 3600); }
Video Demonstration
FAQ
- Q: Can the ESP32-C3 Super Mini control multiple loads?
A: Yes, you can control multiple loads by utilizing additional GPIO pins and expanding the control logic. - Q: How does this project compare to commercial smart plugs?
A: This DIY solution offers greater customization and integration possibilities at a lower cost, but requires more technical knowledge. - Q: Is this system suitable for high-power appliances?
A: The ESP32-C3 itself should control relays or contactors for high-power applications, not directly switch high currents.
Conclusion
The ESP32-C3 Super Mini proves to be a versatile and powerful platform for advanced IoT projects. This DC load control system showcases its capabilities in home automation, demonstrating precise timing, remote management, and smart home integration. Whether you’re automating your home lighting, managing energy consumption, or developing a custom IoT solution, the ESP32-C3 Super Mini provides the perfect foundation for your next project.