News:

;) This forum is the property of Proton software developers

Main Menu

Positron8 - Another Flame Simulator using WS2812B RGB LED chips.

Started by top204, Sep 22, 2025, 12:08 PM

Previous topic - Next topic

top204

The WS2812B RGB LED chips are still a fascination to me, and how easy they are to control, with the bonus that each device gives the same colours as all of its neighbours, making them perfect for displays.

One of my current favourites is trying to simulate fire using them, and the code listing below simulates a single flame on a string of WS2812B LED devices, with white colours at the bottom of the flame, moving up through yellows to the top of the flame, that are reds. All done with a single PIC18F26K22 device connected to 16 WS2812B devices with a single wire! No crystal required, and no buffer transistors etc...

The PIC18F26K22 device is plugged into one of John's Amicus8 boards, because they are so easy to develop code on, and then plug things into it for testing the code written.

The single flame simulator code is listed below:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A single flame simulation using a strip of 16 WS2812B RGB LED chips.
' The different coloured light moves up the flame, from near white at the start of the flame, to yellow in the middle, then red at the top of the flame.
'
' Written by Les Johnson for the Positron8 BASIC Compiler.
' https://sites.google.com/view/rosetta-tech/home
'
    Device = 18F26K22                                                   ' Tell the compiler what device to compile for
    Declare Xtal = 64                                                   ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Auto_Heap_Arrays = On                                       ' Make all arrays "Heap" types, so they always get placed after standard variables, for extra code efficiency.
    Declare Auto_Variable_Bank_Cross = On                               ' Make sure all multi-byte variables remain within a single RAM bank, for even more code efficiency.
'
' Setups for the WS2812B RGB LEDs library
'
$define WS2812B_Pin PORTC.0                                             ' The pin used for the WS2812B chips
$define WS2812B_Amount 16                                               ' The amount of WS2812B chips to control

    Include "WS2812B.inc"                                               ' Load the RGB WS2812B routines into the program

'---------------------------------------------------------------------------------------------
' The main program starts here
' Illuminate a single flame on WS2812B RGB LED chips
'
Main:
    Setup()                                                             ' Setup the program and any peripherals
    Do                                                                  ' Create a loop
        Flame(10, 250, 60)                                              ' Illuminate the flame
    Loop                                                                ' Do it forever

'---------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    Osc_64MHz()                                                         ' Set the device to operate at 64MHz using the internal oscillator
    Seed $0678                                                          ' Seed the pseudo random value generator
    WS2812B_Setup()                                                     ' Initialise the WS2812B LEDs
    WS2812B_Fill(0, WS2812B_Amount - 1, $000000)                        ' Extinguish all the WS2812B LEDs
EndProc

'---------------------------------------------------------------------------------------------
' Illuminate a rising flame on the WS2812B LEDs
' Input     : pHeight Holds the height of the flame. Use a larger value for shorter flames (0 to 255)
'           : pIgnites - Holds the amount of new ignitions. Use a larger value for more ignitions and a more active flame (0 to 255)
'           : pDuration - Holds the speed of the flame. Use a larger value for slower flame speed (0 to 255)
' Output    : None
' Notes     : None
'
Proc Flame(pHeight As Byte, pIgnites As Byte, pDuration As Byte)
    Dim bHeat[WS2812B_Amount] As Byte Heap                              ' Holds the relative heat of each part of the flame
    Dim bCooler      As Byte                                            ' Holds the amount of cooling in the flame
    Dim bIndex       As Byte                                            ' Holds an index to the WS2812B LEDs used
    Dim bIgniteIndex As Byte                                            ' Holds a pseudo random index to the WS2812B LEDs for ingintions
'
' Cool down each flame cell as it rises
'
    For bIndex = 0 To WS2812B_Amount - 1                                ' Create a loop for the amount of WS2812B devices being used
        bCooler = Rand8(((pHeight * 10) / WS2812B_Amount) + 2)
        If bCooler > bHeat[bIndex] Then
            bHeat[bIndex] = 0
        Else
            bHeat[bIndex] = bHeat[bIndex] - bCooler
        EndIf
    Next
