A Fan control system with ATmega2560 Microcontroller

The following project was developed during a class taken at Federal University of Rio Grande do Norte with professor Marcelo. The group was composed by GiovannaJoãoRaí and me.

The group goal was to implement a drying grain system. The process of drying should start with the press of a button, after that, a fan speed is controlled based on a “dry curve” and the reading of a luminosity and temperature sensor. With 3 minutes the routine is finished, and the machine should wait for another button press to start the system again.


The hardware of the control system consists of:

  • An ATmega2560 Microcontroller
  • An LDR to sense the luminosity
  • An NTC sensor for the temperature
  • A Push-Button to start the system
  • LEDs for behavior visualization
  • A “4N25” Photocoupler to protect the Microcontroller from the load
  • An NPN transistor with a diode for circuit protection
  • Power supply to the Fan
  • Some resistors

The last picture represents the components, an Arduino Mega board is used to facilitate communication with the microprocessor, the DC motor illustrates the fan. Since the LDR and NTC sensors are a variable resistance for the desired domain, a voltage divider is applied to convert the input voltage to a measured voltage. An operational range should be specified in this type of configuration, in the example of the NTC temperature sensor, the fixed resistor was chosen based on its measured resistance at the ambient temperature which, for 25 °C is approximately 50Ω.


Source: https://itp.nyu.edu/archive/physcomp-spring2014/sensors/uploads/Schematics/

A Pull-Up system is used with the Push-Button to properly read its state. Pushing the button makes the ground to overpower the power supply because of the Pull-Up resistance.


Source: http://www.starting-point-systems.com/forum/viewtopic.php?id=3

The Photocoupler IC separates the load from the controlling system as shown in the following picture, the transistor is sensitive to variations in the LED-photodiode luminosity and act as a switch based on a given threshold. The isolation prevents the control system not to be damaged by the high voltage on the load system.

Source: https://media.digikey.com/pdf/Catalog%20Drawings/Optoelectronics/

Finally, The NPN transistor gets the signal from the output of the Photocoupler and controls the Fan speed from a PWM signal sent by the ATmega2560. The picture below illustrates the circuit, with the difference that it is using a PNP transistor, which flips the “ON and OFF” logic.

Source: https://cdn.sparkfun.com/assets/learn_tutorials

With the hardware set, a C program is developed to manage the microcontroller signals to control the specified system. The code can be accessed at https://github.com/YangTavares/Embedded-Systems/blob/master/grain_drying_sys/grain_drying/grain_drying.ino

Inside the ATmega microcontroller, there are different hardware components apart from the CPU. For this project, we used the Timer hardware to accurately count the time based on the board clock (16Mhz) and to control the PWM frequency and duty cycle. The ADC hardware was used to read the analog data from the sensors, and we used the USART interface to visualize the real-time data through Matlab. The details of the registers and the whole ATmega IC system can be found in its datasheet, which is also at:


Source: ATmega2560 DataSheet

The following registers configuration were chosen based on the project specifications and the datasheet information. The “Clear Timer on Compare Match (CTC)” operation mode was chosen to reset the timer when the count value reaches the “OCR3A” register value, also when both values are equal, an interruption flag is activated, the interruption breaks the main program execution to run an interrupt handler (ISR) at the same time the interruption occurs. The interrupt event is important to correctly increment a variable representing a time count (in this example) at fixed time intervals. This logic applies to other applications.

The period between Compare Matches on the timer was configured to be 0,25 seconds with the equation below. The clock frequency is 16Mhz, and the set pre-scale is 64. The pre-scale divides the clock frequency by a constant factor, 64 in this case.


TCCR3A = 0b11000000;
TCCR3B = 0b00001011;
TIMSK3 = 0b00000011;

OCR3A = 0xF234;

Source: http://info.atollic.com/hubfs/Blog/

Both Timers 0 and 1 were configured to drive the PWMs ports. The registers were configured to “Fast PWM” mode with the inverting mode. The timers maximum count is 0xFF, and the clock pre-scale is 64.

TCCR0A = 0b11000011;
TCCR1A = 0b11111101;

TCCR0B = 0b00000011;
TCCR1B = 0b00001011;

The ADC where configured to use an internal reference “AVCC” for the conversion reference and a pre-scale of 128. 

ADMUX = 0b01000000;
ADCSRA |= 0b10000111;

To perform a reading from a sensor, the multiplexer needs to be set to a specific input, since the ATmega2560 only has one ADC. The “read_adc” function starts the ADC reading process, which waits for it to end with a while loop. In the example below, first the A0 port is selected, the reading process begins, and the reading value is represented with a 10-bit value “ADC” register, it is shifted twice to the left to comprise the 8-bit methodology followed on the proposed code.

ADMUX &= 0b11110000; //A0
light = ADC >> 2;

void read_adc(){
  while(!(ADCSRA & (1<<ADIF)));
  ADCSRA |= (1<<ADIF);

In this system, another interruption is configured based on the push of the button. The “sei();” function from the AVR library enables all the interruptions on the board to be used. The interruptions are handled with an “ISR()” function, the parameter passed to this function indicates which signal it is waiting. In the following example, the button interruption is configured, and the global interruptions are enabled, the first ISR is waiting for the button signal and the second for the timer period of reset which happens every 1/4 seconds.

EIMSK |= 1<<INT0;
DDRD &= 0;

  EIMSK &= 0b11111110;
  PINB |= 0b00010000;
  count++; //0.25 seconds

When the button is pressed, a flag is set up to indicate the start of the system, the button interruption is disabled to not interfere with the running process, and a LED is turned on to indicate the start.

After the system is started, the fan needs to have its speed controlled based on the following equation (arbitrary):

w(t) =(p(t)/100) * (γ/v1(t)) -β*v2(t)

The p(t) function is called the “drying curve”, it should follow the curve below:


Each equation of the straight lines was calculated for each interval, and the following code represents it. In the end, the runtime flag is set to 0, and the execution finishes.

minute = count/240.0;

if(count < 120){ // < 0,5 min
  f_secagem = 0.9*minute;
else if(count >=120 && count < 360){ // < 1,5 min
  f_secagem = 0.45;
else if(count >= 360 && count < 480){ // < 2 min
  f_secagem = 0.7*minute - 0.6;
else if(count >=480 && count < 648){ // < 2,7 min
  f_secagem = 0.8;
else if(count >=648 && count <= 720){ // < 3 min
  f_secagem = -2.6667*minute + 8.0;

The Beta and Gama constants were calculated based on the possible maximum and minimum values of the fan function. We observed that the temperature sensor was varying between 142 and 150 (from 0 to 255) and the light in the full range of 0 to 255.

255 =(80/100 ) * (γ/142) -β*0  

0 =(0/100 ) * (γ/150) -β*255  

w(t) =(((p(t)/100 ) * (45262/v1(t)) -1*v2(t))+255)/2

With the working parts set, the USART communication was configured to send data through USB with a “BAUD_RATE” of 9600 and a “character_size” of 8-bits representing the transmission buffer size, characters are put in the buffer “UDR0” to be transmitted.

void USART_init(void){
  UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
  UBRR0L = (uint8_t)(BAUD_PRESCALLER);
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);
  UCSR0C = (3<<UCSZ00);

void USART_send( unsigned char data){
  while(!(UCSR0A & (1<<UDRE0)));
  UDR0 = data;

void USART_putstring(char* StringPtr){
  while(*StringPtr != 0x00){

The data is formatted into one string line containing information about the sensors, the fan PWM value, and the runtime:

char int_str[1000];
sprintf(int_str, "%d", light);
USART_putstring(" ");
sprintf(int_str, "%d", temperature);
USART_putstring(" ");
sprintf(int_str, "%d", (int)f_fan);
USART_putstring(" ");
sprintf(int_str, "%d", (int)(minute*60.0));

A Matlab interface was developed to interpret the data coming from the USB:


The code gets the data from the microcontroller by accessing the “ttyACM0” file. The tail application retrieves the last line of the file resulting in the most recent data transmitted.

[q,w] = system(['tail -n ','1',' ','/dev/ttyACM0']);

The data is then split into multiple variables:

at_value = strsplit(w);

light = str2num(at_value{1});
temperature = str2num(at_value{2});
out_value = str2num(at_value{3});

And plotted:

t = [t_inst ; t];

legend(strcat('Light: ',at_value{1}));

legend(strcat('Temperature: ',at_value{2}));

legend(strcat('Fan value: ',at_value{3}));

The making process and project visualization can be seen in the following pictures:



The video at the link: https://youtu.be/i8i3G4AsIRU has the working circuit at the beginning and the Matlab real-time plot at the end.

Folder with the files used in this project:


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s