/*****************************broker MQTT*************************************/
//https://mqtthq.c//https://mqtthq.com/
//     74HC165 Shift register input example
//     by Uri Shaked, https://wokwi.com/arduino/projects/306031380875182657
/*****************************************************************************/

#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#ifndef ESP32
#pragma message(ESTE EXEMPLO É APENAS PARA ESP32!)
#error Selecione a placa ESP32.
#endif

#include <WiFi.h>
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define WIFI_CHANNEL 6

/**********************mqtt FREE*****************************/
#include <PubSubClient.h>
const char* mqtt_server = "public.mqtthq.com";
#define mqtt_port 1883
#define MQTT_USER ""
#define MQTT_PASSWORD ""
#define MQTT_SERIAL_PUBLISH_CH "mqttHQ-client-test"
#define MQTT_SERIAL_RECEIVER_CH "mqttHQ-client-test"
WiFiClient wifiClient;

PubSubClient client(wifiClient);
/********************************************************/
/********************lcd*************************/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd= LiquidCrystal_I2C(0x27, 16, 2);
/*************************************************/



#include "DHTesp.h"
const int DHT_PIN = 15;
DHTesp dhtSensor;

//mapeamento de pinos
#define LED1 14
#define LED2 2

//variáveis auxiliares
typedef struct ablink
{
  uint16_t usTempo;
  uint8_t pino;
}led_t;

led_t led1 = {pdMS_TO_TICKS(2000),LED1};  //estrutura para led1: delay 1 seg e pino=LED1 
led_t led2 = {pdMS_TO_TICKS(1000),LED2};   //estrutura para led2: delay .2 seg e pino=LED2

//handles de tasks e filas
TaskHandle_t taskBlink1Handle, taskBlink2Handle, TaskRecebeHandle, TaskEmiteHandle, TaskDhtHandle;

//Handle de filas
QueueHandle_t xFilaHandle;

//protótipos de funções auxiliares

//protótipos de tasks
void taskBlink(void *pvParameters);
void TaskRecebe(void *pvParameters);
void TaskEmite(void *pvParameters);
void TaskDht(void *pvParameters);

