Skip to content

Instantly share code, notes, and snippets.

@jpraus
Last active December 19, 2023 20:45
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save jpraus/b5084df54580ebec81fac7cec86048d3 to your computer and use it in GitHub Desktop.
Save jpraus/b5084df54580ebec81fac7cec86048d3 to your computer and use it in GitHub Desktop.
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
#define PIN_LEDS 21
#define NUMPIXELS 194
#define LIGHTNESS 0.05f
#define BRIGHTNESS 64
RgbColor BLACK(0, 0, 0);
RgbColor RED(BRIGHTNESS, 0, 0);
RgbColor GREEN(0, BRIGHTNESS, 0);
RgbColor BLUE(0, 0, BRIGHTNESS);
RgbColor YELLOW(BRIGHTNESS, BRIGHTNESS, 0);
RgbColor WHITE(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> leds(NUMPIXELS, PIN_LEDS);
NeoPixelAnimator animations(10); // NeoPixel animation management object
#define NUM_RINGS 11
#define RING_MAX_PIXELS 26
#define RING_MIN_PIXELS 8
// all rings starts at 0 ray
byte RINGS[NUM_RINGS][RING_MAX_PIXELS] = {
{78, 79, 80, 81, 82, 83, 84, 77}, // 8
{65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 63, 64}, // 14
{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 45, 46, 47}, // 18
{28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 25, 26, 27}, // 20
{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, 2, 3, 4}, // 24
{102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 110, 109, 108, 107, 106, 105, 104, 103}, // 26
{127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 134, 133, 132, 131, 130, 129, 128}, // 24
{148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 154, 153, 152, 151, 150, 149}, // 20
{167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 172, 171, 170, 169, 168}, // 18
{182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 186, 185, 184, 183}, // 14
{192, 191, 190, 189, 188, 187, 194, 193} // 8
};
byte RING_SIZES[NUM_RINGS] = {8, 14, 18, 20, 24, 26, 24, 20, 18, 14, 8};
unsigned long startMillis = 0;
short animation = 0;
void setup() {
Serial.begin(115200);
initRandom();
leds.Begin();
leds.ClearTo(BLACK);
leds.Show();
//animations.StartAnimation(0, 6000, rainbowAnimation);
animations.StartAnimation(0, 2000, raysRainbow);
//animations.StartAnimation(0, 2000, ringsRainbow);
//animations.StartAnimation(0, 500, xmasOrbAnimation);
//animations.StartAnimation(0, 1000, ringAnimation);
//animations.StartAnimation(0, 500, rayAnimation);
//animations.StartAnimation(0, 100, randomAnimation);
}
void loop() {
animations.UpdateAnimations();
leds.Show();
delay(10);
if (false && (startMillis == 0 || startMillis + 20000 < millis())) { // 30s
startMillis = millis();
switch (animation) {
case 0:
animations.StartAnimation(0, 500, rayAnimation);
break;
case 1:
animations.StartAnimation(0, 1000, ringAnimation);
break;
case 2:
animations.StartAnimation(0, 2000, ringsRainbow);
break;
case 3:
animations.StartAnimation(0, 2000, raysRainbow);
break;
case 4:
animations.StartAnimation(0, 6000, rainbowAnimation);
break;
case 5:
animations.StartAnimation(0, 100, randomAnimation);
animation = -1;
break;
}
animation ++;
}
}
void randomAnimation(const AnimationParam& param) {
float hue;
HslColor color;
if (param.state == AnimationState_Completed) {
for (byte i = 0; i < 194; i ++) {
hue = random(0, 1000) / 1000.0f;
color = HslColor(hue, 1.0f, LIGHTNESS);
leds.SetPixelColor(i, color);
}
animations.RestartAnimation(0);
}
}
void rainbowAnimation(const AnimationParam& param) {
HslColor color = HslColor(param.progress, 1.0f, LIGHTNESS);
leds.ClearTo(color);
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void raysRainbow(const AnimationParam& param) {
HslColor color;
float hue;
for (int i = 0; i < RING_MAX_PIXELS; i++) {
hue = param.progress + (float) i / (float) RING_MAX_PIXELS;
if (hue > 1.0f) {
hue -= 1.0f;
}
color = HslColor(hue, 1.0f, LIGHTNESS);
rayColor(i, RgbColor(color));
}
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void ringsRainbow(const AnimationParam& param) {
HslColor color;
float hue;
for (int i = 0; i < NUM_RINGS; i++) {
hue = param.progress + (float) i / (float) NUM_RINGS;
if (hue > 1.0f) {
hue -= 1.0f;
}
color = HslColor(hue, 1.0f, LIGHTNESS);
ringColor(i, RgbColor(color));
}
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void xmasOrbAnimation(const AnimationParam& param) {
ringColor(0, WHITE);
ringColor(1, RED);
ringColor(2, RED);
ringColor(3, RED);
ringColor(4, RED);
ringColor(5, WHITE);
ringColor(6, RED);
ringColor(7, RED);
ringColor(8, RED);
ringColor(9, RED);
ringColor(10, WHITE);
byte offset = round(param.progress);
for (byte i = offset; i < RING_SIZES[3]; i+=2) {
leds.SetPixelColor(RINGS[3][i] - 1, WHITE);
}
for (byte i = offset; i < RING_SIZES[7]; i+=2) {
leds.SetPixelColor(RINGS[7][i] - 1, WHITE);
}
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void ringAnimation(const AnimationParam& param) {
int index = param.progress * (NUM_RINGS * 2 - 2);
leds.ClearTo(BLACK);
if (index < NUM_RINGS) {
ringColor(index, BLUE);
}
else {
ringColor(NUM_RINGS - (index - NUM_RINGS) - 2, BLUE);
}
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void rayAnimation(const AnimationParam& param) {
int index = param.progress * (RING_MAX_PIXELS / 2);
if (index > 12) {
index = 12;
}
leds.ClearTo(BLACK);
rayColor(index, BLUE);
rayColor(index + (RING_MAX_PIXELS / 2), BLUE);
if (param.state == AnimationState_Completed) {
animations.RestartAnimation(0);
}
}
void rayColor(byte rayIndex, RgbColor color) {
int pixelIndex;
byte pixel;
if (rayIndex >= RING_MAX_PIXELS) {
return; // prevents out of bounds
}
for (byte i = 0; i < NUM_RINGS; i ++) {
pixelIndex = round((float) RING_SIZES[i] / (float) RING_MAX_PIXELS * rayIndex);
pixel = RINGS[i][pixelIndex];
if (pixel == 0) {
continue; // skip condition
}
leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
}
}
void ringColor(byte ringIndex, RgbColor color) {
byte pixel;
if (ringIndex >= NUM_RINGS) {
return; // prevents out of bounds
}
for (byte i = 0; i < RING_MAX_PIXELS; i ++) {
pixel = RINGS[ringIndex][i];
if (pixel == 0) {
return; // end condition
}
leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
}
}
void initRandom() {
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
uint32_t seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3) {
seed ^= analogRead(0) << shifts;
delay(1);
}
// Serial.println(seed);
randomSeed(seed);
}
RgbColor colorWheel(byte wheelPos) {
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
wheelPos = 255 - wheelPos;
if (wheelPos < 85) {
return RgbColor(255 - wheelPos * 3, 0, wheelPos * 3);
}
if (wheelPos < 170) {
wheelPos -= 85;
return RgbColor(0, wheelPos * 3, 255 - wheelPos * 3);
}
wheelPos -= 170;
return RgbColor(wheelPos * 3, 255 - wheelPos * 3, 0);
}
@Raabensohn
Copy link

Hello,

I'm not that firm in Arduino, in your video (https://www.youtube.com/watch?v=Q5d8gTppuYo) you show different animations. My sphere only shows animation starting at 13:45.

How can i change it? or did i miss something? ;-)

@jpraus
Copy link
Author

jpraus commented Mar 13, 2020

You can select your animation in the loop function:

//animations.StartAnimation(0, 6000, rainbowAnimation);
animations.StartAnimation(0, 2000, raysRainbow);
//animations.StartAnimation(0, 2000, ringsRainbow);
//animations.StartAnimation(0, 500, xmasOrbAnimation);
//animations.StartAnimation(0, 1000, ringAnimation);
//animations.StartAnimation(0, 500, rayAnimation);
//animations.StartAnimation(0, 100, randomAnimation);

just uncomment the one you like :)

@winkle117
Copy link

hi im new to this but when i put the code in i get tis problem
Arduino: 1.8.13 (Windows 7), Board: "ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 115200, None"

neo-sphere:1:25: fatal error: NeoPixelBus.h: No such file or directory

compilation terminated.

exit status 1

NeoPixelBus.h: No such file or directory

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@jpraus
Copy link
Author

jpraus commented Nov 6, 2020 via email

@bertrik
Copy link

bertrik commented May 3, 2021

I can write a simple platformio config for you.

This allows people to build the software without bothering about what libraries to install first, what target to select, board packages, etc.
The platformio config file specifies all of this (including a specific version if necessary), and allows building it all from the command line, so you can even do continuous integration (e.g. a test build every time you commit a change).

If you're interested, please let me know.

@Raabensohn
Copy link

Raabensohn commented May 4, 2021 via email

@bertrik
Copy link

bertrik commented May 4, 2021

I will create a project https://github.com/bertrik/neo-sphere maybe later tonight, with a copy of this ino file and a platformio configuration.

@Raabensohn
Copy link

Raabensohn commented May 4, 2021 via email

@jpraus
Copy link
Author

jpraus commented May 5, 2021

Nope, no improvements to the neosphere project so far.

@bertrik
Copy link

bertrik commented May 17, 2021

The arduino source with added platformio config is available on:
https://github.com/bertrik/neo-sphere

You can use it as follows;

  • install python3 and install platformio, e.g. sudo pip install platformio
  • run platformio to compile and upload: pio run -t upload

I might make the 'upload' action the default target, so you can just run 'pio run'.

@pannovy
Copy link

pannovy commented Dec 19, 2023

Hi, i made the sphere and i saw that the program works with maximum 254 leds and i want to make it to work with 384 leds and 15 rings.
Can you please help me relating to this matter ?

byte RINGS[NUM_RINGS][RING_MAX_PIXELS] = {
{1, 65,129,193,257,321}, //6
{2, 9, 66, 73, 130, 137, 194, 201,258, 265, 322,329}, //12
{3, 10, 17, 67, 74, 81, 131, 138, 145, 195, 202, 209, 259, 266, 273, 323, 330, 337}, //18
{4, 11, 18, 25, 68, 75, 82, 89, 132, 139, 146, 153, 196, 203, 210, 217, 260, 267, 274, 281, 324, 331, 338, 345}, //24
{5, 12, 19, 26, 33, 69, 76, 83, 90, 97, 133, 140, 147, 154, 161, 197, 204, 211, 218, 225, 261,268, 275, 282, 289, 325, 332, 339, 346, 353}, //30
{6, 13, 20, 27, 34, 41, 70, 77,84, 91, 98, 105, 134, 141, 148, 155, 162, 169, 198, 205, 212, 219, 226, 233, 262, 269, 276, 283, 290, 297, 326, 333, 340, 347, 354, 361}, //36
{7, 14, 21, 28, 35, 42, 49, 71, 78, 85, 92, 99, 106, 113, 135, 142, 149, 156, 163, 170, 177, 199, 206, 213,220, 227, 234, 241, 263,270, 277, 284, 291, 298, 305,327, 334, 341, 348, 355, 362, 369}, //42
{8, 15, 22, 29, 36, 43, 50, 57, 72, 79, 86, 93, 100, 107, 114, 121, 136, 143, 150, 157, 164, 171, 178, 185, 200, 207, 214, 221, 228, 235, 242, 249,264,271,278,285,292,299,306,313,328,335,342,349,356,363,370,377}, // 48
{16, 23, 30, 37, 44, 51, 58, 80, 87, 94, 101, 108, 115, 122, 144, 151, 158,165,172,179,186,208,215,222,229,236,243,250,272,279,286,293,300,307,314,336,343,350,357,364,371,378}, //42
{24,31,38,45,52,59,88,95,102,109,116,123,152,159,166,173,180,187,216,223,230,237,244,251,280,287,294,301,308,315,344,351,358,365,372,379}, //36
{32,39,46,53,60,96,103,110,117,124,160,167,174,181,188,224,231,238,245,252,288,295,302,309,316,352,359,366,373,380}, //30
{40,47,54,61,104,111,118,125,168,175,182,189,232,239,246,253,296,303,310,317,360,367,374,381}, //24
{48,55,62,112,119,126,176,183,190,240,247,254,304,311,317,368,375,382}, //18
{56,63,120,127,184,191,248,255,312,319,376,383}, //12
{64,128,192,256,320,384}, //6
};

byte RING_SIZES[NUM_RINGS] = {6, 12, 18, 24, 30, 36, 42, 48, 42, 36, 30, 24, 18, 12, 6};

@pannovy
Copy link

pannovy commented Dec 19, 2023

byte RINGS[NUM_RINGS][RING_MAX_PIXELS] = {
{1, 65,129,193,257,321}, //6
{2, 9, 66, 73, 130, 137, 194, 201,258, 265, 322,329}, //12
{3, 10, 17, 67, 74, 81, 131, 138, 145, 195, 202, 209, 259, 266, 273, 323, 330, 337}, //18
{4, 11, 18, 25, 68, 75, 82, 89, 132, 139, 146, 153, 196, 203, 210, 217, 260, 267, 274, 281, 324, 331, 338, 345}, //24
{5, 12, 19, 26, 33, 69, 76, 83, 90, 97, 133, 140, 147, 154, 161, 197, 204, 211, 218, 225, 261,268, 275, 282, 289, 325, 332, 339, 346, 353}, //30
{6, 13, 20, 27, 34, 41, 70, 77,84, 91, 98, 105, 134, 141, 148, 155, 162, 169, 198, 205, 212, 219, 226, 233, 262, 269, 276, 283, 290, 297, 326, 333, 340, 347, 354, 361}, //36
{7, 14, 21, 28, 35, 42, 49, 71, 78, 85, 92, 99, 106, 113, 135, 142, 149, 156, 163, 170, 177, 199, 206, 213,220, 227, 234, 241, 263,270, 277, 284, 291, 298, 305,327, 334, 341, 348, 355, 362, 369}, //42
{8, 15, 22, 29, 36, 43, 50, 57, 72, 79, 86, 93, 100, 107, 114, 121, 136, 143, 150, 157, 164, 171, 178, 185, 200, 207, 214, 221, 228, 235, 242, 249,264,271,278,285,292,299,306,313,328,335,342,349,356,363,370,377}, // 48
{16, 23, 30, 37, 44, 51, 58, 80, 87, 94, 101, 108, 115, 122, 144, 151, 158,165,172,179,186,208,215,222,229,236,243,250,272,279,286,293,300,307,314,336,343,350,357,364,371,378}, //42
{24,31,38,45,52,59,88,95,102,109,116,123,152,159,166,173,180,187,216,223,230,237,244,251,280,287,294,301,308,315,344,351,358,365,372,379}, //36
{32,39,46,53,60,96,103,110,117,124,160,167,174,181,188,224,231,238,245,252,288,295,302,309,316,352,359,366,373,380}, //30
{40,47,54,61,104,111,118,125,168,175,182,189,232,239,246,253,296,303,310,317,360,367,374,381}, //24
{48,55,62,112,119,126,176,183,190,240,247,254,304,311,317,368,375,382}, //18
{56,63,120,127,184,191,248,255,312,319,376,383}, //12
{64,128,192,256,320,384}, //6
};

byte RING_SIZES[NUM_RINGS] = {6, 12, 18, 24, 30, 36, 42, 48, 42, 36, 30, 24, 18, 12, 6};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment