2023年10月29日 星期日

Arduino Mega2560 Test Fixture Application

Purpose:

1. 兩個按鍵作為進出平台的開關 另一按鍵為平台上另一上下動作的開關
2. 完成所有動作後按任一進出按鍵就進行退出動作在退出時 蜂鳴器要發聲
3. 完成進入動作 COUNTER 要記數加一次為了控制頂針壓合次數
4. 手動跟程式控制同時可以動作

1. Two buttons serve as switches for entering and exiting the platform. The other button serves as another switch for up and down movements on the platform.
2. After completing all actions, press any entry or exit button to exit. The buzzer will sound when exiting.
3. After completing the entry action COUNTER, the count needs to be added once in order to control the number of times the ejection pin is pressed.
4. Manual and program control can be performed at the same time

Architecture:

零件配置:
1. Arduino Mega2560 控制板

2. 8 Port Relay board
3. 3 個 push button
4. 3個氣壓控制閥
5. 1個蜂鳴器
6. 次數記數器
7. 4個氣壓缸
8. ACto12VDC變壓器
YouTube Demo;
Mega2560 Code:
#include <Arduino_FreeRTOS.h>
//---------------structure ----------------------------------------
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t LEDFlag=1;
  uint8_t BTFlag = 0;
  uint8_t ServoFlag = 0;
  uint8_t CloseFlag=0;
  uint8_t InFlag=0;
  uint8_t OutFlag=0;
  uint8_t UpFlag=0;
  uint8_t Up1Flag=0;
  uint8_t Up2Flag=0;
  uint8_t Buzzer_Flag=0;
 
} vFlag;
vFlag *flag_Ptr;
vFlag flag;
//----------uart--------------
#define LINE_BUFFER_LENGTH 64
//--------- uart structure --------------------------------------
typedef struct _vUart
{
  char c;
  int lineIndex = 0;
  int line1Index = 0;
  int BTlineIndex = 0;
  bool lineIsComment;
  bool lineSemiColon;
  char line[128];
  char BTline[20];
  String inputString;
  String BTinputString;
  String S1inputString;
  int V[16];
  char ctemp[30];
  char I2C_Data[80];
  int DC_Spped = 50;
  float Voltage[16];
  int Buffer[128];
  int StartCnt = 0;
  int ReadCnt = 0;
  int sensorValue = 0;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//------------------------------------------------
const uint8_t LED_PIN = 13;
const uint8_t F_PIN = 12;
int buzzerPin=11;
//----relay--------
uint8_t DIO_1 = 22;
uint8_t DIO_2 = 24;
uint8_t DIO_3 = 26;
uint8_t DIO_4 = 28;
uint8_t DIO_5 = 30;
uint8_t DIO_6 = 32;
uint8_t DIO_7 = 34;
uint8_t DIO_8 = 36;
//---------治具定義------------------------------------------
uint8_t In1_B_Pin = 47;  
uint8_t In2_B_Pin = 49;
uint8_t Up_B_Pin = 51;

uint8_t In_S_Pin = 23;
uint8_t Out_S_Pin = 25;
uint8_t UpDown1_S_Pin = 27;
uint8_t UpDown2_S_Pin = 29;
//----relay--------
uint8_t InOut_V_Pin = 22;
uint8_t UpDown1_V_Pin = 24;
uint8_t UpDown2_V_Pin = 26;

uint8_t SPK_O_Pin = 28;  //30預留 12V relay
//uint8_t Count_O_Pin = 30;
uint8_t Count_O_Pin = 32;
//----------------------------------------------------
uint8_t DUTPin = 23;
uint8_t Sensor1Pin = 23;
uint8_t Sensor2Pin = 25;
uint8_t Sensor3Pin = 27;
uint8_t Sensor4Pin = 29;
//-------------------------------------------
char ctemp[20];
//------------------------------
TaskHandle_t hled;
TaskHandle_t huart0;
//------------------------------------------------------------------------------
void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  // create UART task
  xTaskCreate(vUARTTask, "UART Task", configMINIMAL_STACK_SIZE, NULL, 1, &huart0);
  // Check the results
  // create blink task
  xTaskCreate(vLEDFlashTask, "LED Task", configMINIMAL_STACK_SIZE, NULL, 2, &hled);
 
  //-------------------------------------------------------------------

}
void setup()
{
  Uart.inputString.reserve(60);
  initial();
  Serial.begin(9600);
  Serial.setTimeout(2000);
  Serial.println(F("init"));
  //-------------------------------------
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW); // Turn LED off.
  //---------------IO setting-----------------
  pinMode(DIO_1, INPUT);
  pinMode(DIO_2, INPUT);
  pinMode(DIO_3, INPUT);
  pinMode(DIO_4, INPUT);
  pinMode(DIO_5, INPUT);
  pinMode(DIO_6, INPUT);
  pinMode(DIO_7, INPUT);
  pinMode(DIO_8, INPUT);
  pinMode(Sensor1Pin,INPUT_PULLUP); //23
  pinMode(Sensor2Pin,INPUT_PULLUP); //25
  pinMode(Sensor3Pin,INPUT_PULLUP); //27
  pinMode(Sensor4Pin,INPUT_PULLUP); //29
  //----治具 ----------------
  pinMode(In_S_Pin,INPUT_PULLUP); //23
  pinMode(Out_S_Pin,INPUT_PULLUP); //25
  pinMode(UpDown1_S_Pin,INPUT_PULLUP); //27
  pinMode(UpDown2_S_Pin,INPUT_PULLUP); //29
  pinMode(In1_B_Pin,INPUT_PULLUP); //47
  pinMode(In2_B_Pin,INPUT_PULLUP); //49
  pinMode(Up_B_Pin,INPUT_PULLUP); //51
  //----relay--------
  pinMode(InOut_V_Pin, INPUT);   //22
  pinMode(UpDown1_V_Pin, INPUT); //24
  pinMode(UpDown2_V_Pin, INPUT); //26
  pinMode(SPK_O_Pin, INPUT);     //28 //30預留 12V relay
  pinMode(Count_O_Pin, INPUT);   //32
 // start FreeRTOS
  Serial.println("Systom On!");
  vTaskStartScheduler();
  //if the scheduler start the code don't came here
  Serial.println(F("Die"));
  while (1)
    ;
}
//------------------------------------------------------------------------------
void loop()
{
 
}
//------------------------------------------------------------------------------
// high priority for blinking LED
void vLEDFlashTask(void *pvParameters)
{
  (void)pvParameters;

  pinMode(LED_PIN, OUTPUT);
  for (;;)
  {
    digitalWrite(LED_PIN, HIGH);                    // Turn LED on.
    vTaskDelay((150L * configTICK_RATE_HZ) / 1000L); // Sleep for 50 milliseconds.
    digitalWrite(LED_PIN, LOW);                      // Turn LED off.
    vTaskDelay((150L * configTICK_RATE_HZ) / 1000L); // Sleep for 150 milliseconds.
  }
}
//------------------------------------------------------------------------------
void vUARTTask(void *pvParameters)
{
  Uart.lineIsComment = false;
  Uart.lineSemiColon = false;

  while(1)
  {
//---------Fixture -------------------------
    vFixtureTask();   
while (Serial.available() > 0)
    {
      Uart.c = Serial.read();

      if ((Uart.c == '\n') || (Uart.c == '\r'))
      { // End of line reached
        if (Uart.lineIndex > 0)
        { // Line is complete. Then execute!
          Uart.line[Uart.lineIndex] = '\0'; // Terminate string
          //Serial.println( F("Debug") );
          //Serial.println( Uart.inputString );
          processCommand(Uart.line); // do something with the command
          //傳輸給藍芽
         
          Uart.lineIndex = 0;
          Uart.inputString = "";
        }
        else
        {
          // Empty or comment line. Skip block.
        }
        Uart.lineIsComment = false;
        Uart.lineSemiColon = false;
        Serial.println(F("ok>"));
      }
      else
      {
        //Serial.println( c );
        if ((Uart.lineIsComment) || (Uart.lineSemiColon))
        {
          if (Uart.c == ')')
            Uart.lineIsComment = false; // End of comment. Resume line.
        }
        else
        {
          if (Uart.c == '/')
          { // Block delete not supported. Ignore character.
          }
          else if (Uart.c == '~')
          { // Enable comments flag and ignore all characters until ')' or EOL.
            Uart.lineIsComment = true;
          }
          else if (Uart.c == ';')
          {
            Uart.lineSemiColon = true;
          }
          else if (Uart.lineIndex >= LINE_BUFFER_LENGTH - 1)
          {
            Serial.println("ERROR - lineBuffer overflow");
            Uart.lineIsComment = false;
            Uart.lineSemiColon = false;
          }
          else if (Uart.c >= 'a' && Uart.c <= 'z')
          { // Upcase lowercase
            Uart.line[Uart.lineIndex] = Uart.c - 'a' + 'A';
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += (char)(Uart.c - 'a' + 'A');
          }
          else
          {
            Uart.line[Uart.lineIndex] = Uart.c;
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += Uart.c;
          }
        }
      }
    } //while (Serial.available() > 0)
  }
}
//------------------------------------------------------------------------------
//----reset-----------
void (*resetFunc)(void) = 0;
//---------------------------------------------------------
void processCommand(char *data)
{

  int len, xlen, ylen, zlen, alen;
  int tempDIO;
  String stemp;
  unsigned int i, j, Comma;

  int val, maxv, minv;
  unsigned long duration;
  float Vpp;

  len = Uart.inputString.length();

  //-------------RESET---------------
  if (strstr(data, "VER") != NULL)
  {
    //Serial.println(F("W_ATE_Board_20201021"));
    Serial.println(F("Mega_SERIAL_20230723"));
  }
  if (strstr(data, "RESET") != NULL)
  {
    Serial.println(F("Reset"));
    resetFunc();
  }
  //===-----------------DIO port 1----------------
  if (strstr(data, "DIO1") != NULL)
  {
    //-DIO11_LOW
    if (data[3] == '1')
    {
      for (int i = 0; i < len; i++)
      {
        if (data[i] == '_')
        {
          //Serial.println("test");
          //Serial.println(i);
          xlen = i;
        }
        //ctemp[i-4]=data[i];
      }
      for (int i = 4; i < xlen; i++)
      {
        ctemp[i - 4] = data[i];
      }
      ctemp[xlen - 4] = '\0';
      tempDIO = atoi(ctemp);
      //Serial.println(tempDIO);
      for (int i = (xlen + 1); i < len; i++)
      {
        ctemp[i - (xlen + 1)] = data[i];
      }
      ctemp[len - (xlen + 1)] = '\0';

      tempDIO = tempDIO * 2 + 20;
      pinMode(tempDIO, OUTPUT);
      //if(strstr(ctemp, "ON") != NULL)
      //if(strcmp(ctemp, "ON")==0)
      //if (strcmp(Uart.ctemp, "LOW") == 0)
      if (strstr(ctemp, "LOW") != NULL)
      {
        digitalWrite(tempDIO, LOW);
      }
      else if (strstr(ctemp, "ON") != NULL)
      {
        digitalWrite(tempDIO, LOW);
      }
      else if (strstr(ctemp, "HIGH") != NULL)
      {
        digitalWrite(tempDIO, HIGH);
      }
      else if (strstr(ctemp, "OFF") != NULL)
      {
        digitalWrite(tempDIO, HIGH);
      }
    }
  }
  //------fixture --------
  if(strstr(data, "IN") != NULL )
  {
     if( (digitalRead(Out_S_Pin) == LOW) && flag.Up1Flag==0 && flag.Up2Flag==0)
     {
       flag.InFlag=3;
       pinMode(InOut_V_Pin , OUTPUT);
       digitalWrite(InOut_V_Pin , HIGH);    //in進
     }
     else
     {
       Serial.println(F("PLS OUT Fixture first!!"));
     }
  }
 
  if(strstr(data, "OUT") != NULL)
  {
     if( (digitalRead(In_S_Pin) == LOW) && flag.Up1Flag==0 && flag.Up2Flag==0)
     {
       flag.InFlag=5;
       pinMode(InOut_V_Pin , OUTPUT);
       digitalWrite(InOut_V_Pin , LOW); //出
     }
     else
     {
      Serial.println(F("PLS IN Fixture first!!"));
     }
  }
 
  if(strstr(data, "UP1") != NULL)
  {
     //if((flag.InFlag==2 || flag.InFlag==3) && flag.Up1Flag==1 && flag.Up2Flag==0 )
     if((flag.InFlag==2 || flag.InFlag==3) && (digitalRead(UpDown1_S_Pin ) == LOW) && flag.Up2Flag==0 )
     {
      pinMode(UpDown1_V_Pin , OUTPUT);
      digitalWrite(UpDown1_V_Pin , HIGH); //UP1
      flag.Up1Flag=0;
     }
     else
     {
      Serial.println(F("PLS DWON1 Fixture first!!"));
     }
  }

  if(strstr(data, "DOWN1") != NULL)
  {
     if((flag.InFlag==2 || flag.InFlag==3) && flag.Up1Flag==0 && flag.Up2Flag==0 )
     {
      pinMode(UpDown1_V_Pin , OUTPUT);
      digitalWrite(UpDown1_V_Pin , LOW); //UP1
      flag.Up1Flag=1;
     }
     else
     {
      Serial.println(F("PLS UP1 Fixture first!!"));
     }
  }

  if(strstr(data, "UP2") != NULL)
  {
     if((flag.InFlag==2 || flag.InFlag==3) && flag.Up1Flag==1 )
     {
       //Serial.println("flag.InFlag"+String(flag.InFlag));
       //Serial.println("flag.Up1Flag"+String(flag.Up1Flag));
       //Serial.println("flag.Up2Flag"+String(flag.Up2Flag));
       
       if((digitalRead(UpDown2_S_Pin) == LOW))
       {
         pinMode(UpDown2_V_Pin , OUTPUT);
         digitalWrite(UpDown2_V_Pin , HIGH); //UP1
         flag.Up2Flag=0;
       }
       
     }
     else
     {
       Serial.println(F("PLS DWON2 Fixture first!!"));
     }
  }

  if(strstr(data, "DOWN2") != NULL)
  {
     //Serial.println("flag.InFlag"+String(flag.InFlag));
     //Serial.println("flag.Up1Flag"+String(flag.Up1Flag));
     //Serial.println("flag.Up2Flag"+String(flag.Up2Flag));
     
     if((flag.InFlag==2 || flag.InFlag==3) && flag.Up1Flag==1 && flag.Up2Flag==0 )
     {
       pinMode(UpDown2_V_Pin , OUTPUT);
       digitalWrite(UpDown2_V_Pin , LOW); //UP1
       flag.Up2Flag=1;
       pinMode(Count_O_Pin , OUTPUT);
       digitalWrite(Count_O_Pin  , LOW);  
       delay(500);
       digitalWrite(Count_O_Pin  , HIGH);
     }
     else
     {
       Serial.println(F("PLS UP2 Fixture first!!"));
     }
  }
  //-----------fixture end---------------------------------------------
}
//------ vFixtureTask start --------------------------------
void vFixtureTask()
{
 
  if( (digitalRead(In1_B_Pin) == LOW && digitalRead(In2_B_Pin) == LOW ) && (flag.InFlag==5 || flag.InFlag==1 || flag.InFlag==9) )
  {
    flag.InFlag=1;
    pinMode(InOut_V_Pin , OUTPUT);
    digitalWrite(InOut_V_Pin  , HIGH);   //In 因為氣壓跟之前反接 所以 HIGH進

    if(digitalRead(In_S_Pin) == LOW)  
    {
      flag.InFlag=2;
      //Serial.println(flag.InFlag);
      //Serial.println(F("ok>"));
    }
  }

  if( (digitalRead(In_S_Pin) == LOW ) && ( flag.InFlag==2 ) )
  {
    pinMode(UpDown1_V_Pin, OUTPUT);
    digitalWrite(UpDown1_V_Pin , LOW);   //DOWN
    if(digitalRead(UpDown1_S_Pin) == LOW)  
    {
      flag.Up1Flag = 1;  //1在下面 0在上面
      //Serial.println(F("UpDown1_S_Pin"));
      //Serial.println(F("ok>"));
      pinMode(UpDown2_V_Pin, OUTPUT);
      digitalWrite(UpDown2_V_Pin , LOW);   //DOWN
      if(digitalRead(UpDown2_S_Pin) == LOW)  
      {
        flag.Up2Flag = 1;
        //Serial.println(F("UpDown2_S_Pin"));
        //Serial.println(F("ok>"));
        pinMode(Count_O_Pin , OUTPUT);
        digitalWrite(Count_O_Pin  , LOW);  
        delay(1000);
        flag.InFlag = 3;
        //Serial.println(flag.InFlag);
        //Serial.println(F("ok>"));
        digitalWrite(Count_O_Pin  , HIGH);
        //flag.OutFlag = 0;          
      }
    }
  }
  //if( (digitalRead(In1_B_Pin) == HIGH || digitalRead(In2_B_Pin) == HIGH ) && (flag.InFlag==0 || flag.InFlag==1 ) )
  if( (digitalRead(In1_B_Pin) == HIGH || digitalRead(In2_B_Pin) == HIGH ) && ( flag.InFlag == 1 ) )
  {
    //flag.InFlag=8;
    //Serial.println(flag.InFlag);
    //Serial.println(F("ok>"));
   
    pinMode(SPK_O_Pin , OUTPUT);
    digitalWrite(SPK_O_Pin , LOW);   //SPK
   

    pinMode(UpDown2_V_Pin, OUTPUT);
    digitalWrite(UpDown2_V_Pin, HIGH);   //上
    delay(300);
    pinMode(UpDown1_V_Pin, OUTPUT);
    digitalWrite(UpDown1_V_Pin , HIGH);   //上
    delay(300);
    pinMode(InOut_V_Pin , OUTPUT);
    digitalWrite(InOut_V_Pin  , LOW);   //出
   
    digitalWrite(SPK_O_Pin , HIGH);   //SPK

    if(digitalRead(Out_S_Pin) == LOW)  
    {
      digitalWrite(SPK_O_Pin , HIGH);   //SPK
      flag.InFlag=9;
      flag.Up1Flag = 0;
      flag.Up2Flag = 0;
    }
  }

  if( (digitalRead(In1_B_Pin) == LOW || digitalRead(In2_B_Pin) == LOW ) && (digitalRead(In_S_Pin) == LOW && flag.InFlag==0) )
  {
    //Serial.println(F("Out_S_Pin"));
    //Serial.println(F("ok>"));
    flag.InFlag=5;
    pinMode(InOut_V_Pin , OUTPUT);
    digitalWrite(InOut_V_Pin  , LOW);   //出
  }

  //if( (digitalRead(In1_B_Pin) == LOW || digitalRead(In2_B_Pin) == LOW ) && (flag.InFlag==2 || flag.InFlag==3) )
  if( (digitalRead(In1_B_Pin) == LOW || digitalRead(In2_B_Pin) == LOW ) && (flag.InFlag==3) )
  {
    flag.InFlag=1;
  }

  if( (digitalRead(Up_B_Pin ) == LOW ) && flag.Up2Flag == 1  && flag.Up1Flag == 1)
  {
    pinMode(UpDown2_V_Pin, OUTPUT);
    digitalWrite(UpDown2_V_Pin, HIGH);   //上
    delay(300);
    flag.Up2Flag=0;
  }
  else if( (digitalRead(Up_B_Pin ) == LOW )  && flag.Up2Flag == 0 && flag.Up1Flag == 1)
  {
    pinMode(UpDown2_V_Pin, OUTPUT);
    digitalWrite(UpDown2_V_Pin, LOW);   //下
    if(digitalRead(UpDown2_S_Pin) == LOW)  
    {
        flag.Up2Flag=1;
    }
  }
}
//-------------------------------------------



2023年10月27日 星期五

Electrical Safety Compliance Automatic Test System

Purpose:

(Automation for Electrical Safety & Compliance Testing)

安規綜合分析儀7440-自動測試架構, 適合工廠端量產測試實現量化生產減少人工插拔的自動化生產流程, 安排在組裝後走流線測試更顯效益!

Electrical Safety Compliance Analyzer 7440 Automatic Test Architecture, It is suitable for factory-side mass production testing to achieve quantitative production and reduce the automated production process of manual plugging and unplugging. It is more efficient to arrange streamline testing after assembly!

Finished product picture

Detailed architecture

Portion architecture


Fundamental:

(一)儀器介紹

EXTECH7440是台灣華儀公司生產的, 具有交直流耐壓測試, 絕緣電阻測試, 接地電阻測試四功能合一的安規分析儀, 可以程控設置, 並配有RS232和GPIB接口, 可實現自動測試.
EXTECH7440 is produced by Taiwan Extech Electronics Ltd.,Co. Company. It is a safety analyzer with four functions in one: AC and DC withstand voltage testing, insulation resistance testing, and grounding resistance testing. It can be programmed and set up, and is equipped with RS232 and GPIB interfaces to enable automatic testing.

Hipot and Gound Test (耐壓、接地測試)
1. 交流耐壓測試(AC, AC Hipot):適合吃插座電的器具
2. 直流耐壓測試(DC, DC Hipot):適合電池或電容等吃直流電的
3. 絕緣阻抗測試(IR, Insulation Resistance):適合不能太高壓但想知道絕緣能力的,或已經測完高壓想再測絕緣的
4. 接地阻抗測試(GB, Ground Bond):適合測試接地較大的金屬件
5. 接地導通測試(Ground Continuity):適合測試接地較小或細的金屬件
6. 洩漏電流測試(Touch Current):適合人體會時常接觸到的部件,如醫療器材

FRONT PANEL OF MODEL 7440

之前相關文章連結(Links to previous related articles) :

2023年10月24日 星期二

Range versus Rate Automatic Test system with IxChariot

Purpose:
Goal is to Build a repeatable and controllable test method for measuring rate vs. range.
The Performance Measurement of Range versus Rate for Wi-Fi System
Control the console interface of IxChartiot through the GUI written by Csharp and call the tst project file verified and executed by IxChariot. Then use the programmable attenuator to simulate the distance and cooperate with the measurement system (Range Versus Rate Test System) to obtain the attenuation The corresponding data of value (distance) and throughput are saved into a csv file to facilitate data analysis.
經由Csharp所寫的GUI來控制IxChartiot的console介面並呼叫由IxChariot所驗證和執行過的tst專案檔. 再以可程式衰減器模擬距離並配合量測系統(Range Versus Rate Test System),進而得到衰減值(距離)和Throughput之對應資料存成csv檔案以利分析資料. 

覆蓋能力測試(Range Versus Rate Test)
RvR(Range versus Rate), 以可程式衰減器模擬距離並配合量測系統,進而得到衰減值(距離)和Throughput之對應資料

環境架構圖(Environment architecture diagram):

操作畫面:

Path1: Conductive Wireless Throughput Test

Server PC LAN Card<-> AP LAN Port <-> conductive cable <-> Programmable Attenuator<->Client PC WLAN Card

Test Data



Fundamental:

之前相關文章:

c# Ixchariot throughput 測試 

WIFI RvO TEST TOOL FROM C#

Vaunix DigitalAttenuator control tool

IxChariot

IxChariot is the industry's leading pre-deployment and live network performance testing tools and application assessment tool.
IxChariot console is a control platform that can be anywhere on the network, as long as the IP is reachable and can be contacted with Endpoint 1. What needs to be tested and evaluated is the point-to-point performance between Endpoint 1 and 2.
IxChariot可在部署前到部署後,即時評估複雜網路的效能.
IxChariot console 是控制平臺,可以在網路中的任何地方,只要IP可到達,和Endpoint 1之間能夠聯繫上即可。所要測試評估的就是Endpoint 1 和 2 之間的點到點的性能。

YouTubeDemo:

2023年10月19日 星期四

ESP32CAM Four-in-one function

Purpose:

結合之前利用ESP32CAM所做的一些功能, 如Line notify, PIR detect, MQTT, Web Stream等等. 把這些ESP32CAM的code寫在一起方便以後maintain.

Combined with some functions previously project done using ESP32CAM, such as Line notify, PIR detect, MQTT, Web Stream, etc. Write these ESP32CAM codes together for easy maintenance in the future.

之前的文章列表(previously project list):

ESP32CAM PIR DETECTION WITH LINE Notify

ESP32-CAM video streaming with Node Red

ESP32 MQTT Publish/Subscribe DS18B20 Temperature to Node-Red

ESP32-CAM Video Stream by C#

Fundamental:

ESP32CAM circuit




Arduino IDE2

MQTT

請參照之前文章ESP32 MQTT Publish/Subscribe DS18B20 Temperature to Node-Red



Web-Stream Function:

請參照之前文章 ESP32-CAM Video Stream by C#

PIR Detect to Linenotify:

請參照之前文章ESP32CAM PIR DETECTION WITH LINE Notify

YouTubeDemo:


ESP32CAM Code:

#include <PubSubClient.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "soc/soc.h"             //用於電源不穩不重開機
#include "soc/rtc_cntl_reg.h"    //用於電源不穩不重開機
#include "esp_camera.h"          //視訊函式

//ESP32-CAM 安信可模組腳位設定
#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
// Enter your WiFi ssid and password
const char* ssid     = "your network SSID";   //your network SSID
const char* password = "your network password";   //your network password

String lineNotifyToken = "Your Totken";    //Line Notify Token
//----MQTT--------
//------ 以下修改成你MQTT設定 ------
char* MQTTServer = "mqttgo.io";
int MQTTPort = 1883;//MQTT Port
char* MQTTUser = "";//
char* MQTTPassword = "";//
//-----publish-----
char* MQTTTopicPic1  = "yourpublish/pic";  
//subscribe燈號
char* MQTTSubTopic1 = "yoursubscribe/led";
char* MQTTSubTopic2 = "yoursubscribe/flash";
long MQTTLastPublishTime;//此變數用來記錄推播時間
long MQTTPublishInterval = 300;//每0.5秒推撥一次

WiFiClient WifiClient;
PubSubClient MQTTClient(WifiClient);
//--------------------------------------------------------------------------
#define LED_IN 33
const int Led_Flash = 4;
const int Led_run = 13;
int PIR_Sensor = 12;
boolean startTimer = false;
unsigned long time_now=0;
int time_capture=0;
int wifi_Flag=0;
int wifi_cnt=0;
int MqttFlag=0;
int LinenotifyFlag=0;

#define CAMERA_MODEL_AI_THINKER // Has PSRAM
void startCameraServer();

void setup()
{
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);  //關閉電源不穩就重開機的設定
   
  Serial.begin(9600);
  Serial.setDebugOutput(true);  //開啟診斷輸出
  Serial.println();
  setupCam();

  pinMode(Led_Flash, OUTPUT);
  pinMode(Led_run, OUTPUT);
  pinMode(LED_IN, OUTPUT);
  pinMode(PIR_Sensor, INPUT_PULLDOWN);

  //閃光燈(GPIO4)
  ledcAttachPin(Led_Flash, 4);  
  ledcSetup(Led_Flash, 5000, 8);
 
  WiFi.mode(WIFI_AP_STA);  //其他模式 WiFi.mode(WIFI_AP); WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
    wifi_cnt++;
    Serial.println(wifi_cnt);
    if(wifi_cnt==20)
    {
      //change another AP
      WiFi.begin("ras_2.4", "181415181415");
      while (WiFi.status() != WL_CONNECTED)
      {
        delay(500);
        Serial.print(".");
      }
    }
    if (WiFi.status() == WL_CONNECTED) {    //若連線成功
      Serial.println("");
      Serial.println("STAIP address: ");
      Serial.println(WiFi.localIP());
      Serial.println("");

      wifi_Flag=1;
      startCameraServer();
      break;
    }
  }
 
  digitalWrite(Led_Flash, LOW);
 
}

void loop()
{
  int v = digitalRead(PIR_Sensor);
  if (v == 1) {
    //傳照片
    String result = SendImageMQTT();
    Serial.println(result);
    ledcWrite(4,10);
    delay(1000);
    Serial.println("starting to Line");
    sendCapturedImage2LineNotify(lineNotifyToken);
    ledcWrite(4,0);
  }
  if(wifi_Flag==1)
  {
    digitalWrite(LED_IN, LOW);
    delay(200);
    digitalWrite(LED_IN, HIGH);
    delay(200);
  }
  if (!MQTTClient.connected())
  {
    MQTTConnecte();
  }
  if ((millis() - MQTTLastPublishTime) >= MQTTPublishInterval )
  {
    //String result = SendImageMQTT();
    //Serial.println(result);
    MQTTLastPublishTime = millis(); //更新最後傳輸時間
  }
  MQTTClient.loop();//更新訂閱狀態
  delay(100);  //You could only send up to 50 images to Line Notify in one hour.
}

//鏡頭設定
void setupCam() {  
  // #define CAMERA_MODEL_AI_THINKER
  //視訊組態設定  https://github.com/espressif/esp32-camera/blob/master/driver/include/esp_camera.h
  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()){  //是否有PSRAM(Psuedo SRAM)記憶體IC
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  //視訊初始化
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    ESP.restart();
  }

  //可自訂視訊框架預設大小(解析度大小)
  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1); // flip it back
    s->set_brightness(s, 1); // up the brightness just a bit
    s->set_saturation(s, -2); // lower the saturation
  }
  // drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_SVGA);    //解析度 UXGA(1600x1200), SXGA(1280x1024), XGA(1024x768), SVGA(800x600), VGA(640x480), CIF(400x296), QVGA(320x240), HQVGA(240x176), QQVGA(160x120), QXGA(2048x1564 for OV3660)

  //s->set_vflip(s, 1);  //垂直翻轉
  //s->set_hmirror(s, 1);  //水平鏡像
  //s->set_pRotation(s, 1);  //0=不旋轉 1-轉90度 2- 轉180度 3-轉270
}

String sendCapturedImage2LineNotify(String token) {
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
    return "Camera capture failed";
  }
   
  WiFiClientSecure client_tcp;  //啟動SSL wificlient
  client_tcp.setInsecure();   //run version 1.0.5 or above
  Serial.println("Connect to notify-api.line.me");
  if (client_tcp.connect("notify-api.line.me", 443)) {
    Serial.println("Connection successful");
   
    String message = "ESP32-CAM";
    String head = "--Taiwan\r\nContent-Disposition: form-data; name=\"message\"; \r\n\r\n" + message + "\r\n--Taiwan\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--Taiwan--\r\n";

    uint16_t imageLen = fb->len;
    uint16_t extraLen = head.length() + tail.length();
    uint16_t totalLen = imageLen + extraLen;
    //開始POST傳送訊息
    client_tcp.println("POST /api/notify HTTP/1.1");
    client_tcp.println("Connection: close");
    client_tcp.println("Host: notify-api.line.me");
    client_tcp.println("Authorization: Bearer " + token);
    client_tcp.println("Content-Length: " + String(totalLen));
    client_tcp.println("Content-Type: multipart/form-data; boundary=Taiwan");
    client_tcp.println();
    client_tcp.print(head);
   
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    Serial.println("Data Sending....");
    //檔案太大,分段傳送
    for (size_t n=0;n<fbLen;n=n+1024) {
      if (n+1024<fbLen) {
        client_tcp.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client_tcp.write(fbBuf, remainder);
      }
    }  
   
    client_tcp.print(tail);
    client_tcp.println();
    esp_camera_fb_return(fb);

    String getResponse="",Feedback="";
    int waitTime = 10000;   // timeout 10 seconds
    long startTime = millis();
    boolean state = false;
   
    while ((startTime + waitTime) > millis()) {
      Serial.print(".");
      delay(100);      
      while (client_tcp.available()) {  //當有收到回覆資料時
          char c = client_tcp.read();
          if (state==true) Feedback += String(c);        
          if (c == '\n') {
            if (getResponse.length()==0) state=true;
            getResponse = "";
          }
          else if (c != '\r')
            getResponse += String(c);
          startTime = millis();
       }
       if (Feedback.length()>0) break;
    }
    Serial.println();
    client_tcp.stop();
    return Feedback;
  }
  else {
    return "Connected to notify-api.line.me failed.";
  }
}

String sendRequest2LineNotify(String token, String request) {
  request.replace("%","%25");
  request.replace(" ","%20");
  //request.replace("&","%20");
  request.replace("#","%20");
  //request.replace("\'","%27");
  request.replace("\"","%22");
  request.replace("\n","%0D%0A");
  request.replace("%3Cbr%3E","%0D%0A");
  request.replace("%3Cbr/%3E","%0D%0A");
  request.replace("%3Cbr%20/%3E","%0D%0A");
  request.replace("%3CBR%3E","%0D%0A");
  request.replace("%3CBR/%3E","%0D%0A");
  request.replace("%3CBR%20/%3E","%0D%0A");
  request.replace("%20stickerPackageId","&stickerPackageId");
  request.replace("%20stickerId","&stickerId");    

  WiFiClientSecure client_tcp;
  client_tcp.setInsecure();   //run version 1.0.5 or above
 
  Serial.println("Connect to notify-api.line.me");  
 
  if (client_tcp.connect("notify-api.line.me", 443)) {
    Serial.println("Connection successful");
       
    Serial.println(request);    
    client_tcp.println("POST /api/notify HTTP/1.1");
    client_tcp.println("Connection: close");
    client_tcp.println("Host: notify-api.line.me");
    client_tcp.println("User-Agent: ESP8266/1.0");
    client_tcp.println("Authorization: Bearer " + token);
    client_tcp.println("Content-Type: application/x-www-form-urlencoded");
    client_tcp.println("Content-Length: " + String(request.length()));
    client_tcp.println();
    client_tcp.println(request);
    client_tcp.println();
   
    String getResponse="",Feedback="";
    boolean state = false;
    int waitTime = 3000;   // timeout 3 seconds
    long startTime = millis();
    while ((startTime + waitTime) > millis()) {
      Serial.print(".");
      delay(100);      
      while (client_tcp.available()) {
          char c = client_tcp.read();
          if (state==true) Feedback += String(c);        
          if (c == '\n') {
            if (getResponse.length()==0) state=true;
            getResponse = "";
          }
          else if (c != '\r')
            getResponse += String(c);
          startTime = millis();
       }
       if (getResponse.length()>0) break;
    }
    Serial.println();
    client_tcp.stop();
    return Feedback;
  }
  else
    return "Connection failed";  
}

//開始MQTT連線伺服器
void MQTTConnecte()
{
  MQTTClient.setServer(MQTTServer, MQTTPort);
  MQTTClient.setCallback(MQTTCallback);
  while (!MQTTClient.connected()) {
    //以亂數為ClientID
    String  MQTTClientid = "esp32-" + String(random(1000000, 9999999));
    if (MQTTClient.connect(MQTTClientid.c_str(), MQTTUser, MQTTPassword)) {
      //連結成功,顯示「已連線」。
      Serial.println("MQTT已連線");
      //訂閱SubTopic1主題
      MQTTClient.subscribe(MQTTSubTopic1);
      MQTTClient.subscribe(MQTTSubTopic2);
    } else {
      //若連線不成功,則顯示錯誤訊息,並重新連線
      Serial.print("MQTT連線失敗,狀態碼=");
      Serial.println(MQTTClient.state());
      Serial.println("五秒後重新連線");
      delay(5000);
    }
  }
}

//拍照傳送到MQTT
String SendImageMQTT()
{
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("MQTT capture failed");
    delay(1000);
    ESP.restart();
    return "MQTT capture failed";
  }
  size_t fbLen = fb->len;
  //int ps = 512;
  //開始傳遞影像檔,批次傳黨
  MQTTClient.beginPublish(MQTTTopicPic1, fbLen, false);
  uint8_t *fbBuf = fb->buf;
  for (size_t n = 0; n < fbLen; n = n + 2048) {
    if (n + 2048 < fbLen) {
      MQTTClient.write(fbBuf, 2048);
      fbBuf += 2048;
    } else if (fbLen % 2048 > 0) {
      size_t remainder = fbLen % 2048;
      MQTTClient.write(fbBuf, remainder);
    }
  }
  boolean isPublished = MQTTClient.endPublish();
  esp_camera_fb_return(fb);//清除緩衝區
  if (isPublished) {
    return "MQTT傳輸成功";
  }
  else {
    return "MQTT傳輸失敗,請檢查網路設定";
  }
}

//接收到訂閱時
void MQTTCallback(char* topic, byte* payload, unsigned int length)
{
  Serial.print(topic); Serial.print("訂閱通知:");
  String payloadString;//將接收的payload轉成字串
  //顯示訂閱內容
  for (int i = 0; i < length; i++) {
    payloadString = payloadString + (char)payload[i];
  }
  Serial.println(payloadString);
  //比對主題是否為訂閱主題1
  if (strcmp(topic, MQTTSubTopic1) == 0)
  {
    Serial.println("LED:" + payloadString);
    if (payloadString == "ON")
    {
      digitalWrite(16, HIGH);
    }
    if (payloadString == "OFF")
    {
      digitalWrite(16, LOW);
    }
  }
  //比對主題是否為訂閱主題1
  if (strcmp(topic, MQTTSubTopic2) == 0) {
    Serial.println("Flash:" + payloadString);
    if (payloadString == "ON")
    {
      ledcWrite(4,10);
      delay(1000);
      String result = SendImageMQTT();
      Serial.println(result);
      delay(1000);
      Serial.println("starting to Line");
      sendCapturedImage2LineNotify(lineNotifyToken);
      for (int i=0;i<3;i++) {  
        ledcWrite(4,10);
        delay(200);
        ledcWrite(4,0);
        delay(200);    
      }
    }
    if (payloadString == "OFF")
    {
      ledcWrite(4,0);
    }
  }
}