logo elektroda
logo elektroda
X
logo elektroda

WS2812 Christmas animations on PIC12F683 - how many LEDs will 128 bytes of RAM handle?

p.kaczmarek2  29 2157 Cool? (+19)
📢 Listen (AI):

TL;DR

  • Tests a PIC12F683 driving WS2812 RGB LEDs for Christmas-style animations, asking how many pixels fit in 128 bytes of RAM.
  • The code starts with the internal 8 MHz oscillator, then moves to an external 20 MHz clock to meet WS2812 timing.
  • A 360 ns instruction timing led to about 20 LEDs, with 60 bytes reserved for pixel data.
  • Simple colour swapping and a sine-based animation both worked, confirming reliable WS2812 data transfer on the tiny MCU.
  • RAM remains the main limit: extra arrays and SDCC helper routines quickly exhaust memory, so larger effects need heavy optimisation or assembler.
WS2812 LED strip powered by a microcontroller, waveform shown on a RIGOL oscilloscope.
Can the tiny eight-bit PIC12F683 microcontroller handle the dynamic colour animation on the WS2812 LEDs? The MCU used here has just 2048 bytes of Flash memory, 128 bytes of RAM and is clocked at up to 20 MHz (8 MHz for the internal oscillator). Is this enough? Let's find out!

The idea came from the fact that I have a dozen or so of these types of PICs left over from older projects and I decided to try to give them new life. Perhaps something for Christmas. We'll see if I still have time. At the time of publishing the first part (of this topic) I have only just started the second part.

Here I will describe the first part of the adventure, that is, getting the WS2812 up and running and the limitations of this microcontroller.

This topic is part of my series on PIC microcontrollers and the SDCC compiler. For other parts, please see the topics below:
Part 1 - Setting up the operating environment
https://www.elektroda.pl/rtvforum/topic3635522.html#18304424
Part 2 - Blink LED, IO pins, digital inputs and outputs
https://www.elektroda.pl/rtvforum/topic3647884.html#18389188
Part 3 - Oscillator settings. Internal oscillator, external oscillator, quartz resonator, PLL
https://www.elektroda.pl/rtvforum/topic3657704.html
Part 4 - Timers, interrupts
https://www.elektroda.pl/rtvforum/topic3676645.html#18580858
Part 5 - Seven-segment display operation
https://www.elektroda.pl/rtvforum/topic3676650.html#18580877
Part 6 - MM5450 LED display driver
https://www.elektroda.pl/rtvforum/topic3845301.html
Related topic about PIC12F:
PIC12F683 and SDCC - tutorial - we create a simple dimmer (read catalogue notes)


Specification of WS2812
The WS2812 is an individually addressable, colour LED that combines an RGB LED and a controller in a single housing. This allows each module to be controlled independently, enabling the creation of lighting effects, animations and dynamic colour changes in LED strips, arrays or DIY projects. The WS2812 uses a single-line protocol for data transfer, allowing multiple LEDs to be easily connected in series with minimal wiring. The way it works is that the WS2812s are connected in series - the DOUT input of one is connected to the DIN of the other, and their controllers themselves cleverly pass on the signal, taking only their colour described by three bytes.
Data transmission and cascade connection diagram for WS2812 LEDs
The bytes are encoded as a high state - low state pair, where the timing of a given state must meet a specification.
Data transfer timing table for WS2812 LED, showing TH and TL durations.
According to the table - we must be able to generate T0H (0.4 us) and T1H (0.8 us) times with a margin of error of up to 0.15 us (in practice slightly more).

First attempts and instruction timing
The first PIC that caught my eye was the PIC12F675, lying right next to the eponymous PIC12F683. However, it had even less RAM, at a mere 64 bytes:
Comparison of PIC12F629 and PIC12F675 microcontroller specifications
I then compared the notes and decided that the PIC12F683 was the better option after all:
Specification table of the PIC12F683 microcontroller
It's a tiny 8-bit MCU in a DIP8 enclosure, but it's more likely to be enough here?
USB connector next to a PIC12F675 DIP8 microcontroller on a white background
To start with, I got the PIC up and running. PICKIT2 sees it without external components:
Breadboard with connected PICKIT2 programmer and DIP8 microcontroller
For the PIC to work, however, you need a 10 kΩ resistor on the MCLR (pull up to the power supply) and you also need to give a 100 nF decoupling capacitor between power supply and ground.
I started by trying the built-in oscillator - 8 MHz, faster is not possible. This is set in the OSCCON register:
OSCCON register table from PIC12F683 datasheet showing oscillator frequency settings
I wanted to illustrate how long it takes to do the GPIO state setting. This can of course be counted (from the Fosc/4 formula), but it is worthwhile to verify the result anyway. Example code:
Code: C / C++
Log in, to see the code

