#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "agc_mem.h"
#define OLED_W 128
#define OLED_H 64
#define OLED_RESET -1
Adafruit_SSD1306 display(OLED_W, OLED_H, &Wire, OLED_RESET);
const int PIN_CAMPO = 25;
const int PIN_MAS = 26;
const int PIN_MENOS = 27;
const int PIN_ENTR = 32;
int verb = 1;
int noun = 1;
int campo = 0;
bool anterior[4] = {false, false, false, false};
bool lecturaAnterior[4] = {false, false, false, false};
unsigned long cambio[4] = {0, 0, 0, 0};
const unsigned long ANTIRREBOTE = 30;
unsigned long ultimaActualizacion = 0;
unsigned long ultimoPrint = 0;
// ---------------- ANCLA DE SESION ----------------
void imprimirAncla() {
uint64_t mac = ESP.getEfuseMac();
uint32_t sal = esp_random() % 100000;
Serial.printf(
"ANCLA %06llX-%s-%s-%05lu\n",
(unsigned long long)(mac & 0xFFFFFF),
__DATE__,
__TIME__,
(unsigned long)sal
);
}
// ---------------- ESCANER I2C ----------------
void escanearI2C() {
Serial.println("Escaner I2C:");
for (uint8_t addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.printf("Dispositivo: 0x%02X\n", addr);
}
}
Serial.println("Fin del escaneo.");
}
// ---------------- REGISTROS DE E/S ----------------
void actualizarIO() {
IOREG[IO_BOTONES] =
(!digitalRead(25)) |
((!digitalRead(26)) << 1) |
((!digitalRead(27)) << 2) |
((!digitalRead(32)) << 3);
}
// ---------------- DETECCION DE PULSACION ----------------
bool pulsadoUnaVez(int indice, int mascara) {
bool lectura = (IOREG[IO_BOTONES] & mascara) != 0;
unsigned long ahora = millis();
if (lectura != lecturaAnterior[indice]) {
lecturaAnterior[indice] = lectura;
cambio[indice] = ahora;
}
if (ahora - cambio[indice] >= ANTIRREBOTE) {
if (lectura != anterior[indice]) {
anterior[indice] = lectura;
if (lectura) {
return true;
}
}
}
return false;
}
// ---------------- MOSTRAR REGISTROS ----------------
void mostrarPalabra(int fila, palabra_t valor) {
char texto[22];
if (verb == 1) {
snprintf(
texto,
sizeof(texto),
"R%d %06o",
fila + 1,
(unsigned int)(uint16_t)valor
);
} else {
snprintf(
texto,
sizeof(texto),
"R%d %+06d",
fila + 1,
(int)valor
);
}
display.setCursor(0, 16 + fila * 12);
display.println(texto);
}
// ---------------- PANTALLA DSKY ----------------
void ejecutarDSKY() {
palabra_t r1 = 0;
palabra_t r2 = 0;
palabra_t r3 = 0;
if (noun == 1) {
r1 = leerMem((uint16_t)RAM[0]);
r2 = leerMem((uint16_t)RAM[1]);
r3 = leerMem((uint16_t)RAM[2]);
}
else if (noun == 60) {
r1 = IOREG[IO_BOTONES];
r2 = IOREG[IO_JOY_SW];
r3 = IOREG[IO_TICKS];
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.printf("PROG 00 V %02d N %02d", verb, noun);
mostrarPalabra(0, r1);
mostrarPalabra(1, r2);
mostrarPalabra(2, r3);
display.setCursor(0, 54);
display.printf("CAMPO:%d", campo);
if (alarmaMem) {
display.setCursor(72, 54);
display.print("MEM ERR");
}
display.display();
}
// ---------------- CONFIGURAR PINES ----------------
void configurarPines() {
pinMode(25, INPUT_PULLUP);
pinMode(26, INPUT_PULLUP);
pinMode(27, INPUT_PULLUP);
pinMode(32, INPUT_PULLUP);
}
// ---------------- INICIO ----------------
void setup() {
Serial.begin(115200);
configurarPines();
Wire.begin(21, 22);
imprimirAncla();
escanearI2C();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("No se encontro OLED en 0x3C");
while (true) {
delay(100);
}
}
// Direcciones de las primeras palabras de ROM.
RAM[0] = ROM_INICIO;
RAM[1] = ROM_INICIO + 1;
RAM[2] = ROM_INICIO + 2;
Serial.printf("RAM fisica: %p\n", (void*)RAM);
Serial.printf("ROM fisica: %p\n", (const void*)ROM);
alarmaMem = false;
palabra_t original = ROM[0];
escribirMem(ROM_INICIO, 5);
Serial.printf("ROM original: %d\n", (int)original);
Serial.printf("ROM despues: %d\n", (int)ROM[0]);
Serial.printf("Alarma memoria: %s\n",
alarmaMem ? "ACTIVA" : "INACTIVA");
IOREG[IO_JOY_SW] = 1;
Serial.println("DSKY listo.");
Serial.println("Prueba los cuatro botones.");
ejecutarDSKY();
}
// ---------------- BUCLE PRINCIPAL ----------------
void loop() {
if (millis() - ultimaActualizacion < 20) {
return;
}
ultimaActualizacion = millis();
actualizarIO();
// Mostrar el estado de botones cada 500 ms.
if (millis() - ultimoPrint >= 500) {
ultimoPrint = millis();
Serial.printf(
"BOTONES = %04X\n",
(unsigned int)IOREG[IO_BOTONES]
);
}
// CAMPO: selecciona VERB, NOUN o los registros.
if (pulsadoUnaVez(0, 1 << 0)) {
campo = (campo + 1) % 4;
Serial.printf("Campo seleccionado: %d\n", campo);
}
// MAS: aumenta el valor seleccionado.
if (pulsadoUnaVez(1, 1 << 1)) {
if (campo == 0) {
verb = (verb + 1) % 100;
}
else if (campo == 1) {
noun = (noun + 1) % 100;
}
else if (campo == 2) {
RAM[0]++;
}
else if (campo == 3) {
RAM[1]++;
}
Serial.printf("VERB=%02d NOUN=%02d\n", verb, noun);
}
// MENOS: disminuye el valor seleccionado.
if (pulsadoUnaVez(2, 1 << 2)) {
if (campo == 0) {
verb = (verb + 99) % 100;
}
else if (campo == 1) {
noun = (noun + 99) % 100;
}
else if (campo == 2) {
RAM[0]--;
}
else if (campo == 3) {
RAM[1]--;
}
Serial.printf("VERB=%02d NOUN=%02d\n", verb, noun);
}
// ENTR: ejecuta el verbo y sustantivo.
if (pulsadoUnaVez(3, 1 << 3)) {
Serial.printf(
"ENTR: VERB=%02d NOUN=%02d BOTONES=%04X\n",
verb,
noun,
(unsigned int)IOREG[IO_BOTONES]
);
ejecutarDSKY();
}
// Actualiza los registros visibles.
ejecutarDSKY();
}