BaseType_t  xSucessTaskEmite, xSucessTaskRecebe, xSucessTaskBlink1, xSucessTaskBlink2, xSucessTaskDht;


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Tentando conexão ao Broker MQTT...");
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print(String("Conectando MQTT "));

    // Create a random client ID
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str(),MQTT_USER,MQTT_PASSWORD)) {
      Serial.println("conectado!");
      // ... and resubscribe
      client.subscribe("mqttHQ-client-test");
      //Once connected, publish an announcement...
      client.publish("mqttHQ-client-test", "USANDO BROKER MQTT COM WOKWI - WIFI");
      
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void callback(char* topic, byte *payload, unsigned int length) {
    Serial.println("-------new message from broker-----");
    Serial.print("channel:");
    Serial.println(topic);
    Serial.print("data:");  
    Serial.write(payload, length);
    Serial.println();
}

/********************************decarações multiplex****************/
const byte latchPin = 5;        // to latch the inputs into the registers
const byte clockPin = 18;       // I choose the SCK pin
const byte dataPin = 19;        // I choose the MISO pin
uint32_t oldOptionSwitch = 0;   // previous state of all the inputs

const int pulseWidth = 10;      // pulse width in microseconds
#include "LerUm.h"
/**********************fim declarações multiplex**********************/
void setup ()
{
  Serial.begin(115200);
  lcd.init(); // initialize the lcd
  lcd.backlight();
  /************************wifi*********************/
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
  Serial.print("Fazendo conexão ao WiFi ");
  Serial.print(WIFI_SSID);
  // Aguardando a conexão se concretizar no wifi
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(String("Conectando WIFI "));
  }
  Serial.println(" Conectado!");

  Serial.print("Endereço IP: ");
  Serial.println(WiFi.localIP());
/****************fim wifi***************************/

/*************************mqtt**************************/
 
 Serial.setTimeout(500);// Set time out for 
  //setup_wifi();
  client.setServer(mqtt_server,  mqtt_port);
  client.setCallback(callback);
  reconnect();
  
/*********************************fim MQTT********************/

/*****************dht22*********************/
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
/****************fim do DHT22**************************/

  //criar fila
  xFilaHandle=xQueueCreate(5, sizeof(int8_t));
  //teste da criação da fila
  if(xFilaHandle==NULL)Serial.println("Criação da fila falhou");
  else Serial.println("Fila criada = xFila");

  xTaskCreatePinnedToCore(TaskDht,"TASKDHT",configMINIMAL_STACK_SIZE+1024,NULL,1,NULL,PRO_CPU_NUM);
  if(xSucessTaskDht==pdFAIL) Serial.println("Falha na criação da TaskDht");
  xTaskCreatePinnedToCore(taskBlink,"TASKBLINK1",configMINIMAL_STACK_SIZE+1024,&led1,1,NULL,PRO_CPU_NUM);
  if(xSucessTaskBlink1==pdFAIL) Serial.println("Falha na criação da TaskBlink1");
  xTaskCreatePinnedToCore(taskBlink,"TASKBLINK2",configMINIMAL_STACK_SIZE+1024,&led2,1,NULL,PRO_CPU_NUM);
  if(xSucessTaskBlink2==pdFAIL) Serial.println("Falha na criação da TaskBlink2");
  xSucessTaskEmite=xTaskCreatePinnedToCore(TaskEmite,"TaskEmite",configMINIMAL_STACK_SIZE+8192,NULL,1,&TaskEmiteHandle,APP_CPU_NUM);
  if(xSucessTaskEmite==pdFAIL) Serial.println("Falha na criação da Task Emite");
  xSucessTaskRecebe=xTaskCreatePinnedToCore(TaskRecebe,"TaskRecebe",configMINIMAL_STACK_SIZE+4096,NULL,1,&TaskRecebeHandle,APP_CPU_NUM);
  if(xSucessTaskRecebe==pdFAIL) Serial.println("Falha na criação da Task Recebe");

  /*********************setup multiplex**************************************/
  Serial.println( "Turn on and off the switches");
  Serial.println( "Top row is switch 0 (right) to switch 7 (left)");
  Serial.println( "Second row is 8 to 15, and so on");

  pinMode( clockPin, OUTPUT);   // sinal de relógio, idle LOW 
  pinMode( latchPin, OUTPUT);   // atch (copiar entrada em registros), idle HIGH
  pinMode( dataPin, INPUT);
  digitalWrite( latchPin, HIGH);
  /**************************fim setup multiplex*****************************/
}//fim do setup

/************************MQTTT***************************************/

void publishSerialData(char *serialData){
  if (!client.connected()) {
    reconnect();
  }
  client.publish(MQTT_SERIAL_PUBLISH_CH, serialData);
}

/****************************************************************/

void loop ()
{
  
    client.loop();
   if (Serial.available() > 0) {
     char mun[501];
     memset(mun,0, 501);
     Serial.readBytesUntil( '\n',mun,500);
     publishSerialData(mun);
   }

  /********************loop multiplex*************************************/
  // Pulso para carga paralela de todos os 74HC165
  digitalWrite( latchPin, LOW);    
  delayMicroseconds( pulseWidth);
  digitalWrite( latchPin, HIGH);

  // Lendo um 74HC165 de cada vez e combinando-os em uma variável de 32 bits
  // O último 74HC165 está na parte inferior, mas os switches começam a numeração
  // no topo. Então o primeiro byte tem que ser deslocado para o lugar mais alto.
  uint32_t optionSwitch = 0;
  for( int i=24; i>=0; i-=8)
  {
    optionSwitch |= ((uint32_t) ReadOne165()) << i;
  }

  for( int i = 0; i<32; i++)
  {
    if( bitRead( optionSwitch, i) != bitRead( oldOptionSwitch,i))
    {
      Serial.print( "Switch ");
      if( i < 10)
        Serial.print( " ");
        Serial.print( i);
        Serial.print( " is now ");
        
        int chaves = bitRead( optionSwitch, i);
        char data[8];
        char Switch[8];
        dtostrf(i, 2, 0, Switch);
        dtostrf(chaves, 1, 0, data);
        client.publish(MQTT_SERIAL_PUBLISH_CH, Switch);
        client.publish(MQTT_SERIAL_PUBLISH_CH, data);
        
        Serial.println( bitRead( optionSwitch, i) == 0 ? "down ↓" : "up   ↑");
    }
  }
  
  oldOptionSwitch = optionSwitch;
  delay( 25);      // slow down the sketch to avoid switch bounce
  /*********************fim loop multiplex********************************/
}//fim do loop

