ESP32-CAM Power Saving - undefined reference issue

Hi

I am using the ESP32-CAM board for a project running on batteries and need to save power. I have reduced the CPU frequency and other things, but now I am working on the Camera module. I only need to take a picture once every minute or two, so leaving it on and running all the time is wasting 30mA or so. Before initialising the camera, my device consumes around 50ma, after initialising (and after it settles after taking a picture) it consumes around 70ma.

There is some detailed advice here: https://github.com/espressif/esp32-camera/issues/33 about using the private xclk routines to save power, that apparently work, but I cannot get them to work.

Compiling the code below I get the errors:

C:\Users\W\AppData\Local\Temp\arduino_build_632483\sketch\Test.ino.cpp.o:(.literal._Z5setupv+0x30): undefined reference to `camera_enable_out_clock(camera_config_t*)'
C:\Users\W\AppData\Local\Temp\arduino_build_632483\sketch\Test.ino.cpp.o:(.literal._Z5setupv+0x34): undefined reference to `camera_disable_out_clock()'
C:\Users\W\AppData\Local\Temp\arduino_build_632483\sketch\Test.ino.cpp.o: In function `setup()':
C:\Users\W\Documents\ESP32 Cam Pico Project\Code\Test/Test.ino:77: undefined reference to `camera_enable_out_clock(camera_config_t*)'
C:\Users\W\Documents\ESP32 Cam Pico Project\Code\Test/Test.ino:81: undefined reference to `camera_disable_out_clock()'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board AI Thinker ESP32-CAM.

The routines I am calling seem to exist in the binary file libesp32-camera.a which exists on my PC - but how do I use them?

My test code isolating this issue is below:

#include <esp_camera.h>

// Forward declare cam clock stop/start. 
esp_err_t camera_enable_out_clock(camera_config_t *config);
void camera_disable_out_clock();

// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32 //
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0 //
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22 //

#define SerialDebug Serial    // Nice names for Serial ports

camera_config_t cam_config;
camera_fb_t *fb; // Declare the variable for the pointer to the framebuffer

void setup() {
    SerialDebug.begin(115200);
    
    setCpuFrequencyMhz(20); // Save power by going slowly

    // Init out Camera
    cam_config.ledc_channel = LEDC_CHANNEL_0;
    cam_config.ledc_timer = LEDC_TIMER_0;
    cam_config.pin_d0 = Y2_GPIO_NUM;
    cam_config.pin_d1 = Y3_GPIO_NUM;
    cam_config.pin_d2 = Y4_GPIO_NUM;
    cam_config.pin_d3 = Y5_GPIO_NUM;
    cam_config.pin_d4 = Y6_GPIO_NUM;
    cam_config.pin_d5 = Y7_GPIO_NUM;
    cam_config.pin_d6 = Y8_GPIO_NUM;
    cam_config.pin_d7 = Y9_GPIO_NUM;
    cam_config.pin_xclk = XCLK_GPIO_NUM;
    cam_config.pin_pclk = PCLK_GPIO_NUM;
    cam_config.pin_vsync = VSYNC_GPIO_NUM;
    cam_config.pin_href = HREF_GPIO_NUM;
    cam_config.pin_sscb_sda = SIOD_GPIO_NUM;
    cam_config.pin_sscb_scl = SIOC_GPIO_NUM;
    cam_config.pin_pwdn = PWDN_GPIO_NUM;
    cam_config.pin_reset = RESET_GPIO_NUM;
    cam_config.xclk_freq_hz = 20000000;
    cam_config.pixel_format = PIXFORMAT_JPEG;
    //init with high specs to pre-allocate larger buffers
    if (psramFound()) {
      SerialDebug.println("  PS RAM Found.");
      cam_config.frame_size = FRAMESIZE_UXGA;
      cam_config.jpeg_quality = 10;
      cam_config.fb_count = 2;
    } else {
      SerialDebug.println("  Error: PS RAM Not Found.");
      cam_config.frame_size = FRAMESIZE_SVGA;
      cam_config.jpeg_quality = 12;
      cam_config.fb_count = 1;
    }

    // Overide the above for testing
    cam_config.frame_size = FRAMESIZE_VGA;
    cam_config.jpeg_quality = 10;  // Quality of JPEG output. 0-63 lower means higher quality
    cam_config.fb_count = 1;

    delay(10000); // doing stuff here
    
    esp_camera_init(&cam_config);

    SerialDebug.printf("  Taking picture:\n");
    setCpuFrequencyMhz(80); // Camera seems to need more than 40mhz to get a picture
    camera_enable_out_clock(&cam_config);
    delay(500);
    fb = esp_camera_fb_get(); // Get the current frame buffer
    delay(500);
    camera_disable_out_clock();
    setCpuFrequencyMhz(20);
    SerialDebug.printf("  Picture length: %d\n", fb->len);
    
    delay(10000); // doing stuff here
    
    esp_camera_fb_return(fb);
  
}



void loop() {
  
}

Thanks very much in advance.
Kevin

I would expect those functions to be part of the camera library you're using. Try commenting those two prototypes out.

Thanks @Wildbill, but unfortunately not, with them commented out:

Test:81:40: error: 'camera_enable_out_clock' was not declared in this scope
     camera_enable_out_clock(&cam_config);
                                        ^
Test:85:30: error: 'camera_disable_out_clock' was not declared in this scope
     camera_disable_out_clock();
                              ^
exit status 1
'camera_enable_out_clock' was not declared in this scope

I see camera_disable_out_clock defined in xclk.c in the github repository for the camera. There’s an xclck.h too. Maybe include that?

"No such file or directory" :frowning:

To be fair, I can't find it in the file system either!

Thanks
Kevin

I did not have a problem getting the ESP32CAM to sleep, I used the code described here;

I made it work as a high altitude balloon tracker (HAB) , with it reading a GPS, measuring the battery voltage, taking a picture, saving it to SD, sending the HAB tracker payload as LoRa and FSK RTTY, a tiny 100mAhr battery lasted 596 minutes.

Sleep current of the tracker was around 1.14mA without the GPS.

More details here;

https://stuartsprojects.github.io/2020/08/07/ESP32CAM-as-a-Balloon-Tracker-Really!-Copy.html

No full code published yet however.

Thanks Stuart, really useful stuff. I have looked at deep sleep, but I have other activities to do most of the time so couldn't gain much advantage from it. Really looking to stay awake and running, but minimise the power whilst doing so - hence looking to reduce the camera power consumption.

P.S. I have confirmed, as brettbeeson did here, that using the power disable switch doesn't allow the camera to come back on again, hence needed to explore the XCLK method.

Thanks very much
Kevin

KevWal:
Really looking to stay awake and running, but minimise the power whilst doing so - hence looking to reduce the camera power consumption.

And the easiest way of working out what is going on is to measure the current impact of each component.

Putting the ESP32 itself into deep sleep, or light sleep, does not put all other attached components to sleep, so the code in the tutorial I linked to must be putting the camera to sleep once a photo has been taken, and it does work, so it must be worth a look.

Hi Stuart

The Camera only starts to draw the majority of its power after it has been Initialised. A Deep Sleep (which is basically a processor power down / reset) must undo that initialisation and hence reduce the power draw...

I guess I could power down the camera, and then do a deep sleep reboot before I need to take a picture again - but I would rather not go there as there is a simpler and cleaner way!
The stopping the XCLK method described here can work, I just don't understand the Arduinio / ESP32 ecosystem well enough to know what I am doing wrong, trying to use those functions that are declared privately inside the binary of the library...

P.S. Do you have a DORJI DRF1278F Module Eagle footprint by chance - I cant find one anywhere....

Cheers
Kevin

It's not something I have seen in Arduino world before but obviously it's common outside. Usually any functionality you need is open source and provided as a library, so all you need do is grab the appropriate version and you are good to go. Having a binary is odd, at least to me.

I still suspect that you simply don't have the appropriate libraries installed. It may be though that you can use the tool chain to link the binary you have. gcc can do it, I don't know how to persuade the IDE to do it though. Where did the binary come from?

Hi Bill

The binary comes from the Arduino ESP32 Boards manager - https://dl.espressif.com/dl/package_esp32_index.json

It lives here: C:\Users\W\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\sdk\lib

The source is here: GitHub - espressif/arduino-esp32: Arduino core for the ESP32 and here: GitHub - espressif/esp32-camera

Thanks very much
Kevin

KevWal:
Hi Stuart

The Camera only starts to draw the majority of its power after it has been Initialised. A Deep Sleep (which is basically a processor power down / reset) must undo that initialisation and hence reduce the power draw…

I dont see that the deep sleep of the processor has any impact on whether the camera is on or off, that must be happening before the ESP32 code for deep sleep is called.

I was unable to find a Eagle layout for DRF1278F, so like with a lot of components you need to make your own layouts.

DRF1278F.zip (6.41 KB)

Thank you very much for the Eagle layout! Saved me an hour or two of pain laying that out correctly...

Cheers
Kevin

FYI, I have solved the "calling XCLK routines from Arduino" issue, they need to be declared like so:

extern "C" { esp_err_t camera_enable_out_clock(camera_config_t *config); void camera_disable_out_clock(); }

It saves a bit of power and the clock can still be restarted and a picture taken again.

It doesn't save as much as powering down the camera and then powering it up again, but I can't get that to work, it always reboots on me, even though I call esp_camera_deinit() before powering down the camera:

22:36:51.943 -> [E][camera.c:1335] esp_camera_fb_get(): Failed to get the frame on time!
22:36:52.491 -> Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.

Cheers
Kevin