mirror of
https://github.com/usetrmnl/bb_epaper.git
synced 2026-04-29 13:43:26 -07:00
209 lines
6.8 KiB
Arduino
209 lines
6.8 KiB
Arduino
//
|
|
// epaper clock on the humble ATmega328
|
|
//
|
|
#include <Wire.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/power.h>
|
|
#include <avr/sleep.h>
|
|
#include <avr/boot.h>
|
|
#include <avr/wdt.h>
|
|
|
|
#include "Roboto_Black_38.h"
|
|
#include "Roboto_Black_78.h"
|
|
#include <bb_epaper.h>
|
|
BBEPAPER bbep(EP295_128x296);
|
|
//BBEPAPER bbep(EP42B_400x300);
|
|
static int iRunCount = 0;
|
|
static int iSec, iMin, iHour, iWDay, iMDay, iMon;
|
|
int iSleepTime; // number of microseconds to sleep in each wakeup phase
|
|
|
|
// I2C base address of various RTCs
|
|
#define RTC_DS3231_ADDR 0x68
|
|
#define RTC_RV3032_ADDR 0x51
|
|
#define RTC_PCF8563_ADDR 0x51
|
|
#define RTC_ADDR RTC_DS3231_ADDR
|
|
#define RTC_START_REG 0
|
|
// Ugly solder job of Xiao ESP32-C6 and my e-paper adapter
|
|
//#define PIN_SDA 4
|
|
//#define PIN_SCL 3
|
|
//#define POWER_PIN 5
|
|
//#define PIN_DC 14
|
|
//#define PIN_BUSY 16
|
|
//#define PIN_RST 15
|
|
// Arduino Pro mini clone
|
|
#define PIN_DC 15
|
|
#define PIN_BUSY 16
|
|
#define PIN_RST 14
|
|
#define PIN_CS 10
|
|
|
|
const char *szDays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
|
const char *szMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
// AVR Power savings
|
|
// watchdog interrupt
|
|
ISR (WDT_vect)
|
|
{
|
|
wdt_disable(); // disable watchdog
|
|
} // end of WDT_vect
|
|
//
|
|
// Use the watchdog timer to sleep for a fixed time and then wake up
|
|
// 16ms delay shifted left by teh shift value
|
|
//
|
|
void WDSleepOneSec(void)
|
|
{
|
|
ADCSRA = 0; // Disable ADC
|
|
// clear various "reset" flags
|
|
MCUSR = 0;
|
|
// allow changes, disable reset
|
|
WDTCSR = bit (WDCE) | bit (WDE); // allow changes, disable reset
|
|
/**
|
|
* Setting the watchdog pre-scaler value with VCC = 5.0V and 16mHZ
|
|
* WDP3 WDP2 WDP1 WDP0 | Number of WDT | Typical Time-out at Oscillator Cycles
|
|
* 0 0 0 0 | 2K cycles | 16 ms
|
|
* 0 0 0 1 | 4K cycles | 32 ms
|
|
* 0 0 1 0 | 8K cycles | 64 ms
|
|
* 0 0 1 1 | 16K cycles | 0.125 s
|
|
* 0 1 0 0 | 32K cycles | 0.25 s
|
|
* 0 1 0 1 | 64K cycles | 0.5 s
|
|
* 0 1 1 0 | 128K cycles | 1.0 s
|
|
* 0 1 1 1 | 256K cycles | 2.0 s
|
|
* 1 0 0 0 | 512K cycles | 4.0 s
|
|
* 1 0 0 1 | 1024K cycles | 8.0 s
|
|
*/
|
|
// For some reason, the AVR compiler is broken and these must be done as compile-time
|
|
// constants. Experiments with building a mask from individual bits ended in failure
|
|
WDTCSR = (1<<WDIE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0);
|
|
wdt_reset(); // pat the dog
|
|
|
|
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
|
|
noInterrupts (); // timed sequence follows
|
|
sleep_enable();
|
|
|
|
// turn off brown-out enable in software
|
|
MCUCR = bit (BODS) | bit (BODSE);
|
|
MCUCR = bit (BODS);
|
|
interrupts (); // guarantees next instruction executed
|
|
sleep_cpu ();
|
|
|
|
// cancel sleep as a precaution
|
|
sleep_disable();
|
|
Serial.println("woke up!");
|
|
} /* WDSleepOneSec() */
|
|
|
|
int I2CWrite(unsigned char iAddr, unsigned char *pData, int iLen)
|
|
{
|
|
Wire.beginTransmission(iAddr);
|
|
Wire.write(pData, (unsigned char)iLen);
|
|
Wire.endTransmission();
|
|
}
|
|
int I2CReadRegister(unsigned char iAddr, unsigned char u8Register, unsigned char *pData, int iLen)
|
|
{
|
|
int rc;
|
|
int i = 0;
|
|
|
|
Wire.beginTransmission(iAddr);
|
|
Wire.write(u8Register);
|
|
Wire.endTransmission();
|
|
Wire.requestFrom(iAddr, (unsigned char)iLen);
|
|
while (i < iLen) {
|
|
pData[i++] = Wire.read();
|
|
}
|
|
}
|
|
//
|
|
// Minimal code to read the time and create
|
|
// a formatted ASCII string from the hh:mm:ss
|
|
//
|
|
void rtc_get_time()
|
|
{
|
|
uint8_t ucTemp[8];
|
|
|
|
I2CReadRegister(RTC_ADDR, RTC_START_REG, ucTemp, 7);
|
|
// Convert values from BCD
|
|
iSec = ((ucTemp[0] >> 4) * 10) + (ucTemp[0] & 0xf);
|
|
iMin = ((ucTemp[1] >> 4) * 10) + (ucTemp[1] & 0xf);
|
|
// hours are stored in 24-hour format in the tm struct
|
|
if (ucTemp[2] & 64) { // 12 hour format
|
|
iHour = ucTemp[2] & 0xf;
|
|
iHour += ((ucTemp[2] >> 4) & 1) * 10;
|
|
iHour += ((ucTemp[2] >> 5) & 1) * 12; // AM/PM
|
|
} else { // 24 hour format
|
|
iHour = ((ucTemp[2] >> 4) * 10) + (ucTemp[2] & 0xf);
|
|
}
|
|
iWDay = ucTemp[3] - 1; // day of the week 0-6
|
|
iMDay = ucTemp[4] & 0xf;
|
|
iMDay += ((ucTemp[4] >> 4) * 10);
|
|
iMon = ucTemp[5] & 0xf;
|
|
iMon += (((ucTemp[5] >> 4) & 1) * 10);
|
|
iMon--; // returns 1-12 instead of 0-11
|
|
} /* rtc_get_time() */
|
|
|
|
void DrawPlane(int bInvert, int iPlane, char *string1, char *string2)
|
|
{
|
|
int iFG, iBG;
|
|
|
|
if (bInvert) {
|
|
iFG = BBEP_WHITE; iBG = BBEP_BLACK;
|
|
} else {
|
|
iFG = BBEP_BLACK; iBG = BBEP_WHITE;
|
|
}
|
|
bbep.setPlane(iPlane);
|
|
bbep.fillScreen(iBG); // clear both buffers to white for full update
|
|
bbep.setFont(Roboto_Black_78);
|
|
bbep.setTextColor(iFG, iBG);
|
|
bbep.drawString(string1, CENTER_X, 120);
|
|
bbep.setFont(Roboto_Black_38);
|
|
bbep.drawString(szDays[iWDay], CENTER_X, 200);
|
|
bbep.drawString(string2, CENTER_X, 280);
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
// Serial.begin(115200);
|
|
// Serial.println("Starting...");
|
|
Wire.begin();
|
|
Wire.setClock(400000);
|
|
} /* setup() */
|
|
|
|
void loop()
|
|
{
|
|
char szTemp[32] ,szTemp2[32];
|
|
int bPartial, iSleepTime, bInvert;
|
|
|
|
bbep.initIO(PIN_DC, PIN_RST, PIN_BUSY, PIN_CS, MOSI, SCK, 4000000);
|
|
while (1) {
|
|
bInvert = 0;
|
|
if ((iRunCount & 1) == 0) { // get time and update
|
|
bPartial = ((iRunCount & 0x1f) != 0);
|
|
rtc_get_time();
|
|
// EPDInit((bPartial) ? MODE_PARTIAL : MODE_NORMAL);
|
|
if (bbep.getChip() == BBEP_CHIP_UC81xx) {
|
|
// On the UC81xx we have to explicitly write the old data and new for a partial update
|
|
// the SSD16xx remembers the old properly and just needs to know the new
|
|
if (bPartial) { // inverted too, with the old time strings
|
|
DrawPlane(bInvert, PLANE_1, szTemp, szTemp2);
|
|
// bInvert = 0;
|
|
}
|
|
} // BBEP_CHIP_UC81xx
|
|
// Print the current hour:minute
|
|
sprintf(szTemp, "%02d:%02d", iHour, iMin);
|
|
sprintf(szTemp2, "%s %d", szMonths[iMon], iMDay);
|
|
|
|
if (!bPartial) {
|
|
DrawPlane(bInvert, PLANE_1, szTemp, szTemp2);
|
|
}
|
|
DrawPlane(bInvert, PLANE_0, szTemp, szTemp2);
|
|
bbep.refresh((bPartial) ? REFRESH_PARTIAL : REFRESH_FULL, false); // do a full refresh
|
|
// iSleepTime = (bPartial) ? 750 : 4000; // sleep instead of watching the busy signal
|
|
iSleepTime = (bPartial) ? 1 : 4; // sleep instead of watching the busy signal
|
|
} else { // we're waking up after waiting for the update to finish
|
|
bbep.sleep(1); // deep sleep
|
|
iSleepTime = 59; // wake up in 59 seconds
|
|
}
|
|
//EPDDeInit(); // set GPIO to disconnected for lower power
|
|
iRunCount++;
|
|
if (!bPartial && iSleepTime == 59) iSleepTime = 56; // we already slept for 4 seconds
|
|
for (int j=0; j<iSleepTime; j++) {
|
|
WDSleepOneSec();
|
|
}
|
|
} // while (1)
|
|
} /* loop() */
|