Blog 4: Project Development
- amuhsin23
- Feb 16, 2025
- 13 min read
Updated: Feb 26, 2025

I won't beat around the bush—I’m in full-on exam crunch mode with less than a week to go, but here I am, turbocharging this blog post in “light speed” mode. ⚡️ This post covers the last 1.5 months of hard work by my team, with a special focus on my contribution: coding. and yes, I'll be leaning on my trusty digital buddies—ChatGPT, Gemini, DeepSeek, Grok, and Claude—to keep things both informative and fun. 😄
The Chemical device
Our team was tasked with a critical challenge: preventing dangerous indoor air pollution caused by burning biomass. Due to the environmental 🌱 and health ⚕️ impacts of biomass combustion, including the release of harmful gases like CO and volatile organic compounds, a monitoring and ventilation system is essential. However, since this project is on a smaller scale 📐 and handling CO is hazardous ☢️, we will use alcohol vapors 🍶 as a substitute for demonstration purposes.
What’s the Big Idea?
Our brilliant solution is a gas detector that vigilantly watches the air quality like a hawk 🦅 and sounds the alarm (literally 🚨) when gas levels spike. But wait—there’s more! It’s linked to an automatic fan that kicks in to refresh the space with a burst of clean air 🌬️💨. Imagine having your very own indoor superhero saving the day!
How It Works: The Step-by-Step Rundown
1️⃣ Calibration Phase: When you first power it up, our gas detector takes about 3 minutes to calibrate—think of it as its warm-up stretch before the big game 🤸♂️.
2️⃣ Air Quality Check: Once it’s all set, it displays the concentration of harmful substances in the air, keeping you informed like your favorite news ticker 🚦.
3️⃣ Alarm & Action: If the gas levels get too high, the detector starts beeping like a fire alarm 🚨 and activates the fan, instantly ventilating the area. Fresh air to the rescue! ✨
Before we could even dream of building this chemical device, we had to roll up our sleeves and sketch out a detailed design drawing—a blueprint that turns our wild ideas into a solid plan 📝🎨.

This lovely drawing was done by Fiona. You a real one for this.🫡🫡🫡
Team allocation and Planing

Rakshan – Chief Executive Officer (CEO) 👑✨Rakshan leads the team with passion and contributes wherever needed. He helps set our overall timeline 🗓️ and delegates tasks to keep everything on track. Even though arts and crafts aren’t his forte 🎨, he still got hands-on by cutting and folding cardboard ✂️📦, working on 3D modeling in Fusion 🖥️, and creating our final presentation slides 🎞️. He originally wanted to be the janitor of our group but we thought he was just being humble. As such we choose him to be our CEO.
Fiona – Chief Financial Officer (CFO) 💰📊Fiona expertly manages our finances by tracking the Bill of Materials (BOM) 📜 and ensuring we stick to our budget 💸. During the build, she made sure that the gas detector not only looked fantastic ✨ but also measured up perfectly 📏. She’s our go-to for sourcing materials 🛍️ and keeping the team focused, even when distractions pop up 🙈🔔.
Muhsin (Me) – Chief Security Officer (CSO) 🛡️🔒I’m our safety guru, making sure we follow every guideline outlined in our Risk Assessment ✅. During the build, I took charge of coding 💻, set up the Arduino and breadboard connections ⚡🔌, and ensured that everything was secure and efficient. My commitment to safety keeps us protected every step of the way 👀🔐.
🔖 Yoong Sern – Chief Operating Officer (COO) ⚙️🚀While Rakshan handles the overall timeline, Yoong Sern manages the detailed scheduling 📅 and ensures our operations run like a well-oiled machine. He stepped in wherever needed—assembling the gas detector's components with precision 🔩🛠️, leading the primary 3D modeling efforts 🖥️, and even helping with the cardboard tasks ✂️📦. In many ways, he’s like a second CEO, always ready to keep us on track 😎🏆.
Next we have our gantt chat. This was how we decided on who does what for the project

The gantt chart might look confusing but its actually pretty simple. It just shows what tasks we will need to do and by when. It also shows when we actually did it to see whether we were on track or not. As you can see from above we were always on time or slightly ahead of schedule. Next up is our Build of Materials (BOM) 🛠️💡.

