(ESP32 Project) Light Dimmer and Intensity Control of LED

This project shows how to control the intensity of an LED connected to an ESP32 WROOM board. The control is through an Android App that displays a slider that can be moved to vary the intensity of the LED. We have provided the circuit diagram as well as a "main.c" file that should be uploaded to the ESP32 device. A complete know-how has been provided about uploading this file. The test android app is available on the google playstore. Write to us if you need your own customized!
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

Circuit Diagram

Assemble this circuit first. An LED has been connected to GPIO13 through a common collector transistor. The red indicator LED on the board is used as an indicator that the ESP32 device is ready to accept a connection from the mobile phone, so we do not have to use a separate LED for this purpose.

Video Explanation (see it happen!)

Please watch the following youtube video to see how the project works.

Test Android App

We created an android app that you can use for testing the above circuit.

Or, use this link:

https://play.google.com/store/apps/details?id=in.hoven.electrator

Source Code

This is the code for main.c file. This file has to be uploaded to the ESP32 device. Steps for uploading have been given next.

Enter your own wifi ssid and passwords before you upload to ESP32!

Copy this code and paste it in the "main.c" file. Then compile with Visual Studio Code Platform IO.

#define HIDE_LOG
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "driver/gptimer.h"
#include "esp_http_client.h"
#include "esp_mac.h"
#include "esp_http_server.h"
#include "cJSON.h"
#include "driver/gpio.h"
#include "driver/ledc.h"

// auth mode WPA2 for home wifi 
#define ESP_WIFI_SSID "--your-ssid--"
#define ESP_WIFI_PASS "--your-password--"

// WIFI MAC for ASP.NET Core Server, example 32a33489e4f61100, max 8 xx hex numbers 
char MAC_ADDRESS_WIFI[8 * 2 + 1] = { 0 };

//---------------- Event Messages -------------//

static const int MAX_QUE_SIZE = 100;

static const uint16_t MSG_WIFI_CONNECTED = 0x00;
static const uint16_t MSG_WIFI_DISCONNECTED = 0x01;
static const uint16_t MSG_WIFI_FAILED = 0x02;
static const uint16_t MSG_TIMER_TICK = 0x03;

static const uint16_t MSG_SAVE_LASTVAL = 0x04;

static const uint16_t MSG_SET_VALUE = 0x05;

//----------------log tags ---------------//

static const char* TAGWIFI = "tag:Wifi station";
static const char* TAGTCP = "tag:Tcp";
static const char* TAGAPP = "tag:App";
static const char* TAGBLINK = "tag:Blink";

// -------------- storage ----------------// 

static const char* NS_STORAGE = "storage";

static const char* KEY_LAST_VAL = "last_value";

static uint8_t g_value = 0;

//-------------------client commands from phone ----------//
static const uint8_t CMD_QUERY_STATE = 224;
static const uint8_t CMD_SAVE_STATE = 192;

// ---------------event que--------------// 

// user defined event queue 
static QueueHandle_t gpio_evt_queue = NULL;

void queue_message(uint16_t msg, uint16_t data)
{
  // upper 16 bytes hold data, lower 16 bytes hold message code 
  uint32_t m = ((data << 16) + msg);

  // RTOS https://www.freertos.org/a00119.html 
  xQueueSendFromISR(gpio_evt_queue, (void*)&m, NULL);
}

//-----------------system timer events ---------------//

gptimer_handle_t gptimer = NULL;

static bool timer_isr_handler(gptimer_handle_t timer, const gptimer_alarm_event_data_t* edata, void* user_ctx)
{
  queue_message(MSG_TIMER_TICK, 0);

  return false;
}

// ------------------ events of wifi driver --------------------- // 

#define ESP_MAXIMUM_RETRY 15

static int s_retry_num = 0;

