LED Status Indicators
LED Status Indicators
Overview
Section titled “Overview”This section covers the LED indicator system that provides visual feedback for the RFID-based check-in/check-out operations. Learning this section will enable you to:
- Design and implement LED status indicators for user feedback
- Handle different LED states (check-in, check-out, error, ready)
- Implement MQTT-controlled LED patterns
- Troubleshoot common LED circuit issues
Prerequisites
Section titled “Prerequisites”Before starting this section, ensure you have:
- LED circuit wired per 05-02
- MQTT communication between ESP32 and Node-RED working
- Check-in/check-out flow implemented (05-08)
- Basic understanding of ESP32 GPIO control
Key Concepts
Section titled “Key Concepts”LED Color Meanings
Section titled “LED Color Meanings”The system uses two LEDs to convey the current state:
| LED Color | State | Meaning |
|---|---|---|
| Green ON | Check-in success | Tag registered, time recording started |
| Red ON | Check-out success | Time recorded in TimeTagger |
| Green blink | System ready | ESP32 connected and waiting |
| Red blink | Error | Connection issue or API failure |
| Both OFF | Deep sleep or idle | No activity or power saving |
User Experience Flow
Section titled “User Experience Flow”User Experience Timeline:1. User approaches reader → Both LEDs OFF (idle)2. User taps RFID tag → Momentary pause3. ✓ Check-in confirmed → GREEN LED for 3 seconds4. User leaves → LEDs OFF5. User returns, taps again → Momentary pause6. ✓ Check-out confirmed → RED LED for 3 seconds7. Recording saved → Quick blink, then OFFImplementation Steps
Section titled “Implementation Steps”Step 1: LED Pin Configuration
Section titled “Step 1: LED Pin Configuration”// LED Pin Definitions#define LED_GREEN 22#define LED_RED 21
// LED Timing Constantsconst unsigned long LED_INDICATE_TIME = 3000; // 3 secondsconst unsigned long BLINK_INTERVAL = 250; // 250ms for blinkingStep 2: LED Control Functions
Section titled “Step 2: LED Control Functions”// LED State Managementenum LEDState { LED_OFF, LED_GREEN_ON, LED_RED_ON, LED_GREEN_BLINK, LED_RED_BLINK, LED_BOTH_BLINK};
LEDState currentLEDState = LED_OFF;unsigned long ledStateStartTime = 0;
void setLED(LEDState state) { currentLEDState = state; ledStateStartTime = millis();
switch (state) { case LED_OFF: digitalWrite(LED_GREEN, LOW); digitalWrite(LED_RED, LOW); break;
case LED_GREEN_ON: digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_RED, LOW); break;
case LED_RED_ON: digitalWrite(LED_GREEN, LOW); digitalWrite(LED_RED, HIGH); break;
case LED_GREEN_BLINK: case LED_RED_BLINK: case LED_BOTH_BLINK: // Blinking is handled in the main loop break; }}
void updateLED() { unsigned long now = millis();
switch (currentLEDState) { case LED_GREEN_ON: case LED_RED_ON: // Auto-turn off after indication time if (now - ledStateStartTime >= LED_INDICATE_TIME) { setLED(LED_OFF); } break;
case LED_GREEN_BLINK: // Blink green (system ready indicator) if ((now / BLINK_INTERVAL) % 2 == 0) { digitalWrite(LED_GREEN, HIGH); } else { digitalWrite(LED_GREEN, LOW); } break;
case LED_RED_BLINK: // Blink red (error indicator) if ((now / BLINK_INTERVAL) % 2 == 0) { digitalWrite(LED_RED, HIGH); } else { digitalWrite(LED_RED, LOW); } break;
case LED_BOTH_BLINK: // Alternate blink (system starting) if ((now / BLINK_INTERVAL) % 2 == 0) { digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_RED, LOW); } else { digitalWrite(LED_GREEN, LOW); digitalWrite(LED_RED, HIGH); } break;
case LED_OFF: default: // Do nothing — LEDs stay off break; }}Step 3: LED Feedback on MQTT Messages
Section titled “Step 3: LED Feedback on MQTT Messages”The ESP32 listens for MQTT feedback from Node-RED and updates LEDs accordingly:
void setup() { pinMode(LED_GREEN, OUTPUT); pinMode(LED_RED, OUTPUT);
// Startup indication setLED(LED_BOTH_BLINK);
// ... rest of setup
// Ready indication setLED(LED_GREEN_BLINK); // Blinking green = system ready}
// MQTT callback for feedbackvoid callback(char* topic, byte* payload, unsigned int length) { // Parse the message String message; for (int i = 0; i < length; i++) { message += (char)payload[i]; }
Serial.print("Feedback received: "); Serial.println(message);
// Parse JSON DynamicJsonDocument doc(256); DeserializationError error = deserializeJson(doc, message);
if (error) { Serial.println("JSON parse error"); setLED(LED_RED_BLINK); return; }
String action = doc["action"] | ""; bool success = doc["success"] | true;
if (success) { if (action == "CHECK_IN") { // Green = checked in setLED(LED_GREEN_ON); Serial.println("✓ Check-in successful"); } else if (action == "CHECK_OUT") { // Red = checked out setLED(LED_RED_ON); Serial.println("✓ Check-out successful"); } } else { // Error state setLED(LED_RED_BLINK); Serial.print("✗ Operation failed: "); Serial.println(doc["error"].as<String>()); }}Step 4: Connection Status Indication
Section titled “Step 4: Connection Status Indication”Indicate WiFi and MQTT connection status through LED patterns:
void setup() { // Startup sequence setLED(LED_BOTH_BLINK);
// Connect to WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); // Rapid blink during connection attempt digitalWrite(LED_GREEN, !digitalRead(LED_GREEN)); }
// WiFi connected → blink green setLED(LED_GREEN_BLINK);
// Connect to MQTT client.setServer(mqtt_server, 1883); while (!client.connected()) { if (client.connect("ESP32_RFID")) { // MQTT connected → solid brief green setLED(LED_GREEN_ON); delay(500); } else { delay(2000); } }
// System ready setLED(LED_GREEN_BLINK); // Slow blink = waiting for tags}Step 5: Complete LED Management Class
Section titled “Step 5: Complete LED Management Class”For cleaner code organization:
class LEDManager {private: int greenPin; int redPin; LEDState currentState; unsigned long stateStartTime; unsigned long indicationDuration;
public: LEDManager(int gp, int rp) : greenPin(gp), redPin(rp) { pinMode(greenPin, OUTPUT); pinMode(redPin, OUTPUT); currentState = LED_OFF; stateStartTime = 0; indicationDuration = 3000; }
void indicateCheckIn() { setState(LED_GREEN_ON); }
void indicateCheckOut() { setState(LED_RED_ON); }
void indicateError() { setState(LED_RED_BLINK); }
void indicateReady() { setState(LED_GREEN_BLINK); }
void indicateStartup() { setState(LED_BOTH_BLINK); }
void turnOff() { setState(LED_OFF); }
void update() { unsigned long now = millis();
switch (currentState) { case LED_GREEN_ON: case LED_RED_ON: if (now - stateStartTime >= indicationDuration) { turnOff(); } break;
case LED_GREEN_BLINK: if ((now / 250) % 2 == 0) { digitalWrite(greenPin, HIGH); digitalWrite(redPin, LOW); } else { digitalWrite(greenPin, LOW); } break;
case LED_RED_BLINK: if ((now / 250) % 2 == 0) { digitalWrite(redPin, HIGH); digitalWrite(greenPin, LOW); } else { digitalWrite(redPin, LOW); } break;
case LED_BOTH_BLINK: if ((now / 150) % 2 == 0) { digitalWrite(greenPin, HIGH); digitalWrite(redPin, LOW); } else { digitalWrite(greenPin, LOW); digitalWrite(redPin, HIGH); } break;
case LED_OFF: default: digitalWrite(greenPin, LOW); digitalWrite(redPin, LOW); break; } }
private: void setState(LEDState state) { currentState = state; stateStartTime = millis(); }};
// Global instanceLEDManager leds(LED_GREEN, LED_RED);Step 6: Node-RED Side Feedback Generation
Section titled “Step 6: Node-RED Side Feedback Generation”The Node-RED flow sends MQTT feedback messages to control the LEDs:
// Function Node: "Generate LED Feedback"// Creates appropriate feedback message based on operation result
const uid = msg.payload?.uid || "UNKNOWN";const operation = msg.action || "UNKNOWN";const success = msg.success !== false;
if (success) { if (operation === "CHECK_IN") { msg.payload = { action: "CHECK_IN", success: true, name: msg.record?.ds || uid, message: "Time recording started" }; } else if (operation === "CHECK_OUT") { msg.payload = { action: "CHECK_OUT", success: true, name: msg.record?.ds || uid, duration: (msg.record?.t2 - msg.record?.t1) || 0, message: "Time recorded successfully" }; }} else { msg.payload = { action: "ERROR", success: false, uid: uid, error: msg.error || "Unknown error", message: "Operation failed" };}
msg.topic = "asset/tracking/feedback";msg.retain = false;
return msg;Verification
Section titled “Verification”Test the LED system:
// 1. Power on the ESP32// Expected: Both LEDs alternate blink during startup// Then: Green LED blinks slowly (system ready)
// 2. Scan a registered RFID tag (first time)// Expected: Green LED ON for 3 seconds (check-in)
// 3. Scan the same tag again// Expected: Red LED ON for 3 seconds (check-out)
// 4. Disconnect WiFi temporarily// Expected: Red LED blinks (connection error)
// 5. Reconnect WiFi// Expected: Green LED blinks (system ready)Verification Checklist:
- Startup sequence: both LEDs blink
- System ready: green LED slow blink
- Check-in: green LED solid for 3 seconds
- Check-out: red LED solid for 3 seconds
- Error: red LED blink
- WiFi connected: green LED pattern
- WiFi lost: red LED pattern
Troubleshooting
Section titled “Troubleshooting”Issue 1: LEDs Too Dim
Section titled “Issue 1: LEDs Too Dim”Symptom: LEDs barely visible even in indoor lighting
Cause: Resistor value too high
Solution:
// Try lower resistor values:// For 10mA current:// R = (3.3V - 2.0V) / 0.01A = 130Ω → Use 150Ω
// For 15mA (max recommended):// R = (3.3V - 2.0V) / 0.015A = 87Ω → Use 100ΩIssue 2: LEDs Too Bright
Section titled “Issue 2: LEDs Too Bright”Symptom: LEDs are uncomfortably bright
Solution: Increase resistor value or use PWM for software brightness control:
// Software brightness control using PWMvoid setLEDBrightness(int pin, int brightness) { // brightness: 0-255 analogWrite(pin, brightness);}
// Use in setup:// ledcSetup(0, 5000, 8); // Channel 0, 5kHz, 8-bit// ledcAttachPin(LED_GREEN, 0);// ledcWrite(0, 128); // 50% brightnessIssue 3: Wrong LED Lights Up
Section titled “Issue 3: Wrong LED Lights Up”Symptom: Green LED lights up for check-out instead of check-in
Cause: LED wiring reversed (cathode/anode swapped) or code logic error
Solution:
// Verify wiring: LED anode (+) → GPIO via resistor → LED cathode (-) → GND// Check with multimeter: GPIO HIGH should light the LED
// If reversed:// digitalWrite(pin, HIGH) → OFF (if LED wired reversed)// digitalWrite(pin, LOW) → ON// Swap in code:// #define LED_ON LOW// #define LED_OFF HIGHBest Practices
Section titled “Best Practices”- ✅ Recommended: Use consistent LED meanings (green = good, red = bad)
- ✅ Recommended: Include startup test sequence so users know both LEDs work
- ✅ Recommended: Use non-blocking LED control (
millis()instead ofdelay()) - ❌ Avoid: Using
delay()in the LED control code (blocks other operations) - ❌ Avoid: Driving LEDs at maximum current — 10mA is sufficient for indication
Summary
Section titled “Summary”- Green LED: System ready (blink), check-in success (3s solid)
- Red LED: Error (blink), check-out success (3s solid)
- Non-blocking control: Use
millis()timers, notdelay() - MQTT feedback: Node-RED publishes LED commands to
asset/tracking/feedback - Startup sequence: verify both LEDs are functional at power-on
References
Section titled “References”Writing Date: 2026-05-17
Based on Source File: 校正版/10 Time recording witht RFID und TimeTagger.md
Target Audience: Alibaba.com IoT Pre-sales Engineer
Status: ✅ Completed