//--------------------------------------------------------------------------------------------------------
// INCLUDES NECESARIOS
//#include "Config.h" // Donde podemos encontar los "Define"
//#include "src/quirc/quirc.h"
//#include "esp_camera.h"
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
//--------------------------------------------------------------------------------------------------------
// DEFINES NECESARIOS
#define WIFI_RECONNECT_DELAY 500
#define MQTT_RECONNECT_DELAY 5000
#define MAIN_LOOP_DELAY 100
#define TASK_DELAY 100
#define SHUTDOWN_DELAY 3000
//--------------------------------------------------------------------------------------------------------
// COMUNICACIÓN MQTT
// WiFi Parameters
const char* WIFI_SSID = "Wokwi-GUEST"; //"UPV-PSK"; //Enter the router name
const char* WIFI_PASSWORD = ""; //"giirob-pr2-2023"; //Enter the router password
// MQTT Server Parameters
const char* MQTT_CLIENT_ID = "esp8266-weather-demo";
const char* MQTT_BROKER = "broker.mqttdashboard.com"; // "broker.emqx.io";
const char* MQTT_USER = ""; //"emqx";
const char* MQTT_PASSWORD = ""; //"public";
const char* MQTT_TOPIC_COMMANDS = "emqx/Grupo_B3/MQTT/RoboDK/pintura/commands"; // Publico
const char* MQTT_TOPIC_STATUS = "emqx/Grupo_B3/MQTT/RoboDK/pintura/status"; // Me suscribo
const int MQTT_PORT = 1883;
// Definimos parámetros para conectarnos por MQTT
WiFiClient espClient;
PubSubClient client(espClient);
//--------------------------------------------------------------------------------------------------------
// LECTOR QR
/* GPIO of CAMERA_MODEL_ESP32S3_EYE */
/*
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 4
#define SIOC_GPIO_NUM 5
#define Y9_GPIO_NUM 16
#define Y8_GPIO_NUM 17
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 12
#define Y5_GPIO_NUM 10
#define Y4_GPIO_NUM 8
#define Y3_GPIO_NUM 9
#define Y2_GPIO_NUM 11
#define VSYNC_GPIO_NUM 6
#define HREF_GPIO_NUM 7
#define PCLK_GPIO_NUM 13
*/
/* Variables declaration */
/*
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 = "";
*/
//--------------------------------------------------------------------------------------------------------
// DEFINICIÓN DE LOS PINES DE ENTRADA/SALIDA Y LOS ESTRUCTURA DE LOS BOTONES
// Definimos la utilización de los pines
#define PIN_BUTTON_EMERGERCIA 22
#define PIN_BUTTON_MARCHA 34
#define PIN_SENSOR 35
#define LED_MARCHA 32
#define PIN_BIT_0 2
#define PIN_BIT_1 0
#define PIN_BIT_2 19
#define PIN_BIT_3 21
#define PIN_BIT_4 23
#define PIN_BIT_5 33
#define PIN_BIT_6 25
// la función de configuración se ejecuta una vez cuando presionas restablecer o enciendes la placa
// Estructura del boton
struct Button {
const uint8_t PIN;
bool pressed;
};
// Variable para parar el sistema
static bool PARAR=false;
// Declaracion del boton de emergencia
Button Button_Emergencia = {PIN_BUTTON_EMERGERCIA, false};
void IRAM_ATTR isr_Emergencia() {
Button_Emergencia.pressed = true;
PARAR=true;
digitalWrite(LED_MARCHA, LOW);
}
// Variable para saber si se ha pulsador el pulsador de marcha
static bool MARCHA=false;
// Variable para almacenar si ya se ha activado la marcha
static bool marcha_activada = false;
// Variable para que el pulsador no se pueda pulsar varia veces
static bool bloqueo_pulsador = false;
// Declaracion del boton de marcha
Button Button_Marcha = {PIN_BUTTON_MARCHA, false};
void IRAM_ATTR isr_Marcha() {
Button_Marcha.pressed = true;
MARCHA=true;
}
// Variable para que el sensor no se pueda pulsar varia veces
static bool bloqueo_sensor = false;
// Declaracion del sensor
Button Sensor = {PIN_SENSOR, false};
void IRAM_ATTR isr_Sensor() {
Sensor.pressed = true;
}
//--------------------------------------------------------------------------------------------------------
// ESTRUCTURA DEL BUFFER
/*
#define BUFSIZE 10
#define true 1;
#define false 0;
typedef struct Buffer {
float datos[BUFSIZE];
int bufIN=0;
int bufOUT=0;
int contador=0;
portMUX_TYPE taskMux = portMUX_INITIALIZER_UNLOCKED;
} Buffer_Circ;
// Prototipos de las funciones
bool isEmpty(Buffer_Circ* buff);
bool isFull(Buffer_Circ* buff);
int get_item(int* X, Buffer_Circ* buff);
int put_item(int X, Buffer_Circ* buff);
int number(Buffer_Circ* buff);
int listBuffer(Buffer_Circ* buff);
//Función para saber si el Buffer está vacío
bool isEmpty(Buffer_Circ* buff){
if(buff->contador == 0){
return true;
}else{
return false;
}
}
//Función para saber si el Buffer está lleno
bool isFull(Buffer_Circ* buff){
if(buff->contador == BUFSIZE){
return true;
}else{
return false;
}
}
//Funcion para obtener elemento del Buffer
int get_item(float* X, Buffer_Circ* buff ){
if(isEmpty(buff)){
Serial.printf("Error: No hay datos en el vector\n");
return -1;
}
else{
portENTER_CRITICAL (&(buff->taskMux));
*X = buff->datos[buff->bufOUT];
buff->contador--;
//Aritmetica en módulo del índice del vector
buff->bufOUT = (buff->bufOUT+1) % BUFSIZE;
portEXIT_CRITICAL (&(buff->taskMux));
Serial.printf("Sacas dato %d de posicion %d\n", *X, buff->bufOUT);
return 0;
}
}
//Función para introducir elemento en el Buffer
int put_item(float X , Buffer_Circ* buff ){
if(isFull(buff)){
Serial.printf("Error: El vector esta lleno\n");
return -1;
}
else{
portENTER_CRITICAL (&(buff->taskMux));
buff->datos[buff->bufIN]=X;
buff->contador++;
//Aritmetica en módulo del índice del vector
buff->bufIN = (buff->bufIN+1) % BUFSIZE;
portEXIT_CRITICAL (&(buff->taskMux));
Serial.printf("Meto dato %d en posicion %d\n", X, buff->bufIN);
return 0;
}
}
//Función para saber cuántos elementos tiene el Buffer
int number(Buffer_Circ* buff){
return buff->contador;
}
//Función para listar el contenido del Buffer
int listBuffer(Buffer_Circ* buff){
Serial.printf("Tu buffer contiene: ");
for(int i=0; i<BUFSIZE; i++){
Serial.printf("\t%d", buff->datos[i]);
}
Serial.printf("\n");
}
// Definimos la variable del buffer
static Buffer_Circ Mi_buffer;
*/
//--------------------------------------------------------------------------------------------------------
// BIT DE CONTROL DEL SISTEMA
// Variable para controlar por donde se encuentra el sistema de la ESP32
static byte ESPByte = 0b00000000; // Inicializa la variable byte
// Variable para controlar por donde se encuentra el sistema de la estación
static byte sistemaByte = 0b00000000;
//--------------------------------------------------------------------------------------------------------
// DEFINICIÓN DE FUNCIONES
void init_button(); // Función para la inicializar los botones
void init_camera(); // Función para la inicializar la camara
void reconnectMQTT(); // Función para la reconexion MQTT
void reconnectWiFi(); // Función para la reconexion WiFi
void callback(char *topic, byte *payload, unsigned int length); // Callback para cuando recibimos un mensaje
void enviar_mensaje_mqtt(const char* mensaje); // Función para enviar mensajes por MQTT
//--------------------------------------------------------------------------------------------------------
// SETUP Y LOOP
void setup() {
// Función para la inicializar los botones
init_button();
// Inicializacion del pulsador de emergencia
pinMode(Button_Emergencia.PIN, INPUT); // INPUT_PULLUP
attachInterrupt(Button_Emergencia.PIN, isr_Emergencia, FALLING);
// Inicializacion del pulsador de marcha
pinMode(Button_Marcha.PIN, INPUT); // INPUT_PULLUP
attachInterrupt(Button_Marcha.PIN, isr_Marcha, FALLING);
// Inicializacion del sensor
pinMode(Sensor.PIN, INPUT); // INPUT_PULLUP
attachInterrupt(Sensor.PIN, isr_Sensor, FALLING);
Serial.begin(112500);
delay(1000);
// Función para la inicializar la camara
//init_camera();
// Connecting to WiFi
reconnectWiFi();
//connecting to a mqtt broker
client.setServer(MQTT_BROKER, MQTT_PORT);
// Callback para cuando recibimos un mensaje
client.setCallback(callback);
// Conexión con MQTT
reconnectMQTT(); // Función para conectarnos al servidor MQTT
if (client.connected()) {
// Publish
client.publish(MQTT_TOPIC_COMMANDS, "ESP32 Conectado!");
}
// Creación de tareas
xTaskCreate(Control_PulMarcha,"Control_PulMarcha",10000,NULL,2,NULL);
xTaskCreate(Control_Sistema,"Control_Sistema",10000,NULL,2,NULL);
//xTaskCreate(Control_Pintura_robot,"Control_Pintura_robot",10000,&Mi_buffer,2,NULL);
//xTaskCreate(Control_Temperatura_QR,"Control_Temperatura_QR",10000,&Mi_buffer,2,NULL);
}
void loop() {
Serial.println("Inicio Main");
Serial.println("-----------------------");
while (!PARAR)
{
// Verificar y reconectar WiFi si es necesario
if (WiFi.status() != WL_CONNECTED)
{
reconnectWiFi();
}
// Manejar los mensajes MQTT
if (client.connected())
{
client.loop();
}
else
{
reconnectMQTT(); // Intentar reconectar al servidor MQTT
}
delay(MAIN_LOOP_DELAY); // Pequeña pausa para evitar un bucle demasiado rápido
}
Serial.println("CERRANDO");
delay(SHUTDOWN_DELAY);
Serial.println("FIN Main");
exit(0);
}
//--------------------------------------------------------------------------------------------------------
// FUNCIONES PARA INICIALIZAR
// Función para la inicializar los botones
void init_button()
{
// initialize digital pin LED_BUILTIN as an output.
// Inicializacion del LED
pinMode(LED_MARCHA, OUTPUT);
pinMode(PIN_BIT_0, OUTPUT);
pinMode(PIN_BIT_1, OUTPUT);
pinMode(PIN_BIT_2, OUTPUT);
pinMode(PIN_BIT_3, OUTPUT);
pinMode(PIN_BIT_4, OUTPUT);
pinMode(PIN_BIT_5, OUTPUT);
pinMode(PIN_BIT_6, OUTPUT);
}
/*
// Función para la inicializar la camara
init_camera()
{
*/
/* Camera configuration. */
/*
Serial.println("-----------------------");
Serial.println("Start configuring and initializing the camera...");
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 = 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);
Serial.println("Configure and initialize the camera successfully.");
Serial.println();
}
*/
//--------------------------------------------------------------------------------------------------------
// FUNCIONES DESARROLLADAS
// Función para la reconexión WiFi
void reconnectWiFi() {
Serial.println("-----------------------");
Serial.print("Connecting to WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED && !PARAR) {
delay(WIFI_RECONNECT_DELAY);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected!");
} else {
Serial.println("Failed to connect!");
}
Serial.println("-----------------------");
}
// Función para la reconexión MQTT
void reconnectMQTT()
{
Serial.println("Inicio Función reconnectMQTT");
Serial.println("-----------------------");
while (!PARAR && !client.connected() && WiFi.status() == WL_CONNECTED)
{
Serial.print("Attempting MQTT connection...");
if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
Serial.println("connected");
// Nos suscribimos despues de realizar la conexión
client.subscribe(MQTT_TOPIC_STATUS);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(MQTT_RECONNECT_DELAY);
}
}
}
// Callback para cuando recibimos un mensaje
void callback(char *topic, byte *payload, unsigned int length)
{
String mensaje = ""; // Inicializar una cadena vacía para almacenar el mensaje recibido
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
for (int i = 0; i < length; i++)
{
Serial.print((char) payload[i]);
mensaje += (char) payload[i]; // Concatenar cada carácter recibido al final de la cadena
}
Serial.println();
Serial.println("-----------------------");
// Comprobar el mensaje recibido
// Comprovamos si el sistema está en marcha
if (!marcha_activada && mensaje.equals("Comando de Marcha recibido"))
{
ESPByte = 0b00000001; // Arrancamos el sistema
marcha_activada = true; // Establecer la bandera a verdadero
MARCHA = false; // Establecer la bandera a falso
}
else if(!marcha_activada && MARCHA)
{
marcha_activada = false; // Establecer la bandera a falso
MARCHA = false; // Establecer la bandera a falso
Serial.println("Mensaje no reconocido");
digitalWrite(LED_MARCHA, LOW);
digitalWrite(PIN_BIT_0, LOW);
Serial.println("-----------------------");
}
// Comprovamos si el mensaje probiene de la estacion de RoboDK
if (mensaje.equals("Programa Estación RoboDK Ejecutado") && marcha_activada)
{
bloqueo_sensor = false;
ESPByte = (ESPByte << 1); // Desplazamos el bit de control
}
}
// Función para enviar mensajes por MQTT
void enviar_mensaje_mqtt(const char* mensaje) {
if (client.connected()) {
client.publish(MQTT_TOPIC_COMMANDS, mensaje);
} else {
Serial.println("Error: No se pudo enviar el mensaje MQTT, el cliente no está conectado.");
}
}
//--------------------------------------------------------------------------------------------------------
// TAREAS
// Tarea que controla si el pulsador de marcha a sido activado
void Control_PulMarcha( void * parameter )
{
Serial.println("Inicio Tarea Control_PulMarcha");
Serial.println("-----------------------");
while (!PARAR)
{
if (Button_Marcha.pressed && !bloqueo_pulsador) {
Button_Marcha.pressed = false;
bloqueo_pulsador = true;
delay(100);
if(MARCHA && !marcha_activada)
{
Serial.printf("El botón de Marcha ha sido pulsado\n");
Serial.println("-----------------------");
Serial.println("Encendido del LED");
digitalWrite(LED_MARCHA, HIGH);
digitalWrite(PIN_BIT_0, HIGH); // Encendemos bit de control
Serial.println("-----------------------");
enviar_mensaje_mqtt("0b00000001");
delay(1000);
} else {
Button_Marcha.pressed = false;
MARCHA = false; // Establecer la bandera a falso
}
} else if(bloqueo_pulsador && !MARCHA) {
Button_Marcha.pressed = false;
bloqueo_pulsador = false;
}
delay(TASK_DELAY);
}
// Enviamos mensaje para apagar la estación
enviar_mensaje_mqtt("PARAR");
Serial.println("Mensaje para PARAR la estación enviado");
delay(100);
// Apagamos el led de control
digitalWrite(LED_MARCHA, LOW);
Serial.println("Finalizando Tarea Control_PulMarcha");
vTaskDelete( NULL );
}
// Tarea para controlar por donde se encuentra el sistema
void Control_Sistema( void * parameter )
{
Serial.println("Inicio Tarea Control_Sistema");
Serial.println("-----------------------");
while (!PARAR)
{
if (ESPByte != sistemaByte && Sensor.pressed && !bloqueo_sensor)
{
Sensor.pressed = false;
bloqueo_sensor = true;
String mensaje = "0b";
// Busca el bit activo
for (int i = 0; i < 8; i++)
{
//if (ESPByte & (1 << i))
if (ESPByte & (1 << (7 - i)))
{
sistemaByte = ESPByte;
mensaje += "1";
for (int j = (i+1); j < 8; j++)
{
mensaje += "0";
}
// Apagamos bits de control
digitalWrite(PIN_BIT_0, LOW);
digitalWrite(PIN_BIT_1, LOW);
digitalWrite(PIN_BIT_2, LOW);
digitalWrite(PIN_BIT_3, LOW);
digitalWrite(PIN_BIT_4, LOW);
digitalWrite(PIN_BIT_5, LOW);
digitalWrite(PIN_BIT_6, LOW);
// Encendemos bit de control
switch (i) {
case 6:
digitalWrite(PIN_BIT_1, HIGH);
break;
case 5:
digitalWrite(PIN_BIT_2, HIGH);
break;
case 4:
digitalWrite(PIN_BIT_3, HIGH);
break;
case 3:
digitalWrite(PIN_BIT_4, HIGH);
break;
case 2:
digitalWrite(PIN_BIT_5, HIGH);
break;
case 1:
digitalWrite(PIN_BIT_6, HIGH);
break;
}
Serial.printf("El bit %d está activo.\n", 7 - i);
enviar_mensaje_mqtt(mensaje.c_str());
break; // Sal del bucle una vez que encuentres el bit activo
}
mensaje += "0";
}
}
else if(Sensor.pressed && bloqueo_sensor)
{
Sensor.pressed = false;
}
else if(ESPByte == 0b10000000)
{
ESPByte = 0b00000000; // Paramos el sistema
sistemaByte = 0b00000000; // Paramos el sistema sistemaByte = 0b00000000;
marcha_activada = false; // Establecer la bandera a falso
Serial.println("-----------------------");
Serial.println("La estación de pintura ha terminado");
digitalWrite(LED_MARCHA, LOW);
digitalWrite(PIN_BIT_6, LOW);
}
delay(TASK_DELAY);
}
delay(1000);
// Apagamos bits de control
digitalWrite(PIN_BIT_0, LOW);
digitalWrite(PIN_BIT_1, LOW);
digitalWrite(PIN_BIT_2, LOW);
digitalWrite(PIN_BIT_3, LOW);
digitalWrite(PIN_BIT_4, LOW);
digitalWrite(PIN_BIT_5, LOW);
digitalWrite(PIN_BIT_6, LOW);
Serial.println("Finalizando Tarea Control_Sistema");
vTaskDelete( NULL );
}
/*
// Tarea para controlar el sensor de temperatura/humedad
// (En este caso sera simulado por un sersor virtual (vía QR))
void Control_Temperatura_QR( void * pvParameters ){
Serial.println("Inicio Tarea Control_Temperatura_QR");
Serial.println("-----------------------");
// Inicialización del buffer
float temp=0.0f;
TickType_t xLastWakeTime;
Buffer_Circ* buff_prod = (Buffer_Circ*) pvParameters;
xLastWakeTime = xTaskGetTickCount();
while (!PARAR)
{
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;
}
quirc_resize(q, fb->width, fb->height);
image = quirc_begin(q, NULL, NULL);
memcpy(image, fb->buf, fb->len);
quirc_end(q);
int count = quirc_count(q);
if (count > 0) {
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");
temp = data->temperatura;
put_item(temp,buff_prod);
}
Serial.println();
}
esp_camera_fb_return(fb);
fb = NULL;
image = NULL;
quirc_destroy(q);
vTaskDelayUntil( &xLastWakeTime, (TASK_DELAY/ portTICK_PERIOD_MS));
}
Serial.println("Finalizando Tarea Control_Temperatura_QR");
vTaskDelete( NULL );
}
// Tarea para controlar el tipo de pintura del robot dependiendo de la temperatura
void Control_Pintura_robot( void * parameter )
{
Serial.println("Inicio Tarea Control_Pintura_robot");
Serial.println("-----------------------");
// Inicialización de la variables
float leer_temp = 0.0f,
bool bloqueo_alt = false, bloqueo_amb = false, bloqueo_baj = false;
// Inicialización del buffer
TickType_t xLastWakeTime;
Buffer_Circ* buff_cons = (Buffer_Circ*) parameter;
xLastWakeTime = xTaskGetTickCount();
while (!PARAR)
{
if (!isEmpty(buff_cons)) // Comprobamos que el buffer no este bacio
{
// Miramos la temperatura ambiente
get_item(&leer_temp,buff_cons);
if(leer_temp >= 20.0f && bloqueo_alt == false)
{
bloqueo_alt = true;
bloqueo_amb = false;
bloqueo_baj = false;
Serial.printf("La temperatura ambiente esta por encima de los 20ºC.\n\n");
enviar_mensaje_mqtt("Utilizar pintura para temperatura altas");
}
else if (leer_temp >= 10.0f && leer_temp <= 20.0f && bloqueo_amb == false)
{
bloqueo_alt = false;
bloqueo_amb = true;
bloqueo_baj = false;
Serial.printf("La temperatura ambiente es de %fºC.\n\n", temp);
enviar_mensaje_mqtt("Utilizar pintura para temperatura ambiente");
}
else if (leer_temp <= 10.0f && bloqueo_baj == false)
{
bloqueo_alt = false;
bloqueo_amb = false;
bloqueo_baj = true;
Serial.printf("La temperatura ambiente esta por debajo de los 10ºC.\n\n");
enviar_mensaje_mqtt("Utilizar pintura para temperaturas bajas");
}
}
vTaskDelayUntil( &xLastWakeTime, (TASK_DELAY/ portTICK_PERIOD_MS));
}
Serial.println("Finalizando Tarea Control_Pintura_robot");
vTaskDelete( NULL );
}
*/