Compare commits
No commits in common. "master" and "1.3" have entirely different histories.
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,15 +0,0 @@
|
||||||
## 2.1 - 2024-06-09
|
|
||||||
### Fixed
|
|
||||||
* MAC address on boot now displayed correctly.
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0 - 2024-06-09
|
|
||||||
### Added
|
|
||||||
* New Clock mode with arabic numerals
|
|
||||||
* Firmware version now displayed at boot time
|
|
||||||
* Rotation can be hardcoded based on MAC address.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Tweaked gamma adjustment for better yellows
|
|
||||||
* Makefile closer to something useful
|
|
||||||
* Updated library versions
|
|
8
Makefile
8
Makefile
|
@ -1,8 +0,0 @@
|
||||||
BOARD = --board esp32:esp32:featheresp32
|
|
||||||
|
|
||||||
verify: wallart.ino
|
|
||||||
arduino --verify $(BOARD) $<
|
|
||||||
|
|
||||||
install: wallart.ino
|
|
||||||
arduino --upload $(BOARD) $<
|
|
||||||
|
|
65
README.md
65
README.md
|
@ -21,41 +21,6 @@ form different ideas about what it's displaying.
|
||||||
That's cool.
|
That's cool.
|
||||||
|
|
||||||
|
|
||||||
Setup
|
|
||||||
-----
|
|
||||||
|
|
||||||
When you first plug it in,
|
|
||||||
you will see a yellow pattern with blue or red bars around it.
|
|
||||||
The pattern is your mac address.
|
|
||||||
If the bars are red and a pixel is flashing,
|
|
||||||
that means you need to set up WiFi.
|
|
||||||
|
|
||||||
You can also look at the back for a red LED.
|
|
||||||
If it's lit, you need to set up WiFi.
|
|
||||||
|
|
||||||
Get your phone or computer to connect to an access point
|
|
||||||
called "WallArt".
|
|
||||||
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/.
|
|
||||||
|
|
||||||
Please configure the clock before the WiFi.
|
|
||||||
This will set up your time zone,
|
|
||||||
so it doesn't blind you in the middle of the night.
|
|
||||||
|
|
||||||
You can clear the wifi information with a reset.
|
|
||||||
|
|
||||||
|
|
||||||
Reset
|
|
||||||
------
|
|
||||||
|
|
||||||
Plug the device in,
|
|
||||||
and connect GND to pin A0 (right next to GND).
|
|
||||||
The red LED on the Feather board should come on immediately,
|
|
||||||
indicating it needs the network set up again.
|
|
||||||
|
|
||||||
|
|
||||||
Network Server
|
Network Server
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -90,29 +55,15 @@ Clock
|
||||||
At night,
|
At night,
|
||||||
and sometimes during the day,
|
and sometimes during the day,
|
||||||
it displays something like a clock.
|
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 top row is 1 hour (3600 seconds)
|
||||||
* Each pixel in the middle row is 5 minutes (300 seconds)
|
* Each pixel in the middle row is 5 minutes (300 seconds)
|
||||||
* Each pixel in the bottom row is 25 seconds
|
* Each pixel in the bottom row is 25 seconds
|
||||||
* There are four pixels around the bottom that move every 5 seconds
|
* There are four pixels around the bottom that move every 5 seconds
|
||||||
|
|
||||||
Build Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
You'll need the following:
|
|
||||||
|
|
||||||
* esp32 boards: Arduino ESP32 Feather
|
|
||||||
* FastLED library
|
|
||||||
* WifiManagerTZ library (and its dependencies)
|
|
||||||
* ArduinoHttpClient library
|
|
||||||
|
|
||||||
|
|
||||||
Updating Firmware
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
python3 esptool.py --chip esp32 --port "/dev/ttyUSB0" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 wallart.ino.bootloader.bin 0x8000 wallart.ino.partitions.bin 0xe000 boot_app0.bin 0x10000 wallart.ino.bin
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Philosophy
|
Philosophy
|
||||||
----------
|
----------
|
||||||
|
@ -133,13 +84,3 @@ but if you want to make NeoPixel art,
|
||||||
think hard about what the end result should look like.
|
think hard about what the end result should look like.
|
||||||
It's not enough to make a cool light show;
|
It's not enough to make a cool light show;
|
||||||
it has to make people wonder "what is that for?"
|
it has to make people wonder "what is that for?"
|
||||||
|
|
||||||
|
|
||||||
Apology
|
|
||||||
----------
|
|
||||||
|
|
||||||
I am no longer a C++ programmer.
|
|
||||||
The structure of this code is awful.
|
|
||||||
I'm sorry.
|
|
||||||
I didn't feel like a 2-day refresher in a language I never use,
|
|
||||||
for code nobody else is likely to ever compile.
|
|
||||||
|
|
85
clock.h
85
clock.h
|
@ -1,85 +0,0 @@
|
||||||
const uint8_t digits4x4[5][4] = {
|
|
||||||
{
|
|
||||||
0b00100110,
|
|
||||||
0b00101010,
|
|
||||||
0b00101010,
|
|
||||||
0b00101100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b11001110,
|
|
||||||
0b01100010,
|
|
||||||
0b00101000,
|
|
||||||
0b11001110,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b11101010,
|
|
||||||
0b10001010,
|
|
||||||
0b00101110,
|
|
||||||
0b11100010,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b11101000,
|
|
||||||
0b00101100,
|
|
||||||
0b00101010,
|
|
||||||
0b00100110,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b11001110,
|
|
||||||
0b10101010,
|
|
||||||
0b01101110,
|
|
||||||
0b00101110,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void digitDraw(int xoffset, int yoffset, int val, CRGB color) {
|
|
||||||
for (int y=0; y<4; y++) {
|
|
||||||
uint8_t row = digits4x4[val/2][y] >> (4*(val%2));
|
|
||||||
for (int x=0; x<4; x++) {
|
|
||||||
bool bit = (row>>(3-x)) & 1;
|
|
||||||
int pos = (yoffset+y)*8 + (xoffset+x);
|
|
||||||
if (bit) {
|
|
||||||
grid[pos] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayTimeDigits(bool day, unsigned long duration = 20*SECOND) {
|
|
||||||
unsigned long end = millis() + duration;
|
|
||||||
|
|
||||||
bool flash = false;
|
|
||||||
|
|
||||||
while (millis() < end) {
|
|
||||||
struct tm info;
|
|
||||||
getLocalTime(&info);
|
|
||||||
|
|
||||||
int h0 = (info.tm_hour / 1) % 10;
|
|
||||||
int h1 = (info.tm_hour / 10) % 10;
|
|
||||||
int m0 = (info.tm_min / 1) % 10;
|
|
||||||
int m1 = (info.tm_min / 10) % 10;
|
|
||||||
|
|
||||||
uint8_t hhue = day?HUE_AQUA:HUE_ORANGE;
|
|
||||||
uint8_t mhue = day?HUE_ORANGE:HUE_RED;
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
if (day) {
|
|
||||||
fill_solid(grid, 32, CHSV(hhue, 120, 32));
|
|
||||||
fill_solid(grid+32, 32, CHSV(mhue, 120, 32));
|
|
||||||
} else {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw foreground
|
|
||||||
CRGB hcolor = CHSV(hhue, 240, 120);
|
|
||||||
CRGB mcolor = CHSV(mhue, 120, 120);
|
|
||||||
digitDraw(0, 0, h1, hcolor);
|
|
||||||
digitDraw(4, 0, h0, hcolor);
|
|
||||||
digitDraw(0, 4, m1, mcolor);
|
|
||||||
digitDraw(4, 4, m0, mcolor);
|
|
||||||
|
|
||||||
show();
|
|
||||||
pause(SECOND);
|
|
||||||
|
|
||||||
flash = !flash;
|
|
||||||
}
|
|
||||||
}
|
|
40
network.cpp
40
network.cpp
|
@ -1,60 +1,28 @@
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
#include <WiFiManagerTz.h>
|
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include <esp_sntp.h>
|
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
|
||||||
WiFiManager wfm;
|
WiFiManager wfm;
|
||||||
|
|
||||||
void network_reset() {
|
|
||||||
Serial.println("Resetting network");
|
|
||||||
wfm.resetSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool time_was_accurate_once = false;
|
|
||||||
bool clock_is_set() {
|
|
||||||
return time_was_accurate_once;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_time_available(struct timeval *t) {
|
|
||||||
struct tm timeInfo;
|
|
||||||
getLocalTime(&timeInfo, 1000);
|
|
||||||
Serial.println(&timeInfo, "%A, %B %d %Y %H:%M:%S %Z %z ");
|
|
||||||
time_was_accurate_once = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void network_setup(char *password) {
|
void network_setup(char *password) {
|
||||||
String hostname = "WallArt";
|
String hostid = String(ESP.getEfuseMac(), HEX);
|
||||||
|
String hostname = "Wall Art " + hostid;
|
||||||
|
|
||||||
WiFiManagerNS::NTP::onTimeAvailable(&on_time_available);
|
|
||||||
WiFiManagerNS::init(&wfm, nullptr);
|
|
||||||
|
|
||||||
std::vector<const char *> menu = {"wifi", "info", "custom", "param", "sep", "update", "restart", "exit"};
|
|
||||||
wfm.setMenu(menu);
|
|
||||||
wfm.setConfigPortalBlocking(false);
|
wfm.setConfigPortalBlocking(false);
|
||||||
wfm.setHostname(hostname);
|
wfm.setHostname(hostname);
|
||||||
wfm.autoConnect(hostname.c_str(), password);
|
wfm.autoConnect(hostname.c_str(), password);
|
||||||
|
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connected() {
|
bool connected() {
|
||||||
return WiFi.status() == WL_CONNECTED;
|
return WiFi.status() == WL_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool timeConfigured = false;
|
|
||||||
|
|
||||||
void pause(uint32_t dwMs) {
|
void pause(uint32_t dwMs) {
|
||||||
if (connected() && !timeConfigured) {
|
|
||||||
WiFiManagerNS::configTime();
|
|
||||||
timeConfigured = true;
|
|
||||||
}
|
|
||||||
if (!digitalRead(RESET_PIN)) {
|
|
||||||
network_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t t = 0; t < dwMs; t += 10) {
|
for (uint32_t t = 0; t < dwMs; t += 10) {
|
||||||
wfm.process();
|
wfm.process();
|
||||||
digitalWrite(LED_BUILTIN, !connected());
|
digitalWrite(LED_BUILTIN, !connected());
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Short this to ground to reset the network
|
|
||||||
#define RESET_PIN 26
|
|
||||||
|
|
||||||
void network_reset();
|
|
||||||
void network_setup(char *password);
|
void network_setup(char *password);
|
||||||
bool connected();
|
bool connected();
|
||||||
void pause(uint32_t dwMs);
|
void pause(uint32_t dwMs);
|
||||||
void netget(int count);
|
void netget(int count);
|
||||||
bool clock_is_set();
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
#include "picker.h"
|
#include "picker.h"
|
||||||
|
|
||||||
Picker::Picker() {
|
Picker::Picker() {
|
||||||
this->val = random(1, 256);
|
val = random(1, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Picker::Pick(uint8_t likelihood) {
|
bool Picker::Pick(uint8_t likelihood) {
|
||||||
bool picked = false;
|
|
||||||
if ((val > 0) && (val <= likelihood)) {
|
if ((val > 0) && (val <= likelihood)) {
|
||||||
picked = true;
|
val = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
val -= likelihood;
|
val -= likelihood;
|
||||||
return picked;
|
return false;
|
||||||
}
|
}
|
2
picker.h
2
picker.h
|
@ -7,5 +7,5 @@ public:
|
||||||
Picker();
|
Picker();
|
||||||
bool Pick(uint8_t);
|
bool Pick(uint8_t);
|
||||||
private:
|
private:
|
||||||
int val;
|
uint8_t val;
|
||||||
};
|
};
|
||||||
|
|
29
settings.h
29
settings.h
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The hours when the day begins and ends.
|
|
||||||
* At night, all you get is a dim clock.
|
|
||||||
*/
|
|
||||||
#define DAY_BEGIN 6
|
|
||||||
#define DAY_END 20
|
|
||||||
#define DAY_BRIGHTNESS 0x80
|
|
||||||
#define NIGHT_BRIGHTNESS 0x10
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Define these to fetch from a wallart-server
|
|
||||||
*
|
|
||||||
* https://git.woozle.org/neale/wallart-server
|
|
||||||
*/
|
|
||||||
#define ART_HOSTNAME "www.woozle.org"
|
|
||||||
#define ART_PORT 443
|
|
||||||
#define ART_PATH "/wallart/wallart.bin"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The password used when running as an access point.
|
|
||||||
*/
|
|
||||||
#define WFM_PASSWORD "artsy fartsy"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The output pin your neopixel array is connected to.
|
|
||||||
*/
|
|
||||||
#define NEOPIXEL_PIN 32
|
|
|
@ -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);
|
223
wallart.ino
223
wallart.ino
|
@ -2,56 +2,60 @@
|
||||||
#include <ArduinoHttpClient.h>
|
#include <ArduinoHttpClient.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <TimeLib.h>
|
#include <NTPClient.h>
|
||||||
|
#include <Time.h>
|
||||||
#include "durations.h"
|
#include "durations.h"
|
||||||
|
#include "timezones.h"
|
||||||
#include "picker.h"
|
#include "picker.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
#define VERSION 2
|
#define NEOPIXEL_PIN 32
|
||||||
#define GRIDLEN 64
|
#define GRIDLEN 64
|
||||||
|
#define WFM_PASSWORD "artsy fartsy"
|
||||||
|
#define TIMEZONE TZ_US_Mountain
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The hours when the day begins and ends.
|
||||||
|
* At night, all you get is a dim clock.
|
||||||
|
*/
|
||||||
|
#define DAY_BEGIN 6
|
||||||
|
#define DAY_END 20
|
||||||
|
#define DAY_BRIGHTNESS 0x80
|
||||||
|
#define NIGHT_BRIGHTNESS 0x10
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define these to fetch from a wallart-server
|
||||||
|
*
|
||||||
|
* https://git.woozle.org/neale/wallart-server
|
||||||
|
*/
|
||||||
|
#define ART_HOSTNAME "www.woozle.org"
|
||||||
|
#define ART_PORT 443
|
||||||
|
#define ART_PATH "/wallart/wallart.bin"
|
||||||
|
|
||||||
#define HTTPS_TIMEOUT (2 * SECOND)
|
#define HTTPS_TIMEOUT (2 * SECOND)
|
||||||
#define IMAGE_PULL_MIN_INTERVAL (5 * MINUTE)
|
|
||||||
|
|
||||||
CRGB grid[GRIDLEN];
|
CRGB grid[GRIDLEN];
|
||||||
CRGB actual[GRIDLEN];
|
|
||||||
|
|
||||||
// Rotation, in degrees: [0, 90, 180, 270]
|
WiFiUDP ntpUDP;
|
||||||
int rotation = 0;
|
NTPClient timeClient(ntpUDP);
|
||||||
|
|
||||||
void show() {
|
void setup() {
|
||||||
for (int y = 0; y < 8; y++) {
|
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(grid, GRIDLEN);
|
||||||
for (int x = 0; x < 8; x++) {
|
// Maybe it's the plexiglass but for my build I really need to dial back the red
|
||||||
int pos;
|
FastLED.setCorrection(0xc0ffff);
|
||||||
switch (rotation) {
|
network_setup(WFM_PASSWORD);
|
||||||
case 90:
|
|
||||||
pos = (x)*8 + (7-y);
|
|
||||||
break;
|
|
||||||
case 180:
|
|
||||||
pos = (7-y)*8 + (7-x);
|
|
||||||
break;
|
|
||||||
case 270:
|
|
||||||
pos = (7-x)*8 + y;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pos = (y)*8 + x;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
actual[pos] = grid[y*8 + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FastLED.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
bool updateTime() {
|
||||||
fill_solid(grid, GRIDLEN, CRGB::Black);
|
if (timeClient.update()) {
|
||||||
|
time_t now = timeClient.getEpochTime();
|
||||||
|
time_t local = TIMEZONE.toLocal(now);
|
||||||
|
setTime(local);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I am so ashamed of this.
|
|
||||||
// But C++ is a real pain for me at this point.
|
|
||||||
#include "clock.h"
|
|
||||||
|
|
||||||
void fade(int cycles = 2) {
|
void fade(int cycles = 2) {
|
||||||
int reps = (cycles*GRIDLEN) + random(GRIDLEN);
|
int reps = (cycles*GRIDLEN) + random(GRIDLEN);
|
||||||
int hue = random(256);
|
int hue = random(256);
|
||||||
|
@ -60,7 +64,7 @@ void fade(int cycles = 2) {
|
||||||
uint8_t p = cm5xlat(8, (i+pos) % GRIDLEN);
|
uint8_t p = cm5xlat(8, (i+pos) % GRIDLEN);
|
||||||
grid[p] = CHSV(hue, 255, pos * 32);
|
grid[p] = CHSV(hue, 255, pos * 32);
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(80);
|
pause(80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +72,7 @@ void fade(int cycles = 2) {
|
||||||
void singleCursor(int count = 80) {
|
void singleCursor(int count = 80) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
grid[20] = CHSV(0, 210, 127 * (i%2));
|
grid[20] = CHSV(0, 210, 127 * (i%2));
|
||||||
show();
|
FastLED.show();
|
||||||
pause(120);
|
pause(120);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +87,7 @@ void sparkle(int cycles=50) {
|
||||||
pos[j] = random(GRIDLEN);
|
pos[j] = random(GRIDLEN);
|
||||||
grid[pos[j]] = CRGB::Gray;
|
grid[pos[j]] = CRGB::Gray;
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(40);
|
pause(40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +122,7 @@ void glitchPulse(int cycles=1000) {
|
||||||
grid[pos[i]] = c;
|
grid[pos[i]] = c;
|
||||||
steps[i]--;
|
steps[i]--;
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(100);
|
pause(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +150,7 @@ void conwayish(int cycles=5000) {
|
||||||
left[i]--;
|
left[i]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(20);
|
pause(20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,46 +179,11 @@ void cm5(uint8_t width=0, int cycles=200) {
|
||||||
grid[xpos] = CHSV(0, 255, val);
|
grid[xpos] = CHSV(0, 255, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(500);
|
pause(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display MAC address at startup.
|
|
||||||
void displayMacAddress(int cycles=40) {
|
|
||||||
uint64_t addr = ESP.getEfuseMac();
|
|
||||||
|
|
||||||
Serial.println("mac=" + String(ESP.getEfuseMac(), HEX));
|
|
||||||
|
|
||||||
// Set some custom things per device.
|
|
||||||
// It would have been nice if doing this in the Access Point UI were easier than switching the MAC address.
|
|
||||||
switch (addr) {
|
|
||||||
case 0x18fc1d519140:
|
|
||||||
rotation = 270;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; cycles > 0; cycles -= 1) {
|
|
||||||
// Top: version
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
bool bit = (VERSION>>i) & 1;
|
|
||||||
grid[7-i] = bit ? CRGB::Black : CRGB::Aqua;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middle: MAC address
|
|
||||||
for (int i = 0; i < 48; i++) {
|
|
||||||
int pos = i + 8;
|
|
||||||
grid[pos] = CHSV(HUE_YELLOW, 255, ((addr>>(47-i)) & 1)?255:64);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom: connected status
|
|
||||||
fill_solid(grid+56, 8, connected() ? CRGB::Aqua : CRGB::Red);
|
|
||||||
|
|
||||||
show();
|
|
||||||
pause(250*MILLISECOND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Art from the network
|
// Art from the network
|
||||||
int NetArtFrames = 0;
|
int NetArtFrames = 0;
|
||||||
CRGB NetArt[8][GRIDLEN];
|
CRGB NetArt[8][GRIDLEN];
|
||||||
|
@ -226,7 +195,7 @@ void netart(int count=40) {
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
memcpy(grid, NetArt[i%NetArtFrames], GRIDLEN*3);
|
memcpy(grid, NetArt[i%NetArtFrames], GRIDLEN*3);
|
||||||
show();
|
FastLED.show();
|
||||||
pause(500);
|
pause(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,52 +207,32 @@ uint8_t netgetStatus(uint8_t hue) {
|
||||||
positions[j] = random(GRIDLEN);
|
positions[j] = random(GRIDLEN);
|
||||||
grid[positions[j]] = CHSV(hue, 255, 180);
|
grid[positions[j]] = CHSV(hue, 255, 180);
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
pause(500);
|
pause(500);
|
||||||
return hue;
|
return hue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void netget(int count=60) {
|
void netget(int count=60) {
|
||||||
uint8_t hue = netgetStatus(HUE_BLUE);
|
uint8_t hue = netgetStatus(HUE_BLUE);
|
||||||
static unsigned long nextPull = 0; // when to pull next
|
|
||||||
|
|
||||||
#if defined(ART_HOSTNAME) && defined(ART_PORT) && defined(ART_PATH)
|
#if defined(ART_HOSTNAME) && defined(ART_PORT) && defined(ART_PATH)
|
||||||
if (millis() < nextPull) {
|
if (connected()) {
|
||||||
// Let's not bombard the server
|
|
||||||
hue = HUE_ORANGE;
|
|
||||||
} else if (connected()) {
|
|
||||||
WiFiClientSecure scli;
|
WiFiClientSecure scli;
|
||||||
|
|
||||||
nextPull = millis() + IMAGE_PULL_MIN_INTERVAL;
|
|
||||||
|
|
||||||
hue = netgetStatus(HUE_AQUA);
|
hue = netgetStatus(HUE_AQUA);
|
||||||
scli.setInsecure();
|
scli.setInsecure();
|
||||||
|
|
||||||
HttpClient https(scli, ART_HOSTNAME, ART_PORT);
|
HttpClient https(scli, ART_HOSTNAME, ART_PORT);
|
||||||
do {
|
do {
|
||||||
String path = String(ART_PATH) + "?mac=" + String(ESP.getEfuseMac(), HEX);
|
if (https.get(ART_PATH) != 0) break;
|
||||||
Serial.println(path);
|
|
||||||
if (https.get(path) != 0) break;
|
|
||||||
hue = netgetStatus(HUE_GREEN);
|
hue = netgetStatus(HUE_GREEN);
|
||||||
|
|
||||||
if (https.skipResponseHeaders() != HTTP_SUCCESS) break;
|
if (https.skipResponseHeaders() != HTTP_SUCCESS) break;
|
||||||
hue = netgetStatus(HUE_YELLOW);
|
hue = netgetStatus(HUE_YELLOW);
|
||||||
|
|
||||||
size_t readBytes = 0;
|
int artlen = https.read((uint8_t *)NetArt, sizeof(NetArt));
|
||||||
for (int i = 0; i < 12; i++) {
|
|
||||||
size_t artBytesLeft = sizeof(NetArt) - readBytes;
|
|
||||||
|
|
||||||
if (https.endOfBodyReached() || (artBytesLeft == 0)) {
|
|
||||||
hue = netgetStatus(HUE_ORANGE);
|
hue = netgetStatus(HUE_ORANGE);
|
||||||
NetArtFrames = (readBytes / 3) / GRIDLEN;
|
NetArtFrames = (artlen / 3) / GRIDLEN;
|
||||||
break;
|
|
||||||
}
|
|
||||||
int l = https.read((uint8_t *)NetArt + readBytes, artBytesLeft);
|
|
||||||
if (-1 == l) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
readBytes += l;
|
|
||||||
}
|
|
||||||
} while(false);
|
} while(false);
|
||||||
https.stop();
|
https.stop();
|
||||||
}
|
}
|
||||||
|
@ -299,28 +248,25 @@ void spinner(int count=32) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int pos = spinner_pos[i % 4];
|
int pos = spinner_pos[i % 4];
|
||||||
grid[pos] = CRGB::OliveDrab;
|
grid[pos] = CRGB::OliveDrab;
|
||||||
show();
|
FastLED.show();
|
||||||
pause(125);
|
pause(125);
|
||||||
grid[pos] = CRGB::Black;
|
grid[pos] = CRGB::Black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayTimeDozenal(unsigned long duration = 20*SECOND) {
|
void displayTime(unsigned long duration = 20 * SECOND) {
|
||||||
if (!clock_is_set()) return;
|
if (timeStatus() != timeSet) return;
|
||||||
unsigned long end = millis() + duration;
|
unsigned long end = millis() + duration;
|
||||||
|
FastLED.clear();
|
||||||
clear();
|
|
||||||
|
|
||||||
while (millis() < end) {
|
while (millis() < end) {
|
||||||
struct tm info;
|
updateTime();
|
||||||
getLocalTime(&info);
|
int hh = hour();
|
||||||
|
int mmss = now() % 3600;
|
||||||
int hh = info.tm_hour;
|
|
||||||
int mmss = (info.tm_min * 60) + info.tm_sec;
|
|
||||||
uint8_t hue = HUE_YELLOW;
|
uint8_t hue = HUE_YELLOW;
|
||||||
|
|
||||||
// Top: Hours
|
// Top: Hours
|
||||||
if (hh >= 12) {
|
if (isPM()) {
|
||||||
hue = HUE_ORANGE;
|
hue = HUE_ORANGE;
|
||||||
hh -= 12;
|
hh -= 12;
|
||||||
}
|
}
|
||||||
|
@ -333,10 +279,10 @@ void displayTimeDozenal(unsigned long duration = 20*SECOND) {
|
||||||
|
|
||||||
// Outer: 5s
|
// Outer: 5s
|
||||||
uint8_t s = (mmss/5) % 5;
|
uint8_t s = (mmss/5) % 5;
|
||||||
grid[64 - 7 - 1] = CHSV(HUE_GREEN, 128, (s==1)?96:0);
|
grid[64 - 7 - 1] = CHSV(HUE_PURPLE, 128, (s==1)?96:0);
|
||||||
grid[64 - 15 - 1] = CHSV(HUE_GREEN, 128, (s==2)?96:0);
|
grid[64 - 15 - 1] = CHSV(HUE_PURPLE, 128, (s==2)?96:0);
|
||||||
grid[64 - 8 - 1] = CHSV(HUE_GREEN, 128, (s==3)?96:0);
|
grid[64 - 8 - 1] = CHSV(HUE_PURPLE, 128, (s==3)?96:0);
|
||||||
grid[64 - 0 - 1] = CHSV(HUE_GREEN, 128, (s==4)?96:0);
|
grid[64 - 0 - 1] = CHSV(HUE_PURPLE, 128, (s==4)?96:0);
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
// Omit first and last position on a row
|
// Omit first and last position on a row
|
||||||
|
@ -349,49 +295,34 @@ void displayTimeDozenal(unsigned long duration = 20*SECOND) {
|
||||||
grid[pos + 24] = CHSV(HUE_RED, 255, (i<mm)?128:48);
|
grid[pos + 24] = CHSV(HUE_RED, 255, (i<mm)?128:48);
|
||||||
grid[pos + 48] = CHSV(HUE_PINK, 128, (i<ss)?96:48);
|
grid[pos + 48] = CHSV(HUE_PINK, 128, (i<ss)?96:48);
|
||||||
}
|
}
|
||||||
show();
|
FastLED.show();
|
||||||
|
|
||||||
pause(250 * MILLISECOND);
|
pause(250 * MILLISECOND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
|
||||||
pinMode(RESET_PIN, INPUT_PULLUP);
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
|
||||||
Serial.begin(19200);
|
|
||||||
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(actual, GRIDLEN);
|
|
||||||
// Maybe it's the plexiglass, but for my build, I need to dial back the red
|
|
||||||
//FastLED.setCorrection(0xd0ffff);
|
|
||||||
network_setup(WFM_PASSWORD);
|
|
||||||
|
|
||||||
// Show our mac address, for debugging?
|
|
||||||
FastLED.setBrightness(DAY_BRIGHTNESS);
|
|
||||||
displayMacAddress();
|
|
||||||
sparkle();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
Picker p;
|
Picker p;
|
||||||
|
uint8_t getprob = 4;
|
||||||
|
bool conn = connected();
|
||||||
bool day = true;
|
bool day = true;
|
||||||
|
|
||||||
if (clock_is_set()) {
|
updateTime();
|
||||||
struct tm info;
|
if (timeStatus() == timeSet) {
|
||||||
getLocalTime(&info);
|
int hh = hour();
|
||||||
day = ((info.tm_hour >= DAY_BEGIN) && (info.tm_hour < DAY_END));
|
day = ((hh >= DAY_BEGIN) && (hh < DAY_END));
|
||||||
}
|
}
|
||||||
FastLED.setBrightness(day?DAY_BRIGHTNESS:NIGHT_BRIGHTNESS);
|
FastLED.setBrightness(day?DAY_BRIGHTNESS:NIGHT_BRIGHTNESS);
|
||||||
|
|
||||||
// At night, always display the clock
|
// If we don't yet have net art, try a little harder to get it.
|
||||||
if (!day && clock_is_set()) {
|
if ((NetArtFrames == 0) || !conn) {
|
||||||
displayTimeDigits(day);
|
getprob = 16;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.Pick(4) && clock_is_set()) {
|
if (!day || p.Pick(4)) {
|
||||||
displayTimeDigits(day, 2 * MINUTE);
|
// At night, only ever show the clock
|
||||||
} else if (p.Pick(4) && clock_is_set()) {
|
displayTime(2 * MINUTE);
|
||||||
displayTimeDozenal();
|
} else if (p.Pick(getprob)) {
|
||||||
} else if (p.Pick(4)) {
|
|
||||||
netget();
|
netget();
|
||||||
} else if (day && p.Pick(4)) {
|
} else if (day && p.Pick(4)) {
|
||||||
// These can be hella bright
|
// These can be hella bright
|
||||||
|
|
Loading…
Reference in New Issue