上一帖介绍增加屏幕和SD卡组件,并通过Arduino编写基础程序模块,为后面实验做准备。这一贴介绍在前面工作基础上,使用摄像头拍照,将图像缩小显示在屏幕上,将原图保存在SD卡上。
我预期的最终作品是一个鱼缸监视器,其中一个重要功能就是定时拍照鱼缸的图片并存储,本次实验实现这个功能。在上一贴中,已经实现图片缩小和显示,这次要实现拍照,存储,并和上一次的程序整合到一起。
最开始设计的基本流程是:a、设置摄像头参数;b、从摄像头获得图片数据;c、打开SD目录;d、建立图片文件;e、写入文件并关闭文件;f、读取文件;g、用《TJpg_Decoder》库缩小后显示到屏幕上;h、显示图片名称。
其中实现a-e拍照并存储的代码如下。
图1、拍照并存储图片
其中,使用EEPROM的目的是实现图片名称的连续存储,即使掉电也能接续上之前的名称序号继续存储,而不会导致名称重置覆盖掉之前的图片。
在调试过程中,我发现《TJpg_Decoder》库是支持从数组直接进行显示的,这样就不用频繁操作SD卡了,我又把流程进行了更改如下。
a、设置摄像头参数;b、从摄像头获得图片数据;c、用《TJpg_Decoder》库缩小后显示到屏幕上;d、显示图片名称;e、打开SD目录;f、建立图片文件;g、写入文件并关闭文件。
如此更改后,减少了一步读卡过程,提高了效率,减少SD卡寿命磨损。代码如下。
图2、
我设置了连续存储十张照片,并通过串口输出照片的相关信息,可以实时看到程序运行状态。串口输出效果如下。
图3、串口输出
存完的照片,在SD卡上的结果如下图所示。
图4、存到卡上的照片
整体代码如下。
- #include "esp_camera.h"
- #include
- #include "DFRobot_AXP313A.h"
- #include "Arduino.h"
- #include "FS.h"
- #include
- #include "SD.h"
- #include "SPI.h"
- // Include the jpeg decoder library
- #include
- #include // Hardware-specific library
- TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
- // This next function will be called during decoding of the jpeg file to
- // render each block to the TFT. If you use a different TFT library
- // you will need to adapt this function to suit.
- bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
- // Stop further decoding as image is running off bottom of screen
- if (y >= tft.height()) return 0;
- // This function will clip the image block rendering automatically at the TFT boundaries
- tft.pushImage(x, y, w, h, bitmap);
- // This might work instead if you adapt the sketch to use the Adafruit_GFX library
- // tft.drawRGBBitmap(x, y, bitmap, w, h);
- // Return 1 to decode next block
- return 1;
- }
- #define EEPROM_SIZE 1
- int pictureNumber = 0;
- unsigned long times = 0;
- unsigned long timess = 0;
- DFRobot_AXP313A axp;
- #define FORWARD 1
- #define BACKWARD -1
- #define PWDN_GPIO_NUM -1
- #define RESET_GPIO_NUM -1
- #define XCLK_GPIO_NUM 45
- #define SIOD_GPIO_NUM 1
- #define SIOC_GPIO_NUM 2
- #define Y9_GPIO_NUM 48
- #define Y8_GPIO_NUM 46
- #define Y7_GPIO_NUM 8
- #define Y6_GPIO_NUM 7
- #define Y5_GPIO_NUM 4
- #define Y4_GPIO_NUM 41
- #define Y3_GPIO_NUM 40
- #define Y2_GPIO_NUM 39
- #define VSYNC_GPIO_NUM 6
- #define HREF_GPIO_NUM 42
- #define PCLK_GPIO_NUM 5
- void setupCamera() {
- while (axp.begin() != 0) {
- Serial.println("init error");
- delay(1000);
- }
- axp.enableCameraPower(axp.eOV2640);
- camera_config_t config;
- config.ledc_channel = LEDC_CHANNEL_0;
- config.ledc_timer = LEDC_TIMER_0;
- config.pin_d0 = Y2_GPIO_NUM;
- config.pin_d1 = Y3_GPIO_NUM;
- config.pin_d2 = Y4_GPIO_NUM;
- config.pin_d3 = Y5_GPIO_NUM;
- config.pin_d4 = Y6_GPIO_NUM;
- config.pin_d5 = Y7_GPIO_NUM;
- config.pin_d6 = Y8_GPIO_NUM;
- config.pin_d7 = Y9_GPIO_NUM;
- config.pin_xclk = XCLK_GPIO_NUM;
- config.pin_pclk = PCLK_GPIO_NUM;
- config.pin_vsync = VSYNC_GPIO_NUM;
- config.pin_href = HREF_GPIO_NUM;
- config.pin_sscb_sda = SIOD_GPIO_NUM;
- config.pin_sscb_scl = SIOC_GPIO_NUM;
- config.pin_pwdn = PWDN_GPIO_NUM;
- config.pin_reset = RESET_GPIO_NUM;
- config.xclk_freq_hz = 20000000;
- config.frame_size = FRAMESIZE_UXGA; //FRAMESIZE_HVGA; //
- config.pixel_format = PIXFORMAT_JPEG;
- config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
- config.fb_location = CAMERA_FB_IN_PSRAM;
- config.jpeg_quality = 12;
- config.fb_count = 1;
- if (config.pixel_format == PIXFORMAT_JPEG) {
- if (psramFound()) {
- config.jpeg_quality = 10;
- config.fb_count = 1;
- config.grab_mode = CAMERA_GRAB_LATEST;
- } else {
- config.frame_size = FRAMESIZE_SVGA;
- config.fb_location = CAMERA_FB_IN_DRAM;
- }
- } else {
- config.frame_size = FRAMESIZE_240X240;
- #if CONFIG_IDF_TARGET_ESP32S3
- config.fb_count = 2;
- #endif
- }
- esp_err_t err = esp_camera_init(&config);
- if (err != ESP_OK) {
- Serial.printf("Camera init failed with error 0x%x", err);
- return;
- }
- if (psramFound()) {
- heap_caps_malloc_extmem_enable(20000);
- Serial.printf("PSRAM initialized. malloc to take memory from psram above this size");
- }
- }
- void SDinitialization() {
- if (!SD.begin(GDI_SDCS)) {
- Serial.println("Card Mount Failed");
- return;
- }
- uint8_t cardType = SD.cardType();
- if (cardType == CARD_NONE) {
- Serial.println("No SD card attached");
- return;
- }
- Serial.print("SD Card Type: ");
- if (cardType == CARD_MMC) {
- Serial.println("MMC");
- } else if (cardType == CARD_SD) {
- Serial.println("SDSC");
- } else if (cardType == CARD_SDHC) {
- Serial.println("SDHC");
- } else {
- Serial.println("UNKNOWN");
- }
- uint64_t cardSize = SD.cardSize() / (1024 * 1024);
- Serial.printf("SD Card Size: %lluMB\n", cardSize);
- }
- void tft_init(void) {
- // Initialise the TFT
- tft.begin();
- tft.setTextColor(0xFFFF, 0x0000);
- tft.fillScreen(TFT_BLACK);
- tft.setSwapBytes(true); // We need to swap the colour bytes (endianess)
- tft.setRotation(1); // portrait
- // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
- // TJpgDec.setJpgScale(1);//for 480*320
- TJpgDec.setJpgScale(4); //for 1600*1200
- // The decoder must be given the exact name of the rendering function above
- TJpgDec.setCallback(tft_output);
- }
- void setup(void) {
- Serial.begin(115200);
- // Set all chip selects high to avoid bus contention during initialisation of each peripheral
- pinMode(GDI_CS, OUTPUT);
- pinMode(GDI_TCS, OUTPUT);
- pinMode(GDI_SDCS, OUTPUT);
- digitalWrite(GDI_TCS, HIGH); // Touch controller chip select (if used)
- digitalWrite(GDI_CS, HIGH); // TFT screen chip select
- digitalWrite(GDI_SDCS, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
- setupCamera();
- SDinitialization();
- tft_init();
- EEPROM.begin(EEPROM_SIZE);
- EEPROM.write(0, pictureNumber);
- EEPROM.commit();
- }
- void Imagestorage() {
- camera_fb_t *fb = NULL;
- fb = esp_camera_fb_get();
- if (!fb) {
- Serial.println("Camera capture failed");
- return;
- }
- EEPROM.begin(EEPROM_SIZE);
- pictureNumber = EEPROM.read(0) + 1;
- String path = "/picture" + String(pictureNumber) + ".jpg";
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- tft.fillScreen(TFT_RED);
- // Time recorded for test purposes
- uint32_t t = millis();
- // Get the width and height in pixels of the jpeg if you wish
- uint16_t w = 0, h = 0;
- TJpgDec.getJpgSize(&w, &h, fb->buf, fb->len);
- Serial.print("Width = ");
- Serial.print(w);
- Serial.print(", height = ");
- Serial.println(h);
- // Draw the image, top left at 0,0
- TJpgDec.drawJpg(0, 0, fb->buf, fb->len);
- // How much time did rendering take (ESP8266 80MHz 262ms, 160MHz 149ms, ESP32 SPI 111ms, 8bit parallel 90ms
- t = millis() - t;
- Serial.print(t);
- Serial.println(" ms");
- tft.drawString(path.c_str() + 1, 0, 290, 4);
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- fs::FS &fs = SD;
- Serial.printf("Picture file name: %s\n", path.c_str());
- File file = fs.open(path.c_str(), FILE_WRITE);
- if (!file) {
- Serial.println("Failed to open file in writing mode");
- } else {
- file.write(fb->buf, fb->len);
- Serial.printf("Saved file to path: %s\n", path.c_str());
- EEPROM.write(0, pictureNumber);
- EEPROM.commit();
- }
- file.close();
- esp_camera_fb_return(fb);
- }
- void loop() {
- delay(2000);
- Imagestorage();
- times++;
- if (times > 9) {
- Serial.println("finished!");
- while (1)
- ;
- }
- }
整个过程视频如下。