// Fuzzy Logic: In-Car Air Quality Monitoring System
// Adapted from reference project: https://wokwi.com/projects/376037130101397505
// Based on article: https://medium.com/it-paragon/fuzzy-logic-approximating-imprecise-statement-54d444237820
#include <Fuzzy.h>
Fuzzy *fuzzy = new Fuzzy();
// Declare fuzzy set pointers
FuzzySet *coLow, *coModerate, *coHigh, *coCritical;
FuzzySet *vocNormal, *vocElevated, *vocHigh;
FuzzySet *dustClean, *dustPolluted, *dustHazardous;
FuzzySet *scoreNormal, *scoreMild, *scoreWarn, *scoreDanger, *scoreCritical;
void setup() {
Serial.begin(115200);
// ----------- CO (ppm) -----------
FuzzyInput *co = new FuzzyInput(1);
coLow = new FuzzySet(0, 0, 10, 20);
coModerate = new FuzzySet(15, 30, 30, 45);
coHigh = new FuzzySet(40, 60, 60, 80);
coCritical = new FuzzySet(75, 90, 100, 100);
co->addFuzzySet(coLow);
co->addFuzzySet(coModerate);
co->addFuzzySet(coHigh);
co->addFuzzySet(coCritical);
fuzzy->addFuzzyInput(co);
// ----------- VOC (ppm) -----------
FuzzyInput *voc = new FuzzyInput(2);
vocNormal = new FuzzySet(0, 0, 50, 100);
vocElevated = new FuzzySet(80, 150, 150, 220);
vocHigh = new FuzzySet(200, 300, 500, 500);
voc->addFuzzySet(vocNormal);
voc->addFuzzySet(vocElevated);
voc->addFuzzySet(vocHigh);
fuzzy->addFuzzyInput(voc);
// ----------- DUST (PM2.5 µg/m3) -----------
FuzzyInput *dust = new FuzzyInput(3);
dustClean = new FuzzySet(0, 0, 15, 30);
dustPolluted = new FuzzySet(25, 75, 75, 125);
dustHazardous = new FuzzySet(100, 200, 300, 300);
dust->addFuzzySet(dustClean);
dust->addFuzzySet(dustPolluted);
dust->addFuzzySet(dustHazardous);
fuzzy->addFuzzyInput(dust);
// ----------- Monitoring Score (0-10) -----------
FuzzyOutput *score = new FuzzyOutput(1);
scoreNormal = new FuzzySet(0, 0, 1, 2);
scoreMild = new FuzzySet(1.5, 3.5, 3.5, 4.5);
scoreWarn = new FuzzySet(4, 5.5, 5.5, 6.5);
scoreDanger = new FuzzySet(6, 7.5, 7.5, 8.5);
scoreCritical = new FuzzySet(8, 9, 10, 10);
score->addFuzzySet(scoreNormal);
score->addFuzzySet(scoreMild);
score->addFuzzySet(scoreWarn);
score->addFuzzySet(scoreDanger);
score->addFuzzySet(scoreCritical);
fuzzy->addFuzzyOutput(score);
// ----------- Optimized Fuzzy Rules (13 Total) -----------
// Rule 1
FuzzyRuleAntecedent* r1a1 = new FuzzyRuleAntecedent();
r1a1->joinWithAND(coLow, vocNormal);
FuzzyRuleAntecedent* r1 = new FuzzyRuleAntecedent();
r1->joinWithAND(r1a1, dustClean);
FuzzyRuleConsequent* r1c = new FuzzyRuleConsequent();
r1c->addOutput(scoreNormal);
fuzzy->addFuzzyRule(new FuzzyRule(1, r1, r1c));
// Rule 2
FuzzyRuleAntecedent* r2a1 = new FuzzyRuleAntecedent();
r2a1->joinWithAND(coLow, vocElevated);
FuzzyRuleAntecedent* r2 = new FuzzyRuleAntecedent();
r2->joinWithAND(r2a1, dustPolluted);
FuzzyRuleConsequent* r2c = new FuzzyRuleConsequent();
r2c->addOutput(scoreMild);
fuzzy->addFuzzyRule(new FuzzyRule(2, r2, r2c));
// Rule 3
FuzzyRuleAntecedent* r3a1 = new FuzzyRuleAntecedent();
r3a1->joinWithAND(coLow, vocHigh);
FuzzyRuleAntecedent* r3 = new FuzzyRuleAntecedent();
r3->joinWithAND(r3a1, dustHazardous);
FuzzyRuleConsequent* r3c = new FuzzyRuleConsequent();
r3c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(3, r3, r3c));
// Rule 4
FuzzyRuleAntecedent* r4a1 = new FuzzyRuleAntecedent();
r4a1->joinWithAND(coModerate, vocNormal);
FuzzyRuleAntecedent* r4 = new FuzzyRuleAntecedent();
r4->joinWithAND(r4a1, dustClean);
FuzzyRuleConsequent* r4c = new FuzzyRuleConsequent();
r4c->addOutput(scoreMild);
fuzzy->addFuzzyRule(new FuzzyRule(4, r4, r4c));
// Rule 5
FuzzyRuleAntecedent* r5a1 = new FuzzyRuleAntecedent();
r5a1->joinWithAND(coModerate, vocElevated);
FuzzyRuleAntecedent* r5 = new FuzzyRuleAntecedent();
r5->joinWithAND(r5a1, dustPolluted);
FuzzyRuleConsequent* r5c = new FuzzyRuleConsequent();
r5c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(5, r5, r5c));
// Rule 6
FuzzyRuleAntecedent* r6a1 = new FuzzyRuleAntecedent();
r6a1->joinWithAND(coModerate, vocHigh);
FuzzyRuleAntecedent* r6 = new FuzzyRuleAntecedent();
r6->joinWithAND(r6a1, dustHazardous);
FuzzyRuleConsequent* r6c = new FuzzyRuleConsequent();
r6c->addOutput(scoreDanger);
fuzzy->addFuzzyRule(new FuzzyRule(6, r6, r6c));
// Rule 7
FuzzyRuleAntecedent* r7a1 = new FuzzyRuleAntecedent();
r7a1->joinWithAND(coHigh, vocNormal);
FuzzyRuleAntecedent* r7 = new FuzzyRuleAntecedent();
r7->joinWithAND(r7a1, dustClean);
FuzzyRuleConsequent* r7c = new FuzzyRuleConsequent();
r7c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(7, r7, r7c));
// Rule 8
FuzzyRuleAntecedent* r8a1 = new FuzzyRuleAntecedent();
r8a1->joinWithAND(coHigh, vocElevated);
FuzzyRuleAntecedent* r8 = new FuzzyRuleAntecedent();
r8->joinWithAND(r8a1, dustPolluted);
FuzzyRuleConsequent* r8c = new FuzzyRuleConsequent();
r8c->addOutput(scoreDanger);
fuzzy->addFuzzyRule(new FuzzyRule(8, r8, r8c));
// Rule 9
FuzzyRuleAntecedent* r9a1 = new FuzzyRuleAntecedent();
r9a1->joinWithAND(coHigh, vocHigh);
FuzzyRuleAntecedent* r9 = new FuzzyRuleAntecedent();
r9->joinWithAND(r9a1, dustHazardous);
FuzzyRuleConsequent* r9c = new FuzzyRuleConsequent();
r9c->addOutput(scoreCritical);
fuzzy->addFuzzyRule(new FuzzyRule(9, r9, r9c));
// Rule 10
FuzzyRuleAntecedent* r10a1 = new FuzzyRuleAntecedent();
r10a1->joinWithAND(coCritical, vocNormal);
FuzzyRuleAntecedent* r10 = new FuzzyRuleAntecedent();
r10->joinWithAND(r10a1, dustClean);
FuzzyRuleConsequent* r10c = new FuzzyRuleConsequent();
r10c->addOutput(scoreCritical);
fuzzy->addFuzzyRule(new FuzzyRule(10, r10, r10c));
// Rule 11
FuzzyRuleAntecedent* r11a1 = new FuzzyRuleAntecedent();
r11a1->joinWithAND(coModerate, vocNormal);
FuzzyRuleAntecedent* r11 = new FuzzyRuleAntecedent();
r11->joinWithAND(r11a1, dustHazardous);
FuzzyRuleConsequent* r11c = new FuzzyRuleConsequent();
r11c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(11, r11, r11c));
// Rule 12
FuzzyRuleAntecedent* r12a1 = new FuzzyRuleAntecedent();
r12a1->joinWithAND(coLow, vocNormal);
FuzzyRuleAntecedent* r12 = new FuzzyRuleAntecedent();
r12->joinWithAND(r12a1, dustHazardous);
FuzzyRuleConsequent* r12c = new FuzzyRuleConsequent();
r12c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(12, r12, r12c));
// Rule 13
FuzzyRuleAntecedent* r13a1 = new FuzzyRuleAntecedent();
r13a1->joinWithAND(coModerate, vocHigh);
FuzzyRuleAntecedent* r13 = new FuzzyRuleAntecedent();
r13->joinWithAND(r13a1, dustClean);
FuzzyRuleConsequent* r13c = new FuzzyRuleConsequent();
r13c->addOutput(scoreWarn);
fuzzy->addFuzzyRule(new FuzzyRule(13, r13, r13c));
}
void loop() {
// Read inputs from analog pins (sliders)
int rawCO = analogRead(32);
int rawVOC = analogRead(33);
int rawDust = analogRead(34);
// Scale raw values to appropriate sensor ranges
float coInput = (rawCO / 4095.0) * 100.0; // CO in ppm
float vocInput = (rawVOC / 4095.0) * 500.0; // VOC in ppm
float dustInput = (rawDust / 4095.0) * 300.0; // Dust in µg/m3
fuzzy->setInput(1, coInput);
fuzzy->setInput(2, vocInput);
fuzzy->setInput(3, dustInput);
fuzzy->fuzzify();
float result = fuzzy->defuzzify(1);
Serial.print("CO: "); Serial.print(coInput);
Serial.print(" ppm, VOC: "); Serial.print(vocInput);
Serial.print(" ppm, Dust: "); Serial.print(dustInput);
Serial.print(" µg/m3 => Score: "); Serial.println(result, 1);
delay(2000);
}