Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Ai Thinker ESP32-CAM boards #9231

Merged
merged 6 commits into from May 15, 2024

Conversation

gitcnd
Copy link

@gitcnd gitcnd commented May 9, 2024

This adds support for the cheap-and-popular ESP32-CAM boards.

Board Feature			Circuitpython Status
=============                   ====================
Take photos with camera		Tested + Working
Mount SD card			Tested + Working
Wifi				Tested + Working
Inbuilt RED LED (back of board)	Tested + Working
Flashlight (front of board)	Tested + Working
ESP32-CAM-MB Button		Tested + Working
Bluetooth			Not tested
Others				There are no other features I know of

Here is my working sample script which tests the SD Card:

import board
import busio
import sdcardio
import storage

spi = board.SD_SPI()
cs = board.SD_CS

sdcard = sdcardio.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")

Here is my working sample script which takes a photo (and saves it to "out.jpg" in flash)

import io
import os
import board
import espcamera
import socketpool

cam = espcamera.Camera(
    data_pins=board.CAMERA_DATA,
    external_clock_pin=board.CAMERA_XCLK,
    pixel_clock_pin=board.CAMERA_PCLK,
    vsync_pin=board.CAMERA_VSYNC,
    href_pin=board.CAMERA_HREF,
    powerdown_pin=board.CAMERA_PWDN,
    reset_pin=None,
    i2c=board.I2C(),
    external_clock_frequency=20_000_000,
    pixel_format=espcamera.PixelFormat.JPEG,
    frame_size=espcamera.FrameSize.SVGA,
    # jpeg_quality=0,
    # framebuffer_count=0,
    # grab_mode=espcamera.GrabMode.WHEN_EMPTYi
    )

if True:
    frame = cam.take(1)
    fn="out.jpg"
    if isinstance(frame, memoryview):
        jpeg = frame
        print(f"Captured {len(jpeg)} bytes of jpeg data")

        if fn:
            with open(fn, 'wb') as f:
                f.write(jpeg)
            print(f"written to {fn}")
        else:
            print("not written - no filename given")

This is my first PR - if I've done anything wrong, please let me know, along with more-than-average details about what I'm supposed to do to fix it, thanks!

Copy link

@RetiredWizard RetiredWizard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably varies from board to board but you might consider adding aliases for the pins that are silk screened.i.e.:

    { MP_ROM_QSTR(MP_QSTR_SDIO_CLK), MP_ROM_PTR(&pin_GPIO14)},
    { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14)},

@RetiredWizard
Copy link

This may already be clear but since you said this was your first board, I just wanted to be sure... The silkscreen does show the actual GPIO names IO14, etc so a programmer can access them by those names using microcontroller.pin, although most CircuitPython users will be looking for the silk screen name in board.xxxx (board.IO14). In order to support that you would need to add a second line for each of the silk screened pins with both the logical name and the silk screened name.

    { MP_ROM_QSTR(MP_QSTR_SDIO_CLK), MP_ROM_PTR(&pin_GPIO14)},
    { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14)},

I'm pretty sure not all boards add this type of alias though.

@gitcnd
Copy link
Author

gitcnd commented May 9, 2024

... The silkscreen does show the actual GPIO names IO14, etc

