6. Coding

6.1. File Download

COM code files
EFC code files

All codes are written in C and complied and uploaded using avr-dude and git make commands. Both system unit are based in Atmega328. In code folders a text file for lfuse value is included. Note that in both units 7.3-8.1 MHz calibrated internal RC oscillator is used.

**atmega328**
CKDIV8, CKOUT, SUT1, SUT0, CKSEL3, CKSLE2, CKSLE1, CKSLE0 = 11100010 (0xE2)

CKDIV8 = 1
OFF

CKOUT = 1
OFF

SUT1:0 = 10
Start-up Time from Power-down = 6 CK
Additional Delay from Reset = 14CK + 65 ms

CKSEL3:0 = 0010
Frequency Range (MHz) = 7.3-8.1 Mhz (Calibrated Internal RC Oscillator)

6.2. Carbon Monoxide Monitoring Unit Code

The first part in code includes all header files needed.

#include <avr/io.h>
#include <util/delay.h>
#include <math.h>
#include "pins.h"
#include "nrf24L01Tx.h"
#include "lcd4bit.h"

Three header files were created. The first file “pins.h” assigns names to all used pins to make coding easier. The code snippet below shows an example from pins.h file where new names for LCD module pins are defined.

//LCD
#define D4_port PORTC //D4
#define D4_dir DDRC
#define D4_pin PC3

#define D5_port PORTD //D5
#define D5_dir DDRD
#define D5_pin PD2

#define D6_port PORTD //D6
#define D6_dir DDRD
#define D6_pin PD1

#define D7_port PORTD //D7
#define D7_dir DDRD
#define D7_pin PD0

#define E_port PORTC //E
#define E_dir DDRC
#define E_pin PC4

#define RS_port PORTC //RS
#define RS_dir DDRC
#define RS_pin PC5

The second “nrf24L01Rx.h” where the functions to operate the wireless module are defined. The code snippet below shows the nrf24_init() function used to initialize the wireless module.

/* init the hardware pins */
void nrf24_init(){
    set(CS_port,CS_pin);
    output(CS_direction,CS_pin);
    clear(CE_port,CE_pin);
    output(CE_direction,CE_pin);
    clear(SCK_port,SCK_pin);
    output(SCK_direction,SCK_pin);
    clear(MOSI_port,MOSI_pin);
    output(MOSI_direction,MOSI_pin);
    clear(MISO_direction,MISO_pin);            //set MISO as input

    //nrf24_setupPins();
    clear(CE_port, CE_pin);
    set(CS_port, CS_pin);
}

System variables are defined in 4 lines. Ro and RL are used in CO sensor circuit (check here). PPM_max and PPM_min defines the CO levels in PPM at which system will turn the fan on and off (Check Exposure Limits).
Any time sensor is calibrated using the controlled 100 PPM CO environment, the new value of Ro can be fed here and will be used to calculate PPM.

float Ro = 5746.0;
float RL = 10000.0;
int PPM_max = 200;
int PPM_min = 60;

The CO level is updated each reading cycle (150 seconds). The code snippet below shows the part where the reading is converted into digital value and decisions are taken accordingly.

ADCSRA |= (1 << ADSC); ////ADC start = on
while((ADCSRA & (1 << ADSC))){}  //if(!(ADCSRA & (1 << ADSC)))
PPM = calculatePPM(ADC);
LEDs(PPM);
if(PPM >= PPM_max){
    operateFan(200);
    Lcd4_Set_Cursor(1,13);
    Lcd4_Write_Char('1');
    buzzer_port |= (1 << buzzer_pin);
}
if(PPM < PPM_min && fanMode == 'A'){
    operateFan(100);
    Lcd4_Set_Cursor(1,13);
    Lcd4_Write_Char('0');
    buzzer_port &= ~(1 << buzzer_pin);
}
itoa(PPM, buffer, 10);
if(PPM < 10) Lcd4_Set_Cursor(2,6);
else if(PPM < 100) Lcd4_Set_Cursor(2,5);
else if(PPM < 1000) Lcd4_Set_Cursor(2,4);
else if(PPM < 10000) Lcd4_Set_Cursor(2,3);      
Lcd4_Write_String(buffer);

The function “calculatePPM” takes the ADC value and finds PPM level in PPM.

int calculatePPM(int VL){
    float Rs;
    float tempPPM;

    Rs = RL * (1023.0 - (float)VL) / (float)VL;
    tempPPM = pow((29.786 / (Rs / Ro)), (1.0 / 0.737));
    return (int)tempPPM;
}

Note that the cycle of getting CO concentrations should go through two heating cycles, high heating using 5 VDC and low heating using 1.4 VDC. This is achieved by PWM signla through a MOSFET powering the heating coil.

