如何客製化企業 RAG 知識庫?— 從資料庫到知識整合的實戰技術
|

【IOT環控系統開發】監控您的家(上) — 開發準備

   

作者/圖片提供:曹永忠/王景穗

在智慧家庭中,最重要的是家庭的安全與防護,而目前能夠提供安全與防護的方式大部分都選擇安裝視訊監控鏡頭,所以本文要介紹一個物廉價美的一個整合晶片:ESP32 CAM視訊整合模組,這是安可信(AI Thinker)為 ESP32 推出了ESP32 CAM視訊整合模組,本模組目前網路賣場市價約為新台幣二百元到三百元之間,本身附帶一個結合了 OmniVision 的 OV2640 影像感測器模組,基本像數為640480,加上MicroSD 記憶卡卡槽,大小約為2740mm,只要MicroUsb 5V供電,就可以運作,使其適合做為網路攝影機應用。

ESP32 CAM視訊整合模組

簡易主機監控平台架構

筆者為了簡化視訊簡易主機監控平台複雜性,如下圖所示,提出一個架構,筆者使用一個到多個ESP32 CAM視訊整合模組,並且使用NodeMCU-32S Lua WiFi 物聯網開發板,撰寫對應的韌體,使NodeMCU-32S Lua WiFi 成為一個無線基地台,與視訊簡易主機監控網站(網頁伺服器)相連,使用者可以透過無線網路連上其熱點,就可以觀看視訊簡易主機監控網站,就可以看到一個到多個ESP32 CAM視訊整合模組捕抓到的動態視訊。

視訊簡易主機監控平台程式結果畫面

ESP32 CAM視訊整合模組

對於ESP32 CAM視訊整合模組,筆者參閱陸向陽老師的【好物開箱】為低階網路攝影而生的ESP32-CAM一文,可以看到ESP32-CAM硬體功能配置圖,該模組配備OmniVision 的 OV2640 影像感測器模組,基本像數為640*480,為了可以儲存攝影、拍照等資料,又加上MicroSD 記憶卡卡槽,可以插入達4G MicroSD 記憶卡,為了視訊,也加入了PSRAM(Pseudo SRAM)加快整個處理的速度,並且在下方加入內置的高亮度的Flash Lamp,宛如一個功能完整的監控視訊攝影機(陸向陽, 2019)。

