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