'Small Devices/Netduino'에 해당되는 글 14건

  1. 2012.07.28 [Netduino] .NET Micro Framework 에서의 I2C 통신 2 - BMP085 기압 센서 이용 1
반응형

지난번 조도 센서에 이어 (http://alnova2.tistory.com/672) 다음은 Netduino에 BMP085 기압 센서를 붙여 보겠다. BMP085 는 Bosch 에서 나온 기압 센서로 30,000 ~ 110,000 Pa(Pascal)까지 측정 가능하며 0-65'C 의 온도도 측정 가능하다. 다음은 BMP085를 Arduino에서 이용 가능하게 Breakout 으로 만든 제품이다.


이 센서의 breakout 은 artrobot.co.kr 에서 구매 가능하다. 

Tutorial: http://www.sparkfun.com/tutorials/253

Datasheet : http://www.sparkfun.com/datasheets/Components/General/BST-BMP085-DS000-05.pdf


Netduino 의 Analog 5핀에 SCL을, Analog 4핀에 SDA를 연결한다. I2C는 버스를 공유하는 형태로 이전 조도 센서와 같이 연결해서 시험해 보겠다. 전원은 3.3V를 연결한다. 

BMP085는 온도나 기압을 읽도록 명령을 보낼때 해당하는 Register Address를 보내고 Control Register Data를 보낸다. 

다음은 Control Register 값이다.


Control Register 값은 oversampling_setting(osrs)에 따라서 조금씩 달라지며 이에 따라 conversion time이 달라진다. 온도나 기압을 읽는 명령을 내린 다음에 데이터를 읽는 것은 0xF6을 write하고 I2C Read를 하게 되면 각각에 해당하는 바이트가 전송 된다.

다음은 온도를 읽는 코드 이다.

long bmp085ReadUT()

{

    Debug.Print("[bmp085ReadUT]");

    byte[] readBuffer = new byte[2];

    I2CDevice.I2CWriteTransaction writeTransaction = 

                                    I2CDevice.CreateWriteTransaction(new byte[]{0xF4,0x2E});

    I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

        writeTransaction

    };

    int transferred = _i2cdevice.Execute(transactions, 1000);

    Thread.Sleep(5);

    writeTransaction = I2CDevice.CreateWriteTransaction(new byte[] {0xF6 });

    I2CDevice.I2CReadTransaction readTransaction = I2CDevice.CreateReadTransaction(readBuffer);

    transactions = new I2CDevice.I2CTransaction[]{

        writeTransaction,

        readTransaction

    };

    transferred = _i2cdevice.Execute(transactions, 1000);

    return ((readBuffer[0] << 8 | readBuffer[1]));

}

다음은 기압을 읽는 코드이다.

long bmp085ReadUP()

{

    Debug.Print("[bmp085ReadUP]");

    byte[] readBuffer = new byte[3];

    I2CDevice.I2CWriteTransaction writeTransaction = 

                      I2CDevice.CreateWriteTransaction(new byte[] { 0xF4, (byte)(0x34+(OSS<<6)) });

    I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

        writeTransaction

    };

    int transferred = _i2cdevice.Execute(transactions, 1000);

    Thread.Sleep(2 + (3 << OSS));

    writeTransaction = I2CDevice.CreateWriteTransaction(new byte[] { 0xF6 });

    I2CDevice.I2CReadTransaction readTransaction = I2CDevice.CreateReadTransaction(readBuffer);

    transactions = new I2CDevice.I2CTransaction[]{

        writeTransaction,

        readTransaction

    };

    transferred = _i2cdevice.Execute(transactions, 1000);

    return ((readBuffer[0] << 16 | readBuffer[1]<<8 | readBuffer[2])) >> (8-OSS);

}

OSS 값에 따라서 기압은 측정 Resolution이 달라진다. OSS는 0부터 3까지 가능한데 0이 최저 Resolution이고 저전력이다. 읽고 기압을 읽는 부분은 Datasheet를 참고하기 바란다. 상기의 온도와 기압은 보정이 필요하다. 보정에는 기본적인 파라미터들을 calibration 하고 이 값들을 가지고 보정한다. 다음은 보정을 위한 파라미터와 address이다. MSB에 대해서 2바이트씩 읽으면  된다.

 다음은 보정을 위한 2바이트를 읽는 함수이다.

