Digital Barometer with Graphical LCD Display
 
Digital Barometer with Graphical LCD Display


 

Digital Barometer with Graphical LCD Display

 


Here's a digital barometer that uses Atmega8 microcontroller and graphical LCD display. This project uses SCP barometer pressure sensor graphical LCD display connected to Atmega8 microcontroller. Graphical LCD displays latest 128 readings while one reading occur once in 20 minutes. You can see information of about two last days. Provided C source code can be customized to your liking.


Digital Barometer with Graphical LCD Display

Recording barometer/serial LCD code.

SPI barometer pressure sensor
ATmega8
Serial 128/64 LCD

For graph scaling, pressure data for one year were downloaded from Teddington England station
Data have mean +/- sd of 1012.76 +/- 9.9 mB.

So, to cover a range of two standard deviations, need to plot +/- 20 mB about mean of 1013.25 (standard atmosphere).Tests show that this is an excessive range for short term observations.

Graph range currently set by dividing constant SCALE=8 (default)
so, pixel range [0,47] corresponds to millbar range {1032.5 , 994.8 mB}



/*
ATmega8 compile with -Os (optimization for size)
*/

#include
#include
#include
#include
//#include

//OFFSET converts to sea level, was 2299 for first barometer pressure sensor

#define OFFSET 2498

#define MINUTES 20 // number of minutes for linear regression of pressure trend
#define SAMPLES 60 // number of 1-second samples per "minute"

// SCALE converts (0.1 mB units to +/- 24 about 1 Atm) y=24-int(pressure_mB - 1013.25)*10/SCALE)

#define SCALE 8 //graph pressure scale factor (dividing)


// globals for circular FIFO data buffer. CBUFSIZE must be power of 2

#define CBUFSIZE 128
#define MASK (CBUFSIZE-1)

#define FOSC 8000000 //internal RC
#define BAUD 9600 //higher baud rates don't work well with internal RC
#define MYUBRR FOSC/16/BAUD-1

#define sbi(var, mask) ((var) |= (uint8_t) (1 << mask))
#define cbi(var, mask) ((var) &= (uint8_t)~(1 << mask))

// PORT bit definitions

// PORTB

#define STATUS_LED 0

// PORTC, bit banged SPI for interface with sensor

#define SCP_CSB 0
#define SCP_MISO 1
#define SCP_MOSI 2
#define SCP_SCK 3


void ioinit (void); // initializes IO

void scp_read(void);
uint8_t read_register(uint8_t register_name);
uint16_t read_register16(uint8_t register_name);
void write_register(uint8_t register_name, uint8_t register_value);
uint8_t spi_comm(uint8_t outgoing_byte);

void print_decimal(int32_t number, uint8_t place, uint8_t pad);
void Delay_us( unsigned int microseconds );
void Delay_ms( unsigned int milliseconds );

void uart_putc(char c);
void uart_puts(char *str);

void lcd_send(void);
void lcd_cls(void);
void lcd_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
void lcd_loc(unsigned char row, unsigned char col);
void lcd_putpixel(uint8_t x, uint8_t y, uint8_t color);
void lcd_printat(unsigned char row, unsigned char col, char *txt);

signed int fetch(void);
signed int store(signed int value);

// global variables

volatile signed long int pressure,pav,tav;
volatile signed int p,np,pmin,pmax,temperature; //****negative temps OK?****
volatile signed int int0_flag=0; //switch interrupt flag

volatile unsigned char indx=0,jndx=0; //pointers to next storage location
volatile signed int cbuf[CBUFSIZE]; //Circular buffer for pressure data

volatile char buf[21]; //buffer for graphics commands
volatile int nbuf; //number of characters in buf

volatile unsigned char global_ticks; //interrupt routine
volatile unsigned char flag,i,j; //need to put the loop variables for main() here because
volatile float nx,x,y,sx,sx2,sxy,sy; //of apparent optimizer errors when compiling with -Os

/*
** interrupt service routines
*/

ISR (INT0_vect) //button press, turn on backlight
{
int0_flag=30; //for 30 seconds
}

ISR (TIMER2_OVF_vect)
{
global_ticks++;

if(global_ticks == 250) //250 ticks/second
{
flag = 1; //One second has hit
global_ticks = 0;
}

TCNT2 = 256-125; //Preload timer 2 for 125 clicks. 8MHz/256 = 31250 incs/sec / 125 => 250 incs/sec
}


