Fun 3hr lightning cloud project

I saw someone on TikTok that made a lightning cloud with some addressable LED’s and I had to make my own.

With some spare parts laying around I cobbled something up in just a few hours! the photo doesn’t do it justice

The wiring is very simple, I didn’t get a picture before I stuck it in the foam card, but its a USB-C PD-Buddy V1.0 that I had in a draw for the last few years, set to give a 5v 3A output - which is on the limit for this many LED’s, connected to a D1 mini ESP8266 and then to a string of 96 WS2812B RGB LED’s leftover from an old project. The LED’s were broken up into 4 strips of 24 LED’s. To try and keep the voltage high at the top end of the strip I just jumpered the power up rather than looping it all the way through, on other projects I found this helps keep the colour more even, not that it matters with something like this. Everything is just double sided taped on, including the stuffing from an old pillow

For the lightning effect I couldn’t find exactly what I wanted so I set about creating my own effect. it chooses a random width and a position, and does a really quick, but perceivable flash down, then flashes off then back on with some randomness thrown in, creating quick intense thin bolts through to wide slow bolts. The colour is randomised too, it needs a tweak still… its a bit vibrant at the moment. It was fun trying to figure out the maths to get the positions lined up due to the way the LED’s are looped, got the brain juices flowing good! I also have a few other effects thrown in for fun too, the fireworks effect also looks pretty good for simulating lightning .

esphome:
  name: lightning
  friendly_name: lightning

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxx"

ota:
  password: "xxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Lightning Fallback Hotspot"
    password: "xxx"

captive_portal:

web_server:
  port: 80

globals:
  - id: red_level
    type: float
    restore_value: no
    initial_value: '0'
  - id: green_level
    type: float
    restore_value: no
    initial_value: '0'
  - id: blue_level
    type: float
    restore_value: no
    initial_value: '0'
  - id: width
    type: int
    restore_value: no
    initial_value: '0'
  - id: leftness
    type: int
    restore_value: no
    initial_value: '0'
  - id: rightness
    type: int
    restore_value: no
    initial_value: '0'

