Very simple PWM control over OT for esp32 device
authorjweigele <jweigele@local>
Mon, 15 Jan 2024 02:52:44 +0000 (18:52 -0800)
committerjweigele <jweigele@local>
Mon, 15 Jan 2024 02:52:44 +0000 (18:52 -0800)
 * Works for first set of RGB LEDs (control over mosfet)
 * Configurable for GPIO but second set not active yet
 * Needs conditional logic for which GPIO is enabled +
   some gamma correction probably

aqi/main/aqi.c

index 076ba5643d0352058b85bd9213715c1a2d96d5b2..2763596a201d7afcf46a7b5ca2f5c97d26464fd0 100644 (file)
@@ -28,6 +28,26 @@ typedef struct aqi_data_s {
 } aqi_data_t;
 
 
+#ifdef CONFIG_PWM_ENABLED
+#include "driver/ledc.h"
+#define LEDC_TIMER              LEDC_TIMER_0
+#define LEDC_MODE               LEDC_LOW_SPEED_MODE
+#define R1_CHANNEL LEDC_CHANNEL_0
+#define R2_CHANNEL LEDC_CHANNEL_1
+#define G1_CHANNEL LEDC_CHANNEL_2
+#define G2_CHANNEL LEDC_CHANNEL_3
+#define B1_CHANNEL LEDC_CHANNEL_4
+#define B2_CHANNEL LEDC_CHANNEL_5
+#define LEDC_DUTY_RES           LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
+#define LEDC_DUTY               (4096) // Set duty to 50%. (2 ** 13) * 50% = 4096
+#define LEDC_FREQUENCY          (5000) // Frequency in Hertz. Set frequency at 5 kHz
+
+typedef struct pwm_data_s {
+    uint8_t msg[1024];
+    int len;
+} pwm_data_t;
+#endif
+
 typedef struct report_data_s {
 #ifdef CONFIG_UART_ENABLED    
   uint16_t pm10;
@@ -55,6 +75,10 @@ static int motion_pins[2] = {0, 0};
 
 #endif
 
+#ifdef CONFIG_PWM_ENABLED
+static QueueHandle_t pwm_queue;
+#endif
+
 // for tracking and reporting device uptime
 static uint32_t device_uptime_seconds = 0;
 
@@ -676,6 +700,122 @@ aqi_data_t get_average_pm25(uint8_t* data, int data_size){
 }
 
 
+#ifdef CONFIG_PWM_ENABLED
+static void pwm_task(void* discard){
+
+
+    // initialize PWM hardware
+    // Prepare and then apply the LEDC PWM timer configuration
+    ledc_timer_config_t ledc_timer = {
+        .speed_mode       = LEDC_MODE,
+        .timer_num        = LEDC_TIMER,
+        .duty_resolution  = LEDC_DUTY_RES,
+        .freq_hz          = LEDC_FREQUENCY,  // Set output frequency at 5 kHz
+        .clk_cfg          = LEDC_AUTO_CLK
+    };
+    ESP_LOGI(TAG, "configuring ledc timer");
+    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_MODE,
+        .channel        = R1_CHANNEL,
+        .timer_sel      = LEDC_TIMER,
+        .intr_type      = LEDC_INTR_DISABLE,
+        .gpio_num       = CONFIG_PWM_R1_GPIO,
+        .duty           = 0, // Set duty to 0%
+        .hpoint         = 0
+    };
+    ESP_LOGI(TAG, "configuring R1 PWM");
+    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
+
+
+    ledc_channel.channel = G1_CHANNEL;
+    ledc_channel.gpio_num = CONFIG_PWM_G1_GPIO;
+
+    ESP_LOGI(TAG, "configuring G1 PWM");
+    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
+
+    ledc_channel.channel = B1_CHANNEL; 
+    ledc_channel.gpio_num = CONFIG_PWM_B1_GPIO;
+    
+    ESP_LOGI(TAG, "configuring B1 PWM");
+    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
+
+
+    
+
+
+
+    //create a queue to handle gpio event from isr
+    // 1kb, should be enough for parsing?
+    pwm_queue = xQueueCreate(10, sizeof(pwm_data_t));
+    pwm_data_t recv;
+    ESP_LOGI(TAG, "waiting for pwm events");
+    cJSON* current_pwm;
+    cJSON* red_json;
+    cJSON* green_json;
+    cJSON* blue_json;
+    for(;;) {
+        if(xQueueReceive(pwm_queue, &recv, portMAX_DELAY)) {
+            ESP_LOGI(TAG, "received PWM data string %s len %d", recv.msg, recv.len);
+            current_pwm = cJSON_ParseWithLength(&recv.msg, recv.len);
+            if (cJSON_IsObject(current_pwm)){
+                ESP_LOGI(TAG, "parsed successfully I think");
+                char *json_string = cJSON_Print(current_pwm);
+                ESP_LOGI(TAG, "full json string decoded as %s", json_string);
+                red_json = cJSON_GetObjectItemCaseSensitive(current_pwm, "red");
+                if (cJSON_IsNumber(red_json)){
+                    ESP_LOGI(TAG, "parsed red float %f", red_json->valuedouble);
+                    float red_duty_float = (float)(pow(2,13)) * red_json->valuedouble;
+                    int red_duty_int = (int)red_duty_float;
+
+                    ESP_LOGI(TAG, "red duty int calculated as %d", red_duty_int);
+                    ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, R1_CHANNEL, red_duty_int));
+                    // Update duty to apply the new value
+                    ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, R1_CHANNEL));
+
+                }
+
+                green_json = cJSON_GetObjectItemCaseSensitive(current_pwm, "green");
+                if (cJSON_IsNumber(green_json)){
+                    ESP_LOGI(TAG, "parsed green float %f", green_json->valuedouble);
+                    float green_duty_float = (float)(pow(2,13)) * green_json->valuedouble;
+                    int green_duty_int = (int)green_duty_float;
+
+                    ESP_LOGI(TAG, "green duty int calculated as %d", green_duty_int);
+                    ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, G1_CHANNEL, green_duty_int));
+                    // Update duty to apply the new value
+                    ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, G1_CHANNEL));
+
+                }
+                
+
+                blue_json = cJSON_GetObjectItemCaseSensitive(current_pwm, "blue");
+                if (cJSON_IsNumber(blue_json)){
+                    ESP_LOGI(TAG, "parsed blue float %f", blue_json->valuedouble);
+                    float blue_duty_float = (float)(pow(2,13)) * blue_json->valuedouble;
+                    int blue_duty_int = (int)blue_duty_float;
+
+                    ESP_LOGI(TAG, "blue duty int calculated as %d", blue_duty_int);
+                    ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, B1_CHANNEL, blue_duty_int));
+                    // Update duty to apply the new value
+                    ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, B1_CHANNEL));
+
+                }
+                
+            } else {
+                ESP_LOGE(TAG, "failed to parse json successfully!");
+            }
+            cJSON_Delete(current_pwm);
+
+        }
+    }
+
+
+}
+#endif
+
 #if defined(CONFIG_MOTION_FIRST_ENABLED) || defined(CONFIG_MOTION_SECOND_ENABLED)
 static void IRAM_ATTR gpio_isr_handler(void* arg)
 {
@@ -962,6 +1102,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
         mqtt_connected = true;
         mqtt_was_reinit = false;
         ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
+// need to subscribe to MQTT topic for updates
+#ifdef CONFIG_PWM_ENABLED
+        int subret = esp_mqtt_client_subscribe(mqtt_client, CONFIG_PWM_TOPIC, 0);
+        if (subret == -1 ){
+            ESP_LOGE(TAG, "error subscribing to %s", CONFIG_PWM_TOPIC);
+        } else {
+            ESP_LOGI(TAG, "subscribed to %s for PWM updates", CONFIG_PWM_TOPIC);
+        }
+#endif
         break;
     case MQTT_EVENT_DISCONNECTED:
         if ( event->client != mqtt_client ){
@@ -985,6 +1134,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
         ESP_LOGI(TAG, "MQTT_EVENT_DATA");
         printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
         printf("DATA=%.*s\r\n", event->data_len, event->data);
+        pwm_data_t sendpwm;
+        pwm_data_t* sendpwm_ptr = &sendpwm; //malloc(sizeof(pwm_data_t));
+        memset(sendpwm_ptr, 0, 1024);
+        memcpy(sendpwm_ptr->msg, event->data, event->data_len);
+        sendpwm_ptr->len = event->data_len;
+        printf("sendpwm msg: %s\nlen: %d\n", sendpwm_ptr->msg, sendpwm_ptr->len);
+
+        xQueueSend(pwm_queue, sendpwm_ptr, portMAX_DELAY);
+
         break;
     case MQTT_EVENT_ERROR:
         if ( mqtt_client != NULL && event->client != mqtt_client ){
@@ -1129,6 +1287,10 @@ void app_main(void)
     //runit();
 #endif
 
+#ifdef CONFIG_PWM_ENABLED
+    ESP_LOGI(TAG, "installing pwm task");
+    xTaskCreate(pwm_task, "pwm_task", 4096, NULL, 2, NULL);
+#endif
 #ifdef CONFIG_OT_ENABLED
     esp_vfs_eventfd_config_t eventfd_config = {
         .max_fds = 3,