working version

This commit is contained in:
James Devine 2019-08-06 02:31:44 +02:00 committed by GitHub
parent 9a47221c51
commit 56d34a3959
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 2271 additions and 0 deletions

529
cosmicpi-arduino_V1.6.ino Normal file
View file

@ -0,0 +1,529 @@
//Cosmic Pi software for Arduino - modified for STM32,
//J. Devine
//July 2019.
//Licensed under GPL V3 or later.
//cosmicpi.org
#include <Wire.h>
// LPS lib from here: https://github.com/pololu/lps-arduino
#include "src/LPS.h"
// LSM9DS1 lib from here: https://github.com/adafruit/Adafruit_LSM9DS1
//with some address tweaks
//#include "src/Adafruit_Sensor.h"
#include "src/Adafruit_LSM9DS1.h"
// HTU21D lib from here: https://github.com/adafruit/Adafruit_HTU21DF_Library
#include "src/SparkFunHTU21D.h"
//counter for how long the leds stay on.
int last_event_LED = 0;
// i2c
Adafruit_LSM9DS1 lsm = Adafruit_LSM9DS1();
//declare pressure sensor
LPS ps;
//Create an instance of the object
HTU21D myHumidity;
// string used for passing data from the dump routines
static const int TXTLEN = 512;
static char txt[TXTLEN]; //internal buffer
static char txts[TXTLEN]; //sensor buffer
//pinouts
/*
PA0 - Pin 14 - Shaped Signal 1
PA1 - Pin 15 - Shaped Signal 2
PA2 - Pin 16 - TX0
PA3 - Pin 16 - RX0
PA4 - Pin 20 - LED1 - Power/GPS
PA5 - Pin 21 - LED2 - Event
PA6 - Pin 22 - Injection leds
PA7 - Pin 23 - Bias FB1
PA8 - Pin 41 - SCL_Slave
PA9 - Pin 42 - USB_OTG_VBUS
PA10 - Pin 43 - GPSTX
PA11 - Pin 44 - USBOTG DM
PA12 - Pin 45 - USBOTG DP
PA13 - Pin 46 - SWDIO
PA14 - Pin 49 - SWCLK
PA15 - Pin 50 - GPSPPS Input
PB0 - Pin 26 - Bias FB2
PB1 - Pin 27 - Flag to RPi
PB2 Pin 28 - NC
PB 3 - Pin 55 - NC
PB4 - Pin 56 - SDA_Slave
PB5 - Pin 57 - NC
PB6 - Pin 58 - GPSRX
PB7 - Pin 59 - SDA0
PB8 - Pin 61 - SCL0
PB9 - Pin 62 - NC
PB10 - Pin 29 - Trigout (input to STM)
PB12 - Pin 33 - NC
PB13 - Pin 34 0 HVPSU SCLK (Clock to MAX1932)
PB14, PB15 - NC
PC0 - Pin 8 - NC
PC1 - Pin 9 - HVPSU CL1
PC2 - Pin 10 - HVPSU CL2
PC3 - Pin 11 - HV PSU DIN
PC4, PC5, PC6- NC
PC7 - Pin 38 - HVPSU CS2
PC8 - Pin 39 - HVPSU CS1
PC9 - Pin 40 - Mag Interrupt
PC10 - Pin 51 - NC
PC11 - Pin 52 - STRIGOUT B
PC12 - Pin 53 - STRIGOUT A
PC13 - Pin 2 - Baro Int
PC14 - Pin 3 - Accelint 1
PC15 - Pin 4 - Accelint 2.
*/
//eeprom writing table
/*
eeprom status byte - 00
channel 0 high byte - 01
channel 0 low byte - 02
channel 1 high byte - 03
channel 1 low byte - 04
HV bias ch 0 value - 05
HV bias ch 1 value - 06
if eeprom status byte = 1 then only one set of values are stored
if =2 then a backup set are located higher in memory, not used at the moment
*/
#include <Wire.h>
#include <EEPROM.h>
static const int SERIAL_BAUD_RATE = 19200; // Serial baud rate for version 1.5 production
static const int GPS_BAUD_RATE = 9600; // GPS and Serial1 line
// LED pins
#define PPS_PIN PA4 // PPS (Pulse Per Second) and LED
#define EVT_PIN PA5 // Cosmic ray event detected
char numconvbuff[16];
// Leds flag
bool leds_on = true;
// How long the event LED should light up (in ms)
static int event_LED_time = 15;
long eventCount = 0;
int eventStack = 0;
unsigned long pps_micros = 0;
unsigned long pps_micros_old = 0;
unsigned long micros_since_pps = 0;
#define maxevent 100 //we don't expect more events than this
unsigned long evttime [maxevent];
//set up serial output buffer
#define OutBuffSize 1024 //we don't expect more chars than this in the buffer
char outputbuffer[OutBuffSize]; //1024 character output buffer
int writeincounter = 0; //the place for writing to the buffer
int readoutcounter = 0; //the place for reading out from the buffer
void WriteToOutputBuff(char *outstring)
{
int stringlengthmax = strlen(outstring);
for (int addchar = 0; addchar < stringlengthmax; addchar++) {
outputbuffer[writeincounter] = outstring[addchar];
writeincounter++;
}
}
void ReadFromOutputBuff()
{
char digitout[2];
if (writeincounter > readoutcounter)
{
digitout[0] = outputbuffer[readoutcounter];
digitout[1] = '\0';
Serial.print(digitout);
readoutcounter++;
}
if (readoutcounter == writeincounter) {
readoutcounter = 0;
writeincounter = 0;
//Serial.println("BUFFERCLEAR");
}
}
void setupSensor()
{
// 1.) Set the accelerometer range
lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_2G);
//lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_4G);
//lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_8G);
//lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_16G);
// 2.) Set the magnetometer sensitivity
lsm.setupMag(lsm.LSM9DS1_MAGGAIN_4GAUSS);
//lsm.setupMag(lsm.LSM9DS1_MAGGAIN_8GAUSS);
//lsm.setupMag(lsm.LSM9DS1_MAGGAIN_12GAUSS);
//lsm.setupMag(lsm.LSM9DS1_MAGGAIN_16GAUSS);
// 3.) Setup the gyroscope
lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_245DPS);
//lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_500DPS);
//lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_2000DPS);
}
// GPS and time flags
boolean gps_ok = false; // Chip OK flag
boolean pps_recieved = false;
// ------------------------- Arudino Functions
// Arduino setup function, initialize hardware and software
// This is the first function to be called when the sketch is started
//setup serials
HardwareSerial GPS(PA10, PB6);
void setup() {
Serial.begin(SERIAL_BAUD_RATE);
Serial.print("ON");
if (leds_on) {
pinMode(EVT_PIN, OUTPUT); // Pin for the cosmic ray event
pinMode(PPS_PIN, OUTPUT); // Pin for the PPS (LED pin)
}
if (leds_on) {
digitalWrite(PPS_PIN, HIGH); // Turn on led
}
// start the GPS
GPS.begin(GPS_BAUD_RATE);
GpsSetup();
// start the i2c bus
Wire.begin();
Wire.setSDA(PB7);
Wire.setSCL(PB8);
//setup the thresholds
//initialise to values from EEPROM, else use defaults.
int eeprom_read = 0;
eeprom_read = EEPROM.read(0x00);
if (eeprom_read > 0) {
Serial.println("Found existing settings for Thresholds and HV, these will be applied");
Serial.print("First channel threshold settings: 0x");
Serial.print(EEPROM.read(0x01), HEX);
Serial.println(EEPROM.read(0x02), HEX);
Serial.print("Second channel threshold settings: 0x");
Serial.print(EEPROM.read(0x03), HEX);
Serial.println(EEPROM.read(0x04), HEX);
Wire.begin();
Wire.beginTransmission(byte(0x60)); // transmit to device #112
Wire.write(byte(0x00)); //sets value to the first channel
eeprom_read = EEPROM.read(0x01);
Wire.write(byte(eeprom_read));
eeprom_read = EEPROM.read(0x02);
Wire.write(byte(eeprom_read));
Wire.endTransmission(); // stop transmitting
Wire.beginTransmission(byte(0x60)); // transmit to device #112
Wire.write(byte(0x01)); //sets value to the first channel
eeprom_read = EEPROM.read(0x03);
Wire.write(byte(eeprom_read));
eeprom_read = EEPROM.read(0x04);
Wire.write(byte(eeprom_read));
Wire.endTransmission(); // stop transmitting
Serial.println("Threshold settings complete");
}
else
{
Serial.println("No existing settings for Thresholds and HV found, using defaults");
Serial.print("First channel threshold settings: 0x");
Serial.print(0x22F, HEX);
Serial.print("Second channel threshold settings: 0x");
Serial.print(0x22F, HEX);
Wire.begin();
Wire.beginTransmission(byte(0x60)); // transmit to device #112
Wire.write(byte(0x00)); //sets value to the first channel
Wire.write(byte(0x02));
Wire.write(byte(0x2F));
Wire.endTransmission(); // stop transmitting
Wire.beginTransmission(byte(0x60)); // transmit to device #112
Wire.write(byte(0x01)); //sets value to the first channel
Wire.write(byte(0x02));
Wire.write(byte(0x2F));
Wire.endTransmission(); // stop transmitting
Serial.println("Threshold settings complete");
}
//set the high voltage
//config SPI pins
pinMode(PC7, OUTPUT);
pinMode(PC8, OUTPUT);
pinMode(PC3, OUTPUT);
pinMode(PB13, OUTPUT);
eeprom_read = EEPROM.read(0x00);
if (eeprom_read > 0) {
Serial.println("Applying stored values for HV channels");
Serial.print("HV channel settings Ch1: ");
Serial.print(EEPROM.read(0x05), HEX);
Serial.print(" Ch2: ");
Serial.println(EEPROM.read(0x06), HEX);
//set HV channel 1
eeprom_read = EEPROM.read(0x05);
digitalWrite(PC7, LOW);
setHV(byte(eeprom_read));
digitalWrite(PC7, HIGH);
//set HV channel 2
eeprom_read = EEPROM.read(0x06);
digitalWrite(PC8, LOW);
setHV(byte(eeprom_read));
digitalWrite(PC8, HIGH);
Serial.println("HV channels set");
}
else
{
Serial.println("Applying default values for HV channels");
Serial.print("HV channel settings Ch1: ");
Serial.print(0xAC, HEX);
Serial.print(" Ch2: ");
Serial.println(0xAC, HEX);
digitalWrite(PC7, LOW);
setHV(0xAC);
digitalWrite(PC7, HIGH);
//set HV channel 2
digitalWrite(PC8, LOW);
setHV(0xAC);
digitalWrite(PC8, HIGH);
Serial.println("HV channels set");
}
//init the sensor suite
Serial.println("Cosmic Pi V1.6 sensor init routine");
//init LSM9DS1
//lsm.begin();
if (!lsm.begin())
{
Serial.println("Oops ... unable to initialize the LSM9DS1. Check your wiring!");
while (1);
}
setupSensor();
lsm.read(); /* ask it to read in the data */
/* Get a new sensor event */
sensors_event_t a, m, g, temp;
lsm.getEvent(&a, &m, &g, &temp);
//init lps
ps.init();
ps.enableDefault();
//init htu21d
myHumidity.begin();
Serial.println("INFO: Sensor setup complete\n");
//attach the interrupts
attachInterrupt(digitalPinToInterrupt(PA15), GPS_PPS, RISING);
attachInterrupt(digitalPinToInterrupt(PB10), Event_Int, RISING);
Serial.println("INFO: Running\n");
}
void loop() {
pipeGPS();
ReadFromOutputBuff();
//turn off the event led after the set time
if (millis() >= (last_event_LED + event_LED_time)){
if (leds_on) {
digitalWrite(EVT_PIN, LOW);
}
}
}
void GPS_PPS()
{
//stop listening for events while we process this interrupt
detachInterrupt(digitalPinToInterrupt(PB10));
//now PPS is coming through, we can switch off the internal timer.
pps_recieved = true;
//set the pps micros value as the micro time now, and buffer the last value;
pps_micros_old = pps_micros;
pps_micros = micros();
//how many microseconds since the last pps? (just in case we're running fast/slow)
micros_since_pps = pps_micros - pps_micros_old;
if (leds_on) {
digitalWrite(PPS_PIN, !digitalRead(PPS_PIN)); // Toggle led
}
//Serial.println("pps");
//process all events to the event buffer now that the second has ended
//Serial.print("Dumping Event Buffer");
//print out the event stack
for (int i = 0; i < eventStack; i++) {
sprintf(txt, "Event: sub second micros:%d/%d; Event Count:%d\n", evttime[i], micros_since_pps, (eventCount - eventStack + i));
WriteToOutputBuff(txt);
}
sprintf(txt, "PPS: GPS lock:1;\n");
WriteToOutputBuff(txt);
//now print the sensors
//Serial.print("Sensor section");
lsm.read(); /* ask it to read in the data */
/* Get a new sensor event */
sensors_event_t a, m, g, temp;
lsm.getEvent(&a, &m, &g, &temp);
float pressure = ps.readPressureMillibars();
float altitude = ps.pressureToAltitudeMeters(pressure);
float temperature = ps.readTemperatureC();
float humd = myHumidity.readHumidity();
float temphum = myHumidity.readTemperature();
Serial.print("Altitude: ");
Serial.print(altitude, 6);
Serial.println(';');
Serial.print("TemperatureCBaro: ");
Serial.print(temperature, 6);
Serial.println(';');
Serial.print("AccelX: ");
Serial.print(a.acceleration.x, 6);
Serial.println(';');
Serial.print("AccelY: ");
Serial.print(a.acceleration.y, 6);
Serial.println(';');
Serial.print("AccelZ: ");
Serial.print(a.acceleration.z, 6);
Serial.println(';');
Serial.print("MagX: ");
Serial.print(m.magnetic.x, 6);
Serial.println(';');
Serial.print("MagY: ");
Serial.print(m.magnetic.y, 6);
Serial.println(';');
Serial.print("MagZ: ");
Serial.print(m.magnetic.z, 6);
Serial.println(';');
Serial.print("TemperatureCHumid: ");
Serial.print(temphum, 6);
Serial.println(';');
Serial.print("Humidity: ");
Serial.print(humd, 6);
Serial.println(';');
//average temp from all 3 sensors
float out = 0;
out = temphum + temperature;
out = out / 2;
Serial.print("TemperatureC: ");
Serial.print(out, 6);
Serial.println(';');
//Serial.print(txts);
//now we can reset the event stack and listen for events again
eventStack=0;
attachInterrupt(digitalPinToInterrupt(PB10), Event_Int, RISING);
}
void Event_Int()
{
/* WriteToOutputBuff("evtx:");
ltoa(eventCount,numconvbuff,10);
WriteToOutputBuff(numconvbuff);
//Serial.print(name);
Serial.print("count=");
Serial.print(eventCount);
Serial.print(" ");
Serial.println(micros()-pps_micros);
*/
eventCount++;
//Serial.println("e");
//write this event to the stack of times
evttime[eventStack] = micros() - pps_micros;
//increment the stack counter
eventStack++;
//turn on the event led
last_event_LED = millis();
if (leds_on) {
digitalWrite(EVT_PIN, HIGH);
}
}
byte setHV(byte _send) // This function is what bitbangs the data
{
if (_send > 0x5F) {
for (int i = 0; i < 8; i++) // There are 8 bits in a byte
{
digitalWrite(PC3, bitRead(_send, 7 - i)); // Set MOSI
//delay(1);
digitalWrite(PB13, HIGH); // SCK high
//bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO
digitalWrite(PB13, LOW); // SCK low
//digitalWrite(MOSI_pin, LOW); // Set MOSI
}
//digitalWrite(SS_pin[j], HIGH); // SS high again
}
}
;

50
gps_reading.ino Normal file
View file

@ -0,0 +1,50 @@
// WARNING: One up the spout !!
// The GPS chip puts the next nmea string in its output buffer
// only if its been read, IE its empty.
// So if you read infrequently the string in the buffer is old and
// has the WRONG time !!! The string lies around like a bullet in
// the breach waiting for some mug.
//I don't know who wrote that comment.. I'm guessing it was Julian!
boolean pipeGPS() {
while (GPS.available()) {
char c[2];
c[0] = GPS.read();
c[1]='\0'; //null character for termination required.
//Serial.print(c);
WriteToOutputBuff(c);
}
}
// GPS setup
void GpsSetup() {
// definitions for different outputs
// only one of these strings can be used at a time
// otherwise they will overwrite each other
// for more information take a look at the QUECTEL L70 protocoll specification: http://docs-europe.electrocomponents.com/webdocs/147d/0900766b8147dbdd.pdf
#define RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" // RCM & GGA
#define ZDA "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*29" // ZDA
#define GGAZDA "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*28" // GGA & ZDA
#define GGA "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" // GGA
// gets the firmware version
#define FMWVERS "$PMTK605*31" // PMTK_Q_RELEASE
// Sets the update intervall
#define NORMAL "$PMTK220,1000*1F" // PMTK_SET_NMEA_UPDATE_1HZ
// disables updates for the antenna status (only Adafruit ultimate GPS?)
#define NOANTENNA "$PGCMD,33,0*6D" // PGCMD_NOAN
delay(500); //added delay to give GPS time to boot.
GPS.println(NOANTENNA);
GPS.println(GGAZDA);
GPS.println(NORMAL);
GPS.println(FMWVERS);
delay(1000); //wait a bit longer and repeat just in case it hasn't booted
GPS.println(NOANTENNA);
GPS.println(GGAZDA);
GPS.println(NORMAL);
GPS.println(FMWVERS);
}

594
src/Adafruit_LSM9DS1.cpp Normal file
View file

