Skip to content

Commit

Permalink
Make SD cards optional to fix power leak on older boards
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisjtwomey committed Jun 28, 2023
1 parent a33e539 commit c1698a5
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
67 changes: 58 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,22 @@ Both a server and client and required. The main workload is in the server which
2. Attempts to get current network time and update real-time clock.
3. (Optional) Attempts to connect a MQTT topic to publish logs. This allows us to see what the ESP32 controller is doing without needing to monitor the serial connection.
4. Attempt to download the PNG image that the server is hosting.
5. Write the downloaded PNG image to SD card.
5. (Optional) Write the downloaded PNG image to SD card.
6. Read the PNG image back from SD card and write to the e-ink display.
7. Returns to deep sleep until the next scheduled wake time (eg. 24 hours).

#### Features:
- Ultra-low power consumption:
- approx 21µA in deep sleep
- approx 240mA awake
- approx 30 seconds awake time daily
- approx 24µA in deep sleep
- approx 120mA awake
- approx 10-20 seconds awake time daily
- potentially **years** of battery life
- Real-time clock for precise sleep/wake times.
- Daylight savings time handled automatically.
- Can publish to a MQTT topic for remote-logging.
- Renders messages on the e-ink display for critical errors (eg. battery low, wifi connect timeout etc.).
- Stores calendar images on SD card.
- Reconfigure client by updating YAML file on SD card and reboot - easy!
- Optional: stores calendar images on SD card.
- Optional: reconfigure client by updating YAML file on SD card and reboot - easy!

#### Power Consumption

Expand All @@ -61,7 +62,9 @@ See the [server/README.md](server/README.md) for more features.

If you want to use a more generic e-ink display or you just want to do a DIY build, there is a [branch](https://github.com/chrisjtwomey/inkplate10-weather-cal/tree/gxepd2) that's designed to work with GXEPD2, but it's likely missing fixes and features from the main branch.

- **2 GB microSD card ~€5**
- **Optional: 2 GB microSD card ~€5**

**Note: microSD cards are now no longer required and disabled by default. Use build flag `HAS_SDCARD` to re-enable**

Whatever is the cheapest microSD card you can find, you will not likely need more than few hundred kilobytes of storage. It will be mainly used by Inkplate to cache downloaded images from the server until it needs to refresh the next day. The config file for the code will also need to be stored here.

Expand All @@ -83,7 +86,53 @@ See the [server/README.md](server/README.md) for more features.

## Setup

Place `config.yaml` in the root directory of an SD card and connect it to your Inkplate 10 board.
### Option #1: using config header file _(recommended for E-Radionica Inkplate10)_

**Note: The old _E-Radionica_ version of Inkplate10 is missing hardware to control power to the SD card module which results in up to 2mA power consumption during deep sleep, therefore it's recommended you use this option to preserve battery life. See https://github.com/SolderedElectronics/Inkplate-Arduino-library/issues/209.**

Update `config.h` with the:
```
// Assign config values.
const char* calendarUrl = "http://localhost:8080/calendar.png";
const char* calendarDailyRefreshTime = "09:00:00";
const int calendarRetries = 3; // number of times to retry draw/download
// Wifi config.
const char* wifiSSID = "XXXX";
const char* wifiPass = "XXXX";
const int wifiRetries = 6; // number of times to retry WiFi connection
// NTP config.
const char* ntpHost =
"pool.ntp.org"; // the time server host (keep as pool.ntp.org if in doubt)
const char* ntpTimezone = "Europe/Dublin";
// Remote logging config.
bool mqttLoggerEnabled =
false; // set to true for remote logging to a MQTT broker
const char* mqttLoggerBroker = "localhost"; // the broker host
const int mqttLoggerPort = 1883;
const char* mqttLoggerClientID = "inkplate10-weather-client";
const char* mqttLoggerTopic = "mqtt/inkplate10-weather-client";
const int mqttLoggerRetries = 3; // number of times to retry MQTT connection
```

Make sure to update:
- `wifiSSID` - the SSID if your WiFi network.
- `wifiPass` - the WiFi password.
- `calendarUrl` - the hostname or IP address of your server which the client will attempt to download the image from.
- `calendarDailyRefreshTime` - the time you want the client to wake each day, in `HH:MM:SS` format.
- `ntpTimezone` - the timezone you live in (in "Olson" format), otherwise the client might not wake at the expected time.
- `mqttLoggerBroker` - the hostname or IP address of your server (likely the same server as the image host).


### Option #2: Using a microSD card _(recommended for SolderedElectronics Inkplate10)_

**Note: The new/current _SolderedElectronics_ version of Inkplate10 has a MOSFET to control power to the SD card during deep sleep, making this option viable for battery life. https://github.com/SolderedElectronics/Inkplate-Arduino-library/issues/209**

**Note: Use build flag `HAS_SDCARD` to enable SD card usage.**

Insert an SD card into your Inkplate board and place a new file `config.yaml` in the root directory:

```
calendar:
Expand All @@ -106,7 +155,7 @@ mqtt_logger:
retries: 3
```

Likely parameters you'll need to change is
Make sure to update:
- `wifi.ssid` - the SSID if your WiFi network.
- `wifi.pass` - the WiFi password.
- `calendar.url` - the hostname or IP address of your server which the client will attempt to download the image from.
Expand Down
6 changes: 6 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ build_flags =
-DYAML_DISABLE_CJSON
-mfix-esp32-psram-cache-issue
-DBATT_2000MAH
# uncomment below if you want to use an SD card
# WARNING: high power consumption on Inkplate10 V1
# -DHAS_SDCARD
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=4

Expand All @@ -52,5 +55,8 @@ build_flags =
-DYAML_DISABLE_CJSON
-mfix-esp32-psram-cache-issue
-DBATT_2000MAH
# uncomment below if you want to use an SD card
# WARNING: high power consumption on Inkplate10 V1
# -DHAS_SDCARD
-DLOG_LEVEL=4
-DCORE_DEBUG_LEVEL=0
28 changes: 28 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef CONFIG_H
#define CONFIG_H

// Assign config values.
const char* calendarUrl = "http://localhost:8080/calendar.png";
const char* calendarDailyRefreshTime = "09:00:00";
const int calendarRetries = 3; // number of times to retry draw/download

// Wifi config.
const char* wifiSSID = "XXXX";
const char* wifiPass = "XXXX";
const int wifiRetries = 6; // number of times to retry WiFi connection

// NTP config.
const char* ntpHost =
"pool.ntp.org"; // the time server host (keep as pool.ntp.org if in doubt)
const char* ntpTimezone = "Europe/Dublin";

// Remote logging config.
bool mqttLoggerEnabled =
false; // set to true for remote logging to a MQTT broker
const char* mqttLoggerBroker = "localhost"; // the broker host
const int mqttLoggerPort = 1883;
const char* mqttLoggerClientID = "inkplate10-weather-client";
const char* mqttLoggerTopic = "mqtt/inkplate10-weather-client";
const int mqttLoggerRetries = 3; // number of times to retry MQTT connection

#endif
8 changes: 4 additions & 4 deletions src/lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ esp_err_t configureWiFi(const char* ssid, const char* pass, int retries) {
int attempts = 0;
while (attempts++ <= retries && WiFi.status() != WL_CONNECTED) {
logf(LOG_DEBUG, "connection attempt #%d...", attempts);
delay(500);
delay(1000);
}

// If still not connected, error with timeout.
Expand Down Expand Up @@ -464,9 +464,9 @@ void deepSleep() {
WiFi.disconnect();
WiFi.mode(WIFI_OFF);

esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);
#if defined(HAS_SDCARD)
board.sdCardSleep();
#endif

esp_deep_sleep_start();
}
20 changes: 17 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <ArduinoJson.h>
#include <ArduinoYaml.h>
#include <SdFat.h>
#include <StreamUtils.h>
#if defined(HAS_SDCARD)
#include <SdFat.h>
#endif

#include "lib.h"
#include "battery.h"
Expand Down Expand Up @@ -49,13 +51,15 @@ void setup() {
int batteryRemainingPercent = getBatteryCapacity(bvolt);
logf(LOG_INFO, "approx battery capacity: %d%%", batteryRemainingPercent);

#if defined(HAS_SDCARD)
// Init storage.
if (!board.sdCardInit()) {
const char* errMsg = "SD card init failure";
log(LOG_ERROR, errMsg);
displayMessage(errMsg, batteryRemainingPercent);
sleep(CONFIG_DEFAULT_CALENDAR_DAILY_REFRESH_TIME);
}
#endif

if (batteryRemainingPercent <= 1) {
log(LOG_NOTICE, "battery near empty! - sleeping until charged");
Expand All @@ -69,6 +73,7 @@ void setup() {
// Init err state.
esp_err_t err = ESP_OK;

#if defined(HAS_SDCARD)
// Attempt to get config yaml file.
File file = sd.open(CONFIG_FILE_PATH, FILE_READ);
if (!file) {
Expand Down Expand Up @@ -114,6 +119,9 @@ void setup() {
const char* mqttLoggerClientID = mqttLoggerCfg["clientId"];
const char* mqttLoggerTopic = mqttLoggerCfg["topic"];
int mqttLoggerRetries = mqttLoggerCfg["retries"];
#else
#include "config.h"
#endif

// Attempt to connect to WiFi.
err = configureWiFi(wifiSSID, wifiPass, wifiRetries);
Expand Down Expand Up @@ -144,10 +152,13 @@ void setup() {
err = ESP_FAIL;
const char* errMsg;
int attempts = 0;
#if defined(HAS_SDCARD)
const char* imagePath = CALENDAR_RW_PATH;

do {
logf(LOG_DEBUG, "calendar download attempt #%d", attempts + 1);

err = downloadFile(calendarUrl, CALENDAR_IMAGE_SIZE, CALENDAR_RW_PATH);
err = downloadFile(calendarUrl, CALENDAR_IMAGE_SIZE, imagePath);
if (err != ESP_OK) {
errMsg = "file download error";
log(LOG_ERROR, errMsg);
Expand All @@ -168,6 +179,9 @@ void setup() {
// Deep sleep until next refresh time
sleep(calendarDailyRefreshTime);
}
#else
const char* imagePath = calendarUrl;
#endif

// Reset err state.
err = ESP_FAIL;
Expand All @@ -176,7 +190,7 @@ void setup() {
logf(LOG_DEBUG, "calendar draw attempt #%d", attempts + 1);

board.clearDisplay();
err = loadImage(CALENDAR_RW_PATH);
err = loadImage(imagePath);
if (err != ESP_OK) {
errMsg = "image load error";
log(LOG_ERROR, errMsg);
Expand Down

0 comments on commit c1698a5

Please sign in to comment.