Community project · Arduino Uno R4 WiFi

First blink

A circuit on the Arduino Uno R4 WiFi by Lucero Celis V. Sistemas USAT (@lucero-celis-v-sistemas), shared on Mokxi. Open it and press Run: it runs in your browser, free.

Runs in your browser. Free · 4 remixes · 1 heart.

First blink: the circuit as it is drawn in the Mokxi editor.

The code

The sketch exactly as it was saved. Open it in the editor and press Run: it compiles in your browser.

sketch.ino · unor4wifi1
#include <Wire.h>
#include <EEPROM.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>
#include <stdlib.h>

#define DHTTYPE DHT22
const bool MODO_DEMO = false;
const byte SENSOR=2, BOTON=3, VERDE=5;
const byte AMARILLO=6, ROJO=7, BUZZER=8;
DHT dht(SENSOR, DHTTYPE);
LiquidCrystal_I2C lcd(0x27,16,2);

float cache[5];
uint16_t cacheId[5]={0};
byte cacheN=0, cacheNext=0;
float ramT[20];
uint16_t ramId[20]={0};
byte ramN=0, ramNext=0;
float temperaturaActual=0, temperaturaMaxima=0;
uint32_t contadorAlarmas=0, hits=0, misses=0;
bool hayTemperatura=false, sensorOK=false;
bool alarmaAnterior=false;
uint16_t siguienteId=1, solicitado=1;
byte histNext=0, histN=0;
float limiteVerde=25, limiteAlarma=30;
unsigned long ultimaLectura=0, consultaInicio=0;
bool consultaVisible=false;
char linea[24]; byte lineaN=0;
bool lineaDesbordada=false;