'
' Heat from each flame cell drifts up and weakens slightly
'
    For bIndex = WS2812B_Amount - 1 DownTo 2                            ' Create a loop for the amount of WS2812B devices being used
        bHeat[bIndex] = ((bHeat[bIndex - 1] + bHeat[bIndex - 2]) + bHeat[bIndex - 2]) / 3
    Next
'
' Create random new ignitions near the bottom of the flame
'
    If Rand8(255) < pIgnites Then
        bIgniteIndex = Rand8(7)
        bHeat[bIgniteIndex] = bHeat[bIgniteIndex] + Rand8(160)
    EndIf
'
' Convert relative heat to LED colours
'
    For bIndex = 0 To WS2812B_Amount - 1                                ' Create a loop for the amount of WS2812B devices being used
        LED_HeatColour(bIndex, bHeat[bIndex])                           ' Alter the colour of the WS2812B LEDs
    Next
    DelayMS pDuration                                                   ' A delay between flame movements
EndProc

'---------------------------------------------------------------------------------------------
' Alter the colour of an LED based upon its relative heat value
' Input     : pPixel holds the WS2812B LED to alter (0 to 255)
'           : pTemperature holds the relative temperature of an LED
' Output    : None
' Notes     : None
'
Proc LED_HeatColour(pPixel As Byte, pTemperature As Byte)
    Dim bHeatRamp As Byte
    Dim wTemperature As Word = pTemperature

    wTemperature = (wTemperature * 191) / 255                           ' Scale pTemperature from 0-255 to 0-191
'
' Calculate the ramp up
'
    bHeatRamp = wTemperature.Byte0 & 63                                 ' 0...63
    bHeatRamp = bHeatRamp * 4                                           ' Scale up to 0 to 252
'
' Calculate which third of the colour spectrum the flame is in
'
    If wTemperature.Byte0 > 128 Then                                    ' Hot
        WS2812B_Colour(pPixel, 255, 255, bHeatRamp)                     ' Up to a white light
    ElseIf wTemperature.Byte0 > 64 Then                                 ' Middle
        WS2812B_Colour(pPixel, 255, bHeatRamp, 0)                       ' More of a yellow light
    Else                                                                ' Cool
        WS2812B_Colour(pPixel, bHeatRamp, 0, 0)                         ' A red light
    EndIf
EndProc

'---------------------------------------------------------------------------------------------
' Create a pseudo random 8-bit value
' Input     : pLimit holds the maximum value of the random value
' Output    : Returns the pseudo random 8-bit value (0 o 255)
' Notes     : None
'
Proc Rand8(pLimit As Byte), Byte
    Result = (Random) // pLimit
EndProc

'--------------------------------------------------------------------
' Set the PIC18F26K22 to 64MHz operation using its internal oscillator
' Input     : None
' Output    : None
' Notes     : Waits for the oscillator to become stable
'
Proc Osc_64MHz()
    OSCCON  = $70
    OSCCON2 = $04
    OSCTUNE = $40
    Repeat : Until OSCCON2bits_PLLRDY = 1                               ' Wait for PLL to stabilise
EndProc

