| 上一帖介绍增加屏幕和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 libraryTFT_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 1int 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 5void 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)      ;  }}
 整个过程视频如下。   
          
     拍照存卡 |