Public Access
Restructure into a single PlatformIO project with nano and esp32 targets
- Shared BROSE9323 driver in lib/, ESPSERIAL selected via build_flags - Nano firmware in src/nano, ESP32 firmware in src/esp32 - Move WiFi/MQTT credentials to gitignored secrets.h (template provided) - Pin home-assistant-integration to 2.1.0 from the registry - Add esp32_usb environment for first flash / recovery - Add READMEs and CLAUDE.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
0e4d39c47b
18 files changed
+1905
No files matched your search
@@ -0,0 +1,45 @@
|
||||
# ESP32: controller
|
||||
|
||||
PlatformIO environments: `esp32` (OTA) and `esp32_usb` (USB)
|
||||
|
||||
The ESP32 decides what is shown on the display. It never touches the panel itself. The
|
||||
BROSE9323 driver is built with `-D ESPSERIAL`, so every GFX call is sent over `Serial`
|
||||
(115200 baud) to the Nano.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `main.cpp` | setup, OTA, clock / power logic, Home Assistant callbacks |
|
||||
| `libs.h` | includes and global objects (display, MQTT, WiFi, pins, timezone) |
|
||||
| `bitmaps.h` | 1-bit bitmaps (Pikachu, thought bubble, Nintendo logo, ...) |
|
||||
| `secrets.example.h` | template for `secrets.h` (WiFi and MQTT credentials, **gitignored**) |
|
||||
|
||||
## Features
|
||||
|
||||
- **OTA updates** through ArduinoOTA. Hostname: `FlipDotDisplay`.
|
||||
- **OTA panic window:** after a crash or watchdog reset, the ESP32 runs only the OTA
|
||||
handler for 15 seconds before it starts the main code. A firmware that crashes on boot
|
||||
can still be replaced over the air.
|
||||
- **NTP time** with timezone `CET-1CEST,M3.5.0,M10.5.0/3` (Europe/Amsterdam).
|
||||
- **Home Assistant** through MQTT (`home-assistant-integration` library) with two switches:
|
||||
- `FlipDotDisplayClock`: clock on, or the Nintendo logo when off
|
||||
- `FlipDotDisplayPower`: turns the display off through the relay
|
||||
- **Relay on GPIO 13** switches the display's power supply. `LOW` turns it on.
|
||||
- **Status LED** (built-in) blinks fast when WiFi is down and slowly when connected. It is
|
||||
off at night or when the display is powered off.
|
||||
|
||||
## Schedule
|
||||
|
||||
| Time | Display |
|
||||
|------|---------|
|
||||
| 07:00 – 17:00 | Pikachu and the clock, updated every minute |
|
||||
| 17:00 – 22:00 | Yawning Pikachu and the clock |
|
||||
| 22:00 – 23:30 | Clock drawn only on the quarter hour; power is off in between |
|
||||
| night | Screen cleared, power off |
|
||||
|
||||
## Adding bitmaps
|
||||
|
||||
Convert an image with [image2cpp](https://javl.github.io/image2cpp/) to "Arduino code",
|
||||
horizontal, 1 bit per pixel. Paste the array into `bitmaps.h` and draw it with
|
||||
`display.drawBitmap(x, y, bitmap, w, h, 1)`. The display is 84 × 19 pixels.
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
// 'Nintendo logo', 84x19px
|
||||
const unsigned char nintendo_bitmap_Nintendo_new[] PROGMEM = {
|
||||
0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x67, 0x8e, 0x70, 0x00,
|
||||
0x70, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x60, 0x67, 0x8e, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00, 0x1c,
|
||||
0x00, 0x60, 0xc7, 0x8e, 0x07, 0x39, 0xfc, 0xfc, 0x73, 0x83, 0xdc, 0x7c, 0x30, 0xc7, 0x4e, 0x77,
|
||||
0xfc, 0x71, 0xee, 0x7f, 0xc7, 0xfc, 0xfe, 0x30, 0xc7, 0x6e, 0x77, 0x8e, 0x73, 0xc7, 0x78, 0xef,
|
||||
0x3d, 0xc7, 0x30, 0xc7, 0x2e, 0x77, 0x0e, 0x73, 0xff, 0x70, 0xef, 0x3d, 0xc7, 0x30, 0xc7, 0x3e,
|
||||
0x77, 0x0e, 0x73, 0xc0, 0x70, 0xef, 0x3d, 0xc7, 0x30, 0xc7, 0x1e, 0x77, 0x0e, 0x73, 0xc7, 0x70,
|
||||
0xef, 0x3d, 0xc7, 0x30, 0xc7, 0x0e, 0x77, 0x0e, 0x71, 0xee, 0x70, 0xe7, 0xfc, 0xfe, 0x30, 0x67,
|
||||
0x0e, 0x77, 0x0e, 0x70, 0xfc, 0x70, 0xe3, 0xdc, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
|
||||
0x00};
|
||||
|
||||
// 'nintendo border', 84x19px
|
||||
const unsigned char nintendo_bitmap_nintendo_border[] PROGMEM = {
|
||||
0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
|
||||
0x00};
|
||||
|
||||
// 'panda', 17x18px
|
||||
const unsigned char nintendo_bitmap_panda_inverted[] PROGMEM = {
|
||||
0xfe, 0x7f, 0x80, 0xfc, 0x3c, 0x80, 0xfc, 0x38, 0x00, 0xdd, 0xfe, 0x00, 0xbb, 0xff, 0x80, 0x73,
|
||||
0xff, 0x80, 0x77, 0x3f, 0x80, 0x76, 0x33, 0x00, 0x76, 0x73, 0x00, 0x33, 0xfb, 0x00, 0x31, 0x8e,
|
||||
0x00, 0x90, 0xdc, 0x00, 0x80, 0xf0, 0x00, 0x80, 0x00, 0x00, 0xc4, 0x78, 0x80, 0xe4, 0x70, 0x80,
|
||||
0xfe, 0x11, 0x80, 0xff, 0x1f, 0x80};
|
||||
const unsigned char nintendo_bitmap_panda[] PROGMEM = {
|
||||
0x01, 0x80, 0x00, 0x03, 0xc3, 0x00, 0x03, 0xc7, 0x80, 0x22, 0x01, 0x80, 0x44, 0x00, 0x00, 0x8c,
|
||||
0x00, 0x00, 0x88, 0xc0, 0x00, 0x89, 0xcc, 0x80, 0x89, 0x8c, 0x80, 0xcc, 0x04, 0x80, 0xce, 0x71,
|
||||
0x80, 0x6f, 0x23, 0x80, 0x7f, 0x0f, 0x80, 0x7f, 0xff, 0x80, 0x3b, 0x87, 0x00, 0x1b, 0x8f, 0x00,
|
||||
0x01, 0xee, 0x00, 0x00, 0xe0, 0x00};
|
||||
|
||||
// 'yawning_pikachu', 35x19px
|
||||
const unsigned char yawning_pikachu_bitmap_yawning_pikachu [] PROGMEM = {
|
||||
0xff, 0xfc, 0x07, 0xff, 0xe0, 0xff, 0xe3, 0xf8, 0xff, 0xe0, 0xfe, 0x1f, 0xff, 0x0f, 0xe0, 0xf9,
|
||||
0xff, 0xff, 0xf3, 0xe0, 0xe7, 0xff, 0xff, 0xfc, 0xe0, 0xdf, 0x7f, 0xff, 0xdf, 0x60, 0x9f, 0x73,
|
||||
0xf9, 0xdf, 0x20, 0x0c, 0xcf, 0xbe, 0x66, 0x00, 0x02, 0xff, 0xff, 0xe8, 0x00, 0xfe, 0xff, 0x1f,
|
||||
0xef, 0xe0, 0xfd, 0x9e, 0x0f, 0x37, 0xe0, 0xfd, 0x0e, 0xee, 0x17, 0xe0, 0xfd, 0x0e, 0xee, 0x17,
|
||||
0xe0, 0xfd, 0x9e, 0xe3, 0x37, 0xe0, 0xfd, 0xff, 0x0d, 0xf7, 0xe0, 0xfe, 0xff, 0xee, 0xef, 0xe0,
|
||||
0xfe, 0xff, 0xf7, 0x6f, 0xe0, 0xfe, 0x7f, 0xfb, 0x8f, 0xe0, 0xfe, 0xfe, 0x0b, 0xaf, 0xe0
|
||||
};
|
||||
|
||||
// 'yawning_pikachu_black_border', 35x19px
|
||||
const unsigned char yawning_pikachu_bitmap_yawning_pikachu_black_border [] PROGMEM = {
|
||||
0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x07,
|
||||
0xff, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x7f, 0xff, 0xdf, 0x80, 0x7f, 0x73,
|
||||
0xf9, 0xdf, 0xc0, 0xfe, 0xcf, 0xbe, 0x6f, 0xe0, 0xfd, 0xff, 0xff, 0xf7, 0xe0, 0x01, 0xff, 0x1f,
|
||||
0xf0, 0x00, 0x03, 0x9e, 0x0f, 0x38, 0x00, 0x03, 0x0e, 0xee, 0x18, 0x00, 0x03, 0x0e, 0xee, 0x18,
|
||||
0x00, 0x03, 0x9e, 0xe3, 0x38, 0x00, 0x03, 0xff, 0x0d, 0xf8, 0x00, 0x01, 0xff, 0xee, 0xf0, 0x00,
|
||||
0x01, 0xff, 0xf7, 0x70, 0x00, 0x00, 0x7f, 0xfb, 0x80, 0x00, 0x01, 0xfe, 0x0b, 0xb0, 0x00
|
||||
};
|
||||
|
||||
// 'clock thought bubble', 46x19px
|
||||
const unsigned char clock_thought_bubble_bitmap_clock_thought_bubble [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||
0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x3f,
|
||||
0xff, 0xff, 0xff, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
// 'pikachu_black_border', 35x19px
|
||||
const unsigned char pikachu_bitmap_pikachu_black_border [] PROGMEM = {
|
||||
0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x07,
|
||||
0xff, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x73, 0xf9, 0xdf, 0x80, 0x7f, 0x69,
|
||||
0xf4, 0xdf, 0xc0, 0xfe, 0xe1, 0xf0, 0xef, 0xe0, 0xfd, 0xf3, 0xf9, 0xf7, 0xe0, 0x01, 0xff, 0xbf,
|
||||
0xf0, 0x00, 0x03, 0x9f, 0xff, 0x38, 0x00, 0x03, 0x0f, 0xfe, 0x18, 0x00, 0x03, 0x0f, 0xfe, 0x18,
|
||||
0x00, 0x03, 0x9d, 0xb7, 0x38, 0x00, 0x03, 0xfe, 0x4f, 0xf8, 0x00, 0x01, 0xff, 0xff, 0xf0, 0x00,
|
||||
0x01, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x01, 0xfe, 0x0f, 0xf0, 0x00
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <Arduino.h>
|
||||
#include "BROSE9323.h"
|
||||
#include "bitmaps.h"
|
||||
|
||||
// WiFi / MQTT credentials (gitignored, copy secrets.example.h -> secrets.h)
|
||||
#if __has_include("secrets.h")
|
||||
#include "secrets.h"
|
||||
#else
|
||||
#error "Missing src/esp32/secrets.h - copy src/esp32/secrets.example.h to secrets.h and fill it in"
|
||||
#endif
|
||||
|
||||
// fixes
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
// fonts
|
||||
#include <Fonts/FreeSerif12pt7b.h>
|
||||
#include <Fonts/FreeSerifItalic12pt7b.h>
|
||||
// #include <Font2x5FixedMonoNum.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
// wdt?
|
||||
#include <esp_task_wdt.h>
|
||||
#include <rom/rtc.h>
|
||||
|
||||
// hassio
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x34, 0x86, 0x5d, 0x3a, 0x08, 0x28};
|
||||
|
||||
// EthernetClient client;
|
||||
WiFiClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
HASwitch flipDotDisplay("FlipDotDisplayClock");
|
||||
bool hassioDisplayStatus = true;
|
||||
|
||||
HASwitch powerSwitch("FlipDotDisplayPower");
|
||||
bool power = true;
|
||||
|
||||
const int led1 = BUILTIN_LED; // Pin of the LED
|
||||
const int relay = 13; // Pin of the relay
|
||||
|
||||
int hour = 0;
|
||||
int minute = 0;
|
||||
int second = 0;
|
||||
|
||||
//
|
||||
WiFiMulti wifiMulti;
|
||||
|
||||
const char *ssid = WIFI_SSID;
|
||||
const char *password = WIFI_PASSWORD;
|
||||
|
||||
BROSE9323 display(84, 19, 28);
|
||||
|
||||
uint32_t next_display;
|
||||
|
||||
// ntp
|
||||
#define MYTZ "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
const char *ntpServer = "pool.ntp.org";
|
||||
const long gmtOffset_sec = 3600;
|
||||
const int daylightOffset_sec = 3600;
|
||||
// const int daylightOffset_sec = 0;
|
||||
|
||||
bool wifiStatus = false;
|
||||
bool otaPanicMode = true;
|
||||
@@ -0,0 +1,514 @@
|
||||
#include "libs.h"
|
||||
|
||||
void toggleLED(void *parameter);
|
||||
|
||||
uint32_t power_off_delay;
|
||||
int triggered_power_off = false;
|
||||
int turn_hassio_power_off = false;
|
||||
void turnPowerOff(uint32_t delay = 30000, bool _turn_hassio_power_off = false)
|
||||
{
|
||||
if(triggered_power_off == false)
|
||||
{
|
||||
// start new countdown
|
||||
turn_hassio_power_off = _turn_hassio_power_off;
|
||||
power_off_delay = millis() + delay;
|
||||
|
||||
triggered_power_off = true;
|
||||
}
|
||||
}
|
||||
|
||||
void updatePower()
|
||||
{
|
||||
if(millis() > power_off_delay && triggered_power_off == true)
|
||||
{
|
||||
// finished countdown
|
||||
triggered_power_off = false;
|
||||
|
||||
// OFF
|
||||
digitalWrite(relay, HIGH);
|
||||
|
||||
if(turn_hassio_power_off)
|
||||
{
|
||||
power = false;
|
||||
powerSwitch.setState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch *s)
|
||||
{
|
||||
hassioDisplayStatus = (state ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void onPowerSwitchStateChanged(bool state, HASwitch *s)
|
||||
{
|
||||
power = (state ? HIGH : LOW);
|
||||
|
||||
if(power == false)
|
||||
{
|
||||
display.fillScreen(0);
|
||||
display.display();
|
||||
|
||||
delay(1000);
|
||||
|
||||
turnPowerOff(0);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void print_reset_reason(RESET_REASON reason)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case POWERON_RESET:
|
||||
Serial.println("POWERON_RESET");
|
||||
break; /**<1, Vbat power on reset*/
|
||||
case 3:
|
||||
Serial.println("SW_RESET");
|
||||
break; /**<3, Software reset digital core*/
|
||||
case 4:
|
||||
Serial.println("OWDT_RESET");
|
||||
break; /**<4, Legacy watch dog reset digital core*/
|
||||
case 5:
|
||||
Serial.println("DEEPSLEEP_RESET");
|
||||
break; /**<5, Deep Sleep reset digital core*/
|
||||
case 6:
|
||||
Serial.println("SDIO_RESET");
|
||||
break; /**<6, Reset by SLC module, reset digital core*/
|
||||
case 7:
|
||||
Serial.println("TG0WDT_SYS_RESET");
|
||||
break; /**<7, Timer Group0 Watch dog reset digital core*/
|
||||
case 8:
|
||||
Serial.println("TG1WDT_SYS_RESET");
|
||||
break; /**<8, Timer Group1 Watch dog reset digital core*/
|
||||
case 9:
|
||||
Serial.println("RTCWDT_SYS_RESET");
|
||||
break; /**<9, RTC Watch dog Reset digital core*/
|
||||
case 10:
|
||||
Serial.println("INTRUSION_RESET");
|
||||
break; /**<10, Instrusion tested to reset CPU*/
|
||||
case 11:
|
||||
Serial.println("TGWDT_CPU_RESET");
|
||||
break; /**<11, Time Group reset CPU*/
|
||||
case SW_CPU_RESET:
|
||||
Serial.println("SW_CPU_RESET");
|
||||
break; /**<12, Software reset CPU*/
|
||||
case 13:
|
||||
Serial.println("RTCWDT_CPU_RESET");
|
||||
break; /**<13, RTC Watch dog Reset CPU*/
|
||||
case EXT_CPU_RESET:
|
||||
Serial.println("EXT_CPU_RESET");
|
||||
break; /**<14, for APP CPU, reseted by PRO CPU*/
|
||||
case 15:
|
||||
Serial.println("RTCWDT_BROWN_OUT_RESET");
|
||||
break; /**<15, Reset when the vdd voltage is not stable*/
|
||||
case 16:
|
||||
Serial.println("RTCWDT_RTC_RESET");
|
||||
break; /**<16, RTC Watch dog reset digital core and rtc module*/
|
||||
default:
|
||||
Serial.println("NO_MEAN");
|
||||
}
|
||||
}
|
||||
|
||||
bool checkResetStatus()
|
||||
{
|
||||
Serial.println("CPU0 reset reason: ");
|
||||
RESET_REASON reason0 = rtc_get_reset_reason(0);
|
||||
print_reset_reason(reason0);
|
||||
|
||||
// if(reason0 == SW_CPU_RESET)
|
||||
// return true;
|
||||
|
||||
Serial.println("CPU1 reset reason: ");
|
||||
RESET_REASON reason1 = rtc_get_reset_reason(1);
|
||||
print_reset_reason(reason1);
|
||||
|
||||
// if(reason1 == SW_CPU_RESET)
|
||||
// return true;
|
||||
|
||||
if (reason0 != POWERON_RESET && reason1 != EXT_CPU_RESET)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
otaPanicMode = true;
|
||||
pinMode(led1, OUTPUT);
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
WiFi.setHostname("FlipDotDisplay");
|
||||
wifiMulti.addAP(ssid, password);
|
||||
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
ArduinoOTA
|
||||
.onStart([]()
|
||||
{
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
// USE_SERIAL.write(0x0C); //CLEAR SCREEN CMD
|
||||
// Serial.println("Start updating " + type);
|
||||
|
||||
// if (!otaPanicMode)
|
||||
// {
|
||||
// display.begin(&Serial);
|
||||
// display.fillScreen(0);
|
||||
// display.setTextSize(1);
|
||||
// display.setCursor(0, 0);
|
||||
// display.print("ota starting!");
|
||||
// display.display();
|
||||
// }
|
||||
})
|
||||
.onEnd([]()
|
||||
{
|
||||
// USE_SERIAL.write(0x0C); //CLEAR SCREEN CMD
|
||||
// Serial.println("\nEnd");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total)
|
||||
{
|
||||
// USE_SERIAL.write(0x0C); //CLEAR SCREEN CMD
|
||||
// USE_SERIAL.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
// if (!otaPanicMode)
|
||||
// {
|
||||
// display.begin(&Serial);
|
||||
// display.fillScreen(0);
|
||||
// display.setTextSize(1);
|
||||
// display.setCursor(0, 0);
|
||||
// display.printf("ota: %u%%\r", (progress / (total / 100)));
|
||||
// display.display();
|
||||
// }
|
||||
})
|
||||
.onError([](ota_error_t error)
|
||||
{
|
||||
// // USE_SERIAL.write(0x0C); //CLEAR SCREEN CMD
|
||||
// Serial.printf("Error[%u]: ", error);
|
||||
// if (error == OTA_AUTH_ERROR)
|
||||
// Serial.println("Auth Failed");
|
||||
// else if (error == OTA_BEGIN_ERROR)
|
||||
// Serial.println("Begin Failed");
|
||||
// else if (error == OTA_CONNECT_ERROR)
|
||||
// Serial.println("Connect Failed");
|
||||
// else if (error == OTA_RECEIVE_ERROR)
|
||||
// Serial.println("Receive Failed");
|
||||
// else if (error == OTA_END_ERROR)
|
||||
// Serial.println("End Failed");
|
||||
});
|
||||
|
||||
ArduinoOTA.begin();
|
||||
|
||||
do
|
||||
{
|
||||
ArduinoOTA.handle();
|
||||
|
||||
digitalWrite(led1, HIGH);
|
||||
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// Turn the LED off
|
||||
digitalWrite(led1, LOW);
|
||||
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
} while (checkResetStatus() && millis() < 1000 * 15);
|
||||
otaPanicMode = false;
|
||||
|
||||
pinMode(relay, OUTPUT);
|
||||
digitalWrite(relay, LOW);
|
||||
|
||||
delay(4000);
|
||||
|
||||
display.begin(&Serial);
|
||||
// display.setTiming(200);
|
||||
// display.setTiming(180);
|
||||
// display.setTiming(220); // works best so far
|
||||
display.setTiming(225);
|
||||
|
||||
// delay(4000);
|
||||
display.fillScreen(0);
|
||||
display.setCursor(0, 0);
|
||||
display.setTextSize(1);
|
||||
display.println("Connecting...");
|
||||
display.display();
|
||||
|
||||
// display.setFont(&FreeSerif12pt7b);
|
||||
// display.setFont(&FreeSerifItalic12pt7b);
|
||||
|
||||
//init and get the time
|
||||
// configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
configTzTime(MYTZ, ntpServer);
|
||||
|
||||
xTaskCreate(
|
||||
toggleLED, // Function that should be called
|
||||
"Toggle LED", // Name of the task (for debugging)
|
||||
1000, // Stack size (bytes)
|
||||
NULL, // Parameter to pass
|
||||
1, // Task priority
|
||||
NULL // Task handle
|
||||
);
|
||||
|
||||
next_display = millis() / 1000 * 1000;
|
||||
|
||||
// // TEST CODE
|
||||
// do{
|
||||
// ArduinoOTA.handle();
|
||||
|
||||
// digitalWrite(led1, HIGH);
|
||||
|
||||
// vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// // Turn the LED off
|
||||
// digitalWrite(led1, LOW);
|
||||
|
||||
// vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// }while(true);
|
||||
|
||||
// hassio
|
||||
// you don't need to verify return status
|
||||
// client.begin(mac);
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("FlipDotDisplay");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// set icon (optional)
|
||||
flipDotDisplay.setIcon("mdi:lightbulb");
|
||||
flipDotDisplay.setName("FlipDot Display Clock");
|
||||
|
||||
// handle switch state
|
||||
flipDotDisplay.onCommand(onSwitchStateChanged);
|
||||
|
||||
|
||||
powerSwitch.setIcon("mdi:lightbulb");
|
||||
powerSwitch.setName("FlipDot Display Power");
|
||||
powerSwitch.onCommand(onPowerSwitchStateChanged);
|
||||
|
||||
mqtt.begin(BROKER_ADDR, MQTT_USER, MQTT_PASSWORD);
|
||||
|
||||
display.fillScreen(0);
|
||||
display.display();
|
||||
|
||||
hour = millis() / 1000 / 60 / 60;
|
||||
minute = (millis() / 1000 / 60) % 60;
|
||||
second = (millis() / 1000 % 60);
|
||||
}
|
||||
|
||||
void toggleLED(void *parameter)
|
||||
{
|
||||
for (;;)
|
||||
{ // infinite loop
|
||||
if((power == false) || (!(hour >= 7 && hour < 22)))
|
||||
{
|
||||
// OFF
|
||||
digitalWrite(led1, LOW);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turn the LED on
|
||||
digitalWrite(led1, HIGH);
|
||||
|
||||
if (wifiStatus)
|
||||
{
|
||||
// Pause the task for 500ms
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
}
|
||||
else
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
// Turn the LED off
|
||||
digitalWrite(led1, LOW);
|
||||
|
||||
if (wifiStatus)
|
||||
{
|
||||
// Pause the task again for 500ms
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
}
|
||||
else
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
#define DEG2RAD 0.0174532925
|
||||
void drawRotatedBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, bool color, uint16_t angle)
|
||||
{
|
||||
int16_t newx, newy;
|
||||
uint8_t data = 0;
|
||||
|
||||
float cosa = cos(angle * DEG2RAD), sina = sin(angle * DEG2RAD);
|
||||
|
||||
x = x - ((w * cosa / 2) - (h * sina / 2));
|
||||
y = y - ((h * cosa / 2) + (w * sina / 2));
|
||||
|
||||
for (int16_t j = 0; j < h; j++)
|
||||
{
|
||||
for (int16_t i = 0; i < w; i++)
|
||||
{
|
||||
if ((j * w + i) & 7)
|
||||
data <<= 1;
|
||||
else
|
||||
data = pgm_read_byte(bitmap++);
|
||||
|
||||
newx = 0.5 + x + ((i * cosa) - (j * sina));
|
||||
newy = 0.5 + y + ((j * cosa) + (i * sina));
|
||||
|
||||
if (data & 0x80)
|
||||
display.drawPixel(newx, newy, color);
|
||||
//else display.drawPixel(newx, newy, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void turnPowerOn(bool overide = false)
|
||||
{
|
||||
if(power == true || overide == true)
|
||||
{
|
||||
// ON
|
||||
digitalWrite(relay, LOW);
|
||||
delay(3000);
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
ArduinoOTA.handle();
|
||||
mqtt.loop();
|
||||
updatePower();
|
||||
|
||||
wifiStatus = WiFi.waitForConnectResult() == WL_CONNECTED;
|
||||
|
||||
if(next_display < millis())
|
||||
{
|
||||
if (hassioDisplayStatus)
|
||||
// if (false)
|
||||
{
|
||||
char t[20];
|
||||
char timeStringBuff[50]; //50 chars should be enough
|
||||
|
||||
struct tm timeinfo;
|
||||
if (!getLocalTime(&timeinfo))
|
||||
{
|
||||
// printFromStart("Failed to get time");
|
||||
// return;
|
||||
|
||||
// sprintf(t, "%02d:%02d:%02d\0", millis() / 1000 / 60 / 60, (millis() / 1000 / 60) % 60, (millis() / 1000 % 60));
|
||||
// sprintf(t, "%02d%02d%02d\0", millis() / 1000 / 60 / 60, (millis() / 1000 / 60) % 60, (millis() / 1000 % 60));
|
||||
}
|
||||
else
|
||||
{
|
||||
// const char *format = "%H:%M:%S";
|
||||
const char *format = "%H:%M";
|
||||
|
||||
// strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
|
||||
strftime(timeStringBuff, sizeof(timeStringBuff), format, &timeinfo);
|
||||
String asString(timeStringBuff);
|
||||
|
||||
hour = timeinfo.tm_hour;
|
||||
minute = timeinfo.tm_min;
|
||||
second = timeinfo.tm_sec;
|
||||
}
|
||||
|
||||
// display.setFont(&FreeSerif12pt7b);
|
||||
|
||||
// if ((millis() / 1000 % 60) <= 30)
|
||||
// display.setFont(&FreeSerif12pt7b);
|
||||
// else
|
||||
// display.setFont(&FreeSerifItalic12pt7b);
|
||||
|
||||
if(hour <= 7 || (hour >= 23 && minute > 30))
|
||||
{
|
||||
display.fillScreen(0);
|
||||
display.display();
|
||||
|
||||
turnPowerOff();
|
||||
}
|
||||
// show time between 7 and 23 with updates every minute, and after that; every 15 minutes.
|
||||
else if((hour >= 7 && hour < 22) || (minute % 15 == 0))
|
||||
// else if((hour >= 7 && hour < 19) || (minute % 5 == 0))
|
||||
// else if(true)
|
||||
{
|
||||
turnPowerOn();
|
||||
|
||||
int xoffset = 3;
|
||||
|
||||
display.fillScreen(0);
|
||||
// display.setTextSize(2);
|
||||
display.setTextSize(1);
|
||||
// display.setCursor(7, 3);
|
||||
display.setCursor(xoffset + 0, 19 - 2);
|
||||
display.setTextWrap(false);
|
||||
// display.print(t);
|
||||
|
||||
// if(minute % 2 == 0)
|
||||
if(hour >= 17 || hour <= 6)
|
||||
display.drawBitmap(xoffset + 0, 0, yawning_pikachu_bitmap_yawning_pikachu_black_border, 35, 19, 1);
|
||||
else
|
||||
display.drawBitmap(xoffset + 0, 0, pikachu_bitmap_pikachu_black_border, 35, 19, 1);
|
||||
|
||||
display.drawBitmap(xoffset + 35, 0, clock_thought_bubble_bitmap_clock_thought_bubble, 46, 19, 1);
|
||||
|
||||
display.setCursor(xoffset + 43, (19/2) - 5);
|
||||
display.print(timeStringBuff);
|
||||
|
||||
display.display();
|
||||
}
|
||||
else
|
||||
{
|
||||
turnPowerOff();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
display.fillScreen(0);
|
||||
display.drawBitmap(0, 0, nintendo_bitmap_Nintendo_new, 84, 19, 1);
|
||||
|
||||
// display.drawBitmap(0, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
|
||||
// display.drawBitmap(18, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
|
||||
// display.drawBitmap(18*2, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
|
||||
// display.drawBitmap(18*3, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
|
||||
// display.drawBitmap(18*4, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
|
||||
|
||||
// drawRotatedBitmap(0, 0, nintendo_bitmap_Nintendo_new, 17, 18, 1, 90);
|
||||
|
||||
// Text inside border.
|
||||
// display.drawBitmap(0, 0, nintendo_bitmap_nintendo_border, 84, 19, 1);
|
||||
// display.setFont();
|
||||
// display.setTextSize(1);
|
||||
// display.setTextWrap(false);
|
||||
// display.setCursor(5, 4);
|
||||
// display.print("Nintendo");
|
||||
|
||||
// display.setFont();
|
||||
// // display.setTextSize(2);
|
||||
// display.setTextSize(1);
|
||||
// display.setCursor(0, 0);
|
||||
// display.setTextWrap(true);
|
||||
// // display.print(t);
|
||||
// display.print("Carla & Jan Kragt");
|
||||
|
||||
display.display();
|
||||
|
||||
turnPowerOff();
|
||||
}
|
||||
|
||||
next_display += 1000;
|
||||
// next_display += 500;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copy this file to secrets.h and fill in your own values.
|
||||
// secrets.h is gitignored so credentials never end up in the repository.
|
||||
#pragma once
|
||||
|
||||
#define WIFI_SSID "your-wifi-ssid"
|
||||
#define WIFI_PASSWORD "your-wifi-password"
|
||||
|
||||
// Home Assistant MQTT broker
|
||||
#define BROKER_ADDR IPAddress(192, 168, 1, 10)
|
||||
#define MQTT_USER "mqtt-user"
|
||||
#define MQTT_PASSWORD "mqtt-password"
|
||||
Reference in new issue
Block a user