Created
November 17, 2017 23:31
-
-
Save cellularmitosis/4bc0de74b6c3dd03d57ef5ce09c51896 to your computer and use it in GitHub Desktop.
An Arduino sketch which implements an Si7021-based temperature and humidity logger.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* \file crc8.c | |
* Functions and types for CRC checks. | |
* | |
* Generated on Fri Jun 2 23:24:34 2017, | |
* by pycrc v0.9, https://pycrc.org | |
* using the configuration: | |
* Width = 8 | |
* Poly = 0x07 | |
* Xor_In = 0x00 | |
* ReflectIn = False | |
* Xor_Out = 0x00 | |
* ReflectOut = False | |
* Algorithm = bit-by-bit | |
*****************************************************************************/ | |
#include "crc8.h" /* include the header file generated with pycrc */ | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
/** | |
* Update the crc value with new data. | |
* | |
* \param crc The current crc value. | |
* \param data Pointer to a buffer of \a data_len bytes. | |
* \param data_len Number of bytes in the \a data buffer. | |
* \return The updated crc value. | |
*****************************************************************************/ | |
crc_t crc_update(crc_t crc, const void *data, size_t data_len) | |
{ | |
const unsigned char *d = (const unsigned char *)data; | |
unsigned int i; | |
bool bit; | |
unsigned char c; | |
while (data_len--) { | |
c = *d++; | |
for (i = 0; i < 8; i++) { | |
bit = crc & 0x80; | |
crc = (crc << 1) | ((c >> (7 - i)) & 0x01); | |
if (bit) { | |
crc ^= 0x07; | |
} | |
} | |
crc &= 0xff; | |
} | |
return crc & 0xff; | |
} | |
/** | |
* Calculate the final crc value. | |
* | |
* \param crc The current crc value. | |
* \return The final crc value. | |
*****************************************************************************/ | |
crc_t crc_finalize(crc_t crc) | |
{ | |
unsigned int i; | |
bool bit; | |
for (i = 0; i < 8; i++) { | |
bit = crc & 0x80; | |
crc = (crc << 1) | 0x00; | |
if (bit) { | |
crc ^= 0x07; | |
} | |
} | |
return (crc ^ 0x00) & 0xff; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* \file crc8.h | |
* Functions and types for CRC checks. | |
* | |
* Generated on Fri Jun 2 23:24:29 2017, | |
* by pycrc v0.9, https://pycrc.org | |
* using the configuration: | |
* Width = 8 | |
* Poly = 0x07 | |
* Xor_In = 0x00 | |
* ReflectIn = False | |
* Xor_Out = 0x00 | |
* ReflectOut = False | |
* Algorithm = bit-by-bit | |
*****************************************************************************/ | |
#ifndef __CRC8_H__ | |
#define __CRC8_H__ | |
#include <stdlib.h> | |
#include <stdint.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/** | |
* The definition of the used algorithm. | |
* | |
* This is not used anywhere in the generated code, but it may be used by the | |
* application code to call algoritm-specific code, is desired. | |
*****************************************************************************/ | |
#define CRC_ALGO_BIT_BY_BIT 1 | |
/** | |
* The type of the CRC values. | |
* | |
* This type must be big enough to contain at least 8 bits. | |
*****************************************************************************/ | |
typedef uint_fast8_t crc_t; | |
/** | |
* Calculate the initial crc value. | |
* | |
* \return The initial crc value. | |
*****************************************************************************/ | |
static inline crc_t crc_init(void) | |
{ | |
return 0x00; | |
} | |
/** | |
* Update the crc value with new data. | |
* | |
* \param crc The current crc value. | |
* \param data Pointer to a buffer of \a data_len bytes. | |
* \param data_len Number of bytes in the \a data buffer. | |
* \return The updated crc value. | |
*****************************************************************************/ | |
crc_t crc_update(crc_t crc, const void *data, size_t data_len); | |
/** | |
* Calculate the final crc value. | |
* | |
* \param crc The current crc value. | |
* \return The final crc value. | |
*****************************************************************************/ | |
crc_t crc_finalize(crc_t crc); | |
#ifdef __cplusplus | |
} /* closing brace for extern "C" */ | |
#endif | |
#endif /* __CRC8_H__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
A simple temperature / humidity logger, using the Si7021. | |
Hardware connections: | |
The two pins above the AREF pin are (from the top): SCL and SDA. | |
*/ | |
/* | |
The following Si7021 "Weather" code was adapted from Sparkfun's library. | |
See https://github.com/sparkfun/Si7021_Breakout/tree/master/Libraries | |
*/ | |
#include <Arduino.h> | |
#include <Wire.h> | |
#define ADDRESS 0x40 | |
#define TEMP_MEASURE_HOLD 0xE3 | |
#define HUMD_MEASURE_HOLD 0xE5 | |
#define TEMP_MEASURE_NOHOLD 0xF3 | |
#define HUMD_MEASURE_NOHOLD 0xF5 | |
#define TEMP_PREV 0xE0 | |
#define WRITE_USER_REG 0xE6 | |
#define READ_USER_REG 0xE7 | |
#define SOFT_RESET 0xFE | |
#define HTRE 0x02 | |
#define _BV(bit) (1 << (bit)) | |
#define CRC_POLY 0x988000 // Shifted Polynomial for CRC check | |
// Error codes | |
#define I2C_TIMEOUT 998 | |
#define BAD_CRC 999 | |
class Weather { | |
public: | |
// Constructor | |
Weather(); | |
bool begin(); | |
// Si7021 & HTU21D Public Functions | |
float getRH(); | |
float readTemp(); | |
float getTemp(); | |
void heaterOn(); | |
void heaterOff(); | |
void changeResolution(uint8_t i); | |
void reset(); | |
uint8_t checkID(); | |
private: | |
//Si7021 & HTU21D Private Functions | |
uint16_t makeMeasurment(uint8_t command); | |
void writeReg(uint8_t value); | |
uint8_t readReg(); | |
}; | |
//Initialize | |
Weather::Weather(){} | |
bool Weather::begin(void) { | |
Wire.begin(); | |
uint8_t ID_Temp_Hum = checkID(); | |
int x = 0; | |
if(ID_Temp_Hum == 0x15)//Ping CheckID register | |
x = 1; | |
else if(ID_Temp_Hum == 0x32) | |
x = 2; | |
else | |
x = 0; | |
if(x == 1) { | |
return true; | |
} else if(x == 2) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
float Weather::getRH() { | |
// Measure the relative humidity | |
uint16_t RH_Code = makeMeasurment(HUMD_MEASURE_NOHOLD); | |
float result = (125.0*RH_Code/65536)-6; | |
return result; | |
} | |
float Weather::readTemp() { | |
// Read temperature from previous RH measurement. | |
uint16_t temp_Code = makeMeasurment(TEMP_PREV); | |
float result = (175.25*temp_Code/65536)-46.85; | |
return result; | |
} | |
float Weather::getTemp() { | |
// Measure temperature | |
uint16_t temp_Code = makeMeasurment(TEMP_MEASURE_NOHOLD); | |
float result = (175.25*temp_Code/65536)-46.85; | |
return result; | |
} | |
void Weather::heaterOn() { | |
// Turns on the ADDRESS heater | |
uint8_t regVal = readReg(); | |
regVal |= _BV(HTRE); | |
//turn on the heater | |
writeReg(regVal); | |
} | |
void Weather::heaterOff() { | |
// Turns off the ADDRESS heater | |
uint8_t regVal = readReg(); | |
regVal &= ~_BV(HTRE); | |
writeReg(regVal); | |
} | |
void Weather::changeResolution(uint8_t i) { | |
// Changes to resolution of ADDRESS measurements. | |
// Set i to: | |
// RH Temp | |
// 0: 12 bit 14 bit (default) | |
// 1: 8 bit 12 bit | |
// 2: 10 bit 13 bit | |
// 3: 11 bit 11 bit | |
uint8_t regVal = readReg(); | |
// zero resolution bits | |
regVal &= 0b011111110; | |
switch (i) { | |
case 1: | |
regVal |= 0b00000001; | |
break; | |
case 2: | |
regVal |= 0b10000000; | |
break; | |
case 3: | |
regVal |= 0b10000001; | |
default: | |
regVal |= 0b00000000; | |
break; | |
} | |
// write new resolution settings to the register | |
writeReg(regVal); | |
} | |
void Weather::reset() { | |
//Reset user resister | |
writeReg(SOFT_RESET); | |
} | |
uint8_t Weather::checkID() { | |
uint8_t ID_1; | |
// Check device ID | |
Wire.beginTransmission(ADDRESS); | |
Wire.write(0xFC); | |
Wire.write(0xC9); | |
Wire.endTransmission(); | |
Wire.requestFrom(ADDRESS,1); | |
ID_1 = Wire.read(); | |
return(ID_1); | |
} | |
uint16_t Weather::makeMeasurment(uint8_t command) { | |
// Take one ADDRESS measurement given by command. | |
// It can be either temperature or relative humidity | |
// TODO: implement checksum checking | |
uint16_t nBytes = 3; | |
// if we are only reading old temperature, read olny msb and lsb | |
if (command == 0xE0) nBytes = 2; | |
Wire.beginTransmission(ADDRESS); | |
Wire.write(command); | |
Wire.endTransmission(); | |
// When not using clock stretching (*_NOHOLD commands) delay here | |
// is needed to wait for the measurement. | |
// According to datasheet the max. conversion time is ~22ms | |
delay(100); | |
Wire.requestFrom(ADDRESS,nBytes); | |
//Wait for data | |
int counter = 0; | |
while (Wire.available() < nBytes){ | |
delay(1); | |
counter ++; | |
if (counter >100){ | |
// Timeout: Sensor did not return any data | |
return 100; | |
} | |
} | |
unsigned int msb = Wire.read(); | |
unsigned int lsb = Wire.read(); | |
// Clear the last to bits of LSB to 00. | |
// According to datasheet LSB of RH is always xxxxxx10 | |
lsb &= 0xFC; | |
unsigned int mesurment = msb << 8 | lsb; | |
return mesurment; | |
} | |
void Weather::writeReg(uint8_t value) { | |
// Write to user register on ADDRESS | |
Wire.beginTransmission(ADDRESS); | |
Wire.write(WRITE_USER_REG); | |
Wire.write(value); | |
Wire.endTransmission(); | |
} | |
uint8_t Weather::readReg() { | |
// Read from user register on ADDRESS | |
Wire.beginTransmission(ADDRESS); | |
Wire.write(READ_USER_REG); | |
Wire.endTransmission(); | |
Wire.requestFrom(ADDRESS,1); | |
uint8_t regVal = Wire.read(); | |
return regVal; | |
} | |
// a CRC-8 implementation, generated using pycrc, see https://pycrc.org/tutorial.html | |
// ./pycrc.py --model=crc-8 --algorithm=bbb --generate h -o crc8.h | |
// ./pycrc.py --model=crc-8 --algorithm=bbb --generate c -o crc8.c | |
// see also http://crccalc.com/ | |
#include "crc8.h" | |
// a struct representing one message to send over the serial connection. | |
struct message_t { | |
float temp_c; | |
float humidity; | |
uint8_t crc8; | |
}; | |
// a union allowing easy access to the individual bytes in a message_t. | |
union packing_t { | |
struct message_t msg; | |
uint8_t bytes[9]; | |
}; | |
// format and transmit one datapoint over the serial connection. | |
void send(float temp_c, float humidity) { | |
// assemble the message (initially with an empty CRC) | |
struct message_t msg = { .temp_c = temp_c, .humidity = humidity, .crc8 = 0x0 }; | |
// package the message as an array bytes | |
union packing_t packed = { .msg = msg }; | |
// generate the CRC and add it to the packed message | |
uint8_t crc = crc_update(crc_init(), packed.bytes, 8); | |
packed.msg.crc8 = crc_finalize(crc); | |
// transmit the packed bytes | |
for (int8_t i=0; i < 9; i++) { | |
Serial.write(packed.bytes[i]); | |
} | |
} | |
// busy-wait until the specified interval since the given reference. | |
void delay_since(uint32_t interval, uint32_t then) { | |
while (true) { | |
// note: unsigned arithmetic automatically accounts for millis() roll-over. | |
uint32_t now = millis(); | |
uint32_t elapsed = now - then; | |
if (elapsed >= interval) { | |
break; | |
} | |
} | |
} | |
// "main" | |
Weather sensor; | |
void setup() { | |
Serial.begin(9600); | |
sensor.begin(); | |
} | |
void loop() { | |
uint32_t then = millis(); | |
float humidity = sensor.getRH(); | |
float temp_c = sensor.getTemp(); | |
send(temp_c, humidity); | |
delay_since(1000, then); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment