Hardware components | ||||||
| × | 3 | ||||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
![]() |
|
After tiding up my cupboard, I came across a project that I had made about three years ago. It was the "CUBE LED with Neopixel Matrix panels WS2812" by IeC and used a Arduino Nano to paint patterns on three 8x8 RGB WS2812B matrix panels controlled by a IR remote control. It seemed like a good project to upgrade. I decided to replace the Arduino Nano with a WeMOS module and to control the cube from an Internet Browser.
VideoThere is only one file to slice and print. You can either do this using the Slicer software that came with your 3D printer or take it to a commercial 3D print shop. I used a 0.2mm layer height and no supports.
BuildThe actual hardware is very simple. It basically consists of a 5V regulator module and a WeMOS D1 R1 module. I added two physical buttons to control the brightness, although in hindsight, I shouldn't have bothered as it is enough that you can control the brightness from the Browser interface over WiFi.
I did design a PCB. As the circuit is so simple, you can just use a piece of protoboard instead. Eagle files have been included should you want a commercially made PCB or if you want to make one yourself. I used the Toner method to make mine.
I used JST plugs and sockets but you can solder the wires directly to the board. The PCB is held in place with hot glue. The WS2812B panels are held in place using double-sided tape. I suggest you hold off fixing the panels in place until you have the software running. This is so you can get the correct orientation for each panel.
The software configures the cube as a Web server. After you have uploaded the software to the WeMos using the Arduino IDE, invoke the Arduino IDE console at 115200 baud.
When started, the WiFi Manager will try and connect to your WiFi network. If it can't be found or this is the first time you are running the software, it will create a WiFi hotspot called NeoCubeLampAP. Using your phone, connect to this WiFi hotspot. You will be presented with a menu. You can either select your network SID from the list or enter one manually. You will be asked for the password if it is required.
Once connected, the Ardunio IDE console should show you the IP address that your router has assigned. You can now use this IP address to connect to the cube. Open the browser on your phone and type in http://[assigned_ip_address] into the address bar. eg: http://192.168.10.107
I used a modified form of the WS2812FX library. This had all the code necessary for the Browser interface as well a number of built-in animations. It made the coding easier. The down-side is that it uses the Adafruit NeoPixel library. This library is a lot slower than the FastLED library which meant limiting some of the animations that I had originally planned.
/*
ESP8266 NeoPixel Cube Control
This program is based upon the web interface example program from the WS2812FX library
by Harm Aldick (www.aldick.org) which allows the mode, color, speed and brightness to
be controlled over the web.
This program also uses the WiFiManager library to allow WiFi credentials to be set
via a web interface.
This program also uses the ArduinoOTA library that allows the ESP8266 ModeMCU's program to be
updated wirelessly. That is, without having to attach a USB cable to reprogram it. This
is important because the NeoPixel Tree is fairly fragile.
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoOTA.h>
#include "NeoCubeFX.h"
#include "index.html.h"
#include "main.js.h"
#define LED_PIN 13 //D7
#define BTN_D1 5 //D1
#define BTN_D2 4 //D2
#define AP_NAME "NeoCubeLampAP" // Name of AP when configuring WiFi credentials
#define WIFI_TIMEOUT 30000 // checks WiFi every ...ms. Reset after this time, if WiFi cannot reconnect.
#define HTTP_PORT 80
#define DEFAULT_COLOR 0xFF5900
#define DEFAULT_BRIGHTNESS 100
#define DEFAULT_SPEED 200
#define DEFAULT_MODE FX_MODE_AUTO
#define BRIGHTNESS_STEP_BTN 2 // in/decrease brightness by this amount per button press
#define BRIGHTNESS_STEP 15 // in/decrease brightness by this amount per click
#define SPEED_STEP 10 // in/decrease brightness by this amount per click
unsigned long last_wifi_check_time = 0;
String modes = "";
/************************************************************************/
/* Object Instantiations */
/************************************************************************/
// Instantiate the NeoPixel driver
NeoCubeFX ws2812fx = NeoCubeFX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Instantiate a web server
// DELETED IN v2.4.0 => ESP8266WebServer server = ESP8266WebServer(HTTP_PORT);
ESP8266WebServer server (HTTP_PORT);
/************************************************************************/
/* Program Setup */
/************************************************************************/
void setup()
{
pinMode(BTN_D1, INPUT_PULLUP);
pinMode(BTN_D2, INPUT_PULLUP);
randomSeed(analogRead(A0));
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println();
Serial.println("Starting...");
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
// Setup WS2812FX library
Serial.println("WS2812FX setup");
modes.reserve(5000);
modes_setup();
ws2812fx.init();
ws2812fx.start();
Serial.println("Wifi setup");
// Local intialization. Once th WiFiManager's business is done, there is no need to keep it around
WiFiManager wifiManager;
// Reset saved settings for testing purposes
// Should be commented out for normal operation
// wifiManager.resetSettings();
// Fetches ssid and pass from eeprom and tries to connect
// If it does not connect it starts an access point with the specified name
// and goes into a blocking loop awaiting configuration
wifiManager.autoConnect(AP_NAME);
Serial.println("HTTP server setup");
server.on("/", srv_handle_index_html);
server.on("/main.js", srv_handle_main_js);
server.on("/modes", srv_handle_modes);
server.on("/set", srv_handle_set);
server.onNotFound(srv_handle_not_found);
server.begin();
Serial.println("HTTP server started.");
ArduinoOTA.begin();
Serial.println("ready!");
}
/************************************************************************/
/* Program Loop */
/************************************************************************/
void loop()
{
unsigned long now = millis();
delay(1);
server.handleClient();
ws2812fx.service();
ArduinoOTA.handle();
checkButtons();
if (now - last_wifi_check_time > WIFI_TIMEOUT) {
Serial.print("Checking WiFi... ");
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi connection lost. Resetting...");
ESP.reset();
} else {
Serial.println("OK");
}
last_wifi_check_time = now;
}
}
/*
Build <li> string for all modes.
*/
void modes_setup() {
modes = "";
for (uint8_t i = 0; i < ws2812fx.getModeCount(); i++) {
modes += "<li><a href='#' class='m' id='";
modes += i;
modes += "'>";
modes += ws2812fx.getModeName(i);
modes += "</a></li>";
}
}
/* #####################################################
# Webserver Functions
##################################################### */
void srv_handle_not_found() {
server.send(404, "text/plain", "File Not Found");
}
void srv_handle_index_html() {
server.send_P(200, "text/html", index_html);
}
void srv_handle_main_js() {
server.send_P(200, "application/javascript", main_js);
}
void srv_handle_modes() {
server.send(200, "text/plain", modes);
}
void srv_handle_set()
{
for (uint8_t i = 0; i < server.args(); i++) {
if (server.argName(i) == "c") {
String val = server.arg(i);
uint32_t tmp = (uint32_t) strtol(&val[0], NULL, 16);
if (tmp >= 0x000000 && tmp <= 0xFFFFFF) {
ws2812fx.setColor(tmp);
}
}
if (server.argName(i) == "m") {
String val = server.arg(i);
uint8_t tmp = (uint8_t) strtol(&val[0], NULL, 10);
ws2812fx.setMode(tmp % ws2812fx.getModeCount());
}
if (server.argName(i) == "b") {
if (server.arg(i)[0] == '-') {
ws2812fx.decreaseBrightness(BRIGHTNESS_STEP);
} else {
ws2812fx.increaseBrightness(BRIGHTNESS_STEP);
}
}
if (server.argName(i) == "s") {
if (server.arg(i)[0] == '-') {
ws2812fx.decreaseSpeed(SPEED_STEP);
} else {
ws2812fx.increaseSpeed(SPEED_STEP);
}
}
}
server.send(200, "text/plain", "OK");
}
//--------------------------------------------------------------------------
//Tests if buttons have been pressed
void checkButtons()
{
if (digitalRead(BTN_D1) == LOW)
{
//debounce switch
delay(10);
if (digitalRead(BTN_D1) == LOW)
{
ws2812fx.increaseBrightness(BRIGHTNESS_STEP_BTN);
delay(100);
}
}
else if (digitalRead(BTN_D2) == LOW)
{
//debounce switch
delay(10);
if (digitalRead(BTN_D2) == LOW)
{
ws2812fx.decreaseBrightness(BRIGHTNESS_STEP_BTN);
delay(100);
}
}
}
/*
NeoCubeFX.h - Library for WS2812 LED effects.
Harm Aldick - 2016
www.aldick.org
FEATURES
* A lot of blinken modes and counting
* NeoCubeFX can be used as drop-in replacement for Adafruit Neopixel Library
NOTES
* Uses the Adafruit Neopixel library. Get it here:
https://github.com/adafruit/Adafruit_NeoPixel
LICENSE
The MIT License (MIT)
Copyright (c) 2016 Harm Aldick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
CHANGELOG
2016-05-28 Initial beta release
2016-06-03 Code cleanup, minor improvements, new modes
2016-06-04 2 new fx, fixed setColor (now also resets _mode_color)
2017-02-02 added external trigger functionality (e.g. for sound-to-light)
2017-04-10 CAL - Added new modes: OFF, AUTO, LINES, LINES_RANDOM
2019-12-19 JLB - Customised for Hard Disk Lamp - Removed modes and updated existing ones
*/
#ifndef NeoCubeFX_h
#define NeoCubeFX_h
#include "Arduino.h"
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#define DEFAULT_BRIGHTNESS 50
#define DEFAULT_MODE 0
#define DEFAULT_SPEED 150
#define DEFAULT_COLOR 0xFF0000
#define SPEED_MIN 0
#define SPEED_MAX 255
#define BRIGHTNESS_MIN 0
#define BRIGHTNESS_MAX 255
#define AUTO_CHANGE_DELAY_SECS 30 // CAL
#define AUTO_CHANGE_DELAY_MS (AUTO_CHANGE_DELAY_SECS * 1000) // CAL
#define LED_COUNT 192
#define CUBE_ROW_COUNT 15
#define SIDE_COUNT 3
/*
#define MODE_COUNT 52
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
#define FX_MODE_BREATH 2
#define FX_MODE_COLOR_WIPE 3
#define FX_MODE_COLOR_WIPE_RANDOM 4 //0
#define FX_MODE_RANDOM_COLOR 5
#define FX_MODE_SINGLE_DYNAMIC 6
#define FX_MODE_MULTI_DYNAMIC 7 //0
#define FX_MODE_RAINBOW 8
#define FX_MODE_RAINBOW_CYCLE 9 //1
#define FX_MODE_SCAN 10
#define FX_MODE_DUAL_SCAN 11
#define FX_MODE_FADE 12
#define FX_MODE_THEATER_CHASE 13
#define FX_MODE_THEATER_CHASE_RAINBOW 14
#define FX_MODE_RUNNING_LIGHTS 15
#define FX_MODE_TWINKLE 16
#define FX_MODE_TWINKLE_RANDOM 17 //0
#define FX_MODE_TWINKLE_FADE 18
#define FX_MODE_TWINKLE_FADE_RANDOM 19 //1
#define FX_MODE_SPARKLE 20
#define FX_MODE_FLASH_SPARKLE 21
#define FX_MODE_HYPER_SPARKLE 22
#define FX_MODE_STROBE 23
#define FX_MODE_STROBE_RAINBOW 24
#define FX_MODE_MULTI_STROBE 25
#define FX_MODE_BLINK_RAINBOW 26
#define FX_MODE_CHASE_WHITE 27
#define FX_MODE_CHASE_COLOR 28
#define FX_MODE_CHASE_RANDOM 29
#define FX_MODE_CHASE_RAINBOW 30 //1
#define FX_MODE_CHASE_FLASH 31
#define FX_MODE_CHASE_FLASH_RANDOM 32
#define FX_MODE_CHASE_RAINBOW_WHITE 33 //1
#define FX_MODE_CHASE_BLACKOUT 34
#define FX_MODE_CHASE_BLACKOUT_RAINBOW 35
#define FX_MODE_COLOR_SWEEP_RANDOM 36 //1
#define FX_MODE_RUNNING_COLOR 37
#define FX_MODE_RUNNING_RED_BLUE 38 //1
#define FX_MODE_RUNNING_RANDOM 39 //0
#define FX_MODE_LARSON_SCANNER 40
#define FX_MODE_COMET 41
#define FX_MODE_FIREWORKS 42
#define FX_MODE_FIREWORKS_RANDOM 43 //1
#define FX_MODE_MERRY_CHRISTMAS 44 //1
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_FIRE_FLICKER_SOFT 46
#define FX_MODE_LINES 47
#define FX_MODE_LINES_RANDOM 48
#define FX_MODE_LINES_RAINBOW 49
#define FX_MODE_AUTO 50
#define FX_MODE_OFF 51
*/
#define MODE_COUNT 10
#define FX_MODE_COLOR_SWEEP 0
#define FX_MODE_WATER_DROP 1
#define FX_MODE_RAINBOW 2
#define FX_MODE_RAINBOW_CENTER 3
#define FX_MODE_COLOR_SWEEP_RANDOM 4
#define FX_MODE_LINES 5
#define FX_MODE_LINES_RAINBOW 6
#define FX_MODE_FIREWORKS_RANDOM 7
#define FX_MODE_AUTO 8
#define FX_MODE_OFF 9
/* LED locations
ROW
014 120
013 112 121
012 104 113 122
011 096 105 114 123
010 088 097 106 115 124
009 080 089 098 107 116 125
008 072 081 090 099 108 117 126
007 064 073 082 091 100 109 118 127
006 065 074 083 092 101 110 119
005 066 075 084 093 102 111
004 067 076 085 094 103
003 068 077 086 095
002 069 078 087
001 070 079
000 071
FRONT
TOP
LEFT RIGHT
000 007 135
001 006 015 134 143
002 005 014 023 133 142 151
003 004 013 022 031 132 141 150 159
004 003 012 021 030 039 131 140 149 158 167
005 002 011 020 029 038 047 130 139 148 157 166 175
006 001 010 019 028 037 046 055 129 138 147 156 165 174 183
007 000 009 018 027 036 045 054 063 128 137 146 155 164 173 182 191
008 008 017 026 035 044 053 062 136 145 154 163 172 181 190
009 016 025 034 043 052 061 144 153 162 171 180 189
010 024 033 042 051 060 152 161 170 179 188
011 032 041 050 059 160 169 178 187
012 040 049 058 168 177 186
013 048 057 176 185
014 056 184
*/
const uint8_t cirData[] PROGMEM = {
0, 7, 63, 56 | 0x80,
8, 1, 6, 15, 55, 62, 57, 48 | 0x80,
16, 9, 2, 5, 14, 23, 47, 54, 61, 58, 49, 40 | 0x80,
24, 17, 10, 3, 4, 13, 22, 31, 39, 46, 53, 60, 59, 50, 41, 32 | 0x80,
25, 18, 11, 12, 21, 30, 38, 45, 52, 51, 42, 33 | 0x80,
26, 19, 20, 29, 37, 44, 43, 34 | 0x80,
27, 28, 36, 35 | 0x80
};
const uint8_t outData[] PROGMEM = {
28, 36, 35, 27 | 0x80,
19, 20, 21, 29, 37, 45, 44, 43, 42, 34, 26, 18 | 0x80,
10, 11, 12, 13, 14, 22, 30, 38, 46, 54, 53, 52, 51, 50, 49, 41, 33, 25, 17, 9 | 0x80,
1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55, 63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0 | 0x80
};
const uint8_t inData[] PROGMEM = {
7, 15, 23, 31, 39, 47, 55, 63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6 | 0x80,
14, 22, 30, 38, 46, 54, 53, 52, 51, 50, 49, 41, 33, 25, 17, 9, 10, 11, 12, 13 | 0x80,
21, 29, 37, 45, 44, 43, 42, 34, 26, 18, 19, 20 | 0x80,
28, 36, 35, 27 | 0x80
};
const uint8_t colData[] PROGMEM = {
0 | 0x80,
1, 8 | 0x80,
2, 9, 16 | 0x80,
3, 10, 17, 24 | 0x80,
4, 11, 18, 25, 32 | 0x80,
5, 12, 19, 26, 33, 40 | 0x80,
6, 13, 20, 27, 34, 41, 48 | 0x80,
7, 14, 21, 28, 35, 42, 49, 56 | 0x80,
15, 22, 29, 36, 43, 50, 57 | 0x80,
23, 30, 37, 44, 51, 58 | 0x80,
31, 38, 45, 52, 59 | 0x80,
39, 46, 53, 60 | 0x80,
47, 54, 61 | 0x80,
55, 62 | 0x80,
63 | 0x80
};
const uint8_t rowData[] PROGMEM = {
7 | 0x80,
6, 15 | 0x80,
5, 14, 23 | 0x80,
4, 13, 22, 31 | 0x80,
3, 12, 21, 30, 39 | 0x80,
2, 11, 20, 29, 38, 47 | 0x80,
1, 10, 19, 28, 37, 46, 55 | 0x80,
0, 9, 18, 27, 36, 45, 54, 63 | 0x80,
8, 17, 26, 35, 44, 53, 62 | 0x80,
16, 25, 34, 43, 52, 61 | 0x80,
24, 33, 42, 51, 60 | 0x80,
32, 41, 50, 59 | 0x80,
40, 49, 58 | 0x80,
48, 57 | 0x80,
56 | 0x80
};
//EEPROM handling
#define EEPROM_ADDRESS 0
#define EEPROM_MAGIC 0x0BAD0DAD
typedef struct {
uint32_t magic;
uint8_t mode;
uint8_t speed;
uint8_t brightness;
uint32_t color;
} EEPROM_DATA;
class NeoCubeFX : public Adafruit_NeoPixel {
typedef void (NeoCubeFX::*mode_ptr)(void);
public:
/*
NeoCubeFX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) {
_mode[FX_MODE_STATIC] = &NeoCubeFX::mode_static;
_mode[FX_MODE_BLINK] = &NeoCubeFX::mode_blink;
_mode[FX_MODE_BREATH] = &NeoCubeFX::mode_breath;
_mode[FX_MODE_COLOR_WIPE] = &NeoCubeFX::mode_color_wipe;
_mode[FX_MODE_COLOR_WIPE_RANDOM] = &NeoCubeFX::mode_color_wipe_random;
_mode[FX_MODE_RANDOM_COLOR] = &NeoCubeFX::mode_random_color;
_mode[FX_MODE_SINGLE_DYNAMIC] = &NeoCubeFX::mode_single_dynamic;
_mode[FX_MODE_MULTI_DYNAMIC] = &NeoCubeFX::mode_multi_dynamic;
_mode[FX_MODE_RAINBOW] = &NeoCubeFX::mode_rainbow;
_mode[FX_MODE_RAINBOW_CYCLE] = &NeoCubeFX::mode_rainbow_cycle;
_mode[FX_MODE_SCAN] = &NeoCubeFX::mode_scan;
_mode[FX_MODE_DUAL_SCAN] = &NeoCubeFX::mode_dual_scan;
_mode[FX_MODE_FADE] = &NeoCubeFX::mode_fade;
_mode[FX_MODE_THEATER_CHASE] = &NeoCubeFX::mode_theater_chase;
_mode[FX_MODE_THEATER_CHASE_RAINBOW] = &NeoCubeFX::mode_theater_chase_rainbow;
_mode[FX_MODE_RUNNING_LIGHTS] = &NeoCubeFX::mode_running_lights;
_mode[FX_MODE_TWINKLE] = &NeoCubeFX::mode_twinkle;
_mode[FX_MODE_TWINKLE_RANDOM] = &NeoCubeFX::mode_twinkle_random;
_mode[FX_MODE_TWINKLE_FADE] = &NeoCubeFX::mode_twinkle_fade;
_mode[FX_MODE_TWINKLE_FADE_RANDOM] = &NeoCubeFX::mode_twinkle_fade_random;
_mode[FX_MODE_SPARKLE] = &NeoCubeFX::mode_sparkle;
_mode[FX_MODE_FLASH_SPARKLE] = &NeoCubeFX::mode_flash_sparkle;
_mode[FX_MODE_HYPER_SPARKLE] = &NeoCubeFX::mode_hyper_sparkle;
_mode[FX_MODE_STROBE] = &NeoCubeFX::mode_strobe;
_mode[FX_MODE_STROBE_RAINBOW] = &NeoCubeFX::mode_strobe_rainbow;
_mode[FX_MODE_MULTI_STROBE] = &NeoCubeFX::mode_multi_strobe;
_mode[FX_MODE_BLINK_RAINBOW] = &NeoCubeFX::mode_blink_rainbow;
_mode[FX_MODE_CHASE_WHITE] = &NeoCubeFX::mode_chase_white;
_mode[FX_MODE_CHASE_COLOR] = &NeoCubeFX::mode_chase_color;
_mode[FX_MODE_CHASE_RANDOM] = &NeoCubeFX::mode_chase_random;
_mode[FX_MODE_CHASE_RAINBOW] = &NeoCubeFX::mode_chase_rainbow;
_mode[FX_MODE_CHASE_FLASH] = &NeoCubeFX::mode_chase_flash;
_mode[FX_MODE_CHASE_FLASH_RANDOM] = &NeoCubeFX::mode_chase_flash_random;
_mode[FX_MODE_CHASE_RAINBOW_WHITE] = &NeoCubeFX::mode_chase_rainbow_white;
_mode[FX_MODE_CHASE_BLACKOUT] = &NeoCubeFX::mode_chase_blackout;
_mode[FX_MODE_CHASE_BLACKOUT_RAINBOW]= &NeoCubeFX::mode_chase_blackout_rainbow;
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &NeoCubeFX::mode_color_sweep_random;
_mode[FX_MODE_RUNNING_COLOR] = &NeoCubeFX::mode_running_color;
_mode[FX_MODE_RUNNING_RED_BLUE] = &NeoCubeFX::mode_running_red_blue;
_mode[FX_MODE_RUNNING_RANDOM] = &NeoCubeFX::mode_running_random;
_mode[FX_MODE_LARSON_SCANNER] = &NeoCubeFX::mode_larson_scanner;
_mode[FX_MODE_COMET] = &NeoCubeFX::mode_comet;
_mode[FX_MODE_FIREWORKS] = &NeoCubeFX::mode_fireworks;
_mode[FX_MODE_FIREWORKS_RANDOM] = &NeoCubeFX::mode_fireworks_random;
_mode[FX_MODE_MERRY_CHRISTMAS] = &NeoCubeFX::mode_merry_christmas;
_mode[FX_MODE_FIRE_FLICKER] = &NeoCubeFX::mode_fire_flicker;
_mode[FX_MODE_FIRE_FLICKER_SOFT] = &NeoCubeFX::mode_fire_flicker_soft;
_mode[FX_MODE_LINES] = &NeoCubeFX::mode_lines;
_mode[FX_MODE_LINES_RANDOM] = &NeoCubeFX::mode_lines_random;
_mode[FX_MODE_LINES_RAINBOW] = &NeoCubeFX::mode_lines_rainbow;
_mode[FX_MODE_AUTO] = &NeoCubeFX::mode_auto;
_mode[FX_MODE_OFF] = &NeoCubeFX::mode_off;
_name[FX_MODE_STATIC] = "Static";
_name[FX_MODE_BLINK] = "Blink";
_name[FX_MODE_BREATH] = "Breath";
_name[FX_MODE_COLOR_WIPE] = "Color Wipe";
_name[FX_MODE_COLOR_WIPE_RANDOM] = "Color Wipe Random";
_name[FX_MODE_RANDOM_COLOR] = "Random Color";
_name[FX_MODE_SINGLE_DYNAMIC] = "Single Dynamic";
_name[FX_MODE_MULTI_DYNAMIC] = "Multi Dynamic";
_name[FX_MODE_RAINBOW] = "Rainbow";
_name[FX_MODE_RAINBOW_CYCLE] = "Rainbow Cycle";
_name[FX_MODE_SCAN] = "Scan";
_name[FX_MODE_DUAL_SCAN] = "Dual Scan";
_name[FX_MODE_FADE] = "Fade";
_name[FX_MODE_THEATER_CHASE] = "Theater Chase";
_name[FX_MODE_THEATER_CHASE_RAINBOW] = "Theater Chase Rainbow";
_name[FX_MODE_RUNNING_LIGHTS] = "Running Lights";
_name[FX_MODE_TWINKLE] = "Twinkle";
_name[FX_MODE_TWINKLE_RANDOM] = "Twinkle Random";
_name[FX_MODE_TWINKLE_FADE] = "Twinkle Fade";
_name[FX_MODE_TWINKLE_FADE_RANDOM] = "Twinkle Fade Random";
_name[FX_MODE_SPARKLE] = "Sparkle";
_name[FX_MODE_FLASH_SPARKLE] = "Flash Sparkle";
_name[FX_MODE_HYPER_SPARKLE] = "Hyper Sparkle";
_name[FX_MODE_STROBE] = "Strobe";
_name[FX_MODE_STROBE_RAINBOW] = "Strobe Rainbow";
_name[FX_MODE_MULTI_STROBE] = "Multi Strobe";
_name[FX_MODE_BLINK_RAINBOW] = "Blink Rainbow";
_name[FX_MODE_CHASE_WHITE] = "Chase White";
_name[FX_MODE_CHASE_COLOR] = "Chase Color";
_name[FX_MODE_CHASE_RANDOM] = "Chase Random";
_name[FX_MODE_CHASE_RAINBOW] = "Chase Rainbow";
_name[FX_MODE_CHASE_FLASH] = "Chase Flash";
_name[FX_MODE_CHASE_FLASH_RANDOM] = "Chase Flash Random";
_name[FX_MODE_CHASE_RAINBOW_WHITE] = "Chase Rainbow White";
_name[FX_MODE_CHASE_BLACKOUT] = "Chase Blackout";
_name[FX_MODE_CHASE_BLACKOUT_RAINBOW]= "Chase Blackout Rainbow";
_name[FX_MODE_COLOR_SWEEP_RANDOM] = "Color Sweep Random";
_name[FX_MODE_RUNNING_COLOR] = "Running Color";
_name[FX_MODE_RUNNING_RED_BLUE] = "Running Red Blue";
_name[FX_MODE_RUNNING_RANDOM] = "Running Random";
_name[FX_MODE_LARSON_SCANNER] = "Larson Scanner";
_name[FX_MODE_COMET] = "Comet";
_name[FX_MODE_FIREWORKS] = "Fireworks";
_name[FX_MODE_FIREWORKS_RANDOM] = "Fireworks Random";
_name[FX_MODE_MERRY_CHRISTMAS] = "Merry Christmas";
_name[FX_MODE_FIRE_FLICKER] = "Fire Flicker";
_name[FX_MODE_FIRE_FLICKER_SOFT] = "Fire Flicker (soft)";
_name[FX_MODE_LINES] = "Lines";
_name[FX_MODE_LINES_RANDOM] = "Lines Random";
_name[FX_MODE_LINES_RAINBOW] = "Lines Rainbow";
_name[FX_MODE_AUTO] = "Auto";
_name[FX_MODE_OFF] = "Off";
*/
NeoCubeFX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) {
_mode[FX_MODE_COLOR_SWEEP] = &NeoCubeFX::mode_color_sweep;
_mode[FX_MODE_WATER_DROP] = &NeoCubeFX::mode_water_drop;
_mode[FX_MODE_RAINBOW] = &NeoCubeFX::mode_rainbow;
_mode[FX_MODE_RAINBOW_CENTER] = &NeoCubeFX::mode_rainbow_center;
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &NeoCubeFX::mode_color_sweep_random;
_mode[FX_MODE_LINES] = &NeoCubeFX::mode_lines;
_mode[FX_MODE_LINES_RAINBOW] = &NeoCubeFX::mode_lines_rainbow;
_mode[FX_MODE_FIREWORKS_RANDOM] = &NeoCubeFX::mode_fireworks_random;
_mode[FX_MODE_AUTO] = &NeoCubeFX::mode_auto;
_mode[FX_MODE_OFF] = &NeoCubeFX::mode_off;
_name[FX_MODE_COLOR_SWEEP] = "Color Sweep";
_name[FX_MODE_WATER_DROP] = "Water Drop";
_name[FX_MODE_RAINBOW] = "Rainbow";
_name[FX_MODE_RAINBOW_CENTER] = "Rainbow Center";
_name[FX_MODE_COLOR_SWEEP_RANDOM] = "Color Sweep Random";
_name[FX_MODE_LINES] = "Lines";
_name[FX_MODE_LINES_RAINBOW] = "Lines Rainbow";
_name[FX_MODE_FIREWORKS_RANDOM] = "Fireworks Random";
_name[FX_MODE_AUTO] = "Auto";
_name[FX_MODE_OFF] = "Off";
_mode_index = DEFAULT_MODE;
_speed = DEFAULT_SPEED;
_brightness = DEFAULT_BRIGHTNESS;
_running = false;
_led_count = n;
_mode_last_call_time = 0;
_mode_delay = 0;
_color = DEFAULT_COLOR;
_mode_color = DEFAULT_COLOR;
_counter_mode_call = 0;
_counter_mode_step = 0;
}
void
init(void),
service(void),
start(void),
stop(void),
setMode(uint8_t m),
setSpeed(uint8_t s),
increaseSpeed(uint8_t s),
decreaseSpeed(uint8_t s),
setColor(uint8_t r, uint8_t g, uint8_t b),
setColor(uint32_t c),
setBrightness(uint8_t b),
increaseBrightness(uint8_t s),
decreaseBrightness(uint8_t s),
trigger(void);
boolean
isRunning(void);
uint8_t
getMode(void),
getSpeed(void),
getBrightness(void),
getModeCount(void);
uint32_t
getColor(void);
const char*
getModeName(uint8_t m);
private:
void
set_panel_row_color(int led, uint32_t color),
set_panel_row_color(int led, uint8_t red, uint8_t grn, uint8_t blu ),
strip_off(void),
mode_static(void),
mode_blink(void),
mode_color_wipe(void),
mode_color_wipe_random(void),
mode_random_color(void),
mode_single_dynamic(void),
mode_multi_dynamic(void),
mode_breath(void),
mode_fade(void),
mode_scan(void),
mode_dual_scan(void),
mode_theater_chase(void),
mode_theater_chase_rainbow(void),
mode_rainbow(void),
mode_rainbow_center(void),
mode_running_lights(void),
mode_twinkle(void),
mode_twinkle_random(void),
mode_twinkle_fade(void),
mode_twinkle_fade_random(void),
mode_sparkle(void),
mode_flash_sparkle(void),
mode_hyper_sparkle(void),
mode_strobe(void),
mode_strobe_rainbow(void),
mode_multi_strobe(void),
mode_blink_rainbow(void),
mode_chase_white(void),
mode_chase_color(void),
mode_chase_random(void),
mode_chase_rainbow(void),
mode_chase_flash(void),
mode_chase_flash_random(void),
mode_chase_rainbow_white(void),
mode_chase_blackout(void),
mode_chase_blackout_rainbow(void),
mode_color_sweep_random(void),
mode_running_color(void),
mode_running_red_blue(void),
mode_running_random(void),
mode_larson_scanner(void),
mode_comet(void),
mode_fireworks(void),
mode_fireworks_random(void),
mode_merry_christmas(void),
mode_fire_flicker(void),
mode_fire_flicker_soft(void),
mode_fire_flicker_int(int),
mode_color_sweep(void),
mode_water_drop(void),
draw_lines(byte index, uint32_t color),
mode_lines(void),
mode_lines_random(void),
mode_lines_rainbow(void),
mode_auto(void),
mode_off(void),
initialiseTable(const uint8_t* a, uint8_t* t, int r),
displayTable(const uint8_t* a, uint8_t* t, int y, uint32_t l, uint32_t m, uint32_t r),
displayTable(const uint8_t* a, uint8_t* t, int m, int r, uint32_t c),
readEepromData(void),
writeEepromData(void);
boolean
_running,
_triggered,
_auto_mode; // CAL
uint8_t
get_random_wheel_index(uint8_t),
_mode_index,
_speed,
_brightness,
_color_index;
uint16_t
_led_count;
uint32_t
color_wheel(uint8_t),
_color,
_counter_mode_call,
_counter_mode_step,
_mode_color,
_mode_delay;
unsigned long
_mode_last_call_time,
_auto_mode_change_time; // CAL
const char*
_name[MODE_COUNT];
mode_ptr
_mode[MODE_COUNT];
uint8_t
cirs[7], //Used to store index of start of each circle in curData. Initialised in setup
outs[15], //Used to store index of start of each square in sqrData. Initialised in setup
ins[15], //Used to store index of start of each square in sqrData. Initialised in setup
cols[15], //Used to store index of start of each column in colData. Initialised in setup
rows[15]; //Used to store index of start of each row in rowData. Initialised in setup
EEPROM_DATA EepromData; //Current EEPROM settings
};
#endif
/*
NeoCubeFX.cpp - Library for WS2812 LED effects.
Harm Aldick - 2016
www.aldick.org
FEATURES
* A lot of blinken modes and counting
* NeoCubeFX can be used as drop-in replacement for Adafruit Neopixel Library
NOTES
* Uses the Adafruit Neopixel library. Get it here:
https://github.com/adafruit/Adafruit_NeoPixel
LICENSE
The MIT License (MIT)
Copyright (c) 2016 Harm Aldick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
CHANGELOG
2016-05-28 Initial beta release
2016-06-03 Code cleanup, minor improvements, new modes
2016-06-04 2 new fx, fixed setColor (now also resets _mode_color)
2017-02-02 added external trigger functionality (e.g. for sound-to-light)
2017-02-02 removed "blackout" on mode, speed or color-change
2017-04-10 CAL - Added new modes: OFF and AUTO
2021-01-08 JLB - Modified modes for NeoCube, added new modes, added EEPROM support
*/
#include "Arduino.h"
#include "NeoCubeFX.h"
#define CALL_MODE(n) (this->*_mode[n])();
void NeoCubeFX::init()
{
//Setup rows, cols and square arrays with starting indexes
initialiseTable(rowData, rows, 15);
initialiseTable(colData, cols, 15);
initialiseTable(inData, ins, 4);
initialiseTable(outData, outs, 4);
initialiseTable(cirData, cirs, 7);
readEepromData();
_mode_index = EepromData.mode;
_color = EepromData.color;
_speed = EepromData.speed;
_brightness = EepromData.brightness;
Adafruit_NeoPixel::begin();
NeoCubeFX::setBrightness(_brightness);
Adafruit_NeoPixel::show();
}
void NeoCubeFX::service() {
if(_running || _triggered) {
unsigned long now = millis();
// Begin new auto mode - CAL
// Is auto_mode enabled ?
if (_auto_mode && (now > _auto_mode_change_time)) {
// Calculate next mode change time
_auto_mode_change_time = now + AUTO_CHANGE_DELAY_MS;
// Turn off all LEDs
strip_off();
// Setup to call random mode with random color, speed and brightness
_counter_mode_call = 0;
_counter_mode_step = 0;
_mode_last_call_time = 0;
// Select a random mode
_mode_index = random(MODE_COUNT);
// Select a random color
_color = _mode_color = color_wheel(random(256));
// Select a random brightness
_brightness = random(20, 125);
Adafruit_NeoPixel::setBrightness(_brightness);
// Select a random speed
_speed = random(SPEED_MIN, SPEED_MAX);
}
// End new auto mode - CAL
if(now - _mode_last_call_time > _mode_delay || _triggered) {
CALL_MODE(_mode_index);
_counter_mode_call++;
_mode_last_call_time = now;
_triggered = false;
}
}
}
void NeoCubeFX::start() {
_counter_mode_call = 0;
_counter_mode_step = 0;
_mode_last_call_time = 0;
_running = true;
}
void NeoCubeFX::stop() {
_running = false;
strip_off();
}
void NeoCubeFX::trigger() {
_triggered = true;
}
void NeoCubeFX::setMode(uint8_t m) {
_auto_mode = false; // CAL
_auto_mode_change_time= 0; // CAL
_counter_mode_call = 0;
_counter_mode_step = 0;
_mode_last_call_time = 0;
_mode_index = constrain(m, 0, MODE_COUNT-1);
writeEepromData();
_mode_color = _color;
Adafruit_NeoPixel::setBrightness(_brightness);
//strip_off();
}
void NeoCubeFX::setSpeed(uint8_t s) {
_counter_mode_call = 0;
_counter_mode_step = 0;
_mode_last_call_time = 0;
_speed = constrain(s, SPEED_MIN, SPEED_MAX);
writeEepromData();
Serial.println("Speed = " + String(_speed));
//strip_off();
}
void NeoCubeFX::increaseSpeed(uint8_t s) {
s = constrain(_speed + s, SPEED_MIN, SPEED_MAX);
setSpeed(s);
}
void NeoCubeFX::decreaseSpeed(uint8_t s) {
s = constrain(_speed - s, SPEED_MIN, SPEED_MAX);
setSpeed(s);
}
void NeoCubeFX::setColor(uint8_t r, uint8_t g, uint8_t b) {
setColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
}
void NeoCubeFX::setColor(uint32_t c) {
_color = c;
writeEepromData();
_counter_mode_call = 0;
_counter_mode_step = 0;
_mode_last_call_time = 0;
_mode_color = _color;
Adafruit_NeoPixel::setBrightness(_brightness);
//strip_off();
}
void NeoCubeFX::setBrightness(uint8_t b) {
_brightness = constrain(b, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
writeEepromData();
Adafruit_NeoPixel::setBrightness(_brightness);
Adafruit_NeoPixel::show();
}
void NeoCubeFX::increaseBrightness(uint8_t s) {
s = constrain(_brightness + s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
setBrightness(s);
}
void NeoCubeFX::decreaseBrightness(uint8_t s) {
s = constrain(_brightness - s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
setBrightness(s);
}
boolean NeoCubeFX::isRunning() {
return _running;
}
uint8_t NeoCubeFX::getMode(void) {
return _mode_index;
}
uint8_t NeoCubeFX::getSpeed(void) {
return _speed;
}
uint8_t NeoCubeFX::getBrightness(void) {
return _brightness;
}
uint8_t NeoCubeFX::getModeCount(void) {
return MODE_COUNT;
}
uint32_t NeoCubeFX::getColor(void) {
return _color;
}
const char* NeoCubeFX::getModeName(uint8_t m) {
if(m < MODE_COUNT) {
return _name[m];
} else {
return "";
}
}
void NeoCubeFX::set_panel_row_color(int led, uint32_t color)
{
if (led < CUBE_ROW_COUNT)
{
displayTable(rowData, rows, led, color, color, color);
}
}
void NeoCubeFX::set_panel_row_color(int led, uint8_t red, uint8_t grn, uint8_t blu )
{
if (led < CUBE_ROW_COUNT)
{
uint32_t color = Color(red, grn, blu);
displayTable(rowData, rows, led, color, color, color);
}
}
/* #####################################################
#
# Color and Blinken Functions
#
##################################################### */
/*
* Turns everything off. Doh.
*/
void NeoCubeFX::strip_off() {
Adafruit_NeoPixel::clear();
Adafruit_NeoPixel::show();
}
/*
* Put a value 0 to 255 in to get a color value.
* The colours are a transition r -> g -> b -> back to r
* Inspired by the Adafruit examples.
*/
uint32_t NeoCubeFX::color_wheel(uint8_t pos)
{
pos = 255 - pos;
if(pos < 85)
{
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
}
else if(pos < 170)
{
pos -= 85;
return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
}
else
{
pos -= 170;
return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
}
}
/*
* Returns a new, random wheel index with a minimum distance of 42 from pos.
*/
uint8_t NeoCubeFX::get_random_wheel_index(uint8_t pos)
{
uint8_t r = 0;
uint8_t x = 0;
uint8_t y = 0;
uint8_t d = 0;
while(d < 42)
{
r = random(256);
x = abs(pos - r);
y = 255 - x;
d = min(x, y);
}
return r;
}
/*
* No blinking. Just plain old static light.
*/
void NeoCubeFX::mode_static(void)
{
for(uint16_t i=0; i < _led_count; i++)
{
Adafruit_NeoPixel::setPixelColor(i, _color);
}
Adafruit_NeoPixel::show();
_mode_delay = 50;
}
/*
* Normal blinking. 50% on/off time.
*/
void NeoCubeFX::mode_blink(void)
{
if(_counter_mode_call % 2 == 1)
{
for(uint16_t i=0; i < _led_count; i++)
{
Adafruit_NeoPixel::setPixelColor(i, _color);
}
Adafruit_NeoPixel::show();
}
else
{
strip_off();
}
_mode_delay = 100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Lights all LEDs after each other up. Then turns them in
* that order off. Repeat.
*/
void NeoCubeFX::mode_color_wipe(void)
{
if(_counter_mode_step < CUBE_ROW_COUNT)
{
set_panel_row_color(_counter_mode_step, _color);
}
else
{
set_panel_row_color(_counter_mode_step - _led_count, 0);
}
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % (_led_count * 2);
_mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count);
}
/*
* Turns all LEDs after each other to a random color.
* Then starts over with another color.
*/
void NeoCubeFX::mode_color_wipe_random(void)
{
if(_counter_mode_step == 0)
{
_mode_color = get_random_wheel_index(_mode_color);
}
set_panel_row_color(_counter_mode_step, color_wheel(_mode_color));
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* Lights all LEDs in one random color up. Then switches them
* to the next random color.
*/
void NeoCubeFX::mode_random_color(void)
{
_mode_color = get_random_wheel_index(_mode_color);
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++)
{
set_panel_row_color(i, color_wheel(_mode_color));
}
Adafruit_NeoPixel::show();
_mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Lights every LED in a random color. Changes one random LED after the other
* to another random color.
*/
void NeoCubeFX::mode_single_dynamic(void)
{
if(_counter_mode_call == 0)
{
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++)
{
set_panel_row_color(i, color_wheel(random(256)));
}
}
set_panel_row_color(random(CUBE_ROW_COUNT), color_wheel(random(256)));
Adafruit_NeoPixel::show();
_mode_delay = 10 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Lights every LED in a random color. Changes all LED at the same time
* to new random colors.
*/
void NeoCubeFX::mode_multi_dynamic(void) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, color_wheel(random(256)));
}
Adafruit_NeoPixel::show();
_mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Does the "standby-breathing" of well known i-Devices. Fixed Speed.
* Use mode "fade" if you like to have something similar with a different speed.
*/
void NeoCubeFX::mode_breath(void) {
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // step
uint16_t breath_delay_steps[] = { 7, 9, 13, 15, 16, 17, 18, 930, 19, 18, 15, 13, 9, 7, 4, 5, 10 }; // magic numbers for breathing LED
uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16, 15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers!
if(_counter_mode_call == 0) {
_mode_color = breath_brightness_steps[0] + 1;
}
uint8_t breath_brightness = _mode_color; // we use _mode_color to store the brightness
if(_counter_mode_step < 8) {
breath_brightness--;
} else {
breath_brightness++;
}
// update index of current delay when target brightness is reached, start over after the last step
if(breath_brightness == breath_brightness_steps[_counter_mode_step]) {
_counter_mode_step = (_counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t));
}
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color); // set all LEDs to selected color
}
int b = map(breath_brightness, 0, 255, 0, _brightness); // keep brightness below brightness set by user
Adafruit_NeoPixel::setBrightness(b); // set new brightness to leds
Adafruit_NeoPixel::show();
_mode_color = breath_brightness; // we use _mode_color to store the brightness
_mode_delay = breath_delay_steps[_counter_mode_step];
}
/*
* Fades the LEDs on and (almost) off again.
*/
void NeoCubeFX::mode_fade(void) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color);
}
int b = _counter_mode_step - 127;
b = 255 - (abs(b) * 2);
b = map(b, 0, 255, min(25, (int)_brightness), _brightness);
Adafruit_NeoPixel::setBrightness(b);
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % 256;
_mode_delay = 5 + ((15 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Runs a single pixel back and forth.
*/
void NeoCubeFX::mode_scan(void) {
if(_counter_mode_step > (CUBE_ROW_COUNT*2) - 2) {
_counter_mode_step = 0;
}
_counter_mode_step++;
int i = _counter_mode_step - (CUBE_ROW_COUNT - 1);
i = abs(i);
Adafruit_NeoPixel::clear();
set_panel_row_color(abs(i), _color);
Adafruit_NeoPixel::show();
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* Runs two pixel back and forth in opposite directions.
*/
void NeoCubeFX::mode_dual_scan(void) {
if(_counter_mode_step > (CUBE_ROW_COUNT*2) - 2) {
_counter_mode_step = 0;
}
_counter_mode_step++;
int i = _counter_mode_step - (CUBE_ROW_COUNT - 1);
i = abs(i);
Adafruit_NeoPixel::clear();
set_panel_row_color(i, _color);
set_panel_row_color(CUBE_ROW_COUNT - (i+1), _color);
Adafruit_NeoPixel::show();
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* Cycles all LEDs at once through a rainbow.
*/
void NeoCubeFX::mode_rainbow(void) {
uint32_t color = color_wheel(_counter_mode_step);
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, color);
}
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % 256;
_mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Cycles a rainbow over the entire string of LEDs.
*/
void NeoCubeFX::mode_rainbow_center(void)
{
for(uint16_t i=0; i < ((CUBE_ROW_COUNT + 1) / 2); i++)
{
uint32_t c = color_wheel(((i * 256 / CUBE_ROW_COUNT) + _counter_mode_step) % 256);
set_panel_row_color(i, c);
if ((14 - i) != i)
{
set_panel_row_color(14 - i, c);
}
}
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % 256;
_mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Theatre-style crawling lights.
* Inspired by the Adafruit examples.
*/
void NeoCubeFX::mode_theater_chase(void) {
uint8_t j = _counter_mode_call % 6;
if(j % 2 == 0) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
set_panel_row_color(i+(j/2), _color);
}
Adafruit_NeoPixel::show();
_mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
} else {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
set_panel_row_color(i+(j/2), 0);
}
_mode_delay = 1;
}
}
/*
* Theatre-style crawling lights with rainbow effect.
* Inspired by the Adafruit examples.
*/
void NeoCubeFX::mode_theater_chase_rainbow(void) {
uint8_t j = _counter_mode_call % 6;
if(j % 2 == 0) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
set_panel_row_color(i+(j/2), color_wheel((i+_counter_mode_step) % 256));
}
Adafruit_NeoPixel::show();
_mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
} else {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i=i+3) {
set_panel_row_color(i+(j/2), 0);
}
_mode_delay = 1;
}
_counter_mode_step = (_counter_mode_step + 1) % 256;
}
/*
* Running lights effect with smooth sine transition.
*/
void NeoCubeFX::mode_running_lights(void) {
uint8_t r = ((_color >> 16) & 0xFF);
uint8_t g = ((_color >> 8) & 0xFF);
uint8_t b = (_color & 0xFF);
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
int s = (sin(i+_counter_mode_call) * 127) + 128;
set_panel_row_color(i, (((uint32_t)(r * s)) / 255), (((uint32_t)(g * s)) / 255), (((uint32_t)(b * s)) / 255));
}
Adafruit_NeoPixel::show();
_mode_delay = 35 + ((350 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Blink several LEDs on, reset, repeat.
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void NeoCubeFX::mode_twinkle(void) {
if(_counter_mode_step == 0) {
strip_off();
uint16_t min_leds = max(1, _led_count/5); // make sure, at least one LED is on
uint16_t max_leds = max(1, _led_count/2); // make sure, at least one LED is on
_counter_mode_step = random(min_leds, max_leds);
}
Adafruit_NeoPixel::setPixelColor(random(_led_count), _mode_color);
Adafruit_NeoPixel::show();
_counter_mode_step--;
_mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Blink several LEDs in random colors on, reset, repeat.
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void NeoCubeFX::mode_twinkle_random(void) {
_mode_color = color_wheel(random(256));
mode_twinkle();
}
/*
* Blink several LEDs on, fading out.
*/
void NeoCubeFX::mode_twinkle_fade(void)
{
for(uint16_t i=0; i < _led_count; i++)
{
uint32_t px_rgb = Adafruit_NeoPixel::getPixelColor(i);
byte px_r = (px_rgb & 0x00FF0000) >> 16;
byte px_g = (px_rgb & 0x0000FF00) >> 8;
byte px_b = (px_rgb & 0x000000FF) >> 0;
// fade out (divide by 2)
px_r = px_r >> 1;
px_g = px_g >> 1;
px_b = px_b >> 1;
Adafruit_NeoPixel::setPixelColor(i, px_r, px_g, px_b);
}
if(random(3) == 0)
{
Adafruit_NeoPixel::setPixelColor(random(_led_count), _mode_color);
}
Adafruit_NeoPixel::show();
_mode_delay = 100 + ((100 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Blink several LEDs in random colors on, fading out.
*/
void NeoCubeFX::mode_twinkle_fade_random(void)
{
_mode_color = color_wheel(random(256));
mode_twinkle_fade();
}
/*
* Blinks one LED at a time.
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void NeoCubeFX::mode_sparkle(void) {
Adafruit_NeoPixel::clear();
Adafruit_NeoPixel::setPixelColor(random(_led_count),_color);
Adafruit_NeoPixel::show();
_mode_delay = 10 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* Lights all LEDs in the _color. Flashes single white pixels randomly.
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void NeoCubeFX::mode_flash_sparkle(void) {
for(uint16_t i=0; i < _led_count; i++) {
Adafruit_NeoPixel::setPixelColor(i, _color);
}
if(random(10) == 7) {
Adafruit_NeoPixel::setPixelColor(random(_led_count), 255, 255, 255);
_mode_delay = 20;
} else {
_mode_delay = 20 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
Adafruit_NeoPixel::show();
}
/*
* Like flash sparkle. With more flash.
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
void NeoCubeFX::mode_hyper_sparkle(void) {
for(uint16_t i=0; i < _led_count; i++) {
Adafruit_NeoPixel::setPixelColor(i, _color);
}
if(random(10) < 4) {
for(uint16_t i=0; i < max(1, _led_count/3); i++) {
Adafruit_NeoPixel::setPixelColor(random(_led_count), 255, 255, 255);
}
_mode_delay = 20;
} else {
_mode_delay = 15 + ((120 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
Adafruit_NeoPixel::show();
}
/*
* Classic Strobe effect.
*/
void NeoCubeFX::mode_strobe(void) {
if(_counter_mode_call % 2 == 0) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color);
}
_mode_delay = 20;
} else {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, 0);
}
_mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
Adafruit_NeoPixel::show();
}
/*
* Strobe effect with different strobe count and pause, controled by _speed.
*/
void NeoCubeFX::mode_multi_strobe(void) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, 0);
}
if(_counter_mode_step < (2 * ((_speed / 10) + 1))) {
if(_counter_mode_step % 2 == 0) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color);
}
_mode_delay = 20;
} else {
_mode_delay = 50;
}
} else {
_mode_delay = 100 + ((9 - (_speed % 10)) * 125);
}
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % ((2 * ((_speed / 10) + 1)) + 1);
}
/*
* Classic Strobe effect. Cycling through the rainbow.
*/
void NeoCubeFX::mode_strobe_rainbow(void) {
if(_counter_mode_call % 2 == 0) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, color_wheel(_counter_mode_call % 256));
}
_mode_delay = 20;
} else {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, 0);
}
_mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
Adafruit_NeoPixel::show();
}
/*
* Classic Blink effect. Cycling through the rainbow.
*/
void NeoCubeFX::mode_blink_rainbow(void) {
if(_counter_mode_call % 2 == 1) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, color_wheel(_counter_mode_call % 256));
}
Adafruit_NeoPixel::show();
} else {
strip_off();
}
_mode_delay = 100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX);
}
/*
* _color running on white.
*/
void NeoCubeFX::mode_chase_white(void) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, 255, 255, 255);
}
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
set_panel_row_color(n, _color);
set_panel_row_color(m, _color);
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* White running on _color.
*/
void NeoCubeFX::mode_chase_color(void) {
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color);
}
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
set_panel_row_color(n, 255, 255, 255);
set_panel_row_color(m, 255, 255, 255);
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* White running followed by random color.
*/
void NeoCubeFX::mode_chase_random(void) {
if(_counter_mode_step == 0) {
set_panel_row_color(CUBE_ROW_COUNT-1, color_wheel(_mode_color));
_mode_color = get_random_wheel_index(_mode_color);
}
for(uint16_t i=0; i < _counter_mode_step; i++) {
set_panel_row_color(i, color_wheel(_mode_color));
}
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
set_panel_row_color(n, 255, 255, 255);
set_panel_row_color(m, 255, 255, 255);
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* White running on rainbow.
*/
void NeoCubeFX::mode_chase_rainbow(void)
{
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++)
{
set_panel_row_color(i, color_wheel(((i * 256 / CUBE_ROW_COUNT) + (_counter_mode_call % 256)) % 256));
}
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
set_panel_row_color(n, 255, 255, 255);
set_panel_row_color(m, 255, 255, 255);
Adafruit_NeoPixel::show();
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
/*
* White flashes running on _color.
*/
void NeoCubeFX::mode_chase_flash(void) {
const static uint8_t flash_count = 4;
uint8_t flash_step = _counter_mode_call % ((flash_count * 2) + 1);
for(uint16_t i=0; i < CUBE_ROW_COUNT; i++) {
set_panel_row_color(i, _color);
}
if(flash_step < (flash_count * 2)) {
if(flash_step % 2 == 0) {
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
set_panel_row_color(n, 255, 255, 255);
set_panel_row_color(m, 255, 255, 255);
_mode_delay = 20;
} else {
_mode_delay = 30;
}
} else {
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
}
Adafruit_NeoPixel::show();
}
/*
* White flashes running, followed by random color.
*/
void NeoCubeFX::mode_chase_flash_random(void) {
const static uint8_t flash_count = 4;
uint8_t flash_step = _counter_mode_call % ((flash_count * 2) + 1);
for(uint16_t i=0; i < _counter_mode_step; i++) {
set_panel_row_color(i, color_wheel(_mode_color));
}
if(flash_step < (flash_count * 2)) {
uint16_t n = _counter_mode_step;
uint16_t m = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
if(flash_step % 2 == 0) {
set_panel_row_color(n, 255, 255, 255);
set_panel_row_color(m, 255, 255, 255);
_mode_delay = 20;
} else {
set_panel_row_color(n, color_wheel(_mode_color));
set_panel_row_color(m, 0, 0, 0);
_mode_delay = 30;
}
} else {
_counter_mode_step = (_counter_mode_step + 1) % CUBE_ROW_COUNT;
_mode_delay = 1 + ((10 * (uint32_t)(SPEED_MAX - _speed)) / CUBE_ROW_COUNT);
if(_counter_mode_step == 0) {
_mode_color = get_random_wheel_index(_mode_color);
}
}
Adafruit_NeoPixel::show();
}
/*
* Rainbow running on white.
...
This file has been truncated, please download it to see its full contents.
const char index_html[] PROGMEM = R"=====(
<!DOCTYPE html>
<html lang='en'>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>Hard Disk Lamp Control</title>
<script type='text/javascript' src='main.js'></script>
<style>
* {
font-family:sans-serif;
margin:0;
padding:0;
}
body {
width:100%;
max-width:675px;
background-color:#202020;
}
h1 {
margin:25px 0 25px 0;
color:#4545FF;
text-align:center;
font-size: 16pt;
}
#colorbar {
float:left;
display:none;
}
#adjust {
width:30%;
display:inline-block;
float:right;
}
#controls {
width:65%;
display:inline-block;
padding-left:5px;
}
p {
align:center;
font-size: 8px;
font-family: Tahoma;
color: white;
}
ul {
text-align:center;
}
ul#mode li {
display:block;
}
ul.brightness li, ul.speed li {
display:block;
_width:30%;
}
ul li a {
display:block;
margin:3px;
padding:10px 5px;
border:2px solid #454545;
border-radius:5px;
color:#4545FF;
font-weight:bold;
text-decoration:none;
}
ul li a.active {
border:2px solid #909090;
color:#909090;
}
</style>
</head>
<body>
<h1>NeoCube Lamp Control</h1>
<canvas id='colorbar' width='75' height='1080'></canvas>
<div id='adjust'>
<p class='brightness'>Brightness</p>
<ul class='brightness'>
<li><a href='#' class='b' id='+'>☀</a></li>
</ul>
<ul class='brightness' id='brightness'>
<li><a href='#' class='b' id='-'>☼</a></li>
</ul>
<p> </p>
<p class='speed'>Speed</p>
<ul class='speed' id='speed'>
<li><a href='#' class='s' id='+'>+</a></li>
</ul>
<ul class='speed' id='speed'>
<li><a href='#' class='s' id='-'>−</a></li>
</ul>
</div>
<div id='controls'>
<ul id='mode'></ul>
</div>
</body>
</html>
)=====";
const char main_js[] PROGMEM = R"=====(
window.addEventListener('load', setup);
window.addEventListener('resize', drawColorbar);
function handle_M_B_S(e) {
e.preventDefault();
var name = e.target.className;
var val = e.target.id;
if(e.target.className.indexOf('m') > -1) {
elems = document.querySelectorAll('#mode li a');
[].forEach.call(elems, function(el) {
el.classList.remove('active');
name = e.target.className;
});
e.target.classList.add('active');
}
submitVal(name, val);
}
function submitVal(name, val) {
var xhttp = new XMLHttpRequest();
xhttp.open('GET', 'set?' + name + '=' + val, true);
xhttp.send();
}
function compToHex(c) {
hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
}
function getMousePos(can, evt) {
r = can.getBoundingClientRect();
return {
x: evt.clientX - r.left,
y: evt.clientY - r.top
};
}
function Touch(e) {
e.preventDefault();
pos = {
x: Math.round(e.targetTouches[0].pageX),
y: Math.round(e.targetTouches[0].pageY)
};
rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
drawColorbar(rgb);
submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}
function Click(e) {
pos = getMousePos(can, e);
rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
drawColorbar(rgb);
submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}
// Thanks to the backup at http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
function rgbToHsl(r, g, b){
r = r / 255;
g = g / 255;
b = b / 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min) {
h = s = 0;
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h = h / 6;
}
return [h, s, l];
}
function drawColorbar(rgb = [0, 0, 0]) {
can = document.getElementById('colorbar');
ctx = can.getContext('2d');
can.width = document.body.clientWidth * 0.25;
var h = can.height / 360;
var hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]);
for(var i=0; i<=360; i++) {
ctx.fillStyle = 'hsl('+i+', 100%, 50%)';
ctx.fillRect(0, i * h, can.width/2, h);
ctx.fillStyle = 'hsl(' + hsl[0] * 360 + ', 100%, ' + i * (100/360) + '%)';
ctx.fillRect(can.width/2, i * h, can.width/2, h);
}
}
function setup(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById('mode').innerHTML = xhttp.responseText;
elems = document.querySelectorAll('ul li a'); // adds listener also to existing s and b buttons
[].forEach.call(elems, function(el) {
el.addEventListener('touchstart', handle_M_B_S, false);
el.addEventListener('click', handle_M_B_S, false);
});
}
};
xhttp.open('GET', 'modes', true);
xhttp.send();
var can = document.getElementById('colorbar');
var ctx = can.getContext('2d');
drawColorbar();
can.addEventListener('touchstart', Touch, false);
can.addEventListener('click', Click, false);
}
)=====";
Comments
Please log in or sign up to comment.