void pantalla(const char *a, const char *b) {
  lcd.clear(); lcd.setCursor(0,0); lcd.print(a);
  lcd.setCursor(0,1); lcd.print(b);
}
void guardarControl() {
  EEPROM.put(20,contadorAlarmas);
  EEPROM.update(24,histNext);
  EEPROM.update(25,histN);
  EEPROM.put(26,siguienteId);
}
void prepararMemoria() {
  uint16_t marca=0; EEPROM.get(0,marca);
  if(marca!=0x4E52) {
    for(int i=0;i<100;i++) EEPROM.update(i,0);
    int16_t lv=250, la=300;
    EEPROM.put(2,lv); EEPROM.put(4,la);
    guardarControl();
    marca=0x4E52; EEPROM.put(0,marca);
  }
  int16_t lv,la;
  EEPROM.get(2,lv); EEPROM.get(4,la);


  // Mantener exactamente los limites de la tabla.
  if(lv!=250 || la!=300) {
    lv=250; la=300;
    EEPROM.put(2,lv); EEPROM.put(4,la);
  }
  limiteVerde=lv/10.0f; limiteAlarma=la/10.0f;
  EEPROM.get(20,contadorAlarmas);
  histNext=EEPROM.read(24); histN=EEPROM.read(25);
  EEPROM.get(26,siguienteId);
  if(histNext>9 || histN>10 || siguienteId==0) {
    pantalla("Error memoria","Revisar datos");
    Serial.println("Metadatos invalidos; revisar EEPROM.");
    while(true) delay(100);
  }
}
void salidas(float t, bool valido) {
  digitalWrite(VERDE,valido && t<limiteVerde);
  digitalWrite(AMARILLO,valido && t>=limiteVerde
               && t<limiteAlarma);
  digitalWrite(ROJO,valido && t>=limiteAlarma);
  if(valido && t>=limiteAlarma) tone(BUZZER,2000);
  else noTone(BUZZER);
}
void mostrarEstado() {
  lcd.clear(); lcd.setCursor(0,0);
  if(!sensorOK) {
    lcd.print(hayTemperatura ? "Error sensor" : "Sin lectura");
    lcd.setCursor(0,1); lcd.print("Revisar sensor");
    return;
  }
  lcd.print("T:"); lcd.print(temperaturaActual,1);
  lcd.print(" C"); lcd.setCursor(0,1);
  if(temperaturaActual<limiteVerde) lcd.print("NORMAL");
  else if(temperaturaActual<limiteAlarma) lcd.print("ADVERTENCIA");
  else lcd.print("ALARMA");
}
void mostrarCache() {
  Serial.print("CACHE IDs antiguos a recientes: ");
  for(byte j=0;j<cacheN;j++) {
    byte k=(cacheNext+5-cacheN+j)%5;
    Serial.print(cacheId[k]); Serial.print(' ');
  }
  Serial.println();
}
void guardarCritica(uint16_t id,float t) {
  int a=50+5*histNext;
  int16_t decimas=(int16_t)lroundf(t*10.0f);
  EEPROM.update(a,0); // Invalidar antes de escribir.
  EEPROM.put(a+1,id); EEPROM.put(a+3,decimas);
  EEPROM.update(a,165); // Registro completo.
  histNext=(histNext+1)%10;
  if(histN<10) histN++;
  guardarControl();
  Serial.print("EEPROM ID="); Serial.print(id);
  Serial.print(" T="); Serial.print(decimas/10.0f,1);
  Serial.print(" direccion="); Serial.print(a);
  Serial.print(" pagina="); Serial.println(a/10);
}
void procesar(float t) {
  if(!isfinite(t) || t< -40 || t>80) {
    sensorOK=false; salidas(0,false);
    if(!consultaVisible) pantalla("Error sensor","Sin dato valido");
    Serial.println("Lectura invalida; memoria sin cambios.");
    return;
  }
  if(siguienteId>=65535) {
    sensorOK=false; salidas(0,false);
    pantalla("Limite de IDs","Revisar memoria");
    return;
  }
  uint16_t id=siguienteId++;
  // Evitar repetir identificadores despues de reiniciar.
  EEPROM.put(26,siguienteId);
  temperaturaActual=t;
  if(!hayTemperatura || t>temperaturaMaxima)
    temperaturaMaxima=t;
  hayTemperatura=true; sensorOK=true;
  cache[cacheNext]=t; cacheId[cacheNext]=id;
  cacheNext=(cacheNext+1)%5;
  if(cacheN<5) cacheN++;
  ramT[ramNext]=t; ramId[ramNext]=id;
  ramNext=(ramNext+1)%20;
  if(ramN<20) ramN++;
  bool alarmaAhora=t>=limiteAlarma;
  if(alarmaAhora && !alarmaAnterior) {
    contadorAlarmas++;
    EEPROM.put(20,contadorAlarmas);
  }
  alarmaAnterior=alarmaAhora;
  salidas(t,true);
  if(t>limiteAlarma) guardarCritica(id,t);
  Serial.print("LECTURA ID="); Serial.print(id);
  Serial.print(" T="); Serial.print(t,1);
  Serial.print(" MAX="); Serial.print(temperaturaMaxima,1);
  Serial.print(" ALARMAS="); Serial.println(contadorAlarmas);
  mostrarCache();
  if(!consultaVisible) mostrarEstado();
}
bool buscarEEPROM(uint16_t id,float &t) {
  for(byte j=0;j<histN;j++) {
    byte k=(histNext+10-1-j)%10;
    int a=50+5*k;
    if(EEPROM.read(a)!=165) continue;
    uint16_t leido; int16_t decimas;
    EEPROM.get(a+1,leido);
    if(leido==id) {
      EEPROM.get(a+3,decimas); t=decimas/10.0f;
      return true;
    }
  }
  return false;
}
void consultar() {
  float t=0; bool encontrado=false, hit=false;
  const char *origen="NO DISPONIBLE";
  for(byte i=0;i<cacheN;i++) {
    if(cacheId[i]==solicitado) {
      t=cache[i]; encontrado=true; hit=true;
      origen="CACHE"; break;
    }
  }
  if(hit) hits++;
  else {
    misses++;
    for(byte i=0;i<ramN;i++) {
      if(ramId[i]==solicitado) {
        t=ramT[i]; encontrado=true; origen="SRAM"; break;
      }
    }
    if(!encontrado && buscarEEPROM(solicitado,t)) {
      encontrado=true; origen="EEPROM";
    }
  }
  lcd.clear(); lcd.setCursor(0,0);
  lcd.print(hit ? "HIT ID:" : "MISS ID:");
  lcd.print(solicitado); lcd.setCursor(0,1);
  if(encontrado) {
    lcd.print(t,1); lcd.print("C "); lcd.print(origen);
  } else lcd.print("No disponible");
  Serial.print("CONSULTA ID="); Serial.print(solicitado);
  Serial.print(hit ? " HIT " : " MISS ");
  Serial.print(origen);
  if(encontrado) { Serial.print(" T="); Serial.print(t,1); }
  Serial.print(" H="); Serial.print(hits);
  Serial.print(" M="); Serial.println(misses);
  consultaVisible=true; consultaInicio=millis();
}
void comando() {
  linea[lineaN]='\0';
  char *fin;
  if(MODO_DEMO && linea[0]=='T') {
    float t=strtof(linea+1,&fin);
    if(fin!=linea+1 && *fin=='\0') procesar(t);
    else Serial.println("Temperatura no valida.");
  } else {
    long id=strtol(linea,&fin,10);
    if(fin!=linea && *fin=='\0' && id>0 && id<65535) {
      solicitado=(uint16_t)id;
      Serial.print("Seleccionado ID="); Serial.println(solicitado);
    } else Serial.println("Enviar ID valido o Tvalor en DEMO.");
  }
}


