3 Commits

Author SHA1 Message Date
bde51bc675 Added more sunrise patterns. 2017-07-19 17:20:51 -05:00
72cc218fb8 Work on the sunrise clock. 2017-07-13 08:46:28 -05:00
02e5ae137f Improved instructions. 2017-05-06 11:33:25 -05:00
4 changed files with 352 additions and 44 deletions

View File

@ -30,17 +30,27 @@ Patterns are requested by the app from the ESP8266, so as new patterns are added
The web app is stored in SPIFFS (on-board flash memory).
The web app is a single page app with separate files for js and css, using [jQuery](https://jquery.com) and [Bootstrap](http://getbootstrap.com). It has buttons for On/Off, a slider for brightness, a pattern selector, and a color picker (using [jQuery MiniColors](http://labs.abeautifulsite.net/jquery-minicolors)). Event handlers for the controls are wired up, so you don't have to click a 'Send' button after making changes. The brightness slider and the color picker use a delayed event handler, to prevent from flooding the ESP8266 web server with too many requests too quickly.
The only drawback to SPIFFS that I've found so far is uploading the files is extremely slow, requiring several minutes, regardless of how large the files are. It's so slow that I've been just developing the web app and debugging locally on my desktop (with a hard-coded IP for the ESP8266), before uploading to SPIFFS and testing on the ESP8266.
The web app is a single page app that uses [jQuery](https://jquery.com) and [Bootstrap](http://getbootstrap.com). It has buttons for On/Off, a slider for brightness, a pattern selector, and a color picker (using [jQuery MiniColors](http://labs.abeautifulsite.net/jquery-minicolors)). Event handlers for the controls are wired up, so you don't have to click a 'Send' button after making changes. The brightness slider and the color picker use a delayed event handler, to prevent from flooding the ESP8266 web server with too many requests too quickly.
The only drawback to SPIFFS that I've found so far is uploading the files can be extremely slow, requiring several minutes, sometimes regardless of how large the files are. It can be so slow that I've been just developing the web app and debugging locally on my desktop (with a hard-coded IP for the ESP8266), before uploading to SPIFFS and testing on the ESP8266.
Installing
-----------
The app is installed via the Arduino IDE which can be [downloaded here](https://www.arduino.cc/en/main/software). The ESP8266 boards will need to be added to the Arduino IDE which is achieved as follows. Click File > Preferences and copy and paste the URL "http://arduino.esp8266.com/stable/package_esp8266com_index.json" into the Additional Boards Manager URLs field. Click OK. Click Tools > Boards: ... > Boards Manager. Find and click on ESP8266 (using the Search function may expedite this). Click on Install. After installation, click on Close and then select your ESP8266 board from the Tools > Board: ... menu.
The app depends on the following libraries. They must either be downloaded from GitHub and placed in the Arduino 'libraries' folder, or installed as [described here](https://www.arduino.cc/en/Guide/Libraries) by using the Arduino library manager.
* [FastLED](https://github.com/FastLED/FastLED)
* [IRremoteESP8266](https://github.com/markszabo/IRremoteESP8266)
* [Arduino WebSockets](https://github.com/Links2004/arduinoWebSockets)
Download the app code from GitHub using the green Clone or Download button from [the GitHub project main page](https://github.com/jasoncoon/esp8266-fastled-webserver) and click Download ZIP. Decompress the ZIP file in your Arduino sketch folder.
The web app needs to be uploaded to the ESP8266's SPIFFS. You can do this within the Arduino IDE after installing the [Arduino ESP8266FS tool](https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md#uploading-files-to-file-system).
With ESP8266FS installed run the sketch and then upload the web app using `ESP8266 Sketch Data Upload` command in the Arduino Tools menu.
With ESP8266FS installed upload the web app using `ESP8266 Sketch Data Upload` command in the Arduino Tools menu.
Then enter your wi-fi network SSID and password in the .ino file, and upload the sketch using the Upload button.
Compression
-----------

65
Text.h Normal file
View File

@ -0,0 +1,65 @@
bool mask[NUM_LEDS];
const byte digits4x8[8 * 10] = {
0x06, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, // 0
0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e, // 1
0x06, 0x09, 0x08, 0x08, 0x04, 0x02, 0x01, 0x0f, // 2
0x06, 0x09, 0x08, 0x04, 0x08, 0x08, 0x09, 0x06, // 3
0x04, 0x05, 0x05, 0x05, 0x0f, 0x04, 0x04, 0x04, // 4
0x0f, 0x01, 0x01, 0x07, 0x08, 0x08, 0x09, 0x06, // 5
0x06, 0x09, 0x01, 0x07, 0x09, 0x09, 0x09, 0x06, // 6
0x0f, 0x08, 0x08, 0x04, 0x02, 0x01, 0x01, 0x01, // 7
0x06, 0x09, 0x09, 0x06, 0x09, 0x09, 0x09, 0x06, // 8
0x06, 0x09, 0x09, 0x09, 0x0e, 0x08, 0x09, 0x06, // 9
};
// preceed with a call to fillMask(false);
// set mask to true where digit should light
void digit(byte start, byte d) {
byte row, col;
for (col = 0; col < 4; col++) {
for (row = 0; row < 8; row++) {
if ((digits4x8[d * 8 + row] >> col) & 1) {
togglePixelMask(row, col + start, true);
}
else {
}
}
}
}
void displayNum(uint32_t n) {
digit( 0, (n / 10000) % 10);
digit( 5, (n / 1000) % 10);
digit(10, (n / 100) % 10);
digit(15, (n / 10) % 10);
digit(20, (n / 1) % 10);
}
void displayTime(uint32_t tm) {
uint8_t hh = (tm / 3600) % 12;
uint8_t mm = (tm / 60) % 60;
uint8_t ss = (tm) % 60;
if (hh > 9) {
digit( 1, hh / 10);
}
digit( 6, hh % 10);
setPixelMask(2, 11, true);
setPixelMask(3, 11, true);
setPixelMask(5, 11, true);
setPixelMask(6, 11, true);
digit(13, mm / 10);
digit(18, mm % 10);
if (hh > 9) {
digit( 1 + 24, hh / 10);
}
digit( 6 + 24, hh % 10);
setPixelMask(2, 11 + 24, true);
setPixelMask(3, 11 + 24, true);
setPixelMask(5, 11 + 24, true);
setPixelMask(6, 11 + 24, true);
digit(13 + 24, mm / 10);
digit(18 + 24, mm % 10);
}

View File

@ -1,20 +1,20 @@
/*
* ESP8266 + FastLED + IR Remote: https://github.com/jasoncoon/esp8266-fastled-webserver
* Copyright (C) 2015-2016 Jason Coon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
ESP8266 + FastLED + IR Remote: https://github.com/jasoncoon/esp8266-fastled-webserver
Copyright (C) 2015-2016 Jason Coon
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <FastLED.h>
FASTLED_USING_NAMESPACE
@ -39,7 +39,7 @@ extern "C" {
#define HOSTNAME "ESP8266-" ///< Hostname. The setup function adds the Chip ID at the end.
#define RECV_PIN D4
#define RECV_PIN 1
IRrecv irReceiver(RECV_PIN);
#include "Commands.h"
@ -59,12 +59,15 @@ ESP8266HTTPUpdateServer httpUpdateServer;
#include "FSBrowser.h"
#define DATA_PIN D8
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS 24
#define DATA_PIN MOSI
#define CLK_PIN SCK
#define LED_TYPE APA102
#define COLOR_ORDER BGR
#define MatrixWidth 8 * 3
#define MatrixHeight 8
#define NUM_LEDS MatrixWidth * MatrixHeight
#define MILLI_AMPS 2000 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA)
#define MILLI_AMPS 500 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA)
#define FRAMES_PER_SECOND 120 // here you can control the speed. With the Access Point / Web Server the animations run a bit slower.
CRGB leds[NUM_LEDS];
@ -106,6 +109,8 @@ CRGBPalette16 IceColors_p = CRGBPalette16(CRGB::Black, CRGB::Blue, CRGB::Aqua, C
uint8_t currentPatternIndex = 0; // Index number of which pattern is current
uint8_t autoplay = 0;
bool resetPattern = true;
uint8_t autoplayDuration = 10;
unsigned long autoPlayTimeout = 0;
@ -123,6 +128,30 @@ void dimAll(byte value)
}
}
const bool MatrixSerpentineLayout = true;
uint16_t XY(uint8_t x, uint8_t y)
{
uint16_t i;
if ( MatrixSerpentineLayout == false) {
i = (y * MatrixWidth) + x;
}
if ( MatrixSerpentineLayout == true) {
if ( x & 0x01) {
// Odd columns run backwards
uint8_t reverseY = (MatrixHeight - 1) - y;
i = (x * MatrixHeight) + reverseY;
} else {
// Even rows run forwards
i = (x * MatrixHeight) + y;
}
}
return i;
}
typedef void (*Pattern)();
typedef Pattern PatternList[];
typedef struct {
@ -133,10 +162,17 @@ typedef PatternAndName PatternAndNameList[];
#include "Twinkles.h"
#include "TwinkleFOX.h"
#include "Sunrise.h"
// List of patterns to cycle through. Each is defined as a separate function below.
PatternAndNameList patterns = {
{ sunriseStatic, "Sunrise Static" },
{ sunriseFlicker, "Sunrise Flicker" },
{ sunriseWavesVertical, "Sunrise Waves Vertical" },
{ sunriseWavesHorizontal, "Sunrise Waves Horizontal" },
{ sunriseWavesDiagonal, "Sunrise Waves Diagonal" },
{ sunriseWavesRotating, "Sunrise Waves Rotating" },
{ pride, "Pride" },
{ colorWaves, "Color Waves" },
@ -179,8 +215,8 @@ const uint8_t patternCount = ARRAY_SIZE(patterns);
typedef struct {
CRGBPalette16 palette;
String name;
} PaletteAndName;
String name;
} PaletteAndName;
typedef PaletteAndName PaletteAndNameList[];
const CRGBPalette16 palettes[] = {
@ -202,10 +238,10 @@ const String paletteNames[paletteCount] = {
"Cloud",
"Lava",
"Ocean",
"Forest",
"Forest",
"Party",
"Heat",
};
"Heat",
};
#include "Fields.h"
@ -214,8 +250,8 @@ void setup() {
delay(100);
Serial.setDebugOutput(true);
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); // for WS2812 (Neopixel)
//FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS); // for APA102 (Dotstar)
// FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); // for WS2812 (Neopixel)
FastLED.addLeds<LED_TYPE, DATA_PIN, CLK_PIN, COLOR_ORDER>(leds, NUM_LEDS); // for APA102 (Dotstar)
FastLED.setDither(false);
FastLED.setCorrection(TypicalLEDStrip);
FastLED.setBrightness(brightness);
@ -302,7 +338,7 @@ void setup() {
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, password);
}
}
}
httpUpdateServer.setup(&webServer);
@ -354,7 +390,7 @@ void setup() {
webServer.on("/twinkleSpeed", HTTP_POST, []() {
String value = webServer.arg("value");
twinkleSpeed = value.toInt();
if(twinkleSpeed < 0) twinkleSpeed = 0;
if (twinkleSpeed < 0) twinkleSpeed = 0;
else if (twinkleSpeed > 8) twinkleSpeed = 8;
broadcastInt("twinkleSpeed", twinkleSpeed);
sendInt(twinkleSpeed);
@ -363,7 +399,7 @@ void setup() {
webServer.on("/twinkleDensity", HTTP_POST, []() {
String value = webServer.arg("value");
twinkleDensity = value.toInt();
if(twinkleDensity < 0) twinkleDensity = 0;
if (twinkleDensity < 0) twinkleDensity = 0;
else if (twinkleDensity > 8) twinkleDensity = 8;
broadcastInt("twinkleDensity", twinkleDensity);
sendInt(twinkleDensity);
@ -511,6 +547,8 @@ void loop() {
FastLED.show();
resetPattern = false;
// insert a delay to keep the framerate modest
// FastLED.delay(1000 / FRAMES_PER_SECOND);
}
@ -804,7 +842,7 @@ void setPower(uint8_t value)
}
void setAutoplay(uint8_t value)
{
{
autoplay = value == 0 ? 0 : 1;
EEPROM.write(6, autoplay);
@ -879,12 +917,14 @@ void setPattern(uint8_t value)
}
broadcastInt("pattern", currentPatternIndex);
resetPattern = true;
}
void setPatternName(String name)
{
for(uint8_t i = 0; i < patternCount; i++) {
if(patterns[i].name == name) {
for (uint8_t i = 0; i < patternCount; i++) {
if (patterns[i].name == name) {
setPattern(i);
break;
}
@ -906,8 +946,8 @@ void setPalette(uint8_t value)
void setPaletteName(String name)
{
for(uint8_t i = 0; i < paletteCount; i++) {
if(paletteNames[i] == name) {
for (uint8_t i = 0; i < paletteCount; i++) {
if (paletteNames[i] == name) {
setPalette(i);
break;
}
@ -1004,10 +1044,10 @@ void sinelon()
int pos = beatsin16(speed, 0, NUM_LEDS);
static int prevpos = 0;
CRGB color = ColorFromPalette(palettes[currentPaletteIndex], gHue, 255);
if( pos < prevpos ) {
fill_solid( leds+pos, (prevpos-pos)+1, color);
if ( pos < prevpos ) {
fill_solid( leds + pos, (prevpos - pos) + 1, color);
} else {
fill_solid( leds+prevpos, (pos-prevpos)+1, color);
fill_solid( leds + prevpos, (pos - prevpos) + 1, color);
}
prevpos = pos;
}

193
sunrise.h Normal file
View File

@ -0,0 +1,193 @@
const uint16_t sunriseSeconds = 60; // how long should the "sun" take to rise from completely dark to completely lit
const uint16_t sunriseMillis = (sunriseSeconds * 1000);
const uint16_t sunriseInterval = sunriseMillis / 240; // when using palettes, the usable range is 0-240 before it starts wrapping from the last color to the first
uint8_t sunriseIncrement = 4; // how much to change brightness for each level of the matrix
uint8_t sunriseLevel;
const CRGBPalette16 sunrisePalette = HeatColors_p;
const uint8_t centerX = MatrixWidth / 2;
void updateSunrise() {
EVERY_N_MILLIS(sunriseInterval) {
if (sunriseLevel < 240) {
sunriseLevel++;
Serial.print("Current level: "); Serial.println(sunriseLevel);
}
else if (sunriseIncrement > 0) {
sunriseIncrement--;
}
}
if (resetPattern) {
sunriseLevel = 0;
sunriseIncrement = 4;
FastLED.clear();
}
}
void sunriseStatic() {
updateSunrise();
for (uint8_t x = 0; x < MatrixWidth; x++) {
int16_t d = sunriseLevel - sunriseIncrement;
for (uint8_t y = 0; y < MatrixHeight; y++) {
if (d >= 0) {
CRGB newcolor = ColorFromPalette(sunrisePalette, random(0, d), random8(d, 255));
uint16_t pixelnumber = XY(x, y);
nblend(leds[pixelnumber], newcolor, 64);
}
d -= sunriseIncrement;
}
}
}
void sunriseFlicker() {
dimAll(240);
updateSunrise();
for (uint8_t x = 0; x < MatrixWidth; x++) {
int16_t d = sunriseLevel - sunriseIncrement;
for (uint8_t y = 0; y < MatrixHeight; y++) {
if (d >= 0) {
CRGB newcolor = ColorFromPalette(sunrisePalette, random(0, d), random8(d, 255));
uint16_t pixelnumber = XY(x, y);
nblend(leds[pixelnumber], newcolor, 64);
}
d -= sunriseIncrement;
}
}
}
void sunriseWavesDiagonal() {
dimAll(240);
updateSunrise();
uint8_t t = beat8(60);
for (uint8_t x = 0; x < MatrixWidth; x++) {
for (uint8_t y = 0; y < MatrixHeight; y++) {
uint8_t cx = sin8(x);
uint8_t cy = cos8(y);
uint8_t bri8 = sin8(8 * (cx + cy) + t);
CRGB newcolor = ColorFromPalette(sunrisePalette, sunriseLevel, bri8);
uint16_t pixelnumber = XY(x, y);
pixelnumber = (NUM_LEDS - 1) - pixelnumber;
nblend(leds[pixelnumber], newcolor, 64);
}
}
}
void sunriseWavesVertical() {
updateSunrise();
static uint16_t sPseudotime = 0;
static uint16_t sLastMillis = 0;
uint8_t brightdepth = beatsin88( 341, 96, 224);
uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
uint8_t msmultiplier = beatsin88(147, 23, 60);
uint16_t ms = millis();
uint16_t deltams = ms - sLastMillis ;
sLastMillis = ms;
sPseudotime += deltams * msmultiplier;
uint16_t brightnesstheta16 = sPseudotime;
for (uint8_t y = 0; y < MatrixHeight; y++) {
brightnesstheta16 += brightnessthetainc16;
uint16_t b16 = sin16( brightnesstheta16 ) + 32768;
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
for (uint8_t x = 0; x < MatrixWidth; x++) {
CRGB newcolor = ColorFromPalette(sunrisePalette, sunriseLevel, bri8);
uint16_t pixelnumber = XY(x, (MatrixHeight - 1) - y);
pixelnumber = (NUM_LEDS - 1) - pixelnumber;
nblend(leds[pixelnumber], newcolor, 64);
}
}
}
void sunriseWavesHorizontal() {
updateSunrise();
static uint16_t sPseudotime = 0;
static uint16_t sLastMillis = 0;
uint8_t brightdepth = beatsin88( 341, 96, 224);
uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
uint8_t msmultiplier = beatsin88(147, 23, 60);
uint16_t ms = millis();
uint16_t deltams = ms - sLastMillis ;
sLastMillis = ms;
sPseudotime += deltams * msmultiplier;
uint16_t brightnesstheta16 = sPseudotime;
for (uint8_t x = 0; x < MatrixWidth; x++) {
brightnesstheta16 += brightnessthetainc16;
uint16_t b16 = sin16( brightnesstheta16 ) + 32768;
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
for (uint8_t y = 0; y < MatrixHeight; y++) {
CRGB newcolor = ColorFromPalette(sunrisePalette, sunriseLevel, bri8);
uint16_t pixelnumber = XY(x, y);
pixelnumber = (NUM_LEDS - 1) - pixelnumber;
nblend(leds[pixelnumber], newcolor, 64);
}
}
}
void sunriseWavesRotating() {
updateSunrise();
static uint16_t sPseudotime = 0;
static uint16_t sLastMillis = 0;
uint8_t brightdepth = beatsin88( 341, 96, 224);
uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
uint8_t msmultiplier = beatsin88(147, 23, 60);
uint16_t ms = millis();
uint16_t deltams = ms - sLastMillis ;
sLastMillis = ms;
sPseudotime += deltams * msmultiplier;
uint16_t brightnesstheta16 = sPseudotime;
for (uint8_t x = 0; x < MatrixWidth; x++) {
for (uint8_t y = 0; y < MatrixHeight; y++) {
brightnesstheta16 += brightnessthetainc16;
uint16_t b16 = sin16( brightnesstheta16 ) + 32768;
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
CRGB newcolor = ColorFromPalette(sunrisePalette, sunriseLevel, bri8);
uint16_t pixelnumber = XY(x, y);
pixelnumber = (NUM_LEDS - 1) - pixelnumber;
nblend(leds[pixelnumber], newcolor, 64);
}
}
}