diff --git a/docs/esp32/img/esp32_cam.jpg b/docs/esp32/img/esp32_cam.jpg new file mode 100644 index 000000000000..de1430abbf01 Binary files /dev/null and b/docs/esp32/img/esp32_cam.jpg differ diff --git a/ports/esp32/boards/ESP32_CAM/board.json b/ports/esp32/boards/ESP32_CAM/board.json new file mode 100644 index 000000000000..771bddc2fdfe --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/board.json @@ -0,0 +1,25 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "External Flash", + "WiFi" + ], + "images": [ + "esp32_cam.jpg" + ], + "mcu": "esp32", + "product": "ESP32 / CAM", + "thumbnail": "", + "url": "https://www.espressif.com/en/products/modules", + "variants": { + "IDF3": "Compiled with IDF 3.x", + "D2WD": "ESP32 D2WD", + "SPIRAM": "Support for SPIRAM / WROVER", + "UNICORE": "ESP32 Unicore", + "OTA": "Support for OTA" + }, + "vendor": "Espressif" +} diff --git a/ports/esp32/boards/ESP32_CAM/board.md b/ports/esp32/boards/ESP32_CAM/board.md new file mode 100644 index 000000000000..b2d2ec978250 --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/board.md @@ -0,0 +1,20 @@ +The following files are firmware that work on the ESP32 CAM module using +an OV2640 camera. + +ESP32 CAM has PSRAM (aka spiram) but the ESP32-S chip on it does not support bluetooth + +To build this from source, load your env vars:- + + . $HOME/esp/esp-idf/export.sh + +Open a shell in the folder micropython/ports/esp32 + +and run this command:- + + git clone https://github.com/lemariva/micropython-camera-driver.git + git clone https://github.com/espressif/esp32-camera + make USER_C_MODULES=../micropython-camera-driver/src/micropython.cmake BOARD=ESP32_CAM all + +-or- (for ota support):- + + make USER_C_MODULES=../micropython-camera-driver/src/micropython.cmake BOARD=ESP32_CAM MICROPY_BOARD_VARIANT=OTA diff --git a/ports/esp32/boards/ESP32_CAM/mpconfigboard.cmake b/ports/esp32/boards/ESP32_CAM/mpconfigboard.cmake new file mode 100644 index 000000000000..5ff5fabe996d --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/mpconfigboard.cmake @@ -0,0 +1,44 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.spiram +) + +# boards/sdkconfig.ble +# boards/ESP32_CAM/sdkconfig.esp32cam + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_MCU_NAME="ESP32" + # Disable some options to reduce firmware size. + # MICROPY_OPT_COMPUTED_GOTO=0 + # MICROPY_PY_NETWORK_LAN=0 + # ESP32-CAMERA + CONFIG_OV2640_SUPPORT=y + MICROPY_HW_BOARD_NAME="ESP32S CAM module with SPIRAM and OV2640" + CONFIG_COMPILER_OPTIMIZATION_SIZE=n + CONFIG_COMPILER_OPTIMIZATION_PERF=y +) + + +if(MICROPY_BOARD_VARIANT STREQUAL "OTA") + set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC/sdkconfig.ota + ) + + list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_BOARD_NAME="Generic ESP32 module with OTA" + ) +endif() + + + +if(MICROPY_BOARD_VARIANT STREQUAL "UNICORE") + set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_DEFAULTS} + boards/ESP32_GENERIC/sdkconfig.unicore + ) + + list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_MCU_NAME="ESP32-UNICORE" + ) +endif() diff --git a/ports/esp32/boards/ESP32_CAM/mpconfigboard.h b/ports/esp32/boards/ESP32_CAM/mpconfigboard.h new file mode 100644 index 000000000000..22cf1fcdc2b1 --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/mpconfigboard.h @@ -0,0 +1,13 @@ +// Both of these can be set by mpconfigboard.cmake if a BOARD_VARIANT is +// specified. + +#ifndef MICROPY_HW_BOARD_NAME +#define MICROPY_HW_BOARD_NAME "ESP32S CAM module with PSRAM and OV2640" +#endif + +#define MICROPY_PY_BLUETOOTH (0) +#define MODULE_CAMERA_ENABLED (1) + +#ifndef MICROPY_HW_MCU_NAME +#define MICROPY_HW_MCU_NAME "ESP32" +#endif diff --git a/ports/esp32/boards/ESP32_CAM/photo.py b/ports/esp32/boards/ESP32_CAM/photo.py new file mode 100644 index 000000000000..dc0babe214cd --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/photo.py @@ -0,0 +1,166 @@ +# photo.py + +__version__ = '1.0.1' # Major.Minor.Patch + +# Ways to run this program:- + +# 1. With ampy +# ampy --port $PORT run bin/photo.py +# example output:- +# photo fn=out.jpg size=22((default)) quality=10 +# Length of buf: 23579 + +# 2. From REPL shell +# >>> ARGV=["pic.jpg","5","10"];exec(open("bin/photo.py").read()) +# example output:- +# photo fn=pic.jpg size=5(FRAME_QVGA) quality=10 +# Length of buf: 9495 + +# 3. using mipyshell +# To run this program with arguments, install https://github.com/vsolina/mipyshell +# and save this file as bin/photo.py - then (for size 5 and quality 10):- +# photo outfile.jpg 5 10 + + +import camera + + +def __main__(args): + capture(args[2:]) # mipyshell first 2 arguments are "python" and "photo.py" + +def capture(args): + fn="out.jpg" + quality=10 + size=22 + + camera_frames = { 0: {"name": "FRAME_96X96", "value": camera.FRAME_96X96}, + 1: {"name": "FRAME_QQVGA", "value": camera.FRAME_QQVGA}, + 2: {"name": "FRAME_QCIF", "value": camera.FRAME_QCIF}, + 3: {"name": "FRAME_HQVGA", "value": camera.FRAME_HQVGA}, + 4: {"name": "FRAME_240X240", "value": camera.FRAME_240X240}, + 5: {"name": "FRAME_QVGA", "value": camera.FRAME_QVGA}, + 6: {"name": "FRAME_CIF", "value": camera.FRAME_CIF}, + 7: {"name": "FRAME_HVGA", "value": camera.FRAME_HVGA}, + 8: {"name": "FRAME_VGA", "value": camera.FRAME_VGA}, + 9: {"name": "FRAME_SVGA", "value": camera.FRAME_SVGA}, + 10: {"name": "FRAME_XGA", "value": camera.FRAME_XGA}, + 11: {"name": "FRAME_HD", "value": camera.FRAME_HD}, + 12: {"name": "FRAME_SXGA", "value": camera.FRAME_SXGA}, + 13: {"name": "FRAME_UXGA", "value": camera.FRAME_UXGA}, + 14: {"name": "FRAME_FHD", "value": camera.FRAME_FHD}, + 15: {"name": "FRAME_P_HD", "value": camera.FRAME_P_HD}, + 16: {"name": "FRAME_P_3MP", "value": camera.FRAME_P_3MP}, + 17: {"name": "FRAME_QXGA", "value": camera.FRAME_QXGA}, + 18: {"name": "FRAME_QHD", "value": camera.FRAME_QHD}, + 19: {"name": "FRAME_WQXGA", "value": camera.FRAME_WQXGA}, + 20: {"name": "FRAME_P_FHD", "value": camera.FRAME_P_FHD}, + 21: {"name": "FRAME_QSXGA", "value": camera.FRAME_QSXGA}, + 22: {"name": "(default)", "value": None} } + + + if len(args) > 0: + fn = args[0] + + ## ESP32-CAM (default configuration) - https://bit.ly/2Ndn8tN + camera.init(0, format=camera.JPEG, fb_location=camera.PSRAM) + + if len(args) > 1: + size = int(args[1]) + camera.framesize(camera_frames[size]["value"]) + + if len(args) > 2: + quality = int(args[2]) + camera.quality(quality) + + print("photo fn={} size={}({}) quality={}".format(fn,size,camera_frames[size]["name"],quality)) + + # AI-Thinker esp32-cam board + #ai_thinker = {PIN_PWDN:32, PIN_RESET:-1, PIN_XCLK:0, PIN_SIOD:26, PIN_SIOC:27, PIN_D7:35, PIN_D6:34, PIN_D5:39, PIN_D4:36, PIN_D3:21, PIN_D2:19, PIN_D1:18, PIN_D0:5, PIN_VSYNC:25, PIN_HREF:23, PIN_PCLK:22, XCLK_MHZ:16, PIXFORMAT:5, FRAMESIZE:10, JPEG_QUALITY:10, FB_COUNT:1, } + + ## M5Camera (Version B) - https://bit.ly/317Xb74 + # camera.init(0, d0=32, d1=35, d2=34, d3=5, d4=39, d5=18, d6=36, d7=19, format=camera.JPEG, framesize=camera.FRAME_VGA, xclk_freq=camera.XCLK_10MHz, href=26, vsync=25, reset=15, sioc=23, siod=22, xclk=27, pclk=21, fb_location=camera.PSRAM) #M5CAMERA + + ## T-Camera Mini (green PCB) - https://bit.ly/31H1aaF + # import axp202 # source https://github.com/lewisxhe/AXP202_PythonLibrary + # USB current limit must be disabled (otherwise init fails) + #axp=axp202.PMU( scl=22, sda=21, address=axp202.AXP192_SLAVE_ADDRESS ) + #limiting=axp.read_byte( axp202.AXP202_IPS_SET ) + #limiting &= 0xfc + #axp.write_byte( axp202.AXP202_IPS_SET, limiting ) + + #camera.init(0, d0=5, d1=14, d2=4, d3=15, d4=18, d5=23, d6=36, d7=39, format=camera.JPEG, framesize=camera.FRAME_VGA, xclk_freq=camera.XCLK_20MHz, href=25, vsync=27, reset=-1, pwdn=-1, sioc=12, siod=13, xclk=32, pclk=19) + + + # The parameters: format=camera.JPEG, xclk_freq=camera.XCLK_10MHz are standard for all cameras. + # You can try using a faster xclk (20MHz), this also worked with the esp32-cam and m5camera + # but the image was pixelated and somehow green. + + + # ## Other settings: + # # flip up side down + # camera.flip(1) + # # left / right + # camera.mirror(1) + + # # framesize + # camera.framesize(camera.FRAME_240x240) + # # The options are the following: + # # FRAME_96X96 FRAME_QQVGA FRAME_QCIF FRAME_HQVGA FRAME_240X240 + # # FRAME_QVGA FRAME_CIF FRAME_HVGA FRAME_VGA FRAME_SVGA + # # FRAME_XGA FRAME_HD FRAME_SXGA FRAME_UXGA FRAME_FHD + # # FRAME_P_HD FRAME_P_3MP FRAME_QXGA FRAME_QHD FRAME_WQXGA + # # FRAME_P_FHD FRAME_QSXGA + # # Check this link for more information: https://bit.ly/2YOzizz + # + # # special effects + # camera.speffect(camera.EFFECT_NONE) + # # The options are the following: + # # EFFECT_NONE (default) EFFECT_NEG EFFECT_BW EFFECT_RED EFFECT_GREEN EFFECT_BLUE EFFECT_RETRO + # + # # white balance + # camera.whitebalance(camera.WB_NONE) + # # The options are the following: + # # WB_NONE (default) WB_SUNNY WB_CLOUDY WB_OFFICE WB_HOME + # + # # saturation + # camera.saturation(0) + # # -2,2 (default 0). -2 grayscale + # + # # brightness + # camera.brightness(0) + # # -2,2 (default 0). 2 brightness + # + # # contrast + # camera.contrast(0) + # #-2,2 (default 0). 2 highcontrast + # + # # quality + # camera.quality(10) + # # 10-63 lower number means higher quality + # + + buf = camera.capture() + + if buf: + print("Length of buf:", len(buf)) + + if fn: + with open(fn, 'wb') as f: + f.write(buf) + else: + print("not written - no filename given") + #print("Contents of buf in hex:", buf.hex()) + + else: + print("Capture failed (too big for PSRAM?") + + #print("open http://esp32-cam-05.local/foo.jpg") + + camera.deinit() + +try: + # if 'ARGV' in locals(): + eval('capture(ARGV)') # ARGV is supplied by caller thusly: ARGV=["pic.jpg","5","10"];exec(open("bin/photo.py").read()) +except: # Exception as e: + # print(e) # name 'ARGV' isn't defined + capture([]) diff --git a/ports/esp32/boards/ESP32_CAM/sdkconfig.d2wd b/ports/esp32/boards/ESP32_CAM/sdkconfig.d2wd new file mode 100644 index 000000000000..2ac983693d3f --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/sdkconfig.d2wd @@ -0,0 +1,17 @@ +# Optimise using -Os to reduce size +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_PERF=n +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y + +# Change maximum log level to error, to reduce firmware size. +CONFIG_LOG_MAXIMUM_LEVEL_ERROR=y +CONFIG_LOG_MAXIMUM_LEVEL_INFO=n + +# Disable SPI Ethernet driver to reduce firmware size. +CONFIG_ETH_USE_SPI_ETHERNET=n + +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-2MiB.csv" diff --git a/ports/esp32/boards/ESP32_CAM/sdkconfig.ota b/ports/esp32/boards/ESP32_CAM/sdkconfig.ota new file mode 100644 index 000000000000..352dd96f2323 --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/sdkconfig.ota @@ -0,0 +1,7 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB-ota.csv" + +# Reduce firmware size to fit in the OTA partition. +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_PERF=n +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y diff --git a/ports/esp32/boards/ESP32_CAM/sdkconfig.unicore b/ports/esp32/boards/ESP32_CAM/sdkconfig.unicore new file mode 100644 index 000000000000..f0b0b5e03dd4 --- /dev/null +++ b/ports/esp32/boards/ESP32_CAM/sdkconfig.unicore @@ -0,0 +1 @@ +CONFIG_FREERTOS_UNICORE=y