Tuto. 10 - Humidity sensor

PIC tutorial 10 - Humidity sensor

Print
Category: PIC Tutorials
Published Date Written by Francois

Tutorial 10

Fig. 1 Board 4 with humidity sensor and graphic LCD

Description:

This tutorial covers the humidity sensor and some graphic display use: drawing a graph, plotted as lines or dots.
The tutorial will read and store a number of records per given time, and display the graph on the display. The program is setup to record 80 readings over 24 hours. It displays the current humidity, and updates the display every second, while displaying the minimum and maximum reading in the last 24 hours. The graph will autoscale to display the number of records on the given width and the range of values between top (max) and bottom (mini).

You will need a processor board, a power supply, the LCD display and the humidity sensor.

We will build the humidity sensor library, which will count the pulses coming from the sensor with timer 1 over a given time to determine its frequency. The frequency is dependant of the humidity.

You can download the files for this tutorial in the zip file pic_tutorial10.zip.

The boards can be powered by one of the PSU boards (powered from USB port), or between 2.7 to 5V.

Requirements:

Processor boardAny board (board 4 in example)

Extension(s)Humidity sensor

 Graphic LCD display

Power supplyPSU 1, 2 or 3. Depends on board used.

LanguageCCS C

ProgrammerPicKIT 2


Program:

#define board_id 4
#define Nb_records 80 // numbers of records to store in 24 hours
#define samples 5 // numbers of measures to average per record
#define inc_period 10 // Delay in ms between each interrupt
#define Timer0_Reload (256-(inc_period*(CRYSTAL_FREQ/(4*256*1000)))) // Timer 0 reload = 256 - Tint*(Crystal/(4*Prescaler)) where Tint is the delay between interrupts
#define int_count 1000/inc_period // Number of interrupt to get 1 sec
#define int_store 24*3600/Nb_records // Calculate numbers of second to count before updating records in table (24 hours of 3600 seconds / Nb records to store per day)
#include "lib\mainboard.c"
#include "lib\nok3310_lib.c"
#include "lib\RH_lib.c"
#priority RTCC
unsigned char count, index, minRec, maxRec;
unsigned char reading[Nb_records];
long countSec;
unsigned char readData (void) { // Read data to store, with samples records averaged. Reads humidity in this example
  unsigned char i;
  long average=0;
  for(i=0; i<samples; i++){
    average += hum_read();
  }
  average /= samples;
  return average;
}
#int_rtcc void Timer0Interrupt ( void ) { // Timer 0 will generate regular interrupt
  unsigned char rec, i;
  set_timer0(Timer0_Reload); // reload timer 0 with predetermined value
  count ++; // increment interrupt counter
  if (count == int_count) { // if desired number of interrupt counted for 1 sec
    count = 0; // reset interrupt counter
    countSec ++; // Updated second counter
    rec = readData();
    if (countSec >= int_store) { // if number of seconds counted between records
      countSec = 0;
      if (index >= Nb_records) { // All records are filled.
        for(i=0; i<8; i++){
          shift_right(reading, Nb_records, 0); // Shift record table: free last byte and discard first one
        }
        reading[Nb_records-1] = rec; // Store reading at last record
        minRec = 255;
        maxRec = 0;
        for ( i = 0; i < Nb_records; i++ ) { // Go through all records to get mini and maxi
          rec=reading[i];
          if (rec >= maxRec) maxRec = rec; // get maxi
          if (rec <= minRec) minRec = rec; // get mini
        }
      } else { // record table not full
        reading[index] = rec; // Store data at current index
        if (rec >= maxRec) maxRec = rec; // update maxi
        if (rec <= minRec) minRec = rec; // update mini
        index ++; // Increment index
      }
    }
  }
}

