mirror of https://github.com/sqfmi/Watchy.git
Merge be185cbd54
into 342eb48a49
commit
5848cd578d
126
src/Display.cpp
126
src/Display.cpp
|
@ -15,11 +15,44 @@
|
|||
// Link: https://github.com/sqfmi/Watchy
|
||||
|
||||
#include "Display.h"
|
||||
#include "config.h"
|
||||
|
||||
WatchyDisplay::WatchyDisplay(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
|
||||
GxEPD2_EPD(cs, dc, rst, busy, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
|
||||
RTC_DATA_ATTR bool displayFullInit = true;
|
||||
|
||||
void WatchyDisplay::busyCallback(const void *) {
|
||||
gpio_wakeup_enable((gpio_num_t)DISPLAY_BUSY, GPIO_INTR_LOW_LEVEL);
|
||||
esp_sleep_enable_gpio_wakeup();
|
||||
esp_light_sleep_start();
|
||||
}
|
||||
|
||||
WatchyDisplay::WatchyDisplay() :
|
||||
GxEPD2_EPD(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
|
||||
{
|
||||
selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); // Set SPI to 20Mhz (default is 4Mhz)
|
||||
// Setup callback and SPI by default
|
||||
selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0));
|
||||
setBusyCallback(busyCallback);
|
||||
}
|
||||
|
||||
void WatchyDisplay::initWatchy() {
|
||||
// Watchy default initialization
|
||||
init(0, displayFullInit, 2, true);
|
||||
}
|
||||
|
||||
void WatchyDisplay::asyncPowerOn() {
|
||||
// This is expensive if unused
|
||||
if (!waitingPowerOn && !_hibernating) {
|
||||
_InitDisplay();
|
||||
_PowerOnAsync();
|
||||
}
|
||||
}
|
||||
|
||||
void WatchyDisplay::setDarkBorder(bool dark) {
|
||||
if (_hibernating) return;
|
||||
darkBorder = dark;
|
||||
_startTransfer();
|
||||
_transferCommand(0x3C); // BorderWavefrom
|
||||
_transfer(dark ? 0x02 : 0x05);
|
||||
_endTransfer();
|
||||
}
|
||||
|
||||
void WatchyDisplay::clearScreen(uint8_t value)
|
||||
|
@ -324,31 +357,52 @@ void WatchyDisplay::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint1
|
|||
_endTransfer();
|
||||
}
|
||||
|
||||
void WatchyDisplay::_PowerOnAsync()
|
||||
{
|
||||
if (_power_is_on)
|
||||
return;
|
||||
_startTransfer();
|
||||
_transferCommand(0x22);
|
||||
_transfer(0xf8);
|
||||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
waitingPowerOn = true;
|
||||
_power_is_on = true;
|
||||
}
|
||||
|
||||
void WatchyDisplay::_PowerOn()
|
||||
{
|
||||
if (!_power_is_on)
|
||||
if (waitingPowerOn)
|
||||
{
|
||||
_startTransfer();
|
||||
_transferCommand(0x22);
|
||||
_transfer(0xf8);
|
||||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
waitingPowerOn = false;
|
||||
_waitWhileBusy("_PowerOn", power_on_time);
|
||||
}
|
||||
if (_power_is_on)
|
||||
return;
|
||||
_startTransfer();
|
||||
_transferCommand(0x22);
|
||||
_transfer(0xf8);
|
||||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
_waitWhileBusy("_PowerOn", power_on_time);
|
||||
_power_is_on = true;
|
||||
}
|
||||
|
||||
void WatchyDisplay::_PowerOff()
|
||||
{
|
||||
if (_power_is_on)
|
||||
if (waitingPowerOn)
|
||||
{
|
||||
_startTransfer();
|
||||
_transferCommand(0x22);
|
||||
_transfer(0x83);
|
||||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
_waitWhileBusy("_PowerOff", power_off_time);
|
||||
waitingPowerOn = false;
|
||||
_waitWhileBusy("_PowerOn", power_on_time);
|
||||
}
|
||||
if (!_power_is_on)
|
||||
return;
|
||||
_startTransfer();
|
||||
_transferCommand(0x22);
|
||||
_transfer(0x83);
|
||||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
_waitWhileBusy("_PowerOff", power_off_time);
|
||||
_power_is_on = false;
|
||||
_using_partial_mode = false;
|
||||
}
|
||||
|
@ -356,23 +410,54 @@ void WatchyDisplay::_PowerOff()
|
|||
void WatchyDisplay::_InitDisplay()
|
||||
{
|
||||
if (_hibernating) _reset();
|
||||
_writeCommand(0x12); // soft reset
|
||||
_waitWhileBusy("_SoftReset", 10); // 10ms max according to specs
|
||||
|
||||
// No need to soft reset, the Display goes to same state after hard reset
|
||||
// _writeCommand(0x12); // soft reset
|
||||
// _waitWhileBusy("_SoftReset", 10); // 10ms max according to specs*/
|
||||
|
||||
_startTransfer();
|
||||
_transferCommand(0x01); // Driver output control
|
||||
_transfer(0xC7);
|
||||
_transfer(0x00);
|
||||
_transfer(0x00);
|
||||
_transferCommand(0x3C); // BorderWavefrom
|
||||
_transfer(darkBorder ? 0x02 : 0x05);
|
||||
|
||||
if (reduceBoosterTime) {
|
||||
// SSD1675B controller datasheet
|
||||
_transferCommand(0x0C); // BOOSTER_SOFT_START_CONTROL
|
||||
// Set the driving strength of GDR for all phases to maximun 0b111 -> 0xF
|
||||
// Set the minimum off time of GDR to minimum 0x4 (values below sould be same)
|
||||
_transfer(0xF4); // Phase1 Default value 0x8B
|
||||
_transfer(0xF4); // Phase2 Default value 0x9C
|
||||
_transfer(0xF4); // Phase3 Default value 0x96
|
||||
_transfer(0x00); // Duration of phases, Default 0xF = 0b00 11 11 (40ms Phase 1/2, 10ms Phase 3)
|
||||
}
|
||||
|
||||
_transferCommand(0x18); // Read built-in temperature sensor
|
||||
_transfer(0x80);
|
||||
_endTransfer();
|
||||
|
||||
setDarkBorder(darkBorder);
|
||||
|
||||
_setPartialRamArea(0, 0, WIDTH, HEIGHT);
|
||||
}
|
||||
|
||||
void WatchyDisplay::_reset()
|
||||
{
|
||||
// Call default method if not configured the same way
|
||||
if (_rst < 0 || !_pulldown_rst_mode) {
|
||||
GxEPD2_EPD::_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
digitalWrite(_rst, LOW);
|
||||
pinMode(_rst, OUTPUT);
|
||||
delay(_reset_duration);
|
||||
pinMode(_rst, INPUT_PULLUP);
|
||||
// Tested calling _powerOn() inmediately, and works ok, no need to sleep
|
||||
// delay(_reset_duration > 10 ? _reset_duration : 0);
|
||||
_hibernating = false;
|
||||
}
|
||||
|
||||
void WatchyDisplay::_Init_Full()
|
||||
{
|
||||
_InitDisplay();
|
||||
|
@ -395,6 +480,7 @@ void WatchyDisplay::_Update_Full()
|
|||
_transferCommand(0x20);
|
||||
_endTransfer();
|
||||
_waitWhileBusy("_Update_Full", full_refresh_time);
|
||||
displayFullInit = false;
|
||||
}
|
||||
|
||||
void WatchyDisplay::_Update_Part()
|
||||
|
|
|
@ -34,7 +34,13 @@ class WatchyDisplay : public GxEPD2_EPD
|
|||
static const uint16_t full_refresh_time = 2600; // ms, e.g. 2509602us
|
||||
static const uint16_t partial_refresh_time = 500; // ms, e.g. 457282us
|
||||
// constructor
|
||||
WatchyDisplay(int16_t cs, int16_t dc, int16_t rst, int16_t busy);
|
||||
WatchyDisplay();
|
||||
void initWatchy();
|
||||
void setDarkBorder(bool darkBorder);
|
||||
void asyncPowerOn();
|
||||
void _PowerOnAsync();
|
||||
bool waitingPowerOn = false;
|
||||
static void busyCallback(const void *);
|
||||
// methods (virtual)
|
||||
// Support for Bitmaps (Sprites) to Controller Buffer and to Screen
|
||||
void clearScreen(uint8_t value = 0xFF); // init controller memory and screen (default white)
|
||||
|
@ -69,6 +75,8 @@ class WatchyDisplay : public GxEPD2_EPD
|
|||
void hibernate(); // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
|
||||
|
||||
bool darkBorder = false; // adds a dark border outside the normal screen area
|
||||
|
||||
static constexpr bool reduceBoosterTime = true; // Saves ~200ms
|
||||
private:
|
||||
void _writeScreenBuffer(uint8_t command, uint8_t value);
|
||||
void _writeImage(uint8_t command, const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
|
||||
|
@ -83,5 +91,7 @@ class WatchyDisplay : public GxEPD2_EPD
|
|||
void _Update_Full();
|
||||
void _Update_Part();
|
||||
|
||||
void _reset();
|
||||
|
||||
void _transferCommand(uint8_t command);
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
WatchyRTC Watchy::RTC;
|
||||
GxEPD2_BW<WatchyDisplay, WatchyDisplay::HEIGHT> Watchy::display(
|
||||
WatchyDisplay(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY));
|
||||
WatchyDisplay{});
|
||||
|
||||
RTC_DATA_ATTR int guiState;
|
||||
RTC_DATA_ATTR int menuIndex;
|
||||
|
@ -11,7 +11,6 @@ RTC_DATA_ATTR bool WIFI_CONFIGURED;
|
|||
RTC_DATA_ATTR bool BLE_CONFIGURED;
|
||||
RTC_DATA_ATTR weatherData currentWeather;
|
||||
RTC_DATA_ATTR int weatherIntervalCounter = -1;
|
||||
RTC_DATA_ATTR bool displayFullInit = true;
|
||||
RTC_DATA_ATTR long gmtOffset = 0;
|
||||
RTC_DATA_ATTR bool alreadyInMenu = true;
|
||||
RTC_DATA_ATTR tmElements_t bootTime;
|
||||
|
@ -22,11 +21,8 @@ void Watchy::init(String datetime) {
|
|||
Wire.begin(SDA, SCL); // init i2c
|
||||
RTC.init();
|
||||
|
||||
// Init the display here for all cases, if unused, it will do nothing
|
||||
display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); // Set SPI to 20Mhz (default is 4Mhz)
|
||||
display.init(0, displayFullInit, 10,
|
||||
true); // 10ms by spec, and fast pulldown reset
|
||||
display.epd2.setBusyCallback(displayBusyCallback);
|
||||
// Init the display since is almost sure we will use it
|
||||
display.epd2.initWatchy();
|
||||
|
||||
switch (wakeup_reason) {
|
||||
case ESP_SLEEP_WAKEUP_EXT0: // RTC Alarm
|
||||
|
@ -63,22 +59,14 @@ void Watchy::init(String datetime) {
|
|||
RTC.read(bootTime);
|
||||
showWatchFace(false); // full update on reset
|
||||
vibMotor(75, 4);
|
||||
// For some reason, seems to be enabled on first boot
|
||||
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
|
||||
break;
|
||||
}
|
||||
deepSleep();
|
||||
}
|
||||
|
||||
void Watchy::displayBusyCallback(const void *) {
|
||||
gpio_wakeup_enable((gpio_num_t)DISPLAY_BUSY, GPIO_INTR_LOW_LEVEL);
|
||||
esp_sleep_enable_gpio_wakeup();
|
||||
esp_light_sleep_start();
|
||||
}
|
||||
|
||||
void Watchy::deepSleep() {
|
||||
display.hibernate();
|
||||
if (displayFullInit) // For some reason, seems to be enabled on first boot
|
||||
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
|
||||
displayFullInit = false; // Notify not to init it again
|
||||
RTC.clearAlarm(); // resets the alarm flag in the RTC
|
||||
|
||||
// Set GPIOs 0-39 to input to avoid power leaking out
|
||||
|
@ -606,6 +594,8 @@ void Watchy::showAccelerometer() {
|
|||
|
||||
void Watchy::showWatchFace(bool partialRefresh) {
|
||||
display.setFullWindow();
|
||||
// At this point it is sure we are going to update
|
||||
display.epd2.asyncPowerOn();
|
||||
drawWatchFace();
|
||||
display.display(partialRefresh); // partial refresh
|
||||
guiState = WATCHFACE_STATE;
|
||||
|
@ -834,8 +824,8 @@ void Watchy::setupWifi() {
|
|||
// turn off radios
|
||||
WiFi.mode(WIFI_OFF);
|
||||
btStop();
|
||||
display.epd2.setBusyCallback(displayBusyCallback); // enable lightsleep on
|
||||
// busy
|
||||
// enable lightsleep on busy
|
||||
display.epd2.setBusyCallback(WatchyDisplay::busyCallback);
|
||||
guiState = APP_STATE;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ public:
|
|||
explicit Watchy(const watchySettings &s) : settings(s) {} // constructor
|
||||
void init(String datetime = "");
|
||||
void deepSleep();
|
||||
static void displayBusyCallback(const void *);
|
||||
float getBatteryVoltage();
|
||||
void vibMotor(uint8_t intervalMs = 100, uint8_t length = 20);
|
||||
|
||||
|
|
Loading…
Reference in New Issue