@ -0,0 +1,594 @@
/***************************************************************************
This is a library for the LSM9DS1 Accelerometer and magnentometer/compass
Designed specifically to work with the Adafruit LSM9DS1 Breakouts
These sensors use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Kevin Townsend for Adafruit Industries.
BSD license, all text above must be included in any redistribution
***************************************************************************/
#include "Adafruit_LSM9DS1.h"
/***************************************************************************
CONSTRUCTOR
***************************************************************************/
void Adafruit_LSM9DS1::initI2C( TwoWire* wireBus, int32_t sensorID ) {
_i2c = true;
_wire = wireBus;
_lsm9dso_sensorid_accel = sensorID + 1;
_lsm9dso_sensorid_mag = sensorID + 2;
_lsm9dso_sensorid_gyro = sensorID + 3;
_lsm9dso_sensorid_temp = sensorID + 4;
_accelSensor = Sensor(this, &Adafruit_LSM9DS1::readAccel, &Adafruit_LSM9DS1::getAccelEvent, &Adafruit_LSM9DS1::getAccelSensor);
_magSensor = Sensor(this, &Adafruit_LSM9DS1::readMag, &Adafruit_LSM9DS1::getMagEvent, &Adafruit_LSM9DS1::getMagSensor);
_gyroSensor = Sensor(this, &Adafruit_LSM9DS1::readGyro, &Adafruit_LSM9DS1::getGyroEvent, &Adafruit_LSM9DS1::getGyroSensor);
_tempSensor = Sensor(this, &Adafruit_LSM9DS1::readTemp, &Adafruit_LSM9DS1::getTempEvent, &Adafruit_LSM9DS1::getTempSensor);
}
// default
Adafruit_LSM9DS1::Adafruit_LSM9DS1( int32_t sensorID ) {
initI2C(&Wire, sensorID);
}
Adafruit_LSM9DS1::Adafruit_LSM9DS1( TwoWire* wireBus, int32_t sensorID ) {
initI2C(wireBus, sensorID);
}
Adafruit_LSM9DS1::Adafruit_LSM9DS1(int8_t xgcs, int8_t mcs, int32_t sensorID ) {
_i2c = false;
// hardware SPI!
_csm = mcs;
_csxg = xgcs;
_mosi = _miso = _clk = -1;
_lsm9dso_sensorid_accel = sensorID + 1;
_lsm9dso_sensorid_mag = sensorID + 2;
_lsm9dso_sensorid_gyro = sensorID + 3;
_lsm9dso_sensorid_temp = sensorID + 4;
_accelSensor = Sensor(this, &Adafruit_LSM9DS1::readAccel, &Adafruit_LSM9DS1::getAccelEvent, &Adafruit_LSM9DS1::getAccelSensor);
_magSensor = Sensor(this, &Adafruit_LSM9DS1::readMag, &Adafruit_LSM9DS1::getMagEvent, &Adafruit_LSM9DS1::getMagSensor);
_gyroSensor = Sensor(this, &Adafruit_LSM9DS1::readGyro, &Adafruit_LSM9DS1::getGyroEvent, &Adafruit_LSM9DS1::getGyroSensor);
_tempSensor = Sensor(this, &Adafruit_LSM9DS1::readTemp, &Adafruit_LSM9DS1::getTempEvent, &Adafruit_LSM9DS1::getTempSensor);
}
Adafruit_LSM9DS1::Adafruit_LSM9DS1(int8_t sclk, int8_t smiso, int8_t smosi, int8_t xgcs, int8_t mcs, int32_t sensorID ) {
_i2c = false;
// software SPI!
_csm = mcs;
_csxg = xgcs;
_mosi = smosi;
_miso = smiso;
_clk = sclk;
_lsm9dso_sensorid_accel = sensorID + 1;
_lsm9dso_sensorid_mag = sensorID + 2;
_lsm9dso_sensorid_gyro = sensorID + 3;
_lsm9dso_sensorid_temp = sensorID + 4;
_accelSensor = Sensor(this, &Adafruit_LSM9DS1::readAccel, &Adafruit_LSM9DS1::getAccelEvent, &Adafruit_LSM9DS1::getAccelSensor);
_magSensor = Sensor(this, &Adafruit_LSM9DS1::readMag, &Adafruit_LSM9DS1::getMagEvent, &Adafruit_LSM9DS1::getMagSensor);
_gyroSensor = Sensor(this, &Adafruit_LSM9DS1::readGyro, &Adafruit_LSM9DS1::getGyroEvent, &Adafruit_LSM9DS1::getGyroSensor);
_tempSensor = Sensor(this, &Adafruit_LSM9DS1::readTemp, &Adafruit_LSM9DS1::getTempEvent, &Adafruit_LSM9DS1::getTempSensor);
}
bool Adafruit_LSM9DS1::begin()
{
if (_i2c) {
_wire->setSDA(PB7);
_wire->setSCL(PB8);
_wire->begin();
} else if (_clk == -1) {
// Hardware SPI
pinMode(_csxg, OUTPUT);
pinMode(_csm, OUTPUT);
digitalWrite(_csxg, HIGH);
digitalWrite(_csm, HIGH);
SPI.begin();
} else {
//Serial.println("softSPI");
// Sofware SPI
pinMode(_clk, OUTPUT);
pinMode(_mosi, OUTPUT);
pinMode(_miso, INPUT);
pinMode(_csxg, OUTPUT);
pinMode(_csm, OUTPUT);
digitalWrite(_csxg, HIGH);
digitalWrite(_csm, HIGH);
digitalWrite(_clk, HIGH);
}
// soft reset & reboot accel/gyro
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG8, 0x05);
// soft reset & reboot magnetometer
write8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG2_M, 0x0C);
delay(10);
/*
for (uint8_t i=0; i<0x30; i++) {
Serial.print("XG $"); Serial.print(i, HEX); Serial.print(" = 0x");
Serial.println(read8(XGTYPE, i), HEX);
}
for (uint8_t i=0; i<0x30; i++) {
Serial.print("M $"); Serial.print(i, HEX); Serial.print(" = 0x");
Serial.println(read8(MAGTYPE, i), HEX);
}
*/
uint8_t id = read8(XGTYPE, LSM9DS1_REGISTER_WHO_AM_I_XG);
//Serial.print ("XG whoami: 0x"); Serial.println(id, HEX);
if (id != LSM9DS1_XG_ID)
return false;
id = read8(MAGTYPE, LSM9DS1_REGISTER_WHO_AM_I_M);
//Serial.print ("MAG whoami: 0x"); Serial.println(id, HEX);
if (id != LSM9DS1_MAG_ID)
return false;
// enable gyro continuous
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG1_G, 0xC0); // on XYZ
// Enable the accelerometer continous
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG5_XL, 0x38); // enable X Y and Z axis
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG6_XL, 0xC0); // 1 KHz out data rate, BW set by ODR, 408Hz anti-aliasing
// enable mag continuous
//write8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG1_M, 0xFC); // high perf XY, 80 Hz ODR
write8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG3_M, 0x00); // continuous mode
//write8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG4_M, 0x0C); // high perf Z mode
// Set default ranges for the various sensors
setupAccel(LSM9DS1_ACCELRANGE_2G);
setupMag(LSM9DS1_MAGGAIN_4GAUSS);
setupGyro(LSM9DS1_GYROSCALE_245DPS);
return true;
}
/***************************************************************************
PUBLIC FUNCTIONS
***************************************************************************/
void Adafruit_LSM9DS1::read()
{
/* Read all the sensors. */
readAccel();
readMag();
readGyro();
readTemp();
}
void Adafruit_LSM9DS1::readAccel() {
// Read the accelerometer
byte buffer[6];
readBuffer(XGTYPE,
0x80 | LSM9DS1_REGISTER_OUT_X_L_XL,
6, buffer);
uint8_t xlo = buffer[0];
int16_t xhi = buffer[1];
uint8_t ylo = buffer[2];
int16_t yhi = buffer[3];
uint8_t zlo = buffer[4];
int16_t zhi = buffer[5];
// Shift values to create properly formed integer (low byte first)
xhi <<= 8; xhi |= xlo;
yhi <<= 8; yhi |= ylo;
zhi <<= 8; zhi |= zlo;
accelData.x = xhi;
accelData.y = yhi;
accelData.z = zhi;
}
void Adafruit_LSM9DS1::readMag() {
// Read the magnetometer
byte buffer[6];
readBuffer(MAGTYPE,
0x80 | LSM9DS1_REGISTER_OUT_X_L_M,
6, buffer);
uint8_t xlo = buffer[0];
int16_t xhi = buffer[1];
uint8_t ylo = buffer[2];
int16_t yhi = buffer[3];
uint8_t zlo = buffer[4];
int16_t zhi = buffer[5];
// Shift values to create properly formed integer (low byte first)
xhi <<= 8; xhi |= xlo;
yhi <<= 8; yhi |= ylo;
zhi <<= 8; zhi |= zlo;
magData.x = xhi;
magData.y = yhi;
magData.z = zhi;
}
void Adafruit_LSM9DS1::readGyro() {
// Read gyro
byte buffer[6];
readBuffer(XGTYPE,
0x80 | LSM9DS1_REGISTER_OUT_X_L_G,
6, buffer);
uint8_t xlo = buffer[0];
int16_t xhi = buffer[1];
uint8_t ylo = buffer[2];
int16_t yhi = buffer[3];
uint8_t zlo = buffer[4];
int16_t zhi = buffer[5];
// Shift values to create properly formed integer (low byte first)
xhi <<= 8; xhi |= xlo;
yhi <<= 8; yhi |= ylo;
zhi <<= 8; zhi |= zlo;
gyroData.x = xhi;
gyroData.y = yhi;
gyroData.z = zhi;
}
void Adafruit_LSM9DS1::readTemp() {
// Read temp sensor
byte buffer[2];
readBuffer(XGTYPE,
0x80 | LSM9DS1_REGISTER_TEMP_OUT_L,
2, buffer);
uint8_t xlo = buffer[0];
int16_t xhi = buffer[1];
xhi <<= 8; xhi |= xlo;
// Shift values to create properly formed integer (low byte first)
temperature = xhi;
}
void Adafruit_LSM9DS1::setupAccel ( lsm9ds1AccelRange_t range )
{
uint8_t reg = read8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG6_XL);
reg &= ~(0b00011000);
reg |= range;
//Serial.println("set range: ");
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG6_XL, reg );
switch (range)
{
case LSM9DS1_ACCELRANGE_2G:
_accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_2G;
break;
case LSM9DS1_ACCELRANGE_4G:
_accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_4G;
break;
case LSM9DS1_ACCELRANGE_8G:
_accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_8G;
break;
case LSM9DS1_ACCELRANGE_16G:
_accel_mg_lsb =LSM9DS1_ACCEL_MG_LSB_16G;
break;
}
}
void Adafruit_LSM9DS1::setupMag ( lsm9ds1MagGain_t gain )
{
uint8_t reg = read8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG2_M);
reg &= ~(0b01100000);
reg |= gain;
write8(MAGTYPE, LSM9DS1_REGISTER_CTRL_REG2_M, reg );
switch(gain)
{
case LSM9DS1_MAGGAIN_4GAUSS:
_mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_4GAUSS;
break;
case LSM9DS1_MAGGAIN_8GAUSS:
_mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_8GAUSS;
break;
case LSM9DS1_MAGGAIN_12GAUSS:
_mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_12GAUSS;
break;
case LSM9DS1_MAGGAIN_16GAUSS:
_mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_16GAUSS;
break;
}
}
void Adafruit_LSM9DS1::setupGyro ( lsm9ds1GyroScale_t scale )
{
uint8_t reg = read8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG1_G);
reg &= ~(0b00011000);
reg |= scale;
write8(XGTYPE, LSM9DS1_REGISTER_CTRL_REG1_G, reg );
switch(scale)
{
case LSM9DS1_GYROSCALE_245DPS:
_gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_245DPS;
break;
case LSM9DS1_GYROSCALE_500DPS:
_gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_500DPS;
break;
case LSM9DS1_GYROSCALE_2000DPS:
_gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_2000DPS;
break;
}
}
/***************************************************************************
UNIFIED SENSOR FUNCTIONS
***************************************************************************/
/**************************************************************************/
/*!
@brief Gets the most recent accel sensor event
*/
/**************************************************************************/
bool Adafruit_LSM9DS1::getEvent(sensors_event_t *accelEvent,
sensors_event_t *magEvent,
sensors_event_t *gyroEvent,
sensors_event_t *tempEvent )
{
/* Grab new sensor reading and timestamp. */
read();
uint32_t timestamp = millis();
/* Update appropriate sensor events. */
if (accelEvent) getAccelEvent(accelEvent, timestamp);
if (magEvent) getMagEvent(magEvent, timestamp);
if (gyroEvent) getGyroEvent(gyroEvent, timestamp);
if (tempEvent) getTempEvent(tempEvent, timestamp);
return true;
}
/**************************************************************************/
/*!
@brief Gets the sensor_t data
*/
/**************************************************************************/
void Adafruit_LSM9DS1::getSensor(sensor_t *accel, sensor_t *mag,
sensor_t *gyro, sensor_t *temp )
{
/* Update appropriate sensor metadata. */
if (accel) getAccelSensor(accel);
if (mag) getMagSensor(mag);
if (gyro) getGyroSensor(gyro);
if (temp) getTempSensor(temp);
}
/***************************************************************************
PRIVATE FUNCTIONS
***************************************************************************/
void Adafruit_LSM9DS1::write8(boolean type, byte reg, byte value)
{
byte address, _cs;
if (type == MAGTYPE) {
address = LSM9DS1_ADDRESS_MAG;
_cs = _csm;
} else {
address = LSM9DS1_ADDRESS_ACCELGYRO;
_cs = _csxg;
}
if (_i2c) {
_wire->beginTransmission(address);
_wire->write(reg);
_wire->write(value);
_wire->endTransmission();
/*
Serial.print("0x"); Serial.print(address, HEX);
Serial.print(" $"); Serial.print(reg, HEX); Serial.print(" = ");
Serial.println(value, HEX);
*/
} else {
digitalWrite(_cs, LOW);
if (_clk == -1) // hardware SPI
SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE3));
// set address
spixfer(reg & 0x7F); // write data
spixfer(value);
if (_clk == -1) // hardware SPI
SPI.endTransaction();
digitalWrite(_cs, HIGH);
}
}
byte Adafruit_LSM9DS1::read8(boolean type, byte reg)
{
uint8_t value;
readBuffer(type, reg, 1, &value);
return value;
}
byte Adafruit_LSM9DS1::readBuffer(boolean type, byte reg, byte len, uint8_t *buffer)
{
byte address, _cs;
if (type == MAGTYPE) {
address = LSM9DS1_ADDRESS_MAG;
_cs = _csm;
} else {
address = LSM9DS1_ADDRESS_ACCELGYRO;
_cs = _csxg;
}
if (_i2c) {
_wire->beginTransmission(address);
_wire->write(reg);
_wire->endTransmission();
if (_wire->requestFrom(address, (byte)len) != len) {
return 0;
}
/*
Serial.print("0x"); Serial.print(address, HEX);
Serial.print(" $"); Serial.print(reg, HEX); Serial.print(": ");
*/
for (uint8_t i=0; i<len; i++) {
buffer[i] = _wire->read();
//Serial.print(buffer[i], HEX); Serial.print(", ");
}
//Serial.println();
} else {
if (_clk == -1) // hardware SPI
SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE3));
else
digitalWrite(_clk, HIGH);
// set address
digitalWrite(_cs, LOW);
if(type == MAGTYPE)
reg |= 0x40;
spixfer(reg | 0x80 ); // readdata
for (uint8_t i=0; i<len; i++) {
buffer[i] = spixfer(0);
}
if (_clk == -1) // hardware SPI
SPI.endTransaction();
else
digitalWrite(_clk, HIGH);
digitalWrite(_cs, HIGH);
}
return len;
}
uint8_t Adafruit_LSM9DS1::spixfer(uint8_t data) {
if (_clk == -1) {
//Serial.println("Hardware SPI");
return SPI.transfer(data);
} else {
//Serial.println("Software SPI");
uint8_t reply = 0;
for (int i=7; i>=0; i--) {
reply <<= 1;
digitalWrite(_clk, LOW);
digitalWrite(_mosi, data & (1<<i));
digitalWrite(_clk, HIGH);
if (digitalRead(_miso))
reply |= 1;
}
return reply;
}
}
void Adafruit_LSM9DS1::getAccelEvent(sensors_event_t* event, uint32_t timestamp) {
memset(event, 0, sizeof(sensors_event_t));
event->version = sizeof(sensors_event_t);
event->sensor_id = _lsm9dso_sensorid_accel;
event->type = SENSOR_TYPE_ACCELEROMETER;
event->timestamp = timestamp;
event->acceleration.x = accelData.x * _accel_mg_lsb;
event->acceleration.x /= 1000;
event->acceleration.x *= SENSORS_GRAVITY_STANDARD;
event->acceleration.y = accelData.y * _accel_mg_lsb;
event->acceleration.y /= 1000;
event->acceleration.y *= SENSORS_GRAVITY_STANDARD;
event->acceleration.z = accelData.z * _accel_mg_lsb;
event->acceleration.z /= 1000;
event->acceleration.z *= SENSORS_GRAVITY_STANDARD;
}
void Adafruit_LSM9DS1::getMagEvent(sensors_event_t* event, uint32_t timestamp) {
memset(event, 0, sizeof(sensors_event_t));
event->version = sizeof(sensors_event_t);
event->sensor_id = _lsm9dso_sensorid_mag;
event->type = SENSOR_TYPE_MAGNETIC_FIELD;
event->timestamp = timestamp;
event->magnetic.x = magData.x * _mag_mgauss_lsb;
event->magnetic.x /= 1000;
event->magnetic.y = magData.y * _mag_mgauss_lsb;
event->magnetic.y /= 1000;
event->magnetic.z = magData.z * _mag_mgauss_lsb;
event->magnetic.z /= 1000;
}
void Adafruit_LSM9DS1::getGyroEvent(sensors_event_t* event, uint32_t timestamp) {
memset(event, 0, sizeof(sensors_event_t));
event->version = sizeof(sensors_event_t);
event->sensor_id = _lsm9dso_sensorid_accel;
event->type = SENSOR_TYPE_GYROSCOPE;
event->timestamp = timestamp;
event->gyro.x = gyroData.x * _gyro_dps_digit;
event->gyro.y = gyroData.y * _gyro_dps_digit;
event->gyro.z = gyroData.z * _gyro_dps_digit;
}
void Adafruit_LSM9DS1::getTempEvent(sensors_event_t* event, uint32_t timestamp) {
memset(event, 0, sizeof(sensors_event_t));
event->version = sizeof(sensors_event_t);
event->sensor_id = _lsm9dso_sensorid_temp;
event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
event->timestamp = timestamp;
// This is just a guess since the staring point (21C here) isn't documented :(
event->temperature = 21.0 + (float)temperature/8;
//event->temperature /= LSM9DS1_TEMP_LSB_DEGREE_CELSIUS;
}
void Adafruit_LSM9DS1::getAccelSensor(sensor_t* sensor) {
memset(sensor, 0, sizeof(sensor_t));
strncpy (sensor->name, "LSM9DS1_A", sizeof(sensor->name) - 1);
sensor->name[sizeof(sensor->name)- 1] = 0;
sensor->version = 1;
sensor->sensor_id = _lsm9dso_sensorid_accel;
sensor->type = SENSOR_TYPE_ACCELEROMETER;
sensor->min_delay = 0;
sensor->max_value = 0.0; // ToDo
sensor->min_value = 0.0; // ToDo
sensor->resolution = 0.0; // ToDo
}
void Adafruit_LSM9DS1::getMagSensor(sensor_t* sensor) {
memset(sensor, 0, sizeof(sensor_t));
strncpy (sensor->name, "LSM9DS1_M", sizeof(sensor->name) - 1);
sensor->name[sizeof(sensor->name)- 1] = 0;
sensor->version = 1;
sensor->sensor_id = _lsm9dso_sensorid_mag;
sensor->type = SENSOR_TYPE_MAGNETIC_FIELD;
sensor->min_delay = 0;
sensor->max_value = 0.0; // ToDo
sensor->min_value = 0.0; // ToDo
sensor->resolution = 0.0; // ToDo
}
void Adafruit_LSM9DS1::getGyroSensor(sensor_t* sensor) {
memset(sensor, 0, sizeof(sensor_t));
strncpy (sensor->name, "LSM9DS1_G", sizeof(sensor->name) - 1);
sensor->name[sizeof(sensor->name)- 1] = 0;
sensor->version = 1;
sensor->sensor_id = _lsm9dso_sensorid_gyro;
sensor->type = SENSOR_TYPE_GYROSCOPE;
sensor->min_delay = 0;
sensor->max_value = 0.0; // ToDo
sensor->min_value = 0.0; // ToDo
sensor->resolution = 0.0; // ToDo
}
void Adafruit_LSM9DS1::getTempSensor(sensor_t* sensor) {
memset(sensor, 0, sizeof(sensor_t));
strncpy (sensor->name, "LSM9DS1_T", sizeof(sensor->name) - 1);
sensor->name[sizeof(sensor->name)- 1] = 0;
sensor->version = 1;
sensor->sensor_id = _lsm9dso_sensorid_temp;
sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
sensor->min_delay = 0;
sensor->max_value = 0.0; // ToDo
sensor->min_value = 0.0; // ToDo
sensor->resolution = 0.0; // ToDo
}