ESP32-CAM硬體功能配置圖(圖片來源

ESP32 CAM視訊整合模組主要功能模組為下:

  • 最小的 802.11b / g / n Wi-Fi BT SoC 模塊
  • 低功耗 32 位 CPU,也可以為應用處理器服務
  • 高達 160MHz 的時鐘速度,總結計算能力高達 600 DMIPS
  • 內置 520 KB SRAM,外部 4MPSRAM
  • 支持 UART / SPI / I2C / PWM / ADC / DAC
  • 支持 OV2640 和 OV7670 攝像頭,內置閃光燈。
  • 支持圖像 Wi-Fi 上傳
  • 支持 TF 卡
  • 支持多種睡眠模式。
  • 嵌入式 Lwip 和 FreeRTOS
  • 支持 STA / AP / STA + AP 操作模式
  • 支持 Smart Config / AirKiss 技術
  • 支持串列埠本地和遠程韌體升級(FOTA)

(資料來源:SeeedStudio官網

如下表所示,為ESP32 CAM視訊整合模組整體規格:

(資料來源: SeeedStudio官網

ESP32 CAM視訊整合模組開發準備

筆者為了簡化,如下圖所示,購買了ESP32 CAM視訊整合模組與燒錄底座,方便開發整體的系統。

ESP32 CAM與燒錄模組

筆者將ESP32 CAM視訊整合模組至於燒錄底座之上,如下圖所示就完成下圖之視訊攝影機。

整合燒錄模組之ESP32 CAM

ESP32 CAM視訊系統開發

我們遵照前幾章所述,將ESP32 CAM視訊整合模組的驅動程式安裝好之後,我們打開ESP 32開發板的開發工具:Sketch IDE整合開發軟體(安裝Arduino開發環境,請參考『ESP32程式設計(基礎篇):ESP32 IOT Programming (Basic Concept & Tricks)』之『Arduino開發IDE安裝』(曹永忠, 2020a, 2020b, 2020f),安裝ESP 32開發板 SDK請參考『ESP32程式設計(基礎篇):ESP32 IOT Programming (Basic Concept & Tricks)』之『安裝ESP32 Arduino 整合開發環境』(曹永忠, 2020a, 2020b, 2020c, 2020d, 2020e),編寫一段程式,編寫一段程式,並透過開發工具將整個程式編譯後,燒錄後上傳到ESP32 CAM視訊整合模組,進行程式測試。

ESP32CAM視訊鏡頭程式(ESPCAMV20210530.ino)


#include "initPins.h"
#include "esp_camera.h"

#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h"  //disable brownout problems
#include "esp_http_server.h"

//Replace with your network credentials


#define PART_BOUNDARY "123456789000000000000987654321"

// This project was tested with the AI Thinker Model, M5STACK PSRAM Model and M5STACK WITHOUT PSRAM
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM

// Not tested with this model
//#define CAMERA_MODEL_WROVER_KIT

#if defined(CAMERA_MODEL_WROVER_KIT)
  #define PWDN_GPIO_NUM    -1
  #define RESET_GPIO_NUM   -1
  #define XCLK_GPIO_NUM    21
  #define SIOD_GPIO_NUM    26
  #define SIOC_GPIO_NUM    27
 
  #define Y9_GPIO_NUM      35
  #define Y8_GPIO_NUM      34
  #define Y7_GPIO_NUM      39
  #define Y6_GPIO_NUM      36
  #define Y5_GPIO_NUM      19
  #define Y4_GPIO_NUM      18
  #define Y3_GPIO_NUM       5
  #define Y2_GPIO_NUM       4
  #define VSYNC_GPIO_NUM   25
  #define HREF_GPIO_NUM    23
  #define PCLK_GPIO_NUM    22

#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
 
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       32
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
 
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       17
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_AI_THINKER)
  #define PWDN_GPIO_NUM     32
  #define RESET_GPIO_NUM    -1
  #define XCLK_GPIO_NUM      0
  #define SIOD_GPIO_NUM     26
  #define SIOC_GPIO_NUM     27
 
  #define Y9_GPIO_NUM       35
  #define Y8_GPIO_NUM       34
  #define Y7_GPIO_NUM       39
  #define Y6_GPIO_NUM       36
  #define Y5_GPIO_NUM       21
  #define Y4_GPIO_NUM       19
  #define Y3_GPIO_NUM       18
  #define Y2_GPIO_NUM        5
  #define VSYNC_GPIO_NUM    25
  #define HREF_GPIO_NUM     23
  #define PCLK_GPIO_NUM     22
#else
  #error "Camera model not selected"
#endif

static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

httpd_handle_t stream_httpd = NULL;

//-----------------
const char* ssid     = "CAMAP";
const char* password = "12345678";
// 設定ESP32固定IP的位置
IPAddress local_IP(192, 168, 4, 100);
// Set your Gateway IP address
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional
//--------------


static esp_err_t stream_handler(httpd_req_t *req){
  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;
  size_t _jpg_buf_len = 0;
  uint8_t * _jpg_buf = NULL;
  char * part_buf[64];

  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  if(res != ESP_OK){
    return res;
  }

  while(true){
    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      res = ESP_FAIL;
    } else {
      if(fb->width > 400){
        if(fb->format != PIXFORMAT_JPEG){
          bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
          esp_camera_fb_return(fb);
          fb = NULL;
          if(!jpeg_converted){
            Serial.println("JPEG compression failed");
            res = ESP_FAIL;
          }
        } else {
          _jpg_buf_len = fb->len;
          _jpg_buf = fb->buf;
        }
      }
    }
    if(res == ESP_OK){
      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
    }
    if(fb){
      esp_camera_fb_return(fb);
      fb = NULL;
      _jpg_buf = NULL;
    } else if(_jpg_buf){
      free(_jpg_buf);
      _jpg_buf = NULL;
    }
    if(res != ESP_OK){
      break;
    }
    Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
  }
  return res;
}

void startCameraServer(){
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 10000;
//  config.server_port = 10002
  ;
    // map port to Web server
  httpd_uri_t index_uri = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = stream_handler,
    .user_ctx  = NULL
  };
 
  Serial.printf("Starting web server on port: '%d'\n", config.server_port);
  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(stream_httpd, &index_uri);
  }
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);
  Serial.setDebugOutput(false);
 
  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.pixel_format = PIXFORMAT_JPEG;
 
  if(psramFound()){
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
 
  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  // Wi-Fi connection
 
 // Configures static IP address
  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("STA Failed to configure");
  }
 
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());  
  // Start streaming web server
  startCameraServer();
}

void loop() {
  delay(1);
}

ESP32CAM視訊鏡頭程式(initPins.h)


ESP32CAM視訊鏡頭程式(initPins.h)
#include
#include
WiFiMulti wifiMulti;
  IPAddress ip ;
  String ipdata ;
  String apname ;
String MacData ;



//--------------
long POW(long num, int expo)
{
  long tmp =1 ;
  if (expo > 0)
  {
        for(int i = 0 ; i< expo ; i++)
          tmp = tmp * num ;
         return tmp ;
  }
  else
  {
   return tmp ;
  }
}

String SPACE(int sp)
{
    String tmp = "" ;
    for (int i = 0 ; i < sp; i++)
      {
          tmp.concat(' ')  ;
      }
    return tmp ;
}


String strzero(long num, int len, int base)
{
  String retstring = String("");
  int ln = 1 ;
    int i = 0 ;
    char tmp[10] ;
    long tmpnum = num ;
    int tmpchr = 0 ;
    char hexcode[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} ;
    while (ln <= len) { tmpchr = (int)(tmpnum % base) ; tmp[ln-1] = hexcode[tmpchr] ; ln++ ; tmpnum = (long)(tmpnum/base) ; } for (i = len-1; i >= 0 ; i --)
      {
          retstring.concat(tmp[i]);
      }
   
  return retstring;
}

unsigned long unstrzero(String hexstr, int base)
{
  String chkstring  ;
  int len = hexstr.length() ;
 
    unsigned int i = 0 ;
    unsigned int tmp = 0 ;
    unsigned int tmp1 = 0 ;
    unsigned long tmpnum = 0 ;
    String hexcode = String("0123456789ABCDEF") ;
    for (i = 0 ; i < (len ) ; i++) { // chkstring= hexstr.substring(i,i) ; hexstr.toUpperCase() ; tmp = hexstr.charAt(i) ; // give i th char and return this char tmp1 = hexcode.indexOf(tmp) ; tmpnum = tmpnum + tmp1* POW(base,(len -i -1) ) ; } return tmpnum; } String print2HEX(int number) { String ttt ; if (number >= 0 && number < 16)
  {
    ttt = String("0") + String(number,HEX);
  }
  else
  {
      ttt = String(number,HEX);
  }
  return ttt ;
}
String GetMacAddress() {
  // the MAC address of your WiFi shield
  String Tmp = "" ;
  byte mac[6];
 
  // print your MAC address:
  WiFi.macAddress(mac);
  for (int i=0; i<6; i++)
    {
        Tmp.concat(print2HEX(mac[i])) ;
    }
    Tmp.toUpperCase() ;
  return Tmp ;
}

String IpAddress2String(const IPAddress& ipAddress)
{
  return String(ipAddress[0]) + String(".") +\
  String(ipAddress[1]) + String(".") +\
  String(ipAddress[2]) + String(".") +\
  String(ipAddress[3])  ;
}




String chrtoString(char *p)
{
    String tmp ;
    char c ;
    int count = 0 ;
    while (count <100)
    {
        c= *p ;
        if (c != 0x00)
          {
            tmp.concat(String(c)) ;
          }
          else
          {
              return tmp ;
          }
       count++ ;
       p++;
       
    }
}


void CopyString2Char(String ss, char *p)
{
         //  sprintf(p,"%s",ss) ;

  if (ss.length() <=0)
      {
           *p =  0x00 ;
          return ;
      }
    ss.toCharArray(p, ss.length()+1) ;
   // *(p+ss.length()+1) = 0x00 ;
}

boolean CharCompare(char *p, char *q)
  {
      boolean flag = false ;
      int count = 0 ;
      int nomatch = 0 ;
      while (flag <100) { if (*(p+count) == 0x00 or *(q+count) == 0x00) break ; if (*(p+count) != *(q+count) ) { nomatch ++ ; } count++ ; } if (nomatch >0)
      {
        return false ;
      }
      else
      {
        return true ;
      }
     
       
  }

void ShowAP()
{
  Serial.print("Access Point:") ;
  Serial.print(apname) ;
  Serial.print("\n") ;

}


void ShowMAC()
{
  Serial.print("MAC:") ;
  Serial.print(MacData) ;
  Serial.print("\n") ;

}
void ShowIP()
{
  Serial.print("IP:") ;
  Serial.print(ipdata) ;
  Serial.print("\n") ;

}


void ShowInternet()
{
    ShowMAC() ;
    ShowAP() ;
    ShowIP()  ;
}

共同作者介紹

王景穗(Wang-Jing Suei)

國立高雄大學(NUK)電機工程學系碩士,碩士論文主要專研物聯網技術與微處理器應用整合,並協助學校針對實作性課程的遠距教學系統開發,結合自動控制系統與物聯網技術,設計開發教育性遠距操作系統。

(責任編輯:謝涵如)

曹永忠

訂閱MakerPRO知識充電報

與40000位開發者一同掌握科技創新的技術資訊!

Author: 曹永忠

國立中央大學資訊管理學系博士,目前在國立暨南國際大學電機工程學系兼任助理教授、國立高雄科技大學商務資訊應用系兼任助理教授自由作家,專注於軟體工程、軟體開發與設計、物件導向程式設計、物聯網系統開發、Arduino開發、嵌入式系統開發。長期投入資訊系統設計與開發、企業應用系統開發、軟體工程、物聯網系統開發、軟硬體技術整合等領域,並持續發表作品及相關專業著作,並通過台灣圖霸的專家認證。

Share This Post On
468 ad

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *