Skip to main content

LED module

The LED module drives strips and matrices of WS2812 color LEDs (also called NeoPixel): the ones with a chip in each LED, chained along a single data wire. LED.new creates a strip on a pin; with its methods you set the color of each LED and the brightness of the whole strip, and show() sends it to the LEDs.

use LED

Platforms​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
Where you see itOn the WS2812 Strip card of the GPIO simulatorOn the strip wired to the pinOn the strip wired to the pinOn the strip wired to the pin
Strips at once8, with 2,048 LEDs among all of themOne per transmit RMT channel of the chip: 8 on the ESP32 and 4 on the ESP32-S3One per free RMT channelOne per free RMT channel
Memory per LED3 bytes99 bytes: a strip of 1,024 LEDs takes about 100 KB6 bytes6 bytes
setOrderDoesn't change what you seeChanges the order in which the colors are sentSameSame
setBrightness with a decimalSets the brightness to 255Uses the integer partSameSame

On Windows, the module doesn't need the simulator to be open: it keeps the strip in a memory it shares with it, and the card paints it as soon as it sees it. The simulator walkthrough takes you through the LedStrip example step by step.

Functions​

FunctionWhat it doesReturns
LED.new(pin, n)Creates a strip of n LEDs on a pinThe strip

new(pin, n)​

Creates a strip of n LEDs wired to pin pin.

ParameterTypeDescription
pinintegerThe GPIO the strip's data wire goes to. The VM doesn't check that the pin exists or is free.
nintegerHow many LEDs the strip has, from 1 to 1024.

It returns the strip, with every LED set to black, the brightness at 255 and the color order LED.GRB. Nothing lights up until the first show().

If something goes wrong, it throws an exception, which you catch with try (see Errors and exceptions):

ErrorWhen
ERR_INVALID_ARG_NUMBERYou don't pass two arguments.
ERR_INVALID_VALUEn isn't between 1 and 1024.
ERR_TYPE_MISMATCHOn the boards, pin or n aren't integers. On Windows, a pin that isn't an integer counts as 0, and an n that isn't an integer gives the error above.
ERR_INVALID_OPERATIONThere's no room for another strip: on Windows, there are already 8 or it would go over 2,048 LEDs; on a board, there's no free RMT channel left.
ERR_OUT_OF_MEMORYThere's no memory for the strip.
use LED

var strip = LED.new(17, 8) // 8 LEDs on GPIO 17
println(strip.length()) // 8

Strip methods​

The strip keeps the color of each LED in memory. The methods that set colors only change that memory: the LEDs don't change until you call show().

LEDs are counted from 0: on a strip of n, they go from 0 to n - 1. Colors come as three numbers from 0 to 255 (red, green and blue) or in HSV: the hue in degrees, and the saturation and the value from 0 to 255.

MethodWhat it doesReturns
setPixel(i, r, g, b)Sets the color of one LEDtrue
setHSV(i, h, s, v)Sets the color of one LED, in HSVtrue
getPixel(i)Reads the color of one LEDA list [r, g, b]
fill(r, g, b)Sets every LED to the same colortrue
fillHSV(h, s, v)The same, in HSVtrue
clear()Sets every LED to blacktrue
setBrightness(v)Sets the brightness of the whole strip, from 0 to 255true
setOrder(order)Says in which order the strip expects the colorstrue
show()Sends the colors to the LEDstrue
length()How many LEDs the strip hasAn integer
close()Turns the LEDs off and frees the striptrue

Errors shared by all of them (exceptions):

  • ERR_INVALID_ARG_NUMBER if the number of arguments isn't the one the method asks for.
  • ERR_INDEX_OUT_OF_RANGE if LED i isn't on the strip.
  • ERR_INVALID_OPERATION if the strip has been closed with close().

setPixel(i, r, g, b)​

Sets the color of LED i. r, g and b are red, green and blue, from 0 to 255: anything outside that range is clipped (300 stays at 255, and -5, at 0).

All four have to be integers. A decimal color counts as 0, and a decimal i gives ERR_INDEX_OUT_OF_RANGE: turn decimals into integers first with int() or Math.floor.

use LED

var strip = LED.new(17, 8)
strip.setPixel(0, 255, 0, 0) // the first one, red
strip.setPixel(7, 0, 0, 255) // the last one, blue
strip.setPixel(1, 300, -5, 128) // it gets clipped
println(strip.getPixel(1)) // [255, 0, 128]
strip.show()

setHSV(i, h, s, v)​

Sets the color of LED i in HSV, which is handier for rainbows and gradients:

  • h, the hue, in degrees: 0 is red, 120 green and 240 blue. It wraps around: 480 is 120, and -120 is 240.
  • s, the saturation, from 0 to 255. With 0, the LED is gray (or white), at the level of v.
  • v, the value (how much light), from 0 to 255.

s and v aren't clipped: outside 0 to 255 you get a different color. As with setPixel, all four have to be integers.

use LED

var strip = LED.new(17, 3)
strip.setHSV(0, 0, 255, 255) // red
strip.setHSV(1, 120, 255, 255) // green
strip.setHSV(2, 240, 255, 255) // blue
println(strip.getPixel(1)) // [0, 255, 0]
strip.show()

getPixel(i)​

Returns the color of LED i as a list [r, g, b]. It's the color the strip keeps, without the brightness: after setBrightness(32), a red LED is still [255, 0, 0]. A color set in HSV comes back converted to red, green and blue.

fill(r, g, b)​