We are interested here in the timing of the high state, because after the low state there is still a jump to the start of the loop, and this is also an instruction:
Oscilloscope waveform showing a 520 ns pulse measured on Rigol screen
It comes out 500 ns... and we need 400 ns - 800 ns, a bit low.


Migrating to an external oscillator
8 MHz is rather too slow. You need to switch to an external oscillator:
Clock source block diagram of a PIC microcontroller with external oscillator highlighted
I have just such a 20 MHz one:
20 MHz quartz resonator held with tweezers, background box with other components
All you need to do is connect it on the right pins:
Pin diagram of PIC12F683 microcontroller in 8-pin PDIP/SOIC package
You also need to enable it in the configuration word.
Configuration word register of PIC12F with FOSC field highlighted in red
New code - we are testing:
Code: C / C++
Log in, to see the code

We are interested here in the timing of the low state, as there is still a spike after the high state:
Rigol DS1054Z oscilloscope screen showing square wave data signal for LED
It is better, 360 ns some, maybe less.


First colours
Since the time of one command is about 360 ns, two commands will give us about 720 ns. The second command can be completed with the command nop - processor passivity.
The zeros and ones times with the WS2812 are not critical, you can deviate to some extent from the expected timings and the diodes can still potentially work. For this reason, I was hoping that simple bit-checking wouldn't fully mess up communication. Similarly with my WS_HIGH and WS_LOW macros, which I'm not sure about - you'd have to check in the generated listing what the compiler converts them to.
Code: C / C++
Log in, to see the code

In the worst case scenario, I was planning to expand the byte send so that there is no loop, or write the whole bit in assembler. Another interesting trick is to load the whole byte into a GPIO register and shift it by one bit so as to expose its bits to a given pin sequentially. I have seen this done in the past with a PAL signal generator, but here it was not needed.
The rest of the code speaks for itself - I rigidly prepared three pixels, or nine bytes. The colours in turn separately.
Code: C / C++
Log in, to see the code

I then connected the LED strip to the chosen GPIO. Both the PIC and WS2812 run on 5 V, so no power supply problems. The LED strip has a separate data, power and ground line, so you don't have to worry about the current capacity of the PIC pin.
Effect:
WS2812 LED strip glowing in three colors with Pickit 2 programmer and DIP IC on white surface
It appears to work, that the times obtained are within the WS2812 tolerances, but are we sure?


Pseudo-animation - colour swapping
To verify the operation, I prepared the simplest animation - a colour swap. Two arrays of data displayed with a gap so that you can see the change.
Code: C / C++
Log in, to see the code

It appears that the transfer works reliably enough and alternates the two sets of colours displayed. No data is lost:
Three glowing WS2812 LEDs in red, blue, and green colors


First animation
The next step is already authentic animation - just what kind? Maybe some smooth colour transitions based on a sine function. You can get a different coloured effect this way, as long as you introduce some offset between the numerical values of red, green and blue. For simplicity, I have introduced an array of sine values (in byte form), although this is largely a disastrous approach - I lose as much as 32 bytes for it alone!
Code: C / C++
Log in, to see the code

The effect works, but there is one problem:
Colorful WS2812 LEDs driven by microcontroller, oscilloscope in background
No more pixels can be added because there is not enough RAM.
Compilation error log for PIC12F683 showing relocation and memory allocation issues


Optimisations and limitations
I've tried various approaches, but even switching to dynamically counted values doesn't change much either - SDCC needs memory for them too, as it creates helper functions for them when the operation is not supported by the processor:
warning: Relocation symbol "_cinit" [0x0022] has no section. (pass 0)
warning: Relocation of section "UDL__moduint_0" failed, relocating to a shared memory location.
error: No target memory available for section "UDL__moduint_0".
error: Error while writing hex file.

warning: Relocation of section "IDD_idata_0" failed, relocating to a shared memory location.
error: No target memory available for section "IDD_idata_0".
error: Error while writing hex file.

