Zápisník experimentátora
Hierarchy: WS2812
Tento článok popisuje algoritmus, ktorý simuluje horenie ohňa. Je to môj prvý pokus o jeho simuláciu. Simulácia sa skúša na NeoPixel Ringu s 24 LED diódami. Simulátor nie je určený na priame pozeranie. Predpokladá sa jeho umiestnenie pri bielej stene, na ktorej vytvára mihotanie podobné reálnemu ohňu v kozube.
Výsledky simulácie by som chcel využiť v LED pásiku, dlhom 5 metrov. Jedným z efektov by mal byť aj tento oheň.
Budeme potrebovať nasledovné súčiastky:
Ochranné súčiastky sú nutné preto, aby sa NeoPixel Ring nepoškodil. Podrobnosti ochrany sú uvedené v článku o napájaní týchto diód.
Na obrázku je Ring s 12 diódami. Ring s 24 diódami sa nijako mimoriadne od neho nelíši a je preto možné použiť aj menší ring po príslušnej úprave kódu.
Simulácia ohňa je jednoduchá. Potrebujeme poznať RGB hodnotu ohňa. Hodnoty 156,42,0 a 255,153,0 predstavujú odtiene oranžovej farby, z ktorých budeme vychádzať. LED WS2812 majú zelenú farbu trochu intenzívnejšiu, takže z nej budeme trochu uberať, aby sme výslednú farbu mali viac červenu než zelenú. Pretože tieto diódy svietia dosť silno, uberieme trochu aj z intenzity farby, aby sme dostali jas menšieho ohníka namiesto blčiaceho ohňa, ktorý taví kov.
Samotný algoritmus pozostáva z dvoch krokov:
Podstatné časti z programu sú uvedené na nasledovných riadkoch. Vychádzal som z môjho predchádzajúceho kódu na ovládanie NeoPixel Ring hodín. Do kódu som doplnil funkciu na odčítanie hodnôt, pomocou ktorej tlmím farbu na jednotlivých diódach.
Definícia triedy, ktorá simuluje horiaci oheň. Podstatná funkcia je Draw a Substract, ktoré vytvárajú tmavšie 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žitie objektu simulácie ohňa.
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));
}
Nik nemá takú dobrú predstavivosť, preto si môžete pozrieť výsledné video na Youtube.
23.09.2015