void taskBlink(void *pvParameters){
  //UBaseType_t uxMemTask;   //teste de uso da RAM
  led_t * led = (led_t *)pvParameters;
  pinMode(led->pino,OUTPUT);

    while(true){
    /*
    uxMemTask = uxTaskGetStackHighWaterMark(NULL);
    Serial.print("Mem TaskBlink: ");
    Serial.println(uxMemTask);
    */
    digitalWrite(led->pino,!digitalRead(led->pino)); //inverte estado do LED
    vTaskDelay(pdMS_TO_TICKS(led->usTempo));//determina o tempo para cada estado
    xQueueSend(xFilaHandle,&led->pino,portMAX_DELAY);//transmite via fila para a impressão
  }
    

};//fim da taskBlink

void TaskEmite(void *pvParameters){
  //UBaseType_t uxMemTask;  //Teste de uso da RAM
  //int contador = 100;
  //UBaseType_t uxHighWaterMark;
  while(true){  
    /*
    uxMemTask = uxTaskGetStackHighWaterMark(NULL);
    Serial.print("Mem TaskEmite: ");
    Serial.println(uxMemTask);
    */
    /*
    if(contador<110){
     xQueueSend(xFilaHandle,&contador,portMAX_DELAY);
     contador++;
    }else contador=100;
    vTaskDelay(pdMS_TO_TICKS(5000));
    */
  }
}


void TaskRecebe(void *pvParameters){
  //UBaseType_t uxMemTask;  //Teste de uso da RAM
  int valorRecebido;
  while (true)
  {
    /*
    uxMemTask = uxTaskGetStackHighWaterMark(NULL);
    Serial.print("Mem TaskRecebe: ");
    Serial.println(uxMemTask);
    */
    xQueueReceive(xFilaHandle,&valorRecebido,portMAX_DELAY);
    /*
    Serial.println("o valor recebido: "+ String(valorRecebido)); 
    
    int val =valorRecebido;   
    
    // Convert the value to a char array
    char valString[8];
    dtostrf(val, 1, 0, valString);
    Serial.print("Valor recebido para envio: ");
    Serial.println(valString);
    client.publish(MQTT_SERIAL_PUBLISH_CH, valString);
    */
}
}


void TaskDht(void *pvParameters){
  //UBaseType_t uxMemTask;    //Teste de uso da RAM
  while(true){
    /*
    uxMemTask = uxTaskGetStackHighWaterMark(NULL);
    Serial.print("Mem TaskDht: ");
    Serial.println(uxMemTask);
    */
  TempAndHumidity  valor = dhtSensor.getTempAndHumidity();
  /*
  Serial.println("Temp: " + String(valor.temperature, 1) + "°C");
  Serial.println("Umid: " + String(valor.humidity, 1) + "%");
  Serial.println("---");
  */
    int valTem = valor.temperature;   
    int valUmi = valor.humidity;
    
    // Convert the value to a char array
    char valTemString[8];
    char valUmiString[8];
    dtostrf(valTem, 1, 1, valTemString);
    dtostrf(valUmi, 1, 1, valUmiString);
    //Serial.print("Valor recebido para envio: ");
    //Serial.println(valTemString);
    //Serial.println(valUmiString);
    /*
    client.publish(MQTT_SERIAL_PUBLISH_CH, "Temperatura: ");
    client.publish(MQTT_SERIAL_PUBLISH_CH, valTemString);
    client.publish(MQTT_SERIAL_PUBLISH_CH, "Umidade: ");
    client.publish(MQTT_SERIAL_PUBLISH_CH, valUmiString);
    */

    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(String("Temp: "));
    lcd.setCursor(0,1);
    lcd.print(String(valTemString));
    lcd.print(String("C"));
    lcd.setCursor(8,0);
    lcd.print(String("Umid: "));
    lcd.setCursor(8,1);
    lcd.print(String(valUmiString));
    lcd.print(String("%"));




  vTaskDelay(pdMS_TO_TICKS(5000));
  }
}



74HC165
74HC165
74HC165
74HC165