ECE 4760: Introduction to Microcontroller Programming

Robert Clain |
and Miguel Salas |
Cornell University 2009

Introduction High Level Design Hardware/Sofware Details Results Conclusions Appendices References

*A Big Thanks to Alphasense, Atmel and Maxim for their part dontations*


Our project is a fart intensity detector which ranks fart magnitude on a scale from 0-9 according to sound, temperature, and gas concentrations.

The inspiration for this project was to determine who could generate the worst flatulence measurable in a personally unbiased manner. To do so, however, requires measuring the intensity of these emissions. Therefore, a multitude of sensors were used in conjunction with a microcontroller to coordinate and analyze these sensor readings. Sound, temperature, and gas sensors were chosen for this purpose. As a readout itself is not very user friendly, additional functionality was added with an LCD screen, speaker, LEDs, fan, and playback button. The LCD screen alerts the user to the status of the device as well as displaying rankings from each sensor and an overall ranking, as do flashing LEDs. Overall rank is stated by the speaker, with beeping of varying intensity signifying fart rank. If the fart is ranked high enough, a fan turns on to blow it away. A playback button is also added to allow the user to hear his previous fart. All of these components were assembled in a component box on a tripod, or a Fart Intensity Detection Station, for ease of use.



The idea to design a fart detector came from the fact that any device that can sense farts would require us to actually make use of the capabilities of the MCU, integrate hardware and software for the sensors, and on top of being a nontrivial project, it would be an enjoyable and certainly unique project to take on.

The truth is that farts are something recurrent in everyday life. Just like we breathe, we fart. From the reader of this webpage to the designers of this device, we all do it. This device might not be a innovative proof-of-concept gadget, but it certainly fulfills its two functions:

1. It creates an opportunity for the designers to show their expertise in hardware to software integration, since the project is based on chemical, sound and temperature sensing, data processing and conditional output. Likewise, it is not a device with a straight-forward design. Since it has never been done, it certainly poses a lot of research challenges, such as ultra-low H2S detection, super-sensitive temperature detection, and coming up with user-friendly ways to tell the user of the severity of the fart.

2. It is a device that everyone, from a PhD student to a little kid, can feel eager to play with. It is a fascinating idea that something that induces so much laughter in society can be actually measured and objectively ranked. In fact, it is a project that shows the world out there that electrical engineering can indeed be simply deliciously FUN!

Background math

The first step in determining the feasibility of this project was to find out what distinguishes a common weak fart from a mighty intense one. Upon doing some background research (see References section at the end of the page), we found out that intense farts could be best described as those that are the most unpleasant to others (for the one responsible for the emission, perhaps an inversely proportional scale applies, but that is out of the scope here). Farts are gaseous emissions of hydrogen, oxygen, nitrogen and other trace gases. The sulphur-containing trace gases, such as Hydrogen sulphide and mercaptans, are the ones accountable for the smell. Bacteria breaks down organic components inside our stomachs into hydrogen sulphide (H2S), which gives the infamous 'rotten eggs smell'.

For us, this meant that we needed to detect trace amounts of hydrogen sulphide. A fart contains from 0.001 PPM to 1 PPM of H2S (indeed, the human nose is very sensitive). The human odor threshold is above 0.005PPM and prolongued exposition of more than 1 hour to H2S above 1 PPM is a health hazard (those are truly killer farts). To make sure that it was feasible to measure such amounts we obtained the breakdown of gases found in room temperature air:

According to wikipedia, there is about 0.0001 PPM of H2S in the air. Since the amount of H2S found in the atmosphere is way below that in a fart, we determined such low-level sensing would be possible. After some negotiations, we were able to have a 4mA-20mA transmitter board with a H2S sensor sampled. Such board is commonly found on toxic detectors in factories. For our custom designed board, 4mA was the output when there was absolute 0 PPM in the air and 20mA when the maximum detectable 20 PPM was detected. After a current-to-voltage conversion, our calculation showed that:

This meant that unless the voltage went up by around 2.5 mV (0.8mA/330ohms), the fart could be ranked to 0. On the other hand, a 150mV change would be ranked at the top. This was great news since the ADC is accurate up to around 1mV on 10-bit mode with an A_ref of 5V (5V/1024 is roughly 1mV).

However, the most dangerous farts are not just those with containing hydrogen sulfide, but those that on top of this chemical, are warmer. This is because as the temperature goes up, the environment for this H2S producing bacteria becomes more encouraging, increasing the amount of the H2S in the fart and also amplifying the range of the fart, since gases move faster when they are hotter as dictacted by:

Sound is another factor that makes farts unpleasant to the bystanders. There is certainly something we have built-in that makes us feel uncomfortable when a fart noise is emitted next to us (of course, that is excluding the laughing riot it might ensue afterwards). As a sound-to-voltage transducer, we used a simple electret microphone. This cheap microphone does not do any filtering, and so low frequency noise had to be extracted out. As such, we needed to design a high pass filter. To find out the RC constant of the filter, we solved and obtained the following:

Finally, since speech was going to be an output from the speaker, we had to set and generate a sampling frequency for speech. We used 8kHz since that is larger enough to hear a smooth signal but small enough not to run out of memory. To generate this 8kHz frequency we used the equation:


Where TOP is the largest size of the counter, for which we used timer 0 (an 8-bit counter) producing TOP=255. N is the clock prescalar and fclk is the frequency of the microprocessor, which we ran with 16 MHz crystals.

Logical structure

The flatulence intensity detector was designed with the goal of detecting the intensity of a fart. To that end, the dectector measures hydrogen sulfide (H2S) concentration with an H2S sensor, temperature with a super sensitive thermistor, as well as sound with a microphone, to determine the danger of a fart. In general, Hydrogen sulfide concentration determines how malodorous a fart is (other gases, such as methane, also contribute to the smell but are not present in every fart). Temperature, on the other hand, is related to how much smelly body bacteria has been trapped in a fart the hotter the fart, the more threatening. Finally, noise is important for determining the danger of a fart while not directly related to smell, it is related to the awareness of those nearby (a scare factor). Sensor values are compared against those in look-up tables in order to determine the ranking of a fart. Once the flatulence is detected, the user is alerted by a speaker and an LCD display. The user also has the option of playing back his masterpiece at the push of a button.