280
src/Adafruit_LSM9DS1.h Normal file
View file

@ -0,0 +1,280 @@
/***************************************************************************
This is a library for the LSM9DS1 Accelerometer and magnentometer/compass
Designed specifically to work with the Adafruit LSM9DS1 Breakouts
These sensors use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Kevin Townsend for Adafruit Industries.
BSD license, all text above must be included in any redistribution
***************************************************************************/
#ifndef __LSM9DS1_H__
#define __LSM9DS1_H__
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Wire.h"
#include <SPI.h>
#include "Adafruit_Sensor.h"
#define LSM9DS1_ADDRESS_ACCELGYRO (0x6A)
#define LSM9DS1_ADDRESS_MAG (0x1C)
#define LSM9DS1_XG_ID (0b01101000)
#define LSM9DS1_MAG_ID (0b00111101)
// Linear Acceleration: mg per LSB
#define LSM9DS1_ACCEL_MG_LSB_2G (0.061F)
#define LSM9DS1_ACCEL_MG_LSB_4G (0.122F)
#define LSM9DS1_ACCEL_MG_LSB_8G (0.244F)
#define LSM9DS1_ACCEL_MG_LSB_16G (0.732F)
// Magnetic Field Strength: gauss range
#define LSM9DS1_MAG_MGAUSS_4GAUSS (0.14F)
#define LSM9DS1_MAG_MGAUSS_8GAUSS (0.29F)
#define LSM9DS1_MAG_MGAUSS_12GAUSS (0.43F)
#define LSM9DS1_MAG_MGAUSS_16GAUSS (0.58F)
// Angular Rate: dps per LSB
#define LSM9DS1_GYRO_DPS_DIGIT_245DPS (0.00875F)
#define LSM9DS1_GYRO_DPS_DIGIT_500DPS (0.01750F)
#define LSM9DS1_GYRO_DPS_DIGIT_2000DPS (0.07000F)
// Temperature: LSB per degree celsius
#define LSM9DS1_TEMP_LSB_DEGREE_CELSIUS (8) // 1°C = 8, 25° = 200, etc.
#define MAGTYPE (true)
#define XGTYPE (false)
/* Forward reference required for function pointers below. */
class Adafruit_LSM9DS1;
/* Pointer to member functions for read, get event, and get sensor. These are used */
/* by the Adafruit_LSM9DS1::Sensor class to read and retrieve individual sensors. */
typedef void (Adafruit_LSM9DS1::*lsm9ds1_read_func)(void);
typedef void (Adafruit_LSM9DS1::*lsm9ds1_get_event_func)(sensors_event_t*, uint32_t);
typedef void (Adafruit_LSM9DS1::*lsm9ds1_get_sensor_func)(sensor_t*);
class Adafruit_LSM9DS1
{
public:
Adafruit_LSM9DS1 ( int32_t sensorID = 0 );
Adafruit_LSM9DS1 ( TwoWire* wireBus, int32_t sensorID = 0 );
Adafruit_LSM9DS1 ( int8_t xmcs, int8_t gcs, int32_t sensorID = 0 );
Adafruit_LSM9DS1 ( int8_t clk, int8_t miso, int8_t mosi, int8_t xmcs, int8_t gcs, int32_t sensorID = 0 );
void initI2C( TwoWire* wireBus, int32_t sensorID );
typedef enum
{
LSM9DS1_REGISTER_WHO_AM_I_XG = 0x0F,
LSM9DS1_REGISTER_CTRL_REG1_G = 0x10,
LSM9DS1_REGISTER_CTRL_REG2_G = 0x11,
LSM9DS1_REGISTER_CTRL_REG3_G = 0x12,
LSM9DS1_REGISTER_TEMP_OUT_L = 0x15,
LSM9DS1_REGISTER_TEMP_OUT_H = 0x16,
LSM9DS1_REGISTER_STATUS_REG = 0x17,
LSM9DS1_REGISTER_OUT_X_L_G = 0x18,
LSM9DS1_REGISTER_OUT_X_H_G = 0x19,
LSM9DS1_REGISTER_OUT_Y_L_G = 0x1A,
LSM9DS1_REGISTER_OUT_Y_H_G = 0x1B,
LSM9DS1_REGISTER_OUT_Z_L_G = 0x1C,
LSM9DS1_REGISTER_OUT_Z_H_G = 0x1D,
LSM9DS1_REGISTER_CTRL_REG4 = 0x1E,
LSM9DS1_REGISTER_CTRL_REG5_XL = 0x1F,
LSM9DS1_REGISTER_CTRL_REG6_XL = 0x20,
LSM9DS1_REGISTER_CTRL_REG7_XL = 0x21,
LSM9DS1_REGISTER_CTRL_REG8 = 0x22,
LSM9DS1_REGISTER_CTRL_REG9 = 0x23,
LSM9DS1_REGISTER_CTRL_REG10 = 0x24,
LSM9DS1_REGISTER_OUT_X_L_XL = 0x28,
LSM9DS1_REGISTER_OUT_X_H_XL = 0x29,
LSM9DS1_REGISTER_OUT_Y_L_XL = 0x2A,
LSM9DS1_REGISTER_OUT_Y_H_XL = 0x2B,
LSM9DS1_REGISTER_OUT_Z_L_XL = 0x2C,
LSM9DS1_REGISTER_OUT_Z_H_XL = 0x2D,
} lsm9ds1AccGyroRegisters_t;
typedef enum
{
LSM9DS1_REGISTER_WHO_AM_I_M = 0x0F,
LSM9DS1_REGISTER_CTRL_REG1_M = 0x20,
LSM9DS1_REGISTER_CTRL_REG2_M = 0x21,
LSM9DS1_REGISTER_CTRL_REG3_M = 0x22,
LSM9DS1_REGISTER_CTRL_REG4_M = 0x23,
LSM9DS1_REGISTER_CTRL_REG5_M = 0x24,
LSM9DS1_REGISTER_STATUS_REG_M = 0x27,
LSM9DS1_REGISTER_OUT_X_L_M = 0x28,
LSM9DS1_REGISTER_OUT_X_H_M = 0x29,
LSM9DS1_REGISTER_OUT_Y_L_M = 0x2A,
LSM9DS1_REGISTER_OUT_Y_H_M = 0x2B,
LSM9DS1_REGISTER_OUT_Z_L_M = 0x2C,
LSM9DS1_REGISTER_OUT_Z_H_M = 0x2D,
LSM9DS1_REGISTER_CFG_M = 0x30,
LSM9DS1_REGISTER_INT_SRC_M = 0x31,
} lsm9ds1MagRegisters_t;
typedef enum
{
LSM9DS1_ACCELRANGE_2G = (0b00 << 3),
LSM9DS1_ACCELRANGE_16G = (0b01 << 3),
LSM9DS1_ACCELRANGE_4G = (0b10 << 3),
LSM9DS1_ACCELRANGE_8G = (0b11 << 3),
} lsm9ds1AccelRange_t;
typedef enum
{
LSM9DS1_ACCELDATARATE_POWERDOWN = (0b0000 << 4),
LSM9DS1_ACCELDATARATE_3_125HZ = (0b0001 << 4),
LSM9DS1_ACCELDATARATE_6_25HZ = (0b0010 << 4),
LSM9DS1_ACCELDATARATE_12_5HZ = (0b0011 << 4),
LSM9DS1_ACCELDATARATE_25HZ = (0b0100 << 4),
LSM9DS1_ACCELDATARATE_50HZ = (0b0101 << 4),
LSM9DS1_ACCELDATARATE_100HZ = (0b0110 << 4),
LSM9DS1_ACCELDATARATE_200HZ = (0b0111 << 4),
LSM9DS1_ACCELDATARATE_400HZ = (0b1000 << 4),
LSM9DS1_ACCELDATARATE_800HZ = (0b1001 << 4),
LSM9DS1_ACCELDATARATE_1600HZ = (0b1010 << 4)
} lm9ds1AccelDataRate_t;
typedef enum
{
LSM9DS1_MAGGAIN_4GAUSS = (0b00 << 5), // +/- 4 gauss
LSM9DS1_MAGGAIN_8GAUSS = (0b01 << 5), // +/- 8 gauss
LSM9DS1_MAGGAIN_12GAUSS = (0b10 << 5), // +/- 12 gauss
LSM9DS1_MAGGAIN_16GAUSS = (0b11 << 5) // +/- 16 gauss
} lsm9ds1MagGain_t;
typedef enum
{
LSM9DS1_MAGDATARATE_3_125HZ = (0b000 << 2),
LSM9DS1_MAGDATARATE_6_25HZ = (0b001 << 2),
LSM9DS1_MAGDATARATE_12_5HZ = (0b010 << 2),
LSM9DS1_MAGDATARATE_25HZ = (0b011 << 2),
LSM9DS1_MAGDATARATE_50HZ = (0b100 << 2),
LSM9DS1_MAGDATARATE_100HZ = (0b101 << 2)
} lsm9ds1MagDataRate_t;
typedef enum
{
LSM9DS1_GYROSCALE_245DPS = (0b00 << 3), // +/- 245 degrees per second rotation
LSM9DS1_GYROSCALE_500DPS = (0b01 << 3), // +/- 500 degrees per second rotation
LSM9DS1_GYROSCALE_2000DPS = (0b11 << 3) // +/- 2000 degrees per second rotation
} lsm9ds1GyroScale_t;
typedef struct vector_s
{
float x;
float y;
float z;
} lsm9ds1Vector_t;
lsm9ds1Vector_t accelData; // Last read accelerometer data will be available here
lsm9ds1Vector_t magData; // Last read magnetometer data will be available here
lsm9ds1Vector_t gyroData; // Last read gyroscope data will be available here
int16_t temperature; // Last read temperzture data will be available here
bool begin ( void );
void read ( void );
void readAccel ( void );
void readMag ( void );
void readGyro ( void );
void readTemp ( void );
void setupAccel ( lsm9ds1AccelRange_t range );
void setupMag ( lsm9ds1MagGain_t gain );
void setupGyro ( lsm9ds1GyroScale_t scale );
void write8 ( boolean type, byte reg, byte value );
byte read8 ( boolean type, byte reg);
byte readBuffer ( boolean type, byte reg, byte len, uint8_t *buffer);
uint8_t spixfer ( uint8_t data );
/* Adafruit Unified Sensor Functions (not standard yet ... the current base class only */
/* supports one sensor type, and we need to update the unified base class to support */
/* multiple sensors in a single driver, returning an array */
bool getEvent ( sensors_event_t* accel, sensors_event_t* mag, sensors_event_t* gyro, sensors_event_t* temp );
void getSensor ( sensor_t* accel, sensor_t* mag, sensor_t* gyro, sensor_t* temp );
/* Subclass to expose each sensor on the LSM9DS1 as an Adafruit_Sensor instance. */
class Sensor: public Adafruit_Sensor {
public:
Sensor() {}
Sensor(const Sensor& copy):
_parent(copy._parent),
_readFunc(copy._readFunc),
_eventFunc(copy._eventFunc),
_sensorFunc(copy._sensorFunc)
{}
Sensor(Adafruit_LSM9DS1* parent, lsm9ds1_read_func readFunc,
lsm9ds1_get_event_func eventFunc, lsm9ds1_get_sensor_func sensorFunc):
_parent(parent),
_readFunc(readFunc),
_eventFunc(eventFunc),
_sensorFunc(sensorFunc)
{}
virtual bool getEvent(sensors_event_t* event) {
/* Take new reading. */
(_parent->*_readFunc)();
/* Fill in event data. */
(_parent->*_eventFunc)(event, millis());
}
virtual void getSensor(sensor_t* sensor) {
/* Fill in sensor metadata. */
(_parent->*_sensorFunc)(sensor);
}
private:
Adafruit_LSM9DS1* _parent;
lsm9ds1_read_func _readFunc;
lsm9ds1_get_event_func _eventFunc;
lsm9ds1_get_sensor_func _sensorFunc;
};
/* Individual Adafruit_Sensor instances for each sensor on the board. */
Sensor& getAccel ( void ) { return _accelSensor; }
Sensor& getMag ( void ) { return _magSensor; }
Sensor& getGyro ( void ) { return _gyroSensor; }
Sensor& getTemp ( void ) { return _tempSensor; }
private:
boolean _i2c;
TwoWire* _wire;
int8_t _csm, _csxg, _mosi, _miso, _clk;
float _accel_mg_lsb;
float _mag_mgauss_lsb;
float _gyro_dps_digit;
int32_t _lsm9dso_sensorid_accel;
int32_t _lsm9dso_sensorid_mag;
int32_t _lsm9dso_sensorid_gyro;
int32_t _lsm9dso_sensorid_temp;
Sensor _accelSensor;
Sensor _magSensor;
Sensor _gyroSensor;
Sensor _tempSensor;
/* Functions to get individual sensor measurements and metadata. */
/* Note that these functions will NOT update the sensor state before getting */
/* a new reading. You MUST call read() manually to update the sensor state */
/* before calling these functions! */
void getAccelEvent ( sensors_event_t* event, uint32_t timestamp );
void getMagEvent ( sensors_event_t* event, uint32_t timestamp );
void getGyroEvent ( sensors_event_t* event, uint32_t timestamp );
void getTempEvent ( sensors_event_t* event, uint32_t timestamp );
void getAccelSensor ( sensor_t* sensor );
void getMagSensor ( sensor_t* sensor );
void getGyroSensor ( sensor_t* sensor );
void getTempSensor ( sensor_t* sensor );
};
#endif

156
src/Adafruit_Sensor.h Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software< /span>
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
* extended sensor support to include color, voltage and current */
#ifndef _ADAFRUIT_SENSOR_H
#define _ADAFRUIT_SENSOR_H
#ifndef ARDUINO
#include <stdint.h>
#elif ARDUINO >= 100
#include "Arduino.h"
#include "Print.h"
#else
#include "WProgram.h"
#endif
/* Intentionally modeled after sensors.h in the Android API:
* https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h */
/* Constants */
#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */
#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */
#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */
#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH)
#define SENSORS_MAGFIELD_EARTH_MAX (60.0F) /**< Maximum magnetic field on Earth's surface */
#define SENSORS_MAGFIELD_EARTH_MIN (30.0F) /**< Minimum magnetic field on Earth's surface */
#define SENSORS_PRESSURE_SEALEVELHPA (1013.25F) /**< Average sea level pressure is 1013.25 hPa */
#define SENSORS_DPS_TO_RADS (0.017453293F) /**< Degrees/s to rad/s multiplier */
#define SENSORS_GAUSS_TO_MICROTESLA (100) /**< Gauss to micro-Tesla multiplier */
/** Sensor types */
typedef enum
{
SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */
SENSOR_TYPE_MAGNETIC_FIELD = (2),
SENSOR_TYPE_ORIENTATION = (3),
SENSOR_TYPE_GYROSCOPE = (4),
SENSOR_TYPE_LIGHT = (5),
SENSOR_TYPE_PRESSURE = (6),
SENSOR_TYPE_PROXIMITY = (8),
SENSOR_TYPE_GRAVITY = (9),
SENSOR_TYPE_LINEAR_ACCELERATION = (10), /**< Acceleration not including gravity */
SENSOR_TYPE_ROTATION_VECTOR = (11),
SENSOR_TYPE_RELATIVE_HUMIDITY = (12),
SENSOR_TYPE_AMBIENT_TEMPERATURE = (13),
SENSOR_TYPE_VOLTAGE = (15),
SENSOR_TYPE_CURRENT = (16),
SENSOR_TYPE_COLOR = (17)
} sensors_type_t;
/** struct sensors_vec_s is used to return a vector in a common format. */
typedef struct {
union {
float v[3];
struct {
float x;
float y;
float z;
};
/* Orientation sensors */
struct {
float roll; /**< Rotation around the longitudinal axis (the plane body, 'X axis'). Roll is positive and increasing when moving downward. -90°<=roll<=90° */
float pitch; /**< Rotation around the lateral axis (the wing span, 'Y axis'). Pitch is positive and increasing when moving upwards. -180°<=pitch<=180°) */
float heading; /**< Angle between the longitudinal axis (the plane body) and magnetic north, measured clockwise when viewing from the top of the device. 0-359° */
};
};
int8_t status;
uint8_t reserved[3];
} sensors_vec_t;
/** struct sensors_color_s is used to return color data in a common format. */
typedef struct {
union {
float c[3];
/* RGB color space */
struct {
float r; /**< Red component */
float g; /**< Green component */
float b; /**< Blue component */
};
};
uint32_t rgba; /**< 24-bit RGBA value */
} sensors_color_t;
/* Sensor event (36 bytes) */
/** struct sensor_event_s is used to provide a single sensor event in a common format. */
typedef struct
{
int32_t version; /**< must be sizeof(struct sensors_event_t) */
int32_t sensor_id; /**< unique sensor identifier */
int32_t type; /**< sensor type */
int32_t reserved0; /**< reserved */
int32_t timestamp; /**< time is in milliseconds */
union
{
float data[4];
sensors_vec_t acceleration; /**< acceleration values are in meter per second per second (m/s^2) */
sensors_vec_t magnetic; /**< magnetic vector values are in micro-Tesla (uT) */
sensors_vec_t orientation; /**< orientation values are in degrees */
sensors_vec_t gyro; /**< gyroscope values are in rad/s */
float temperature; /**< temperature is in degrees centigrade (Celsius) */
float distance; /**< distance in centimeters */
float light; /**< light in SI lux units */
float pressure; /**< pressure in hectopascal (hPa) */
float relative_humidity; /**< relative humidity in percent */
float current; /**< current in milliamps (mA) */
float voltage; /**< voltage in volts (V) */
sensors_color_t color; /**< color in RGB component values */
};
} sensors_event_t;
/* Sensor details (40 bytes) */
/** struct sensor_s is used to describe basic information about a specific sensor. */
typedef struct
{
char name[12]; /**< sensor name */
int32_t version; /**< version of the hardware + driver */
int32_t sensor_id; /**< unique sensor identifier */
int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
float max_value; /**< maximum value of this sensor's value in SI units */
float min_value; /**< minimum value of this sensor's value in SI units */
float resolution; /**< smallest difference between two values reported by this sensor */
int32_t min_delay; /**< min delay in microseconds between events. zero = not a constant rate */
} sensor_t;
class Adafruit_Sensor {
public:
// Constructor(s)
Adafruit_Sensor() {}
virtual ~Adafruit_Sensor() {}
// These must be defined by the subclass
virtual void enableAutoRange(bool enabled) { (void)enabled; /* suppress unused warning */ };
virtual bool getEvent(sensors_event_t*) = 0;
virtual void getSensor(sensor_t*) = 0;
private:
bool _autoRange;
};
#endif