void leerSerial() {
  while(Serial.available()) {
    char c=Serial.read();
    if(c=='\r') continue;
    if(c=='\n') {
      if(!lineaDesbordada && lineaN>0) comando();
      else if(lineaDesbordada) Serial.println("Comando muy largo.");
      lineaN=0; lineaDesbordada=false;
    } else if(lineaN<sizeof(linea)-1) linea[lineaN++]=c;
    else lineaDesbordada=true;
  }
}
void atenderBoton() {
  static bool anterior=HIGH, estable=HIGH;
  static unsigned long cambio=0;
  bool leido=digitalRead(BOTON);
  if(leido!=anterior) { cambio=millis(); anterior=leido; }
  if(millis()-cambio>=40 && leido!=estable) {
    estable=leido;
    if(estable==LOW) consultar();
  }
}
void setup() {
  Serial.begin(115200);
  pinMode(BOTON,INPUT_PULLUP);
  pinMode(VERDE,OUTPUT); pinMode(AMARILLO,OUTPUT);
  pinMode(ROJO,OUTPUT); pinMode(BUZZER,OUTPUT);
  lcd.init(); lcd.backlight(); dht.begin();
  if(EEPROM.length()<100) {
    pantalla("Sin memoria","EEPROM < 100");
    while(true) delay(100);
  }
  prepararMemoria(); salidas(0,false);
  pantalla("Monitor listo",MODO_DEMO ? "DEMO: Tvalor" : "Esperar sensor");
  Serial.print("Contador recuperado="); Serial.println(contadorAlarmas);
  Serial.println("Seleccionar ID y pulsar boton para consultar.");
}
void loop() {
  leerSerial();
  if(!MODO_DEMO && millis()-ultimaLectura>=2000) {
    ultimaLectura=millis(); procesar(dht.readTemperature());
  }
  atenderBoton();
  if(consultaVisible && millis()-consultaInicio>=3000) {
    consultaVisible=false; mostrarEstado();
  }
}


sketch.ino
// Mokxi.
// This circuit has no board, so Run simulates the parts only.
// Add a board and pick a program in its Firmware panel: the editor then shows
// that program's source, and Run builds what you type here for that board.

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}

Parts list

16 parts, plus the jumper wires. Every one is in the editor's parts bin.