void main() {
  boot_up();
  count = 0;
  countSec = 65530;
  index = 0;
  minRec = 255;
  maxRec = 0;
  setup_timer_0 ( RTCC_DIV_256 | RTCC_8_BIT ); // SETUP TIMER 0
  Init_display();
  enable_interrupts ( INT_RTCC ); // enable Timer0 interrupt
  enable_interrupts ( GLOBAL ); // enable all interrupts
  while (true) {
    if (count == 0) { // count = 0 every second, update display
      printf(display_putc, "\f%02u%% mM:%02u-%02u%%", readData(), minRec, maxRec);
      if (index >= Nb_records) { // if record table full, plot all records
        Nok3310_graph(reading, Nb_records, 28, 80, 0);
      } else { // else, only display the records stored
        Nok3310_graph(reading, index, 28, 80, 0);
      }
    }
  }
}

The program first defines various variables and macros. They determine how measurement are done and how often they are stored:

  • board_id: processor board used (board 4 with PIC 18F2320 in the exemple)
  • Nb_records: numbers of records to store in set time
  • samples: number of successive readings to average when reading the sensor
  • inc_period: delay in ms between each interrupt
  • Timer0_Reload: macro that calculates timer 0 reload to get interrupts every inc_period
  • int_count: number of interrupts to count to get 1 sec
  • int_store: macro to calculate the number of seconds to count between each table update

We then loads the libraries for the processor, graphic lcd display and humidity sensor. A few variables are then defined to store various data.

The function readData is called when measuring. It reads the humidity sensor samples times and average the result. It is fairly easy to modify this function to get data from other sensors.

The main part of the program is in the interrupt from timer 0: it basically counts until it needs to read the sensor, and update the array. Timer 0 is loaded to get an interrupt every 10ms (inc_period). If one second has elapsed since the last reading, when read the sensor. If it is time to update the table, we check if we need to update the next record (table not full) or shift the whole table, delete the oldest record and store the new reading. The min and max values are also updated.

The main program initialize the board, some variables, timer 0, the display and then enables the interrupts. The main loop is then started. If the second flag is set (count = 0), we update the screen with the current reading, min and max value and update the graph, before starting the loop again. If the table is not full, we only display the readings stored.

long rh_corr;
unsigned char hum_read() {
  long pulses;
  unsigned char hum;
  disable_interrupts ( INT_RTCC ); // disable timer 0 interrupts during measurement
  rh_corr = 0; // correction for counter (used when calibrating the sensor
  set_timer1(0); // set timer 1 counter to 0
  setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1); // set Timer 1 to external clock input and no prescaler. The output of the humidity sensor is connected to timer 1 input
  delay_ms(50); // count pulses from sensor for 50ms
  pulses = get_timer1(); // Read timer 1, which contains the number of pulses that came from the sensor
  pulses += rh_corr; // Correct counter
  if (pulses > 1700) { // calculate humidity
    hum = (410-0.1955*pulses);
  } else {
    hum = (320-0.1429*pulses);
  }
  if (hum > 98) hum = 98;
  if (hum < 6) hum = 6;
  enable_interrupts ( INT_RTCC ); // re-enable timer 0 interrupts
  return(hum);
}

RH_lib.c has just one function, to read the humidity. The frequency coming out of the humidity sensor is proportional to the relative humidity of the air. So by counting the pulses during a set time, we can determine the frequency, and therefore the humidity.
First we disable the interrupts from timer 0. rh_corr is used to correct the count of pulses when calibrating the sensor. We will come back to it later. Timer 0 is reset to 0 and set to count the pulses coming from the external input. Then it waits for 50 ms, time to count the pulses from the sensor. We can then read timer 0 to get the number of pulses. The count is updated with the correction and then the humidity is calculated. Here are some explanation:

The frequency from the sensor is determined by the following formula: F = 1.38/(300 000 x Cx), where Cx is the capacity from the humidity H1 sensor. We count during 50ms, so the counter is determined with: =1.38/(300 000 x Cx / 50ms). The following table shows the counters for different humidity (taken from datasheet of H1 sensor). It will allow to determine a formula to get the humidity out of the counter read. Fig. 2 shows the graph of humidity vs counter.


