Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
|
caeb06eea7 | |
|
9ced99c376 | |
|
0379530672 | |
|
1681139205 | |
|
d16613cb5d |
|
@ -1 +1,5 @@
|
|||
.vscode
|
||||
debug.cfg
|
||||
debug_custom.json
|
||||
esp32.svd
|
||||
.vscode/
|
||||
build/
|
||||
|
|
53
README.md
53
README.md
|
@ -20,6 +20,31 @@ Most people, after watching it for a bit,
|
|||
form different ideas about what it's displaying.
|
||||
That's cool.
|
||||
|
||||
Reset
|
||||
------
|
||||
|
||||
Plug the device in,
|
||||
connect GND to pin A0,
|
||||
and briefly press the reboot button.
|
||||
It will flash orange and blue a few times
|
||||
to let you know it has reset.
|
||||
|
||||
|
||||
WiFi
|
||||
-----
|
||||
|
||||
If the red light on the board is lit,
|
||||
that means it doesn't know how to connect to the WiFi.
|
||||
|
||||
Get your phone or computer to connect to an access point
|
||||
called "Wall Art xxxxxxxxx".
|
||||
The password is "artsy fartsy", unless you changed it in the source code.
|
||||
Once connected,
|
||||
you should get a browser window that lets you connect.
|
||||
If not, try going to http://neverssl.com/.
|
||||
|
||||
You can clear the wifi information with a reset.
|
||||
|
||||
|
||||
Network Server
|
||||
--------------
|
||||
|
@ -49,6 +74,33 @@ A happy setup will cycle through each color once,
|
|||
and then display orange for a while.
|
||||
|
||||
|
||||
Clock
|
||||
-----
|
||||
|
||||
At night,
|
||||
and sometimes during the day,
|
||||
it displays something like a clock.
|
||||
You will need to tell it your time zone.
|
||||
It doesn't do daylight saving time, sorry.
|
||||
I suggest you set it to standard time and pretend it's in sync with the sun.
|
||||
|
||||
* Each pixel in the top row is 1 hour (3600 seconds)
|
||||
* Each pixel in the middle row is 5 minutes (300 seconds)
|
||||
* Each pixel in the bottom row is 25 seconds
|
||||
* There are four pixels around the bottom that move every 5 seconds
|
||||
|
||||
|
||||
Update
|
||||
------
|
||||
|
||||
You can upload a new version of the firmware.
|
||||
Reset the device,
|
||||
and select the "Update" button instead of configuring WiFi.
|
||||
Then you can upload the new .bin firmware file.
|
||||
|
||||
You will have to reconfigure networking after this.
|
||||
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
|
@ -68,4 +120,3 @@ but if you want to make NeoPixel art,
|
|||
think hard about what the end result should look like.
|
||||
It's not enough to make a cool light show;
|
||||
it has to make people wonder "what is that for?"
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
WiFiManager wfm;
|
||||
|
||||
void network_reset() {
|
||||
wfm.resetSettings();
|
||||
}
|
||||
|
||||
void network_setup(char *password) {
|
||||
String hostid = String(ESP.getEfuseMac(), HEX);
|
||||
String hostname = "Wall Art " + hostid;
|
||||
|
@ -14,8 +18,6 @@ void network_setup(char *password) {
|
|||
wfm.setConfigPortalBlocking(false);
|
||||
wfm.setHostname(hostname);
|
||||
wfm.autoConnect(hostname.c_str(), password);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void network_reset();
|
||||
void network_setup(char *password);
|
||||
bool connected();
|
||||
void pause(uint32_t dwMs);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#include <Timezone.h>
|
||||
|
||||
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};
|
||||
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};
|
||||
Timezone TZ_US_Eastern(usEDT, usEST);
|
||||
|
||||
TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300};
|
||||
TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360};
|
||||
Timezone TZ_US_Central(usCDT, usCST);
|
||||
|
||||
TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360};
|
||||
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
|
||||
Timezone TZ_US_Mountain(usMDT, usMST);
|
||||
|
||||
TimeChangeRule usPDT = {"EDT", Second, Sun, Mar, 2, -420};
|
||||
TimeChangeRule usPST = {"EST", First, Sun, Nov, 2, -480};
|
||||
Timezone TZ_US_Pacific(usPDT, usPST);
|
132
wallart.ino
132
wallart.ino
|
@ -1,23 +1,25 @@
|
|||
#include <FastLED.h>
|
||||
#include <FastLED.h>
|
||||
#include <ArduinoHttpClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <NTPClient.h>
|
||||
#include "times.h"
|
||||
#include <TimeLib.h>
|
||||
#include "durations.h"
|
||||
#include "timezones.h"
|
||||
#include "picker.h"
|
||||
#include "network.h"
|
||||
|
||||
#define NEOPIXEL_PIN 32
|
||||
#define GRIDLEN 64
|
||||
#define WFM_PASSWORD "artsy fartsy"
|
||||
#define TIME_OFFSET (-7 * HOUR / SECOND)
|
||||
#define TIMEZONE TZ_US_Mountain
|
||||
|
||||
/*
|
||||
* The hours when the day begins and ends.
|
||||
* During the day, everything is brighter.
|
||||
* At night, all you get is a dim clock.
|
||||
*/
|
||||
#define DAY_BEGIN 7
|
||||
#define DAY_END 21
|
||||
#define DAY_BEGIN 6
|
||||
#define DAY_END 20
|
||||
#define DAY_BRIGHTNESS 0x80
|
||||
#define NIGHT_BRIGHTNESS 0x10
|
||||
|
||||
|
@ -32,18 +34,47 @@
|
|||
|
||||
#define HTTPS_TIMEOUT (2 * SECOND)
|
||||
|
||||
#define RESET_PIN 26
|
||||
|
||||
CRGB grid[GRIDLEN];
|
||||
|
||||
WiFiUDP ntpUDP;
|
||||
NTPClient timeClient(ntpUDP, TIME_OFFSET);
|
||||
NTPClient timeClient(ntpUDP);
|
||||
|
||||
void do_reset() {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 8; j += 1) {
|
||||
grid[j] = ((i+j)%2) ? (CRGB::Orange) : (CRGB::Blue);
|
||||
}
|
||||
FastLED.show();
|
||||
digitalWrite(LED_BUILTIN, i%2);
|
||||
delay(300 * MILLISECOND);
|
||||
}
|
||||
network_reset();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(RESET_PIN, INPUT_PULLUP);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(grid, GRIDLEN);
|
||||
// Maybe it's the plexiglass but for my build I really need to dial back the red
|
||||
FastLED.setCorrection(0xc0ffff);
|
||||
// Maybe it's the plexiglass, but for my build, I need to dial back the red
|
||||
FastLED.setCorrection(0xd0ffff);
|
||||
if (!digitalRead(RESET_PIN)) {
|
||||
do_reset();
|
||||
}
|
||||
network_setup(WFM_PASSWORD);
|
||||
}
|
||||
|
||||
|
||||
bool updateTime() {
|
||||
if (timeClient.update()) {
|
||||
time_t now = timeClient.getEpochTime();
|
||||
time_t local = TIMEZONE.toLocal(now);
|
||||
setTime(local);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void fade(int cycles = 2) {
|
||||
int reps = (cycles*GRIDLEN) + random(GRIDLEN);
|
||||
int hue = random(256);
|
||||
|
@ -218,9 +249,21 @@ void netget(int count=60) {
|
|||
if (https.skipResponseHeaders() != HTTP_SUCCESS) break;
|
||||
hue = netgetStatus(HUE_YELLOW);
|
||||
|
||||
int artlen = https.read((uint8_t *)NetArt, sizeof(NetArt));
|
||||
hue = netgetStatus(HUE_ORANGE);
|
||||
NetArtFrames = (artlen / 3) / GRIDLEN;
|
||||
size_t readBytes = 0;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
size_t artBytesLeft = sizeof(NetArt) - readBytes;
|
||||
|
||||
if (https.endOfBodyReached() || (artBytesLeft == 0)) {
|
||||
hue = netgetStatus(HUE_ORANGE);
|
||||
NetArtFrames = (readBytes / 3) / GRIDLEN;
|
||||
break;
|
||||
}
|
||||
int l = https.read((uint8_t *)NetArt + readBytes, artBytesLeft);
|
||||
if (-1 == l) {
|
||||
break;
|
||||
}
|
||||
readBytes += l;
|
||||
}
|
||||
} while(false);
|
||||
https.stop();
|
||||
}
|
||||
|
@ -243,22 +286,35 @@ void spinner(int count=32) {
|
|||
}
|
||||
|
||||
void displayTime(unsigned long duration = 20 * SECOND) {
|
||||
if (!connected()) return;
|
||||
if (timeStatus() != timeSet) return;
|
||||
unsigned long end = millis() + duration;
|
||||
FastLED.clear();
|
||||
|
||||
while (millis() < end) {
|
||||
timeClient.update();
|
||||
int hh = timeClient.getHours();
|
||||
int mm = timeClient.getMinutes();
|
||||
int ss = timeClient.getSeconds();
|
||||
updateTime();
|
||||
int hh = hour();
|
||||
int mmss = now() % 3600;
|
||||
uint8_t hue = HUE_YELLOW;
|
||||
|
||||
if (hh >= 12) {
|
||||
// Top: Hours
|
||||
if (isPM()) {
|
||||
hue = HUE_ORANGE;
|
||||
hh -= 12;
|
||||
}
|
||||
|
||||
// Middle: 5m (300s)
|
||||
uint8_t mm = (mmss/300) % 12;
|
||||
|
||||
// Bottom: 25s
|
||||
uint8_t ss = (mmss/25) % 12;
|
||||
|
||||
// Outer: 5s
|
||||
uint8_t s = (mmss/5) % 5;
|
||||
grid[64 - 7 - 1] = CHSV(HUE_PURPLE, 128, (s==1)?96:0);
|
||||
grid[64 - 15 - 1] = CHSV(HUE_PURPLE, 128, (s==2)?96:0);
|
||||
grid[64 - 8 - 1] = CHSV(HUE_PURPLE, 128, (s==3)?96:0);
|
||||
grid[64 - 0 - 1] = CHSV(HUE_PURPLE, 128, (s==4)?96:0);
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
// Omit first and last position on a row
|
||||
int pos = i + 1;
|
||||
|
@ -267,8 +323,8 @@ void displayTime(unsigned long duration = 20 * SECOND) {
|
|||
}
|
||||
|
||||
grid[pos + 0] = CHSV(hue, 255, (i<hh)?128:48);
|
||||
grid[pos + 24] = CHSV(HUE_RED, 255, (i<mm/5)?128:48);
|
||||
grid[pos + 48] = CHSV(HUE_PINK, 128, (i<ss/5)?96:48);
|
||||
grid[pos + 24] = CHSV(HUE_RED, 255, (i<mm)?128:48);
|
||||
grid[pos + 48] = CHSV(HUE_PINK, 128, (i<ss)?96:48);
|
||||
}
|
||||
FastLED.show();
|
||||
|
||||
|
@ -276,33 +332,33 @@ void displayTime(unsigned long duration = 20 * SECOND) {
|
|||
}
|
||||
}
|
||||
|
||||
void adjustBrightness() {
|
||||
int hh = timeClient.getHours();
|
||||
if ((hh >= DAY_BEGIN) && (hh < DAY_END)) {
|
||||
FastLED.setBrightness(DAY_BRIGHTNESS);
|
||||
} else {
|
||||
FastLED.setBrightness(NIGHT_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Picker p;
|
||||
uint8_t getprob = 4;
|
||||
bool conn = connected();
|
||||
bool day = true;
|
||||
|
||||
timeClient.update();
|
||||
if (timeClient.isTimeSet()) {
|
||||
int hh = timeClient.getHours();
|
||||
day = ((hh > DAY_BEGIN) && (hh < DAY_END));
|
||||
switch (timeStatus()) {
|
||||
case timeNotSet:
|
||||
case timeNeedsSync:
|
||||
updateTime();
|
||||
break;
|
||||
}
|
||||
if (timeStatus() == timeSet) {
|
||||
int hh = hour();
|
||||
day = ((hh >= DAY_BEGIN) && (hh < DAY_END));
|
||||
}
|
||||
FastLED.setBrightness(day?DAY_BRIGHTNESS:NIGHT_BRIGHTNESS);
|
||||
|
||||
// If we don't yet have net art, try a little harder to get it.
|
||||
if ((NetArtFrames == 0) || !conn) {
|
||||
getprob = 16;
|
||||
}
|
||||
|
||||
if (p.Pick(getprob)) {
|
||||
|
||||
if (!day || p.Pick(4)) {
|
||||
// At night, only ever show the clock
|
||||
displayTime(2 * MINUTE);
|
||||
} else if (p.Pick(getprob)) {
|
||||
netget();
|
||||
} else if (day && p.Pick(4)) {
|
||||
// These can be hella bright
|
||||
|
@ -324,7 +380,5 @@ void loop() {
|
|||
cm5(8);
|
||||
} else if (p.Pick(2)) {
|
||||
cm5(16);
|
||||
} else if (p.Pick(4)) {
|
||||
displayTime(1 * MINUTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue