Notifications
Clear all

Automated sunrise sunset lamp using LED arrays and ESP32

4 Posts
1 Users
0 Reactions
307 Views
(@wild_bill)
Member
Joined: 7 months ago
Posts: 13
Topic starter  

 

PXL 20240807 155214617

Hi Bill, I hope your mom is doing better and that it probably takes a lot of your time. So I know I had asked about this before and got a couple suggestions from you and other members. I wasn't able to express what I wanted to create adequately thus the responses I got were things I had already tried. Sooo..., I got an Open AI account and started to use chat GPT, a whole other lesson in communication. After much frustration and feeling like I just became  'Costello' in "Who's on First?" many times. I finally managed to get it to understand what I wanted and, with much trial and error it came up with this Python sim that does almost what I want. Near enough to call it success for now.

 

import matplotlib
print("Using backend:", matplotlib.get_backend())
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# Constants
NUM_ROWS, NUM_COLS = 16, 32
CENTER_ROW, CENTER_COL = NUM_ROWS // 2, NUM_COLS // 2
TOTAL_BRIGHTNESS = 10000  # Total desired brightness at peak
MIN_BRIGHTNESS = 5  # Minimal brightness to show at the night phase

# Initialize the LED matrix
led_matrix = np.zeros((NUM_ROWS, NUM_COLS))

def calculate_brightness(frame, total_brightness):
    """Calculate the brightness distribution based on the frame number."""
    time_of_day = frame % 1440  # Modulo 1440 to simulate a 24-hour cycle
    # Adjust these times to match your desired timings
    if time_of_day < 420 or time_of_day >= 1290:  # Extended night phase
        current_brightness = MIN_BRIGHTNESS  # Keep minimal brightness
    elif time_of_day < 720:  # Sunrise from 0700 to 1200
        current_brightness = (time_of_day - 420) / 300 * total_brightness  # Gradual increase
    elif 720 <= time_of_day < 990:  # Daylight from 1200 to 2130
        current_brightness = total_brightness
    else:  # Sunset from 2130 to 2150
        current_brightness = (1290 - time_of_day) / 300 * total_brightness  # Gradual decrease

    # Adjust the matrix values
    for row in range(NUM_ROWS):
        for col in range(NUM_COLS):
            distance = np.sqrt((CENTER_ROW - row) ** 2 + (CENTER_COL - col) ** 2)
            led_matrix[row, col] = current_brightness / (1 + distance)  # Inverse distance weighting

    return led_matrix

# Visualization setup
fig, ax = plt.subplots()
img = ax.imshow(led_matrix, cmap='hot', interpolation='nearest', vmin=0, vmax=255)
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes, color='white')