humidity graph

Fig. 2 Humidity/counter graph

Cx (pF)HumidityCounter
112 10 % 2054
115 20 % 2000
118 30 % 1949
121 40 % 1901
124.5 50 % 1847
128 60 % 1797
132 70 % 1742
137 80 % 1679
143.5 90 % 1603

To keep the formula simple, we will split the graph in 2 linear zones, with a counter of 1700 (75% rh) as the limit of one and begining of the second.. Because the graph is fairly linear, this trick keeps the accuraty of the sensor pretty good. So we get the folowing formulas;
- up to 75% rh: rh = (410-0.1955*pulses).
- above 75% rh: rh = = (320-0.1429*pulses).

In the libray, if the counter (pulses) is above 1700, it uses one formula, else it uses the other one. The value is then checked to make sure it is within measurable values and timer 0 interrupt is re-enabled.

The graphic library is updated with a new function: Nok3310_graph. It will plot as bargraph or dots a set of data on the screen, while auto ranging to better display the data. It will display the lowest value -1 at the bottom, and the highest + 1 as the ceiling. The function works with 8 bits value, as the resolution is limited by the screen (48 pixels max with the nokia screen)

void Nok3310_graph(unsigned char *Ptr, unsigned char nbRec, unsigned char height, unsigned char width, unsigned char type) {
  unsigned char i, j, k, maxi, mini;
  long rec;
  maxi = 0; // Get mini and maxi to scale graph
  mini = 255;
  for ( i = 0; i < nbRec; i++ ) { // Go through all records to get mini and maxi
    rec=Ptr[i];
    if (rec >= maxi) maxi = rec; // get maxi
    if (rec <= mini) mini = rec; // get mini
  }
  if (maxi < 255) maxi += 1; // update maxi and mini to display maxi-mini +-1
  if (mini > 0) mini -=1;
  k = width/nbRec; // k will give the number of time to repeat the same reading to spread the number of records across the width of the graph
  for ( i = 0; i < nbrec; i++ ) { // plot each record k times to scale the graph
    if (maxi-mini == 0) {
      rec = 3;
    } else {
      rec = (Ptr[i]-mini)*height/(maxi-mini)+3; // Get record value and scale it: (Reading-mini)*height/((maxi-mini))
    }
    for ( j = 0; j<k; j++ ) {
      Nok3310_plot((i*k)+j+1, rec, type);
    }
  }
}

The function takes the folowing parameters:

  • Ptr: pointer to the array of variables to display.
  • nbRec: number of records from the array to display.
  • height: height in pixels of the graph
  • width: width in pixels of the graph
  • type: display a bargraph (0) or dot graph (1)

First, the mini and maxi values are initialised, then each record is read to get the proper values. It will determine the range of data to display, and the higher and lower limits. The limits are updated if they are not at the min or max value (0 or 255).
With the width and number of records to display, we can determine the width in pixel that each record will take on the graph (held in k).
Then for each record, we calculate the value in pixels to plot, based on the value read, min and max limits and height of the graph. height = record value * height / (maxi - mini). An offset of 3 is added because my screen has 36 pixels high. (offset from the bottom).
The record is displayed with k columns of determined pixels.

Compile the project and program the target with a PicKIT 2 programmer for example. Connect the processor board to the graphic LCD display board on port B and the humidity sensor on port C, making sure the jumpers on the board match the processor board used to map the timer1 clock in pin.

Power up the boards.
It will take 24 hours before the board fills the graph with 80 readings. It reads the humidity every 18 mins to update the graph, but updates the current humidity every second.


Files and links:

Tutorial 10 source files.
Processor board 4.
Graphic LCD Display board.
Humidity sensor board.

Post your comments...

    Copyright 2011. Poker Games. Copyright © 2012 riaDesign