int main (void)
{

global_ticks=0;

ioinit (); //set defaults
int0_flag=70; //backlight on for 70 seconds

sbi(PORTB, STATUS_LED);
Delay_ms(500);
lcd_cls();

uart_putc(170); uart_putc(30); Delay_ms(50); //turn on backlight

lcd_printat(4,4,"Barometer v2.3");
Delay_ms(1000);
cbi(PORTB, STATUS_LED);

// startup error checking

for(i=1; i<7; i++) {
j = read_register(0x07); //STATUS register
if ((j&1)==0) break; //successful if LSB=0
Delay_ms(10);
}

// reinitialize to fix checksum error
// write_register(address, data)

write_register(0x06, 0x01); //write 1 to RSTR (software reset)
Delay_ms(60);

for(i=1; i<7; i++) {
j = read_register(0x07); //STATUS register
if ((j&1)==0) break;
Delay_ms(10);
}
j = read_register(0x1F); //DATARD8 register
if (j!=1) lcd_printat(6,2,"Startup failure!"); //should equal 1 if eeprom checksum OK
Delay_ms(2000);

//Configure barometer pressure sensor with low noise configuration
//=====================================
//write_register(address, data)
write_register(0x02, 0x2D);
write_register(0x01, 0x03);
write_register(0x03, 0x02);

Delay_ms(100);

//Select High Resolution Mode
write_register(0x03, 0x0A);
//=====================================

lcd_cls();//clear display

lcd_rect(0,0,127,47); //outline for graph

lcd_printat(7,1,"Sampling");

// buf[0]=170; buf[1]=0; nbuf=2; lcd_send; //doesn't work!! backlight off
// uart_putc(170); uart_putc(30); //this does work to set backlight to 0/255 intensity

sx=0.; sx2=0.; nx = (float) MINUTES;
for(i=0; i x= (float) i;
sx += x;
sx2 += x*x;
}

while(1)
{

sxy=0. ; sy=0.; //initialize linear regression

for(i=0; i
tav=0; pav=0;

// loop executes each second

for(j=0; j scp_read(); //read pressure and temperature from barometer pressure sensor
tav+=temperature;
pav+=pressure+OFFSET;

while(flag == 0) { //wait until 1 second has passed
//check backlight switch. If low, set backlight=30/255
if(int0_flag != 0) {uart_putc(170); uart_putc(30); Delay_ms(100);}
}
flag = 0;
if (int0_flag>0) int0_flag--; //count down seconds for backlight, then off

if (int0_flag == 0) {uart_putc(170); uart_putc(0); Delay_ms(10);} //backlight off

} //end for j


sbi(PORTB, STATUS_LED);

tav=tav/SAMPLES; pav=pav/SAMPLES;
x= (float) i; y=(float) pav; //do linear regression sums to
sy += y; // calculate rate of change of pressure (dPa/hr)
sxy += x*y;
lcd_loc(7,1);
print_decimal(pav, 2, 0);
lcd_printat(7,8," mB");
lcd_loc(7,16);
print_decimal(tav, 1, 0);
lcd_printat(7,20," C");
lcd_printat(8,20," ");
if ((MINUTES-i-1)>9) lcd_loc(8,20); else lcd_loc(8,21);
print_decimal((MINUTES-i-1),0,1); // countdown to update
cbi(PORTB, STATUS_LED);

} //end for i

// temperature = (90*tav)/5 + 3200; //degrees F
// data for running plot of pressure (last 43 hours for 20 minute sample period)

lcd_cls(); //clear display
lcd_rect(0,0,127,47); //outline for graph
lcd_loc(7,1);
print_decimal(pav, 2, 0); //decimal place = 2 digits from right
lcd_printat(7,8," mB");
lcd_loc(7,16);
print_decimal(tav, 1, 0); //temp in Celcius
lcd_printat(7,20," C");

p=(pav-101325L)/10L; // (Pa-std atmosphere) in units of 0.1 millibar (integer)
np=store(p); //save it in circular buffer
/*
graph autoscaling not yet implemented...
*/

//plot the most recent pressure data, up to 128 points

jndx=indx; //point to last value stored+1
for (i=np;i>0;i--) {
p=24-(fetch()/SCALE);
if( (p>=0) && (p<=47) ) {j=p; lcd_putpixel(i-1,j,2);} //2=invert pixel at location
}

// calculate trend in pressure = slope of P versus time
// = (sum(x*y) - sum(x)*sum(y)/N)/(sum(x^2)-(sum(x))^2/N)
// -> delta Pa/hr

x=(60.0/nx)*(sxy-sx*sy/nx)/(sx2-sx*sx/nx); //(60./nx) -> per hour basis

pav = x+0.5; //round to integer
lcd_printat(8,1,"dPa/hr");
lcd_loc(8,8);
print_decimal(pav,0,1);

}

return (0);
}

