Arduino
WiFi Cuckoo clock

This is a Wi-Fi-based “cuckoo” clock 🐦 running on ESP8266 and capable of the following functions:
- 🌐 Time synchronization About NTP.
- 🖥️ Display time on a TM1637 4-digit LED display.
- 🔊 Playing a soft "cuckoo" sound via relay or beeper (PIN1) every full hour.
- 🌤️/🌙 Hungarian summer/winter time (CET/CEST) automatic management.
💡 Functions
- ⏱️ Retrieve time from NTP (
hu.pool.ntp.org) with automatic time zone management (CET-1CEST,M3.5.0/2,M10.5.0/3). - 🕛 Display 12-hour format (1-12) for TM1637 on the display.
- 🔯 The colon (":") flashes every second.
- 🚀 At first start-up, the clock "catch up" with the current time starting from a standard hour (for example, 6 o'clock).
- 🐤 One cuckoo can be heard every entire hour (except for the first catch-up after switching on).
- ☀️ Daylight saving time at the beginning: on the last Sunday of March at 03:00, an extra cuckoo indicates that the clock has been set forward.
- 🌧️ At the start of winter time: on the last Sunday of October at 02:00, one cuckoo is omitted, adjusting to the time change.
🔧 Hardware
- 🧠 ESP8266 (e.g. NodeMCU v2) Software Dependencies
- ESP8266WiFi
- WiFiUdp
- TM1637Display 🧩
- time.h
- ArduinoOTA 📡
Installing the TM1637 library from the Arduino IDE Library Manager:
- Search for to "TM1637" (usually the
TM1637Displayversion is required).
⚙️ Settings
Change these in the code:
const char* ssid = "WIFI_SSID";const char* password = "WIFI_CREDENTIAL";OTA / hálózat:
- 🌐 OTA hosztnév:
cuckoo - 🔐 OTA jelszó:
password
📡 Over-The-Air (OTA) frissítés
Firmware can be updated remotely:
- Arduino IDE → Devices → Ports →
cuckoo(OTA device). - 🗝️ Use the set OTA password.
🚀 Usage
- 🔗 Assemble the hardware (ESP8266 + TM1637 + relay/beep).
- 📤 Upload the program using the Arduino IDE.
- 💬 Enter your Wi-Fi data (
ssid,password). - ⚡ Turn on the ESP8266; connects to Wi-Fi and synchronizes time from NTP.
- 🕛 The time is displayed in 12-hour format and the clock cuckoos every full hour.
🛠️ Development / Modifications
- 🎵 The sound pattern or duration can be changed in the
sendCuckoo()function. - 🕐 The starting base hour can be modified with the
baseHourvariable of thehandleCuckoo()function. - 📅 The summer/winter time calculation logic (last Sunday of March/October) automatically calculates the appropriate day using
temp.tm_mdayandmktime().
The code:
Alex#include <ESP8266WiFi.h>#include <WiFiUdp.h>#include <TM1637Display.h>#include <time.h>#include <ArduinoOTA.h>// ------------------------// WIFI// ------------------------const char* ssid = "WIFI_SSID";const char* password = "WIFI_CREDENTIAL";bool summerCuckooDone = false;bool winterCuckooDone = false;int lastHourChecked = -1;// ------------------------// PINOUT// ------------------------#define CLK D1#define DIO D2#define PIN1 D5 // cuckooTM1637Display display(CLK, DIO);// ------------------------// NTP// ------------------------WiFiUDP udp;const char* ntpServer = "hu.pool.ntp.org";const int localNtpPort = 123;// ------------------------// TIME// ------------------------time_t now;tm timeinfo;bool showColon = true;unsigned long lastUpdate = 0;bool initialHandled = false;int lastCuckooHour = -1;int lastCuckooMinute = -1;bool skipNextCuckoo = false;// ------------------------// CUCKOO// ------------------------void sendCuckoo(int count) {for (int i = 0; i < count; i++) {digitalWrite(PIN1, HIGH);delay(50);digitalWrite(PIN1, LOW);delay(1000);}}void handleCuckoo(struct tm *t) {int hr = t->tm_hour;int min = t->tm_min;if (min != lastCuckooMinute) {if (!initialHandled) {int baseHour = 6;int toSend = (hr >= baseHour) ? hr - baseHour : (24 - baseHour + hr);for (int i = 0; i < toSend; i++) sendCuckoo(1);initialHandled = true;}else if (hr != lastCuckooHour) {if (skipNextCuckoo) {skipNextCuckoo = false;} else {sendCuckoo(1);}}lastCuckooHour = hr;lastCuckooMinute = min;}}// ------------------------// WIFI CONNECT// ------------------------void connectWiFi() {WiFi.mode(WIFI_STA);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);}}// ------------------------// NTP TIME SYNC// ------------------------void syncTime() {configTzTime("CET-1CEST,M3.5.0/2,M10.5.0/3", ntpServer);delay(1000);time(&now);}// ------------------------// OTA SETUP// ------------------------void setupOTA() {ArduinoOTA.setHostname("cuckoo");ArduinoOTA.setPassword("password");ArduinoOTA.begin();}void handleDSTCuckoo(struct tm *t) {int hr = t->tm_hour;int month = t->tm_mon + 1;int day = t->tm_mday;if (hr == lastHourChecked) return;lastHourChecked = hr;// ===== SUMMER TIME FIX =====if (month == 3 && !summerCuckooDone) {tm temp = *t;temp.tm_mday = 31;mktime(&temp);int lastSunday = 31 - temp.tm_wday;if (day == lastSunday && hr == 3) {sendCuckoo(1);summerCuckooDone = true;}}// ===== WINTER TIME FIX =====if (month == 10 && !winterCuckooDone) {tm temp = *t;temp.tm_mday = 31;mktime(&temp);int lastSunday = 31 - temp.tm_wday;if (day == lastSunday && hr == 2) {skipNextCuckoo = true;winterCuckooDone = true;}}}// ------------------------// SETUP// ------------------------void setup() {pinMode(PIN1, OUTPUT);digitalWrite(PIN1, LOW);display.setBrightness(7);connectWiFi();udp.begin(localNtpPort);syncTime();initialHandled = false;setupOTA();}// ------------------------// LOOP// ------------------------void loop() {ArduinoOTA.handle();if (WiFi.status() != WL_CONNECTED) {connectWiFi();}unsigned long nowMs = millis();if (nowMs - lastUpdate >= 1000) {lastUpdate = nowMs;time(&now);localtime_r(&now, &timeinfo);static int lastDay = -1;if (timeinfo.tm_mday != lastDay) {lastDay = timeinfo.tm_mday;summerCuckooDone = false;winterCuckooDone = false;lastHourChecked = -1;}handleDSTCuckoo(&timeinfo);handleCuckoo(&timeinfo);int hr = timeinfo.tm_hour;int min = timeinfo.tm_min;int hr12 = hr % 12;if (hr12 == 0) hr12 = 12;display.showNumberDecEx(hr12 * 100 + min,showColon ? 0b11100000 : 0,true);showColon = !showColon;}}
2026-05-08 09:25:16.030804+02
Access card system unlocking
This project is an RFID-based automatic event triggering system that combines an Arduino RFID reader and a Python script.
If an authorized RFID card is detected, the system will automatically run commands on the computer.
⚙️ How does it work?
🧠 Arduino page
- Arduino reads RFID cards with an MFRC522 RFID Reader module
- Reads the card Your UID
- Compares it with a predefined UID
- If it matches:
- prints: “Jackpot FoR the Winnner”
- If it doesn’t match:
- Sends the message “Incorrect UID”
🐍 Python page
Python script:
- reads Arduino serial port (
pyserial) - monitors RFID messages
- if it receives the right text:
- simulates keystrokes (
pyautogui) - runs commands on the system
- stops the screensaver (XFCE Screensaver)
🚀 Triggered actions
If the RFID card is correct:
- Ctrl key event
- Enter text:
password - Press Enter
- Turn off screen saver
🔌 Hardware
- Arduino (any compatible)
- MFRC522 RFID Reader
- RFID card / tag
- USB connection to PC
🧰 Python dependencies
pip install pyserial pyautogui
📦 Main Logic
Arduino:
- RFID UID Read
- UID Check
- Send Serial Message
Python:
- Serial port monitoring
- String search:
"Jackpot FoR the Winnner" - Automatic action trigger
💡 What is it good for?
- Door opener / login system
- Physical key → digital access
- Automatic desktop action trigger
- Smart home integration basic
⚠️ Notes
- The serial port (
/dev/ttyUSB1) per machine may change - active desktop session may be required due to GUI automation
- DISPLAY setting is sometimes required on Linux systems
Arduino code:
2026-05-07 11:08:27.361725+02
ESP32-CAM FTP image upload to idokep.hu
A lightweight, stable ESP32-CAM image capture and FTP upload system designed for 24/7 operation, with OTA (over-the-air update) support and optimized camera handling.
🚀 Features
- 📸 High-resolution image capture (UXGA / SVGA)
- 🌐 FTP upload (passive mode)
- 🔁 OTA firmware update support
- 🧠 Optimized camera exposure management (automatic warm-up + recording)
- ⚡ Stable WiFi operation
- 🌙 Optional night inactive mode
- 🔒 Memory-safe camera buffer management
🧠 Design goals
- Prevent ESP32 freezing and WiFi lag, which the continuous automatic camera exposure may cause recalculation
- Maintain OTA update availability during normal operation
- Provide reliable FTP data transfer
- Support long-term unattended operation
🧰 Hardware
- ESP32-CAM (AI Thinker)
- OV2640 camera module
- Stable 5V power supply (recommended: 1-2A, short cable)
- WiFi connection
- FTP server with PASV (passive) mode enabled
🔌 Pin assignment (AI Thinker)
#define PWDN_GPIO_NUM 32#define RESET_GPIO_NUM -1#define XCLK_GPIO_NUM 0#define SIOD_GPIO_NUM 26#define SIOC_GPIO_NUM 27#define Y9_GPIO_NUM 35#define Y8_GPIO_NUM 34#define Y7_GPIO_NUM 39#define Y6_GPIO_NUM 36#define Y5_GPIO_NUM 21#define Y4_GPIO_NUM 19#define Y3_GPIO_NUM 18#define Y2_GPIO_NUM 5#define VSYNC_GPIO_NUM 25#define HREF_GPIO_NUM 23#define PCLK_GPIO_NUM 22
⚙️ Configuration
WiFi Setting
const char* ssid = "YOUR_WIFI"; const char* password = "YOUR_PASSWORD"; />
FTP settings
const char* ftp_host = "ftp.example.com";const int ftp_port = 21;const char* ftp_user = "username";const char* ftp_pass = "password";
📸 Camera settings
Default high quality profile:
config.frame_size = FRAMESIZE_UXGA;config.jpeg_quality = 10;config.fb_count = 2;
⚠️ Note: UXGA is a heavy load. The project uses this, but the camera exposure is carefully managed to avoid system slowdown.
🧠 Camera stabilization (warming up → recording)
Continuous automatic exposure and amplification can cause CPU load and WiFi delay.
The solution:
- The camera first adjusts automatically over a few frames
- Then the settings are recorded for stable operation for
Warm up (auto mode)
s->set_gain_ctrl(s, 1);s->set_exposure_ctrl(s, 1);
Capture (manual mode)
s->set_exposure_ctrl(s, 0);s->set_gain_ctrl(s, 0);
🌙 Night mode
No image creation between 22:00 and 05:00:
bool isNightTime() {struct tm timeinfo;if (!getLocalTime(&timeinfo)) return false;int hour = timeinfo.tm_hour;return (hour >= 22 || hour < 5);}
✅ Tested
- ESP32-CAM (AI Thinker)
- OV2640 camera
- FTP server with PASV support
🧩 Optional enhancements
- Timestamped filenames
- FTP retry logic
- Day / night camera profiles
- Watchdog protection
Arduino code:
2026-05-07 10:45:49.844301+02
WeatherWizard weather station
A low-power BLE weather station using ESP32-C3 and Raspberry Pi 5.
This project measures temperature, humidity, air pressure, UV intensity and rain level using the following sensors:
- BME280 (I²C — temperature / humidity / air pressure)
- GUVA-S12SD (UV sensor)
- Rain sensor module
The ESP32-C3 transmits data via Bluetooth Low Energy (BLE) to a Raspberry Pi 5, which updates the Home Assistant sensors using the REST API.
🔧 Used hardware
| KomponensLeírás | |
| ESP32-C3 SuperMini | BLE + WiFi mikrokontroller |
| BME280 szenzor | Hőmérséklet / páratartalom / légnyomás |
| GUVA-S12SD | UV intenzitás érzékelő |
| Esőérzékelő modul | Analóg esőérzékelő |
| TP4056 lítium akkumulátor töltő | Akkumulátortöltő napelemes támogatással |
| Napelem | Az eszköz tápellátásához |
| Samsung 18650 3250 mAh akkumulátor | Az eszköz akkumulátora |
Arduino code:
#include <Wire.h>#include <Adafruit_Sensor.h>#include <Adafruit_BME280.h>#include <BLEDevice.h>#include <BLEUtils.h>#include <BLEServer.h>#include "esp_sleep.h"
// ----- Sensor pins -----int guvaPin = 0;int rainPin = 1;#define BME_SDA 8#define BME_SCL 9#define LED_PIN 2
Adafruit_BME280 bme;
// ----- BLE settings -----#define SERVICE_UUID "9f5b0001-8b6d-4e2b-9a37-8a6f1e4d9a90"#define CHARACTERISTIC_UUID "9f5b0002-8b6d-4e2b-9a37-8a6f1e4d9a90"
BLECharacteristic *pCharacteristic;BLEServer* pServer;
// ---- Measure sensors and send data ----void sendSensorData() { int guvaVal = analogRead(guvaPin); float guvaVoltage = guvaVal * (3.3 / 4095.0);
int rainVal = analogRead(rainPin); float rainVoltage = rainVal * (3.3 / 4095.0);
bme.takeForcedMeasurement(); float temperature = bme.readTemperature(); float humidity = bme.readHumidity(); float pressure = bme.readPressure() / 100.0F;
char dataString[128]; snprintf(dataString, sizeof(dataString), "T:%.2f,H:%.2f,P:%.2f,U:%.3f,R:%.3f", temperature, humidity, pressure, guvaVoltage, rainVoltage);
pCharacteristic->setValue(dataString); pCharacteristic->notify();}
// ---- BLE server callback ----class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer* pServer) { sendSensorData(); // Send data when client connects }
void onDisconnect(BLEServer* pServer) { // Put the device into deep sleep after disconnect Wire.beginTransmission(0x76); Wire.write(0xF4); Wire.write(0x00); Wire.endTransmission(); digitalWrite(LED_PIN, LOW); esp_sleep_enable_timer_wakeup(110 * 1000000ULL); esp_deep_sleep_start(); }};
void initBME280Loop() { if (bme.begin(0x76, &Wire)) { bme.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, Adafruit_BME280::SAMPLING_X1, Adafruit_BME280::SAMPLING_X1, Adafruit_BME280::FILTER_OFF); }}
void setup() { Wire.begin(BME_SDA, BME_SCL); initBME280Loop();
BLEDevice::init("WeatherWizard 32-C3"); pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY ); pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->start();
digitalWrite(LED_PIN, HIGH);
// Enter deep sleep if no client connects within 10 seconds delay(10000); if (pServer->getConnectedCount() == 0) { digitalWrite(LED_PIN, LOW); esp_sleep_enable_timer_wakeup(110 * 1000000ULL); esp_deep_sleep_start(); }}
void loop() { // Nothing to do here, runs once after wake-up}
Python code:
import asynciofrom bleak import BleakScanner, BleakClientfrom datetime import datetimeimport requests
# --- Home Assistant configuration ---HA_TOKEN = "YOUR_HOME_ASSISTANT_TOKEN"HEADERS = { "Authorization": f"Bearer {HA_TOKEN}", "Content-Type": "application/json",}HA_SERVICE_URL = "http://homeassistant.local:8123/api/services/input_number/set_value" # Your HomeAssistant's address
# --- BLE constants ---ESP_NAME = "WeatherWizard 32-C3"CHAR_UUID = "9f5b0002-8b6d-4e2b-9a37-8a6f1e4d9a90"
# --- entity_id mapping ---HA_ENTITIES = { "temperature": "input_number.weatherwizard_temperature", "humidity": "input_number.weatherwizard_humidity", "pressure": "input_number.weatherwizard_pressure", "uv": "input_number.weatherwizard_uv", "rain": "input_number.weatherwizard_rain"}
async def scan_and_send(): while True: devices = await BleakScanner.discover(timeout=1) found = next((d for d in devices if d.name == ESP_NAME), None)
if found: try: async with BleakClient(found.address) as client: value = await client.read_gatt_char(CHAR_UUID) data_str = value.decode() print(f"📡 Received data: {data_str}")
parts = data_str.split(",") temperature = float(parts[0].split(":")[1]) humidity = float(parts[1].split(":")[1]) pressure = float(parts[2].split(":")[1]) uv = float(parts[3].split(":")[1]) rain = float(parts[4].split(":")[1])
for key, value in [ ("temperature", temperature), ("humidity", humidity), ("pressure", pressure), ("uv", uv), ("rain", rain) ]: payload = { "entity_id": HA_ENTITIES[key], "value": value } response = requests.post(HA_SERVICE_URL, json=payload, headers=HEADERS) if response.ok: print(f"✅ Updated {key} = {value}") else: print(f"❌ Failed to update {key}: {response.status_code}")
await asyncio.sleep(110) except Exception as e: print(f"⚠ Error while reading BLE data: {e}") pass else: print("🔍 No WeatherWizard device found.") pass
if __name__ == "__main__": asyncio.run(scan_and_send())
2026-05-07 10:38:44.936159+02
Kirby the WiFi scanner
The Kirby WiFi Dashboard is an ESP32-based interactive WiFi scanner and monitor system that features cute Kirby animation, NeoPixel LED effects, audio alerts, and an OLED display. It monitors available WiFi networks, logs them to the LittleFS file system, and can “feed” Kirby the networks it finds. The tool also includes timing features, notifications, and a web interface to manage settings and monitor Kirby's activity.

Functions
🌐 WiFi search and logging
- Continuously scans WiFi networks (in AP+STA mode)
- Saves the detected WiFi networks to the
wifi.txtfile (new networks) or to theeat.txtfile (already "eaten" by Kirby networks) - Uses RAM cache and Bloom filter to effectively track known networks
- Automatically removes "consumed" WiFis from the active list
🐱 Kirby pet
- Kirby "eats" found WiFi networks every 3 hours
- Tracks Kirby's age and number of WiFis consumed
- An animated Kirby sprite appears on the OLED display
🖥️ OLED display
Displays:
- Kirby's animation
- The current age
- The "cooler" (WiFis waiting to be fed)
- All WiFis number
- The number of active WiFi networks
Additional functions:
- Show notifications (new WiFi or Kirby eating events)
- Sleep mode support for power saving
🌈 NeoPixel LED effects
- Rainbow transition effect with adjustable hue and with brightness
- Timed on and off
🔔 Sound signals (Buzzer)
- Play Mario coin sound when Kirby “eats” a WiFi
- Support mute function
- Mute can be timed
⏱️ Timer / Scheduler
Enables:
- Setting the sleep and wake-up time of the display
- Scheduling the sound mute
- Turning WiFi scanning on and off
- Controlling the LEDs
- Deep sleep timing energy saving for
🌐 Web interface
Full dashboard available from a browser
(default IP: 192.168.4.1)
Functions:
- The known and consumed WiFis display
- Buttons:
- Sleep
- Mute
- LED
- Scan
- Change scheduler settings directly from the website
- Kirby logo and cute user interface
🛠️ Hardware requirements
- ESP32-C3 (tested)
- SSD1306 OLED display (128x64)
- NeoPixel / WS2812 LED strip (integrated in ESP)
- Buzzer
- Optional: DS3231 real-time clock (RTC) for for accurate timing
⚡ Installation
- Upload the firmware to the ESP32 using Arduino IDE
- Connect the OLED display, NeoPixel and buzzer according to the pins given in the code
- Place
wifi.txtandeat.txtfiles in LittleFS filesystem or let the firmware create them automatically - Power on ESP32 — Kirby animation starts on the OLED display and starts searching for WiFi networks
- Web dashboard is accessible via AP IP address to manage settings
🚀 Usage
- Kirby automatically searches and logs WiFi networks
- OLED display shows Kirby's animation, age and WiFi stats
- LEDs display continuous rainbow effect (can be turned off)
- Buzzer plays sound when Kirby “eats” a WiFi (can be muted)
- Scheduler handles display, sound, scanning, LEDs and deep sleep
📝 Notes
- The system uses LittleFS file system to store
wifi.txtandeat.txt - Bloom filter provides effective duplication control for WiFi networks
- Kirby is automatically fed in 3-hour cycles from "birth time" based
- Supports web switches and scheduler settings
If you want to use the maximum 4MB partition for ESP:
- Copy
board.txtto: -
/packages/esp32/hardware/esp32/version/ - A
no_ota_bigger.csvfile here: -
/packages/esp32/hardware/esp32/version/tools/partitions/
Download
Github repo
Alex2026-05-07 10:05:38.110648+02
Virtual Plant Mood Display with Soil Moisture and Light Sensors
This Arduino program reads data from a soil moisture and ambient light sensor, then displays a cute, animated face on a 16x2 I2C LCD display to show the plant's "mood".
The face changes based on moisture and light values and alternates between different states: happy, sad, sleeping, and "dead" facial expressions.

Functions
- Soil moisture (analog input A0)
- Read light sensor data (analog input A1)
- Show mood with 4-frame ASCII animations on an I2C 16x2 LCD display (using (
LiquidCrystal_I2Clibrary)) - Animated facial expressions:
- Happy 😊 per ms
Requirements
Hardware
- Arduino development board (Uno, Nano, Mega, etc.)
- Soil moisture sensor (with analog output)
- Light sensor (with analog output, e.g. photoresistor or photodiode)
- 16x2 I2C LCD display (compatible with (
LiquidCrystal_I2Clibrary) - Connecting wires and breadboard
Software
- Arduino IDE (version 1.8.x or later recommended)
- Arduino
LiquidCrystal_I2Clibrary
Install:
Sketch -> Include Library -> Manage Libraries -> find: "LiquidCrystal_I2C"
The code:
2026-05-07 09:56:56.038538+02
