Compare commits

...

9 Commits
1.4 ... master

6 changed files with 137 additions and 108 deletions

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
BOARD = --board adafruit:samd:adafruit_trinket_m0
verify: main.ino
arduino --verify $(BOARD) $<
install: main.ino
arduino --upload $(BOARD) $<

View File

@ -20,32 +20,42 @@ 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
Setup
-----
If the red light on the board is lit,
that means it doesn't know how to connect to the WiFi.
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 "Wall Art xxxxxxxxx".
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
--------------
@ -80,9 +90,6 @@ 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)
@ -90,15 +97,11 @@ I suggest you set it to standard time and pretend it's in sync with the sun.
* There are four pixels around the bottom that move every 5 seconds
Update
------
Updating Firmware
-----------------
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.
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
You will have to reconfigure networking after this.
Philosophy

View File

@ -1,6 +1,8 @@
#include <FastLED.h>
#include <WiFiManager.h>
#include <WiFiManagerTz.h>
#include <esp_wifi.h>
#include <esp_sntp.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include "network.h"
@ -8,13 +10,31 @@
WiFiManager wfm;
void network_reset() {
Serial.println("Resetting network");
wfm.resetSettings();
}
void network_setup(char *password) {
String hostid = String(ESP.getEfuseMac(), HEX);
String hostname = "Wall Art " + hostid;
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) {
String hostname = "WallArt";
WiFiManagerNS::NTP::onTimeAvailable(&on_time_available);
WiFiManagerNS::init(&wfm);
std::vector<const char *> menu = {"wifi", "info", "custom", "param", "sep", "update", "restart", "exit"};
wfm.setMenu(menu);
wfm.setConfigPortalBlocking(false);
wfm.setHostname(hostname);
wfm.autoConnect(hostname.c_str(), password);
@ -24,7 +44,17 @@ bool connected() {
return WiFi.status() == WL_CONNECTED;
}
bool timeConfigured = false;
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) {
wfm.process();
digitalWrite(LED_BUILTIN, !connected());

View File

@ -1,7 +1,11 @@
#pragma once
// Short this to ground to reset the network
#define RESET_PIN 26
void network_reset();
void network_setup(char *password);
bool connected();
void pause(uint32_t dwMs);
void netget(int count);
bool clock_is_set();

View File

@ -1,17 +0,0 @@
#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,18 +1,15 @@
#include <FastLED.h>
#include <FastLED.h>
#include <ArduinoHttpClient.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
#include <NTPClient.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 TIMEZONE TZ_US_Mountain
/*
* The hours when the day begins and ends.
@ -33,48 +30,11 @@
#define ART_PATH "/wallart/wallart.bin"
#define HTTPS_TIMEOUT (2 * SECOND)
#define IMAGE_PULL_MIN_INTERVAL (5 * MINUTE)
#define RESET_PIN 26
CRGB grid[GRIDLEN];
WiFiUDP ntpUDP;
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 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);
@ -203,6 +163,26 @@ void cm5(uint8_t width=0, int cycles=200) {
}
}
void displayMacAddress(int cycles=40) {
uint64_t addr = ESP.getEfuseMac();
for (; cycles > 0; cycles -= 1) {
bool conn = connected();
fill_solid(grid, GRIDLEN, CHSV(conn?HUE_AQUA:HUE_RED, 128, 64));
for (int i = 0; i < 48; i++) {
int pos = i + 8;
grid[pos] = CHSV(HUE_YELLOW, 255, ((addr>>(47-i)) & 1)?255:64);
}
grid[0] = CRGB::Black;
if (!conn && (cycles % 2)) {
grid[1] = CRGB::Black;
}
FastLED.show();
pause(250*MILLISECOND);
}
}
// Art from the network
int NetArtFrames = 0;
CRGB NetArt[8][GRIDLEN];
@ -233,17 +213,25 @@ uint8_t netgetStatus(uint8_t hue) {
void netget(int count=60) {
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 (connected()) {
if (millis() < nextPull) {
// Let's not bombard the server
hue = HUE_ORANGE;
} else if (connected()) {
WiFiClientSecure scli;
nextPull = millis() + IMAGE_PULL_MIN_INTERVAL;
hue = netgetStatus(HUE_AQUA);
scli.setInsecure();
HttpClient https(scli, ART_HOSTNAME, ART_PORT);
do {
if (https.get(ART_PATH) != 0) break;
String path = String(ART_PATH) + "?mac=" + String(ESP.getEfuseMac(), HEX);
Serial.println(path);
if (https.get(path) != 0) break;
hue = netgetStatus(HUE_GREEN);
if (https.skipResponseHeaders() != HTTP_SUCCESS) break;
@ -285,19 +273,22 @@ void spinner(int count=32) {
}
}
void displayTime(unsigned long duration = 20 * SECOND) {
if (timeStatus() != timeSet) return;
void displayTime(unsigned long duration = 20*SECOND) {
if (!clock_is_set()) return;
unsigned long end = millis() + duration;
FastLED.clear();
while (millis() < end) {
updateTime();
int hh = hour();
int mmss = now() % 3600;
struct tm info;
getLocalTime(&info);
int hh = info.tm_hour;
int mmss = (info.tm_min * 60) + info.tm_sec;
uint8_t hue = HUE_YELLOW;
// Top: Hours
if (isPM()) {
if (hh >= 12) {
hue = HUE_ORANGE;
hh -= 12;
}
@ -310,10 +301,10 @@ void displayTime(unsigned long duration = 20 * SECOND) {
// 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);
grid[64 - 7 - 1] = CHSV(HUE_GREEN, 128, (s==1)?96:0);
grid[64 - 15 - 1] = CHSV(HUE_GREEN, 128, (s==2)?96:0);
grid[64 - 8 - 1] = CHSV(HUE_GREEN, 128, (s==3)?96:0);
grid[64 - 0 - 1] = CHSV(HUE_GREEN, 128, (s==4)?96:0);
for (int i = 0; i < 12; i++) {
// Omit first and last position on a row
@ -332,21 +323,30 @@ void displayTime(unsigned long duration = 20 * SECOND) {
}
}
void setup() {
pinMode(RESET_PIN, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(19200);
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(grid, 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?
displayMacAddress();
sparkle();
}
void loop() {
Picker p;
uint8_t getprob = 4;
bool conn = connected();
bool day = true;
switch (timeStatus()) {
case timeNotSet:
case timeNeedsSync:
updateTime();
break;
}
if (timeStatus() == timeSet) {
int hh = hour();
day = ((hh >= DAY_BEGIN) && (hh < DAY_END));
if (clock_is_set()) {
struct tm info;
getLocalTime(&info);
day = ((info.tm_hour >= DAY_BEGIN) && (info.tm_hour < DAY_END));
}
FastLED.setBrightness(day?DAY_BRIGHTNESS:NIGHT_BRIGHTNESS);
@ -355,8 +355,9 @@ void loop() {
getprob = 16;
}
if (!day || p.Pick(4)) {
// At night, only ever show the clock
if (!day && clock_is_set()) {
displayTime();
} else if (p.Pick(4) && clock_is_set()) {
displayTime(2 * MINUTE);
} else if (p.Pick(getprob)) {
netget();