To design this project, sensors were individually tested using our designed readout/amplification circuits. Each sensor was set to trigger over a threshold value determined through testing. The LCD screen and speaker output code were likewise written and tested separately. Sensors were then combined into a single file, toggling between analog-to-digital conversion channels. A uniform trigger based on sound was used, as the air pressure generated by a fart is enough for the lowest sound ranking. The LCD screen pauses when the fart is sensed so 1000 samples can be generated for each sensor. In the meanwhile, flashing LEDs would indicate the user of this sensing delay. A maximum value is then taken, with a ranking from each sensor along with an overall ranking sent to the LCD screen. The overall ranking is then voiced by the speaker. Afterwards, the speaker beeps with varying frequency according to the ranking of the fart. Simulatenous to the beeping, a fan would turn on if the obtained ranking was one of the top three to dissipate the malefactor. Finally, a playback command sent via a pushbutton plays back recorded sound information at the sampling frequency. All of these components were assembled in a component box on a tripod, into a Fart Intensity Detection Station, for ease of use.

In summary, the task of measuring the intensity of a fart can be broken down in three stages.The first stage is, not surprisingly, the Power Supply stage . This is where we designed how the input power was going to be delivered to the circuitry. Since we opted to have the device into a station, we optimized the design to take in power from the outlet using a AC-DC adapter. However, the incoming DC needed to be regulated. Its logical structure follows:

The second stage is the Transducing stage, or "input circuitry", where sound, temperature and the odorous chemical (Hydrogen Sulphide) is transformed into a voltage to be fed into the ADC. The following Diagram shows its logical structure:

The final stage is the output stage, or User Indication stage, where the user is told of its fart intensity ranking through speech, LCD display and a beeping to indicate the user the level of danger, with beeping of varying frequency. A fan would turn on only for the specially dangerous farts: Those with rankings of 7, 8 and 9. The logical structure of this stage is as follows:

To implement all the functionality described, we developed it into a simple state machine. This simplified the task a great deal. The following shows our state machine:

Hardware/software tradeoffs

In implementing our device, we faced a number of hardware v.s. software trade-offs. First, we chose to send the input signals from the tranducers into the ADC converter to do rank processing and find do a discretized analysis of the signals. However, we could have saved some ADC load by using a transistor networks on which depending on how much AC voltage is seen from the sensors, one of 10 signals could have been sent to the ouput stage (speaker, LCD and Fan). However such design practice would have required so much precision hardware (since we are dealing with small voltage differences) that software was clearly the best way to proceed.

Second, we had to decide how much amplification these super-sensitive transducers had to be done by hardware and how much could just be reliably read by the ADC. This is because without amplification, the noise, temperature and chemical amount being measured would give the voltage changes in microvolts if at all noticeable. Thus, we did some hardware amplification until the voltage changes of interest were in the range of around 0.1V. This meant that we relied more on hardware than on software, since the ADC converter was accurate up to 5mV, and that is excluding other errors which may worsen this upper limit.
Another aspect in which this trade off came to play was

Finally, in doing speech generation from a sampled wavefile, we downsampled a .wav file and saved the data into program memory for replay based on fart ranking. In this way, we made use of hard-coded harware after using downsampled and information processing from software. We could have produced this sound by the pure use of hardware, by using a DSP chip to process the ranking given from the MCU and in that way, do an extremely fast adaptable hardware-based approach. However, since the sole purpose was to inform the user of its ranking, such real-time evaluation was not worth the overhead of DSP processing.

Standards Considered

In our project, we considered three standards. The first standard was the Federal Standard 1037C. This standard defines the telecommunication terms. voice generation frequency is defined to be keep between 300 HZ and 3400 Hz). No wireless EM transmission at this frequency was intended, as we sent this voltage frequency through an isulated wire to a transducer (speaker). Then, the transducer uses acoustic waves at this frequency.

The other two standards we used was the OSHA 1008 and OSHA 1910.1000 standards. The OSHA 1008 defines how H2S should be measured safely. We made sure that the sampled sensor used a safe procedure in compliance with the standard for tests in the lab. The OSHA 1910.1000 standard defines the H2S levels that are safe for working conditions. This meant that, even for testing, we could not bring to the lab a H2S sample due to its toxity.

In fact the NIOSH gives the following table indicating its toxicity levels:

Related Patents

In implementing our idea, we were careful not to steal any intellectual property by doing research on related patented devices. The two closest we found were:

1. Sensing for home automation network Patent. This is a remote sensing device in the shape of a diper. It detects human body odor and fluids. It relates to our project in that it does human-produced odor-sensing and process its data. However, we did not violate this idea because the purpose of our device is to calculate the intensity (not just the existance) of a specific body emission.

2. Fart cancelling cushion Patent. This device is a cushion that when a human sits on and releases a fart, the noise and the smell will be cancelled out, thus avoiding nearby bystanders the unpleasant smell of the fart. Our device is not aimed to cancel the fart out (as that would require a lot of chemicals to be expelled). Instead it just measures its intensity. However, it does have an added-in feature that a fan will turn on when the amount of odorous gas expelled is too high. This is just a user-friendly feature.


Program Details

Our main program file, FartDetect.c, was separated using a state machine for different functions. Two sub-programs were used for lcd screen manipulation and debugging through the hyperterminal, and these are referred to in the References section. Below is a description of how the main file operates in addition to its state breakdown, neglecting include statements and variable declarations:

Initialization - In the initialization stage, all variables are initialized. This stage is entered upon power-up. It is also here that execution is delayed for 20 seconds. This time delay allows for the H2S sensor to warm up, as well as for initial sensor readings. The sound sensor and hydrogen sulfide sensor do not require initialization, as ambient sound levels and H2S concentrations in the air are fairly consistent. The temperature sensor, however, must calibrate to the temperature of the room. This temperature can then be subracted from the fart temperature to find the difference level. The LCD screen reads "Initializing..." during this stage and the LED's are lit.

Main Method:

