#include <LiquidCrystal.h>
#include "Bitcoin.h"
#define IN_LEFT 19
#define IN_RIGHT 18
#define IN_BACK 5
#define IN_OK 17
#define MENU_SEM 0
#define MENU_DIR 1
#define LISTA_PALABRAS 2
#define LISTA_DIRECC 3
#define EDICION_PALABRA 4
LiquidCrystal lcd(13, 12, 14, 27, 26, 25);
// Variable para alternar pantallas
int estado = 0;
bool letraParpadea = true;
String palabraEdicion = "";
int nPalabraActual = 0;
int nDireccionActual = 0;
String direccion = "";
int letraActual = 0;
char letraSeleccionada = ' ';
char alfabeto[] = " abcdefghijklmnopqrstuvwxyz"; // Incluye espacio en blanco
int alfabetoTam = sizeof(alfabeto) - 1;
unsigned long tiempo = 0;
HDPrivateKey hd;
HDPrivateKey account;
int nPantallaDir = 0;
// Lista de 24 palabras
String palabras[24] = {
"", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "",
"", "", "", ""
};
void setup() {
// Entrada botones
pinMode(IN_LEFT, INPUT_PULLUP);
pinMode(IN_RIGHT, INPUT_PULLUP);
pinMode(IN_BACK, INPUT_PULLUP);
pinMode(IN_OK, INPUT_PULLUP);
// Salida LCD
lcd.begin(16, 2);
Serial.begin(115200);
// Pantalla inicial
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Semilla");
}
void mostrarPalabra() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String(nPalabraActual + 1) + " - " + palabras[nPalabraActual]);
}
void mostrarLetra() {
//lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String(nPalabraActual + 1) + " - " + palabraEdicion);
if (!letraParpadea) {
lcd.print(letraSeleccionada);
} else {
lcd.print("_");
}
}
String construyeSemilla() {
// TODO: construir a partir del array de palabras
String semilla = "";
for (int i = 0; i < 24; i++) {
semilla = semilla + palabras[i];
if (i < 23) {
semilla = semilla + " ";
}
}
Serial.println(semilla);
return semilla;
}
void mostrarDireccion() {
// Mostrar DIR. N en la primera linea del LCD
// Mostrar una parte del texto de direccion.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Dir. " + String(nDireccionActual) + " " + String(nPantallaDir+1) + "/3");
lcd.setCursor(0, 1);
lcd.print(direccion.substring(nPantallaDir*16,(nPantallaDir+1)*16));
}
void calculaXpub() {
String semilla = construyeSemilla();
hd = HDPrivateKey(semilla, "");
if(!hd){ // check if it is valid
Serial.println("Invalid xpub");
return;
}
Serial.println("Root private key:");
Serial.println(hd);
Serial.println("bip84 master private key:");
account = hd.derive("m/84'/0'/0'/");
Serial.println(account);
Serial.println("bip84 master public key:");
Serial.println(account.xpub());
direccion = account.derive("m/0/0/").address();
Serial.println("first address:");
Serial.println(direccion);
estado = LISTA_DIRECC;
nDireccionActual = 0;
nPantallaDir = 0;
mostrarDireccion();
}
void loop() {
if (estado == MENU_SEM) {
if (digitalRead(IN_RIGHT) == LOW) {
estado = MENU_DIR;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Direcciones");
delay(200); // Anti-rebote
}
// Entrar en funcionalidad de palabras
if (digitalRead(IN_OK) == LOW) {
estado = LISTA_PALABRAS;
nPalabraActual = 0; // Reiniciar a la primera palabra
mostrarPalabra();
delay(200); // Anti-rebote
}
} else if (estado == MENU_DIR) {
if (digitalRead(IN_LEFT) == LOW) {
estado = MENU_SEM;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Semilla");
delay(200); // Anti-rebote
}
// Entrar en funcionalidad de direcciones
if (digitalRead(IN_OK) == LOW) {
calculaXpub();
delay(200); // Anti-rebote
}
} else if (estado == LISTA_DIRECC) {
// Navegar en la lista de direcciones
if ((nDireccionActual > 0 || nPantallaDir > 0) && (digitalRead(IN_LEFT) == LOW)) {
nPantallaDir = (nPantallaDir - 1 + 3) % 3;
if (nPantallaDir == 2) {
nDireccionActual--;
direccion = account.derive("m/0/" + String(nDireccionActual) + "/").address();
Serial.println(direccion);
}
mostrarDireccion();
delay(200); // Anti-rebote
} else if (digitalRead(IN_RIGHT) == LOW) {
nPantallaDir = (nPantallaDir + 1) % 3;
if (nPantallaDir == 0) {
nDireccionActual++;
direccion = account.derive("m/0/" + String(nDireccionActual) + "/").address();
Serial.println(direccion);
}
mostrarDireccion();
delay(200); // Anti-rebote
}
// Salir de la lista de direcciones
if (digitalRead(IN_BACK) == LOW) {
estado = MENU_DIR;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Direcciones");
delay(200); // Anti-rebote
}
} else if (estado == LISTA_PALABRAS) {
// Navegar en la lista de palabras
if (digitalRead(IN_LEFT) == LOW) {
nPalabraActual = (nPalabraActual - 1 + 24) % 24; // Ir hacia atrás con bucle
mostrarPalabra();
delay(200); // Anti-rebote
} else if (digitalRead(IN_RIGHT) == LOW) {
nPalabraActual = (nPalabraActual + 1) % 24; // Ir hacia adelante con bucle
mostrarPalabra();
delay(200); // Anti-rebote
}
// Entrar en edición
if (digitalRead(IN_OK) == LOW) {
estado = EDICION_PALABRA;
letraActual = 0;
letraSeleccionada = ' ';
letraParpadea = false;
tiempo = millis();
palabraEdicion = "";
lcd.clear();
mostrarLetra();
delay(200); // Anti-rebote
}
// Salir de la lista de palabras
if (digitalRead(IN_BACK) == LOW) {
estado = MENU_SEM;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Semilla");
delay(200); // Anti-rebote
}
} else if (estado == EDICION_PALABRA) {
// Cambiar letra seleccionada
if (digitalRead(IN_LEFT) == LOW) {
letraActual = (letraActual - 1 + alfabetoTam) % alfabetoTam; // Bucle hacia atrás
letraSeleccionada = alfabeto[letraActual];
letraParpadea = false;
tiempo = millis();
mostrarLetra();
delay(200); // Anti-rebote
} else if (digitalRead(IN_RIGHT) == LOW) {
letraActual = (letraActual + 1) % alfabetoTam; // Bucle hacia adelante
letraSeleccionada = alfabeto[letraActual];
letraParpadea = false;
tiempo = millis();
mostrarLetra();
delay(200); // Anti-rebote
}
// Confirmar letra
if (digitalRead(IN_OK) == LOW) {
if (letraActual == 0) {
palabras[nPalabraActual] = palabraEdicion;
estado = LISTA_PALABRAS;
mostrarPalabra();
delay(200); // Anti-rebote
return;
} else {
palabraEdicion += letraSeleccionada;
letraActual = 0;
letraSeleccionada = ' ';
letraParpadea = false;
tiempo = millis();
mostrarLetra();
delay(200); // Anti-rebote
}
}
// Cancelar edición
if (digitalRead(IN_BACK) == LOW) {
estado = LISTA_PALABRAS;
mostrarPalabra();
delay(200); // Anti-rebote
return;
}
// Hacer parpadear la letra
if (millis() - tiempo >= 500) {
letraParpadea = !letraParpadea;
tiempo = millis();
}
mostrarLetra();
}
}