View file

@ -0,0 +1,25 @@
Copyright (c) 2013 Pololu Corporation. For more information, see
http://www.pololu.com/
http://forum.pololu.com/
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

9
src/LICENSE_sparkfun.md Normal file
View file

@ -0,0 +1,9 @@
License Information
-------------------
The hardware is released under [Creative Commons Share-alike 3.0](http://creativecommons.org/licenses/by-sa/3.0/).
All other code is open source so please feel free to do anything you want with it; you buy me a beer if you use this and we meet someday ([Beerware license](http://en.wikipedia.org/wiki/Beerware)).

235
src/LPS.cpp Normal file
View file

@ -0,0 +1,235 @@
#include "LPS.h"
#include <Wire.h>
// Defines ///////////////////////////////////////////////////////////
// The Arduino two-wire interface uses a 7-bit number for the address,
// and sets the last bit correctly based on reads and writes
#define SA0_LOW_ADDRESS 0b1011100
#define SA0_HIGH_ADDRESS 0b1011101
#define TEST_REG_NACK -1
#define LPS331AP_WHO_ID 0xBB
#define LPS25H_WHO_ID 0xBD
// Constructors //////////////////////////////////////////////////////
LPS::LPS(void)
{
_device = device_auto;
// Pololu board pulls SA0 high, so default assumption is that it is
// high
address = SA0_LOW_ADDRESS;
}
// Public Methods ////////////////////////////////////////////////////
// sets or detects device type and slave address; returns bool indicating success
bool LPS::init(deviceType device, byte sa0)
{
if (!detectDeviceAndAddress(device, (sa0State)sa0))
return false;
switch (_device)
{
case device_25H:
translated_regs[-INTERRUPT_CFG] = LPS25H_INTERRUPT_CFG;
translated_regs[-INT_SOURCE] = LPS25H_INT_SOURCE;
translated_regs[-THS_P_L] = LPS25H_THS_P_L;
translated_regs[-THS_P_H] = LPS25H_THS_P_H;
return true;
break;
case device_331AP:
translated_regs[-INTERRUPT_CFG] = LPS331AP_INTERRUPT_CFG;
translated_regs[-INT_SOURCE] = LPS331AP_INT_SOURCE;
translated_regs[-THS_P_L] = LPS331AP_THS_P_L;
translated_regs[-THS_P_H] = LPS331AP_THS_P_H;
return true;
break;
}
}
// turns on sensor and enables continuous output
void LPS::enableDefault(void)
{
if (_device == device_25H)
{
// 0xB0 = 0b10110000
// PD = 1 (active mode); ODR = 011 (12.5 Hz pressure & temperature output data rate)
writeReg(CTRL_REG1, 0xB0);
}
else if (_device == device_331AP)
{
// 0xE0 = 0b11100000
// PD = 1 (active mode); ODR = 110 (12.5 Hz pressure & temperature output data rate)
writeReg(CTRL_REG1, 0xE0);
}
}
// writes register
void LPS::writeReg(int reg, byte value)
{
// if dummy register address, look up actual translated address (based on device type)
if (reg < 0)
{
reg = translated_regs[-reg];
}
Wire.beginTransmission(address);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
// reads register
byte LPS::readReg(int reg)
{
byte value;
// if dummy register address, look up actual translated address (based on device type)
if (reg < 0)
{
reg = translated_regs[-reg];
}
Wire.beginTransmission(address);
Wire.write(reg);
Wire.endTransmission(false); // restart
Wire.requestFrom(address, (byte)1);
value = Wire.read();
Wire.endTransmission();
return value;
}
// reads pressure in millibars (mbar)/hectopascals (hPa)
float LPS::readPressureMillibars(void)
{
return (float)readPressureRaw() / 4096;
}
// reads pressure in inches of mercury (inHg)
float LPS::readPressureInchesHg(void)
{
return (float)readPressureRaw() / 138706.5;
}
// reads pressure and returns raw 24-bit sensor output
int32_t LPS::readPressureRaw(void)
{
Wire.beginTransmission(address);
// assert MSB to enable register address auto-increment
Wire.write(PRESS_OUT_XL | (1 << 7));
Wire.endTransmission();
Wire.requestFrom(address, (byte)3);
while (Wire.available() < 3);
uint8_t pxl = Wire.read();
uint8_t pl = Wire.read();
uint8_t ph = Wire.read();
// combine bytes
return (int32_t)(int8_t)ph << 16 | (uint16_t)pl << 8 | pxl;
}
// reads temperature in degrees C
float LPS::readTemperatureC(void)
{
return 42.5 + (float)readTemperatureRaw() / 480;
}
// reads temperature in degrees F
float LPS::readTemperatureF(void)
{
return 108.5 + (float)readTemperatureRaw() / 480 * 1.8;
}
// reads temperature and returns raw 16-bit sensor output
int16_t LPS::readTemperatureRaw(void)
{
Wire.beginTransmission(address);
// assert MSB to enable register address auto-increment
Wire.write(TEMP_OUT_L | (1 << 7));
Wire.endTransmission();
Wire.requestFrom(address, (byte)2);
while (Wire.available() < 2);
uint8_t tl = Wire.read();
uint8_t th = Wire.read();
// combine bytes
return (int16_t)(th << 8 | tl);
}
// converts pressure in mbar to altitude in meters, using 1976 US
// Standard Atmosphere model (note that this formula only applies to a
// height of 11 km, or about 36000 ft)
// If altimeter setting (QNH, barometric pressure adjusted to sea
// level) is given, this function returns an indicated altitude
// compensated for actual regional pressure; otherwise, it returns
// the pressure altitude above the standard pressure level of 1013.25
// mbar or 29.9213 inHg
float LPS::pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar)
{
return (1 - pow(pressure_mbar / altimeter_setting_mbar, 0.190263)) * 44330.8;
}
// converts pressure in inHg to altitude in feet; see notes above
float LPS::pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg)
{
return (1 - pow(pressure_inHg / altimeter_setting_inHg, 0.190263)) * 145442;
}
// Private Methods ///////////////////////////////////////////////////
bool LPS::detectDeviceAndAddress(deviceType device, sa0State sa0)
{
if (sa0 == sa0_auto || sa0 == sa0_high)
{
address = SA0_HIGH_ADDRESS;
if (detectDevice(device)) return true;
}
if (sa0 == sa0_auto || sa0 == sa0_low)
{
address = SA0_LOW_ADDRESS;
if (detectDevice(device)) return true;
}
return false;
}
bool LPS::detectDevice(deviceType device)
{
int id = testWhoAmI(address);
if ((device == device_auto || device == device_25H) && id == LPS25H_WHO_ID)
{
_device = device_25H;
return true;
}
if ((device == device_auto || device == device_331AP) && id == LPS331AP_WHO_ID)
{
_device = device_331AP;
return true;
}
return false;
}
int LPS::testWhoAmI(byte address)
{
Wire.beginTransmission(address);
Wire.write(WHO_AM_I);
Wire.endTransmission();
Wire.requestFrom(address, (byte)1);
if (Wire.available())
return Wire.read();
else
return TEST_REG_NACK;
}

110
src/LPS.h Normal file
View file

@ -0,0 +1,110 @@
#ifndef LPS_h
#define LPS_h
#include <Arduino.h> // for byte data type
class LPS
{
public:
enum deviceType { device_331AP, device_25H, device_auto };
enum sa0State { sa0_low, sa0_high, sa0_auto };
// register addresses
// Note: where register names differ between the register mapping table and
// the register descriptions in the datasheets, the names from the register
// descriptions are used here.
enum regAddr
{
REF_P_XL = 0x08,
REF_P_L = 0x09,
REF_P_H = 0x0A,
WHO_AM_I = 0x0F,
RES_CONF = 0x10,
CTRL_REG1 = 0x20,
CTRL_REG2 = 0x21,
CTRL_REG3 = 0x22,
CTRL_REG4 = 0x23, // 25H
STATUS_REG = 0x27,
PRESS_OUT_XL = 0x28,
PRESS_OUT_L = 0x29,
PRESS_OUT_H = 0x2A,
TEMP_OUT_L = 0x2B,
TEMP_OUT_H = 0x2C,
FIFO_CTRL = 0x2E, // 25H
FIFO_STATUS = 0x2F, // 25H
AMP_CTRL = 0x30, // 331AP
RPDS_L = 0x39, // 25H
RPDS_H = 0x3A, // 25H
DELTA_PRESS_XL = 0x3C, // 331AP
DELTA_PRESS_L = 0x3D, // 331AP
DELTA_PRESS_H = 0x3E, // 331AP
// dummy addresses for registers in different locations on different devices;
// the library translates these based on device type
// value with sign flipped is used as index into translated_regs array
INTERRUPT_CFG = -1,
INT_SOURCE = -2,
THS_P_L = -3,
THS_P_H = -4,
// update dummy_reg_count if registers are added here!
// device-specific register addresses
LPS331AP_INTERRUPT_CFG = 0x23,
LPS331AP_INT_SOURCE = 0x24,
LPS331AP_THS_P_L = 0x25,
LPS331AP_THS_P_H = 0x26,
LPS25H_INTERRUPT_CFG = 0x24,
LPS25H_INT_SOURCE = 0x25,
LPS25H_THS_P_L = 0x30,
LPS25H_THS_P_H = 0x31,
};
LPS(void);
bool init(deviceType device = device_auto, byte sa0 = sa0_auto);
deviceType getDeviceType(void) { return _device; }
byte getAddress(void) { return address; }
void enableDefault(void);
void writeReg(int reg, byte value);
byte readReg(int reg);
float readPressureMillibars(void);
float readPressureInchesHg(void);
int32_t readPressureRaw(void);
float readTemperatureC(void);
float readTemperatureF(void);
int16_t readTemperatureRaw(void);
static float pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar = 1013.25);
static float pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg = 29.9213);
private:
deviceType _device; // chip type (331AP or 25H)
byte address;
static const int dummy_reg_count = 4;
regAddr translated_regs[dummy_reg_count + 1]; // index 0 not used
bool detectDeviceAndAddress(deviceType device, sa0State sa0);
bool detectDevice(deviceType device);
int testWhoAmI(byte address);
};
#endif

211
src/SparkFunHTU21D.cpp Normal file
View file

@ -0,0 +1,211 @@
/*
HTU21D Humidity Sensor Library
By: Nathan Seidle
SparkFun Electronics
Date: September 22nd, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
This library allows an Arduino to read from the HTU21D low-cost high-precision humidity sensor.
If you have feature suggestions or need support please use the github support page: https://github.com/sparkfun/HTU21D
Hardware Setup: The HTU21D lives on the I2C bus. Attach the SDA pin to A4, SCL to A5. If you are using the SparkFun
breakout board you *do not* need 4.7k pull-up resistors on the bus (they are built-in).
Link to the breakout board product:
Software:
Call HTU21D.Begin() in setup.
HTU21D.ReadHumidity() will return a float containing the humidity. Ex: 54.7
HTU21D.ReadTemperature() will return a float containing the temperature in Celsius. Ex: 24.1
HTU21D.SetResolution(byte: 0b.76543210) sets the resolution of the readings.
HTU21D.check_crc(message, check_value) verifies the 8-bit CRC generated by the sensor
HTU21D.read_user_register() returns the user register. Used to set resolution.
*/
#include <Wire.h>
#include "SparkFunHTU21D.h"
HTU21D::HTU21D()
{
//Set initial values for private vars
}
//Begin
/*******************************************************************************************/
//Start I2C communication
bool HTU21D::begin(TwoWire &wirePort)
{
_i2cPort = &wirePort; //Grab which port the user wants us to use
_i2cPort->setSDA(PB7);
_i2cPort->setSCL(PB8);
_i2cPort->begin();
// check availability
/*_i2cPort->beginTransmission(HTU21D_ADDRESS);
int err = _i2cPort->beginTransmission(HTU21D_ADDRESS);
if (err == 0){
return true;
} else {
return false;
}*/
return true;
}
#define MAX_WAIT 100
#define DELAY_INTERVAL 10
#define MAX_COUNTER (MAX_WAIT/DELAY_INTERVAL)
//Given a command, reads a given 2-byte value with CRC from the HTU21D
uint16_t HTU21D::readValue(byte cmd)
{
//Request a humidity reading
_i2cPort->beginTransmission(HTU21D_ADDRESS);
_i2cPort->write(cmd); //Measure value (prefer no hold!)
_i2cPort->endTransmission();
//Hang out while measurement is taken. datasheet says 50ms, practice may call for more
bool validResult;
byte counter;
for (counter = 0, validResult = 0 ; counter < MAX_COUNTER && !validResult ; counter++)
{
delay(DELAY_INTERVAL);
//Comes back in three bytes, data(MSB) / data(LSB) / Checksum
validResult = (3 == _i2cPort->requestFrom(HTU21D_ADDRESS, 3));
}
if (!validResult) return (ERROR_I2C_TIMEOUT); //Error out
byte msb, lsb, checksum;
msb = _i2cPort->read();
lsb = _i2cPort->read();
checksum = _i2cPort->read();
uint16_t rawValue = ((uint16_t) msb << 8) | (uint16_t) lsb;
if (checkCRC(rawValue, checksum) != 0) return (ERROR_BAD_CRC); //Error out
return rawValue & 0xFFFC; // Zero out the status bits
}
//Read the humidity
/*******************************************************************************************/
//Calc humidity and return it to the user
//Returns 998 if I2C timed out
//Returns 999 if CRC is wrong
float HTU21D::readHumidity(void)
{
uint16_t rawHumidity = readValue(TRIGGER_HUMD_MEASURE_NOHOLD);
if(rawHumidity == ERROR_I2C_TIMEOUT || rawHumidity == ERROR_BAD_CRC) return(rawHumidity);
//Given the raw humidity data, calculate the actual relative humidity
float tempRH = rawHumidity * (125.0 / 65536.0); //2^16 = 65536
float rh = tempRH - 6.0; //From page 14
return (rh);
}
//Read the temperature
/*******************************************************************************************/
//Calc temperature and return it to the user
//Returns 998 if I2C timed out
//Returns 999 if CRC is wrong
float HTU21D::readTemperature(void)
{
uint16_t rawTemperature = readValue(TRIGGER_TEMP_MEASURE_NOHOLD);
if(rawTemperature == ERROR_I2C_TIMEOUT || rawTemperature == ERROR_BAD_CRC) return(rawTemperature);
//Given the raw temperature data, calculate the actual temperature
float tempTemperature = rawTemperature * (175.72 / 65536.0); //2^16 = 65536
float realTemperature = tempTemperature - 46.85; //From page 14
return (realTemperature);
}
//Set sensor resolution
/*******************************************************************************************/
//Sets the sensor resolution to one of four levels
//Page 12:
// 0/0 = 12bit RH, 14bit Temp
// 0/1 = 8bit RH, 12bit Temp
// 1/0 = 10bit RH, 13bit Temp
// 1/1 = 11bit RH, 11bit Temp
//Power on default is 0/0
void HTU21D::setResolution(byte resolution)
{
byte userRegister = readUserRegister(); //Go get the current register state
userRegister &= B01111110; //Turn off the resolution bits
resolution &= B10000001; //Turn off all other bits but resolution bits
userRegister |= resolution; //Mask in the requested resolution bits
//Request a write to user register
writeUserRegister(userRegister);
}
//Read the user register
byte HTU21D::readUserRegister(void)
{
byte userRegister;
//Request the user register
_i2cPort->beginTransmission(HTU21D_ADDRESS);
_i2cPort->write(READ_USER_REG); //Read the user register
_i2cPort->endTransmission();
//Read result
_i2cPort->requestFrom(HTU21D_ADDRESS, 1);
userRegister = _i2cPort->read();
return (userRegister);
}
void HTU21D::writeUserRegister(byte val)
{
_i2cPort->beginTransmission(HTU21D_ADDRESS);
_i2cPort->write(WRITE_USER_REG); //Write to the user register
_i2cPort->write(val); //Write the new resolution bits
_i2cPort->endTransmission();
}
//Give this function the 2 byte message (measurement) and the check_value byte from the HTU21D
//If it returns 0, then the transmission was good
//If it returns something other than 0, then the communication was corrupted
//From: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
//POLYNOMIAL = 0x0131 = x^8 + x^5 + x^4 + 1 : http://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks
#define SHIFTED_DIVISOR 0x988000 //This is the 0x0131 polynomial shifted to farthest left of three bytes
byte HTU21D::checkCRC(uint16_t message_from_sensor, uint8_t check_value_from_sensor)
{
//Test cases from datasheet:
//message = 0xDC, checkvalue is 0x79
//message = 0x683A, checkvalue is 0x7C
//message = 0x4E85, checkvalue is 0x6B
uint32_t remainder = (uint32_t)message_from_sensor << 8; //Pad with 8 bits because we have to add in the check value
remainder |= check_value_from_sensor; //Add on the check value
uint32_t divsor = (uint32_t)SHIFTED_DIVISOR;
for (int i = 0 ; i < 16 ; i++) //Operate on only 16 positions of max 24. The remaining 8 are our remainder and should be zero when we're done.
{
//Serial.print("remainder: ");
//Serial.println(remainder, BIN);
//Serial.print("divsor: ");
//Serial.println(divsor, BIN);
//Serial.println();
if ( remainder & (uint32_t)1 << (23 - i) ) //Check if there is a one in the left position
remainder ^= divsor;
divsor >>= 1; //Rotate the divsor max 16 times so that we have 8 bits left of a remainder
}
return (byte)remainder;
}