The Main Method serves three purposes - to obtain button state information, to cycle through states, and to update timers used for beeping. Button state is for the playback feature, and is keeping track of whether the button to initiate this state has been pushed. Beeping timers are reset at the end of the main method to 0 after being count down with an ISR (Interrupt-Service-Routine). The states entered with the main method are:

Standby State - In this stage, a variable "fartDetected" is set to 0. A message reading "FART DETECTOR..." is displayed on the LCD screen. Throughout a while(1) loop in the main method all sensor values are read as fast as possible. Sensor values are read using the on-board ADC, using ADMUX to switch between inputs A0, A1, and A2. These inputs are used for the sound sensor, temperature sensor, and H2S sensor respectively. If in this loop a fart triggers the sensors, record variables are set high for each sensor. The trigger itself is a threshold of both sound and gas concentration, in order to make sure there are no false alarms. Temperature is not included as fluctuations in air temperature may be misleading at times. Once the Record variables are set, an if statement is entered making sure a trigger cannot occur while sensing is in progress. The LCD screen displays "Sensing, wait plz" and the LED's flash while data is being recorded. One thousand samples of sound are stored in an array, while only maximum values of temperature and gas are stored. These stored sound samples can be used for later playback. Maximum values from each sensor are used in determining fart rank. Each sensor is ranked 0-9, with a final rank being determined with an average of sensor ranks. Final rank later determines the frequency used for speaker output. If a rank is high enough, a fan motor is turned on with a high bit. The LCD screen displays all rankings and overall rank, record variables are reset, and the fartDetected variable is set to 1.

Vocalized Rank State - When "fartDetected" is set to 1, the TCCROA register is enabled for sound playback using fast PWM mode on the chip. For voice playback, a .wav file was quantized using Matlab encoder written for a previous year's 476 class, in References. The playback code for the digits 0-9 was also modified for the Mega644, writen for the Mega32. Digits were extracted by chaning a tableSize parameter and a table index. The file could then be played back by changing the OCR0A register to the desired value in the ISR. An 8KHz timer in the ISR makes sure that the file plays back at the sampling rate. When the file is finished playing, registers are zeroed and "fartDetected: is set to 2.

Beeping State - When "fartDetected" is set to 2, the TCCROA register is enabled for set amplitude playback using fast PWM mode on the chip. Desired frequencies are altered by setting a variable timeBeep, which is a measure of group pulse distances. A variable called timeSpeak, on the other hand, is fixed and determines pulse width. This operates in a very similar manner to the previous state, switching over the standby state when completed.

Playback State - When ButtonState() registers that playback is desired and that the current state is standby, "fartDetected" is set to 3. This enters the playback state, which operates similarly to the vocalization state. Data stored in the array from the ADC is played back, with control returning to the standby method after the playback has finished. The LCD screen during this state shows that playback should be occurring.

ISR Timer 0 on Overflow

The interrupt service routine executes at full speed, updating a millisecond and quarter millesecond timer used for audio timing. The ISR is also responsible for generating the 8kHz PWM signal, updating the OCR0A. The ADC could have been triggered in this routine, but instead was updated in the main method using a while loop to determine when ADC's were finished.


This keeps track of whether a button has been pressed, and debounces the button.

The tricky part of writing this code was in timing coordination. As multiple channels were being read, the ADMUX had to be switched continuously. Initially, only one sensor reading was being recorded for all of the sensors. This was due to not letting enough cycles for ADMUX to change, which is only described in documentation of taking about 1 clock cycle. Another issue was in sound playback and sampling rate, something very difficult with the limited memory of the chip. While the vocal rankings could be stored in program memory, sound data had to be recorded to the minimal flash memory. Due to poor speaker and microphone qualtiy, this resulted in a very poor playback of farts. Additionally, there was not enough memory on the chip to make playback sound very good. Possilby a future addition of a flash memory chip and transfer protocol could alleviate this problem if fast enough.

Hardware Details

Hardware design consisted of regulating power, constructing sensor circuits, and user interface circuits such as buttons, LCD screen, fan, and speaker. All circuits are in the Appendices, Appendix B. After initial design, all hardware was combined into an Intensity Detector box for ease of use, as well as attached to a tripod for variable height.

The circuit for the microphone consists of a high pass filter, a dc bias, a gain stage with coupling to reduce ground noise, a high pass filter, and a gain stage. The reason for this setup is to filter out the noise of the signal, bias the signal since the op-amp used has a voltage swing of 0-5V and a signal may be negative, a gain stage to amplify, filter out the noise again, and bias in order to record 0-5V. The exact RC time constants are on the order of ~100Hz, thus filtering out DC signals below this frequency.

The circuit for the hydrogen sulfide sensor is simply a resistor, as the sensor outputs 4ma-20ma and this resistor converts the signal to voltage readable by the MCU. The resistor chosen has to be less than the total loop resistance of 700 ohms, so 330 ohms was used. This gave a sensor reading in a clean room of about 1.35V.

The circuit for the thermistor is a wheatstone bridge. The op-amp is used as a differential amplifier, amplifying the difference between the thermistor and a set dc bias. A gain of 50 is applied to this signal to detect the slightest variations from room temperature.

The output circuit of the lcd was fairly straight forward, except the screen is one line. This required modifying writing half a line at a time since borrowed code assumed 2 lines.

The output circuit of the speaker required amplication due to the poor speaker quality. Since the speaker needs enough current to operate, a pull up push down power circuit was used at the output, with gain variablely chosen by a user to change volume.

Minor circuits of lights and switches were simple to create and do not require much explanation.

The MCU circuit board was provided by ECE 476 and is in the References section. Pins A0-A2 were used for ADC sensor inputs, while Pin A7 was used for a playback button input. Pins B0,B1,and B2 were used for LED outputs, while B3 was used for speaker output. Port C was entirely used for the LCD screen, while Port D was left open for use with hyperterminal.

Below is all combined circuitry before combination into the Intensity Detector box:

