/* =========================================================
BMS Thermal Monitoring – Noise-Resistant Alarm Demo
Platform : Wokwi (Arduino-style) – Nucleo C031C6
Sensor : Slide Potentiometer on A0
Alert : LED on D8 (blinks on alarm)
UART : Serial Monitor @115200
========================================================= */
// ---------------- PIN DEFINITIONS ----------------
const int ADC_PIN = A0; // PA0 – ADC input
const int LED_PIN = 8; // External LED on D8
// ---------------- CONFIGURATION ----------------
const int SAMPLE_RATE_MS = 100; // Sampling every 100 ms
const int WINDOW_SIZE = 20; // *** SAMPLE SIZE = 20 ***
const int VALID_COUNT = 2; // Consecutive filtered samples to trigger alarm
const float TEMP_THRESHOLD = 70.0; // °C
const float HYSTERESIS = 5.0; // °C
const float TEMP_MAX = 150.0; // Max mapped temperature
// ---------------- RUNTIME VARIABLES ----------------
float windowArr[WINDOW_SIZE];
int widx = 0;
int validCounter = 0;
bool alarmState = false;
int ADC_MAX = 4095;
// LED blinking control
unsigned long lastBlinkTime = 0;
bool ledState = false;
const unsigned long BLINK_INTERVAL = 300; // ms
// ---------------- HELPER FUNCTIONS ----------------
// Convert ADC value to temperature
float adcToTemp(int raw) {
return ((float)raw / (float)ADC_MAX) * TEMP_MAX;
}
// Generic median filter (works for even window sizes like 20)
float medianN(const float *v, int n) {
float t[n];
// Copy values
for (int i = 0; i < n; i++) {
t[i] = v[i];
}
// Insertion sort
for (int i = 1; i < n; i++) {
float key = t[i];
int j = i - 1;
while (j >= 0 && t[j] > key) {
t[j + 1] = t[j];
j--;
}
t[j + 1] = key;
}
// Even window → average of middle two
return (t[n/2 - 1] + t[n/2]) / 2.0;
}
// ---------------- SETUP ----------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
delay(200);
// Detect ADC resolution
int sample = analogRead(ADC_PIN);
ADC_MAX = (sample <= 1023) ? 1023 : 4095;
Serial.println("BMS Thermal Monitoring Started");
Serial.print("ADC Resolution: ");
Serial.println(ADC_MAX);
Serial.println("RAW_ADC RAW_TEMP FILT_TEMP COUNT STATUS");
// Initialize window to avoid startup spikes
for (int i = 0; i < WINDOW_SIZE; i++) {
windowArr[i] = adcToTemp(analogRead(ADC_PIN));
delay(10);
}
}
// ---------------- LOOP ----------------
void loop() {
int raw_adc = analogRead(ADC_PIN);
float rawTemp = adcToTemp(raw_adc);
// Update sliding window
windowArr[widx] = rawTemp;
widx = (widx + 1) % WINDOW_SIZE;
// Median-filtered temperature
float filtTemp = medianN(windowArr, WINDOW_SIZE);
// Validation logic
if (filtTemp >= TEMP_THRESHOLD) {
validCounter++;
if (validCounter >= VALID_COUNT) {
alarmState = true;
}
} else if (filtTemp < (TEMP_THRESHOLD - HYSTERESIS)) {
validCounter = 0;
alarmState = false;
digitalWrite(LED_PIN, LOW);
ledState = false;
}
// LED blinking during alarm
if (alarmState) {
unsigned long now = millis();
if (now - lastBlinkTime >= BLINK_INTERVAL) {
lastBlinkTime = now;
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
}
}
// UART diagnostics
Serial.print(raw_adc);
Serial.print("\t");
Serial.print(rawTemp, 1);
Serial.print("C\t");
Serial.print(filtTemp, 1);
Serial.print("C\t");
Serial.print(validCounter);
Serial.print("\t");
Serial.println(alarmState ? "ALARM" : "OK");
delay(SAMPLE_RATE_MS);
}