Mailbox Bot 1 .0 | IoT for the lazy

I recently took an ESP32-Cam module and built it into my mailbox. Got tired of walking outside to check it only to find it’s empty.
It was a mostly straight forward process, however, I did run into a few snags so I thought I’d document it a little in case you are stuck in a similar project. The camera takes images of the inside of the mailbox a few times a day and uploads them to an FTP server.
It is powered by a single 18650 lithium-ion cell that is kept charged by a small solar panel.

 

Check out the Youtube video

Oh yeah, I made a video about it. It’s fairly general and I skimmed over a a few of the technical details, hence, this page.

In general

This is how everything is hooked up. Nothing particularly special here. Note the output of a voltage divider is going to an input on the camera module. It is used to monitor the battery voltage.

Regulations

I am not using the onboard regulator of the ESP module (AMS1117). It is too inefficient for a battery powered device. There is a world of regulators out there but I am just using one that I have readily available. The XC6210B332MR. It is a 700mA, 3.3V fixed output, linear voltage regulator. It has a tiny quiescent current of only 55 uA and a dropout of 100mV.

I used a fairly beefy reservoir cap (1000uF) since the module can get quite power hungry when components like the camera or Wifi are enabled. There also is a bypass cap on the battery monitor that smooths out the readings. They are very noisy without. Again, all pretty standard stuff.

Battery monitor

There really isn’t all that much choice when it comes to available GPIO pins on the ESP32-Cam module. Most of the pins are used by the camera, the SD card or the onboard flash and the ones that are not are mostly special purpose pins that can only be used in certain situations. So I had to switch the SD card to 1 Bit mode which frees up a few pins and I managed to use GPIO13 as an ADC input for the battery monitor.

 

Code

If you are interested in checking out my code, I dropped it on my Github (see the link). It is quite rudimentary and could deal with error states a bit better but it’s not driving a car or anything 😉
I did modify the ftp library and gave it a new name to add a function to upload a file directly from the SD card.
A lot of this code originates from all over the web. Check the references below for links.

Simplified, the code does this:

First it checks what the battery is doing.

Github
  float batv = measure_battery_voltage();

Then takes a picture.

  camera_fb_t *fb = cam_capture();

Powers up the Wifi.

    WiFi.begin( WIFI_SSID, WIFI_PASS );
    for(int c = 0; c < 100 && WiFi.status() != WL_CONNECTED; ++c) {
        delay(500);
    }

Connects to the FTP server and uploads the image.

    ftp.OpenConnection();

    // Create the new file and send the image
    ftp.InitFile("Type I");
    String filename = "mailbox." + String(pictureNumber) +".jpg";
    ftp.NewFile(filename.c_str());
    ftp.WriteData( fb->buf, fb->len );
    ftp.CloseFile();

Grab the file stamp of the file we just uploaded.

    ftp.GetLastModifiedTime(filename.c_str(), ftime);

Check if there is a file called Mailboxbot.bin on the FTP. If there is, get it und flash it.

  long szRemote = ftp.GetFileSize(FIRWARE_FILENAME);
  if(szRemote > 0) // file present on ftp
  {
    // delete file on sd if it exists
    SD_MMC.remove("/" FIRWARE_FILENAME);
    File outfile = SD_MMC.open("/" FIRWARE_FILENAME, FILE_WRITE);
    if(outfile)
    {    
      ftp.InitFile("Type I");
      bool res = ftp.DownloadFileToStream(FIRWARE_FILENAME, outfile);
      long szLocal = outfile.size(); 
      outfile.close();

      if(szLocal==szRemote) {
        log += "new firmware downloaded\n";
        ftp.DeleteFile(FIRWARE_FILENAME);   // delete remote image
      }else {
        log += "firmware download failed\n";
        SD_MMC.remove("/" FIRWARE_FILENAME);    // don't leave this around if it's not the correct size        
      }
    }

    // try to flash firmware from SD, nothing happens if there is no file      
    fw_updated = update_from_sd(SD_MMC);

And finally, go to sleep, or restart if the firmware was updated.

  if(fw_updated) {
    ESP.restart();
  }

  esp_sleep_enable_timer_wakeup(next_wakeup * 60ULL * 1000 * 1000); 
  esp_deep_sleep_start();

Obviously, I omitted a bunch of code to keep it civil. Again, check the git repo for the full source.

References

This project leans heavily on other people’s efforts. Here are links to the sites I found very helpful.

Randomnerdtutorials, camera, sleep, schematics, pinouts, and more..
https://randomnerdtutorials.com/esp32-cam-ai-thinker-pinout/
Firmware Update
https://github.com/espressif/arduino-esp32/blob/master/libraries/Update/examples/SD_Update/SD_Update.ino
FTP client
https://github.com/ldab/ESP32_FTPClient

 

Thanks for all the fish

I hope this is helpful. The ESP32-CAM module is quite limited but for the price and ease of use it’s hard to beat. There is almost no reason not to get a handful of them and build all sorts of things with it. Remote controlled robots with video over Wifi, security cams, wildlife monitoring, …..

Hit me up on Facebook and let me know what you are building.