Zápisník experimentátora
Hierarchy: WS2812
Tento článek popisuje algoritmus, který simuluje hoření ohně. Je to můj první pokus o jeho simulaci. Simulace se zkouší na NeoPixel Ringu s 24 LED diodami. Simulátor není určen k přímému koukání. Předpokládá se jeho umístění u bílé zdi, na níž vytváří mihotání podobné reálnému ohni v krbu.
Výsledky simulace bych chtěl využít v LED pásku, dlouhém 5 metrů. Jedním z efektů by měl být i tento oheň.
![]()
Budeme potřebovat následující součástky:
Ochranné součástky jsou nutné proto, aby se NeoPixel Ring nepoškodil. Podrobnosti ochrany jsou uvedeny v článku o napájení těchto diod.
![]()
Na obrázku je Ring s 12 diodami. Ring s 24 diodami se nijak mimořádně od něj neliší a je proto možné použít i menší ring po příslušné úpravě kódu.
Simulace ohně je jednoduchá. Potřebujeme znát RGB hodnotu ohně. Hodnoty 156,42,0 a 255,153,0 představují odstíny oranžové barvy, ze kterých budeme vycházet. LED WS2812 mají zelenou barvu trochu intenzivnější, takže z ní budeme trochu ubírat, abychom výslednou barvu měli více červenou než zelenou. Protože tyto diody svítí dost silně, ubereme trochu i z intenzity barvy, abychom dostali jas menšího ohýnku namísto hořící ohně, který taví kov.
![]()
Samotný algoritmus se skládá ze dvou kroků:
Podstatné části z programu jsou uvedeny na následujících řádcích. Vycházel jsem z mého předchozího kódu na ovládání NeoPixel Ring hodin. Do kódu jsem doplnil funkci na odečítání hodnot, pomocí které tlumíme barvu na jednotlivých diodách.
![]()
Definice třídy, která simuluje hořící oheň. Podstatná funkce je Draw a Substract, které vytvářejí tmavší body v ohni.
// led count
#define CNT 24
uint32_t fire_color = strip.Color ( 80, 35, 00);
uint32_t off_color = strip.Color ( 0, 0, 0);
///
/// Set all colors
///
void NeoFire::Draw()
{
Clear();
for(int i=0;i<CNT;i++)
{
AddColor(i, fire_color);
int r = random(80);
uint32_t diff_color = strip.Color ( r, r/2, r/2);
SubstractColor(i, diff_color);
}
strip.show();
}
///
/// Set color of LED
///
void NeoFire::AddColor(uint8_t position, uint32_t color)
{
uint32_t blended_color = Blend(strip.getPixelColor(position), color);
strip.setPixelColor(position, blended_color);
}
///
/// Set color of LED
///
void NeoFire::SubstractColor(uint8_t position, uint32_t color)
{
uint32_t blended_color = Substract(strip.getPixelColor(position), color);
strip.setPixelColor(position, blended_color);
}
///
/// Color blending
///
uint32_t NeoFire::Blend(uint32_t color1, uint32_t color2)
{
uint8_t r1,g1,b1;
uint8_t r2,g2,b2;
uint8_t r3,g3,b3;
r1 = (uint8_t)(color1 >> 16),
g1 = (uint8_t)(color1 >> 8),
b1 = (uint8_t)(color1 >> 0);
r2 = (uint8_t)(color2 >> 16),
g2 = (uint8_t)(color2 >> 8),
b2 = (uint8_t)(color2 >> 0);
return strip.Color(constrain(r1+r2, 0, 255), constrain(g1+g2, 0, 255), constrain(b1+b2, 0, 255));
}
///
/// Color blending
///
uint32_t NeoFire::Substract(uint32_t color1, uint32_t color2)
{
uint8_t r1,g1,b1;
uint8_t r2,g2,b2;
uint8_t r3,g3,b3;
int16_t r,g,b;
r1 = (uint8_t)(color1 >> 16),
g1 = (uint8_t)(color1 >> 8),
b1 = (uint8_t)(color1 >> 0);
r2 = (uint8_t)(color2 >> 16),
g2 = (uint8_t)(color2 >> 8),
b2 = (uint8_t)(color2 >> 0);
r=(int16_t)r1-(int16_t)r2;
g=(int16_t)g1-(int16_t)g2;
b=(int16_t)b1-(int16_t)b2;
if(r<0) r=0;
if(g<0) g=0;
if(b<0) b=0;
return strip.Color(r, g, b);
}
Použití objektu simulace ohně.
NeoFire fire(strip);
///
/// Setup
///
void setup()
{
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
///
/// Main loop
///
void loop()
{
fire.Draw();
delay(random(50,150));
}
Nikdo nemá takovou dobrou představivost, proto si můžete prohlédnout výsledné video na Youtube.
18.06.2019