After assembly, all sensors were attached to the side of the box. LED's signifying sensor activity are also positioned on this side. A switch for box turn-on, as well as playback button, speaker, and LCD screen appear on the top side of the box. On two other sides, there is a power outlet and a potentiometer used for volume control. On the bottom is a small opening for external wires, such as the MCU programming cable for debugging mode. Also, we noticed a separate ground from the MCU was needed to power on the fan and thus we decided to use a 9V battery attached to the tripod outside along with its fan. Since the battery is not the full 12V, the fan is not at its full CPM power.


Our design, for the most part, executed as planned at the maximum clock speed. There were initially some issues with flicker on the LCD screen, but these had been removed by making all write values the full length of the screen, thus eliminating the need for a clear function. There were some issues regarding timing, however, and these mostly revolved around getting sensor values. Getting samples at the full rate does not allow for most of the sensors to reach a maximum, nor decrease back to normal levels before another fart detection is triggered. This was avoided by introducing some small delays in the code with util/delay.h. As the only user interface regarding human input is a push button for playback, it was not necessary to slow timing to cater to human input.

There was one part of the initial design which we did have trouble implementing fully, which is the playback feature. We were trying to do this with an undersampled fart noise, but all this produced was static. As humans hear very well at 8kHz, if we were to play back 1 second of something sampled at this rate this would need 64,000 bits of data. This is not feasible with the flash memory of one chip. It was also of concern how poor quality our speaker was, which did not seem to play back human voice as clearly as we would have liked from program memory either. Implementing this feature may be better with external memory, but it is questionable whether the recording rate to the external chip would keep up with the desired execution rate.

The main focus of accuracy for our design revolves around the three sensor readings. For the microphone, there was very little noise in relation to detected signal, thus reducing the rate of false alarms. A loud signal could easily generate over a volt difference from the bias. A picture of this is shown below:

For the thermistor, there was some issue of the output saturating when the room temperature was too warm. However, most times this was not the case, and the reading was perfectly detectable. The signal sometimes did not raise very high (mV) and fluctuated with the room temperature. Also, the reading took some time to decrease in amplitude:

For the hydrogen sulfide detector, 4-20mA were output. For detectable signals, this was extremely small. Luckily, it appeared that running this current across a resistor yielded mV detectable voltage differences:

The following shows the entire box set-up along with all the user-friendly features, although without the tripod/fan base.

In terms of enforcing safety in our design, we implemented:

1. Diode-protection from the Power Supply in all circuits (custom board for all 5V, h2s trans board has one, fan has one).
2. Insulation to avoid dangerous shorts through insulating tape and insulating plastic encase interference with other people's designs (e.g. cpu noise, RF interference).
3. Tried to keep EM interference at a minimum by twisting power/gnd wires
usability by you and other people (perhaps with special needs).

Our design itself does not interfere with other people's designs. The device is self contained, plus only measures the area around it. The only possible concern would be audio output, but our speaker does not play any noise above 9kHz.

Usability-wise, the fart intensity detector is very user friendly. The device is glued to a mount which can be inserted and removed from an adjustable tripod. In this way, anyone of any height can use the device. Alternatively, the device can be handheld if removed from the tripod. As well, LED's alert the user to the status of the device, in addition to the speaker and LCD screen. Anyone with a disability for seeing or hearing should thus be able to use the fart intensity detector too.


In the great scheme of things, we are extremely satisfied with our accomplishment. We managed to do ultra-sensitive detection since the chemical emissions, temperature differences and sound levels involved in biological processes always pose a challenge in the accuracy at which detection can be made. We were successful in extending the sensing into full scale ranking based on our theory of how unpleasant farts are. Finally, we were able to integrate all the separate sensors and ciruitry into an insulated and space-efficient tiny box with an impressive level of user friendliness, as can be seen by the various output channels of communicating the user as well as an adjustable station for the user to fart in a carefree fashion.

That being said, we believe we could have added much more functionality, accuracy and amusing features if we have had more design and testing time, and certainly much more budget flexibility. For instance, we could have made the smell ranking based not only on hydrogren sulphide, but also on methane (which occurs in roughly 30% of people) and mercaptans (other sulfur-based components that add to the unpleasant smell of farts). Furthermore, we could have an accurate playback implementation by the use of an external memory chip with a communication protocol to the MCU so that perhaps 1 or 2 seconds of the fart couldn be replayed successfully at roughly 8kHz (60kB of flash memory). Also, we could have made the ranking based on the length of the fart as well, for which external memory would also have been required. We could have even added a GUI interface or large LCD display to show fart statistics such as gas % breakdown, temperature and other such attributes, including the estimated health of an individual deduced by the statistics of their fart. We could have even have extended this detection and ranking mechanism to user tracking. By using air changes in pressure, position sensing and chemical detection, we could have guessed who was the farter when a fart is detected in the air! The possibilites are endless!

Our design conform with the applicable standards because no EM transmission actually occurred when voice frequency was converted into acoustic waves. Also, EM interferance was minimized by twisting the wires, so that not even through insulated wires could our unfiltered voltages of frequencies above the allowed voice frequency could be transmitter unintentionally. Also we conformed with the H2S standards by avoiding the testing of the H2S sensor by other than through human byproducts such as bad breath and actual farts, which can be guaranteed to be inherently harmless. We also made sure to read the datasheet of the H2S 4mA-20mA transmitter board, ensuring that it followed the standards to test H2S safely. We understood that this was the case, as long as no AC power was sent to the sensor, and as long as the temperature does not exceed the maximum working temperatue (well above 150 F), the acid inside will not leak out.

When designing our detector, we made sure to acknowledge all the code and circuit designs obtained from third parties. This included only ECE 476 material from this year (LCD code, uart code, conditional fan circuit) and the previous year (speech generation from a wav file though using up programming memory). Please see References section below for further information. In our project, we did not use any other form of code or design in the public domain neither did we reverse engineered a design. In fact, we had to sign a terms of sale/samples sheet for the H2S sensor specifying that we will not attempt to reverse engineer the design and that they are not responsible for the user not following the specified working conditions.Also, as mentioned in the High Level Design, our design does not violates any exisiting patent or trademark.