For 5 VDC, PWM duty cycle is 107/255 for 60 seconds, and for 1.4 VDC PWM duty cycle is 30/255 for 90 seconds. This value is set using the OCR1A register and PWM signal is at PB1 pin.

        Lcd4_Set_Cursor(2,12);
        Lcd4_Write_String("H0");
        OCR1A = 107;
        timeCount = 0;
        while(timeCount < 60){
            itoa(timeCount, buffer, 10);
            if(timeCount < 10) Lcd4_Set_Cursor(2,14);
            else Lcd4_Set_Cursor(2,13);
            Lcd4_Write_String(buffer);

            subTime = 0;
            while(subTime < 100){
                if((BT1_port & (1 << BT1_pin)) && (BT2_port & (1 << BT2_pin))){
                    buzzer_port &= ~(1 << buzzer_pin);
                }
                while((BT1_port & (1 << BT1_pin)) && (BT2_port & (1 << BT2_pin))){}

                if((BT2_port & (1 << BT2_pin))){
                    if(fanMode == 'A'){
                        fanMode = 'M';
                        operateFan(200);
                        Lcd4_Set_Cursor(1, 13);
                        Lcd4_Write_String("1 M");
                    }
                    else{
                        fanMode = 'A';
                        Lcd4_Set_Cursor(1, 15);
                        Lcd4_Write_Char('A');
                    }
                }
                while((BT2_port & (1 << BT2_pin))){}

                _delay_ms(10);
                subTime++;
            }
            timeCount++;
        }

        Lcd4_Set_Cursor(2,12);
        Lcd4_Write_String("L0");
        OCR1A = 30;
        timeCount = 0;
        while(timeCount < 90){
            itoa(timeCount, buffer, 10);
            if(timeCount < 10) Lcd4_Set_Cursor(2,14);
            else Lcd4_Set_Cursor(2,13);
            Lcd4_Write_String(buffer);

            subTime = 0;
            while(subTime < 100){
                if((BT1_port & (1 << BT1_pin)) && (BT2_port & (1 << BT2_pin))){
                    buzzer_port &= ~(1 << buzzer_pin);
                }
                while((BT1_port & (1 << BT1_pin)) && (BT2_port & (1 << BT2_pin))){}

                if((BT2_port & (1 << BT2_pin))){
                    if(fanMode == 'A'){
                        fanMode = 'M';
                        operateFan(200);
                        Lcd4_Set_Cursor(1, 13);
                        Lcd4_Write_String("1 M");
                    }
                    else{
                        fanMode = 'A';
                        Lcd4_Set_Cursor(1, 15);
                        Lcd4_Write_Char('A');
                    }
                }
                while((BT2_port & (1 << BT2_pin))){}

                _delay_ms(10);
                subTime++;
            }
            timeCount++;
        }

6.3. Exhaust Fan Control Unit

The first part in code includes all header files needed.

#include <avr/io.h>
#include <util/delay.h>
#include "pins.h"
#include "nrf24L01Rx.h"

Two header files were created. The first file “pins.h” assigns names to all used pins to make coding easier. The code snippet below shows an example from pins.h file where new names for relay and LEDs module pins are defined.

//Relay
#define fan_relay_port PORTB //active low
#define fan_relay_dir DDRB
#define fan_relay_pin PB6

//LED module
#define fan_on_port PORTB //active low LED
#define fan_on_dir DDRB
#define fan_on_pin PB7

#define fan_off_port PORTD //active low LED
#define fan_off_dir DDRD
#define fan_off_pin PD5

The second “nrf24L01Rx.h” where the functions to operate the wireless module are defined. The code snippet below shows the nrf24_init() function used to initialize the wireless module.

/* init the hardware pins */
void nrf24_init(){
    set(CS_port,CS_pin);
    output(CS_direction,CS_pin);
    clear(CE_port,CE_pin);
    output(CE_direction,CE_pin);
    clear(SCK_port,SCK_pin);
    output(SCK_direction,SCK_pin);
    clear(MOSI_port,MOSI_pin);
    output(MOSI_direction,MOSI_pin);
    clear(MISO_direction,MISO_pin);            //set MISO as input

    //nrf24_setupPins();
    clear(CE_port, CE_pin);
    set(CS_port, CS_pin);
}

When the EFC unit is generated the fan will be off. It continuously (while loop) listens to the data transmitted by the COM unit and changes the fan state accordingly.

while(1){
   if(nrf24_dataReady()){
     nrf24_getData(data_array);
     if(data_array[0] == 100){ //fan off
        fan_relay_port |= (1 << fan_relay_pin);
        fan_off_port &= ~(1 << fan_off_pin);
        fan_on_port |= (1 << fan_on_pin);
     }
     else if(data_array[0] == 200){ //fan on
        fan_relay_port &= ~(1 << fan_relay_pin);
        fan_off_port |= (1 << fan_off_pin);
        fan_on_port &= ~(1 << fan_on_pin);
     }
   }
 }

Microcontrollers: How to program
NRF24L01+ Transceiver
ATmega328 and LCD
FAB TinyISP
Indoor Air Quality