I wanted a small, battery-powered timelapse camera. First I tried mobile phones but they were not stable in the long term. Previously I’d used a Raspberry Pi which is good, but not battery friendly, a bit big, and a bit of an overkill.
A PiZero would be an alternative (update 2020: I now use PiZero (see tmv on github)). The high quality camera is a big improvement and development is faster. Battery life is worse than the Esp32 but can run for a day.
I originally choose a ESP32-Cam which can:
- go to deep-sleep (10uA for the ESP, Cam to be tested) overnight
- save JPG files from its mini camera (but good enough) to SD card
- connect to WiFi to transmit / control
- be small and cheap!
I use the following:
- ESP32-Cam from diymore.cc which is similar to AI-Thinker
- A 2000mAh 585460 flat LiPo battery. A 18650 LiPo battery worked, but needs seperate low-voltage protection.
- Adafruit Solar Charger
- 3v to 5v USB step up (from battery output to 5V* for Cam)
- FTDI Programmer to flash the firmware
- 1W 6V solar panel (20cmx20cm)
* Note 5V is required for the ESP32-Cam, although the ESP32 needs only 3.3V.
I used a 3.3V / 5V (switchable via toggle) FDTI programmer. I used 5V thinking that I’d supply 5V to VCC and 3.3V to the RX/TX pins. Oops! It actually supplies 5V to RX/TX pins too. Seems ok but better to select 3.3V to get this on the RX/TX and grab a 5V breakout pin to power the board.
Power Supply and Battery
A 1W solar panel provides 4Wh/W (Brisbane) x 1W = 4Wh per day.
This is 4Wh / 3.3V = 1.2Ah per day @ 3.3V. Say 1000mA/day. The 18650 battery is 2600mAh, so needs 2.6 days to charge!
Power consumption of the camera is to be tested. ESP32 is 50mA and only 10uA alseep. Maybe 50mA average (?), or 2600mAh / 50mA = 52h on battery. On panels, maybe 50mA * 10h = 500 mAh/day required:
First test 1 June 2019: Without sleeping, ran for about 20 hours continuously with photos at 10s. This is about right: in this mode, we have Wifi, Cam and SD on. That takes about 130mA which gives a run-time of 15 hours.
More detailed readings:
- Deepsleep is 4mA instead of bare ESP32 of 10uA. No a major problem in this application.
- Camera is an additional 10mA, regardless of taking or just ‘on’.
- That bright LED is a power sucker.
- Turning the SD has no power penalty. Even holding a file open has no impact.
- Using esp_wifi_set_ps(WIFI_PS_NONE)) will improve upload speed, but
|Mode||WiFi||Cam||SD Active||CPU||LED Pin4||mA||Run time @ 2000mAh (h)|
|Continuous SD Save (No LED)||x||x||90||22|
|SD, WiFi,||x (No PS)||x||x||x||120|
|SD, WiFi, Cam||x (No PS)||x||x||x||180 (230 photo)|
|SD, Wifi, Cam, Modem Sleep / PS On||x (PS)||x||x||x||150 (average)||13|
|SD, Wifi Off, Cam (Wifi was on)||x||x||x||100 (170 photo)|
|SD, Wifi Off, Cam Off (Cam,Wifi were on)||x||x||70|
|FTP Uploading (LED)||x||x||x||x||180||11|
|Continuous SD Save (LED)||x||x||x||340||6|
We see that compared to a ‘bare’ ESP32, the Cam version reasonable energy consumption if you remove the LED. However, the deepsleep mode is no longer extremely low consumption. This suggests a battery-only unit would only run for a week, but a solar-and-battery is feasible. Alternatively, a weekly recharge might work.
WiFi takes about 1.3s to start and the Cam turns 0.5s to start.
Deepsleep might be good for long periods. We estimate the runtime (in hours).
|Method||Photos @ 3s|
|Photos @ 10s|
|Photos @ 60s|
[Deepsleep, Batch Upload]
|Photos @ 600s
[Deepsleep, Batch Upload]
|Always On, No WiFi||23||31||38||40|
|Deep Sleep, No Wifi||26||61||222||500|
|Deep Sleep, Save, Upload in Batch||18||43||182||426|
|Always On, WiFi, Upload in Batch||15||24||36||39|
So regardless of if we have WiFi or not, the upshot is,
- Intervals <10s: Always On is fine
- Use Modem Sleep (default) on to reduce standby WiFi power
- 13h of operation is insufficient – use a larger battery (5000mAh) to get 2.5 days operation and use a solar panel.
- Intervals >60s: Deepsleeping worthwhile.
Energy Results and Further Improvements
Real-life battery life on 2000mAh is still poor : only 6 hours. This is due to the standby 70mA (i.e. cam not taking but init’d, WiFi off). Arduino IDE doesn’t allow full control of sleep modes. So I switched to ESP-IDF.
Based on these excellent posts about ESP32 battery life, I tested the ESP32-CAM board. This is based on the power_save example. Moved to using a 1ohm resistor and measuring voltage to find the current (I=V/R) – this is more convenient that an in-line DMM current meter.
|Default||21||Full defaults, wait at startup|
|Add basic DFS||14||Enable DFS from menuconfig|
|Better DFS||5||Scale: 10MHz to 80MHz|
|Wifi On||120||PS_NONE||(DFS reset to 10 to 240MHz)|
|Wifi Default||15 (20 avg?)||PS_MODEM_MIN|
|Wifi Max Powersave||7 (10 avg?)||PS_MODEM_MAX||The "listen_interval" only affects this mode. Greater than 10 (default: 3) is not interactive.|
We see that Dynamic Frequency Scaling (i.e. clock gate the CPU when idle) is very effective to stop idle time chewing mA. With WiFi docs, using the MODEM_SLEEP mode in default (“MIN”) mode is pretty good. Bumping up the “MAX” mode allows the listen_interval to be set. It’s in 100mS units. Setting to 1s (i.e 10x) results in some improvements.
This looks promising. Now we need check the current when the camera is init’d but idle.
Lack of GPIO!
The ESP32 needs to know the state of the battery and solar, so it can control itself. It uses the Adafruit solar controller’s output. It also uses time-of-day to off when dark (a PE cell would be an alternative).
Reading voltage required two GPIO pins. However, these are NOT AVAILABLE on the ESP-32 cam. All broken out pins appear to be used by either the SD card or UART or camera. GPIO16 didn’t work (TBC!). I couldn’t use the SD card pins. The only option I see is to use the TX/RX pins (GPIO1, GPIO3). These are not connected to the ADC so cannot read analog. Also this means serial is not available, but RemoteDebugger library is possible.
GPIO3 (RX) works to read the open-drain of the “charging” or “done” indicator. I can’t get GPIO1 (TX) to read, nor GPIO16. GPIO2 works but is used by the SD card. This comment agrees with the TX/RX issue. Hence only a single digital input is available – either “charging” or “done”. As the least worst, I’ll use “done” to signal that uploading should start.
Hence, the following is ideal, but not possible:
Since the maximum possible voltage is 6V (“the voltage at L+ can rise to whatever comes out of your solar panel”), I use a voltage divider to halve the voltages before measuring with the ESP32’s ADC.
|Lowish Battery, solar powering||L+ > B+ (by < 150mV)|
B+ > 3.2V
|Low Battery||B+ <= 3.2V||Deep sleep|
|Good solar, battery > 50%||L+ > B+ (by > 150mV)|
B+ > 3.5V
|No solar, battery > 50%||L+ < B+|
B+ > 3.5V
|No solar, battery < 50%||L+ < B+|
B+ <= 3.5V
Image File Transfer
Wifi Minimal means upload limited snapshots of the current images, while Wifi Full means upload all images. Some upload options:
- Dropbox : adapt a library
- Upload to a server running Apache or similiar, via HTTP
- scp : no libraries?
- FTP: easily adapt a library
The dropbox option looked simplest, but the only library needed heavy modification to work with ESP32. This worked okay, but was very slow: approximately 3kB/s. This is only one medium quality image per 10s.
A quality C library for FTP on the Esp32 worked well. The only trick was the ESP32-Arduino SD_MMC library mounts the card to “/sdcard” and fails if you try to mount at “/”. The Arduino File libraries understand this read from “/”. However, the FILE* ftp library sees “/sdcard”. A thin-wrapper in C++ to the FTP library always it to play nice with Arduino File classes.
Performance for FTP (ESP32 -> WiFi -> AWS EC2 server) was 120kB/S. This is a few images per second.
File opening is 300ms on the SD Card. Posix functions (FILE*) appear faster than fs::File classes in Arduino.
vsftpd worked well on AWS and Ubuntu after Googling fixed passive mode problems.
Movie Creation and Monitoring
The general approach is to get the image files to a server as simply as possible. Then do complex stuff on the server.
The latest image is copied to a known location for a webserver to display. A more advanced system would copy one file a minute (or so) to the webserver, which could then have a simliar forward/back viewer to preview the movie.
Every hour (or so), a script runs to create an hour-long movies. It would have to work out which movies are to be updated, based on new images. Use python and ffmpeg, and TimeLapseMovieMaker to create videos.
A “movie-to-now” could be created by joining the hourly movies together.
Also, each day a daily movie could be made.
Images can be deleted once an hourly movie is made. However, how to handle the case where late-arriving images need to be added to an old movie? Perhaps some kind of simple database is required to track images and movies? Or a simple metric of delete-when-7 days old-and-movie-is-make?
Some of the elements are similar to “Blobs” software, but sufficiently different to warrant a new project.
- Implement different tasks (upload, camera, etc) via FreeRTOS.
- Use WifiManger to connect to station or use AP, as required.
- On hard-reset (or some other signal such as flag on web server / physical button / every 1 hour), run configuration webserver.
- Camera loop can sleep between takes
- Time Task to check internet time / RTC (for timestamps)
- Power Task to check power supply and set flags / control operation. Related: sleep when dark.
- Minimal upload can be done after image is taken. If so (i.e. enough battery/solar), it just uploads current image (every nth image) and closes WiFi.
- Full upload as a permanent task which runs when possible.
The separate tasks check their own conditions and block if not required, then a sleep_idle_hook to sleep whenever possible. This is event-driven.
( An option would be to run in a loop, executing tasks when conditions are met. This is a more procedural style. )
WebApp to Modify On-the-Fly
A webpage to view the state of the ESP32, the last image and to modify things like the photo interval would be handy.
- Run as a single web page webapp and use AJAX or websockets to communicate.
- Have a file browser and editor
The Expressif WebServer for arduino-esp32 is pretty simple and doesn’t support websocket, but has nice example and a ready-to-go file browser. I choose this initially. But the SDWebServer doesn’t work out of the box (some easy “/” related problems) and is limited to a single client.
The more complex AsyncServer seems more production-ready and has websockets. There are, however, fewer examples and no SD card file server for the ESP32. With some modifications, SPIFFSEditor can use SD_MMC filesystem (no folders, though – would need to add). The addHandler process is easy. I’ll use this.
AJAX or websockets will allow nicer development. I’m trying BootStrap to get a tidy interface. Bootply.com and layitout.com enable you to easily create a css/html set of file to display nice buuttons and the like.
Next, how to use JS or jQuery to communicate with the webserver? WebStorm is good for editting the html/css/js and provides a webserver to test it locally (on a pc).