short bmp085ReadShort(byte[] writeBuffer)

{

    byte[] readBuffer = new byte[2];

    I2CDevice.I2CWriteTransaction writeTransaction = I2CDevice.CreateWriteTransaction(writeBuffer);

    I2CDevice.I2CReadTransaction readTransaction = I2CDevice.CreateReadTransaction(readBuffer);

    I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

        writeTransaction,

        readTransaction

    };

    int transferred = _i2cdevice.Execute(transactions, 1000);

    return (short)(readBuffer[0] << 8 | readBuffer[1]);

} 

다음은 보정 파라미터를 선언하고 보정하는 함수이다.

 private short _ac1;

private short _ac2;

private short _ac3;

private ushort _ac4;

private ushort _ac5;

private ushort _ac6;

private short _b1;

private short _b2;

private short _mb;

private short _mc;

private short _md;

 void bmp085Calibration()

{

    Debug.Print("[bmp085Calibration]");

    _ac1 = bmp085ReadShort(new byte[]{0xAA});

    _ac2 = bmp085ReadShort(new byte[]{0xAC});

    _ac3 = bmp085ReadShort(new byte[]{0xAE});

    _ac4 = (ushort)bmp085ReadShort(new byte[]{0xB0});

    _ac5 = (ushort)bmp085ReadShort(new byte[]{0xB2});

    _ac6 = (ushort)bmp085ReadShort(new byte[]{0xB4});

    _b1 = bmp085ReadShort(new byte[] { 0xB6 });

    _b2 = bmp085ReadShort(new byte[] { 0xB8 });

    _mb = bmp085ReadShort(new byte[] { 0xBA });

    _mc = bmp085ReadShort(new byte[] { 0xBC });

    _md = bmp085ReadShort(new byte[] { 0xBE });

}

이 값들을 가지고 보정된 값을 읽는 함수이다. 실제 보정하는 원리는 datasheet를 참고하기 바란다. 추가로 altitude와 기대 기압에 대하 함수도 추가한다. (역시 datasheet 참고)

public long bmp085GetTemperature()

{

    long ut=this.bmp085ReadUT();

    long x1, x2;

    x1 = (((long)ut - (long)_ac6)*(long)_ac5) >> 15;

    x2 = ((long)_mc << 11)/(x1 + _md);

    b5 = x1 + x2;

    _temperature = ((b5 + 8) >> 4);

    return _temperature;  

}

public long bmp085GetPressure()

{

    long x1, x2, x3, b3, b6, p;

    long b4, b7;

    long up = this.bmp085ReadUP();

    b6 = b5 - 4000;

    // Calculate B3

    x1 = (_b2 * (b6 * b6)>>12)>>11;

    x2 = (_ac2 * b6)>>11;

    x3 = x1 + x2;

    b3 = (((((long)_ac1)*4 + x3)<<OSS) + 2)>>2;

    // Calculate B4

    x1 = (_ac3 * b6)>>13;

    x2 = (_b1 * ((b6 * b6)>>12))>>16;

    x3 = ((x1 + x2) + 2)>>2;

    b4 = (_ac4 * (long)(x3 + 32768))>>15;

    b7 = ((long)(up - b3) * (50000>>OSS));

    if (b7 < 0x80000000) p = (b7<<1)/b4;

    else p = (b7/b4)<<1;

    x1 = (p>>8) * (p>>8);

    x1 = (x1 * 3038)>>16;

    x2 = (-7357 * p)>>16;

    p += (x1 + x2 + 3791)>>4;

    _pressure = p;

    return _pressure;

}

public double bmp85GetAltitude()

{

    _altitude = 44330 * (1 - System.Math.Pow((double)_pressure / 101325, 0.190295));

    return _altitude;

 }

public double bmp85ExpectedPressure(double inAltitude)

{

    double epressure;

    epressure = 101325 * System.Math.Pow((1 -  inAltitude / 44330), 5255);

    return epressure;

} 

http://www.daftlogic.com/sandbox-google-maps-find-altitude.htm 에서  구글 맴상의 altitude를 확인해 볼수 있다. 기대 pressure와 실제 기압을 비교하여 기상을 이야기 할 수 있다. (하기의 main code 참조)