warning: Relocation of section "UDL__mulint_0" failed, relocating to a shared memory location.
error: No target memory available for section "UDL__mulint_0".
error: Error while writing hex file.

For best performance this would need to be written in assembler and limited to types that fit in registers, maybe then a bit more bytes could be racked up.
The version below supports up to about 20 LEDs, so 60 bytes are allocated to pixels. This is less than 50% of the available RAM, as there are 128 bytes in total.
Code: C / C++
Log in, to see the code

End result:



Now you can implement already other animations, knight rider, chasing fireflies, fireworks, rainbows, of course in a given firmware compilation there will probably be only one of them, depends how much computation is required.

Summary
On the PIC12F683 we have a total of 128 bytes of RAM. One RGB pixel is classically assumed to be 3 bytes, so assuming half the RAM goes to pixels (because there are still variables, etc) then we have 64/3 = about 21 pixels. In practice I was able to run up to 20 pixels, although the code could probably still be slimmed down considerably.
Potentially you could try to optimise this, but I don't see many options here:
- there are unlikely to be any peripherals on the PIC12F, such as hardware SPI, that could send these bytes
- The PIC12F is rather too slow to generate these bytes on the fly
- an interesting idea would be to pack the pixels in fewer bytes, but then the animation and decoding of the results itself gets complicated
Probably rewriting the whole thing to the assembler would help a bit, but at this stage I find that those 128 bytes of RAM are however largely an impassable obstacle in terms of animating more LEDs.
The PIC12F is capable of controlling the WS2812, but only short strings, even more so if you want to add something else to it - button control or different modes there.
Follow up in a separate topic.
I am attaching the project (code + bat script for SDCC)
Attachments:
  • pic12f683_ws.zip (16.79 KB) You must be logged in to download this attachment.

About Author
p.kaczmarek2
p.kaczmarek2 wrote 14221 posts with rating 12114 , helped 647 times. Been with us since 2014 year.

Comments

TechEkspert 20 Dec 2025 16:04

An interesting Christmas surprise! Such optimisations may seem like logic puzzles that can be bypassed by using a more powerful microcontroller. However, I personally think it is worth overcoming such... [Read more]

p.kaczmarek2 20 Dec 2025 16:11

I was also a bit taken aback by these optimisations. As I gave up on that lookup table for the sine function and tried to rewrite the effect to count on the fly, I quickly started to run into more problems.... [Read more]

TechEkspert 20 Dec 2025 16:28

These rings allow you to generate nice effects more easily to give the impression of movement and cyclicity of the effect. Perhaps if linear changes were used instead of a sine, such increments would be... [Read more]

p.kaczmarek2 20 Dec 2025 16:40

Yes, this holding of settings is useful, you don't have to "refresh non-stop every frame" as with, for example, PAL image generation, or there images for monitors in general, VGA the same. For the time... [Read more]

TechEkspert 20 Dec 2025 16:53

The most advanced WS2812 animations I've come across on elektroda.pl are here: LED star with WS2812B LEDs and ATTINY85 microcontroller [Read more]

p.kaczmarek2 20 Dec 2025 17:01

512 bytes of RAM the author had: https://obrazki.elektroda.pl/2725650800_1766246374_bigthumb.jpg [Read more]

Andrzej_Tomaszewski 20 Dec 2025 23:48

Hi! When I saw this circle with 24 WSs I was immediately reminded of how I managed to "figure out" these pixels in a BASCOM AVR environment almost 10 years ago. The main idea was to use an 8-leg uC and... [Read more]

_ACeK_ 21 Dec 2025 04:47

:smile: Julian Ilett showed on his video that even 4 MHz :idea: But to turn on - off you need more Mega Herts :wink: :arrow: Programme from film :razz: #include "p12F675" __CONFIG... [Read more]

TechEkspert 21 Dec 2025 09:48

PIC, AVR, C, Arduino, Bascom AVR, assembler you can see that in such combinations, engineering creativity is the most important thing and the tool is less important. The tool and the platform can always... [Read more]

p.kaczmarek2 21 Dec 2025 10:57

And remember the days when the WS2812 didn't exist yet? Interesting topic: A strip of individually controlled RGB LEDs https://hackaday.com/2019/03/26/can-you-live-without-the-ws2812/ [Read more]

