Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 |
I always wanted to have a temperature and humidity motor in my room so that I can know how to dress everyday. But that is just an idea until I got a no-Chip embedded LCD.
After I got a tiny LCD, I realize it's time to work on this project. This is in fact a simple project. You only need a sensor which can monitor the humidity and temperature, an LCD display and, of course, a powerful MCU to process all those data. In my project, I use SHT30 as the sensor, a 4-seg LCD without a chip in it, and an electron from Particle so that the data can be loaded to cloud for future upgrades. I also use a FRAM MB85RS1MT to store the data and also a no-contact infrared thermopile sensor TMP007 on this board.
Design the schematicThe first step of this project is to design the schematic, which is in fact quite simple. The key point is spending some time figuring out how to wire the LCD.
Since there is no chip for that LCD, we need some trick wire method. All the COM ports are connected to electron directly. More detail of that kind of LCD can be found from here.
Finish the layoutAfter populating the board, it should be something like that. Still have some components that didn't populated.
More detail can be found from my attached code file. But currently, I only drive the temperature senor and didn't touch the others chips and no more other functions.
#include "application.h"
#include "Particle.h"
#include "math.h"
// uncomment for debugging!
//#define TMP007_DEBUG 1
#define TMP007_VOBJ 0x00
#define TMP007_TDIE 0x01
#define TMP007_CONFIG 0x02
#define TMP007_TOBJ 0x03
#define TMP007_STATUS 0x04
#define TMP007_STATMASK 0x05
#define TMP007_CFG_RESET 0x8000
#define TMP007_CFG_MODEON 0x1000
#define TMP007_CFG_1SAMPLE 0x0000
#define TMP007_CFG_2SAMPLE 0x0200
#define TMP007_CFG_4SAMPLE 0x0400
#define TMP007_CFG_8SAMPLE 0x0600
#define TMP007_CFG_16SAMPLE 0x0800
#define TMP007_CFG_ALERTEN 0x0100
#define TMP007_CFG_ALERTF 0x0080
#define TMP007_CFG_TRANSC 0x0040
#define TMP007_STAT_ALERTEN 0x8000
#define TMP007_STAT_CRTEN 0x4000
#define TMP007_I2CADDR 0x40
#define TMP007_DEVID 0x1F
class Adafruit_TMP007 {
public:
Adafruit_TMP007(uint8_t addr = TMP007_I2CADDR);
boolean begin(uint8_t samplerate = TMP007_CFG_16SAMPLE); // by default go highres
int16_t readRawDieTemperature(void);
int16_t readRawVoltage(void);
double readObjTempC(void);
double readDieTempC(void);
private:
uint8_t _addr;
uint16_t read16(uint8_t addr);
void write16(uint8_t addr, uint16_t data);
};
/***************************************************
This is a library for the TMP007 Temp Sensor
Designed specifically to work with the Adafruit TMP007 Breakout
----> https://www.adafruit.com/products/2023
These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_TMP007.h"
#define ARDUINO 200
//#define TESTDIE 0x0C78
//#define TESTVOLT 0xFEED
Adafruit_TMP007::Adafruit_TMP007(uint8_t i2caddr) {
_addr = i2caddr;
}
boolean Adafruit_TMP007::begin(uint8_t samplerate) {
Wire1.begin();
write16(TMP007_CONFIG, TMP007_CFG_MODEON | TMP007_CFG_ALERTEN |
TMP007_CFG_TRANSC | samplerate);
write16(TMP007_STATMASK, TMP007_STAT_ALERTEN |TMP007_STAT_CRTEN);
// enable conversion ready alert
uint16_t did;
did = read16(TMP007_DEVID);
#ifdef TMP007_DEBUG
Serial.print("did = 0x"); Serial.println(did, HEX);
#endif
if (did != 0x78) return false;
return true;
}
//////////////////////////////////////////////////////
double Adafruit_TMP007::readDieTempC(void) {
double Tdie = readRawDieTemperature();
Tdie *= 0.03125; // convert to celsius
#ifdef TMP007_DEBUG
Serial.print("Tdie = "); Serial.print(Tdie); Serial.println(" C");
#endif
return Tdie;
}
double Adafruit_TMP007::readObjTempC(void) {
int16_t raw = read16(TMP007_TOBJ);
// invalid
if (raw & 0x1) return NAN;
raw >>=2;
double Tobj = raw;
Tobj *= 0.03125; // convert to celsius
#ifdef TMP007_DEBUG
Serial.print("Tobj = "); Serial.print(Tobj); Serial.println(" C");
#endif
return Tobj;
}
int16_t Adafruit_TMP007::readRawDieTemperature(void) {
int16_t raw = read16(TMP007_TDIE);
#if TMP007_DEBUG == 1
#ifdef TESTDIE
raw = TESTDIE;
#endif
Serial.print("Raw Tambient: 0x"); Serial.print (raw, HEX);
float v = raw/4;
v *= 0.03125;
Serial.print(" ("); Serial.print(v); Serial.println(" *C)");
#endif
raw >>= 2;
return raw;
}
int16_t Adafruit_TMP007::readRawVoltage(void) {
int16_t raw;
raw = read16(TMP007_VOBJ);
#if TMP007_DEBUG == 1
#ifdef TESTVOLT
raw = TESTVOLT;
#endif
Serial.print("Raw voltage: 0x"); Serial.print (raw, HEX);
float v = raw;
v *= 156.25;
v /= 1000;
Serial.print(" ("); Serial.print(v); Serial.println(" uV)");
#endif
return raw;
}
/*********************************************************************/
uint16_t Adafruit_TMP007::read16(uint8_t a) {
uint16_t ret;
Wire1.beginTransmission(_addr); // start transmission to device
#if (ARDUINO >= 100)
Wire1.write(a); // sends register address to read from
#else
Wire1.send(a); // sends register address to read from
#endif
Wire1.endTransmission(); // end transmission
Wire1.beginTransmission(_addr); // start transmission to device
Wire1.requestFrom(_addr, (uint8_t)2);// send data n-bytes read
#if (ARDUINO >= 100)
ret = Wire1.read(); // receive DATA
ret <<= 8;
ret |= Wire1.read(); // receive DATA
#else
ret = Wire1.receive(); // receive DATA
ret <<= 8;
ret |= Wire1.receive(); // receive DATA
#endif
Wire1.endTransmission(); // end transmission
return ret;
}
void Adafruit_TMP007::write16(uint8_t a, uint16_t d) {
Wire1.beginTransmission(_addr); // start transmission to device
#if (ARDUINO >= 100)
Wire1.write(a); // sends register address to read from
Wire1.write(d>>8); // write data
Wire1.write(d); // write data
#else
Wire1.send(a); // sends register address to read from
Wire1.send(d>>8); // write data
Wire1.send(d); // write data
#endif
Wire1.endTransmission(); // end transmission
}
#include "application.h"
#include "Particle.h"
#include "math.h"
//by compare with a more standard temperature/humidity sensor, got the belwo calibration value
#define CALIB_TEMP 4
#define CALIB_HUM 10
#define SHT31_DEFAULT_ADDR 0x44
#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06
#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D
#define SHT31_MEAS_LOWREP_STRETCH 0x2C10
#define SHT31_MEAS_HIGHREP 0x2400
#define SHT31_MEAS_MEDREP 0x240B
#define SHT31_MEAS_LOWREP 0x2416
#define SHT31_READSTATUS 0xF32D
#define SHT31_CLEARSTATUS 0x3041
#define SHT31_SOFTRESET 0x30A2
#define SHT31_HEATEREN 0x306D
#define SHT31_HEATERDIS 0x3066
class Adafruit_SHT31 {
public:
Adafruit_SHT31();
boolean begin(uint8_t i2caddr = SHT31_DEFAULT_ADDR);
float readTemperature(void);
float readHumidity(void);
uint16_t readStatus(void);
void reset(void);
void heater(boolean);
uint8_t crc8(const uint8_t *data, int len);
private:
boolean readTempHum(void);
void writeCommand(uint16_t cmd);
uint8_t _i2caddr;
boolean readData(void);
float humidity, temp;
};
class SHT30{
public:
bool setAddress(int a0);
bool update();
double temperature;
double humidity;
private:
int address = 0x44;
int buffer[6] = {0,0,0,0,0,0};
};
#include "Adafruit_SHT30.h"
#define ARDUINO 200
Adafruit_SHT31::Adafruit_SHT31() {
}
boolean Adafruit_SHT31::begin(uint8_t i2caddr) {
Wire1.begin();
_i2caddr = i2caddr;
reset();
//return (readStatus() == 0x40);
return true;
}
uint16_t Adafruit_SHT31::readStatus(void) {
writeCommand(SHT31_READSTATUS);
Wire1.requestFrom(_i2caddr, (uint8_t)3);
uint16_t stat = Wire1.read();
stat <<= 8;
stat |= Wire1.read();
//Serial.println(stat, HEX);
return stat;
}
void Adafruit_SHT31::reset(void) {
writeCommand(SHT31_SOFTRESET);
delay(10);
}
void Adafruit_SHT31::heater(boolean h) {
if (h)
writeCommand(SHT31_HEATEREN);
else
writeCommand(SHT31_HEATERDIS);
}
float Adafruit_SHT31::readTemperature(void) {
if (! readTempHum()) return NAN;
return temp;
}
float Adafruit_SHT31::readHumidity(void) {
if (! readTempHum()) return NAN;
return humidity;
}
boolean Adafruit_SHT31::readTempHum(void) {
uint8_t readbuffer[6];
writeCommand(SHT31_MEAS_HIGHREP);
delay(500);
Wire1.requestFrom(_i2caddr, (uint8_t)6);
if (Wire1.available() != 6)
return false;
for (uint8_t i=0; i<6; i++) {
readbuffer[i] = Wire1.read();
// Serial.print("0x"); Serial.println(readbuffer[i], HEX);
}
uint16_t ST, SRH;
ST = readbuffer[0];
ST <<= 8;
ST |= readbuffer[1];
if (readbuffer[2] != crc8(readbuffer, 2)) return false;
SRH = readbuffer[3];
SRH <<= 8;
SRH |= readbuffer[4];
if (readbuffer[5] != crc8(readbuffer+3, 2)) return false;
// Serial.print("ST = "); Serial.println(ST);
double stemp = ST;
stemp *= 175;
stemp /= 0xffff;
stemp = -45 + stemp;
temp = stemp-CALIB_TEMP;
// Serial.print("SRH = "); Serial.println(SRH);
double shum = SRH;
shum *= 100;
shum /= 0xFFFF;
humidity = shum+CALIB_HUM;
return true;
}
void Adafruit_SHT31::writeCommand(uint16_t cmd) {
Wire1.beginTransmission(_i2caddr);
Wire1.write(cmd >> 8);
Wire1.write(cmd & 0xFF);
Wire1.endTransmission();
}
uint8_t Adafruit_SHT31::crc8(const uint8_t *data, int len)
{
const uint8_t POLYNOMIAL(0x31);
uint8_t crc(0xFF);
for ( int j = len; j; --j ) {
crc ^= *data++;
for ( int i = 8; i; --i ) {
crc = ( crc & 0x80 )
? (crc << 1) ^ POLYNOMIAL
: (crc << 1);
}
}
return crc;
}
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_SHT30.h"
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_TMP007.h"
//for the display theory, please refer to
//http://www.atmel.com/images/doc8103.pdf
#define PORT_COM1 C0
#define PORT_COM2 C1
#define PORT_COM3 C2
#define PORT_COM4 C3
#define COM1 1
#define COM2 2
#define COM3 3
#define COM4 4
#define SEG_A D0
#define SEG_B D1
#define SEG_F D2
#define SEG_G D3
#define SEG_C D4
#define SEG_E D5
#define SEG_D D6
#define SEG_P D7
//this is used for polority flip
#define POSITIVE 1
#define NEGATIVE 0
#define TEMP 1
#define HUMIDITY 2
#define UNIT_C 0xa7 //the temperature unit of 'C' 0xa6|0x01
//#define MARK_C 0x01 //the temperature mark of `round circule`
#define UNIT_H 0x7D //the humidity unit of 'H' 0X7C|0X01
#define MARK_DOT 0x01
#define MARK_DOT_NONE 0x00
//0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90
uint8_t displayTable[10]={0xee,0x48,0xd6,0xda,0x78,0xba,0xbe,0xc8,0xfe,0xfa};
SYSTEM_MODE(MANUAL);
Adafruit_SHT31 sht31;
float t,h;
unsigned long interval = 0;
//init the GPIO for LCD display
void lcdDisplayInit(void)
{
pinMode(C0,OUTPUT);
pinMode(C1,INPUT);
pinMode(C2,INPUT);
pinMode(C3,INPUT);
pinMode(D0,OUTPUT);
pinMode(D1,OUTPUT);
pinMode(D2,OUTPUT);
pinMode(D3,OUTPUT);
pinMode(D4,OUTPUT);
pinMode(D5,OUTPUT);
pinMode(D6,OUTPUT);
pinMode(D7,OUTPUT);
}
void loadDisplayTable(uint8_t data)
{
digitalWrite(SEG_A,((data>>7)&0x01));
digitalWrite(SEG_B,((data>>6)&0x01));
digitalWrite(SEG_F,((data>>5)&0x01));
digitalWrite(SEG_G,((data>>4)&0x01));
digitalWrite(SEG_C,((data>>3)&0x01));
digitalWrite(SEG_E,((data>>2)&0x01));
digitalWrite(SEG_D,((data>>1)&0x01));
digitalWrite(SEG_P,((data>>0)&0x01));
}
//displayData had beem enlarged by 10
void lcdDisplay(uint16_t displayData,uint8_t unit)
{
static uint8_t comCounter = 1;
static uint8_t lastDisplayData=0;
static uint8_t firstBit=0,secondBit=0,thirdBit=0;
static uint8_t dotDisplay = MARK_DOT; //display the dot when in default mode
static uint8_t comPolarity = NEGATIVE; //the default voltage on COM port is 0v
uint8_t tempValue = 0;
if(lastDisplayData!=displayData) //update bits only input value had changed
{
lastDisplayData = displayData;
if(displayData<=999)
{
firstBit = displayData/100;
secondBit = displayData%100/10;
thirdBit = displayData%10;
dotDisplay = MARK_DOT; //need to display dot when the value is smaller than 100degree,like 23.2 or 02.3
}
else
{
firstBit = displayData/1000;
secondBit = displayData%1000/100;
thirdBit = displayData%100/10; //omit the last fourth bit
dotDisplay = MARK_DOT_NONE; //only display integer when the value is bigger than 99.9degree
}
}
switch(comCounter)
{
case COM1:
pinMode(PORT_COM2,INPUT);
pinMode(PORT_COM3,INPUT);
pinMode(PORT_COM4,INPUT);
if(comPolarity==POSITIVE)
tempValue = ~displayTable[firstBit];
else
tempValue = displayTable[firstBit];
loadDisplayTable(tempValue);
pinMode(PORT_COM1,OUTPUT);
digitalWrite(PORT_COM1,comPolarity);
break;
case COM2:
pinMode(PORT_COM1,INPUT);
pinMode(PORT_COM3,INPUT);
pinMode(PORT_COM4,INPUT);
if(comPolarity==POSITIVE)
tempValue = ~(displayTable[secondBit]|dotDisplay);
else
tempValue = displayTable[secondBit]|dotDisplay;
loadDisplayTable(tempValue);
pinMode(PORT_COM2,OUTPUT);
digitalWrite(PORT_COM2,comPolarity);
break;
case COM3:
pinMode(PORT_COM1,INPUT);
pinMode(PORT_COM2,INPUT);
pinMode(PORT_COM4,INPUT);
if(comPolarity==POSITIVE)
tempValue = ~displayTable[thirdBit];
else
tempValue = displayTable[thirdBit];
loadDisplayTable(tempValue);
pinMode(PORT_COM3,OUTPUT);
digitalWrite(PORT_COM3,comPolarity);
break;
case COM4:
pinMode(PORT_COM1,INPUT);
pinMode(PORT_COM2,INPUT);
pinMode(PORT_COM3,INPUT);
if(unit==TEMP)
tempValue = UNIT_C; // UNIT_C|MARK_C;
else
tempValue = UNIT_H;
if(comPolarity==POSITIVE)
tempValue = ~tempValue; //displayTable[firstBit]
loadDisplayTable(tempValue);
pinMode(PORT_COM4,OUTPUT);
digitalWrite(PORT_COM4,comPolarity);
break;
default:
break;
}
/*
comCounter++;
if(comCounter==5)
{
comCounter = 1;
comPolarity=(comPolarity==1)? 0:1;
}
*/
//which means one seg had finished the AC from 0-1
if(comPolarity==1) //0-->1--0-->1
{
comCounter++;
if(comCounter==5)
{
comCounter=0;
comPolarity=1; //start from negative after execut the below program
}
}
comPolarity = (comPolarity==1)?0:1;
}
//display the seg every 2ms, which is around 60HZ
Timer timer(2,updateDisplay);
void setup(void)
{
lcdDisplayInit();
Serial.begin(9600);
if (! sht31.begin(0x44)) {
Serial.println("Couldn't find SHT30");
}
timer.start();
}
void updateDisplay()
{
lcdDisplay((uint16_t)t,TEMP);
}
void loop(void)
{
if((millis()-interval)>=1000)
{
interval = millis();
t = sht31.readTemperature();
t*=10;
h = sht31.readHumidity();
h*=10;
}
}
Comments