The clip played is from The Melancholy of Haruhi Suzumiya (2009).
A self-sustained AVI player to replay the summer days.
- ESP32 to play MJPEG-AVI video files from microSD at 24 fps.
- Utilizes ULP coprocessor for BAT voltage monitoring, Deep Sleep management etc.
- 5V solar panel and small LiPo battery as the main power source.
- (Planned) LTC3130-1 for more efficient power conversion.
WIP
- ESP32 WeMos LOLIN32 Lite
- ST7735 80x160 LCD module
- microSD card (>= 2 GB)
- 5V 2W solar panel
All the video files to be placed on the MicroSD must be MJPEG-encoded AVI.
# Transcode EP 12~19 to ee_%d.avi, with scaling and cropping
$ seq 12 19 | xargs -L 1 -P1 -I% bash -c 'ffmpeg -i ee_%.mp4 -r 24 -vf scale=160:-1,crop=160:80 -vcodec mjpeg -q:v 5 -an ee_%.avi -y'
See sdcard/ for a set of sample vid files. These are converted from BigBuckBunny.mp4.
# The sample vid files were from
$ seq 12 19 | xargs -L 1 -P1 -I% bash -c 'ffmpeg -ss $(((% - 12)*2)) -to $(((% - 12 + 1) * 2)) -i BigBuckBunny.mp4 -r 24 -vf scale=160:-1,crop=160:80 -vcodec mjpeg -q:v 5 -an ee_%.avi -y'
$ git clone https://github.com/likeablob/endless-endless-eight.git
$ cd endless-endless-eight
$ cp include/user_config.g.template cp include/user_config.h
$ code include/user_config.h
$ pio run || pio run # The very 1st run may fail due to ulptool-pio
$ pio run -t upload
See ./enclosure.
Centering on the deep sleep mode, this application has several wake-up states as depicted in the following diagram.
- The application switches back-and-forth between the nominal mode and deep sleep depending on the battery voltage. (See Power management strategy for details.)
- Also, under the certain condition, it wakes in;
- Emergency mode to prepare for a complete power loss (See State persistence)
- Periodic Reporting mode for reporting housekeeping data via MQTT
- Basically every wake up transition is triggered by the ULP routine.
stateDiagram-v2
Playing: Playing Video (Nominal)
[*] --> Playing
Playing --> DeepSleep: At low battery
DeepSleep --> Playing: At high battery
DeepSleep --> Emergency: At very low battery
Emergency --> DeepSleep: ASAP
DeepSleep --> PeriodicReporting: Every 30 minutes
PeriodicReporting --> DeepSleep: ASAP
It's quite simple; Wake the Main CPU up if the battery voltage (BAT_V) >= 3.9 V
and put into sleep if BAT_V <= 3.75 V
.
The ULP co-processor executes own routine every one second, but still is able to keep the deep sleep current consumption at uA-level.
flowchart TB
subgraph CPU["Main CPU"]
%% flow
power_boot(" ")
boot("Boot")
check_ulp_boot{"Wake by ULP?"}
init_ulp["Init ULP routine"]
%% flow
init_loop(" ")
check_batv_sleep{"BAT_V <= SLEEP_TH"}
sleep("Deep sleep")
power_boot --> boot --> check_ulp_boot
check_ulp_boot -->|"Yes"| init_loop
check_ulp_boot -->|"No"| init_ulp --> init_loop
init_loop --> check_batv_sleep
check_batv_sleep -->|"Yes"| sleep
check_batv_sleep -->|"No"| init_loop
sleep -.-> |"Wake by ULP"| boot
end
subgraph ULP
%% nodes
boot_ulp(" ")
measure_batv["Measure Battery voltage (BAT_V) by ADC"]
check_batv_wake{"BAT_V >= WAKE_TH"}
wake_main["Wake Main CPU"]
end_ulp(" ")
%% flow
boot_ulp --> measure_batv --> check_batv_wake
check_batv_wake -->|"Yes"| wake_main ---> end_ulp
check_batv_wake --->|"No"| end_ulp
end_ulp -.->|"Every 1 sec"| boot_ulp
end
%% flow
init_ulp -.-> boot_ulp
The play state such as a file playing, its position and loop count etc. are kept over deep sleep or even complete power outage, thanks to Nagato RTC SLOW MEM and Non-Volatile Storage. The former is volatile but survives deep sleep, and the latter is nothing but a reserved region in flash.
In short there are two pathways for each storage.
- A. Save to
RTC_SLOW_MEM
before entering deep sleep. Restore fromRTC_SLOW_MEM
after waking up from deep sleep. - B. Save to
NVS
at very-low battery voltage. Restore fromNVS
on boot.
Note: About B., the ULP routine wakes the main CPU in the Emergency mode when BAT_V <= 3.55 V
. This happens only once and the related flag will be cleared by the main CPU after normal wake up.
flowchart
%% nodes
init(" ")
check_ulp_boot{"Wake by ULP?"}
restore_from_nvs["Restore from Non-volatile storage"]
restore_from_rtc["Restore from RTC SLOW RAM\n(Volatile, Deep-sleep persistent)"]
check_ulp_flag_emergency{"Emergency Mode?\n(caused by very low battery)"}
save_to_nvs["Save to Non-volatile storage"]
sleep("Deep sleep")
init_loop("Main Loop")
check_low_bat{"Low battery?"}
save_to_rtc["Save to RTC SLOW MEM"]
init ---> check_ulp_boot
check_ulp_boot -->|"Yes"| check_ulp_flag_emergency
check_ulp_boot -->|"No"| restore_from_nvs --> init_loop
check_ulp_flag_emergency --> |"Yes"| save_to_nvs --> sleep
check_ulp_flag_emergency --> |"No"| restore_from_rtc --> init_loop
init_loop --> check_low_bat
check_low_bat --> |"Yes"| save_to_rtc --> sleep
check_low_bat --> |"No"| init_loop
sleep -.-> init
classDef restore fill:#dce4ef,stroke:#333,stroke-width:2px;
classDef save fill:#e7f4e4,stroke:#333,stroke-width:2px;
class restore_from_nvs,restore_from_rtc restore
class save_to_nvs,save_to_rtc save
MIT
This projects is here thanks to a lot of superb OSS libraries. See platformio.ini for details. Thank you to all the devs.