Sets every LED to the same color. The values are clipped and have to be integers, as in setPixel.

fillHSV(h, s, v)​

Sets every LED to the same color, in HSV, as setHSV does.

use LED

var strip = LED.new(17, 8)
strip.fillHSV(30, 255, 255) // orange
println(strip.getPixel(0)) // [255, 127, 0]
strip.fillHSV(0, 0, 100) // gray
println(strip.getPixel(0)) // [100, 100, 100]
strip.show()

clear()​

Sets every LED to black. Like the others, it turns nothing off until the next show().

setBrightness(v)​

Sets the brightness of the whole strip, from 0 (off) to 255 (the default). Anything outside that range is clipped.

The brightness is applied when show() sends the colors: each color is multiplied by (v + 1) / 256. The colors the strip keeps don't change, so you can turn the brightness up and down without losing them, and getPixel returns them without it.

use LED

var strip = LED.new(17, 8)
strip.fill(255, 120, 0) // all orange
strip.setBrightness(32) // at an eighth of the brightness
strip.show()
println(strip.getPixel(0)) // [255, 120, 0]

On the boards, a decimal counts as its integer part, and a value that isn't a number gives ERR_TYPE_MISMATCH. On Windows, anything that isn't an integer sets the brightness to 255. Pass the brightness as an integer.

setOrder(order)​

Says in which order the strip expects the three colors. Most WS2812s want them as green, red and blue, which is the default (LED.GRB). If your strip shows red when you ask for green, try another of the constants.

setPixel, fill and getPixel still speak red, green and blue: the order only changes what is sent to the strip. A value that isn't one of the constants gives ERR_INVALID_VALUE.

use LED

var strip = LED.new(17, 8)
strip.setOrder(LED.RGB) // this strip expects red, green and blue

On Windows, the simulator card shows the colors just as the program sets them, and setOrder changes nothing.

show()​

Sends the strip's colors to the LEDs, with the brightness applied. On a board, the program waits until they have all been sent. On Windows, the simulator's WS2812 Strip card is painted at that moment.

It returns true; on a board, false if they couldn't be sent.

length()​

Returns how many LEDs the strip has: the n of LED.new. It still works after close().

close()​

Turns the LEDs off (it sends black to the whole strip) and frees its memory and its channel, so that another strip can use it. Afterwards, the methods that touch colors give ERR_INVALID_OPERATION. Calling it again does nothing and returns true.

use LED

var strip = LED.new(17, 8)
strip.fill(0, 255, 0)
strip.show()
pause(1000)
strip.close() // the strip goes dark

Constants​

The color order for setOrder:

ConstantValueOrder in which they're sent
LED.GRB0Green, red, blue. It's the default, the one most WS2812s use.
LED.RGB1Red, green, blue.
LED.BRG2Blue, red, green.
LED.RBG3Red, blue, green.
LED.GBR4Green, blue, red.
LED.BGR5Blue, green, red.

Complete example​

A rainbow that spins along a strip of 30 LEDs for 5 seconds. Then it turns the strip off:

use LED

const NUM_LEDS = 30

var strip = LED.new(17, NUM_LEDS) // 30 LEDs on GPIO 17
strip.setBrightness(64) // at a quarter of the brightness

var spin = 0
var start = clock()
while (clock() - start < 5000)
for (var i = 0; i < NUM_LEDS; i++)
var hue = (i * 360) div NUM_LEDS + spin
strip.setHSV(i, hue, 255, 255)
end
strip.show()
spin = (spin + 6) % 360
pause(30)
end

strip.close() // turns the strip off and frees it

The LedStrip example​

JARU IDE comes with the LedStrip example project: 17 programs for an 8 × 8 LED matrix (a strip of 64) on GPIO 17, with plasma, fire, fireworks, the game of life, a countdown… You create it from Home › Project › New, in Example Projects. Its main program is Code/Matrix8x8Plasma.aru; to try another one, pick it in the explorer's Main File list and press Run (Projects).

The programs count the LEDs row by row, with a function like this one:

use LED

const WIDTH = 8

var strip = LED.new(17, 64)

func xy(x, y)
return y * WIDTH + x // row by row, from left to right
end

strip.setPixel(xy(3, 2), 255, 0, 0) // column 3, row 2
strip.show()

If your matrix is wired in a zigzag (every other row backwards), change that calculation. On Windows, the shape and the wiring of the matrix are chosen on the simulator card, in Layout and Wiring.

Pitfalls and known issues​

  • Nothing shows until show(). setPixel, fill, clear and setBrightness only change what the strip keeps.
  • Colors as integers. A decimal color counts as 0, with no error: strip.setPixel(0, 127.5, 0, 0) leaves the LED off. Use int(), div or Math.floor.
  • In HSV, the saturation and the value aren't clipped. Outside 0 to 255 you get colors you don't expect.
  • Brightness with decimals, on Windows. setBrightness with a decimal sets the brightness to 255 on Windows and uses the integer part on a board. Pass it as an integer.
  • Without close(), the LEDs stay on. If the program ends without closing the strip, on a board the LEDs keep the last colors they received.
  • Memory on the ESP32 and ESP32-S3. Each LED takes 99 bytes of the VM's memory while the strip is open. On a board without PSRAM, a long strip can leave the program short of memory.
  • The room of a closed strip, on Windows. When you close a strip, its space among the 2,048 LEDs isn't reused while another strip created after it is still open.

All the known issues of JARU, together, are in Known issues.