72
src/SparkFunHTU21D.h Normal file
View file

@ -0,0 +1,72 @@
/*
HTU21D Humidity Sensor Library
By: Nathan Seidle
SparkFun Electronics
Date: September 22nd, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
Get humidity and temperature from the HTU21D sensor.
This same library should work for the other similar sensors including the Si
*/
#pragma once
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire.h>
#define HTU21D_ADDRESS 0x40 //Unshifted 7-bit I2C address for the sensor
#define ERROR_I2C_TIMEOUT 998
#define ERROR_BAD_CRC 999
#define TRIGGER_TEMP_MEASURE_HOLD 0xE3
#define TRIGGER_HUMD_MEASURE_HOLD 0xE5
#define TRIGGER_TEMP_MEASURE_NOHOLD 0xF3
#define TRIGGER_HUMD_MEASURE_NOHOLD 0xF5
#define WRITE_USER_REG 0xE6
#define READ_USER_REG 0xE7
#define SOFT_RESET 0xFE
#define USER_REGISTER_RESOLUTION_MASK 0x81
#define USER_REGISTER_RESOLUTION_RH12_TEMP14 0x00
#define USER_REGISTER_RESOLUTION_RH8_TEMP12 0x01
#define USER_REGISTER_RESOLUTION_RH10_TEMP13 0x80
#define USER_REGISTER_RESOLUTION_RH11_TEMP11 0x81
#define USER_REGISTER_END_OF_BATTERY 0x40
#define USER_REGISTER_HEATER_ENABLED 0x04
#define USER_REGISTER_DISABLE_OTP_RELOAD 0x02
class HTU21D {
public:
HTU21D();
//Public Functions
bool begin(TwoWire &wirePort = Wire); //If user doesn't specificy then Wire will be used
float readHumidity(void);
float readTemperature(void);
void setResolution(byte resBits);
byte readUserRegister(void);
void writeUserRegister(byte val);
//Public Variables
private:
//Private Functions
TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
byte checkCRC(uint16_t message_from_sensor, uint8_t check_value_from_sensor);
uint16_t readValue(byte cmd);
//Private Variables
};