static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
  if (WIFI_EVENT == event_base)
  {
    switch (event_id)
    {
    // station started 
    case WIFI_EVENT_STA_START: 
    {
      esp_wifi_connect();
    }
    break;

    // wifi could not connect at all OR if it was connected, then some disruption occurred 
    case WIFI_EVENT_STA_DISCONNECTED: 
    {
      if (s_retry_num < ESP_MAXIMUM_RETRY)
      {
        vTaskDelay(4000 / portTICK_PERIOD_MS);

        esp_wifi_connect();

        s_retry_num++;

        ESP_LOGI(TAGWIFI, "wifi state is disconnected, retrying...");

        queue_message(MSG_WIFI_DISCONNECTED, 0);
      }
      else
      {
        // raise failure event 
        queue_message(MSG_WIFI_FAILED, 0);
      }
    }
    break;

    case WIFI_EVENT_STA_CONNECTED:
    {
      wifi_event_sta_connected_t* event = (wifi_event_sta_connected_t*)event_data;

      ESP_LOGI(TAGWIFI, "Connected to: %s", (char*)event->ssid);

      s_retry_num = 0;
    }
    break;

    default:
    }
  }
  else if (IP_EVENT == event_base)
  {
    if (IP_EVENT_STA_GOT_IP == event_id)
    {
      ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;

      ESP_LOGI(TAGWIFI, "Connected as IP: " IPSTR, IP2STR(&event->ip_info.ip));

      s_retry_num = 0;

      queue_message(MSG_WIFI_CONNECTED, 0);
    }
  }
}

// -----------------forward declarations of helpers -----------// 

void tcp_server();

// called before restart 
void do_shutdown();

// ----------------- main -----------------// 