Our BOM gives you the full scoop on everything we needed, and it turns out we nearly hit our $100 budget mark 💸😅. In future projects, we’re determined to trim the costs even further so we can stay comfortably under budget.
Enough with the paperwork—let’s get to the fun part: building! 🏗️🔧
Design and Build Process
I won’t go into all the minute details here (check out Rakshan’s and Yoong Sern’s blogs for the deep dive on the gas detector’s construction), but here’s the scoop on my part. I’ll be sharing some cool pictures along the way, but my main focus is on the coding—where I did most of the heavy lifting with a bit of help from my digital buddies 🤖💪.

Here is the embedded file and if you want to use it and edit it you can download the file below
Final code for gas detector |
#include <LiquidCrystal_I2C.h> #include <Wire.h> // Pin Definitions const int gasSensorPin = A0; const int ledPin = 5; const int buzzerPin = 8; const int fanPin = 7; // Pin for the fan // Sensor Constants const float RL = 10000.0; // Load resistance (Ω) float freshAirRatio = 1.52; // LPG Sensor Calibration (from sensor datasheet) // (Replace with values from your sensor's datasheet) const float m = -0.473; const float b = 1.413; // Threshold for alert (PPM) const float gasThresholdPPM = 1000.0; // Variables float gasPPM; int gasValue; float R0; // LCD Setup LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { pinMode(ledPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(fanPin, OUTPUT); // Set fan pin as output Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // Calibrate R0 in clean air R0 = calculateR0(); Serial.print("Calibrated R0: "); Serial.println(R0); Serial.println("Calibration Complete. Monitoring Gas Levels."); } void loop() { const int numReadings = 10; // Number of readings to average float total = 0; float sensorResistance; float ratio; // Take multiple readings for stability for (int i = 0; i < numReadings; i++) { gasValue = analogRead(gasSensorPin); if (gasValue > 1023) gasValue = 1023; sensorResistance = ((1023.0 / gasValue) - 1) * RL; ratio = sensorResistance / R0; total += pow(10, (log10(ratio) - b) / m); delay(50); } gasPPM = total / numReadings; // Debug prints Serial.print("Raw Analog: "); Serial.println(gasValue); Serial.print("Calculated PPM: "); Serial.println(gasPPM); // Update LCD display lcd.clear(); lcd.setCursor(0, 0); lcd.print("Conc: "); lcd.print(gasPPM); lcd.print(" PPM"); if (gasPPM > gasThresholdPPM) { lcd.setCursor(0, 1); lcd.print("Status: HIGH!"); digitalWrite(fanPin, HIGH); // For high gas concentrations, run the alarm routine repeatedly for 1 second unsigned long alarmStart = millis(); while (millis() - alarmStart < 1000) { triggerAlarm(gasPPM); } } else { lcd.setCursor(0, 1); lcd.print("Status: Normal"); digitalWrite(fanPin, LOW); blinkLED(); delay(1000); } } /* * triggerAlarm() produces a beep on the buzzer and a flash on the LED. * The on-time is fixed at 50ms. The off-time is mapped from 200ms (at 1,000 PPM) * down to 50ms (at 5,000 PPM), so the beep rate increases as gas concentration rises. */ void triggerAlarm(float concentration) { const float maxConcentration = 5000.0;
int onTime = 50; // beep on-time in ms // Map concentration to offTime: at gasThresholdPPM (1000 PPM) offTime ~200ms, at maxConcentration offTime ~50ms. int offTime = map(concentration, gasThresholdPPM, maxConcentration, 200, 50); offTime = constrain(offTime, 50, 200); // Activate buzzer and LED together for the beep tone(buzzerPin, 1000); // 1kHz tone digitalWrite(ledPin, HIGH); delay(onTime); noTone(buzzerPin); digitalWrite(ledPin, LOW); delay(offTime); } /* * blinkLED() is used when gas concentration is normal. * It makes the LED blink slowly (1 second on, 1 second off). */ void blinkLED() { digitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); delay(1000); } /* * calculateR0() calibrates the sensor in clean air. * It reads the sensor several times and calculates the baseline resistance R0. */ float calculateR0() { float sumRs = 0; int samples = 50; float sensorResistance; Serial.println("Calibrating R0 (Clean Air)..."); for (int i = 0; i < samples; i++) { int sensorVal = analogRead(gasSensorPin); sensorResistance = ((1023.0 / sensorVal) - 1) * RL; sumRs += sensorResistance; delay(50); Serial.print("Sample "); Serial.print(i + 1); Serial.print(": Rs = "); Serial.println(sensorResistance); } float R0_calibrated = sumRs / (samples * freshAirRatio); Serial.print("Calibrated R0: "); Serial.println(R0_calibrated); return R0_calibrated; } |