Andrzej_Tomaszewski 21 Dec 2025 11:12

Yes, but this number of bytes of RAM is in my case just an unnecessary addition because I wanted to have as much program memory as possible in an 8-legged uC in order to fit as many effects as possible... [Read more]

p.kaczmarek2 21 Dec 2025 11:20

Well, it's exactly like mine, except that I, with 128 bytes of RAM and with the extra operations/changes, was already struggling to fit 24 LEDs, although 21 managed to fit. I'd have to do a bit more thinking. And... [Read more]

TechEkspert 21 Dec 2025 11:31

This changes the situation, 72 bytes of RAM were needed for the ring and 192 bytes of RAM for the star. In the second case, this is already more than the title PIC12F683 has. That is, we have an optimisation... [Read more]

p.kaczmarek2 21 Dec 2025 12:19

I don't have a reliable source in front of me at the moment, but on these PICs one instruction is 4 clock cycles (I guess the exception is jumps - they take two instruction cycles), so at 20 MHz one instruction... [Read more]

TechEkspert 21 Dec 2025 13:21

Ok actually it is in-flight unrealisable. For that the RP2040 and RP2350 has tempting and paralleled hardware support in this area. [Read more]

Andrzej_Tomaszewski 21 Dec 2025 22:13

Here is the compilation report. https://obrazki.elektroda.pl/3380322300_1766348675_thumb.jpg Cd details of the variables used. https://obrazki.elektroda.pl/8065666700_1766348840_thumb.jpg... [Read more]

p.kaczmarek2 22 Dec 2025 02:00

That RAM would go along with my estimate.... thanks for posting the screenshots. However, now I am thinking about one more issue. How much does one instruction last on this ATTINY85 in your case? Personally... [Read more]

TechEkspert 22 Dec 2025 10:35

This would agree PIC most instructions 4 bars, some instructions 8 clock bars (e.g. jumps). Avr most instructions one clock bar, some 2 bars (e.g. jumps, multiplications), there are also instructions... [Read more]

p.kaczmarek2 22 Dec 2025 10:41

I would still be curious about one thing. How much would it be possible to overclock this PIC of mine. It is known that this should not be done for production, because who will guarantee that it will work... [Read more]

FAQ

