Skip to content

Instantly share code, notes, and snippets.

@bboyho
Created December 20, 2021 03:00
Show Gist options
  • Save bboyho/87fbfe8ae7668277f44d9f2d67264efd to your computer and use it in GitHub Desktop.
Save bboyho/87fbfe8ae7668277f44d9f2d67264efd to your computer and use it in GitHub Desktop.
/******************************************************************************
Digital Indoor Temperature Monitor with the TMP117
Written by: Ho Yun "Bobby" Chan
@ SparkFun Electronics
Updated: Dec 19, 2021
Date: Mar 6, 2020
Description: This sketch configures temperature sensors and prints the
temperature in degrees celsius and fahrenheit to the Qwiic microOLED.
Simply adjust the `output_select` to view the °C, °F, or both. You can
also output the values to the SerialUSB Monitor or Plotter at 115200 baud
to view the data. There is also a demo mode that displays each output
on the microOLED with a progress bar at the bottom.
To save the screen, the microOLED will turn off after a certain amount of
time. If the distance sensor detects something in front of the screen,
the display will turn on and display the temperature readings. There is
also an output for the distance sensor.
Resources/Libraries:
Wire.h (included with Arduino IDE)
SparkFunTMP117.h (included in the src folder) http://librarymanager/All#SparkFun_TMP117
SFE_MicroOLED.h (included in the src folder) http://librarymanager/All#SparkFun_micro_OLED
SparkFun_VL53L1X.h (included in the src folder) http://librarymanager/All#SparkFun_VL53L1X
Development Environment Specifics:
Arduino 1.8.10+
License:
This code is released under the MIT License (http://opensource.org/licenses/MIT)
Distributed as-is; no warranty is given.
******************************************************************************/
/*
NOTE: For the most accurate readings using the TMP117:
- Avoid heavy bypass traffic on the I2C bus
- Use the highest available communication speeds
- Use the minimal supply voltage acceptable for the system
- Place device horizontally and out of any airflow when storing
For more information on reaching the most accurate readings from the sensor,
reference the "Precise Temperature Measurements with TMP116" datasheet that is
linked on Page 35 of the TMP117's datasheet
*/
#include <Wire.h> // Used to establish Serial communication on the I2C bus
#include <SparkFun_TMP117.h> // Used to send and recieve specific information from our sensor
#include <SFE_MicroOLED.h> // Include the SFE_MicroOLED library
//#define SerialUSB Serial //Uncomment if you are not using a native USB like the Atmega32U4 or SAMD21
// digital I/O pins
const byte STAT1 = 13;
boolean STAT1_blink = HIGH;
///////////////////////////////
///////// micro OLED //////////
///////////////////////////////
#define PIN_RESET 7 // A pin needs to be declared even though we are not physically connecting to the Qwiic micro OLED via I2C
#define DC_JUMPER 1
MicroOLED oled(PIN_RESET, DC_JUMPER); // I2C declaration
///////////////////////////////
/////////// TMP117 ////////////
///////////////////////////////
// The default address of the device is 0x48 = (GND)
TMP117 sensor; // Initalize sensor
float tempC = 0;
float tempF = 0;
//0 = output degrees °C
//1 = output degrees °F
//2 or any other number = output degrees °C and °F
int output_select = 3; //select output
#include "SparkFun_VL53L1X.h"
//Optional interrupt and shutdown pins.
#define SHUTDOWN_PIN 2
#define INTERRUPT_PIN 3
SFEVL53L1X distanceSensor;
//Uncomment the following line to use the optional shutdown and interrupt pins.
//SFEVL53L1X distanceSensor(Wire, SHUTDOWN_PIN, INTERRUPT_PIN);
int distance = 0;
int prevDistance = 0;
int currentDistance = 0;
float distanceInches = 0.00;
float distanceFeet = 0.00;
///////////////////////////////
// Display Mode Page Control //
///////////////////////////////
// This enum defines all of our display modes and their order.
enum t_displayModes {
DISPLAY_BIG_C,
DISPLAY_BIG_F,
DISPLAY_C_F,
DISPLAY_VL53L1X,
DISPLAY_CUBE
};
const int NUM_DISPLAY_MODES = 5; // Number of values defined in enum above
volatile int displayMode = NUM_DISPLAY_MODES - 1; // Keeps track of current display page
const unsigned long DISPLAY_UPDATE_RATE = 4000; // Cycle display every 5 seconds
unsigned long lastDisplayUpdate = 0; // Stores time of last display update
unsigned long lastLEDUpdate = 0; // Stores time of last LED update
unsigned long currentMillis = 0; // Stores time of last display update
boolean activity = true; //used to keep track of movement or button press
const unsigned long noActivityMillis = NUM_DISPLAY_MODES * (DISPLAY_UPDATE_RATE + 1000); //used to compare amount of time when no activity to turn of screen
unsigned long lastActivityMillis = 0;
float percentage = 0; //store percent for progress bar
int progressWidth = 0; // Width of progress bar depends on the [% * (64 pixels wide)]
int progressY = 0; //location of the progress bar at the botton of the microOLED
///////////////////////////////
/////// Initialize Cube ///////
///////////////////////////////
int SCREEN_WIDTH = oled.getLCDWidth();
int SCREEN_HEIGHT = oled.getLCDHeight();
float d = 3;
float px[] = {
-d, d, d, -d, -d, d, d, -d
};
float py[] = {
-d, -d, d, d, -d, -d, d, d
};
float pz[] = {
-d, -d, -d, -d, d, d, d, d
};
float p2x[] = {
0, 0, 0, 0, 0, 0, 0, 0
};
float p2y[] = {
0, 0, 0, 0, 0, 0, 0, 0
};
float r[] = {
0, 0, 0
};
#define SHAPE_SIZE 600
// Define how fast the cube rotates. Smaller numbers are faster.
// This is the number of ms between draws.
//#define ROTATION_SPEED 0
void setup() {
pinMode(STAT1, OUTPUT); //Status LED Blue
delay(100);
Wire.begin();
oled.begin(); // Initialize the OLED
oled.clear(ALL); // Clear the display's internal memory
oled.display(); // Display what's in the buffer (splashscreen)
delay(1000); // Delay 1000 ms
oled.clear(PAGE); // Clear the buffer.
SerialUSB.begin(115200); // Start SerialUSB communication at 115200 baud
Wire.setClock(400000); // Set clock speed to be the fastest for better communication (fast mode)
if (sensor.begin() == true ) // Function to check if the sensor will correctly self-identify with the proper Device ID/Address
{
//SerialUSB.println("Begin");
if (output_select == 0 ) {
SerialUSB.println("TMP117[°C]");
}
else if (output_select == 1) {
SerialUSB.println("TMP117[°F]");
}
else {
SerialUSB.print("TMP117[°C]");
SerialUSB.print(",");
SerialUSB.println("TMP117[°F]");
//SerialUSB.print(",");
//SerialUSB.print("VL53L1X[m]");
//SerialUSB.print(",");
//SerialUSB.println("VL53L1X[ft]");
}
}
else
{
SerialUSB.println("Device failed to setup- Freezing code.");
while (1); // Runs forever
}
delay(100);
if (distanceSensor.begin() != false) //Begin returns 0 on a good init
{
SerialUSB.println("Sensor failed to begin. Please check wiring. Freezing...");
while (1)
;
}
}//end setup
void loop() {
// Data Ready is a flag for the conversion modes - in continous conversion the dataReady flag should always be high
if (sensor.dataReady() == true) // Function to make sure that there is data ready to be printed, only prints temperature values when data is ready
{
//get TMP117 sensor readings
tempC = sensor.readTempC();
tempF = sensor.readTempF();
}
distanceSensor.startRanging(); //Write configuration bytes to initiate measurement
while (!distanceSensor.checkForDataReady())
{
delay(1);
}
distance = distanceSensor.getDistance(); //Get the result of the measurement from the sensor
distanceSensor.clearInterrupt(); distanceSensor.stopRanging();
currentDistance = distance;
distanceInches = distance * 0.0393701;
distanceFeet = distanceInches / 12.0;
//get time based on how long the Arduino has been running
currentMillis = millis();
// Another method of updating display:
// The display will cycle itself every DISPLAY_UPDATE_RATE seconds
if ( currentMillis >= (lastDisplayUpdate + DISPLAY_UPDATE_RATE + 1000) )
{
// Increment displayMode, next time through a new page will be displayed:
displayMode = (displayMode + 1) % NUM_DISPLAY_MODES;
// Update lastDisplayTime, so we don't come back for DISPLAY_UPDATE_RATE seconds
lastDisplayUpdate = currentMillis;
}
//check to see if something got in front of the ToF sensor
if ( (abs(currentDistance - prevDistance) > 100) ) {
activity = true;
lastActivityMillis = currentMillis;
}
if (activity == true && (( currentMillis - lastActivityMillis ) < noActivityMillis)) {
// display
if (output_select == 0 ) {
oled.clear(PAGE); // Clear the display
displayC();
oled.display();
// Print temperature in °C
//SerialUSB.print("Temperature in Celsius: ");
SerialUSB.println(tempC);//TMP117 temperature
}
else if (output_select == 1) {
oled.clear(PAGE); // Clear the display
displayF();
oled.display();
// Print temperature in °F
//SerialUSB.print("Temperature in Fahrenheit: ");
SerialUSB.println(tempF);
}
else if (output_select == 2) {
//Display both temperatures as °C and °F, and distance
oled.clear(PAGE); // Clear the display
displayC_F();
oled.display();
//TMP117 temperature with comma delimiter for graphing or datalogging
SerialUSB.print(tempC);
SerialUSB.print(","); //seperator
SerialUSB.println(tempF);
//SerialUSB.print(","); //separator
//VL53L1X distance with comma delimiter for graphing or datalogging
//SerialUSB.print(distance);
//SerialUSB.print(F(",")); //separator
//SerialUSB.println(distanceFeet, 2);
}
else {
// Displaying °C and °F and distance with a scroll bar
oled.clear(PAGE); // Clear the display
updateDisplay();
displayProgressBar(); // Draws a progress bar at the bottom of the screen
oled.display();
//TMP117 temperature with comma delimiter for graphing or datalogging
SerialUSB.print(tempC);
SerialUSB.print(","); //seperator
SerialUSB.println(tempF);
//SerialUSB.print(","); //separator
//VL53L1X distance with comma delimiter for graphing or datalogging
//SerialUSB.print(distance);
//SerialUSB.print(F(",")); //separator
//SerialUSB.println(distanceFeet, 2);
}
if (currentMillis >= lastLEDUpdate + 1000) {
STAT1_blink = !STAT1_blink;
digitalWrite(STAT1, STAT1_blink); //Blink stat LED
lastLEDUpdate = currentMillis;
}
}//end display if activity
else
{
//if nothing got in front of the ToF sensor, don't display anything
oled.clear(PAGE); // Clear the display}
oled.display();
digitalWrite(STAT1, false); //Blink stat LED
activity = false;
}
prevDistance = currentDistance;
//delay(5); // Delay added for easier readings
}//end loop
//This function updates the display if we are scrolling through all displays with a progress bar.
void updateDisplay() {
switch (displayMode)
{
case DISPLAY_BIG_C:
displayC();
break;
case DISPLAY_BIG_F:
displayF();
break;
case DISPLAY_C_F:
displayC_F();
break;
case DISPLAY_VL53L1X:
displayVL53L1X();
break;
case DISPLAY_CUBE: //used as a screensaver
drawCube();
break;
}
}
// This function displays the temperature in °C as big digits.
void displayC() {
//oled.clear(PAGE); // Clear the display, this is already called before we enter this function
oled.setFontType(0); // Smallest font
oled.setCursor(6, 0); // Set cursor to top-left-ish
oled.print("Room Temp"); // Print
oled.setFontType(2); // medium font
oled.setCursor(6, 13); // Set cursor to middle-ish
//oled.print(" "); // Print
oled.print(tempC); // Print temp, assuming that it is within room temp in tens
oled.setFontType(1); // Smallest font
//oled.setCursor(19, 34); // Set cursor to middle-ish
//oled.print("deg C"); // Print
oled.setCursor(32, 32); // Set cursor to middle-ish
oled.print("C"); // Print
oled.circle(28, 33, 1); //"degree" sign after output values
//oled.display(); // Update the display, this is already called after we exit this function
}
// This function displays the temperature in °F as big digits.
void displayF() {
//oled.clear(PAGE); // Clear the display, this is already called before we enter this function
oled.setFontType(0); // Smallest font
oled.setCursor(6, 0); // Set cursor to top-ish
oled.print("Room Temp"); // Print
oled.setFontType(2); // medium font
oled.setCursor(6, 13); // Set cursor to middle-ish
//oled.print(" "); // Print
oled.print(tempF); // Print temp, assuming that it is within room temp in tens
oled.setFontType(1); // Smallest font
//oled.setCursor(19, 34); // Set cursor to middle-ish
//oled.print("deg F"); // Print
oled.setCursor(32, 32); // Set cursor to middle-ish
oled.print("F"); // Print
oled.circle(28, 33, 1); //"degree" sign after output values
//oled.display(); // Update the display, this is already called after we exit this function
}
void displayVL53L1X() {
oled.setFontType(0); // Smallest font
oled.setCursor(14, 0); // Set cursor to top-middle
oled.print(F("VL53L1X")); // Print
//assuming readings isn't bigger than 4 digits (5 digits if no decimal point)
oled.setFontType(1); // medium font
oled.setCursor(0, 8); // Set cursor to top-ish
oled.print(F(" mm")); // Print distance
oled.setCursor(0, 8); // Set cursor to top-ish
if (distance < 10) {
oled.print(F(" ")); // Print space
}
else if (distance >= 10 && distance < 100) {
oled.print(F(" ")); // Print space
}
else if (distance >= 100 && distance < 1000) {
oled.print(F(" ")); // Print space
}
else if (distance >= 1000) {
oled.print(F(" ")); // Print space
}
oled.print(distance); // Print distance
oled.setCursor(0, 21); // Set cursor to middle-ish
oled.print(F(" in")); // Print distance
oled.setCursor(0, 21); // Set cursor to middle-ish
if (distanceInches < 10) {
oled.print(F(" ")); // Print space
oled.print(distanceInches, 2); // Print distance
}
else if (distanceInches >= 10 && distanceInches < 100) {
oled.print(distanceInches, 2); // Print distance
}
else if (distanceInches >= 100 && distanceInches < 1000) {
oled.print(distanceInches, 1); // Print distance
}
oled.setCursor(0, 34); // Set cursor to bottom-ish
oled.print(F(" ft")); // Print distance
oled.setCursor(0, 34); // Set cursor to bottom-ish
if (distanceFeet < 10) {
oled.print(F(" ")); // Print space
}
oled.print(distanceFeet); // Print distance
}
// This function animates a cube. This is used as a quick screensaver.
void drawCube()
{
r[0] = r[0] + 10 * PI / 180.0; // Add a degree
r[1] = r[1] + 10 * PI / 180.0; // Add a degree
r[2] = r[2] + 10 * PI / 180.0; // Add a degree
if (r[0] >= 360.0 * PI / 180.0) r[0] = 0;
if (r[1] >= 360.0 * PI / 180.0) r[1] = 0;
if (r[2] >= 360.0 * PI / 180.0) r[2] = 0;
for (int i = 0; i < 8; i++)
{
float px2 = px[i];
float py2 = cos(r[0]) * py[i] - sin(r[0]) * pz[i];
float pz2 = sin(r[0]) * py[i] + cos(r[0]) * pz[i];
float px3 = cos(r[1]) * px2 + sin(r[1]) * pz2;
float py3 = py2;
float pz3 = -sin(r[1]) * px2 + cos(r[1]) * pz2;
float ax = cos(r[2]) * px3 - sin(r[2]) * py3;
float ay = sin(r[2]) * px3 + cos(r[2]) * py3;
float az = pz3 - 150;
p2x[i] = SCREEN_WIDTH / 2 + ax * SHAPE_SIZE / az;
p2y[i] = SCREEN_HEIGHT / 2 + ay * SHAPE_SIZE / az;
}
for (int i = 0; i < 3; i++)
{
oled.line(p2x[i], p2y[i], p2x[i + 1], p2y[i + 1]);
oled.line(p2x[i + 4], p2y[i + 4], p2x[i + 5], p2y[i + 5]);
oled.line(p2x[i], p2y[i], p2x[i + 4], p2y[i + 4]);
}
oled.line(p2x[3], p2y[3], p2x[0], p2y[0]);
oled.line(p2x[7], p2y[7], p2x[4], p2y[4]);
oled.line(p2x[3], p2y[3], p2x[7], p2y[7]);
}
// This function displays both °C and °F on the microOLED
void displayC_F() {
//oled.clear(PAGE); // Clear the display, this is already called before we enter this function
oled.setFontType(0); // Smallest font
oled.setCursor(6, 0); // Set cursor to top-left
oled.print("Room Temp"); // Print
oled.setFontType(1); // medium font
oled.setCursor(0, 12); // Set cursor to middle-ish
oled.print(String(tempC, 2) + " C"); // Print temp, assuming that it is within room temp in tens
oled.print(String(tempF, 2) + " F");// Print temp, assuming that it is within room temp in tens
oled.circle(50, 13, 1); //"degree" sign after output values
oled.circle(50, 29, 1); //"degree" sign after output values
//oled.display(); // Update the display, this is already called after we exit this function
}
// This function draws a line at the very bottom of the screen showing how long
// it'll be before the screen updates.
// Based on Jim's micro OLED code used in the Photon SIK KIT => [ https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments/blob/master/11-OLEDApps/Code/02-WeatherForecast/WeatherApp.ino ]
void displayProgressBar() {
// Use lastDisplayUpdate's time, the time Arduino has been running
// (since we do not have an RTC, Internet, or GPS), and the total time
// per page (DISPLAY_UPDATE_RATE) to calculate what portion
// of the display bar needs to be drawn.
percentage = (float)(currentMillis - lastDisplayUpdate) / (float)DISPLAY_UPDATE_RATE;
//for debugging progress bar
//SerialUSB.println(currentMillis);
//SerialUSB.println(lastDisplayUpdate);
//SerialUSB.println(DISPLAY_UPDATE_RATE);
//SerialUSB.println(percentage);
//SerialUSB.println(oled.getLCDWidth());
// Mutliply that value by the total lcd width to get the pixel length of our line
progressWidth = percentage * oled.getLCDWidth();
// the y-position of our line should be at the very bottom of the screen:
progressY = oled.getLCDHeight() - 1;
// First, draw a blank line to clear any old lines out:
oled.line(0, progressY, oled.getLCDWidth(), progressY, BLACK, NORM);
// Next, draw our line:
oled.line(0, progressY, progressWidth, progressY);
//oled.display(); // Update the display, this is already called after we exit this function
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment