/*
ESP32-CAM QR code Reader
Author : ChungYi Fu (Kaohsiung, Taiwan)  2021-8-13 20:00
https://www.facebook.com/francefu

Refer to the code
https://github.com/alvarowolfx/ESP32QRCodeReader

自訂指令格式 http://192.168.xxx.xxx/control?cmd=P1;P2;P3;P4;P5;P6;P7;P8;P9

http://192.168.xxx.xxx/?ip                      //取得APIP, STAIP
http://192.168.xxx.xxx/?mac                     //取得MAC位址
http://192.168.xxx.xxx/?digitalwrite=pin;value  //數位輸出
http://192.168.xxx.xxx/?analogwrite=pin;value   //類比輸出
http://192.168.xxx.xxx/?digitalread=pin         //數位讀取
http://192.168.xxx.xxx/?analogread=pin          //類比讀取
http://192.168.xxx.xxx/?touchread=pin           //觸碰讀取
http://192.168.xxx.xxx/?restart                 //重啟電源
http://192.168.xxx.xxx/?flash=value             //閃光燈 value= 0~255
http://192.168.xxx.xxx/?servo=pin;value         //伺服馬達 value= 0~180
http://192.168.xxx.xxx/?relay=pin;value         //繼電器 value = 0, 1
http://192.168.xxx.xxx/?uart=value              //序列埠
*/



// Serial.printf(); kann anscheinend keine Strings ausgeben, die länger als 13 Zeichen sind -> dadurch gehts: Serial.printf("Insertcommand: %s\n:", INSERT_SQL.c_str());

#include "esp_camera.h"         
//#include "soc/soc.h"             
#include "soc/rtc_cntl_reg.h"    
#include "quirc.h"

//Includes&defines etc für den MariaDB Entry ###########################################################
#include "defines.h"
#include "Credentials.h"

#include <MySQL_Generic.h>

#define USING_HOST_NAME     false

#if USING_HOST_NAME
  // Optional using hostname, and Ethernet built-in DNS lookup
  char server[] = "your_account.ddns.net"; // change to your server's hostname/URL
#else
  IPAddress server(10, 42, 0, 1);
#endif

uint16_t server_port = 3306;    //5698;

char default_database[] = "myTestDb";           //"test_arduino";
char default_table[]    = "myTestTable";          //"test_arduino";

String default_value    = "defaultString"; 

// Sample query
String INSERT_SQL = String("INSERT INTO ") + default_database + "." + default_table 
                 + " (qrCodeText) VALUES ('" + default_value + "')";


#define LED_BUILTIN 4   //die Zeile wird für das LED benötigt


MySQL_Connection conn((Client *)&client);

MySQL_Query *query_mem;
//Includes&defines etc für den MariaDB Entry - ENDE ####################################################

TaskHandle_t Task;

//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

struct QRCodeData
{
  bool valid;
  int dataType;
  uint8_t payload[1024];
  int payloadLen;
};

struct quirc *q = NULL;
uint8_t *image = NULL;  
camera_fb_t * fb = NULL;
struct quirc_code code;
struct quirc_data data;
quirc_decode_error_t err;
struct QRCodeData qrCodeData;  
String QRCodeResult = "";



camera_config_t config;

//###############################################################################################
void setup() {
  //WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
    
  Serial.begin(115200);
  //Serial.setDebugOutput(false);
  Serial.println();

  Serial.println("--------------------------Starting the EPS32-QR-Code-Reader--------------------------");

  pinMode(LED_BUILTIN, OUTPUT); //die Zeile wird für das LED benötigt
  
https://github.com/espressif/esp32-camera/blob/master/driver/include/esp_camera.h
  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 = 10000000;
  config.pixel_format = PIXFORMAT_GRAYSCALE;
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 15;
  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();
  s->set_framesize(s, FRAMESIZE_QVGA);


  xTaskCreatePinnedToCore(
             QRCodeReader,  // Task function.
             "Task",        // name of task.
             10000,         // Stack size of task
             NULL,          // parameter of the task
             1,             // priority of the task
             &Task,         // Task handle to keep track of created task
             0);            // pin task to core 0 

  //Serial.print("listenConnection running on core ");
  //Serial.println(xPortGetCoreID());   

//Setup für insert in MariaDB #########################################################
Serial.begin(115200);
  while (!Serial && millis() < 5000); // wait for serial port to connect

/* Infoausgabe
  MYSQL_DISPLAY1("\n starting QR-Code reader on", BOARD_NAME);
  MYSQL_DISPLAY(MYSQL_MARIADB_GENERIC_VERSION);
*/

  // Remember to initialize your WiFi module
#if ( USING_WIFI_ESP8266_AT  || USING_WIFIESPAT_LIB ) 
  /* Infoausgabe
  #if ( USING_WIFI_ESP8266_AT )
    MYSQL_DISPLAY("Using ESP8266_AT/ESP8266_AT_WebServer Library");
  #elif ( USING_WIFIESPAT_LIB )
    MYSQL_DISPLAY("Using WiFiEspAT Library");
  #endif
  */
  
  // initialize serial for ESP module
  EspSerial.begin(115200);
  // initialize ESP module
  WiFi.init(&EspSerial);

  MYSQL_DISPLAY(F("WiFi shield init done"));

  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD)
  {
    MYSQL_DISPLAY(F("WiFi shield not present"));
    // don't continue
    while (true);
  }