void app_main(void)
{
  //---------------- basic setup and init-------------//

  ESP_LOGI(TAGAPP, "[APP] Startup..");
  ESP_LOGI(TAGAPP, "[APP] Free memory: %lu bytes", esp_get_free_heap_size());
  ESP_LOGI(TAGAPP, "[APP] IDF version: %s", esp_get_idf_version());

  // levels NONE, ERROR, WARN, INFO, DEBUG, VERBOSE 
#ifdef HIDE_LOG
  esp_log_level_set("*", ESP_LOG_NONE);
  esp_log_level_set(TAGBLINK, ESP_LOG_INFO);
#else
  esp_log_level_set("*", ESP_LOG_DEBUG);
#endif

  esp_err_t ret = nvs_flash_init();

  if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  {
    ESP_ERROR_CHECK(nvs_flash_erase());

    ret = nvs_flash_init();
  }

  ESP_ERROR_CHECK(ret);
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());

  esp_register_shutdown_handler(do_shutdown);

  // ----------- event queue for messages ---------------// 

  // message loop is running at the end of main 
  gpio_evt_queue = xQueueCreate(MAX_QUE_SIZE, sizeof(uint32_t));

  // -------------- configure wifi ----------// 

  esp_netif_create_default_wifi_sta();

  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

  ESP_ERROR_CHECK(esp_wifi_init(&cfg));

  wifi_config_t wifi_config = {

      .sta = {
          .ssid = ESP_WIFI_SSID,

          .password = ESP_WIFI_PASS,
          
          // threshold auth mode WPA2 for home wifi 
          .threshold.authmode = WIFI_AUTH_WPA2_PSK, 

          .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
      },
  };

  // also saves to non-volatile-memory 
  esp_wifi_set_config(WIFI_IF_STA, &wifi_config);

  esp_event_handler_instance_t instance_any_id;

  esp_event_handler_instance_t instance_got_ip;

  ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
    ESP_EVENT_ANY_ID,
    &event_handler,
    NULL,
    &instance_any_id));

  ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
    IP_EVENT_STA_GOT_IP,
    &event_handler,
    NULL,
    &instance_got_ip));

  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

  ESP_ERROR_CHECK(esp_wifi_start());

  {
    uint8_t mac[8];

    esp_efuse_mac_get_default(mac);

    sprintf(MAC_ADDRESS_WIFI, "%02x%02x%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]);

    ESP_LOGI(TAGWIFI, "MAC Address %s", MAC_ADDRESS_WIFI);
  }

  //---------------- timer and its callback ISR ---------------------//
  {
    gptimer_config_t timer_config = {
        
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        
        .direction = GPTIMER_COUNT_UP,
        
        // 1MHz, 1 tick = 1us 
        .resolution_hz = 1 * 1000 * 1000, 
    };

    gptimer_new_timer(&timer_config, &gptimer);

    gptimer_alarm_config_t alarm_config = {
        
        // counter will reload with 0 on alarm event 
        .reload_count = 0,                  

        // period = 1 x 1s @resolution 1MHz 
        .alarm_count = 1 * 1000000,         

        // enable auto-reload 
        .flags.auto_reload_on_alarm = true, 
    };

    gptimer_set_alarm_action(gptimer, &alarm_config);

    gptimer_event_callbacks_t cbs = {

        // register user callback 
        .on_alarm = timer_isr_handler, 
    };

    ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
  }

  gptimer_enable(gptimer);

  gptimer_start(gptimer);

  //----------- configure IO Pins for output --------------- //

  const int BLINK_GPIO = GPIO_NUM_4;

  gpio_reset_pin(BLINK_GPIO);

  gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);

  // ----------- start tcp server -------------------// 

  xTaskCreate(tcp_server, "tcp_server", 4096, NULL, 5, NULL);

  // --------------PWM setup ----------------------------// 

  // Prepare and then apply the LEDC PWM timer configuration 
  ledc_timer_config_t ledc_timer = {
      
      .speed_mode = LEDC_LOW_SPEED_MODE,
      
      .timer_num = LEDC_TIMER_0,
      
      // 3 - 13 for 5kHz 
      .duty_resolution = LEDC_TIMER_7_BIT, 
      
      // Set output frequency at 5 kHz 
      .freq_hz = 5000,                     
      
      .clk_cfg = LEDC_AUTO_CLK 
      
  };

  ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

  // Prepare and then apply the LEDC PWM channel configuration 
  ledc_channel_config_t ledc_channel = {

      .speed_mode = LEDC_LOW_SPEED_MODE,

      .channel = LEDC_CHANNEL_0,

      .timer_sel = LEDC_TIMER_0,

      .intr_type = LEDC_INTR_DISABLE,

      .gpio_num = GPIO_NUM_13,

      .duty = 0,

      .hpoint = 0 
  
  };

  ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));

  //----------- start the event message loop--------------- //

  uint32_t message_id;

  bool is_wifi_connected = false;

  while (true)
  {
    if (xQueueReceive(gpio_evt_queue, &message_id, portMAX_DELAY))
    {
      uint16_t msg = (message_id & 0xff);

      switch (msg)
      {

      case MSG_WIFI_CONNECTED:
      {
        is_wifi_connected = true;
      }
      break;

      // raised multiple times during reconnection 
      case MSG_WIFI_DISCONNECTED:
      {
        is_wifi_connected = false;
      }
      break;

      case MSG_WIFI_FAILED:
      {
        ESP_LOGI(TAGWIFI, "Wifi failed after %d attempts.", ESP_MAXIMUM_RETRY);

        goto gracefulRestart;
      }
      break;

      case MSG_TIMER_TICK:
      {
        static bool isOn = true;

        gpio_set_level(BLINK_GPIO, isOn = is_wifi_connected ? !isOn : false);

        // just a dummy blink of the red led 
        ESP_LOGI(TAGBLINK, "timer ticking...");
        ESP_LOGI(TAGBLINK, "timer ticking...");
      }
      break;

      case MSG_SET_VALUE:
      {
        g_value = (uint8_t)(message_id >> 16);

        ESP_LOGD(TAGAPP, "data received %d", g_value);

        ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, (int)((126 * g_value) / 100));

        ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
      }
      break;

      case MSG_SAVE_LASTVAL:
      {
        // read last value from nvs 
        nvs_handle_t h;

        if (ESP_OK == nvs_open(NS_STORAGE, NVS_READWRITE, &h))
        {
          if (ESP_OK == nvs_set_u8(h, KEY_LAST_VAL, g_value))
          {
            nvs_commit(h);

            ESP_LOGD(TAGAPP, "NVS cached = %d", g_value);
          }

          nvs_close(h);
        }
      }
      break;

      default:
      {
        ESP_LOGI(TAGAPP, "Unmapped message received . . .");
      }
      break;
      }
    }
  }

gracefulRestart:

  // cleanup - unregister 
  esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &instance_any_id);

  esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &instance_got_ip);

  // restart 
  esp_restart();
}

// this function is called on a graceful restart or exit 
void do_shutdown()
{
  // shutdown already done 
  if (NULL == gptimer)
  {
    return;
  }

  ESP_LOGI(TAGAPP, "cleanup shutdown . . .");

  gptimer_stop(gptimer);

  gptimer_disable(gptimer);

  gptimer_del_timer(gptimer);

  vQueueDelete(gpio_evt_queue);

  esp_wifi_stop();

  gptimer = NULL;

  esp_unregister_shutdown_handler(do_shutdown);
}