다음은 bmp085 클래스의 전체 코드이다.

using System;

using System.Net;

using System.Net.Sockets;

using System.Threading;

using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;

using SecretLabs.NETMF.Hardware;

using SecretLabs.NETMF.Hardware.NetduinoPlus;


namespace LuxAndBarometer

{

    public class BMP085

    {

        private short _ac1;

        private short _ac2;

        private short _ac3;

        private ushort _ac4;

        private ushort _ac5;

        private ushort _ac6;

        private short _b1;

        private short _b2;

        private long b5;

        private long _temperature;

        private long _pressure;

        private double _altitude;

        private short _mb;

        private short _mc;

        private short _md;

        private const byte OSS = 0;

        private I2CDevice.Configuration _i2cconfig;

        private I2CDevice _i2cdevice;

        public BMP085(ushort address, int kHz)

        {

            _i2cconfig = new I2CDevice.Configuration(address, kHz);

            _i2cdevice = new I2CDevice(_i2cconfig);

            this.bmp085Calibration();

        }

        byte bmp085Read(byte[] writeBuffer)

        {

            byte[] readBuffer = new byte[1];

            I2CDevice.I2CWriteTransaction writeTransaction =

                                         I2CDevice.CreateWriteTransaction(writeBuffer);

            I2CDevice.I2CReadTransaction readTransaction = 

                                         I2CDevice.CreateReadTransaction(readBuffer);

            I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction,

                readTransaction

            };

            int transferred=_i2cdevice.Execute(transactions,1000);

            return readBuffer[0];

        }

        short bmp085ReadShort(byte[] writeBuffer)

        {

            byte[] readBuffer = new byte[2];

            I2CDevice.I2CWriteTransaction writeTransaction = 

                              I2CDevice.CreateWriteTransaction(writeBuffer);

            I2CDevice.I2CReadTransaction readTransaction = 

                              I2CDevice.CreateReadTransaction(readBuffer);

            I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction,

                readTransaction

            };

            int transferred = _i2cdevice.Execute(transactions, 1000);

