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}; };