void ioinit (void)
{
//1 = output, 0 = input
DDRB = (1< DDRC = (1< DDRD = (1< PORTD = (1< MCUCR = 0; //low level activates interrupt
GICR = (1<
// USART Baud rate: 9600
UBRRH = (MYUBRR) >> 8;
UBRRL = MYUBRR;
UCSRB = (1< UCSRC = (1<

sbi(PORTC, SCP_CSB); //Deselect barometer pressure sensor

//Init timer 2
ASSR = 0;
TCCR2 = (1< TIMSK = (1< TCNT2=256-125; //125 clicks to overflow, 250 clock ticks per second
sei();

}

//Takes an int and prints a decimal in a given place
//(87004, 3, 0) = 87.004
//10 digit max = 1234567890
//pad will put zeros in front to make the length equal to "pad" characters
//(87004, 3, 7) = 0087.004
// stores character result in buf[]

// Bug for number=0 and pad=0 -- nothing printed! Fix: set pad=1

void print_decimal(int32_t number, uint8_t place, uint8_t pad)
{
uint8_t i;
unsigned char nums[11];

for(i = 0 ; i < 11 ; i++) nums[i] = 0;
nbuf=0; //output character buffer index
if (number < 0) {number=-number; buf[nbuf++]='-';} // else buf[nbuf++]=' ';

for(i = 10 ; number > 0 ; i--)
{
nums[i] = number % 10;
number /= 10;
}

//Pad the number of spots
if (pad <= 10 && (pad > (10 - i)) ) i = 10 - pad;
for(i++ ; i < 11 ; i++)
{
buf[nbuf++]=nums[i]+'0';

//Check for decimal place
if ( ((i + place) == 10) && (place != 0) ) buf[nbuf++]='.';
}
buf[nbuf++]=255; //terminate string for lcd, strlen=nbuf
lcd_send(); //write it to the lcd
Delay_ms(20); //need a short delay
}

//
void scp_read(void)
{
int32_t temp_data;

//Wait for new data
while(1)
{
temp_data = read_register(0x07); //Check the status register for bit 6 to go high
if (temp_data & 32) break;
}

temperature = read_register16(0x21); //Read the temperature data
temperature /= 2; //This is (temp * 10) / 20 so that the answer of 27.9 comes out as 279 (easier to print)

temp_data = read_register(0x1F); //Read MSB pressure data - 3 lower bits
pressure = read_register16(0x20);

temp_data <<= 16; //Shift it
pressure = temp_data | pressure;
pressure >>= 2;

}

///////////////////////////////////////////////////////////////
// Print character to uart
void uart_putc(char c)
{
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
}

//Read 8-bit register
uint8_t read_register(uint8_t register_name)
{
uint8_t in_byte;

register_name <<= 2;
register_name &= 0b11111100; //Read command

cbi(PORTC, SCP_CSB); //Select SPI Device

in_byte = spi_comm(register_name); //Write byte to device
//in_byte is nothing, we need to clock in another 8 bits
in_byte = spi_comm(0x00); //Send nothing, but we should get back the register value

sbi(PORTC, SCP_CSB);

return(in_byte);
}

//Read 16-bit register
uint16_t read_register16(uint8_t register_name)
{
uint16_t in_byte;

register_name <<= 2;
register_name &= 0b11111100; //Read command

cbi(PORTC, SCP_CSB); //Select SPI Device

in_byte = spi_comm(register_name); //Write byte to device
//in_byte is nothing, we need to clock in another 8 bits
in_byte = spi_comm(0x00); //Send nothing, but we should get back the register value
in_byte <<= 8;
in_byte |= spi_comm(0x00); //Send nothing, but we should get back the register value

sbi(PORTC, SCP_CSB);

return(in_byte);
}

//Sends a write command to barometer pressure sensor
void write_register(uint8_t register_name, uint8_t register_value)
{
uint8_t in_byte;

register_name <<= 2;
register_name |= 0b00000010; //Write command

cbi(PORTC, SCP_CSB); //Select SPI device

in_byte = spi_comm(register_name); //Send register location
in_byte = spi_comm(register_value); //Send value to record into register

sbi(PORTC, SCP_CSB);

//Return nothing
}

//Basic SPI send and receive
uint8_t spi_comm(uint8_t outgoing_byte)
{
uint8_t incoming_byte, x;

incoming_byte = 0;

for(x = 8 ; x > 0 ; x--)
{
cbi(PORTC, SCP_SCK); //Toggle the SPI clock

//Put bit on SPI data bus
if(outgoing_byte & (1 << (x-1)))
sbi(PORTC, SCP_MOSI);
else
cbi(PORTC, SCP_MOSI);

sbi(PORTC, SCP_SCK);

//Read bit on SPI data bus
incoming_byte <<= 1;
if ( (PINC & (1 << SCP_MISO)) ) incoming_byte |= 1;
}

return(incoming_byte);
}
/*
// globals for circular data buffer. CBUFSIZE must be power of 2

#define CBUFSIZE 128
#define MASK (CBUFSIZE-1)

volatile unsigned char indx=0,jndx=0; //pointers to next storage location
volatile int cbuf[CBUFSIZE];

*/

// store value in circular buffer.
// Returns nstore, # of values stored
// updates global variable "indx" to point to next free location

signed int store(signed int value) {

static signed int nstore=0;

cbuf[indx] = value;
indx++;
indx &= MASK; //MASK obviates range check

if (nstore < CBUFSIZE) nstore++;
return nstore;
}

// fetch the jndx value from the circular buffer
// jndx must be initialized externally and is predecremented

signed int fetch(void) {

volatile signed int value;
jndx--;
jndx &= MASK;
value = cbuf[jndx];
return value;
}

////////////////////////////////////////////////////////////////////
//graphical lcd routines
////////////////////////////////////////////////////////////////////

void lcd_send() {
int m;
for (m=0;m uart_putc(buf[m]); Delay_ms(1);
}
return;
}

void lcd_cls() {

buf[0]=186; nbuf=1; lcd_send();
Delay_ms(50);
return;
}

// coordinate system upper left = (0,0), lower right=(127, 63)

void lcd_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {

buf[0]=200; buf[1]=2; buf[2]=x1; buf[3]=y1;
buf[4]=x2; buf[5]=y2; buf[6]=1; buf[7]=200;
nbuf=8; lcd_send(); Delay_ms(150);
return;
}

// put a pixel at (x,y) color 0=white, 1=black, 2=invert

void lcd_putpixel(uint8_t x, uint8_t y, uint8_t color) {

buf[0]=200;
buf[1]=5; buf[2]=x; buf[3]=y;
buf[4]=color; buf[5]=200;
nbuf=6; lcd_send();
Delay_ms(1); //not needed?
return;
}

void lcd_loc(unsigned char row, unsigned char col) {

buf[0]=126+row;
buf[1]=135+col;
nbuf=2; lcd_send(); //set print lcation to (row,col)
Delay_ms(10);
}
void lcd_printat(unsigned char row, unsigned char col, char *txt) {

lcd_loc(row,col);
nbuf=0;
while(*txt != 0) buf[nbuf++]=*txt++; //pack buffer, terminate with 255 and send
buf[nbuf++]=255;
lcd_send(); //nbuf needs to be one more than the buf location
Delay_ms(10);
return;
}

/*
* Delay_us
*
* wait in a loop for the specified number of microseconds.
*
*/

void Delay_us( unsigned int microseconds )
{
register unsigned int loop_count;
/* 8mhz clock, 4 instructions per loop_count */
loop_count = microseconds<<1;

__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" ( loop_count )
: "0" ( loop_count )
);
}

/* Delay_ms
*
* wait in a loop for the specified number of milliseconds.
*
*/

void Delay_ms( unsigned int milliseconds )
{
uint16_t i;

for ( i = 0; i < milliseconds; ++i )
{
Delay_us( 1000 );
}
}






Accurate LC Meter Capacitance Inductance Meter with 16F628 and LCD
Volt Ampere Meter with 16F876 Microcontroller and LCD display
 
Accurate LC Meter

Build your own Accurate LC Meter (Capacitance Inductance Meter) and start making your own coils and inductors. This LC Meter allows to measure incredibly small inductances making it perfect tool for making all types of RF coils and inductors. LC Meter can measure inductances starting from 10nH - 1000nH, 1uH - 1000uH, 1mH - 100mH and capacitances from 0.1pF up to 900nF. The circuit includes an auto ranging as well as reset switch and produces very accurate and stable readings.
PIC Volt Ampere Meter

Volt Ampere Meter measures voltage of 0-70V or 0-500V with 100mV resolution and current consumption 0-10A or more with 10mA resolution. The meter is a perfect addition to any power supply, battery chargers and other electronic projects where voltage and current must be monitored. The meter uses PIC16F876A microcontroller with 16x2 backlighted LCD.

50MHz 60MHz Frequency Meter / Counter with 16F628 & LCD
1Hz - 2MHz XR2206 Function Generator
60MHz Frequency Meter / Counter

Frequency Meter / Counter measures frequency from 10Hz to 60MHz with 10Hz resolution. It is a very useful bench test equipment for testing and finding out the frequency of various devices with unknown frequency such as oscillators, radio receivers, transmitters, function generators, crystals, etc.
1Hz - 2MHz XR2206 Function Generator

1Hz - 2MHz XR2206 Function Generator produces high quality sine, square and triangle waveforms of high-stability and accuracy. The output waveforms can be both amplitude and frequency modulated. Output of 1Hz - 2MHz XR2206 Function Generator can be connected directly to 60MHz Counter for setting precise frequency output.

BA1404 HI-FI Stereo FM Transmitter
USB IO Board PIC18F2455 / PIC18F2550
BA1404 HI-FI Stereo FM Transmitter

Be "On Air" with your own radio station! BA1404 HI-FI Stereo FM Transmitter broadcasts high quality stereo signal in 88MHz - 108MHz FM band. It can be connected to any type of stereo audio source such as iPod, Computer, Laptop, CD Player, Walkman, Television, Satellite Receiver, Tape Deck or other stereo system to transmit stereo sound with excellent clarity throughout your home, office, yard or camp ground.
USB IO Board

USB IO Board is a tiny spectacular little development board / parallel port replacement featuring PIC18F2455/PIC18F2550 microcontroller. USB IO Board is compatible with Windows / Mac OSX / Linux computers. When attached to Windows IO board will show up as RS232 COM port. You can control 16 individual microcontroller I/O pins by sending simple serial commands. USB IO Board is self-powered by USB port and can provide up to 500mA for electronic projects. USB IO Board is breadboard compatible.

ESR Meter / Transistor Tester Kit
Audiophile Headphone Amplifier Kit
 
ESR Meter / Capacitance / Inductance / Transistor Tester Kit

ESR Meter kit is an amazing multimeter that measures ESR values, capacitance (100pF - 20,000uF), inductance, resistance (0.1 Ohm - 20 MOhm), tests many different types of transistors such as NPN, PNP, FETs, MOSFETs, Thyristors, SCRs, Triacs and many types of diodes. It also analyzes transistor's characteristics such as voltage and gain. It is an irreplaceable tool for troubleshooting and repairing electronic equipment by determining performance and health of electrolytic capacitors. Unlike other ESR Meters that only measure ESR value this one measures capacitor's ESR value as well as its capacitance all at the same time.
Audiophile Headphone Amplifier Kit

Audiophile headphone amplifier kit includes high quality audio grade components such as Burr Brown OPA2134 opamp, ALPS volume control potentiometer, Ti TLE2426 rail splitter, Ultra-Low ESR 220uF/25V Panasonic FM filtering capacitors, High quality WIMA input and decoupling capacitors and Vishay Dale resistors. 8-DIP machined IC socket allows to swap OPA2134 with many other dual opamp chips such as OPA2132, OPA2227, OPA2228, dual OPA132, OPA627, etc. Headphone amplifier is small enough to fit in Altoids tin box, and thanks to low power consumption may be supplied from a single 9V battery.
 

Arduino Prototype Kit
RF Remote Control 433MHz Four Channel
 
Arduino Prototype Kit

Arduino Prototype is a spectacular development board fully compatible with Arduino Pro. It's breadboard compatible so it can be plugged into a breadboard for quick prototyping, and it has VCC & GND power pins available on both sides of PCB. It's small, power efficient, yet customizable through onboard 2 x 7 perfboard that can be used for connecting various sensors and connectors. Arduino Prototype uses all standard through-hole components for easy construction, two of which are hidden underneath IC socket. Board features 28-PIN DIP IC socket, user replaceable ATmega328 microcontroller flashed with Arduino bootloader, 16MHz crystal resonator and a reset switch. It has 14 digital input/output pins (0-13) of which 6 can be used as PWM outputs and 6 analog inputs (A0-A5). Arduino sketches are uploaded through any USB-Serial adapter connected to 6-PIN ICSP female header. Board is supplied by 2-5V voltage and may be powered by a battery such as Lithium Ion cell, two AA cells, external power supply or USB power adapter.
200m 4-Channel 433MHz Wireless RF Remote Control

Having the ability to control various appliances inside or outside of your house wirelessly is a huge convenience, and can make your life much easier and fun. RF remote control provides long range of up to 200m / 650ft and can find many uses for controlling different devices, and it works even through the walls. You can control lights, fans, AC system, computer, printer, amplifier, robots, garage door, security systems, motor-driven curtains, motorized window blinds, door locks, sprinklers, motorized projection screens and anything else you can think of.
 

Electronics-DIY.com © 2002-2018. All Rights Reserved.