Compare commits

...

5 Commits
1.1 ... master

7 changed files with 172 additions and 43 deletions

6
.gitignore vendored
View File

@ -1 +1,5 @@
.vscode
debug.cfg
debug_custom.json
esp32.svd
.vscode/
build/

View File

@ -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?"

View File

@ -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() {

View File

@ -1,5 +1,6 @@
#pragma once
void network_reset();
void network_setup(char *password);
bool connected();
void pause(uint32_t dwMs);

17
timezones.h 100644
View File

@ -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);

View File

@ -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);
}
}
}