'---------------------------------------------------------------------------------------------
' Setup the config fuses to use the internal oscillator on a PIC18Fx6K22 device.
' OSC Pins (RA6 and RA7) are general purpose I/O.
'
Config_Start
    FOSC     = INTIO67                                                  ' Internal oscillator. Port function on RA6 and RA7
    PRICLKEN = Off                                                      ' Primary clock disabled
    MCLRE    = EXTMCLR                                                  ' MCLR Pin Enabled
    WDTEN    = Off                                                      ' Watchdog Timer disabled
    Debug    = Off                                                      ' Background debugger disabled
    PLLCFG   = Off                                                      ' Oscillator used directly
    XINST    = Off                                                      ' Extra Instruction Set Disabled
    FCMEN    = Off                                                      ' Fail-Safe Clock Monitor disabled
    IESO     = Off                                                      ' Oscillator Switchover mode disabled
    PWRTEN   = On                                                       ' Power up timer enabled
    BOREN    = Off                                                      ' Brown-out Reset disabled
    BORV     = 190                                                      ' VBOR set to 1.9V nominal
    WDTPS    = 8192                                                     ' 1:8192 (approx 30 seconds)
    HFOFST   = Off                                                      ' The Access clock is not held off until the HF-INTOSC is stable
    PBADEN   = Off                                                      ' PORTB<4:0> pins are configured as digital on reset
    CCP2MX   = PORTC1                                                   ' CCP2 input/output is multiplexed with RC1
    CCP3MX   = PORTB5                                                   ' P3A/CCP3 input/output is multiplexed with RB5
    T3CMX    = PORTC0                                                   ' T3CKI is on RC0
    P2BMX    = PORTB5                                                   ' P2B is on RB5
    STVREN   = On                                                       ' Stack full/underflow will cause a reset
    LVP      = Off                                                      ' Single-Supply ICSP disabled
    Cp0      = Off                                                      ' Block 0 (000800-001FFF) not code protected
    CP1      = Off                                                      ' Block 1 (002000-003FFF) not code protected
    CPB      = Off                                                      ' Boot block (000000-0007FF) not code protected
    CPD      = Off                                                      ' Data EEPROM not code protected
    WRT0     = Off                                                      ' Block 0 (000800-001FFF) not write protected
    WRT1     = Off                                                      ' Block 1 (002000-003FFF) not write protected
    WRTB     = Off                                                      ' Boot block (000000-0007FF) not write protected
    WRTC     = Off                                                      ' Configuration registers (300000-3000FF) not write protected
    WRTD     = Off                                                      ' Data EEPROM not write protected
    EBTR0    = Off                                                      ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1    = Off                                                      ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTRB    = Off                                                      ' Boot block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

A video of the single flame simulator operating with 16 WS2812B LED chips is below. I used one of the strips of the 16x16 matrix, because that was already lying on my desk, and it does the job just the same as a seperate strip of 16 WS2812B devices. More WS2812B device can be used for the simulation if required, by just changing the 'WS2812B_Amount' value, but make sure the microcontroller has enough RAM for the colour maps.


John Lawton

Hi Les,

just the job now the evenings are drawing in here in the UK.

Thanks for the mention of my Amicus boards, they are available here:

https://easy-driver.co.uk/amicus/

Best wishes,

John


charliecoutas

That's brilliant Les. I have your first flame-sim incorporated in my flame-effect fire. I tried the 16x16 version but my power supply sat down, but I will look into boosting it for next time. I might swap to the effect you show in this demo, it's so realistic. I have a row of 18 WS2818B's across the rear of my fire, where the "flames" (atomised water droplets) exit. I'll experiment but at the moment I'm finishing woodwork surround (18mm MDF, it's massive!)

Good work, don't set yourself on fire.

Charlie

charliecoutas

 I've made a .MOV video of my flame-effect fire running. How do I upload it to here please? If I try to drag it the forum complains about the file type.

Charlie   

charliecoutas


top204

Absolutely bloody brilliant Charlie!

A true work of art, and professionalism.

diebobo

Nicee.. you have some work to do, see some flickering / off colours.. perhaps interrupts at work when transmission is going ?

charliecoutas

Thanks Les. It was your Flame Effect Mk1 that inspired me. The "smoke" is supposed to be flames but that needs a little more work. My wife, Lynda, says she feels warmer, even though there is no heat output!

Charlie

JonW

Thats spot on Charlie!  How long does it take to cook the sausages  :P

charliecoutas

Diebobo: the flashes are intended to (and do) look like a fire flickering and doing what fires do. I deliberately moved the camera to show the back-row of leds, but you can't normally see them directly.

JonW: Les inspired me to do this whole thing, but now you have inspired me to think about a rotating spit for sausages........