TL;DR: With 128 bytes of RAM, a PIC12F683 can animate about 20 WS2812 LEDs; “128 bytes of RAM are… an impassable obstacle.” [Elektroda, p.kaczmarek2, post #21785486]

Why it matters: This FAQ helps makers decide how many NeoPixels they can drive on tiny PICs and how to wire and code them efficiently.

Quick Facts

  • PIC12F683: 2 KB Flash, 128 B RAM, internal 8 MHz, external up to 20 MHz; ~20 LEDs demonstrated.
  • WS2812 timing: T0H ≈ 0.4 μs, T1H ≈ 0.8 μs; single-wire daisy-chain protocol.
  • Frame buffer math: 3 bytes per RGB LED; 20 LEDs need 60 bytes; 32-step sine table adds 32 bytes.
  • Power note: A 24-LED ring can draw ~500 mA at higher brightness.
  • Refresh behavior: WS2812 holds its state; compute between frames safely.

How many WS2812 LEDs can a PIC12F683 with 128 bytes of RAM drive for animations?

About 20 LEDs, based on a working build that buffered 60 bytes for pixels and kept variables minimal. The rule of thumb is 3 bytes per LED, so half the RAM (~64 bytes) yields about 21 LEDs on paper. Practical code overhead reduced that to 20 LEDs in testing. “128 bytes of RAM are… an impassable obstacle” for scaling beyond that without assembly-level optimization. [Elektroda, p.kaczmarek2, post #21785486]

Do I need an external 20 MHz clock to meet WS2812 timing on PIC12F683?

Yes, for reliable bit timing. Internal 8 MHz toggling produced about 500 ns highs, leaving little margin. Switching to a 20 MHz crystal improved instruction timing (~360 ns low pulses, ~720 ns with two ops), fitting WS2812 windows better. Configure HS oscillator in the config word and wire the crystal to the OSC pins. This reduced jitter and stabilized color data transmission. [Elektroda, p.kaczmarek2, post #21785486]

How much RAM does each WS2812 LED require?

Each RGB LED requires 3 bytes (G, R, B). For 20 LEDs, budget about 60 bytes just for the frame buffer. Extra arrays, like a 32-entry sine table, add 32 bytes, which matters on a 128-byte device. Keep working variables tiny and prefer bytes over ints. [Elektroda, p.kaczmarek2, post #21785486]

Can I stream pixel data on-the-fly without a full buffer?

Not on this setup. The author notes calculations must finish before transmission; combining calculations during the strict WS2812 send window was not viable. The strip retains its state after a frame, so compute between frames instead. Plan RAM for a buffer, even if small. [Elektroda, p.kaczmarek2, post #21785747]

Why do division, modulo, or int multiplication blow up memory in SDCC here?

SDCC inserts helper routines for operations the PIC core lacks. On PIC12F683, those helpers allocate extra sections, causing link errors like “No target memory available for section UDL__moduint_0.” Use byte math, look-up tables, or assembly to avoid those heavy helpers. [Elektroda, p.kaczmarek2, post #21785718]

What current should I expect for a 24‑LED WS2812 ring at higher brightness?

Plan for around 500 mA. That aligns with observed draw on 24-LED rings when brightness is high. Use a 5 V supply with adequate headroom and route grounds carefully. Add a large electrolytic across the strip’s power and a small series resistor on DIN. [Elektroda, TechEkspert, post #21785727]

Do WS2812 LEDs need continuous refresh like VGA?

No. “The holding of settings is useful,” so the pixels keep their color after the frame ends. You can compute the next frame at a relaxed pace and then transmit. This behavior simplifies timing and frees CPU between frames on small MCUs. [Elektroda, p.kaczmarek2, post #21785747]

How do I wire and configure a PIC12F683 to start driving WS2812? (3 steps)

  1. Add 10 kΩ pull‑up to MCLR and a 100 nF decoupling cap across Vdd–Vss.
  2. Fit a 20 MHz crystal on OSC pins and set HS oscillator in config.
  3. Connect one GPIO to DIN of the strip, share 5 V and GND, and send GRB data. [Elektroda, p.kaczmarek2, post #21785486]

What’s a minimal animation that fits in 128 bytes?

Start with two 9‑byte frames (3 LEDs) that swap colors, or an 18–20 LED sine‑wave using a compact 32‑byte table. The author showed both working, noting the LUT cost and the need to keep variables byte-sized. A 32‑step sine table consumes 32 bytes. [Elektroda, p.kaczmarek2, post #21785486]

Can multiple PICs extend strip length?

Yes. Use several synchronized PICs, each driving its own segment. Since WS2812 holds its state, you can coordinate frames across controllers without high refresh rates. Share a timing reference or simple trigger line for effect alignment. [Elektroda, TechEkspert, post #21785727]

Which programmers and chips were used or suggested?

PICKIT2 worked well with auto-detect and reflash during iterations. For more capable parts like PIC12F1840, PICKIT3 may be needed if PICKIT2 fails to recognize the device. This enables scaling RAM and peripherals when 12F683 limits bite. [Elektroda, p.kaczmarek2, post #21785718]

What are good alternatives if I need more LEDs or easier timing?

Consider MCUs and methods with native helpers: PIC18F with libraries, ESP32 using RMT, SPI+DMA tricks, or platforms like WLED and DMX over RS485. The author links examples and tutorials covering these approaches for larger, smoother animations. [Elektroda, p.kaczmarek2, post #21785747]

What is OpenBeken in this context?

OpenBeken is firmware referenced by the author that includes WS2812B animation support, an HTTP panel, and a PixelAnim driver. It’s a route when moving beyond tiny PIC constraints while keeping DIY flexibility. [Elektroda, p.kaczmarek2, post #21785747]

What timing edge cases can break WS2812 updates on PIC12F683?

WS2812 expects T0H ≈ 0.4 μs and T1H ≈ 0.8 μs. Internal 8 MHz produced marginal pulses (~500 ns highs). Moving to 20 MHz matched windows better (~360–720 ns steps). If timing drifts, colors corrupt or bits drop. [Elektroda, p.kaczmarek2, post #21785486]

Is there an example of advanced animations on small AVRs for inspiration?

Yes. A LED star project on ATTINY85 used 512 bytes of RAM for richer effects, showing what extra memory enables. Use it as a benchmark when choosing your MCU for animations. [Elektroda, p.kaczmarek2, post #21785779]
%}