#endif



  // Begin WiFi section
  Serial.printf("Connecting to %s. \n", ssid);
  //MYSQL_DISPLAY1("Connecting to", ssid);

  //WiFi.begin(ssid, pass);
  WiFi.begin(ssid);
  
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    //MYSQL_DISPLAY0(".");
    
    //ESP32-LED Output, dass es nicht mit dem WLAN verbunden ist
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
  }

  // print out info about the connection:
  MYSQL_DISPLAY1("Connected to network. My IP address is:", WiFi.localIP());

  MYSQL_DISPLAY3("Connecting to SQL Server @", server, ", Port =", server_port);
  MYSQL_DISPLAY5("User =", user, ", PW =", password, ", DB =", default_database);


  //ESP32-LED Output, dass es mit dem WLAN verbunden ist
  for(int i=0; i<3;i++){
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(300);
  }




//Setup für insert in MariaDB - Ende #########################################################
}

void ledOutputError(){
  
      //ESP32 LED Ausgabe, dass etwas nicht funktioniert hat
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(1000);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);

}


//###################################################################################
void runInsert() // MariaDB-Insert-Funktion
{
  // Initiate the query class instance
  MySQL_Query query_mem = MySQL_Query(&conn);

  if (conn.connected())
  {
    MYSQL_DISPLAY(INSERT_SQL);
    
    // Execute the query
    // KH, check if valid before fetching
    if ( !query_mem.execute(INSERT_SQL.c_str()) )
    {
      MYSQL_DISPLAY("Insert error");
      ledOutputError();
    }
    else
    {
      MYSQL_DISPLAY("Data Inserted.");

      //ESP32 LED Ausgabe, dass etwas inserted wurde
      digitalWrite(LED_BUILTIN, HIGH);
      delay(1000);
      digitalWrite(LED_BUILTIN, LOW);
    }
  }
  else
  {
    MYSQL_DISPLAY("Disconnected from Server. Can't insert.");
    ledOutputError();
  }
}



//###############################################################################################----------------------------
void loop() {

}


void charToString(const char *str, String &dbInsertString){

  dbInsertString = "";  //max Länge 13 Zeichen für eine Ausgabe in Serial.printf 
  size_t len = strlen(str);
  dbInsertString.reserve(len);
  
  for(size_t i=0;i<len;i++){
    dbInsertString += str[i];
  }
  
  //Serial.printf("Ausgabe des Strings: %s\n",dbInsertString.c_str()); 
}


//###############################################################################################----------------------------
void QRCodeReader( void * pvParameters ){
  /* Infoausgabe
  Serial.print("QRCodeReader running on core ");
  Serial.println(xPortGetCoreID());
  */

  while(1){
      q = quirc_new();
      if (q == NULL){
        Serial.print("can't create quirc object\r\n");  
        continue;
      }
    
      fb = esp_camera_fb_get();
      if (!fb)
      {
        Serial.println("Camera capture failed");
        continue;
      }   
      
      //Serial.printf("quirc_begin\r\n");
      quirc_resize(q, fb->width, fb->height);
      image = quirc_begin(q, NULL, NULL);
      //Serial.printf("Frame w h len: %d, %d, %d \r\n", fb->width, fb->height, fb->len);
      memcpy(image, fb->buf, fb->len);
      quirc_end(q);
      //Serial.printf("quirc_end\r\n");
      
      int count = quirc_count(q);
      if (count > 0) {
        //Serial.println(count);
        quirc_extract(q, 0, &code);
        err = quirc_decode(&code, &data);
    
        if (err){
          Serial.println("Decoding FAILED");
          QRCodeResult = "Decoding FAILED";
        } else {
          Serial.printf("Decoding successful:\n");
          dumpData(&data);

          //MariaDb-part #################--------------------
          MYSQL_DISPLAY("Connecting...");
      
                 
          if (conn.connectNonBlocking(server, server_port, user, password) != RESULT_FAIL)
          {
            delay(500);

            char * str  = (char *) &data.payload[0];  //nicht sicher ob man die Umwandlung braucht
            charToString(str,default_value);
            
            INSERT_SQL = String("INSERT INTO ") + default_database + "." + default_table + " (qrCodeText) VALUES ('" + default_value + "')";
            //Serial.printf("Insertcommand: %s\n:", INSERT_SQL.c_str());

            runInsert();
            conn.close();                     // close the connection
          } 
          else 
          {
            MYSQL_DISPLAY("\nConnect failed. Trying again on next iteration.");
            ledOutputError();
          }

        
          //MYSQL_DISPLAY("\nSleeping...");
          MYSQL_DISPLAY("================================================");
          delay(2000);  //um nicht den gleichen Eintrag sofort doppelt drinnen zu haben
          //MariaDb-part-Ende #################--------------------

        } 
        Serial.println();
      } 

      
      esp_camera_fb_return(fb);
      fb = NULL;
      image = NULL;  
      quirc_destroy(q);
  }
}

//###############################################################################################
void dumpData(const struct quirc_data *data)
{
  //Serial.printf("Version: %d\n", data->version);
  //Serial.printf("ECC level: %c\n", "MLHQ"[data->ecc_level]);
  //Serial.printf("Mask: %d\n", data->mask);
  //Serial.printf("Length: %d\n", data->payload_len);
  Serial.printf("Inhalt des QR-Codes: %s\n", data->payload);

}