However, even though our project does not violate any existing intellectual property and it's probably one of the most original projects among all ECE 476 final projects, its lack of essential applications other than as a high tech toy and a proof-of-concept, makes publishing and patenting opportunities rather limited. The only opportunity we might have is that With some marketing magic, our device could be used in the entertaining industry. It is important to note that We are aware our project is not going to get us the noble prize, but we expect that this will at least go into the hall of oddities. Jokes aside, perhaps we this could help us in developing a machine that by determining the quality of a patient's flatulence, we could estimate how their diet is determining their health. Another bio-application might be that next time, we might want to measure other body odors and ranking its quality and quantity, studies could be conducted to draw correlations between that and health, life expectancy and other factors of the population, perhaps as a function of gender, ethnicity and/or age.

Another important aspect worth mentioning is how the IEEE code of Ethics, by which the members of this groups have pledged to follows as active IEEE members of the Cornell chapter, relates to this device and its uses. The code of ethics of IEEE clearly states that the designers of a device accept full responsibility for the safety of their design. We tried our best to insulate connections, and put our entire device in a insulated case precisely because of this. Furthermore, our device attempts albeit not fully functionally, to record a fart. If we had taken it a step further, we could have linked the farts to people's name and then, the possibility of black mailing would require us to explicitly mantain the highest ethical standards.In accordance to the code, we have not hidden or made up data that did not exist. The sensors do not artificially recreate pseudo-random results, but instead record to the best of their abilities the actual intensity based on biasing and threshold levels. This is important to note because with the use of the software, a lot this questionable fixes to a sensor-based project could be made.

Also, in accordance to the code of ethics, we vow to reject any bribery and dishonest use of our acquired knowledge. In fact, we did this project not only because it was an entertaining idea, but because we could prove first hand how sensors could interact with the MCU. This deepens our understanding of technology and we expect to pass it to our student coworkers, as well as cultivate our and their minds.Finally, we vow to remain open to criticism and open to new ideas and suggestions. In fact, we would welcome anyone willing to contact us (see our emails on the top of the page) with their feedback on the project and we would be extremely proud if the project serves as inspiration for other such interesting projects on the subject matter. As students of ECE 476 at Cornell University, we believe our understanding of ECE has greatly deepen and look forward to solve the problems in our field that the future beholds for us.


APPENDIX A - Commented Software

//Mega644 ECE 4760 Final Project
//Fart Intensity Detector
//Robert Clain rac82
//Miguel Salas mos22

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <string.h>
#include "lcd_lib.h"
#include "uart.h"
#include <util/delay.h>
#include <math.h>
#include "DPCMAllDigits.h"

// UART file descriptor
// putchar and getchar are in uart.c
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
//lcd inits
const int8_t LCD_init1[] PROGMEM = "INITIALI";
const int8_t LCD_init2[] PROGMEM = "ZING....";

const int8_t LCD_standby1[] PROGMEM = "FART DET";
const int8_t LCD_standby2[] PROGMEM = "ECTOR!!!";

const int8_t LCD_detect1[] PROGMEM = "Sensing,";
const int8_t LCD_detect2[] PROGMEM = "wait plz";

const int8_t LCD_sound[] PROGMEM = "S:  ";
const int8_t LCD_temp[] PROGMEM = "T:  ";
const int8_t LCD_gas[] PROGMEM = "G:  ";
const int8_t LCD_overall[] PROGMEM = "R:  ";

const int8_t LCD_playb1[] PROGMEM = "PLAYBACK";
const int8_t LCD_playb2[] PROGMEM = "        ";

int8_t lcd_buffer[2];    // LCD display buffer

//time for tasks
#define tS 2
#define tO 20000
//variable frequency of pulses, vary according to fart scale readout
signed int tB=4000;
//total time beeping occurs after fart detected
volatile signed int timeOutput;
//variable which goes high if a fart is detected
unsigned char fartDetected;

//all the usual mcu stuff 
void initialize(void); 

void ButtonState(void);
unsigned char PushState;
//index for playback, which is fartdetected = 3;
volatile signed int m;

//timer for counting .24ms
volatile signed char countQMS;
//timer for counting 1ms
volatile signed char countMS;
//timer for sound generation
volatile signed int timeSpeak;
//timer for beeping
volatile signed int timeBeep;
//timer for fart recording and playback
volatile signed int fartTime;

//table for storing recorded sounds
signed int SoundRec[1000];
//index for SoundRec
signed int i;
//variable to tell whether to start recording
char RecordS;

//raw A to D number
signed int Sin ; 
//max value
signed int Smax;
//1-10 sound
signed int scaleS;

//index for TempRec
signed int j;
//variable to tell whether to start recording
char RecordT;

//raw A to D number
signed int Tin ; 
//max value
signed int Tmax;
//1-10 temp
signed int scaleT;
//initial temperature readout
signed int InitTemp;

//index for SoundRec
signed int k;
//variable to tell whether to start recording
char RecordG;

//raw A to D number
signed int Gin ; 
//max value
signed int Gmax;
//1-10 sound
signed int scaleG;    

//scale for overall rating
signed int scaleO;    

//init some variables for digit sounds

unsigned int TableSize=13580; //refers to the following incl file
//Contains the packed 2-bit codes for syntehsis
//Generated by the program Make2code476.m
//#include "DPCMcode.h"

//reconstruction differentials 
//flash char PCMvalue[4] = {-78, -16, 16, 78};
volatile signed char PCMvalue[4] = {-20, -4, 4, 20};

volatile unsigned int outI, tableI;          //indexes
volatile unsigned char cycle ;              //decode phase counter
volatile signed char out, lastout;        //output values
volatile signed char p1, p2, p3, p4     ;    //hold 4 differentials
volatile signed char packed    ;            //byte containing 4 2-bit values    
volatile char t1, t2;

