Difference between revisions of "Techinc Wheel"

From Technologia Incognita
Jump to: navigation, search
m (led strip)
 
(29 intermediate revisions by 7 users not shown)
Line 2: Line 2:
 
|picture=20130729-techinc-wheel-at-ohm.jpeg
 
|picture=20130729-techinc-wheel-at-ohm.jpeg
 
|ProjectSkills=Electronics, Mechanics,
 
|ProjectSkills=Electronics, Mechanics,
|ProjectStatus=Active
+
|ProjectStatus=Finished
 
|ProjectNiche=Electronics
 
|ProjectNiche=Electronics
 
|ProjectPurpose=Promotion
 
|ProjectPurpose=Promotion
 
}}
 
}}
Formerly known as ARTS: Auto stereoscopic Rainbow glowing TechInc logo Steering wheel
+
== Bringing the wheel to events ==
  
== Code ==
+
If you can't reach Narya or Mariejel, assume a "yes" when you (a techinc member) want to bring the wheel to an event. Please put it back on the wall when the event is over. If the LEDs come loose you can simply put it back with double-sided tape and/or ductape.
[https://github.com/techinc/wheel PCB layout and code for the LEDs] - ''work in progress''
 
  
== Things to make and do ==
+
== Summary ==
This project has several parts: '''backlit''' the wheel, '''mount''' the wheel to the wall or on some stand, and make the wheel '''control''' something. It would be nice to be able to unmount the wheel and take it somewhere else, e.g. to display it at events like OHM.
+
 
 +
* The wooden techinc wheel has a WS2801 LED strip stuck on the back and is controlled via an <s>Arduino Nano</s> esp8266 with ESPHome
 +
* The 8 strips consist of 5 pixels each (3 LEDs per pixel). The first "spoke" goes outwards, the second inwards, the third outwards, etc
 +
* The data pin is GPIO4 and the clock pin GPIO2
 +
* Color order is BRG, which is supported by the FastLED library, but for other libraries you need to put the colors in the right order
 +
* The Wheel is on wifi, as 'ti-wheel.local'
 +
* The wheel is connected to our HomeAssistant instance on https://spass.techinc.nl: device light.techinc_wheel
 +
* The old arduino which ran the wheel before is attached to the power supply, in case a revert is ever needed.
 +
 
 +
Feel free to add/suggest extra code to the ESPhome settup, add sensors or whatever.
 +
 
 +
[http://www.narya.dds.nl/stuff/techinc-wheel.mp4 MOVING PICTURES]
 +
 
 +
[[File:wheelesp.jpg|thumb|the brains of the project]]
 +
 
 +
The wheel is not connected to [[Powerbars]]
 +
 
 +
== Old Code ==
 +
<s>Currently the ColorPalette code runs on the Arduino.</s>
 +
 
 +
<s>[https://github.com/techinc/wheel Arduino code] - ''work in progress''</s>
 +
 
 +
==Things to make and do==
 +
 
 +
This project has several parts: '''backlit''' the wheel, '''mount''' the wheel to the wall or on some stand, and make the wheel '''control''' something. It would be nice to be able to unmount the wheel and take it somewhere else, e.g. to display it at events.
 +
 
 +
=== Mount ===
 +
Who: justa, narya, control-k
 +
* <s>Location: on the wall - done</s>
 +
* Location: infront of the window - Done
 +
[[File:wheel-on-the-wall.JPG|200px|thumb|none|wheel when it was mounted on the wall]]
  
 
=== Painting ===
 
=== Painting ===
Who: [[User:Narya|Narya]]
 
 
* NO color painting!
 
* NO color painting!
* I will fix the scratched parts with varnish
+
* Scratched parts could eventually be fixed with varnish
  
 
=== Backlit ===
 
=== Backlit ===
Who: [[User:Narya|Narya]], [[User:Brainsmoke | Brainsmoke]] and [[User:Mariejel | Mariejel]]
+
Who: [[User:Narya|Narya]], [[User:Brainsmoke | Brainsmoke]] and [[User:Mariejel | Mariejel]]  
  
==== Hardware components ====
+
Who, later: [[User:Justa|Justa]] who moved it to ESPHome
For the wheel
 
* [https://www.adafruit.com/products/285#Description 12V RGB LED strip] (needs extra stuff to make it addressable)
 
* 60 [https://www.adafruit.com/products/1378 WS2811 chips]
 
* Arduino (diecisomething and a nano)
 
* 12V adapter
 
* capacitor and resistor smd set for about 20 rgb-led-sets - ''ordered on 28-12-2013''
 
  
Test stuff
+
Soldering: done
* about 60 5mm common anode RGB LEDs
 
* 5V RGB LED strip (addressable, needs more powerful adapter)
 
* [http://www.re-innovation.co.uk/web12/index.php/en/products-2/rgb-led-shield-product RGB LED arduino shield]
 
* 5V adapter
 
* RGB reel, 60*3 LEDs, addressable via WS2801 chips, 12V
 
  
Lesson 1: the 12V LEDs are not addressable
+
'''WS2801 12V magic strip'''
 +
* 12V
 +
* 40 addressable pixels, color profile BRG
 +
* Wires: green 12V (not attached), blue clock (<s>pin 11</s> GPIO02), red data (<s>pin 12</s> GPIO04), black GND (pin GND)
  
''And now what?''
+
==== Color patterns ====
* Cut current strip to even smaller pieces and use WS2801's or WS2811's for every pixel you want to address. Or, use the current set-up in another project (space lighting or something like that, where you just have one colour for the entire thing, e.g. in combination with this [http://learn.adafruit.com/adafruit-color-sensors/overview color sensor], in house)
+
* Make a few color patterns:
* We also have RGB LEDs (5mm). Build one small board for every pixel. Takes a long time, but might be fun. (needs: WS2811/WS2801 chips, resistors, board)
+
** No rotation: colors of logo
* Use *addressable* RGB LEDs (WS2801 or WS2811). This is the easiest way to make it work. I currently have 30 LEDs of the WS2811 type at home. Maybe use these.
+
** From center to outside and vice versa (knight rider)
 +
** Rainbow glow - Done
 +
** Rotating colors - Done
 +
** FIRE  - Done
 +
** Twinkle - Done
  
''We are going to use the 12V LED strip with the WS2811 chips, and make pcb's for that''
+
==== Sensors ====
 +
* Color sensor to change knight rider color for example
 +
* Distance sensor: change color and/or speed. If hand is within 4cm it can function as a switch (switch between color programs)
 +
 
 +
=== Software ===
 +
* FastLED library http://fastled.io/
 +
* Neopixel library https://github.com/adafruit/Adafruit_NeoPixel
 +
* Adafruit_WS2801 https://github.com/adafruit/Adafruit-WS2801-Library
 +
* FastSPI library https://code.google.com/p/fastspi/
 +
* ShiftPWM http://www.elcojacobs.com/using-shiftpwm-to-control-led-strips-with-arduino/
 +
* Related projects @techinc: [[LEDLightDistrict]], [[Neopixel]]
  
Learn how to make a PCB design, make a pcb according to the example in the [https://www.adafruit.com/datasheets/WS2811.pdf datasheet]
 
* [http://www.kicad-pcb.org/display/KICAD/KiCad+Documentation KiCad getting started guide]
 
* [http://per.launay.free.fr/kicad/kicad_php/composant.php Component libraries]
 
* [http://fritzing.org/home/ Fritzing] - looks very intuitive, will try this out too
 
  
PCB for connecting a 5mm RGB LED or a 12V RGB LED strip (layout is the same, only the resistor values are different, and the connector pads/holes)
 
** schematic of the PCB is done ([https://github.com/techinc/wheel git])
 
** to do: draw footprints for the data connectors and for the RGB LED connections
 
** to do: make layout for the PCB
 
** etch a prototype
 
** order definitive pcb
 
  
'''WS2901 12V magic strip'''
+
== Old ==
* 12V
+
[[Techinc_Wheel/Old]]
* 36 LEDs/m (180 LEDs in total for 5m), 60 addressable pixels
+
 
* Power consumption = 8.64W/m, so 45W for total. Need power supply of at least 3.75A.
+
== Recent code ==
* Wires: green 12V (not attached), blue clock (pin 11), red data (pin 12), black GND (pin GND)
+
 
* Problem (7-6-2014): When lights are supposed to be off/black the LEDs are red. LED strip becomes hot
+
The most recent code should be on the ESPHome plugin of Home Assistant.
** Tried different data speeds
+
Below is a copy of the code as it is on 2023-10-19, for informative purposes.
** Tried swapping data and clock, different pins
+
 
** Tried different libraries: [https://github.com/adafruit/Adafruit_NeoPixel NeoPixel], [https://github.com/adafruit/Adafruit-WS2801-Library Adafruit_WS2801], and [http://fastled.io/ FastLED] library. Color profiles work (note: it is BRG), but problem is the same for every library
+
<pre>
** Need to read this: http://forum.arduino.cc/index.php?topic=173363.0
+
substitutions:
* Problem solved (27-6-2014): using another power supply did the trick. Previous one was a 12V 6.32A supply. Now using a 12V 5.42A power supply. Don't really understand why it didn't work before, because the previous supply had enough power.
+
  devicename:  ti-wheel
 +
  friendly_name:  TI Wheel
 +
  device_description: ESP12E TI Wheel Profile
 +
  project_name: "Espressif.ESP12E"
 +
  project_version: "ESP12E TI Wheel by Justa" 
 +
# ${friendly_name}
 +
 
 +
esphome:
 +
  name: $devicename
 +
  comment: ${device_description}
 +
  project:
 +
    name: ${project_name}
 +
    version: ${project_version}
 +
  on_boot:
 +
    priority: 600
 +
    then:
 +
      light.turn_on:
 +
        id: 'ti_wheel'
 +
        effect: 'Rainbow'
 +
 
 +
esp8266:
 +
  board: esp12e
 +
  framework:
 +
    version: 2.7.4
 +
 
 +
wifi:
 +
  ssid: !secret wifi_ssid
 +
  password: !secret wifi_password
 +
  fast_connect: True
 +
#  use_address: [REDACTED]
 +
#  enable_mdns: True
 +
 
 +
  # Enable fallback hotspot (captive portal) in case wifi connection fails
 +
  ap:
 +
    ssid: "${friendly_name} Fallback Hotspot"
 +
    password: !secret hotspot_password
 +
 
 +
captive_portal:
 +
 
 +
# Enable logging
 +
logger:
 +
 
 +
# Enable Home Assistant API
 +
api:
 +
  encryption:
 +
    key: !secret api_key
 +
  #password: !secret api_password
 +
  reboot_timeout: 0s
 +
 
 +
ota:
 +
  password: !secret ota_password
 +
  safe_mode: True
 +
 
 +
web_server:
 +
  port: 80
 +
  # local: true
 +
  auth:
 +
    username: !secret web_user
 +
    password: !secret web_password
 +
 
 +
light:
 +
  - platform: fastled_spi
 +
    name: "Techinc Wheel"
 +
    id: "ti_wheel"
 +
    chipset: WS2801
 +
    data_pin: GPIO4
 +
    clock_pin: GPIO2
 +
    num_leds: 40
 +
    rgb_order: BRG
 +
    effects:
 +
            - addressable_rainbow:
 +
                    name: Rainbow
 +
                    speed: 10
 +
                    width: 40
 +
            - strobe:
 +
                name: Strobe1
 +
                colors:
 +
                - state: true
 +
                  brightness: 100%
 +
                  red: 100%
 +
                  green: 90%
 +
                  blue: 0%
 +
                  duration: 500ms
 +
                - state: false
 +
                  duration: 250ms
 +
                - state: true
 +
                  brightness: 100%
 +
                  red: 0%
 +
                  green: 100%
 +
                  blue: 0%
 +
                  duration: 500ms
 +
            - addressable_twinkle:
 +
                name: Twinkle
 +
                twinkle_probability: 5%
 +
                progress_interval: 4ms
 +
            - addressable_lambda:
 +
                name: "Fire"
 +
                update_interval: 15ms
 +
                lambda: |-
 +
                  int Cooling = 55;
 +
                  int Sparking = 110;
 +
                  // Adjust size of array to length of pixels
 +
                  static byte heat[40];
 +
                  int cooldown;
 +
             
 +
                  // Step 1. Cool down every cell a little
 +
                  for( int i = 0; i < it.size(); i++) {
 +
                    cooldown = random(0, ((Cooling * 10) / it.size()) + 2);
 +
             
 +
                    if(cooldown>heat[i]) {
 +
                      heat[i]=0;
 +
                    } else {
 +
                      heat[i]=heat[i]-cooldown;
 +
                    }
 +
                  }
 +
           
 +
                  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
 +
                  for( int k= it.size() - 1; k >= 2; k--) {
 +
                    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
 +
                  }
 +
           
 +
                  // Step 3.  Randomly ignite new 'sparks' near the bottom
 +
                  if( random(255) < Sparking ) {
 +
                    int y = random(7);
 +
                    heat[y] = heat[y] + random(160,255);
 +
                  }
 +
           
 +
                  // Step 4. Convert heat to LED colors
 +
                  for( int Pixel = 0; Pixel < it.size(); Pixel++) {
 +
                    // Scale 'heat' down from 0-255 to 0-191
 +
                    byte t192 = round((heat[Pixel]/255.0)*191);
 +
           
 +
                    // calculate ramp up from
 +
                    byte heatramp = t192 & 0x3F; // 0..63
 +
                    heatramp <<= 2; // scale up to 0..252
 +
           
 +
                    // figure out which third of the spectrum we're in:
 +
                    //this is where you can reverse the effect by switching the commented out lines in all 3 places.
 +
                    if( t192 > 0x80) {                    // hottest
 +
                      //it[it.size() - Pixel - 1] = ESPColor(255, 255, heatramp);
 +
                      it[Pixel] = ESPColor(255, 255, heatramp);
 +
                    } else if( t192 > 0x40 ) {            // middle
 +
                      //it[it.size() - Pixel - 1] = ESPColor(255, heatramp, 0);
 +
                      it[Pixel] = ESPColor(255, heatramp, 0);
 +
                    } else {                              // coolest
 +
                      //it[it.size() - Pixel - 1] = ESPColor(heatramp, 0, 0);
 +
                      it[Pixel] = ESPColor(heatramp, 0, 0);
 +
                    }
 +
                  }
 +
button:
 +
  - platform: restart
 +
    name: "${friendly_name} Restart Button"
  
==== Software ====
+
switch:
* FastSPI library https://code.google.com/p/fastspi/
+
  - platform: restart
* Neopixel library https://github.com/adafruit/Adafruit_NeoPixel
+
    name: "${friendly_name} Restart"   
* Take a look at: ShiftPWM http://www.elcojacobs.com/using-shiftpwm-to-control-led-strips-with-arduino/
+
  - platform: safe_mode
* Related projects @techinc: [[LEDLightDistrict]], [[Neopixel]]
+
    name: "${friendly_name} Restart (Safe Mode)" 
 +
  - platform: shutdown
 +
    name: "${friendly_name} Shutdown"
  
Design a few patterns for the wheel
+
binary_sensor:
* Info about colors on [http://en.wikipedia.org/wiki/HSL_and_HSV Wikipedia]
+
  - platform: status
* Make a plan for controlling the lights, ideas:
+
    name: "${friendly_name} Status"
** No rotation: colors of logo
+
sensor:
** <s>Rotation: full color [http://www.ladyada.net/make/spokepov/index.html SpokePOV] ([http://janpaulposma.nl/pimpmybike-biking-at-night-with-style PimpMyBike blog])</s> - based on comments: maybe not such a good idea, since wheel has to spin fast
+
  - platform: uptime
** From center to outside and vice versa
+
    name: "${friendly_name} Uptime"
** Rotating colors
+
    id: uptime_sensor
** ...
+
    update_interval: 30s
 +
    on_raw_value:
 +
      then:
 +
        - text_sensor.template.publish:
 +
            id: uptime_human
 +
            state: !lambda |-
 +
              int seconds = round(id(uptime_sensor).raw_state);
 +
              int days = seconds / (24 * 3600);
 +
              seconds = seconds % (24 * 3600);
 +
              int hours = seconds / 3600;
 +
              seconds = seconds % 3600;
 +
              int minutes = seconds / 60;
 +
              seconds = seconds % 60;
 +
              return (
 +
                (days ? String(days) + "d " : "") +
 +
                (hours ? String(hours) + "h " : "") +
 +
                (minutes ? String(minutes) + "m " : "") +
 +
                (String(seconds) + "s")
 +
              ).c_str();
  
=== Mount ===
+
text_sensor:
Who: justa, narya, control-k
+
  - platform: template
* Location: on the wall - done
+
    name: "${friendly_name} Human Uptime"
[[File:wheel-on-the-wall.JPG|200px|thumb|none|wheel mounted on the wall]]
+
    id: uptime_human
 +
    icon: mdi:clock-start
 +
  - platform: version
 +
    name: "${friendly_name} Version"
 +
    icon: mdi:new-box
 +
    hide_timestamp: True
 +
  - platform: wifi_info
 +
    ip_address:
 +
      name: "${friendly_name} IP Address" 
 +
      icon: mdi:ip-network-outline
  
=== Control ===
+
</pre>
Who: you?
 
  
Random ideas, feel free to add your ideas below and add your name if you like to work on it
+
== Old Arduino inmplementation ==
* <s>Make something that registers when the wheel is turned</s>
+
<code>
* <s>Let it control X when the wheel is turned (don't know what "X" should be, ideas are welcome)</s>
+
* <s>No function, just let it rotate</s>
+
#include <SPI.h>  
* For now: no rotation
+
 +
void setup()
 +
{
 +
    SPI.begin();
 +
    SPI.setBitOrder(MSBFIRST);
 +
    SPI.setDataMode(SPI_MODE0);
 +
    SPI.setClockDivider(SPI_CLOCK_DIV8);
 +
}
 +
 +
uint8_t fade[] =
 +
{
 +
//[ int(((.5-cos(2*pi*x/240.)*.5)**2.2)*255) for x in range(240) ]
 +
   
 +
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,6,7,7,8,9,10,12,13,14,16,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,58,62,65,69,72,76,80,84,88,92,96,100,104,108,113,117,121,126,130,135,139,144,148,153,157,162,166,171,175,179,184,188,192,196,200,204,208,211,215,218,222,225,228,231,234,236,239,241,243,245,247,248,250,251,252,253,254,254,254,255,254,254,254,253,252,251,250,248,247,245,243,241,239,236,234,231,228,225,222,218,215,211,208,204,200,196,192,188,184,179,175,171,166,162,157,153,148,144,139,135,130,126,121,117,113,108,104,100,96,92,88,84,80,76,72,69,65,62,58,55,52,49,46,43,40,38,35,33,30,28,26,24,22,20,19,17,16,14,13,12,10,9,8,7,7,6,5,4,4,3,3,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 +
};
 +
 
 +
uint8_t r=0,g=80,b=160;
 +
 +
void loop()
 +
{
 +
    for(;;)
 +
    {
 +
        r+=1;g+=1;b+=1;
 +
 +
        for (i=0; i<40; i++)
 +
        { 
 +
            r+=6;g+=6;b+=6;
 +
            if (r >= 240) r-= 240;
 +
            if (g >= 240) g-= 240;
 +
            if (b >= 240) b-= 240;
 +
 +
            SPI.transfer(fade[r]);
 +
            SPI.transfer(fade[g]);
 +
            SPI.transfer(fade[b]);
 +
 +
        }
 +
        delay(20);
 +
    }
 +
}
 +
</code>

Latest revision as of 16:55, 16 November 2023

Projects
20130729-techinc-wheel-at-ohm.jpeg
Participants Narya
Skills Electronics, Mechanics
Status Finished
Niche Electronics
Purpose Promotion

Bringing the wheel to events

If you can't reach Narya or Mariejel, assume a "yes" when you (a techinc member) want to bring the wheel to an event. Please put it back on the wall when the event is over. If the LEDs come loose you can simply put it back with double-sided tape and/or ductape.

Summary

  • The wooden techinc wheel has a WS2801 LED strip stuck on the back and is controlled via an Arduino Nano esp8266 with ESPHome
  • The 8 strips consist of 5 pixels each (3 LEDs per pixel). The first "spoke" goes outwards, the second inwards, the third outwards, etc
  • The data pin is GPIO4 and the clock pin GPIO2
  • Color order is BRG, which is supported by the FastLED library, but for other libraries you need to put the colors in the right order
  • The Wheel is on wifi, as 'ti-wheel.local'
  • The wheel is connected to our HomeAssistant instance on https://spass.techinc.nl: device light.techinc_wheel
  • The old arduino which ran the wheel before is attached to the power supply, in case a revert is ever needed.

Feel free to add/suggest extra code to the ESPhome settup, add sensors or whatever.

MOVING PICTURES

the brains of the project

The wheel is not connected to Powerbars

Old Code

Currently the ColorPalette code runs on the Arduino.

Arduino code - work in progress

Things to make and do

This project has several parts: backlit the wheel, mount the wheel to the wall or on some stand, and make the wheel control something. It would be nice to be able to unmount the wheel and take it somewhere else, e.g. to display it at events.

Mount

Who: justa, narya, control-k

  • Location: on the wall - done
  • Location: infront of the window - Done
wheel when it was mounted on the wall

Painting

  • NO color painting!
  • Scratched parts could eventually be fixed with varnish

Backlit

Who: Narya, Brainsmoke and Mariejel

Who, later: Justa who moved it to ESPHome

Soldering: done

WS2801 12V magic strip

  • 12V
  • 40 addressable pixels, color profile BRG
  • Wires: green 12V (not attached), blue clock (pin 11 GPIO02), red data (pin 12 GPIO04), black GND (pin GND)

Color patterns

  • Make a few color patterns:
    • No rotation: colors of logo
    • From center to outside and vice versa (knight rider)
    • Rainbow glow - Done
    • Rotating colors - Done
    • FIRE - Done
    • Twinkle - Done

Sensors

  • Color sensor to change knight rider color for example
  • Distance sensor: change color and/or speed. If hand is within 4cm it can function as a switch (switch between color programs)

Software


Old

Techinc_Wheel/Old

Recent code

The most recent code should be on the ESPHome plugin of Home Assistant. Below is a copy of the code as it is on 2023-10-19, for informative purposes.

substitutions:
  devicename:  ti-wheel
  friendly_name:  TI Wheel
  device_description: ESP12E TI Wheel Profile
  project_name: "Espressif.ESP12E"
  project_version: "ESP12E TI Wheel by Justa"  
# ${friendly_name}

esphome:
  name: $devicename
  comment: ${device_description}
  project:
    name: ${project_name}
    version: ${project_version}
  on_boot:
    priority: 600
    then:
      light.turn_on:
        id: 'ti_wheel'
        effect: 'Rainbow'

esp8266:
  board: esp12e
  framework:
    version: 2.7.4

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: True
#  use_address: [REDACTED]
#  enable_mdns: True

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${friendly_name} Fallback Hotspot"
    password: !secret hotspot_password

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: !secret api_key
  #password: !secret api_password
  reboot_timeout: 0s

ota:
  password: !secret ota_password
  safe_mode: True
  
web_server:
  port: 80
  # local: true
  auth:
    username: !secret web_user
    password: !secret web_password

light:
  - platform: fastled_spi
    name: "Techinc Wheel"
    id: "ti_wheel"
    chipset: WS2801
    data_pin: GPIO4
    clock_pin: GPIO2
    num_leds: 40
    rgb_order: BRG
    effects:
            - addressable_rainbow:
                    name: Rainbow
                    speed: 10
                    width: 40
            - strobe:
                name: Strobe1
                colors:
                - state: true
                  brightness: 100%
                  red: 100%
                  green: 90%
                  blue: 0%
                  duration: 500ms
                - state: false
                  duration: 250ms
                - state: true
                  brightness: 100%
                  red: 0%
                  green: 100%
                  blue: 0%
                  duration: 500ms
            - addressable_twinkle:
                name: Twinkle
                twinkle_probability: 5%
                progress_interval: 4ms
            - addressable_lambda:
                name: "Fire"
                update_interval: 15ms
                lambda: |-
                  int Cooling = 55;
                  int Sparking = 110;
                  // Adjust size of array to length of pixels
                  static byte heat[40];
                  int cooldown;
              
                  // Step 1.  Cool down every cell a little
                  for( int i = 0; i < it.size(); i++) {
                    cooldown = random(0, ((Cooling * 10) / it.size()) + 2);
              
                    if(cooldown>heat[i]) {
                      heat[i]=0;
                    } else {
                      heat[i]=heat[i]-cooldown;
                    }
                  }
            
                  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
                  for( int k= it.size() - 1; k >= 2; k--) {
                    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
                  }
            
                  // Step 3.  Randomly ignite new 'sparks' near the bottom
                  if( random(255) < Sparking ) {
                    int y = random(7);
                    heat[y] = heat[y] + random(160,255);
                  }
            
                  // Step 4.  Convert heat to LED colors
                  for( int Pixel = 0; Pixel < it.size(); Pixel++) {
                    // Scale 'heat' down from 0-255 to 0-191
                    byte t192 = round((heat[Pixel]/255.0)*191);
             
                    // calculate ramp up from
                    byte heatramp = t192 & 0x3F; // 0..63
                    heatramp <<= 2; // scale up to 0..252
             
                    // figure out which third of the spectrum we're in:
                    //this is where you can reverse the effect by switching the commented out lines in all 3 places.
                    if( t192 > 0x80) {                     // hottest
                      //it[it.size() - Pixel - 1] = ESPColor(255, 255, heatramp);
                      it[Pixel] = ESPColor(255, 255, heatramp);
                    } else if( t192 > 0x40 ) {             // middle
                      //it[it.size() - Pixel - 1] = ESPColor(255, heatramp, 0);
                      it[Pixel] = ESPColor(255, heatramp, 0);
                    } else {                               // coolest
                      //it[it.size() - Pixel - 1] = ESPColor(heatramp, 0, 0);
                      it[Pixel] = ESPColor(heatramp, 0, 0);
                    }
                  }
button:
  - platform: restart
    name: "${friendly_name} Restart Button"

switch:
  - platform: restart
    name: "${friendly_name} Restart"    
  - platform: safe_mode
    name: "${friendly_name} Restart (Safe Mode)"   
  - platform: shutdown
    name: "${friendly_name} Shutdown"

binary_sensor:
  - platform: status
    name: "${friendly_name} Status"
sensor:
  - platform: uptime
    name: "${friendly_name} Uptime"
    id: uptime_sensor
    update_interval: 30s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? String(days) + "d " : "") +
                (hours ? String(hours) + "h " : "") +
                (minutes ? String(minutes) + "m " : "") +
                (String(seconds) + "s")
              ).c_str();

text_sensor:
  - platform: template
    name: "${friendly_name} Human Uptime"
    id: uptime_human
    icon: mdi:clock-start
  - platform: version
    name: "${friendly_name} Version"
    icon: mdi:new-box
    hide_timestamp: True
  - platform: wifi_info
    ip_address:
      name: "${friendly_name} IP Address"  
      icon: mdi:ip-network-outline

Old Arduino inmplementation

#include <SPI.h> 

void setup()
{
    SPI.begin();
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    SPI.setClockDivider(SPI_CLOCK_DIV8);
}

uint8_t fade[] =
{
//[ int(((.5-cos(2*pi*x/240.)*.5)**2.2)*255) for x in range(240) ]
   
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,6,7,7,8,9,10,12,13,14,16,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,58,62,65,69,72,76,80,84,88,92,96,100,104,108,113,117,121,126,130,135,139,144,148,153,157,162,166,171,175,179,184,188,192,196,200,204,208,211,215,218,222,225,228,231,234,236,239,241,243,245,247,248,250,251,252,253,254,254,254,255,254,254,254,253,252,251,250,248,247,245,243,241,239,236,234,231,228,225,222,218,215,211,208,204,200,196,192,188,184,179,175,171,166,162,157,153,148,144,139,135,130,126,121,117,113,108,104,100,96,92,88,84,80,76,72,69,65,62,58,55,52,49,46,43,40,38,35,33,30,28,26,24,22,20,19,17,16,14,13,12,10,9,8,7,7,6,5,4,4,3,3,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
 
uint8_t r=0,g=80,b=160;

void loop()
{
   for(;;)
   {
       r+=1;g+=1;b+=1;

       for (i=0; i<40; i++)
       {   
           r+=6;g+=6;b+=6;
           if (r >= 240) r-= 240;
           if (g >= 240) g-= 240;
           if (b >= 240) b-= 240;

           SPI.transfer(fade[r]);
           SPI.transfer(fade[g]);
           SPI.transfer(fade[b]);

       }
       delay(20);
   }
}