Pengukur Infrared — Arduino Nano simulation

An interactive Arduino Nano circuit simulation you can run free in your browser on Velxio, by learnbm11.

Components

Sketch code

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

#define TRIG_PIN     6
#define ECHO_PIN     7
#define ROTARY_CLK   2
#define ROTARY_DT    3
#define ROTARY_SW    10
#define START_BUTTON 4
#define MENU_BUTTON  5
#define LED_PIN      8
#define BUZZER_PIN   9
#define LCD_ADDRESS  0x27

#define EEPROM_SIG_ADDR  0
#define EEPROM_TIME_ADDR 1
#define EEPROM_SIG_VAL   0xA7

#define SENSOR_N  5
#define EMA_ALPHA 0.2f

#define LP(r,t) lcd.setCursor(0,r); lcd.print(t)

LiquidCrystal_I2C lcd(LCD_ADDRESS, 16, 2);
int lastClk = HIGH;

enum SystemState {
  STATE_WELCOME, STATE_IDLE,
  STATE_SET_JAM, STATE_SET_MENIT, STATE_SET_DETIK,
  STATE_CONFIRM, STATE_CONFIRM_STOP, STATE_SAVING, STATE_STARTING,
  STATE_RUNNING, STATE_STOPPING, STATE_ALARM, STATE_DONE
};
SystemState currentState = STATE_WELCOME;

int jamSetting = 0, menitSetting = 5, detikSetting = 0;
unsigned long totalSeconds = 300UL, waktuMulai = 0;

uint8_t lastStateD4 = HIGH, lastStateD5 = HIGH, lastStateSW = HIGH;
unsigned long lastTimeD4 = 0, lastTimeD5 = 0, lastTimeSW = 0;

float   sensorBuf[SENSOR_N] = {0};
uint8_t sensorIdx = 0;
bool    sensorFull = false;
float   jarakCm = 0;
unsigned long lastSensorRead = 0, lastLCDRefresh = 0;
const unsigned long sensorInterval = 200, lcdRefreshInterval = 150;

unsigned long stateUntil = 0, note2At = 0;
bool note2Done = false;

uint8_t       alarmStep      = 0;
unsigned long alarmStepUntil = 0;
struct { uint16_t freq, dur, pause; }
const ALARM[] = {{1000,150,200},{1500,150,200},{2000,250,300}};

void nadaKlik() { tone(BUZZER_PIN, 1800,  25); }
void nadaTick() { tone(BUZZER_PIN, 2400,  10); }

void loadEEPROM() {
  if (EEPROM.read(EEPROM_SIG_ADDR) != EEPROM_SIG_VAL) return;
  unsigned long saved;
  EEPROM.get(EEPROM_TIME_ADDR, saved);
  if (saved >= 60 && saved <= 359999UL) totalSeconds = saved;
}

void saveEEPROM() {
  EEPROM.update(EEPROM_SIG_ADDR, EEPROM_SIG_VAL);
  EEPROM.put(EEPROM_TIME_ADDR, totalSeconds);
}

void printBaris(uint8_t row, const char* text) {
  lcd.setCursor(0, row); lcd.print(F("                "));
  lcd.setCursor(0, row); lcd.print(text);
}
void printLCD(const char* b0, const char* b1) { printBaris(0, b0); printBaris(1, b1); }

void formatJarak(char* buf) {
  if (jarakCm > 0 && jarakCm < 200) snprintf(buf, 17, "JARAK: %d cm", (int)jarakCm);
  else snprintf(buf, 17, "JARAK: --- cm  ");
}

void formatWaktu(char* buf, unsigned long d, const char* label) {
  snprintf(buf, 17, "%s %02d:%02d:%02d", label,
    (uint16_t)(d/3600), (uint16_t)((d%3600)/60), (uint16_t)(d%60));
}

bool cekKlikInstan(uint8_t pin, uint8_t &lastState, unsigned long &lastTime) {
  uint8_t cur = digitalRead(pin);
  unsigned long now = millis();
  bool klik = (lastState == HIGH && cur == LOW && now - lastTime > 50);
  if (klik) lastTime = now;
  lastState = cur;
  return klik;
}

int bacaRotary() {
  int clk = digitalRead(ROTARY_CLK);
  int delta = (clk != lastClk && clk == HIGH) ? (digitalRead(ROTARY_DT) == LOW ? 1 : -1) : 0;
  lastClk = clk;
  return