//timer 0 overflow ISR
ISR (TIMER0_OVF_vect) 
        if(t2 == 100){
            t2 = 0;
            OCR0A = SoundRec[m];
            //fprintf(stdout, "%d \n\r", m);
            TCCR0A = 0;

        if (t1 == 8) {
    //fprintf(stdout, "ROB BURPS\n\r") ;
    //compute next sample
        cycle = outI%4;
            if (cycle==0)      //do we need to unpack more data?
                if (tableI<TableSize)  //end of stored wave?
                    //unpack a table entry
                    //uint8_t packed;
                    packed = pgm_read_byte(((uint16_t)(DPCMAllDigits)) + tableI);
                    //fprintf(stdout, "Byte Read: %d \n\r", packed);
                    p1 = (packed & 192)>>6;
                       p2 = (packed & 48)>>4;
                    p3 = (packed & 12)>>2;
                    p4 = (packed & 3); 
                    tableI++ ; 
                } //} unpack table entry 
             //compute the output and send to PWM
                out = lastout + PCMvalue[p1] - (lastout>>4) ;      
             else if (cycle==1)    //don't need to unpack yet--just ouput
                 out = lastout + PCMvalue[p2] - (lastout>>4) ;
              else if (cycle==2)
                  out = lastout + PCMvalue[p3] - (lastout>>4) ; 
              else if (cycle==3) 
                  out = lastout + PCMvalue[p4] - (lastout>>4) ;
            //update outputs
            OCR0A = out + 160; //output
            lastout = out; //saves previous output
            outI++; //update index
            t1 = 0;

            //at end, turn off TCCROA
            if (tableI==TableSize) {
                TCCR0A = 0;
            //fprintf(stdout, "%d %d %d %d %d %d %d %d %d\n\r",out, cycle, outI, packed, 
			tableI, PCMvalue[p1], PCMvalue[p2], PCMvalue[p3], PCMvalue[p4]) ;
            //fprintf(stdout, "To OCR0A: %d \n\r", out); 

      // generate time base for MAIN
    // 15 counts is about .24 mSec
    if (0 == countQMS)
    //when beeping, start timers!
        --timeSpeak;    //in .24 mSec
    //milsec counter for playback attempt
    if (0 == countMS){

//Entry point and task scheduler loop
int main(void){

  //light LEDs B1,2,4
  PORTB |= 0b00010110;
  ADMUX = 1;
  ADCSRA |=  (1<<ADSC); 
  while (ADCSRA & (1<<ADSC));
  InitTemp = ADC;
  ADMUX = 0;
  PORTB &= 0b11101001;
  CopyStringtoLCD(LCD_standby1, 0, 0);
  CopyStringtoLCD(LCD_standby2, 0, 1);
  // tasks scheduler and measure/display loop
      //button state machine

        CopyStringtoLCD(LCD_playb1, 0, 0);
          CopyStringtoLCD(LCD_playb2, 0, 1);
        OCR0A = 128;
        TCNT0 = 0;
        TCCR0A = (1<<COM0A0) | (1<<COM0A1) | (1<<WGM00) | (1<<WGM01) ;
        while (TCCR0A>0){};
        CopyStringtoLCD(LCD_standby1, 0, 0);
          CopyStringtoLCD(LCD_standby2, 0, 1);
        //fprintf(stdout,"playback \n\r");
        fartDetected = 0;

    //beeping code
        //pulse is half a period
        //dont pulse if in first half or if speaker shouldnt be playing
                TCCR0A = 0;
                //pulse PWM in second half
                 TCCR0A = (1<<COM0A0) | (1<<COM0A1) | (1<<WGM00) | (1<<WGM01) ;
        fartDetected=0;//go back to sensing
        timeOutput=tO; //lcd screen flash time
        PORTB &= 0b11111110;
          CopyStringtoLCD(LCD_standby1, 0, 0);
          CopyStringtoLCD(LCD_standby2, 0, 1);

    //digits speak rank
    if (fartDetected==1){
        OCR0A = 128;
        TCNT0 = 0;
           TCCR0A = (1<<COM0A0) | (1<<COM0A1) | (1<<WGM00) | (1<<WGM01);
          //init the ouptut value
           lastout = 128; 
        while (TCCR0A>0){};
        OCR0A = 128;

    //sensing state
    if (fartDetected==0){
            //get the sound sample
            ADCSRA |=  (1<<ADSC); 
            while (ADCSRA & (1<<ADSC)); //makes sure no recording by mistake
             Sin = ADC-512;

            //start another conversion
            //fprintf(stdout, "%d\n\r", Ain);
            if((Sin<=-80 || Sin>=80) && (Gin>=160)  && (RecordS==0) && (RecordT==0) && 
		RecordS = 1;
                RecordT = 1;
                RecordG = 1;
                  CopyStringtoLCD(LCD_detect1, 0, 0);
                  CopyStringtoLCD(LCD_detect2, 0, 1);
            //flash LEDs
            PORTB |= 0b00010110;
            //records ADC data
            //record 1000 samples

                if(i<=999 && i>=0){
                    SoundRec[i] = Sin;
                    if(i%8 == 0) PORTB |= 0b00010110; //LED flash
                    if(i%8 == 1) PORTB &= 0b11101001; //LED flash
                    //obtain max
                    RecordS = 0;//stop recording when done with 1000 samples
                    i = 0;

                    fartDetected = 1; //starts speaking digit

                    //FART INDEX FOR SOUND
                    if(Smax<=110) {scaleS = 0; tB = 4000;}
                    if(Smax>110 && Smax<=140) {scaleS = 1; tB = 3500;}
                    if(Smax>140 && Smax<=170) {scaleS = 2; tB = 3000;}
                    if(Smax>170 && Smax<=200) {scaleS = 3; tB = 2500;}
                    if(Smax>200 && Smax<=230) {scaleS = 4; tB = 2000;}
                    if(Smax>230 && Smax<=260) {scaleS = 5; tB = 1500;}
                    if(Smax>260 && Smax<=290) {scaleS = 6; tB = 1000;}
                    if(Smax>290 && Smax<=320) {scaleS = 7; tB = 800;}
                    if(Smax>320 && Smax<=350) {scaleS = 8; tB = 600;}
                    if(Smax>350) {scaleS = 9; tB = 300;}
                    sprintf(lcd_buffer,"%1d",scaleS);    //format string                  

                    LCDGotoXY(0, 0);
                    CopyStringtoLCD(LCD_sound, 0, 0);
                    LCDstring(lcd_buffer, strlen(lcd_buffer));    // display the scale
                    Smax = 0;

                    //FART INDEX FOR TEMP
                    if(Tmax<=40) {scaleT = 0;}
                    if(Tmax>40 && Tmax<=80) {scaleT = 1;}
                    if(Tmax>80 && Tmax<=120) {scaleT = 2;}
                    if(Tmax>120 && Tmax<=160) {scaleT = 3;}
                    if(Tmax>160 && Tmax<=200) {scaleT = 4;}
                    if(Tmax>200 && Tmax<=240) {scaleT = 5;}
                    if(Tmax>240 && Tmax<=280) {scaleT = 6;}
                    if(Tmax>280 && Tmax<=320) {scaleT = 7;}
                    if(Tmax>320 && Tmax<=360) {scaleT = 8;}
                    if(Tmax>360) {scaleT = 9;}
                    sprintf(lcd_buffer,"%1d",scaleT);    //format string                  

                    LCDGotoXY(4, 0);
                    CopyStringtoLCD(LCD_temp, 4, 0);
                    LCDstring(lcd_buffer, strlen(lcd_buffer));    // display the scale

                    //FART INDEX FOR GAS
                    if(Gmax<=180) {scaleG = 0;}
                    if(Gmax>180 && Gmax<=190) {scaleG = 1;}
                    if(Gmax>190 && Gmax<=200) {scaleG = 2;}
                    if(Gmax>200 && Gmax<=210) {scaleG = 3;}
                    if(Gmax>210 && Gmax<=220) {scaleG = 4;}
                    if(Gmax>220 && Gmax<=230) {scaleG = 5;}
                    if(Gmax>230 && Gmax<=240) {scaleG = 6;}
                    if(Gmax>240 && Gmax<=250) {scaleG = 7;}
                    if(Gmax>250 && Gmax<=260) {scaleG = 8;}
                    if(Gmax>260) {scaleG = 9;}
                    sprintf(lcd_buffer,"%1d",scaleG);    //format string                  

                    LCDGotoXY(0, 1);
                    CopyStringtoLCD(LCD_gas, 0, 1);
                    LCDstring(lcd_buffer, strlen(lcd_buffer));    // display the scale

                    //FART INDEX FOR OVERALL

                    //Overall Ranking is average
                    scaleO = floor((scaleS+scaleT+scaleG)/3);

                    if(scaleO==0){ tableI=0; TableSize=1358;}
                    if(scaleO==1){ tableI=1358; TableSize=2716;}
                    if(scaleO==2){ tableI=2716; TableSize=4074;}
                    if(scaleO==3){ tableI=4074; TableSize=5432; PORTB |= 0b00000001;}
                    if(scaleO==4){ tableI=5432; TableSize=6790;}
                    if(scaleO==5){ tableI=6790; TableSize=8148;}
                    if(scaleO==6){ tableI=8148; TableSize=9506;}
                    if(scaleO==7){ tableI=9506; TableSize=10864; PORTB |= 0b00000001;}
                    if(scaleO==8){ tableI=10864; TableSize=12222; PORTB |= 0b00000001;}
                    if(scaleO==9){ tableI=12222; TableSize=13580; PORTB |= 0b00000001;}
                    sprintf(lcd_buffer,"%1d",scaleO);    //format string                  

                    //turn LED's off
                    PORTB &= 0b11101001;

                    LCDGotoXY(4, 1);
                    CopyStringtoLCD(LCD_overall, 4, 1);
                    LCDstring(lcd_buffer, strlen(lcd_buffer));    // display the scale

            ADMUX = 1;
            //get the temp sample
            //start another conversion
            ADCSRA |=  (1<<ADSC); 
            while (ADCSRA & (1<<ADSC)); //makes sure no recording by mistake
            Tin = ADC-InitTemp; //room temperature set as base through testing

            //fprintf(stdout, "%d\n\r", Tin);
                if(j<=999 && j>=0){
                RecordT = 0;
                j = 0;
                Tmax = 0;
            //fprintf(stdout, "%d\n\r", TempRec[0]);

            ADMUX = 2;

            //get the gas sample
            //start another conversion
            ADCSRA |=  (1<<ADSC);
            while (ADCSRA & (1<<ADSC)); //makes sure no recording by mistake
            Gin = ADC-260; //5/1024*278 = 1.357V, 4.11ma across 330 resistor

            //fprintf(stdout, "%d\n\r", Tin);

                if(k<=999 && k>=0){
                    RecordG = 0;
                    k = 0;
                    Gmax = 0;
            //fprintf(stdout, "%d\n\r", TempRec[0]);

            ADMUX = 0;
            fprintf(stdout,"%d %d\n\r",fartDetected, InitTemp);

        //reset timeSpeak, timeBeep and reset TCCR0A
            TCCR0A = 0;
            timeSpeak= tS;

        //fprintf(stdout, "%d %d\n\r", Record, fartDetected);    
    }//end while(1)
} //end main 
//Set it all up
void initialize(void){
  //set up PWM
  //run timer 0 at full speed
  TCCR0B= 1; //0b00000001;    
  //turn on overflow ISR
  TIMSK0 = (1<<TOIE0);
  //init the task timers
  t1 = 0;
  t2 = 0;

  //init fart detect
  fartDetected = 0;
  i = 0;
  RecordS = 0;

  j = 0;
  RecordT = 0;

  k = 0;
  RecordG = 0;

  Smax = 0;
  Tmax = 0;
  Gmax = 0;
  DDRD = 0xff; // make PORT D output for hyperterminal
  PORTD = 0x00;
  DDRB = 0xff; // make PORT B an output for the speaker
  PORTB = 0x00;
  DDRC = 0xff; // make PORT C an output for the LCD screen
  PORTC = 0x00;
  DDRA = 0x00; // make PORT A an input for sensors

  //init the UART -- uart_init() is in uart.c
  stdout = stdin = stderr = &uart_str;
  //LCD initializations
  LCDinit();    //initialize the display
  LCDclr();        //clear the display

  CopyStringtoLCD(LCD_init1, 0, 0);
  CopyStringtoLCD(LCD_init2, 0, 1);

  //init the A to D converter 
  //channel zero/ left adj /EXTERNAL Aref
  //!!!CONNECT Aref jumper!!!!
  ADMUX = 0;//(1<<ADLAR);  
  //enable ADC and set prescaler to 1/1*16MHz=16,000,000
  //and clear interupt enable
  //and start a conversion
  ADCSRA = ((1<<ADEN) | (1<<ADSC)); 
  //init PushState
  //crank up the ISRs


//Debounce push button for playback
//playback on pressing button to pin A7
void ButtonState(void) 
  switch (PushState)
       case 1:
        if (PINA & 0x80) PushState=2;
        else PushState=1;
     case 2:
        if (PINA & 0x80) 
        else PushState=1;
     case 3:  
        if (PINA & 0x80) PushState=3; 
        else PushState=4;    
     case 4:
        if (PINA & 0x80) PushState=3; 
           if(fartDetected==0 && RecordS==0) {fartDetected=3;}

//nopush=1, maybepush=2, pushed=3, maybenopush=4

APPENDIX B - Schematics

Microphone Circuit Schematic
Thermistor Circuit Schematic
H2S Sensor Circuit Schematic
Speaker Circuit Schematic
Fan Schematic
LED Schematic
Regulator Schematic
Button Circuit Schematic

APPENDIX C - Cost Details

The following is an expense table with each of the major components in our device. The only ones not included are resistors and capacitors, which are trivial.

Part number Vendor Price towards Budget
MCU assembly
ATmega644 ATMEL $ 0.00 (Donated)
Custom PC Board PH 238 Electronics Lab $ 4.00
RS323 connector PH 238 Electronics Lab $ 1.00
MaX233CPP MAXIM $ 0.00 (Donated)
40 header socket pins PH 238 Electronics Lab $ 2.00
AC-to-DC power supply PH 238 Electronics Lab $ 5.00
12V regulator PH 238 Electronics Lab $ 0.00 (surplus)
9V battery PH 238 Electronics Lab $ 2.00
Input circuitry
H2S-AH sensor + transm. board ALPHASENSE $ 0.00 (Donated)
NF51E Precision NTC Thermistor DIGIKEY $ 2.00
Electret Microphone PH 238 Electronics Lab $ 0.00 (surplus)
LM358 Op-Amp X 3 PH 238 Electronics Lab $ 3.00
RP3508 Red pushbutton switch DIGIKEY $ 7.86
General purpose push-button PH 238 Electronics Lab $ 1.00
Output circuitry
S01601DTR LCD display DIGIKEY $ 7.50
GA0281M Speaker DIGIKEY $ 2.68
FBA06A12U1A 12V DC Fan DIGIKEY $ 8.68
Potentiometer PH 238 Electronics Lab $ 0.00 (surplus)
NPN Transistor PH 238 Electronics Lab $ 0.00 (surplus)
PNP Transistor PH 238 Electronics Lab $ 0.00 (surplus)
BUZ73 SIPMOS Power Transistor PH 238 Electronics Lab $ 0.00
4N35 phototransistor optocoupler PH 238 Electronics Lab $ 0.00 (surplus)
Green LEDs x 3 PH 238 Electronics Lab $ 0.00 (surplus)
Mechanical Interface
Compact Adjustable Tripod eBAY $ 5.95
Black Plastic Enclosure eBAY $ 5.99
Total cost towards budget: $ 61.00

APPENDIX D - Division of Work

The design and implementation of the project was done jointly in almost every aspect of the work. This is the result of working on the lab simultaneously on almost all design problems. The main difference lies in the general aspects of the parts that the members worked on while solving a design problem

Robert Clain: Focus on software design challenges such as code development, ADC hardware-software interface implementation and its debugging.

Miguel Salas: Focus on hardware design challenges such as specific circuit design, soldering and negotiating with vendors for free samples.

No Further project breakdown can be given, since both members worked on the same problem at different levels of abstraction at the same time.



The following are the datasheets for the components used in the fabrication of the device:

ATmega644 Microcontroller
H2S sensor with 4mA-20mA transmitter board
GA0381M Speaker
Electret Microphone
S01601DTR 1-line LCD display
FBA06A12U1A 12V DC Fan
4N35 phototransistor optocoupler
Max233CPP RS-232 Driver/Receiver
RS-232 Connector
NF51E High Precision NTC Thermistor
2N3906 PNP transistor
2N3904 NPN transistor
LM340-T12 12v Regulator
LM358 Op-Amp
BUZ73 SIPMOS Power Transistor
1N4001 Diode Rectifier
RP3508 Red pushbutton switch
General purpose push button
General purpose LEDs


PH 238 Electronics Laboratory


We used reference code from previous lab assigments, specifically this year's LCD test program (including its library files, lcd_lib.c and lcd_lib.h) and the ADC test program.

Also, we used code provided for last year's class on speech generation. We used a reference 0-9 Digit speech testing program and its header file DPCMAllDigits.h, which contained the decoded data from a .wav file to speak the digits from 0 to 9 using MATLAB and Codevision code for the atmega32. We modified to make it playable in the ATmega644 MCU.

Moreover, to aid us in the testing phase, hyperterm availability was made possible thanks to the provided uart.c and the uart.h files. The rest of the code was built from scratch based on our acquired knowledge of previous labs.

Finally, we borrowed two hardware schematics, those for the construction of the MCU board and the Fan motor control circuit from a previous lab, both provided by ECE 476.


The following websites contained useful information, initially in obtaining the feasibility of the project and then in understanding how to proceed with it.

Dr. Fart Speaks is an article by a renown flatulence expert Dr. Michael D. Levitt. We found out that the average human male farts 110 milliliters of gas per emission.

Wikipedia:Flatulence is the wikipedia article documenting the gas breakdown in a fart, as well as noting that odorous gases such as Hydrogen Sulfide and Mercaptans are responsible for the smell, even though they account for less than 5% of the content.

Breakdown of gases in the air shows the amount of each gas found naturally in "unfarted" air. This sets the boundaries for chemical gas detection.

Facts on Farts was definitely the most informative of all websites. It documented why farts smell, get warm and even why they make sound. Definitely worth taking a look!

For your reference, here is how a level 9 (highest ranking) fart would be like:
Dont lie. I am sure you have experienced one of these first hand before :)