def update(frame):
    """Update function for the animation."""
    brightness_matrix = calculate_brightness(frame, TOTAL_BRIGHTNESS)
    img.set_data(brightness_matrix)
    # Update the time display
    hours = (frame // 60) % 24
    minutes = frame % 60
    time_text.set_text(f'Time: {hours:02}:{minutes:02}')
    return img, time_text,

# Create the animation
ani = animation.FuncAnimation(fig, update, frames=1440, interval=100, blit=True, repeat=True)
plt.show()

 After using the LLM to do some analysis I decided to use an ESP32 WROVER E as the controller, I have DS3231RTC, on oled for display of time so I have a visual if the board hangs. The main code relies on 2 custom Libraries. One for the SSID info called connections, that I won't post as I got the idea from you. The second library I had Chat GPT write to allow for custom arrays. Mine uses 2 8x32 WS2812B rotated 180 in relation to ea other. The code allows for custom mapping of arrays and does the calcs.

#ifndef CUSTOM_MATRIX_H
#define CUSTOM_MATRIX_H

#include <FastLED.h>
#define NUM_LEDS_PER_MATRIX 256  // Adjust according to your setup
#define COLOR_ORDER GRB  // GRB color order for WS2812B
#define BRIGHTNESS 255

struct MatrixConfig {
    uint16_t pin;
    uint16_t numLEDs;
    bool rotate180;  // Indicate if the matrix is rotated
};

// Define matrix configurations
const MatrixConfig matrixConfigs[] = {
    {5, NUM_LEDS_PER_MATRIX, false},  // First matrix, pin 5, no rotation
    {4, NUM_LEDS_PER_MATRIX, true}    // Second matrix, pin 4, rotated
};

CRGB leds_1[NUM_LEDS_PER_MATRIX];
CRGB leds_2[NUM_LEDS_PER_MATRIX];

void setupLEDs() {
    FastLED.addLeds<WS2812B, matrixConfigs[0].pin, COLOR_ORDER>(leds_1, matrixConfigs[0].numLEDs).setCorrection(TypicalLEDStrip);
    FastLED.addLeds<WS2812B, matrixConfigs[1].pin, COLOR_ORDER>(leds_2, matrixConfigs[1].numLEDs).setCorrection(TypicalLEDStrip);
    FastLED.setBrightness(BRIGHTNESS);
}

void updateLightingPattern(void * parameter) {
    for (;;) {
        // Implement lighting pattern updates here
        FastLED.show();
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
}

#endif // CUSTOM_MATRIX_H

 

And this brings us to the main body of code. Doing this exercise has shown me what I was failing to communicate the first time. My thought was this, I have an array of, in this case, 512 individually addressable LEDS. Each of these LED can be controlled, in real use without burnout, in 195 cubed possible light/luminosity combinations TIMES 512 leds. This leads to a much richer possible combination of luminosity and palette. Unfortunately, that level of calculus is FAR FAR beyond my abilities.... And it seems my ability to communicate with Chat GPT, the more subtle effects I was looking for. I can't get a "blue" phase to represent the time when the blue waves are being scattered before and after sunrise/set without way too many LEDs lighting in the sim. Maybe you could fix it but I know my limits. I found that if I tried for any type of combination of cycles ended up with a dead period so this is one long cycle. It has a "test" loop that uses a boolean user variable to run off RTC or a 12 min "Sim" day. It uses both cores. based on these criteria I asked Chat GPT summarize

Sure, let’s review and confirm the features and functionalities that the code should include, based on our extensive discussion:

  1. Dual-Core Operation:

    • One core manages the OLED display and RTC to display the current time.
    • The other core handles LED control for two matrices, ensuring independent and simultaneous operation.
  2. LED Control:

    • Two LED arrays, controlled by separate pins (Pin 5 and Pin 4).
    • Custom LED mapping managed through a CustomMatrix.h library, allowing for future configurations of additional arrays with different setups.
    • Simulation of a 24-hour light cycle, with specified timings for sunrise, full daylight, sunset, and night.
    • The ability to test the lighting pattern with a sped-up cycle (12-minute cycle simulating 24 hours).
  3. Timekeeping:

    • Use of an RTC (Real-Time Clock) for accurate time management.
    • NTP (Network Time Protocol) integration for initial time setting or correction if the RTC has lost power or drifted.
    • Automatic adjustment of the RTC if the time drifts by 5 seconds or more, either on board reset or after 100 cycles.
  4. WiFi Connectivity:

    • Connection to a WiFi network using credentials stored in the connections.h library.
    • This functionality is critical for NTP time synchronization.
  5. OLED Display:

    • Display of the current time for visual verification of system functionality.
    • Useful for checking if the board is functioning correctly without needing to connect to a serial monitor.
  6. User Variables and Configurability:

    • Top of the code will include user-defined variables for easy configuration (e.g., LED brightness, test cycle duration).
    • Comments explaining each variable’s purpose and impact on the system, ensuring clarity for future adjustments or expansions.
  7. Libraries and Modular Code:

    • CustomMatrix.h for custom LED matrix configuration, supporting different array orientations and connections.
    • connections.h for WiFi credentials, keeping sensitive information separate from the main program logic.
  8. System Robustness and Debugging:

    • Serial outputs for debugging and system status updates, especially during WiFi connection attempts and system initialization.
    • Error handling for RTC failures and WiFi connectivity issues, preventing system hang-ups.
#include <Arduino.h>
#include <FastLED.h>
#include "CustomMatrix.h"
#include "RTClib.h"
#include <U8g2lib.h>
#include <WiFi.h>
#include "connections.h"

// Definitions for LED pinouts and types
#define LED_PIN_1 5
#define LED_PIN_2 4
#define COLOR_ORDER GRB
#define CHIPSET WS2812B
#define BRIGHTNESS 255

// Total number of LEDs per matrix
const int totalLEDs = NUM_LEDS_PER_MATRIX;

// Create LED arrays
CRGB leds_1[totalLEDs];
CRGB leds_2[totalLEDs];

// OLED and RTC setup
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
RTC_DS3231 rtc;

// Function prototypes
void setupLEDs();
void displayTime();
void updateLightingPattern();
void connectToWiFi();

void setup() {
  Serial.begin(115200);
  connectToWiFi(); // Connect to WiFi network
  setupLEDs();
  oled.begin();
  oled.setFont(u8g2_font_ncenB08_tr);
  
  // RTC setup
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // Setup tasks on different cores
  xTaskCreatePinnedToCore(
    updateLightingPattern, /* Task function */
    "updateLEDs", /* Name of the task */
    10000,  /* Stack size in words */
    NULL,  /* Task input parameter */
    1,  /* Priority of the task */
    NULL,  /* Task handle */
    0); /* Core where the task should run */
}

void loop() {
  displayTime();  // Update the OLED with current time
  delay(1000);  // Update every second
}

void setupLEDs() {
  FastLED.addLeds<CHIPSET, LED_PIN_1, COLOR_ORDER>(leds_1, totalLEDs).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<CHIPSET, LED_PIN_2, COLOR_ORDER>(leds_2, totalLEDs).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

void displayTime() {
  DateTime now = rtc.now();
  oled.clearBuffer();
  oled.setCursor(0, 10);
  oled.print("Time: ");
  oled.print(now.hour(), DEC);
  oled.print(':');
  oled.print(now.minute(), DEC);
  oled.print(':');
  oled.print(now.second(), DEC);
  oled.sendBuffer();
}

void connectToWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi..");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to the WiFi network");
}

void updateLightingPattern(void * parameter) {
  for (;;) {
    // Update your LED pattern here
    FastLED.show();
    vTaskDelay(10 / portTICK_PERIOD_MS); // Update every 10 ms
  }
}

 

 

Mine has 2 arrays, pins 4 and 5, I used wire wrapping on the board in the attached pics. I used a $20 toolbox to mount the 5V 80A power supply in, A generic dollar store picture frame to mount the array and a couple terminal strips to make it somewhat "modular" for placement and hook up. I am gong to add a version of code that will allow for this to be used as a main controller with peripherals running their own custom matrix and the sequence the second core runs. It may need tweaks as I got Chat GPT to make this an integrated package.

 

I hope this gives you a video, I can't do much in the way of financial support at the moment, this is justified as a "Business Expense" for "Livestock" so... while I put in the work I don't get to see it except in my store making the frogs happy. It's yours free of any encumbrance, but an honorable mention would be nice LOL. 

 

The reason I went with using both cores is that I want to add future basic vivarium controls to this such as automated top up off the water reservoir, maybe a temp controlled vent fan, the setup means those can be added to the core controlling time and display etc and leave the second to handle the light cycle.

PXL 20240807 155214617
PXL 20240807 155209613
PXL 20240807 155221789

 


   
Quote
(@wild_bill)
Member
Joined: 7 months ago
Posts: 13
Topic starter  

FMG, I can't seem to proof read, The first sentence should have an ..I know...inserted...


   
ReplyQuote
(@wild_bill)
Member
Joined: 7 months ago
Posts: 13
Topic starter  

OK I must have had a dream where this complied correctly... Working on it.


   
ReplyQuote
(@wild_bill)
Member
Joined: 7 months ago
Posts: 13
Topic starter  

Ok I have had one of the most "disturbing" things happen. I gave myself a false memory obviously. I have a VERY vivid memory of getting this to work JUST like I wanted, came in the next morning. made this post all happy with what I had done.... And that was clearly wrong... I'll fix it and resubmit the code...


   
ReplyQuote