// ============================================================ // ATtiny412 Warning Node // Address: 0x16 // // Receives commands from ESP32-C6 Teacher over I2C: // 0x01 → start warning (sent at XKT_ON_TIME - ALARM_TIME) // 0x00 → stop warning (sent on sequence complete or reverse) // // On warning start: // - Passive buzzer on PA7 sweeps FREQ_LOW → FREQ_HIGH over ALARM_TIME // - XKT001X transmitter on PA6 (via switch board) pulses in sync // - Hall sensor on PA3 silences buzzer while triggered // (transmitter continues pulsing regardless) // - Auto-stops after ALARM_TIME if no stop command received // // Timer note: // tone() uses the ATtiny412 TCB timer for PA7 // digitalWrite() used for PA6 to avoid timer conflict // // TUNE: change only ALARM_TIME to adjust warning length // ============================================================
include ¶
// ──────────────────────────────────────────────────────────── // I2C ADDRESS // ────────────────────────────────────────────────────────────
define MY_ADDRESS 0x16¶
// ──────────────────────────────────────────────────────────── // PIN ASSIGNMENTS // ────────────────────────────────────────────────────────────
define SENSOR_PIN PIN_PA3 // Hall sensor analog input¶
define TRANSMITTER_PIN PIN_PA6 // XKT001X via switch board — digitalWrite()¶
define BUZZER_PIN PIN_PA7 // passive buzzer — tone()¶
// ──────────────────────────────────────────────────────────── // HALL SENSOR // ────────────────────────────────────────────────────────────
define DEVIATION 20 // ADC counts from baseline to trigger — tune if needed¶
// ──────────────────────────────────────────────────────────── // WARNING PARAMETERS // ← ALARM_TIME is the only value you need to change // ────────────────────────────────────────────────────────────
define ALARM_TIME 30000 // ms — total warning duration¶
define STEPS (ALARM_TIME / 500) // one step per 500ms¶
define FREQ_LOW 800 // Hz at start of warning¶
define FREQ_HIGH 1600 // Hz at end of warning¶
define TONE_DURATION 200 // ms buzzer sounds per step¶
define ON_TIME 300 // ms transmitter on per step¶
define OFF_TIME (500 - ON_TIME) // ms transmitter off per step¶
// ──────────────────────────────────────────────────────────── // STATE // ──────────────────────────────────────────────────────────── volatile bool startWarning = false; // set by I2C interrupt volatile bool stopWarning = false; // set by I2C interrupt bool warningActive = false; int baseline = 0;
// ============================================================ // SETUP // ============================================================ void setup() { Wire.begin(MY_ADDRESS); Wire.onReceive(receiveCommand);
pinMode(SENSOR_PIN, INPUT); pinMode(BUZZER_PIN, OUTPUT); pinMode(TRANSMITTER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW); digitalWrite(TRANSMITTER_PIN, LOW); // transmitter OFF at boot
// ── baseline calibration ───────────────────────────────── long sum = 0; for (int i = 0; i < 64; i++) { sum += analogRead(SENSOR_PIN); delay(5); } baseline = sum / 64; }
// ============================================================ // MAIN LOOP // ============================================================ void loop() { if (startWarning && !warningActive) { startWarning = false; stopWarning = false; warningActive = true; runWarning(); warningActive = false; } }
// ============================================================ // WARNING SEQUENCE // Runs for ALARM_TIME ms or until stopWarning flag is set. // Each step is 500ms: ON_TIME transmitter on + OFF_TIME off. // Tone frequency rises from FREQ_LOW to FREQ_HIGH over all steps. // Hall sensor silences buzzer only — transmitter always pulses. // ============================================================ void runWarning() { for (int i = 0; i < STEPS; i++) {
// ── check for stop command ────────────────────────────
if (stopWarning) break;
// ── read hall sensor ──────────────────────────────────
int reading = analogRead(SENSOR_PIN);
bool muted = abs(reading - baseline) > DEVIATION;
// ── rising tone frequency mapped to step ──────────────
int freq = map(i, 0, STEPS, FREQ_LOW, FREQ_HIGH);
// ── buzzer chirp — silenced if magnet present ─────────
if (!muted) {
tone(BUZZER_PIN, freq, TONE_DURATION);
} else {
noTone(BUZZER_PIN);
}
// ── transmitter pulse ON — always ─────────────────────
digitalWrite(TRANSMITTER_PIN, HIGH);
delay(ON_TIME);
// ── check stop mid-pulse ──────────────────────────────
if (stopWarning) break;
// ── transmitter pulse OFF — always ────────────────────
digitalWrite(TRANSMITTER_PIN, LOW);
delay(OFF_TIME);
}
// ── clean up ───────────────────────────────────────────── noTone(BUZZER_PIN); digitalWrite(TRANSMITTER_PIN, LOW); stopWarning = false; startWarning = false; }
// ============================================================ // I2C CALLBACK // 0x01 → start warning // 0x00 → stop warning // ============================================================ void receiveCommand(int bytes) { while (Wire.available()) { byte cmd = Wire.read(); if (cmd == 0x01) { startWarning = true; stopWarning = false; } if (cmd == 0x00) { stopWarning = true; startWarning = false; } } }