I'm reluctant to introduce a whole pile of non-standard non-portable aliases simply to drop the leading "G" off everything (and waste memory and add confusion)... while I can see where circuitpython is going with that, I would very much prefer NOT to encourage that kind of thing. I'm working currently on the ESP32-WROVER-DEV board which (minus the SD card) is almost identical. Code written for either will run fine, unchanged, on the other... so long as people don't use silly pin names. The latter drops the "IO" prefix on its silkscreen, which will add a whole extra level of extreme confusion if I go down this route ...

  { MP_ROM_QSTR(MP_QSTR_16), MP_ROM_PTR(&pin_GPIO16) 

will resolve to an int instead of a string in those cases...

That said - I'm happy to keep you happy. This is my first time doing this stuff.

Update: You can't use numeric silkscreens:-

Adafruit CircuitPython 9.1.0-beta.1-18-g781c577745 on 2024-05-09; Espressif ESP32-WROVER-DEV with ESP32
>>> 
>>> import board
>>> dir(board)
['__class__', '__name__', '1', '3', 'BUTTON', 'CAMERA_DATA', 'CAMERA_DATA2', 'CAMERA_DATA3', 'CAMERA_DATA4', 'CAMERA_DATA5', 'CAMERA_DATA6', 'CAMERA_DATA7', 'CAMERA_DATA8', 'CAMERA_DATA9', 'CAMERA_HREF', 'CAMERA_PCLK', 'CAMERA_PWDN', 'CAMERA_SIOC', 'CAMERA_SIOD', 'CAMERA_VSYNC', 'CAMERA_XCLK', 'FLASHLIGHT', 'I2C', 'LED', 'LED_INVERTED', 'SCL', 'SDA', '__dict__', 'board_id']
>>> board.1
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax
>>> board.BUTTON
board.BUTTON
>>> 

@gitcnd
Copy link
Author

gitcnd commented May 9, 2024

Turns out a very large number of other ESP32 ports introduce IOnn aliases... so in the interests of letting those folks run their code unchanged on this port, I added the aliases.

Copy link

@RetiredWizard RetiredWizard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I loaded up the artifact from this PR and tested WiFi, SD Card, LED and Camera operations. I didn't find any issues other than the annoyingly bright "flashlight" led defaulting to on.

@gitcnd
Copy link
Author

gitcnd commented May 10, 2024

FYI... More handy info about these boards is here: https://randomnerdtutorials.com/esp32-cam-ai-thinker-pinout/

@RetiredWizard
Copy link

I loaded up the artifact with the flashlight fix and everything seems good, no more bright light 😁

Copy link

@RetiredWizard RetiredWizard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, I've done some basic testing and everything seems to work fine. I'm actually using the latest artifact in a project that allows me to snap a photo of the charge state of my solar batteries using adafruit.io. That allows me to turn on/off the inverter when I'm away from home depending on the charge state. I first tried using the Memento but ran into an issue so it was fortunate that this PR was in place 😁

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the testing @RetiredWizard and the PR @gitcnd

@tannewt tannewt merged commit 7e57fa8 into adafruit:main May 15, 2024
15 checks passed
@bill88t
Copy link

bill88t commented May 23, 2024

@tannewt As a note, this board used - instead of _ for the spaces for the board name making it not a valid python identifier (Reminder, #6979 and #7267).

Is it too late to fix this one?

@tannewt
Copy link
Member

tannewt commented May 23, 2024

@tannewt As a note, this board used - instead of _ for the spaces for the board name making it not a valid python identifier (Reminder, #6979 and #7267).

Is it too late to fix this one?

I think the first step is adding a test that will fail if the board name isn't an id. Otherwise we'll continue to have this problem. After we have that, we can fix all the cases that need to be fixed.

@gitcnd
Copy link
Author

gitcnd commented May 23, 2024

Others:-

raytac_mdbt50q-db-40
raytac_mdbt50q-rx
TG-Watch
ai_thinker_esp32-c3s
ai_thinker_esp32-c3s-2m
ai-thinker-esp32-cam
beetle-esp32-c3
columbia-dsl-sensor
crcibernetica-ideaboard
lilygo_ttgo_t-01c3
lilygo_ttgo_t-oi-plus
morpheans_morphesp-240
zrichard_rp2.65-f
cp32-m4
kicksat-sprite
silicognition-m4-shim

@gitcnd
Copy link
Author

gitcnd commented May 23, 2024

@tannewt As a note, this board used - instead of _ for the spaces for the board name making it not a valid python identifier (Reminder, #6979 and #7267).

Is it too late to fix this one?

Why would it need to be a valid python identifier?

If someone wants this to be a rule and actually has a sensible reason why - all the board porting instructions need to be changed so that everyone knows this... it's actually quite confusing already - there's at least 3 names involved (a human name with spaces, some kind of identifier constructed from CIRCUITPY_CREATOR_ID and CIRCUITPY_CREATION_ID, and the folder name where the files live) - with none of this stuff being explained in the porting instructions...

Oh yeah - and there appear to be aliases for variants as well, also not explained I think.

@tannewt
Copy link
Member

tannewt commented May 24, 2024

@tannewt As a note, this board used - instead of _ for the spaces for the board name making it not a valid python identifier (Reminder, #6979 and #7267).
Is it too late to fix this one?

Why would it need to be a valid python identifier?

Some folks use the board id in a module name.

If someone wants this to be a rule and actually has a sensible reason why - all the board porting instructions need to be changed so that everyone knows this... it's actually quite confusing already - there's at least 3 names involved (a human name with spaces, some kind of identifier constructed from CIRCUITPY_CREATOR_ID and CIRCUITPY_CREATION_ID, and the folder name where the files live) - with none of this stuff being explained in the porting instructions...

Yes, agreed.

Oh yeah - and there appear to be aliases for variants as well, also not explained I think.

@gitcnd
Copy link
Author

gitcnd commented May 25, 2024

Some folks use the board id in a module name.

LOL - I was all fired up to argue against a "no hyphen" requirement because I couldn't think of any valid reason why... then you give a valid reason :-)

I'd estimate it's probably a days work to add the rule, fix the doc, and update all the boards currently using hyphens.

@dhalbert
Copy link
Collaborator

The board_id's are also used for https://circuitpython.org, so if you rename a board, you need to make that still work with both the 9.0.x and the 9.1.0 versions. There is a board_alias feature in the code for the website, but I'm not sure you can have more than one alias.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants