diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index e385a5e..17ff0c9 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -4,7 +4,8 @@
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
- "${env:PICO_SDK_PATH}/**"
+ "${env:PICO_SDK_PATH}/**",
+ "${env:PICO_EXTRAS_PATH}/**"
],
"defines": [],
"compilerPath": "/usr/bin/arm-none-eabi-gcc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ce34f8..2c088b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,9 @@ set(PICO_BOARD pico_w CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
+# Pull in Raspberry Pi Pico Extras
+include(pico_extras_import.cmake)
+
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
diff --git a/README.md b/README.md
index 13f5176..9603c5d 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Idee
Das Gerät soll am Vorabend (ab 18:00 Uhr) anzeigen, welche Abfallarten am nächsten Tag abgeholt werden. Die Anzeige erfolgt über eine RGB-LED.
-* Keine Abholung: minütliches blinken in **WEIß** (0xffffff / 0x000000)
+* Keine Abholung: minütliches blinken in **WEIẞ** (0xffffff / 0x000000)
(Daran erkennt man, dass das Gerät noch läuft.)
* Restmüll: **ROT** (0xff0000)
* Papier: **BLAU** (0x0000ff)
diff --git a/pico_extras_import.cmake b/pico_extras_import.cmake
new file mode 100644
index 0000000..b75b9b3
--- /dev/null
+++ b/pico_extras_import.cmake
@@ -0,0 +1,62 @@
+# This is a copy of /external/pico_extras_import.cmake
+
+# This can be dropped into an external project to help locate pico-extras
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH))
+ set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH})
+ message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT))
+ set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT})
+ message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH))
+ set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH})
+ message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+if (NOT PICO_EXTRAS_PATH)
+ if (PICO_EXTRAS_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_EXTRAS_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_extras
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-extras
+ GIT_TAG master
+ )
+ if (NOT pico_extras)
+ message("Downloading Raspberry Pi Pico Extras")
+ FetchContent_Populate(pico_extras)
+ set(PICO_EXTRAS_PATH ${pico_extras_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras")
+ set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras)
+ message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}")
+ else()
+ message(FATAL_ERROR
+ "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif()
+ endif ()
+endif ()
+
+set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS")
+set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable")
+set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS")
+
+get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_EXTRAS_PATH})
+ message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found")
+endif ()
+
+set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE)
+
+add_subdirectory(${PICO_EXTRAS_PATH} pico_extras)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0bea1be..2a23ef7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -61,6 +61,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
pico_lwip_sntp
hardware_rtc
hardware_pio
+ hardware_sleep
)
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
diff --git a/src/abfall.cpp b/src/abfall.cpp
index a69218b..f914b7c 100644
--- a/src/abfall.cpp
+++ b/src/abfall.cpp
@@ -1,5 +1,4 @@
#include
-#include
#include
#include
@@ -8,8 +7,10 @@
#include "hardware/pio.h"
#include "hardware/rtc.h"
+#include "hardware/structs/scb.h"
#include "pico/cyw43_arch.h"
+#include "pico/sleep.h"
#include "pico/stdlib.h"
#include "pico/util/datetime.h"
@@ -32,8 +33,6 @@ int main() {
#endif
printf("Firmware version: %s\n", PROJECT_VERSION);
- WS2812 led(WS2812_PIN);
-
wifi_enable(); // Enable Wifi in order to set time and retrieve data
NtpClient::setDateTime();
@@ -63,13 +62,22 @@ int main() {
NtpClient::stop();
wifi_disable(); // We don't need Wifi anymore
- datetime_t dt;
-
auto dates = parseCsv(csv);
#ifdef DEBUG
printf("Number of Dates: %d\n", dates.size());
#endif
+ datetime_t dt;
+
+ // save values for later
+ uint scb_orig = scb_hw->scr;
+ uint clock0_orig = clocks_hw->sleep_en0;
+ uint clock1_orig = clocks_hw->sleep_en1;
+ uart_default_tx_wait_blocking();
+ sleep_run_from_xosc();
+
+ WS2812 led(WS2812_PIN);
+
while (true) {
auto timestamp = time_us_64() + 60'000'000ull;
@@ -82,17 +90,19 @@ int main() {
return date.date == tomorrowYMD;
});
- int8_t hour = dt.hour + 1; // MEZ
- if (isDST(dt.day, dt.month, dt.dotw))
- ++hour; // MESZ
+ int8_t hour = isDST(dt) ? dt.hour + 1 : dt.hour;
+#ifdef DEBUG
printf("%d-%02d-%02d %02d:%02d.%02d\n", dt.year, dt.month, dt.day, hour, dt.min, dt.sec);
+#endif
// If there was a waste bin pickup found AND we are in the evening (>= 18:00) …
- if (it != dates.end() && hour >= 18) {
+ if (it != dates.end() && hour >= 18 && hour < 23) {
auto wasteDate = *it;
size_t count{0};
auto currentTime = time_us_64();
+ WS2812 led(WS2812_PIN);
+
while (currentTime < timestamp) {
size_t index = count % wasteDate.wasteTypes.size();
@@ -127,8 +137,23 @@ int main() {
}
} else {
+ // If we start already close to a full hour, dont't do a deep sleep yet
+ if (dt.min == 59 && dt.sec >= 57) {
+ sleep_ms((60 - dt.sec) * 1'000);
+ continue;
+ }
+
led.blinkReady();
- sleep_until(static_cast(timestamp));
+
+ datetime_t dtUntil = dt;
+ add_one_hour(dtUntil); // next hour
+ dtUntil.sec = 0;
+ dtUntil.min = 0;
+
+ led.deinit(); // deinit LED before going to deep sleep
+ perform_sleep(dtUntil);
+ recover_from_sleep(scb_orig, clock0_orig, clock1_orig);
+ led.init(); // init LED again after deep sleep
}
}
diff --git a/src/ntp_client.cpp b/src/ntp_client.cpp
index d187a5f..af43122 100644
--- a/src/ntp_client.cpp
+++ b/src/ntp_client.cpp
@@ -8,24 +8,20 @@
// Called by lwip sntp in order to set the time
void set_system_time(u32_t sec) {
+ sec += 3600; // MEZ
+
time_t epoch = sec;
- struct tm *utc = gmtime(&epoch);
+ struct tm *mez = gmtime(&epoch);
datetime_t datetime;
- datetime.year = utc->tm_year + 1900;
- datetime.month = utc->tm_mon + 1;
- datetime.day = utc->tm_mday;
- datetime.hour = utc->tm_hour;
- datetime.min = utc->tm_min;
- datetime.sec = utc->tm_sec;
- datetime.dotw = utc->tm_wday;
+ datetime.year = mez->tm_year + 1900;
+ datetime.month = mez->tm_mon + 1;
+ datetime.day = mez->tm_mday;
+ datetime.hour = mez->tm_hour;
+ datetime.min = mez->tm_min;
+ datetime.sec = mez->tm_sec;
+ datetime.dotw = mez->tm_wday;
- bool success = rtc_set_datetime(&datetime);
-
-#ifdef DEBUG
- if (success) {
- printf("RTC successfully set.\n");
- }
-#endif
+ rtc_set_datetime(&datetime);
}
void NtpClient::setDateTime() {
diff --git a/src/utils.cpp b/src/utils.cpp
index 391e92e..8a97c95 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -4,23 +4,30 @@
#include
#include
+#include "pico/sleep.h"
+#include "pico/stdlib.h"
+// #include "stdlib.h"
+#include "hardware/clocks.h"
+#include "hardware/rosc.h"
+#include "hardware/structs/scb.h"
+
using namespace std;
-bool isDST(int8_t day, int8_t month, int8_t dow) {
+bool isDST(const datetime_t &dt) {
// January, february, november and december are out.
- if (month < 3 || month > 10) {
+ if (dt.month < 3 || dt.month > 10) {
return false;
}
// April to September are in
- if (month > 3 && month < 10) {
+ if (dt.month > 3 && dt.month < 10) {
return true;
}
- int previousSunday = day - dow;
+ int previousSunday = dt.day - dt.dotw;
// In march, we are DST if our previous sunday was on or after the 25th.
- if (month == 3) {
+ if (dt.month == 3) {
return previousSunday >= 25;
}
@@ -186,4 +193,57 @@ void wifi_disable() {
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA);
cyw43_arch_deinit();
-}
\ No newline at end of file
+}
+
+void recover_from_sleep(uint scb_orig, uint clock0_orig, uint clock1_orig) {
+
+ // Re-enable ring Oscillator control
+ rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS);
+
+ // reset procs back to default
+ scb_hw->scr = scb_orig;
+ clocks_hw->sleep_en0 = clock0_orig;
+ clocks_hw->sleep_en1 = clock1_orig;
+
+ // reset clocks
+ clocks_init();
+ stdio_init_all();
+
+ return;
+}
+
+void perform_sleep(datetime_t &untilDt) {
+ printf("Going to sleep...\n");
+ uart_default_tx_wait_blocking();
+
+ sleep_goto_sleep_until(&untilDt, nullptr);
+}
+
+void add_one_day(datetime_t &dt) {
+ int daysPerMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ // Is it a leap year?
+ if ((dt.year % 4 == 0 && dt.year % 100 != 0) || dt.year % 400 == 0) {
+ daysPerMonth[2] = 29;
+ }
+
+ if (dt.month == 12 && dt.day == daysPerMonth[12]) {
+ dt.year += 1;
+ dt.month = 1;
+ dt.day = 1;
+ } else if (dt.day == daysPerMonth[dt.month]) {
+ dt.month = (dt.month + 1) % 12;
+ dt.day = 1;
+ } else {
+ dt.day += 1;
+ }
+}
+
+void add_one_hour(datetime_t &dt) {
+ if (dt.hour == 23) {
+ add_one_day(dt);
+ dt.hour = 0;
+ } else {
+ dt.hour += 1;
+ }
+}
diff --git a/src/utils.h b/src/utils.h
index dbcb4d2..f7c9d2c 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -27,4 +27,12 @@ void wifi_enable();
void wifi_disable();
-bool isDST(int8_t day, int8_t month, int8_t dow);
\ No newline at end of file
+bool isDST(const datetime_t &dt);
+
+void recover_from_sleep(uint scb_orig, uint clock0_orig, uint clock1_orig);
+
+void perform_sleep(datetime_t &untilDt);
+
+//void add_one_day(datetime_t &dt);
+
+void add_one_hour(datetime_t &dt);
\ No newline at end of file
diff --git a/src/ws2812.cpp b/src/ws2812.cpp
index c7ad5ac..edf35b7 100644
--- a/src/ws2812.cpp
+++ b/src/ws2812.cpp
@@ -1,10 +1,8 @@
#include "ws2812.h"
-WS2812::WS2812(uint gpio, PIO pio, uint sm) : m_pio{pio}, m_sm{sm} {
- uint offset = pio_add_program(m_pio, &ws2812_program);
- ws2812_program_init(m_pio, m_sm, offset, gpio, 800000, true);
- switchColor(Color::OFF);
-}
+WS2812::WS2812(uint gpio, PIO pio, uint sm) : m_pio{pio}, m_gpio{gpio}, m_sm{sm} { init(); }
+
+WS2812::~WS2812() { deinit(); }
void WS2812::switchColor(Color color) { putPixel(static_cast(color)); }
@@ -17,4 +15,21 @@ void WS2812::blinkReady() {
}
}
+void WS2812::init() {
+ if (m_offset != 0)
+ return;
+
+ m_offset = pio_add_program(m_pio, &ws2812_program);
+ ws2812_program_init(m_pio, m_sm, m_offset, m_gpio, 800000, true);
+ switchColor(Color::OFF);
+ sleep_ms(1);
+}
+
+void WS2812::deinit() {
+ switchColor(Color::OFF);
+ sleep_ms(1);
+ pio_remove_program(m_pio, &ws2812_program, m_offset);
+ m_offset = 0;
+}
+
void WS2812::putPixel(uint32_t pixel_grb) { pio_sm_put_blocking(m_pio, m_sm, pixel_grb << 8u); }
\ No newline at end of file
diff --git a/src/ws2812.h b/src/ws2812.h
index 229fccf..f7746bb 100644
--- a/src/ws2812.h
+++ b/src/ws2812.h
@@ -17,11 +17,18 @@ enum class Color : uint32_t {
class WS2812 {
public:
WS2812(uint gpio, PIO pio = pio0, uint sm = 0);
+ virtual ~WS2812();
+ WS2812(const WS2812 &other) = delete;
+ WS2812 &operator=(const WS2812 &) = delete;
void switchColor(Color color);
void blinkReady();
+ void init();
+ void deinit();
private:
void putPixel(uint32_t pixel_rgb);
PIO m_pio;
+ uint m_gpio;
uint m_sm;
+ uint m_offset{0};
};