Week 09 - Connection and communication

Task #09

The task for the ninth week is to control a circuit using wireless network and fetching data from the Internet.

Controlling Motor over WebServer

I will do something for my final project. I already bought ESP32 Cam and made it work. Now, I want to control the motor in the Turntable using the camera's webserver. Additionally, I want to turn on a IR LED lamp during night time. I do not have the LED lamp yet, so I will simulate with normal red LED.

📟 Electronic components

🔌 Wiring diagram
No image

💻 Programming
There is GitHub repository for a ESP32 Cam WebServer. It offers basic and more advanced controls of the connected camera. I will use this code and edit it a bit to add motor and LED control.

First, I renamed the config file to myconfig.h and saved my Wi-fi SSID and password there. Then, I add these parts of code to index_OV2640.h file to create new control slider.

<div class="input-group" id="turn-group" title="Turning the table">
    <label for="turn">Turn</label>
    <div class="range-min">-180</div>
    <input type="range" id="turn" min="-180" max="180" value="0" class="default-action">
    <div class="range-max">180</div>
</div>
/*
...
*/
const turnGroup = document.getElementById('turn-group')
No image

I defined my stepper motor in app_httpd.cpp and created the turning logic.

//Includes the Arduino Stepper Library
#include <Stepper.h>

// Defines the number of steps per rotation
const int stepsPerRevolution = 2038;

// Creates an instance of stepper class
// Pins entered in sequence IN1-IN3-IN2-IN4 for proper step sequence
Stepper myStepper = Stepper(stepsPerRevolution, 2, 15, 14, 13);
/*
...
*/
int turnDegree;
/*
...
*/
static esp_err_t cmd_handler(httpd_req_t *req){
...
    else if (!strcmp(variable, "turn")) {
        int diff = val - turnDegree;
        int steps = 2 * diff * (stepsPerRevolution / 360); // 2:1 gear ration

        // Rotate fast at 10 RPM
        myStepper.setSpeed(10);
        myStepper.step(steps);

        turnDegree = val;
    }

And finally, I edited esp32-cam-webserver and implemented fetching weather data from OpenWeather. I am particulary interested in sunrise and sunset. The LED should turn on 30 minutes after sunset and turn off 30 minutes before sunrise. It checks every 15 minutes. Also current temperature is printed.

#include <HTTPClient.h>
#include <ArduinoJson.h> // Install from Library Manager

const String apiKey = "1008dca4dd348ba868f400b270b8766f";
const String lat = "50.09";
const String lon = "14.42";
String weatherURL = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&appid=" + apiKey  + "&units=metric";

// Timezone offset in seconds (e.g., +2 hours = 7200)
const int timezoneOffset = 2 * 3600;
unsigned long lastUpdate = 0;
const unsigned long updateInterval = 15UL * 60UL * 1000UL; // 15 minutes in ms
// LED pin
const int ledIRPin = 12;
/*
...
*/
void printTime(const String& label, time_t rawTime) {
    struct tm * timeinfo = localtime(&rawTime);
    Serial.print(label);
    Serial.print(": ");
    Serial.print(asctime(timeinfo));
}

void fetchWeatherData() {
HTTPClient http;
http.begin(weatherURL);
int httpCode = http.GET();

if (httpCode > 0) {
    String payload = http.getString();
    DynamicJsonDocument doc(2048);
    deserializeJson(doc, payload);

    float temperature = doc["main"]["temp"];
    time_t sunrise = doc["sys"]["sunrise"];
    time_t sunset = doc["sys"]["sunset"];
    time_t current = doc["dt"];

    // Convert to local time
    sunrise += timezoneOffset;
    sunset += timezoneOffset;
    current += timezoneOffset;

    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.println(" °C");

    printTime("Current", current);
    printTime("Sunrise", sunrise);
    printTime("Sunset", sunset);

    // Light control logic
    if (current >= (sunset + 1800)) {
    digitalWrite(ledIRPin, HIGH); // Turn on LED after 30 mins past sunset
    Serial.println("LED ON (it's dark)");
    } else if (current >= (sunrise - 1800) && current < sunrise) {
    digitalWrite(ledIRPin, LOW); // Turn off LED 30 mins before sunrise
    Serial.println("LED OFF (light is coming soon)");
    }

} else {
    Serial.print("HTTP error: ");
    Serial.println(httpCode);
}

http.end();
}

void setup() {
    // Set LED
    pinMode(ledIRPin, OUTPUT);
    digitalWrite(ledIRPin, LOW);
/*
...
*/
void loop() {
    /*
     *  Just loop forever, reconnecting Wifi As necesscary in client mode
     * The stream and URI handler processes initiated by the startCameraServer() call at the
     * end of setup() will handle the camera and UI processing from now on.
    */
    // fetching weather and operating LED
    unsigned long now = millis();

    // Only fetch every 15 minutes
    if (now - lastUpdate >= updateInterval || lastUpdate == 0) {
        fetchWeatherData();
        lastUpdate = now;
    }

🎞️ Demo video
In the video below you can see how the camera can be operated. There is bit of interference visible in the video stream, which is likely caused by the stepper motor. I will be fixing that for the final project.