light:
  - platform: neopixelbus
    type: GRB
    variant: WS2811
    pin: D4
    num_leds: 96
    name: "Lightning"
    id: lightning_rgb
    effects:
      - automation:
          name: "Lightning Randomised"
          sequence:
            - light.addressable_set:
                id: lightning_rgb
                red: 0%
                green: 0%
                blue: 0%
            - delay: 100ms
            - lambda: |-
                id(red_level) = (((rand() % 100) + 150) / 255.0);
                ESP_LOGD("TAG", "Red: %f", id(red_level));
            - lambda: |-
                id(green_level) = (((rand() % 200) + 50) / 255.0);
                ESP_LOGD("TAG", "Green: %f", id(green_level));
            - lambda: |-
                id(blue_level) = (((rand() % 100) + 150) / 255.0);
                ESP_LOGD("TAG", "Blue: %f", id(blue_level));
            - lambda: |-
                id(width) = (rand() % 23);
                ESP_LOGD("TAG", "Width: %d", id(width));
            - lambda: |-
                id(leftness) = (rand() % (23 - id(width)));
                ESP_LOGD("TAG", "Leftness: %d", id(leftness));
            - lambda: |-
                id(rightness) = (id(leftness) + id(width));
                ESP_LOGD("TAG", "Rightness: %d", id(rightness));
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(95 - id(rightness));
                range_to: !lambda return(95 - id(leftness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness) + 48);
                range_to: !lambda return(id(rightness) + 48);
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(47 - id(leftness));
                range_to: !lambda return(47 - id(rightness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness));
                range_to: !lambda return(id(rightness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 20ms  
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(95 - id(rightness));
                range_to: !lambda return(95 - id(leftness));
                red: 0%
                green: 0%
                blue: 10%
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness) + 48);
                range_to: !lambda return(id(rightness) + 48);
                red: 0%
                green: 0%
                blue: 10%
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(47 - id(rightness));
                range_to: !lambda return(47 - id(leftness));
                red: 0%
                green: 0%
                blue: 10%
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness));
                range_to: !lambda return(id(rightness));
                red: 0%
                green: 0%
                blue: 10%
            - delay: 30ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(95 - id(rightness));
                range_to: !lambda return(95 - id(leftness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness) + 48);
                range_to: !lambda return(id(rightness) + 48);
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(47 - id(rightness));
                range_to: !lambda return(47 - id(leftness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: 10ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness));
                range_to: !lambda return(id(rightness));
                red: !lambda return(id(red_level));
                green: !lambda return(id(green_level));
                blue: !lambda return(id(blue_level));
            - delay: !lambda return((rand() % 100) + 80);  
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(95 - id(rightness));
                range_to: !lambda return(95 - id(leftness));
                red: 10%
                green: 0%
                blue: 10%
            - delay: 20ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness) + 48);
                range_to: !lambda return(id(rightness) + 48);
                red: 10%
                green: 0%
                blue: 10%
            - delay: 20ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(47 - id(rightness));
                range_to: !lambda return(47 - id(leftness));
                red: 10%
                green: 0%
                blue: 10%
            - delay: 20ms
            - light.addressable_set:
                id: lightning_rgb
                range_from: !lambda return(id(leftness));
                range_to: !lambda return(id(rightness));
                red: 10%
                green: 0%
                blue: 10%
            - delay: !lambda return((rand() % 100) + 80);
            - light.addressable_set:
                id: lightning_rgb
                range_from: 0
                range_to: 96
                red: 0%
                green: 0%
                blue: 0%
            - delay: !lambda return((rand() % 1000 + 1) * 10);
      - lambda:
          name: randomflicker
          update_interval: 200 ms
          lambda: |-
            auto call = id(lightning_rgb).turn_on();
            float r = random_float() * 2.0f - 1.0f; 
            r =  r * r * r;
            call.set_transition_length(r);
            call.set_brightness(0.7 + r/10);
            call.set_publish(false);
            call.set_save(false);
            call.perform();
      - lambda:
          name: Chill Mode
          update_interval: 60s
          lambda: |-
            auto call = id(lightning_rgb).turn_on();
            call.set_transition_length(5000);
            call.set_white(0.0);
            esphome::Color c = esphome::Color::random_color();
            call.set_rgb((c.red%255)/255.0, (c.green%255)/255.0, (c.blue%255)/255.0);
            call.set_publish(false);
            call.set_save(false);
            call.perform();
      - lambda:
          name: Chill Mode (Vibrant)
          update_interval: 60s
          lambda: |-
            auto call = id(lightning_rgb).turn_on();
            call.set_transition_length(5000);
            call.set_white(0.0);
            esphome::Color c = esphome::Color::random_color();
            call.set_rgb((c.red%128)/255.0, (c.green%128)/255.0, (c.blue%128)/255.0);
            call.set_publish(false);
            call.set_save(false);
            call.perform();
      - lambda:
          name: Disco
          update_interval: 500ms
          lambda: |-
            auto call = id(lightning_rgb).turn_on();
            call.set_transition_length(rand() % 400 + 1);
            //call.set_transition_length(500);
            call.set_white(0.0);
            esphome::Color c = esphome::Color::random_color();
            call.set_rgb((c.red)/255.0, (c.green)/255.0, (c.blue)/255.0);
            call.set_publish(false);
            call.set_save(false);
            call.perform();
      - lambda:
          name: WTF
          update_interval: 200ms
          lambda: |-
            auto call = id(lightning_rgb).turn_on();
            call.set_transition_length(200);
            call.set_white(0.0);
            call.set_brightness((rand() % 255 + 1) / 400.0);
            call.perform();   
      - addressable_rainbow:
          name: Rainbow
          speed: 10
          width: 15
      - addressable_scan:
          name: Scan
          move_interval: 50ms
          scan_width: 1
      - addressable_twinkle:
          name: Twinkle
          twinkle_probability: 5%
          progress_interval: 32ms
      - addressable_random_twinkle:
          name: Colour Twinkle
          twinkle_probability: 5%
          progress_interval: 32ms
      - addressable_fireworks:
          name: Fireworks
          update_interval: 32ms
          spark_probability: 10%
          use_random_color: false
          fade_out_rate: 80
      - addressable_fireworks:
          name: Fireworks Slow
          update_interval: 64ms
          spark_probability: 20%
          use_random_color: false
          fade_out_rate: 90
      - addressable_fireworks:
          name: Colour Fireworks
          update_interval: 32ms
          spark_probability: 8%
          use_random_color: true
          fade_out_rate: 80

my plan is to change between the fireworks effect and the random lightning effects and see how it looks… ill update once i get it looking nice.

I’ve also ordered a speaker, I want to mess around with making some thundery sounds to go with it, maybe some rain sounds too

1 Like