#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Adafruit_PCF8574.h>
#include <RotaryEncoder.h> // https://github.com/mathertel/RotaryEncoder
const byte direccion_placa1 = 0x25; // nombre en el programa placa_uno;
const byte direccion_placa2 = 0x21; // nombre en el programa placa_dos;
const byte direccion_placa3 = 0x28; // nombre en el programa placa_tres;
const byte direccion_placa4 = 0x29; // nombre en el programa placa_cuatro;
const byte direccion_lcd4li = 0x27; // pantalla lcd 4x20
#define MIGUEL
#define DEBUG 80
#define PANTALLA 1
/* en este apartado damos las indicaciones precisas de las placas y pines que tenemos */
const int numero_placas = 1; // numero de placas.SOLO MOVER ESTE NUMERO, LO DEMAS SE CALCULA
const int MaximoAgujas_placa = 4; // las agujas que podemos controlar 8 pines = 4 agujas.
const int Pines_PlacaPCF = 8; // pines que podemos manejar con la placa.
const int cantidad_de_agujas = numero_placas * MaximoAgujas_placa; //= numero_placas * 4 ; // numero de agujas que vamos a controlar.
const int total_pines = Pines_PlacaPCF * numero_placas; // = (cantidad_de_agujas * numero_placas) * 2; // pines por placa 8
// encoder
const int Encoder_clk = 3;
const int Encoder_dt = 2;
const int pinBotonEncoder = 4;
int estado_clk_old = 0;
int estado_btn_old = 0;
int rotaciones = 0; // las vueltas que vamos dando
const int RANGO_MINIMO_ENCODER = 0; // Valor mínimo del encoder (varía según el menú que se encuentre)
const int RANGO_MAXIMO_ENCODER = cantidad_de_agujas - 1; // Valor máximo del encoder (varía según el menú que se encuentre)
const bool estado_desvio_recto = 0;
const bool estado_desvio_curvo = 1;
const int UsoledSi = 0;
const int UsoledNo = 1;
const int fogonazo = 80; // tiempo que esta activa el motor de la aguja
// objetos
#ifdef MIGUEL
//LiquidCrystal_I2C lcd(0x3F, 16, 2);
LiquidCrystal_I2C lcd(direccion_lcd4li, 20, 4);
#endif
RotaryEncoder encoder(Encoder_clk, Encoder_dt, RotaryEncoder::LatchMode::FOUR3);
Adafruit_PCF8574 placa_uno;
Adafruit_PCF8574 placa_dos;
Adafruit_PCF8574 placa_tres;
Adafruit_PCF8574 placa_cuatro;
// pequeño struc solo para datos minimos
struct mantenimiento {
int struct_aguja; // el desvio afecto
int struct_placa; // numero de placa, por si nos sirve.
bool struct_estado; // segun este estado, iremos a ponerlo al reves.
int struct_recto; // el pin del recto
int struct_curvo; // el pin del curvo
};
// mantenimiento es la estructura principal, con esta instruccion creamos un array llamado bigdata y con dimensión fija.
mantenimiento bigdata[cantidad_de_agujas];
// Inicializamos la cantidad de pines necesarios para las agujas. Quedarán pines sin inicializar
// esto es mas complicado automatizarlo, porque no sabemos los nombres... hay que rellenarlo si
// utilizamos mas de cuatro placas
/*!
@brief Conseguimos mover el motor.
@param placa La placa PCF que selecionamos.
@param pin Pin que tenemos selecionado y debemos mover.
@param enciende si usamos led en lugar de agujas, nos sirve para que se queden encendidos. 1 seria para los desvios
@return No devuelve nada. ejecuta la accion.
*/
void pruebatodo(int placa, int pin, int enciende) {
int x = placa;
int _pin = pin;
switch (x) {
case 1:
placa_uno.digitalWrite(_pin, HIGH);
delay(fogonazo);
if (enciende == 1) {
placa_uno.digitalWrite(_pin, LOW);
}
break;
case 2:
placa_dos.digitalWrite(_pin, HIGH);
delay(fogonazo);
if (enciende == 1) {
placa_dos.digitalWrite(_pin, LOW);
}
break;
case 3:
placa_tres.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_tres.digitalWrite(_pin, LOW);
break;
case 4:
placa_cuatro.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_cuatro.digitalWrite(_pin, LOW);
break;
default:
break;
}
}
void checkPosition() {
encoder.tick();
}
int ENCODER() {
static int pos = 1;
int nAgujaMuestro;
encoder.tick();
rotaciones = encoder.getPosition();
if (rotaciones < RANGO_MINIMO_ENCODER) {
encoder.setPosition(RANGO_MINIMO_ENCODER);
rotaciones = RANGO_MINIMO_ENCODER;
} else if (rotaciones > RANGO_MAXIMO_ENCODER) { // (rotaciones >= RANGO_MAXIMO_ENCODER)
encoder.setPosition(RANGO_MAXIMO_ENCODER);
rotaciones = RANGO_MAXIMO_ENCODER;
}
if (pos != rotaciones) {
pos = rotaciones;
nAgujaMuestro = rotaciones;
#if DEBUG == 1
Serial.print(F("ROTACIONES -> "));
Serial.print(rotaciones);
Serial.print(F(" LO QUE ENVIO -> "));
Serial.println(nAgujaMuestro);
#endif
mosrtar_posicion_encoder(rotaciones);
mostrar_valor_posicion(bigdata[nAgujaMuestro].struct_estado);
}
}
// esto es una pasada, me he comido el coco un monton de horas, para automatizar todo
// el proceso, lo que parece sencillo ahora,
/*!
@brief Iniciar todas las variables que lleva cada desvio.
@return Sin devolucion de datos.
*/
void inicalizarbigdata() {
int _placaM = 1;
int _cuenta = 0;
int _contador = 0;
int _saltoplaca = 0;
// lcd.clear();
for (int i = 0; i < cantidad_de_agujas; i++) {
bigdata[i].struct_aguja = i; // podemos llamar a cada aguja por orden o por aguja->placa ?????
bigdata[i].struct_placa = _placaM;
bigdata[i].struct_recto = _contador;
_contador++;
bigdata[i].struct_estado = estado_desvio_recto;
bigdata[i].struct_curvo = _contador;
pruebatodo(_placaM, _contador, UsoledNo); // leer el enunciado para mas info.
if (i % 2 == 0) { // muevo la aguja para dejarlo en recto, o eso intento 0,1,2,3,4,5,6,7, 8,9,10,11,12,13,14,15
pruebatodo(_placaM, _contador, 1); // 0,1,02,03,04,05,06,07
}
_contador++;
_cuenta++;
_saltoplaca++;
if (_cuenta == 4) {
_cuenta = 0;
_placaM = _placaM + 1;
_saltoplaca = 0;
_contador = 0;
}
#if DEBUG == 22
Serial.print(bigdata[i].struct_aguja);
Serial.print(bigdata[i].struct_placa);
Serial.print(bigdata[i].struct_recto);
Serial.print(bigdata[i].struct_curvo);
Serial.println(bigdata[i].struct_estado);
#endif
lcd.setCursor(0, 4);
lcd.print("BIGDATA");
lcd.setCursor(1, 2);
lcd.print(bigdata[i].struct_aguja);
}
}
/*!
@brief Pone en pantalla y en humano el valor actual de la aguja selecionada
@param _valor de la posicion de la aguja.
@return Sin retorno de valores.
*/
void mostrar_valor_posicion(bool _valor) {
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 2);
if (_valor == true) lcd.print("CURVO ");
if (_valor == false) lcd.print("RECTO");
}
// esto hay que mejorarlo, es muy artesanal
/*!
@brief Activa las placas y comprueba que esten conectadas.
@return Muestra por lcd si falla alguna placa.
*/
void setupPlacas() {
if (numero_placas == 1) {
if (!placa_uno.begin(direccion_placa1, &Wire)) {
lcd.clear();
lcd.setCursor(0, 2);
lcd.print("SIN PLACA 1");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_uno.pinMode(p, OUTPUT);
placa_uno.digitalWrite(p, LOW);
}
}
}
if (numero_placas == 2) {
if (!placa_uno.begin(direccion_placa1, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 1");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_uno.pinMode(p, OUTPUT);
placa_uno.digitalWrite(p, LOW);
}
}
if (!placa_dos.begin(direccion_placa2, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 2");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_dos.pinMode(p, OUTPUT);
placa_dos.digitalWrite(p, LOW);
}
}
}
if (numero_placas == 3) {
if (!placa_uno.begin(direccion_placa1, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 1");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_uno.pinMode(p, OUTPUT);
placa_uno.digitalWrite(p, LOW);
}
}
if (!placa_dos.begin(direccion_placa2, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 2");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_dos.pinMode(p, OUTPUT);
placa_dos.digitalWrite(p, LOW);
}
}
if (!placa_tres.begin(direccion_placa3, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 3");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_tres.pinMode(p, OUTPUT);
placa_tres.digitalWrite(p, LOW);
}
}
}
if (numero_placas == 4) {
if (!placa_uno.begin(direccion_placa1, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 1");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_uno.pinMode(p, OUTPUT);
placa_uno.digitalWrite(p, LOW);
}
}
if (!placa_dos.begin(direccion_placa2, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 2");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_dos.pinMode(p, OUTPUT);
placa_dos.digitalWrite(p, LOW);
}
}
if (!placa_tres.begin(direccion_placa3, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 3");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_tres.pinMode(p, OUTPUT);
placa_tres.digitalWrite(p, LOW);
}
}
if (!placa_cuatro.begin(direccion_placa4, &Wire)) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("SIN PLACA 4");
delay(250);
} else {
for (int p = 0; p < 8; p++) {
placa_cuatro.pinMode(p, OUTPUT);
placa_cuatro.digitalWrite(p, LOW);
}
}
}
lcd.clear();
}
void _pantalla() {
for (int i = 0; i < cantidad_de_agujas; i++) {
Serial.print(F(" orden "));
Serial.print(i);
Serial.print(F(" aguja -> "));
Serial.print(bigdata[i].struct_aguja);
Serial.print(F(" placa -> "));
Serial.print(bigdata[i].struct_placa);
Serial.print(F(" estado -> "));
Serial.print(bigdata[i].struct_estado);
Serial.print(F(" pin recto "));
Serial.print(bigdata[i].struct_recto);
Serial.print(F(" pin curvo "));
Serial.print(bigdata[i].struct_curvo);
Serial.println(F(""));
}
}
void setup() {
Serial.begin(115200); // iniciamos el puerto serie.
lcd.init(); // iniciamos el lcd ( esto es un mundo)
lcd.clear();
lcd.backlight();
pinMode(Encoder_clk, INPUT_PULLUP); // pin del enoder
pinMode(Encoder_dt, INPUT_PULLUP); // pin del encoder
pinMode(pinBotonEncoder, INPUT_PULLUP); // boton del encoder
setupPlacas(); // setupPlacas(); REVISADO
inicalizarbigdata(); //REVISADO
_pantalla(); // REVISADO
Serial.println(__FILE__);
mostrar_menu();
encoder.setPosition(RANGO_MINIMO_ENCODER);
encoder.tick();
attachInterrupt(digitalPinToInterrupt(Encoder_dt), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(Encoder_clk), checkPosition, CHANGE);
}
/*!
@brief Activa por un tiempo la bobina para conseguir el cambio de aguja
@param _multiplexor La placa PCF que selecionamos.
@param _pin Pin que tenemos selecionado y debemos mover.
@return No devuelve nada. ejecuta la accion.
*/
void posicionAgujas(int _multiplexor, int _pin) {
#if DEBUG == 2
Serial.println(F(" "));
Serial.println(F("Entrando a Mover "));
Serial.print(F(" DESVIO A MOVER ---> "));
Serial.print(rotaciones);
Serial.print(F(" PLACA PCF ---> "));
Serial.print(_multiplexor);
Serial.print(F(" PIN A MOVER ---> "));
Serial.println(_pin);
#endif
switch (_multiplexor) {
case 1:
placa_uno.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_uno.digitalWrite(_pin, LOW);
break;
case 2:
placa_dos.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_dos.digitalWrite(_pin, LOW);
break;
case 3:
placa_tres.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_tres.digitalWrite(_pin, LOW);
break;
case 4:
placa_cuatro.digitalWrite(_pin, HIGH);
delay(fogonazo);
placa_cuatro.digitalWrite(_pin, LOW);
break;
default:
// statements
break;
}
}
void loop() {
ENCODER();
botonEncoder();
}
/*!
@brief Lee continuamente el boton. Al puslar desencadena las acciones asignadas.
@return Sin devolucion de nada, informa por pantalla y monitor serie.
*/
void botonEncoder() {
bool estado_btn = digitalRead(pinBotonEncoder);
bool estadoAguja;
int placa_afectada;
int pin_afectado;
int pin_NOafectado;
int nAgujaMover;
nAgujaMover = rotaciones;
if (estado_btn_old == HIGH && estado_btn == LOW) {
estadoAguja = bigdata[nAgujaMover].struct_estado; // solo es para cambiar el estado, no afecta para el cambio de aguja
placa_afectada = bigdata[nAgujaMover].struct_placa; // esto es importante.
if (estadoAguja == estado_desvio_curvo) { // devemos activar el pin curvo.
pin_afectado = bigdata[nAgujaMover].struct_curvo;
// pin_NOafectado = bigdata[nAgujaMover].struct_recto; // selecionamos el pin curvo
pruebatodo(placa_afectada, pin_afectado, UsoledNo);
// pruebatodo(placa_afectada, pin_NOafectado, UsoledNo); // funcion para mover la aguja/placa
} else { // debemos activar el pin recto, ya que esta en curvo.
pin_afectado = bigdata[nAgujaMover].struct_recto;
// pin_NOafectado = bigdata[nAgujaMover].struct_curvo; // selecionamos el pin recto
pruebatodo(placa_afectada, pin_afectado, UsoledNo);
// pruebatodo(placa_afectada, pin_NOafectado, UsoledNo);
}
bigdata[nAgujaMover].struct_estado = !estadoAguja; // actualizamos el estado.
mostrar_valor_posicion(bigdata[nAgujaMover].struct_estado);
#if DEBUG == 2
Serial.println(F(" BOTON ENCODER TERMINADO "));
Serial.print(F(" placa -> "));
Serial.print(placa_afectada);
Serial.print(F(" aguja -> "));
Serial.print(bigdata[nAgujaMover].struct_aguja);
Serial.print(F(" estado -> "));
Serial.print(bigdata[nAgujaMover].struct_estado);
Serial.print(F(" recto -> "));
Serial.print(bigdata[nAgujaMover].struct_recto);
Serial.print(F(" curvo -> "));
Serial.print(bigdata[nAgujaMover].struct_curvo);
Serial.print(F(" PIN AFECTADO -> "));
Serial.println(pin_afectado);
#endif
delay(60);
} // si se pulsa el boton.
estado_btn_old = estado_btn;
} // de la funcion
// Mostramos solo la parte que interesa del encoder, que sería el número de la posición
/*!
@brief Muestra por pantalla o monitor informacion sobre la aguja selecionada.
@param _posicion Numero de orden dentro del struc de la aguja
@return Sin datos a devolver
*/
void mosrtar_posicion_encoder(int _posicion) {
lcd.setCursor(0, 1);
lcd.print("Desvio -> ");
lcd.setCursor(10, 1);
int mostrarPosicion = _posicion; // +1
if (mostrarPosicion < 10) lcd.print(" "); // ponemos un espacio delante si es de un dígito
lcd.print(mostrarPosicion);
#if DEBUG == 2
Serial.print(F("Desvio -> "));
Serial.print(mostrarPosicion);
Serial.print(F(" Placa -> "));
Serial.print(bigdata[mostrarPosicion].struct_placa);
Serial.print(F(" Estado -> "));
Serial.println(bigdata[mostrarPosicion].struct_estado);
#endif
}
// Mostramos la primera línea del display, que será fija
void mostrar_menu() {
rotaciones = 0;
lcd.setCursor(0, 0);
lcd.print("Menu: FeTren");
lcd.setCursor(0, 1);
lcd.print("Desvio -> ");
//lcd.setCursor(10, 1);
lcd.print(rotaciones);
mostrar_valor_posicion(bigdata[rotaciones].struct_estado);
}