Simulace ohně

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ň.

Součástky

Budeme potřebovat následující součástky:

  • Arduino Uno alebo Mini.
  • NeoPixel Ring s 24 diodami. Já jsem můj kupoval od​ Banggood. Dá se koupit i na Ebay (pod názvem led ring 24bit) a průměrná cena v době nákupu byla 5,6 USD za kus.
  • Ochranný rezistor 470R.
  • Ochranný kondenzátor 1000 uF.

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.

Algoritmus

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ů:

  • Nastavení všech bodů na maximální hodnotu barvy ohně.
  • Výběr náhodného čísla pro každou diodu. Tuto hodnotu odečteme od maximální hodnoty ohně. Náhodné číslo vybíráme tak, abychom vytvořili širší rozsah tmavších odstínů původní barvy.
  • Po nastavení všech diod následuje krátký časový úsek s nepravidelnou délkou, který zpomaluje efekt na rychlost, jakou by přibližně hořel i skutečný oheň.

Programování

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));
}

Video

Nikdo nemá takovou dobrou představivost, proto si můžete prohlédnout výsledné video na Youtube.



Video



Download

18.09.2017


Menu