Automated sunrise s...

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

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
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

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 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.setBrightness(BRIGHTNESS);
}

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

#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!");
}

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

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

void setupLEDs() {
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() {
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.

(@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...

(@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.

(@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...