            return (short)(readBuffer[0] << 8 | readBuffer[1]);

        }

        void bmp085Calibration()

        {

            Debug.Print("[bmp085Calibration]");

            _ac1 = bmp085ReadShort(new byte[]{0xAA});

            _ac2 = bmp085ReadShort(new byte[]{0xAC});

            _ac3 = bmp085ReadShort(new byte[]{0xAE});

            _ac4 = (ushort)bmp085ReadShort(new byte[]{0xB0});

            _ac5 = (ushort)bmp085ReadShort(new byte[]{0xB2});

            _ac6 = (ushort)bmp085ReadShort(new byte[]{0xB4});

            _b1 = bmp085ReadShort(new byte[] { 0xB6 });

            _b2 = bmp085ReadShort(new byte[] { 0xB8 });

            _mb = bmp085ReadShort(new byte[] { 0xBA });

            _mc = bmp085ReadShort(new byte[] { 0xBC });

            _md = bmp085ReadShort(new byte[] { 0xBE });

        }

        long bmp085ReadUT()

        {

            Debug.Print("[bmp085ReadUT]");

            byte[] readBuffer = new byte[2];

            I2CDevice.I2CWriteTransaction writeTransaction =

                                I2CDevice.CreateWriteTransaction(new byte[]{0xF4,0x2E});

            I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction

            };

            int transferred = _i2cdevice.Execute(transactions, 1000);

            Thread.Sleep(5);

            writeTransaction = I2CDevice.CreateWriteTransaction(new byte[] {0xF6 });

            I2CDevice.I2CReadTransaction readTransaction = 

                              I2CDevice.CreateReadTransaction(readBuffer);

            transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction,

                readTransaction

            };

            transferred = _i2cdevice.Execute(transactions, 1000);

            return ((readBuffer[0] << 8 | readBuffer[1]));

        }

        long bmp085ReadUP()

        {

            Debug.Print("[bmp085ReadUP]");

            byte[] readBuffer = new byte[3];

            I2CDevice.I2CWriteTransaction writeTransaction =

                        I2CDevice.CreateWriteTransaction(new byte[] { 0xF4, (byte)(0x34+(OSS<<6)) });

            I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction

            };

            int transferred = _i2cdevice.Execute(transactions, 1000);

            Thread.Sleep(2 + (3 << OSS));

            writeTransaction = I2CDevice.CreateWriteTransaction(new byte[] { 0xF6 });

            I2CDevice.I2CReadTransaction readTransaction = 

                           I2CDevice.CreateReadTransaction(readBuffer);

            transactions = new I2CDevice.I2CTransaction[]{

                writeTransaction,

                readTransaction

            };

            transferred = _i2cdevice.Execute(transactions, 1000);

            return ((readBuffer[0] << 16 | readBuffer[1]<<8 | readBuffer[2])) >> (8-OSS);

        }

        public long bmp085GetTemperature()

        {

            Debug.Print("[bmp085GetTemperature]");

            long ut=this.bmp085ReadUT();

            long x1, x2;

            x1 = (((long)ut - (long)_ac6)*(long)_ac5) >> 15;

            x2 = ((long)_mc << 11)/(x1 + _md);

            b5 = x1 + x2;

            _temperature = ((b5 + 8) >> 4);

            return _temperature;  

        }

        public long bmp085GetPressure()

        {

            Debug.Print("[bmp085GetPressure]");

            long x1, x2, x3, b3, b6, p;

            long b4, b7;

            long up = this.bmp085ReadUP();

            b6 = b5 - 4000;

            // Calculate B3

            x1 = (_b2 * (b6 * b6)>>12)>>11;

            x2 = (_ac2 * b6)>>11;

            x3 = x1 + x2;

            b3 = (((((long)_ac1)*4 + x3)<<OSS) + 2)>>2;

            // Calculate B4

            x1 = (_ac3 * b6)>>13;

            x2 = (_b1 * ((b6 * b6)>>12))>>16;

            x3 = ((x1 + x2) + 2)>>2;

            b4 = (_ac4 * (long)(x3 + 32768))>>15;

            b7 = ((long)(up - b3) * (50000>>OSS));

            if (b7 < 0x80000000) p = (b7<<1)/b4;

            else p = (b7/b4)<<1;

            x1 = (p>>8) * (p>>8);

            x1 = (x1 * 3038)>>16;

            x2 = (-7357 * p)>>16;

            p += (x1 + x2 + 3791)>>4;

            _pressure = p;

            return _pressure;

        }

        public double bmp85GetAltitude()

        {

            _altitude = 44330 * (1 - System.Math.Pow((double)_pressure / 101325, 0.190295));

            return _altitude;

         }

        public double bmp85ExpectedPressure(double inAltitude)

        {

            double epressure;

            epressure = 101325 * System.Math.Pow((1 - inAltitude / 44330), 5255);

            return epressure;

        }

    }

} 

이를 이용한 메인 클래스는 다음과 같다.

using System;

using System.Net;

using System.Net.Sockets;

using System.Threading;

using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;

using SecretLabs.NETMF.Hardware;

using SecretLabs.NETMF.Hardware.NetduinoPlus;


namespace LuxAndBarometer

{

    public class Program

    {

        public static void Main()

        {

            // write your code here

            BMP085 bmp85=new BMP085(0x77,100);

            long temperature;

            long pressure;

            double altitude;

            double epressure;

            double weatherDiff;

            while (true)

            {

                temperature = bmp85.bmp085GetTemperature();

                pressure = bmp85.bmp085GetPressure();

                altitude = bmp85.bmp85GetAltitude();

                epressure = bmp85.bmp85ExpectedPressure(93.922);

                Debug.Print("Temperature:" + temperature.ToString() + " Pressure:" + 

                                                pressure.ToString()+" Altitude:"+altitude.ToString()+" m");

                weatherDiff = pressure - epressure;

                if (weatherDiff > 250) Debug.Print("Sunny!");

                else if (weatherDiff<=250 || weatherDiff>=-250) Debug.Print("Partly Cloudy");

                else if (weatherDiff>-250) Debug.Print("Rain");

                Thread.Sleep(3000);

            }

        }


    }

} 

다음은 실행 화면 이다.






반응형
Posted by alias
,