void tcp_server()
{
  {
    uint8_t lastValue = 0;

    // read last value from nvs 
    nvs_handle_t h;

    if (ESP_OK == nvs_open(NS_STORAGE, NVS_READWRITE, &h))
    {
      if (ESP_OK != nvs_get_u8(h, KEY_LAST_VAL, &lastValue))
      {
        lastValue = 0;
      }

      nvs_close(h);
    }

    g_value = lastValue;

    queue_message(MSG_SET_VALUE, (uint16_t)lastValue);
  }

  ESP_LOGD(TAGAPP, "NVS last_value = %d", g_value);

  int keepAlive = 1;
  int keepIdle = 5;
  int keepInterval = 5;
  int keepCount = 3;
  int PORT = 50000;
  struct sockaddr_storage dest_addr;
  struct sockaddr_in* dest_addr_ip4 = (struct sockaddr_in*)&dest_addr;
  dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
  dest_addr_ip4->sin_family = AF_INET;
  dest_addr_ip4->sin_port = htons(PORT);

  int attempts = 0;

  while (attempts++ < 5)
  {
    int listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    if (listen_socket >= 0)
    {
      ESP_LOGI(TAGTCP, "Socket created");

      attempts = 0;

      int opt = 1;

      setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

      int err = bind(listen_socket, (struct sockaddr*)&dest_addr, sizeof(dest_addr));

      if (0 == err)
      {

        ESP_LOGI(TAGTCP, "Socket bound, port %d", PORT);

        err = listen(listen_socket, 1);

        if (0 == err)
        {

          int sock = -1;

          do
          {
            ESP_LOGI(TAGTCP, "waiting for connection...");

            struct sockaddr_storage source_addr;

            socklen_t addr_len = sizeof(source_addr);

            sock = accept(listen_socket, (struct sockaddr*)&source_addr, &addr_len);

            if (sock < 0)
            {
              ESP_LOGE(TAGTCP, "Unable to accept connection: errno %d", errno);
            }
            else
            {
              ESP_LOGI(TAGTCP, "client connected!");

              // Set tcp keepalive option 
              setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
              setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
              setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
              setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));

              unsigned char data;

              while (recv(sock, &data, sizeof(data), 0) > 0)
              {
                // signals from client are negative numbers 
                if (data & 128)
                {
                  // 1-1-1-x [don't care] read g_value 
                  if (data == CMD_QUERY_STATE) 
                  {
                    ESP_LOGI(TAGTCP, "ping received sending %d...", g_value);

                    if (send(sock, &g_value, sizeof(g_value), 0) <= 0)
                      break;
                  }
                  // 1-1-x [don't care] save last value 
                  else if (data == CMD_SAVE_STATE) 
                  {
                    // MSG_SAVE_LASTVAL 
                    queue_message(MSG_SAVE_LASTVAL, 0);
                  }
                  // it's a ping 
                  else 
                  {
                    unsigned char resp = 0x1;

                    if (send(sock, &resp, sizeof(resp), 0) <= 0)
                      break;
                  }
                }
                else
                {
                  queue_message(MSG_SET_VALUE, (uint16_t)data);
                }
              };

              ESP_LOGI(TAGTCP, "client disconnected");

              ESP_LOGI(TAGTCP, "shutting down connection...");

              shutdown(sock, 0);

              close(sock);
            }

          } while (sock >= 0);
        }
        else
        {
          ESP_LOGE(TAGTCP, "Error occurred during listen: errno %d", errno);
        }
      }
      else
      {
        ESP_LOGE(TAGTCP, "Socket unable to bind: errno %d", errno);
      }

      close(listen_socket);
    }

    ESP_LOGE(TAGTCP, "Socket closed. Retrying after 8 seconds...");

    // very rarely expected to reach here. so wait 8 seconds 
    vTaskDelay(8000 / portTICK_PERIOD_MS);
  }

  vTaskDelete(NULL);
}

How to Upload to ESP32

Follow the third video of this playlist to upload "main.c" file.


This Blog Post/Article "(ESP32 Project) Light Dimmer and Intensity Control of LED" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.