Skip to content

Commit 27d2419

Browse files
author
betzrhodes
authored
Merge pull request #11 from electricimp/develop
Updated to support all imps
2 parents 2bdbd92 + 8b41731 commit 27d2419

File tree

4 files changed

+122
-75
lines changed

4 files changed

+122
-75
lines changed

Diff for: README.md

+16-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# WS2812 v2.0.2
1+
# WS2812 3.0.0
22

33
This class allows the imp to drive WS2812 and WS2812B LEDs. The WS2812 is an all-in-one RGB LED with integrated shift register and constant-current driver. The parts are daisy-chained, and a proprietary one-wire protocol is used to send data to the chain of LEDs. Each pixel is individually addressable and this allows the part to be used for a wide range of effects animations.
44

@@ -9,18 +9,18 @@ Some example hardware that uses the WS2812 or WS2812B:
99
* [30 LED - 1m strip](http://www.adafruit.com/products/1376)
1010
* [NeoPixel Stick](http://www.adafruit.com/products/1426)
1111

12-
**To add this library to your project, add `#require "WS2812.class.nut:2.0.2"` to the top of your device code.**
13-
14-
You can view the library’s source code on [GitHub](https://github.com/electricimp/ws2812/tree/v2.0.2).
12+
**To add this library to your project, add** `#require "WS2812.class.nut:3.0.0"` **to the top of your device code.**
1513

1614
## Hardware
1715

18-
WS2812s require a 5V power supply and logic, and each pixel can draw up to 60mA when displaying white in full brightness, so be sure to size your power supply appropriatly. Undersized power supplies (lower voltages and/or insufficent current) can cause glitches and/or failure to produce and light at all.
16+
WS2812s require a 5V power supply and logic, and each pixel can draw up to 60mA when displaying white in full brightness, so be sure to size your power supply appropriately. Undersized power supplies (lower voltages and/or insufficient current) can cause glitches and/or failure to produce and light at all.
1917

2018
Because WS2812s require 5V logic, you will need to shift your logic level to 5V. A sample circuit can be found below using Adafruit’s [4-channel Bi-directional Logic Level Converter](http://www.adafruit.com/products/757):
2119

2220
![WS2812 Circuit](./circuit.png)
2321

22+
**Warning** We do not recommend using the imp005 with WS2812s. Unlike the imp001, imp002, imp003 and imp004m, the imp005 does not use DMA for SPI data transfers. Instead, each byte is written out individually, and this means there will always be a small gap between each byte. As a result, the LEDs may not work as expected and the performance of other operations, such as Agent/Device communications, are blocked when the *draw()* method is called.
23+
2424
## Class Usage
2525

2626
All public methods in the WS2812 class return `this`, allowing you to easily chain multiple commands together:
@@ -33,54 +33,38 @@ pixels
3333
.draw();
3434
```
3535

36-
### Constructor: WS2812(spi, frameSize, [draw])
36+
### Constructor: WS2812(*spi, numberOfPixels[, draw]*)
3737

38-
Instantiate the class with a pre-configured SPI object and the number of pixels that are connected. The SPI object must be configured at 7500kHz and have the *MSB_FIRST* flag set:
38+
Instantiate the class with an imp SPI object and the number of pixels that are connected. The SPI object will be configured by the constructor. An optional third parameter can be set to control whether the class will draw an empty frame on initialization. The default value is `true`.
3939

4040
```squirrel
41-
#require "ws2812.class.nut:2.0.2"
41+
#require "ws2812.class.nut:3.0.0"
4242
43-
// Configure the SPI bus
43+
// Select the SPI bus
4444
spi <- hardware.spi257;
45-
spi.configure(MSB_FIRST, 7500);
4645
4746
// Instantiate LED array with 5 pixels
4847
pixels <- WS2812(spi, 5);
4948
```
5049

51-
An optional third parameter can be set to control whether the class will draw an empty frame on initialization. The default value is `true`.
52-
5350
## Class Methods
5451

55-
### configure()
56-
57-
Rather than pass a preconfigured SPI object to the constructor, you can pass an unconfigured SPI object, and have the *configure()* method automatically configure the SPI object for you.
58-
59-
**NOTE:** If you are using the *configure* method, you **must** pass `false` the the *draw* parameter of the constructor:
60-
61-
```squirrel
62-
#require "ws2812.class.nut:2.0.2"
63-
64-
// Create and configure an LED array with 5 pixels:
65-
pixels <- WS2812(hardware.spi257, 5, false).configure();
66-
```
67-
6852
### set(*index, color*)
6953

70-
The *set* method changes the color of a particular pixel in the frame buffer. The color is passed as as an array of three integers between 0 and 255 representing `[red, green, blue]`.
54+
The *set()* method changes the color of a particular pixel in the frame buffer. The color is passed as as an array of three integers between 0 and 255 representing `[red, green, blue]`.
7155

72-
NOTE: The *set* method does not output the changes to the pixel strip. After setting up the frame, you must call `draw` (see below) to output the frame to the strip.
56+
**Note** The *set()* method does not output the changes to the pixel strip. After setting up the frame, you must call *draw()* (see below) to output the frame to the strip.
7357

7458
```squirrel
7559
// Set and draw a pixel
7660
pixels.set(0, [127,0,0]).draw();
7761
```
7862

79-
### fill(*color, [start], [end]*)
63+
### fill(*color[, start][, end]*)
8064

81-
The *fill* methods sets all pixels in the specified range to the desired color. If no range is selected, the entire frame will be filled with the specified color.
65+
The *fill()* method sets all pixels in the specified range to the desired color. If no range is selected, the entire frame will be filled with the specified color.
8266

83-
NOTE: The *fill* method does not output the changes to the pixel strip. After setting up the frame, you must call `draw` (see below) to output the frame to the strip.
67+
**Note** The *fill()* method does not output the changes to the pixel strip. After setting up the frame, you must call *draw()* (see below) to output the frame to the strip.
8468

8569
```squirrel
8670
// Turn all LEDs off
@@ -92,13 +76,13 @@ pixels.fill([0,0,0]).draw();
9276
// and the other half blue
9377
pixels
9478
.fill([100,0,0], 0, 2)
95-
.fill([0,0,100], 3, 4);
79+
.fill([0,0,100], 3, 4)
9680
.draw();
9781
```
9882

9983
### draw()
10084

101-
The *draw* method draws writes the current frame to the pixel array (see examples above).
85+
The *draw()* method draws writes the current frame to the pixel array (see examples above).
10286

10387
## License
10488

Diff for: WS2812.class.nut

+105-41
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,58 @@
33
// http://opensource.org/licenses/MIT
44

55
class WS2812 {
6-
// This class uses SPI to emulate the WS2812s' one-wire protocol.
7-
// This requires one byte per bit to send data at 7.5 MHz via SPI.
8-
// These consts define the "waveform" to represent a zero or one
96

10-
static VERSION = [2,0,2];
7+
static VERSION = "3.0.0";
8+
9+
static ERROR_005 = "Use of this imp module is not advisable.";
10+
11+
// This class uses SPI to emulate the WS2812s' one-wire protocol.
12+
// The ideal speed for WS2812 LEDs is 6400 MHz via SPI.
1113

14+
// The closest Imp001 & Imp002 supported SPI datarate is 7500 MHz
15+
// Imp004m supported SPI datarate is 6000 MHz
16+
// Imp005 supported SPI datarate is 6400 MHz
17+
// These consts define the "waveform" to represent a zero or one
1218
static ZERO = 0xC0;
1319
static ONE = 0xF8;
1420
static BYTES_PER_PIXEL = 24;
1521

22+
// The closest Imp003 supported SPI datarate is 9 MHz.
23+
// These consts define the "waveform" to represent a zero-zero, zero-one, one-zero, one-one.
24+
static ZERO_ZERO = "\xE0\x0E\x00";
25+
static ZERO_ONE = "\xE0\x0F\xC0";
26+
static ONE_ZERO = "\xFC\x0E\x00";
27+
static ONE_ONE = "\xFC\x0F\xC0";
28+
static IMP3_BYTES_PER_PIXEL = 36;
29+
1630
// When instantiated, the WS2812 class will fill this array with blobs to
1731
// represent the waveforms to send the numbers 0 to 255. This allows the
1832
// blobs to be copied in directly, instead of being built for each pixel.
19-
2033
static _bits = array(256, null);
2134

2235
// Private variables passed into the constructor
23-
24-
_spi = null; // imp SPI interface (pre-configured)
36+
_spi = null; // imp SPI interface
2537
_frameSize = null; // number of pixels per frame
2638
_frame = null; // a blob to hold the current frame
39+
_bytes_per_pixel = null; // number of bytes per pixel
2740

2841
// Parameters:
29-
// spi A pre-configured SPI bus (MSB_FIRST, 7500)
42+
// spi A SPI bus
3043
// frameSize Number of Pixels per frame
3144
// _draw Whether or not to initially draw a blank frame
3245
constructor(spiBus, frameSize, _draw = true) {
33-
// spiBus must be configured
34-
_spi = spiBus;
46+
local impType = _getImpType();
47+
_configureSPI(spiBus, impType);
3548

3649
_frameSize = frameSize;
37-
_frame = blob(_frameSize * BYTES_PER_PIXEL + 1);
38-
_frame[_frameSize * BYTES_PER_PIXEL] = 0;
50+
_frame = blob(_frameSize * _bytes_per_pixel + 1);
51+
_frame[_frameSize * _bytes_per_pixel] = 0;
3952

4053
// Used in constructing the _bits array
41-
local bytesPerColor = BYTES_PER_PIXEL / 3;
54+
local bytesPerColor = _bytes_per_pixel / 3;
4255

43-
// Fill the _bits array if required
4456
// (Multiple instance of WS2812 will only initialize it once)
45-
if (_bits[0] == null) {
46-
for (local i = 0; i < 256; i++) {
47-
local valblob = blob(bytesPerColor);
48-
valblob.writen((i & 0x80) ? ONE:ZERO,'b');
49-
valblob.writen((i & 0x40) ? ONE:ZERO,'b');
50-
valblob.writen((i & 0x20) ? ONE:ZERO,'b');
51-
valblob.writen((i & 0x10) ? ONE:ZERO,'b');
52-
valblob.writen((i & 0x08) ? ONE:ZERO,'b');
53-
valblob.writen((i & 0x04) ? ONE:ZERO,'b');
54-
valblob.writen((i & 0x02) ? ONE:ZERO,'b');
55-
valblob.writen((i & 0x01) ? ONE:ZERO,'b');
56-
_bits[i] = valblob;
57-
}
58-
}
57+
if (_bits[0] == null) _fillBitsArray(impType, bytesPerColor);
5958

6059
// Clear the pixel buffer
6160
fill([0,0,0]);
@@ -66,15 +65,6 @@ class WS2812 {
6665
}
6766
}
6867

69-
// Configures the SPI Bus
70-
//
71-
// NOTE: If using the configure method, you *must* pass `false` to the
72-
// _draw parameter in the constructor (or else an error will be thrown)
73-
function configure() {
74-
_spi.configure(MSB_FIRST, 7500);
75-
return this;
76-
}
77-
7868
// Sets a pixel in the buffer
7969
// index - the index of the pixel (0 <= index < _frameSize)
8070
// color - [r,g,b] (0 <= r,g,b <= 255)
@@ -84,7 +74,7 @@ class WS2812 {
8474
index = _checkRange(index);
8575
color = _checkColorRange(color);
8676

87-
_frame.seek(index * BYTES_PER_PIXEL);
77+
_frame.seek(index * _bytes_per_pixel);
8878

8979
// Create a blob for the color
9080
// Red and green are swapped for some reason, so swizzle them back
@@ -118,13 +108,13 @@ class WS2812 {
118108

119109
// Create a blob for the color
120110
// Red and green are swapped for some reason, so swizzle them back
121-
local colorBlob = blob(BYTES_PER_PIXEL);
111+
local colorBlob = blob(_bytes_per_pixel);
122112
colorBlob.writeblob(_bits[color[1]]);
123113
colorBlob.writeblob(_bits[color[0]]);
124114
colorBlob.writeblob(_bits[color[2]]);
125115

126116
// Write the color blob to each pixel in the fill
127-
_frame.seek(start*BYTES_PER_PIXEL);
117+
_frame.seek(start*_bytes_per_pixel);
128118
for (local index = start; index <= end; index++) {
129119
_frame.writeblob(colorBlob);
130120
}
@@ -140,6 +130,9 @@ class WS2812 {
140130
return this;
141131
}
142132

133+
// Private functions
134+
// --------------------------------------------------
135+
143136
function _checkRange(index) {
144137
if (index < 0) index = 0;
145138
if (index >= _frameSize) index = _frameSize - 1;
@@ -153,4 +146,75 @@ class WS2812 {
153146
}
154147
return colors
155148
}
156-
}
149+
150+
function _getImpType() {
151+
local env = imp.environment();
152+
if (env == ENVIRONMENT_CARD) {
153+
return 1;
154+
}
155+
if (env == ENVIRONMENT_MODULE) {
156+
return hardware.getdeviceid().slice(0,1).tointeger();
157+
}
158+
}
159+
160+
function _configureSPI(spiBus, impType) {
161+
_spi = spiBus;
162+
switch (impType) {
163+
case 1:
164+
// same as 002 config
165+
case 2:
166+
_bytes_per_pixel = BYTES_PER_PIXEL;
167+
_spi.configure(MSB_FIRST, 7500);
168+
break;
169+
case 3:
170+
_bytes_per_pixel = IMP3_BYTES_PER_PIXEL;
171+
_spi.configure(MSB_FIRST, 9000);
172+
break;
173+
case 4:
174+
_bytes_per_pixel = BYTES_PER_PIXEL;
175+
_spi.configure(MSB_FIRST, 6000);
176+
break;
177+
case 5:
178+
server.error(ERROR_005);
179+
_bytes_per_pixel = BYTES_PER_PIXEL;
180+
// Note: to see the actual rate log actualRate
181+
// Passing in 6000 actually sets datarate to 6400
182+
local actualRate = _spi.configure(MSB_FIRST, 6000);
183+
// server.log(actual Rate)
184+
break;
185+
}
186+
}
187+
188+
function _fillBitsArray(impType, bytesPerColor) {
189+
if (impType == 3) {
190+
for (local i = 0; i < 256; i++) {
191+
local valblob = blob(bytesPerColor);
192+
valblob.writestring(_getNumber((i /64) % 4));
193+
valblob.writestring(_getNumber((i /16) % 4));
194+
valblob.writestring(_getNumber((i /4) % 4));
195+
valblob.writestring(_getNumber(i % 4));
196+
_bits[i] = valblob;
197+
}
198+
} else {
199+
for (local i = 0; i < 256; i++) {
200+
local valblob = blob(bytesPerColor);
201+
valblob.writen((i & 0x80) ? ONE:ZERO,'b');
202+
valblob.writen((i & 0x40) ? ONE:ZERO,'b');
203+
valblob.writen((i & 0x20) ? ONE:ZERO,'b');
204+
valblob.writen((i & 0x10) ? ONE:ZERO,'b');
205+
valblob.writen((i & 0x08) ? ONE:ZERO,'b');
206+
valblob.writen((i & 0x04) ? ONE:ZERO,'b');
207+
valblob.writen((i & 0x02) ? ONE:ZERO,'b');
208+
valblob.writen((i & 0x01) ? ONE:ZERO,'b');
209+
_bits[i] = valblob;
210+
}
211+
}
212+
}
213+
214+
function _getNumber(num) {
215+
if(num == 0) return ZERO_ZERO;
216+
if(num == 1) return ZERO_ONE;
217+
if(num == 2) return ONE_ZERO;
218+
if(num == 3) return ONE_ONE;
219+
}
220+
}

Diff for: circuit.png

-14.9 KB
Loading

Diff for: examples/neoweather/neoweather.device.nut

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#require "ws2812.class.nut:2.0.2"
1+
#require "ws2812.class.nut:3.0.0"
22

33
class NeoWeather extends WS2812 {
44

@@ -487,7 +487,6 @@ agent.on("seteffect", function(val) {
487487
const NUMPIXELS = 24;
488488

489489
spi <- hardware.spi257;
490-
spi.configure(MSB_FIRST, 7500);
491490
display <- NeoWeather(spi, NUMPIXELS);
492491

493492
server.log("Ready, running impOS "+imp.getsoftwareversion());

0 commit comments

Comments
 (0)