I am playing with my Adafruit Memento a.k.a. PyCamera which means digging into Adafruit's CircuitPython library and sample code. I first looked over its photography parameters under software control, and now I move on to the software implementation. Skimming through source code for PyCamera CircuitPython library, I see its functionality were largely stitched together from existing Adafruit CircuitPython libraries corresponding to hardware components on board. The notable exception is that PyCamera has its own code to interface with the OV5640 camera module instead of leveraging an existing OV5640 CircuitPython library. This is interesting, why might Adafruit choose to split their OV5640 support?

PyCamera library is very much a "batteries included" bundle optimized to a particular piece of hardware, and the split fits with this design priority. Not only does the library expose interface to the camera module and the LCD screen module, it has code optimized for them to work together. The viewfinder capability is one example. This is literally all it takes to continuously send camera sensor data to LCD:

import adafruit_pycamera

pycam = adafruit_pycamera.PyCamera()
while True:
    pycam.blit(pycam.continuous_capture())

Adafruit's OV5640 library does not have a continuous_capture() method, and their ST7789 library lacks a blit() method. Together these two PyCamera-specific APIs minimize delay between camera capture and LCD output so we can have a responsive viewfinder screen. Code comments in blit() explain it bypasses their displayio graphics library for speed, but incurs the tradeoff of not playing well with overlapping graphics elements. To mitigate this problem, camera UI text are not rendered by camera app code. They are rendered by make_camera_ui() within PyCamera library to ensure they stay clear of blit() zone. This was not how I had expected the implementation to go, interesting!

Another difference I found is that PyCamera OV5640 code is built on top of espcamera module, restricting it to run on Espressif microcontrollers. In contrast, the standalone OV5640 library uses an abstraction layer called imagecapture. Searching in CircuitPython source code, I see implementations for Atmel SAMD and Raspberry Pi Pico microcontrollers, but not for Espressif. I'm sure it would have been possible to add Espressif support to existing OV5640 library, but I can see how it was easier for PyCamera to go its own way. It knows it has an ESP32-S3 and it wants speed optimizations tailored to hardware.

While I can understand this approach to designing PyCamera library, it does make exploration more difficult. A big monolithic library means it's harder to compose its elements in different ways to experiment off the designed path. I want a quick experiment, and the monolithic nature means I have to design something that largely resembles existing PyCamera sample code but with my added twist.


Appendix: On the Arduino side, PyCamera is also tied to Espressif's camera library. For the OV5640 by itself, Adafruit's guide to their breakout module didn't mention Arduino at all.