From the code, you'll notice several comments that explain how sensor values are derived (based on the sensor datasheet 📚—check it out if you're interested!). In the near future, I plan to share this code on Hackster.io along with a guide on programming and wiring your Arduino. But for now, let’s just focus on the code and how it works. 😎

From the figures above you can see how I got my sensor values. These figures can be found in the datasheet.
Code explanation
Library Imports & Pin Setup 📚🔌
We kick things off by including essential libraries:
LiquidCrystal_I2C.h: For controlling our LCD display 📟
Wire.h: To handle I2C communication for smooth data transfers 🔄
Next, we define our key pins:
gasSensorPin (A0): Where our gas sensor gets its readings
🌫️
ledPin (5): To light up an LED when needed 💡
buzzerPin (8): For the sound alarm that alerts us 🚨
fanPin (7): To power our ventilation fan and keep things cool 🌬️
We also set up sensor constants like the load resistance (RL = 10,000Ω) and calibration constants (m and b) to convert raw sensor data into gas concentration (PPM). We even define a gasThresholdPPM (set at 1000 PPM) to trigger alerts when air quality dips. 📏💨
2. LCD Initialization & Sensor Calibration 📟⚙️
Before the action begins, our LCD display is set up with the address 0x27 and dimensions 16x2. We then move into the setup() function:
Pin Modes: We declare the LED, buzzer, and fan pins as outputs.
Serial Communication: Initialized at 9600 baud for real-time debugging messages.
LCD Setup: The LCD is initialized, backlight turned on, and cleared for fresh info.
Sensor Calibration:
We call the calculateR0() function to calibrate the sensor in clean air. This function takes 50 readings, computes the sensor’s resistance, and then calculates a baseline resistance (R0) by averaging and adjusting with our fresh air ratio. This baseline is key for determining gas concentrations later on. Calibration details are printed out to the Serial Monitor for verification. 📊🔍
3. The Main Loop: Monitoring & Alerting in Action 🔄📈
Inside the loop() function, our code runs continuously to keep an eye on the air quality. Here’s how it works:
Averaging Sensor Readings:
We take 10 quick readings to smooth out any noise. For each reading:
Analog Read: We read the value from our gas sensor.
Sensor Resistance Calculation:
Using the formula:

Gas Concentration Calculation:
We compute the ratio of the sensor resistance to the calibrated R0 and then convert it to parts per million (PPM) using a logarithmic formula. This average gives us the current gas concentration. 📏🌫️
Displaying Results:
The LCD is updated to show the current gas concentration. If it’s above our threshold, it displays “Status: HIGH!”; otherwise, it shows “Status: Normal.” 📟✨
Alert Mechanism:
High Gas Concentration:
When gas levels are too high (> 1000 PPM), the code turns on the fan to ventilate the area and triggers an alarm routine.
The triggerAlarm() function is particularly cool—it makes the buzzer beep and flashes the LED. The beep frequency increases with higher gas concentrations by dynamically adjusting the delay between beeps. 🔔💥
Normal Conditions:
If everything’s A-OK, the LED gently blinks using the blinkLED() function (1 second on, 1 second off) to indicate that the environment is safe. 💡😌
4. Helper Functions: Bringing It All Together 🔧🎉
triggerAlarm(concentration):This function creates an urgent yet rhythmic alert by:
Emitting a 1 kHz tone from the buzzer.
Flashing the LED.
Adjusting the off-time between beeps based on the gas concentration (more gas = faster beeps). Think of it as the code’s way of saying, “Warning, warning!” 🚨🔊
blinkLED():A simple yet effective routine that makes the LED blink slowly when everything is normal. It’s our visual “all-clear” signal! 💡👍
calculateR0():This calibration function samples the sensor in clean air, calculates resistance for each reading, and then averages them (adjusted by a fresh air ratio) to determine the baseline R0. Accurate calibration is crucial, and this function ensures our sensor knows what “clean air” looks like. 📏🛠️

You would think that after writing a crisp 155 lines of code, our gas detector would work smoothly right out of the gate? 🤔Well... wrong! It HAS to be properly warmed up first first🔥. Turns out, our sensor needs a little time to reach optimal operating conditions, and that's where the magic happens! In the picture on the left, you can see me performing some *hair-raising* work (literally 💇♂️🔦) by using a hair dryer to give the gas detector a gentle, toasty treatment.
Now that all the hiccups have been ironed out, it's showtime! 🎬✨ On the right, you'll catch a glimpse of me giving the gas detector a quick wipe-down with an alcohol swab 🍶🧼, triggering it into full alarm mode 🚨. I'll be honest: my original code was pretty rough—total trash 🗑️. The magic behind displaying neat PPM readings (instead of just raw sensor data) came courtesy of my digital heroes, ChatGPT and the Google Gemini API 🤖🌟. I'd say roughly 95% of the code was their masterpiece, and my humble 5% was all about getting that initial setup just right.
Finally we can put everything together and see how it looks like.

As you can see from above, it turned out extremely well. I am quite happy with how the internals just fit right in the head of the Chicken giving it a clean look with no loose wires hanging outside it. I also liked how it looks like the Chicken from crossy road
And I think that just about wraps up the Design and Build Process section. Now me move onto problems and solutions.
Problems and Solutions
I had multiple versions of the code just not working sometimes and other times it would work just fine even though I I did not change even a single line of code. Ignoring all though instances I can only think of 2 problems that really took me a long time to solve.
Problem 1: Blue Screen of death💻

When I first started testing, everything appeared to be smooth sailing until I triggered the gas detector’s alarm mode. Suddenly, my computer would crash and display the dreaded blue screen! 😱 I spent an embarrassingly long time scratching my head over this mystery. Fortunately, the brilliant Gemini API pointed me in the right direction.

From the Picture on the left you can see the highlighted code that it wrote. Basically what was happening was that the output that the gas sensor was giving to the Arduino was higher than the limit of 1024 which causes an overflow error which in turn crashed my computer.
Now that single line of code basically says that if the output from the gas detector is grater than 1023 just assume it to be 1023. Overall an easy fix considering how huge of an issue this was.
Problem 1: Wrong Wiring on the Arduino.

From the images above you can see how messy and cluttered our wiring was. It did not help that there was no standardisation for the wiring. This all resulted in me wiring up the Arduino improperly and causing it to malfunction. The solution to all of this was quite simple. We first started by introducing a consistent color-coding system (Blue and Purple for 5V and ground, green and yellow for signals) and secondly we grouped wires to prevent future confusion by not spiting them when connecting them to the components like the gas sensor and LCD.
Now finally its time for the reflection.
Personal Learning Reflection
This project stands out as one of the most demanding—and, at times, downright stressful—academic experiences I’ve ever encountered. Every attempt to program the Arduino brought its own set of frustrations, with repeated setbacks that pushed me to the edge. Yet, the moment everything finally clicked, the relief and sense of accomplishment were simply priceless. 🙌💖
Rethinking Problem-Solving
One of the most valuable lessons was learning to adapt my problem-solving approach. While coding the fan control, I initially assumed that a standard Arduino pin could power the fan. 🚀🔌 I soon discovered that the fan required the 5V pin to operate properly—a pin that was already dedicated to the LCD. It was a moment of panic, realizing I’d hit a roadblock. However, after digging deeper and rethinking the challenge, I found that wiring a transistor was the perfect solution. This experience taught me that even when a solution appears out of reach, a fresh perspective can illuminate a simple fix. 🔍💡
The Power of Teamwork
This journey wouldn’t have been possible without the unwavering support of my teammates. Their unique skills and relentless dedication transformed a daunting challenge into a resounding success:
Rakshan: Your leadership, meticulous planning, and tireless effort in managing the paperwork and slides kept us on track. 👑📚
Fiona: Your creative design ideas for the gas detector were nothing short of game-changing. Thank you for igniting our project with your innovation. 🎨💡
Yoong Sern: Your expertise in 3D modeling brought our vision to life, bridging the gap between concept and reality. 🖥️🔧
A Journey of Growth
I now see that every moment of frustration and every setback was a stepping stone to growth—both personally and as a team. I’m grateful for every lesson learned, and I’m excited for the future, confident that with persistence, creativity, and collaboration, no challenge is insurmountable. 🚀😊
Anyway I think that is enough Yapping from me. Hopefully I will never have to write a blog ever again. But I actually feel kinda sad now that everything is over




Comments