FB pixel

Non-contact Forehead Infrared Thermometer - Part 3 Software

Published


In the second part of this project/tutorial, we discussed the hardware part of the non-contact forehead IR thermometer. We discussed its operation and the function of its main components. Now, in this part, we are going to discuss the software or the code of the thermometer to see how it was initialized and how its operation was programmed.

Non-contact Forehead IR Thermometer Code Flowchart
Non-contact Forehead IR Thermometer Code Flowchart

The flowchart above shows how the code below works. As we discuss the code of the non-contact forehead IR thermometer, we can check the flowchart so that we can easily understand how the code or the program works.

/******************************************************************************

USBasp Connection to ATmega328P-PU:

* USBasp GND to ground

* USBasp MOSI to PB3 or MOSI or ATmega328P-PU pin 17

* USBasp +5V to +5V supply

* USBasp SS to PC6 or /RESET or ATmega328P-PU pin 1

* USBasp SCK to PB5 or SCK or ATmega328P-PU pin 19

* USBasp MISO to PB4 or MISO or ATmega328P-PU pin 18

1MHz Output[*] // e-Gizmo USBasp Pinout

GND[*][*]SS

MOSI[*][*]SCK

+5V[*][*]MISO

******************************************************************************/

/******************************************************************************

CH340 USB to Serial Connection to ATmega328P-PU:

* CH340 GND to ground

* CH340 TXD to RXD or PD0 or ATmega328P-PU pin 2

* CH340 RXD to TXD or PD1 or ATmega328P-PU pin 3

* CH340 +5V to +5V supply

******************************************************************************/

/******************************************************************************

SparkFun MLX90614 Arduino Library License:

The MIT License (MIT)

Copyright (c) 2015 SparkFun Electronics

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.

MLX90614 ------------- Arduino Connection

VIN ------------------ 5V

GND ------------------ GND

SCL ------------------ PC5 or ATmega328P-PU pin 28 or SCL (A5 on older boards)

SDA ------------------ PC4 or ATmega328P-PU pin 27 or SDA (A4 on older boards)

******************************************************************************/

/******************************************************************************

Martin Šošić HC-SR04 Library License:

The MIT License (MIT)

Copyright (c) 2016 Martin Šošić

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.

HC-SR04 -------------- ATmega328P-PU/Arduino Uno Connection

Vcc ------------------ 5V

Trig ----------------- PD7 or ATmega328P-PU pin 13 or D7

Echo ----------------- PB0 or ATmega328P-PU pin 14 or D8

Gnd ------------------ GND

******************************************************************************/

/******************************************************************************

Adafruit SSD1306 Arduino Library License:

Software License Agreement (BSD License)

Copyright (c) 2012, Adafruit Industries

All rights reserved.

Redistribution and use in source and binary forms, with or without

modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright

notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright

notice, this list of conditions and the following disclaimer in the

documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holders nor the

names of its contributors may be used to endorse or promote products

derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY

EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY

DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES

(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

OLED Display Connection to ATmega328P-PU/Arduino Uno:

GND ------------------ GND

VDD ------------------ 5V

SCK ------------------ PC5 or ATmega328P-PU pin 28 or SCL (A5 on older boards)

SDA ------------------ PC4 or ATmega328P-PU pin 27 or SDA (A4 on older boards)

******************************************************************************/

/******************************************************************************

AM312 PIR Motion Sensor Connection to ATmega328P-PU/Arduino Uno:

VIN/+ ------------------ 5V

OUT ------------------ PD2 or ATmega328P-PU pin 4 or Arduino D2

GND/- ------------------ GND

******************************************************************************/

/******************************************************************************

Buzzer 1 and 2 Connection to ATmega328P-PU/Arduino Uno:

Buzzer 1 Transistor Base Terminal ------------------ PD5 or ATmega328P-PU pin 11 or Arduino D5

Buzzer 2 Transistor Base Terminal ------------------ PD6 or ATmega328P-PU pin 12 or Arduino D6

******************************************************************************/

//Libraries included:

#include <Wire.h> // I2C Library, required for MLX90614

#include <SparkFunMLX90614.h> // SparkFun MLX90614 Library

#include <LowPower.h> // Rocket Scream Electronics Low Power Library for Arduino

#include <HCSR04.h> // Martin Šošić HC-SR04 Library for Arduino Uno

#include <Adafruit_GFX.h> // Adafruit GFX Library

#include <Adafruit_SSD1306.h> // Adafruit SSD1306 Library

#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

IRTherm temp; // Create an IRTherm object to interact with throughout

float HCSR04_Temperature = 0;

float ambientTemp = 0;

float objectTemp = 0;

float ambientTempSum = 0;

float ambientTempAvg = 0;

float ambientTempAvgRoundOff = 0;

float objectTempSum = 0;

float objectTempAvg = 0;

float roundOff_ambientTempAvg = 0;

float roundOff_objectTempAvg = 0;

float calibration_value = 0;

float calibrated_objectTempAvg = 0;

int StandbyTimeAfterStartup = 0;

int StandbyTimeAfterScan = 0;

int motionSensor = 2; // PD2 or ATmega328P-PU pin 4 or Arduino D2

int LEDpin = 9; // PB1 or ATmega328P-PU pin 15 or Arduino D9

int buzzer1 = 5; // PD5 or ATmega328P-PU pin 11 or Arduino D5

int buzzer2 = 6; // PD6 or ATmega328P-PU pin 12 or Arduino D6

// Initialize sensor that uses digital pins 7 and 8.

int triggerPin = 7; // PD7 or ATmega328P-PU pin 13 or D7

int echoPin = 8; // PB0 or ATmega328P-PU pin 14 or D8

UltraSonicDistanceSensor distanceSensor(triggerPin, echoPin);

double distance = 0;

double distancedisplay = 0;

const unsigned char CircuitBreadLogo [] PROGMEM = {

// 'CircuitBread Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0xdf, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0xdf, 0xfc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0x9f, 0xfc, 0x1f, 0x8c, 0x00, 0x00, 0x00, 0x63, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x70,

0x0f, 0xff, 0x1f, 0xfc, 0x3f, 0xcc, 0x00, 0x00, 0x00, 0x63, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x70,

0x07, 0xfe, 0x3f, 0xf8, 0x39, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x70, 0x00, 0x00, 0x00, 0x70,

0x07, 0xfe, 0x3f, 0xf8, 0x70, 0xcc, 0xd8, 0xf8, 0xc6, 0x6f, 0xcc, 0x73, 0x67, 0xc7, 0xc7, 0xf0,

0x07, 0xfc, 0x3f, 0xf8, 0x70, 0x0c, 0xf9, 0xfc, 0xc6, 0x6f, 0xcc, 0x73, 0xef, 0xef, 0xef, 0xf0,

0x07, 0xf8, 0x03, 0xf8, 0x60, 0x0c, 0xe1, 0x8c, 0xc6, 0x63, 0x0f, 0xe3, 0x8c, 0x6c, 0x6e, 0x70,

0x07, 0xf0, 0x07, 0xf8, 0x60, 0x0c, 0xc3, 0x8c, 0xc6, 0x63, 0x0f, 0xc3, 0x0c, 0x60, 0x6e, 0x70,

0x07, 0xff, 0x0f, 0xf8, 0x60, 0x0c, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0xe3, 0x0f, 0xe0, 0x6e, 0x70,

0x07, 0xff, 0x0f, 0xf8, 0x70, 0xcc, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0x73, 0x0f, 0xe7, 0xee, 0x70,

0x07, 0xff, 0x1f, 0xf8, 0x70, 0xcc, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0x73, 0x0c, 0x0f, 0xee, 0x70,

0x07, 0xff, 0x3f, 0xf8, 0x30, 0xcc, 0xc3, 0x8c, 0xc6, 0x63, 0x0c, 0x73, 0x0c, 0x6c, 0x6e, 0x70,

0x07, 0xff, 0x7f, 0xf8, 0x39, 0xcc, 0xc1, 0x8c, 0xee, 0x63, 0x0c, 0xf3, 0x0c, 0x6c, 0x6e, 0x70,

0x07, 0xfe, 0x7f, 0xf8, 0x1f, 0x8c, 0xc1, 0xfc, 0x7e, 0x63, 0xcf, 0xe3, 0x0f, 0xef, 0xef, 0xf0,

0x07, 0xfe, 0xff, 0xf8, 0x0f, 0x0c, 0xc0, 0xf8, 0x36, 0x63, 0xcf, 0xc3, 0x07, 0xc7, 0xe7, 0xf0,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

const unsigned char ThermometerLogo [] PROGMEM = {

// 'Thermometer Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x01, 0x80, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x30, 0x07, 0xf8, 0x00,

0x01, 0x80, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x91, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0x9e, 0x79, 0xcf, 0x1c, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x93, 0x0c, 0x30, 0x06, 0x00, 0x00,

0x01, 0xbf, 0x7b, 0xdb, 0x3d, 0x9b, 0xf0, 0x00, 0x00, 0x00, 0x63, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xb3, 0x33, 0x11, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xb3, 0x33, 0x01, 0xb1, 0xfb, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xb3, 0x33, 0x0f, 0xb1, 0x83, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x01, 0xb3, 0x33, 0x19, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x07, 0xf8, 0x00,

0x01, 0xb3, 0x33, 0x19, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0xb3, 0x33, 0x1f, 0xb0, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0xe3, 0xc7, 0x36, 0xc7, 0x8d, 0xb1, 0xe7, 0x9e, 0x38, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0xf6, 0x6f, 0x7f, 0xec, 0xdf, 0xfb, 0x37, 0xb3, 0x78, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0x37, 0xec, 0x66, 0x6c, 0xd9, 0x9b, 0xf3, 0x3f, 0x60, 0x00, 0x70, 0x07, 0x00, 0x00,

0x00, 0x63, 0x36, 0x0c, 0x66, 0x6c, 0xd9, 0x9b, 0x03, 0x30, 0x60, 0x00, 0xe0, 0x03, 0x80, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x63, 0x33, 0xcc, 0x66, 0x67, 0x99, 0x99, 0xe3, 0x9e, 0x60, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x80, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

const unsigned char ScanningTemperatureLogo [] PROGMEM = {

// 'Scanning Temperature Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xf0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x03, 0xf8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x30, 0x07, 0xf8, 0x00,

0x03, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x03, 0x01, 0xe3, 0xc7, 0x8f, 0x33, 0xc7, 0xc0, 0x00, 0x00, 0x93, 0x0c, 0x30, 0x06, 0x00, 0x00,

0x03, 0x83, 0xf6, 0xcf, 0xdf, 0xb7, 0xef, 0xc0, 0x00, 0x00, 0x63, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xe3, 0x34, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x7b, 0x00, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x1b, 0x03, 0xec, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x03, 0x1b, 0x36, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x07, 0xf8, 0x00,

0x03, 0xfb, 0xf6, 0x6c, 0xd9, 0xb6, 0x6f, 0xc0, 0x00, 0x00, 0x01, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0xf1, 0xe7, 0xec, 0xd9, 0xb6, 0x67, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc3, 0xc6, 0xd9, 0xf1, 0xe3, 0x9e, 0x7b, 0x33, 0x9e, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc6, 0x6f, 0xfd, 0xfb, 0x37, 0xb6, 0x7b, 0x37, 0xb3, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0x9b, 0x36, 0x23, 0x33, 0x36, 0x33, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc7, 0xec, 0xcd, 0x9b, 0xf6, 0x03, 0x33, 0x36, 0x3f, 0x00, 0x00, 0x70, 0x07, 0x00, 0x00,

0x00, 0xc6, 0x0c, 0xcd, 0x9b, 0x06, 0x1f, 0x33, 0x36, 0x30, 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0x9b, 0x36, 0x33, 0x33, 0x36, 0x33, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0xfb, 0x36, 0x33, 0x33, 0xf6, 0x33, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0xc3, 0xcc, 0xcd, 0xf1, 0xe6, 0x3f, 0x39, 0xf6, 0x1e, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x80, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

void setup()

{

// put your setup code here, to run once:

pinMode(motionSensor, INPUT);

pinMode(LEDpin, OUTPUT);

pinMode(buzzer1, OUTPUT);

pinMode(buzzer2, OUTPUT);

Serial.begin(9600); // Initialize Serial Port

Wire.begin(); // Initialize I2C

digitalWrite(LEDpin, HIGH);

OLED_Display_Initialization(); // Initialize LCD

MLX90614_Initialization(); // Initialize MLX90614

digitalWrite(LEDpin, LOW);

}

void OLED_Display_Initialization()

{

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))

{ // Address 0x3D for 128x64

Serial.println(F("SSD1306 Allocation Failed"));

for(;;);

}

delay(2000);

Serial.println(F("OLED Display Initialized!"));

display.clearDisplay(); //for Clearing the display

display.drawBitmap(0, 0, CircuitBreadLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

delay(4000);

}

void MLX90614_Initialization()

{

if (temp.begin() == false) // Initialize MLX90614

{

Serial.println(F("Failed to initialize MLX90614..."));

while(1);

}

Serial.println(F("MLX90614 Initialized!"));

Serial.print(F("Emissivity: "));

Serial.println(temp.readEmissivity());

display.clearDisplay(); //for Clearing the display

display.drawBitmap(0, 0, ThermometerLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 45;

for (int progressbarloop = 0; progressbarloop < 38; progressbarloop++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(50);

progressbar--;

}

delay(1000);

temp.setUnit(TEMP_C); // Set the library's units to Celsius

// Alternatively, TEMP_C can be replaced with

// TEMP_F for Farenheit or TEMP_K for Kelvin.

//Get ambient temperature for HC-SR04 Ultrasonic Sensor

if (temp.read()) // On success, read() will return 1, on fail 0.

{

// Use the object() and ambient() functions to grab the object and ambient

// temperatures.

// They'll be floats, calculated out to the unit you set with setUnit().

HCSR04_Temperature = temp.ambient();

Serial.print(F("HC-SR04 Ultrasonic Sensor Temperature: "));

Serial.print(HCSR04_Temperature);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

}

Serial.println(F("Non-Contact Forehead Infrared Thermometer Initialized!"));

Serial.print(F("\n"));

display.clearDisplay();

display.display();

buzzer_1();

delay(50);

buzzer_2();

display.clearDisplay();

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

void loop()

{

// put your main code here, to run repeatedly:

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterStartup++;

if ((distance > 1) && (distance < 7))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

Serial.print(F("\n"));

}

else{ }

if (StandbyTimeAfterStartup == 1)

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

}

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

StandbyTimeAfterStartup = 0;

display.clearDisplay();

display.display();

distancedisplay = distance;

buzzer_1();

delay(50);

buzzer_2();

get_Ambient_and_Object_Temperature();

display_Ambient_and_Object_Temperature_on_OLED();

ReadButtonMain:

delay(250);

display.clearDisplay();

delay(250);

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

void get_Ambient_and_Object_Temperature()

{

digitalWrite(LEDpin, HIGH);

Serial.print(F("Ambient"));

Serial.print(F("\t\t"));

Serial.println(F("Object"));

Serial.print(F("\n"));

for (int readTemp = 0; readTemp < 50; readTemp++)

{

// Call temp.read() to read object and ambient temperatures from the sensor.

if (temp.read()) // On success, read() will return 1, on fail 0.

{

// Use the object() and ambient() functions to grab the object and ambient

// temperatures.

// They'll be floats, calculated out to the unit you set with setUnit().

ambientTemp = temp.ambient();

objectTemp = temp.object();

Serial.print(ambientTemp);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.print(F("\t\t"));

Serial.print(objectTemp);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

ambientTempSum = ambientTempSum + ambientTemp;

objectTempSum = objectTempSum + objectTemp;

}

if (readTemp == 4)

{

display.clearDisplay(); // for Clearing the display

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 45;

for (int bar = 0; bar < 14; bar++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

else if (readTemp == 29)

{

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 32;

for (int bar = 0; bar < 14; bar++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

else if (readTemp == 49)

{

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 19;

for (int progressbarloop = 0; progressbarloop < 12; progressbarloop++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

}

Serial.print(F("\n"));

Serial.println(F("Sum:"));

Serial.print(ambientTempSum);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.print(F("\t"));

Serial.print(objectTempSum);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

Serial.print(F("\n"));

ambientTempAvg = ambientTempSum/50;

Serial.println(F("Average:"));

Serial.print(ambientTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print("C");

Serial.print("\t\t");

ambientTempSum = 0;

objectTempAvg = objectTempSum/50;

Serial.print(objectTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

objectTempSum = 0;

Serial.print(F("\n"));

Serial.println(F("Rounding Off Ambient Temperature:"));

int int_ambientTempAvg = ambientTempAvg;

Serial.println(int_ambientTempAvg);

float decimal_ambientTemp = ambientTempAvg - int_ambientTempAvg;

Serial.println(decimal_ambientTemp);

float decimaltimes10_ambientTemp = decimal_ambientTemp * 10;

Serial.println(decimaltimes10_ambientTemp);

float roundOff_ambientTemp = round(decimaltimes10_ambientTemp);

Serial.println(roundOff_ambientTemp);

float backTodecimal_ambientTemp = roundOff_ambientTemp/10;

Serial.println(backTodecimal_ambientTemp);

roundOff_ambientTempAvg = int_ambientTempAvg + backTodecimal_ambientTemp;

Serial.print(roundOff_ambientTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.println(F("\n"));

Serial.println(F("Rounding Off Object Temperature:"));

int int_objectTempAvg = objectTempAvg;

Serial.println(int_objectTempAvg);

float decimal_objectTemp = objectTempAvg - int_objectTempAvg;

Serial.println(decimal_objectTemp);

float decimaltimes10_objectTemp = decimal_objectTemp * 10;

Serial.println(decimaltimes10_objectTemp);

float roundOff_objectTemp = round(decimaltimes10_objectTemp);

Serial.println(roundOff_objectTemp);

float backTodecimal_objectTemp = roundOff_objectTemp/10;

Serial.println(backTodecimal_objectTemp);

roundOff_objectTempAvg = int_objectTempAvg + backTodecimal_objectTemp;

Serial.print(roundOff_objectTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.println(F("\n"));

buzzer_1();

delay(50);

buzzer_2();

digitalWrite(LEDpin, LOW);

}

void display_Ambient_and_Object_Temperature_on_OLED()

{

DisplayResultsAgainAfterExitingLowPowerMode:

delay(200);

display.clearDisplay();

display.display();

delay(200);

display.clearDisplay();

delay(100);

display.setTextSize(1);

display.setCursor(0, 0);

display.print(F("A:"));

display.print(roundOff_ambientTempAvg, 1);

display.drawCircle(38, 1, 1, WHITE);

display.setCursor(41, 0);

display.print(F("C"));

display.setCursor(80, 0);

display.print(F("D:"));

display.print(distancedisplay);

display.print(F("cm"));

display.drawLine(0, 12, 128, 12, WHITE);

calibration_value = calibrate_objectTemp(roundOff_ambientTempAvg, distancedisplay);

Serial.print(F("Calibration Value: "));

Serial.println(calibration_value);

calibrated_objectTempAvg = roundOff_objectTempAvg + calibration_value;

Serial.print(F("Calibrated Object Temperature: "));

Serial.print(calibrated_objectTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

display.setTextSize(3);

display.setCursor(14, 27);

display.print(calibrated_objectTempAvg, 1);

display.drawCircle(92, 30, 3, WHITE);

display.setCursor(100, 27);

display.print(F("C"));

display.drawLine(0, 63, 128, 63, WHITE);

display.display();

display.setTextSize(1);

Serial.print(F("\n"));

HCSR04_Temperature = roundOff_ambientTempAvg;

Serial.print(F("HC-SR04 Ultrasonic Sensor Temperature: "));

Serial.print(roundOff_ambientTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance < 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(1000);

}

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterScan++;

if ((distance > 1) && (distance < 8))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

}

else{ }

if (StandbyTimeAfterScan == 1)

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

}

else if (StandbyTimeAfterScan == 2000) // more or less 30 seconds

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

LowPower_powerDown();

StandbyTimeAfterScan = 0;

goto DisplayResultsAgainAfterExitingLowPowerMode;

}

}

Serial.println(F("\n"));

StandbyTimeAfterScan = 0;

digitalWrite(LEDpin, HIGH);

display.clearDisplay();

delay(200);

digitalWrite(LEDpin, LOW);

}

void LowPower_powerDown()

{

digitalWrite(LEDpin, HIGH);

delay(500);

digitalWrite(LEDpin, LOW);

display.clearDisplay();

display.display();

// Allow wake up pin to trigger interrupt on low.

attachInterrupt(digitalPinToInterrupt(motionSensor), wakeUp, RISING);

// Enter power down state with ADC and BOD module disabled.

// Wake up when wake up pin is low.

LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

// Disable external pin interrupt on wake up pin.

detachInterrupt(digitalPinToInterrupt(motionSensor));

digitalWrite(LEDpin, HIGH);

delay(500);

digitalWrite(LEDpin, LOW);

Serial.print(F("\n"));

Serial.println(F("Motion Detected!"));

}

void wakeUp()

{

// Just a handler for the pin interrupt.

}

void buzzer_1()

{

for (int buzzer1loop = 0; buzzer1loop < 60; buzzer1loop++)

{

digitalWrite(buzzer1, HIGH);

delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz

digitalWrite(buzzer1, LOW);

delayMicroseconds(1000 - 100);

}

}

void buzzer_2()

{

for (int buzzer2loop = 0; buzzer2loop < 100; buzzer2loop++)

{

digitalWrite(buzzer2, HIGH);

delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz

digitalWrite(buzzer2, LOW);

delayMicroseconds(1000 - 100);

}

}

float calibrate_objectTemp(float roundOff_ambientTempAvg, double distancedisplay)

{

Serial.print(F("Ambient Temperature: "));

Serial.print(roundOff_ambientTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

Serial.print(F("Distance: "));

Serial.print(distancedisplay);

Serial.println(F("cm"));

if (((roundOff_ambientTempAvg > 20) && (roundOff_ambientTempAvg < 25)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 4.2;

}

else if (((roundOff_ambientTempAvg > 25) && (roundOff_ambientTempAvg < 30)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 3.4;

}

else if (((roundOff_ambientTempAvg > 30) && (roundOff_ambientTempAvg < 32.5)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 2.9;

}

else if (((roundOff_ambientTempAvg > 32.5) && (roundOff_ambientTempAvg < 35)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 2.2;

}

else

{

calibration_value = 0;

}

delay(50);

Serial.print(F("Calibration Value: "));

Serial.println(calibration_value);

return calibration_value;

}

The code really starts at line 149. Line 1 to line 148 only show the connection between the components and the MCU, and the licenses of the libraries included in the code.

//Libraries included:

#include <Wire.h> // I2C Library, required for MLX90614

#include <SparkFunMLX90614.h> // SparkFun MLX90614 Library

#include <LowPower.h> // Rocket Scream Electronics Low Power Library for Arduino

#include <HCSR04.h> // Martin Šošić HC-SR04 Library for Arduino Uno

#include <Adafruit_GFX.h> // Adafruit GFX Library

#include <Adafruit_SSD1306.h> // Adafruit SSD1306 Library

In line 150 to line 155, we can see the different libraries included in the code. These libraries help us to easily configure and use the functions of the devices connected to the MCU. In line 150, we included the Wire library “#include <Wire.h>” from Arduino which allows the MCU to communicate with I2C or TWI devices such as the MLX90614 temperature sensor and the SSD1306 OLED display.

In line 151, we included the MLX90614 library “#include <SparkFunMLX90614.h>” from SparkFun. If you’re going to read the MLX90614 documentation, there are really a lot of things that we need to configure, which will take a lot of time and effort, to use the MLX90614. But thanks to SparkFun, we can just call the functions of the library they made for the MLX90614 device to scan the temperature, read the results, and change its emissivity settings.

As mentioned earlier, to save energy, the MCU enters into low power mode if the thermometer is not being used for about 30-40 seconds. We easily implemented this one by including the low power library created by Rocket Scream Electronics in line 152#include <LowPower.h>”. This low power library helps us use the low power mode feature of the ATmega328P without directly setting the registers.

In line 153, we included the library for the HC-SR04 ultrasonic sensor “#include <HCSR04.h>” created by Martin Šošić. This library helps us easily measure distance by just calling its function. For the OLED display, we use Adafruit GFX and SSD1306 library in line 154#include <Adafruit_GFX.h>“ and line 155#include <Adafruit_SSD1306.h>”.

#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

In line 157 and line 158, we defined the width and height of the OLED display and set them in line 161. Since we’re using a 128 by 64 pixels display, the width is set to 128#define SCREEN_WIDTH 128” and the height is set to 64#define SCREEN_HEIGHT 64”.

IRTherm temp; // Create an IRTherm object to interact with throughout

float HCSR04_Temperature = 0;

float ambientTemp = 0;

float objectTemp = 0;

float ambientTempSum = 0;

float ambientTempAvg = 0;

float ambientTempAvgRoundOff = 0;

float objectTempSum = 0;

float objectTempAvg = 0;

float roundOff_ambientTempAvg = 0;

float roundOff_objectTempAvg = 0;

float calibration_value = 0;

float calibrated_objectTempAvg = 0;

int StandbyTimeAfterStartup = 0;

int StandbyTimeAfterScan = 0;

In line 163, we created the IRTherm object “temp” which we will use later when calling MLX90614 library functions. In line 164 to line 177, we initialized the variables that we are going to use later to store the values of the temperatures that the MLX90614 measured.

int motionSensor = 2; // PD2 or ATmega328P-PU pin 4 or Arduino D2

int LEDpin = 9; // PB1 or ATmega328P-PU pin 15 or Arduino D9

int buzzer1 = 5; // PD5 or ATmega328P-PU pin 11 or Arduino D5

int buzzer2 = 6; // PD6 or ATmega328P-PU pin 12 or Arduino D6

// Initialize sensor that uses digital pins 7 and 8.

int triggerPin = 7; // PD7 or ATmega328P-PU pin 13 or D7

int echoPin = 8; // PB0 or ATmega328P-PU pin 14 or D8

UltraSonicDistanceSensor distanceSensor(triggerPin, echoPin);

double distance = 0;

double distancedisplay = 0;

In line 179, we assigned digital pin 2 (D2) or pin 4 of the MCUint motionSensor = 2;” as the input pin for the motion sensor output while in line 180, we assigned digital pin 9 (D9) or pin 15 of the MCUint LEDpin = 9;” as the output pin for the red LED indicator. For buzzer 1 and buzzer 2, we set digital pin 5 (D5) and digital pin 6 (D6) or pin 11 and pin 12 of the MCU as outputs in line 182int buzzer1 = 5;” and line 183int buzzer2 = 6;”, respectively. These pins will drive the base-to-emitter junction of the BJTs driving the buzzers. In line 186, we assigned digital pin 7 (D7) or pin 13 of the MCUint triggerPin = 7;” as the MCU’s output pin for the ultrasonic sensor Trig pin while in line 187, we assigned the digital pin 8 (D8) or pin 14 of the MCU “int echoPin = 8;“ as the MCU’s input pin for the ultrasonic sensor Echo pin. In line 190 to line 191, we initialized the variables where we are going to store the value of the distance measured by the ultrasonic sensor.

const unsigned char CircuitBreadLogo [] PROGMEM = {

// 'CircuitBread Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0xdf, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0xdf, 0xfc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x0f, 0xff, 0x9f, 0xfc, 0x1f, 0x8c, 0x00, 0x00, 0x00, 0x63, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x70,

0x0f, 0xff, 0x1f, 0xfc, 0x3f, 0xcc, 0x00, 0x00, 0x00, 0x63, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x70,

0x07, 0xfe, 0x3f, 0xf8, 0x39, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x70, 0x00, 0x00, 0x00, 0x70,

0x07, 0xfe, 0x3f, 0xf8, 0x70, 0xcc, 0xd8, 0xf8, 0xc6, 0x6f, 0xcc, 0x73, 0x67, 0xc7, 0xc7, 0xf0,

0x07, 0xfc, 0x3f, 0xf8, 0x70, 0x0c, 0xf9, 0xfc, 0xc6, 0x6f, 0xcc, 0x73, 0xef, 0xef, 0xef, 0xf0,

0x07, 0xf8, 0x03, 0xf8, 0x60, 0x0c, 0xe1, 0x8c, 0xc6, 0x63, 0x0f, 0xe3, 0x8c, 0x6c, 0x6e, 0x70,

0x07, 0xf0, 0x07, 0xf8, 0x60, 0x0c, 0xc3, 0x8c, 0xc6, 0x63, 0x0f, 0xc3, 0x0c, 0x60, 0x6e, 0x70,

0x07, 0xff, 0x0f, 0xf8, 0x60, 0x0c, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0xe3, 0x0f, 0xe0, 0x6e, 0x70,

0x07, 0xff, 0x0f, 0xf8, 0x70, 0xcc, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0x73, 0x0f, 0xe7, 0xee, 0x70,

0x07, 0xff, 0x1f, 0xf8, 0x70, 0xcc, 0xc3, 0x00, 0xc6, 0x63, 0x0c, 0x73, 0x0c, 0x0f, 0xee, 0x70,

0x07, 0xff, 0x3f, 0xf8, 0x30, 0xcc, 0xc3, 0x8c, 0xc6, 0x63, 0x0c, 0x73, 0x0c, 0x6c, 0x6e, 0x70,

0x07, 0xff, 0x7f, 0xf8, 0x39, 0xcc, 0xc1, 0x8c, 0xee, 0x63, 0x0c, 0xf3, 0x0c, 0x6c, 0x6e, 0x70,

0x07, 0xfe, 0x7f, 0xf8, 0x1f, 0x8c, 0xc1, 0xfc, 0x7e, 0x63, 0xcf, 0xe3, 0x0f, 0xef, 0xef, 0xf0,

0x07, 0xfe, 0xff, 0xf8, 0x0f, 0x0c, 0xc0, 0xf8, 0x36, 0x63, 0xcf, 0xc3, 0x07, 0xc7, 0xe7, 0xf0,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

const unsigned char ThermometerLogo [] PROGMEM = {

// 'Thermometer Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x01, 0x80, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x30, 0x07, 0xf8, 0x00,

0x01, 0x80, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x91, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0x9e, 0x79, 0xcf, 0x1c, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x93, 0x0c, 0x30, 0x06, 0x00, 0x00,

0x01, 0xbf, 0x7b, 0xdb, 0x3d, 0x9b, 0xf0, 0x00, 0x00, 0x00, 0x63, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xb3, 0x33, 0x11, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xb3, 0x33, 0x01, 0xb1, 0xfb, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xb3, 0x33, 0x0f, 0xb1, 0x83, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x01, 0xb3, 0x33, 0x19, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x07, 0xf8, 0x00,

0x01, 0xb3, 0x33, 0x19, 0xb1, 0x9b, 0x30, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0xb3, 0x33, 0x1f, 0xb0, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0xe3, 0xc7, 0x36, 0xc7, 0x8d, 0xb1, 0xe7, 0x9e, 0x38, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0xf6, 0x6f, 0x7f, 0xec, 0xdf, 0xfb, 0x37, 0xb3, 0x78, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x63, 0x37, 0xec, 0x66, 0x6c, 0xd9, 0x9b, 0xf3, 0x3f, 0x60, 0x00, 0x70, 0x07, 0x00, 0x00,

0x00, 0x63, 0x36, 0x0c, 0x66, 0x6c, 0xd9, 0x9b, 0x03, 0x30, 0x60, 0x00, 0xe0, 0x03, 0x80, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x63, 0x36, 0x6c, 0x66, 0x6c, 0xd9, 0x9b, 0x33, 0x33, 0x60, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x63, 0x33, 0xcc, 0x66, 0x67, 0x99, 0x99, 0xe3, 0x9e, 0x60, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x80, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

const unsigned char ScanningTemperatureLogo [] PROGMEM = {

// 'Scanning Temperature Logo, 128x64px

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x01, 0xf0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x03, 0xf8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x30, 0x07, 0xf8, 0x00,

0x03, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x03, 0x01, 0xe3, 0xc7, 0x8f, 0x33, 0xc7, 0xc0, 0x00, 0x00, 0x93, 0x0c, 0x30, 0x06, 0x00, 0x00,

0x03, 0x83, 0xf6, 0xcf, 0xdf, 0xb7, 0xef, 0xc0, 0x00, 0x00, 0x63, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x01, 0xe3, 0x34, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x7b, 0x00, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x1b, 0x03, 0xec, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x03, 0x1b, 0x36, 0x6c, 0xd9, 0xb6, 0x6c, 0xc0, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x07, 0xf8, 0x00,

0x03, 0xfb, 0xf6, 0x6c, 0xd9, 0xb6, 0x6f, 0xc0, 0x00, 0x00, 0x01, 0xf8, 0x30, 0x06, 0x00, 0x00,

0x01, 0xf1, 0xe7, 0xec, 0xd9, 0xb6, 0x67, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xff, 0xe0,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0xf8, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc3, 0xc6, 0xd9, 0xf1, 0xe3, 0x9e, 0x7b, 0x33, 0x9e, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc6, 0x6f, 0xfd, 0xfb, 0x37, 0xb6, 0x7b, 0x37, 0xb3, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0x9b, 0x36, 0x23, 0x33, 0x36, 0x33, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,

0x00, 0xc7, 0xec, 0xcd, 0x9b, 0xf6, 0x03, 0x33, 0x36, 0x3f, 0x00, 0x00, 0x70, 0x07, 0x00, 0x00,

0x00, 0xc6, 0x0c, 0xcd, 0x9b, 0x06, 0x1f, 0x33, 0x36, 0x30, 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0x9b, 0x36, 0x33, 0x33, 0x36, 0x33, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0xc6, 0x6c, 0xcd, 0xfb, 0x36, 0x33, 0x33, 0xf6, 0x33, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0xc3, 0xcc, 0xcd, 0xf1, 0xe6, 0x3f, 0x39, 0xf6, 0x1e, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1f, 0xfc, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0f, 0xf8, 0x30, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0xf0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xe0, 0x60, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x80, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0e, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

In line 193 to line 395, we can see the byte arrays of the images or logo that we are going to display on the OLED display such as the CircuitBreadLogo [] (displayed right after turning ON the thermometer), ThermometerLogo [] (displayed during MLX90614 initialization), and ScanningTemperatureLogo [] (displayed while scanning temperature).

setup() Function

setup() Function Flowchart
setup() Function Flowchart

Now, after defining some parameters, initializing variables, etc., let’s proceed to the setup() function. The flowchart above shows the flow of the setup() function. To see how the MCU pins and libraries are initialized, let’s check what's inside the setup() function.

void setup()

{

// put your setup code here, to run once:

pinMode(motionSensor, INPUT);

pinMode(LEDpin, OUTPUT);

pinMode(buzzer1, OUTPUT);

pinMode(buzzer2, OUTPUT);

Serial.begin(9600); // Initialize Serial Port

Wire.begin(); // Initialize I2C

digitalWrite(LEDpin, HIGH);

OLED_Display_Initialization(); // Initialize LCD

MLX90614_Initialization(); // Initialize MLX90614

digitalWrite(LEDpin, LOW);

}

The setup() function starts at line 397 and ends at line 416. This is where we set the pin modes and initialize the libraries used in the code. As you can see, in line 401, we set our motion sensor pin (D2/MCU pin 4/motionSensor) as an input and in line 402, we set the red LED pin (D9/MCU pin 15/LEDpin) as an output by using the pinMode(pin, mode) function. In line 404 and line 405, we also set the buzzer 1 (D5/MCU pin 11/buzzer1) and buzzer 2 (D6/MCU pin 12/buzzer2) pins as outputs by using the same pinMode(pin, mode) function. As you can see on the flowchart, the next part should be setting the ultrasonic sensor pins. However, there’s no need to set it in the setup() function anymore since it was already set in the HC-SR04 library through line 185 to line 188.

// Initialize sensor that uses digital pins 7 and 8.

int triggerPin = 7; // PD7 or ATmega328P-PU pin 13 or D7

int echoPin = 8; // PB0 or ATmega328P-PU pin 14 or D8

UltraSonicDistanceSensor distanceSensor(triggerPin, echoPin);

To open the serial port and set its data rate to 9600bps, we use the Serial.begin(9600) function in line 407 and to initiate the Wire library and join the I2C bus as a master, we use the Wire.begin() function in line 408. Before initializing the OLED display and the MLX90614, in line 410, we set first the D9 pin (pin 15 of the MCU/LEDpin) into HIGH “digitalWrite(LEDpin, HIGH);” to turn on the red LED, indicating that the thermometer is processing something. Then in line 412, we proceed to the OLED display initialization. For the OLED display initialization, let’s look at line 418 to line 433 of the code.

void OLED_Display_Initialization()

{

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))

{ // Address 0x3D for 128x64

Serial.println(F("SSD1306 Allocation Failed"));

for(;;);

}

delay(2000);

Serial.println(F("OLED Display Initialized!"));

display.clearDisplay(); //for Clearing the display

display.drawBitmap(0, 0, CircuitBreadLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

delay(4000);

}

To initialize the OLED display, in line 420, we use the SSD1306 library function “display.begin(SSD1306_SWITCHCAPVCC, 0x3C)“ with the vcs (SSD1306_SWITCHCAPVCC - to generate the display voltage (step up) from the 3.3V source) and addr (0x3C - I2C address of corresponding SSD1306 display) parameters. The code in line 420 is actually an if statement with a !display.begin(SSD1306_SWITCHCAPVCC, 0x3C) condition. This means that the statements inside the if statement will be executed or not depending on the return value of the display.begin(SSD1306_SWITCHCAPVCC, 0x3C) function.

CircuitBread Logo on OLED Display
CircuitBread Logo on OLED Display

If the function returns a false value, this means that the OLED display initialization failed. The MCU will transmit a message “SSD1306 Allocation Failed” through its serial port then does nothing forever (line 422 to line 423). But if the function returns a true value, this means that OLED display initialization is successful. So line 422 to line 423 will be skipped. After the 2 seconds delay (line 426), the MCU transmits the “OLED Display Initialized!” message through the serial port (line 427) then displays the CircuitBread logo on the OLED display using line 429 to line 432 for about 4 seconds. Then the code jumps back to line 413 for the MLX90614 initialization. For the MLX90614 initialization, let’s refer to line 435 to line 497.

void MLX90614_Initialization()

{

if (temp.begin() == false) // Initialize MLX90614

{

Serial.println(F("Failed to initialize MLX90614..."));

while(1);

}

Serial.println(F("MLX90614 Initialized!"));

Serial.print(F("Emissivity: "));

Serial.println(temp.readEmissivity());

display.clearDisplay(); //for Clearing the display

display.drawBitmap(0, 0, ThermometerLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 45;

for (int progressbarloop = 0; progressbarloop < 38; progressbarloop++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(50);

progressbar--;

}

delay(1000);

The initialization of the MLX90614 temperature sensor is just similar to the OLED display initialization. In line 437, we call the temp.begin() function and if it returns a false value, the MCU will transmit a message “Failed to initialize MLX90614…” through the serial port and the MCU will do nothing forever (line 439 to line 440). However, if the initialization is successful, the MCU will send the message “MLX90614 Initialized!” through the serial port (line 443) including the MLX90614 emissivity (line 445) by calling the function temp.readEmissivity(). Then the MCU displays the thermometer initialization logo (ThermometerLogo) on the OLED display in line 447 to line 458.

Thermometer Initialization Logo Animation
Thermometer Initialization Logo Animation

temp.setUnit(TEMP_C); // Set the library's units to Celsius

// Alternatively, TEMP_C can be replaced with

// TEMP_F for Farenheit or TEMP_K for Kelvin.

In line 460, we set the temperature unit of the library to degree Celsius using the function “temp.setUnit(TEMP_C);”. If you want to set it to Farenheit or Kelvin, you can replace TEMP_C with TEMP_F or TEMP_K, respectively.

The ultrasonic sensor measures distance by using this single line of code, “distance = distanceSensor.measureDistanceCm();”. As you can see, it doesn’t have a parameter. If you’re going to leave its parameter empty, the HCSR04 library, by default, will use 19.307°C as the temperature when calculating the speed of sound. This will affect the calculation of the distance especially if there’s a significant difference between the default library temperature and the actual ambient temperature.

//Get ambient temperature for HC-SR04 Ultrasonic Sensor

if (temp.read()) // On success, read() will return 1, on fail 0.

{

// Use the object() and ambient() functions to grab the object and ambient

// temperatures.

// They'll be floats, calculated out to the unit you set with setUnit().

HCSR04_Temperature = temp.ambient();

Serial.print(F("HC-SR04 Ultrasonic Sensor Temperature: "));

Serial.print(HCSR04_Temperature);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

}

So in line 465, we perform an initial reading of the ambient temperature using the temp.read() function of the MLX90614 library. Again, the code is similar to the OLED display and MLX90614 initialization (line 420 and line 437). The return value of the temp.read() function is the condition of the if statement in line 465. The temp.read() function reads both the ambient and object temperatures by executing the readAmbient() and readObject() functions of the MLX90614 library. If the MLX90614 successfully reads the ambient and object temperatures, both functions, readAmbient() and readObject(), will return a true value to the temp.read() function. In this case, the temp.read() function will also return a true value to the if statement and the code inside the if statement will be executed.

If the MLX90614 device is not malfunctioning, then we can assume that the return value will always be “true”. In this case, line 471 will be executed. In line 471, we grab the result of the ambient temperature and store it on the HCSR04_Temperature variable. Next time we measure the distance using the ultrasonic sensor, we will use the HCSR04_Temperature variable as a parameter of the distance = distanceSensor.measureDistanceCm(); function. Line 472 to line 476 is just for checking the ambient temperature result in the Arduino IDE serial monitor.

Serial.println(F("Non-Contact Forehead Infrared Thermometer Initialized!"));

Serial.print(F("\n"));

display.clearDisplay();

display.display();

buzzer_1();

delay(50);

buzzer_2();

In line 479 to line 480, we serially print a message to the UART port “Non-Contact Forehead Infrared Thermometer Initialized!” to show that the thermometer initialization is done. Then in line 482 to line 483, we clear the OLED display. In line 485 to line 487, we call the buzzer_1() and buzzer_2() functions to produce beep sounds that will indicate that the thermometer initialization is done.

void buzzer_1()

{

for (int buzzer1loop = 0; buzzer1loop < 60; buzzer1loop++)

{

digitalWrite(buzzer1, HIGH);

delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz

digitalWrite(buzzer1, LOW);

delayMicroseconds(1000 - 100);

}

}

void buzzer_2()

{

for (int buzzer2loop = 0; buzzer2loop < 100; buzzer2loop++)

{

digitalWrite(buzzer2, HIGH);

delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz

digitalWrite(buzzer2, LOW);

delayMicroseconds(1000 - 100);

}

}

You can check the buzzer functions in line 855 to line 875. The two functions are just similar. Their purpose is to generate a PWM signal on the buzzer pins (D5/MCU Pin 11/buzzer1 and D6/MCU Pin 12/buzzer2). They just differ in the number of loops.

display.clearDisplay();

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

In line 489 to line 496, we display “Position Forehead” on the OLED display then the code returns to the setup() function then executes line 415digitalWrite(LEDpin, LOW);” to turn OFF the red LED. After that, the code will now enter the loop() function and the non-contact forehead IR thermometer is now ready to be used.

loop() Function

loop() Function Flow Chart
loop() Function Flow Chart

As you can see on the flowchart above, the first thing that happens inside the loop() function is the measurement of the distance between the sensor and the forehead. See the code below.

void loop()

{

// put your main code here, to run repeatedly:

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterStartup++;

if ((distance > 1) && (distance < 7))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

Serial.print(F("\n"));

}

else{ }

if (StandbyTimeAfterStartup == 1)

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

}

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

StandbyTimeAfterStartup = 0;

display.clearDisplay();

display.display();

distancedisplay = distance;

buzzer_1();

delay(50);

buzzer_2();

get_Ambient_and_Object_Temperature();

display_Ambient_and_Object_Temperature_on_OLED();

ReadButtonMain:

delay(250);

display.clearDisplay();

delay(250);

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

The loop() function starts at line 499 and ends at line 561. In line 502, we use the “distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);” code to measure distance. This time we’re now using the HCSR04_Temperature variable as its parameter to include the ambient temperature that the MLX90614 measured during the initialization in the calculation of the distance. As you can see on the flowchart, after measuring the distance, the next task that the MCU will execute depends if the distance measured by the ultrasonic sensor is less than or greater than 7cm. Let’s discuss first what will happen if the distance is greater than 7cm.

As shown in the flowchart above, if the distance is greater than 7cm (this means that no one uses the thermometer), the MCU will enter a while loop and then trigger the ultrasonic sensor to measure the distance again. To make things clear, the first measurement of the distance is for the MCU to decide whether it will enter the while loop (if distance is greater than 7cm) or will start scanning the temperature (if distance is less than 7cm). The next distance measurement, happening inside the while loop, will determine if the program will stay or exit the while loop.

After measuring the distance again, the next thing that will happen is that the MCU will increment the value of the “StandbyTimeAfterStartup” variable by 1. The purpose of this is that if the “StandbyTimeAfterStartup” variable value reaches 2000 and the distance measured is still greater than 7cm, the MCU will enter into low power mode. After incrementing the value of the “StandbyTimeAfterStartup” variable, the MCU will check if its value is equal to 1. This is just for monitoring purposes as the value of the “StandbyTimeAfterStartup” variable can be monitored on the Arduino IDE Serial Monitor. If the value of the “StandbyTimeAfterStartup” variable is equal to 1, the MCU prints its value on the serial monitor then the MCU will check again if the distance measured last time is greater or less than 7cm. If the distance is still greater than 7cm, the MCU will trigger the ultrasonic sensor again to measure the distance and then increments the value of the “StandbyTimeAfterStartup” variable by 1 again. Since the “StandbyTimeAfterStartup” value is now greater than 1, the program will proceed into checking if its value is equal to 2000. If the value is not yet equal to 2000 and the distance measured by the ultrasonic sensor is still greater than 7cm, the MCU will continue to increment the value of the “StandbyTimeAfterStartup” variable until it reaches 2000.

If the distance measured by the ultrasonic sensor is still greater than 7cm and the value of the “StandbyTimeAfterStartup” variable reaches 2000, the MCU will print “StandbyTimeAfterStartup” value on the serial monitor then enters low power mode. This time the MCU will just sleep and just keeps on reading the status of the D2 interrupt pin (MCU pin 4/motionSensor) where the output of the AM312 PIR motion sensor is connected. When there’s no motion detected by the motion sensor, its OUT pin stays at LOW level. Therefore, the D2 pin is also at LOW level and the MCU remains in low power mode. But when the motion sensor detects a motion, its OUT pin outputs a HIGH signal then the MCU exits low power mode.

After exiting low power mode, the MCU resets the value of the “StandbyTimeAfterStartup” variable, displays “Position Forehead” again on the OLED display, and then measures the distance again. If the distance is still greater than 7cm (which means that a motion is detected but still no one uses the thermometer), the whole process mentioned above will be repeated again. But if the distance is now less than 7cm, the thermometer will scan the forehead temperature. Now, let’s check line 504 to line 533 of the code to see how we programmed this operation.

void loop()

{

// put your main code here, to run repeatedly:

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterStartup++;

if ((distance > 1) && (distance < 7))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

Serial.print(F("\n"));

}

else{ }

if (StandbyTimeAfterStartup == 1)

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

}

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

In order to do the operation that we’ve discussed above, in line 504 of the code we use a while loop with the “(distance < 1) || (distance > 7)” condition. If you’re not familiar with while loop, a while loop executes a statement or block of statements repeatedly as long as the condition remains true. So here, the while loop will keep on executing line 506 to line 531 as long as the distance measured is greater than 7cm “(distance > 7)”. I’ve included the “(distance < 1)” in the condition too because if you’re going to check the HCSR04 library, the calculation sometimes returns a -1 value if the distance is equal to zero or greater than 400cm.

void loop()

{

// put your main code here, to run repeatedly:

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterStartup++;

if ((distance > 1) && (distance < 7))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

Serial.print(F("\n"));

}

else{ }

In line 506, the ultrasonic sensor measures the distance again. This is the distance measurement happening inside the while loop that we’ve discussed earlier. The first distance measurement that we’ve mentioned is in line 502. The code in line 508 increments the value of the “StandbyTimeAfterStartup” variable by 1. The if statement in line 510 to line 517 will be executed only if the distance measured is less than 7cm.

if (StandbyTimeAfterStartup == 1)

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

}

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

The if statement in line 520 to line 524 will check if the value of the “StandbyTimeAfterStartup” variable is equal to 1. If it is, then the MCU will transmit a message through the UART port containing the value of the “StandbyTimeAfterStartup” variable, which can be monitored on the Arduino IDE serial monitor if the UART port is connected to a computer via USB-to-serial converter. In the next loop, if the distance is still greater than 7cm, the value of the “StandbyTimeAfterStartup” variable will be incremented again by 1 and this time it is now greater than 1, then line 520 to line 524 will be skipped and the program will proceed to the else if statement in line 525 to line 532.

If the distance measured is still greater than 7cm and the value of the “StandbyTimeAfterStartup” variable is not yet equal to 2000, line 525 to line 532 will not be executed and the program will just keep on measuring the distance and incrementing the “StandbyTimeAfterStartup” variable. However, if the value of the “StandbyTimeAfterStartup” variable reaches 2000 and the distance is still greater than 7cm, the else if statement in line 525 to line 532 will be executed. The first thing that will happen is that In line 527 to line 528, the MCU will transmit a message which contains the value of the “StandbyTimeAfterStartup” variable on the UART port which can also be monitored on the Arduino IDE serial monitor. In line 529, the function LowPower_powerDown() will be executed and the MCU will enter in low power mode. Let’s check the LowPower_powerDown() function.

void LowPower_powerDown()

{

digitalWrite(LEDpin, HIGH);

delay(500);

digitalWrite(LEDpin, LOW);

display.clearDisplay();

display.display();

// Allow wake up pin to trigger interrupt on low.

attachInterrupt(digitalPinToInterrupt(motionSensor), wakeUp, RISING);

// Enter power down state with ADC and BOD module disabled.

// Wake up when wake up pin is low.

LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

// Disable external pin interrupt on wake up pin.

detachInterrupt(digitalPinToInterrupt(motionSensor));

digitalWrite(LEDpin, HIGH);

delay(500);

digitalWrite(LEDpin, LOW);

Serial.print(F("\n"));

Serial.println(F("Motion Detected!"));

}

void wakeUp()

{

// Just a handler for the pin interrupt.

}

The LowPower_powerDown() function starts at line 824 and ends at line 853. The function starts by turning ON the red LED for about 500ms in line 826 to line 828 to indicate that the MCU will enter into low power mode. Then in line 829 to line 830 the OLED display buffer contents are cleared to set all pixels to OFF. The low power library included in this code contains different functions of low power mode configurations. As you can in line 837, I chose the powerDown mode in which you can use external pin interrupt to exit low power mode.

Since this powerDown function uses external pin interrupt, in line 833, we’ve set D2 (MCU pin 4/motionSensor) as an interrupt pin using the function “attachInterrupt(digitalPinToInterrupt(motionSensor), wakeUp, RISING);”. As you can see, the attachInterrupt() function has three parameters. In the first parameter “digitalPinToInterrupt(motionSensor)”, we put the interrupt pin “motionSensor”. The second parameter “wakeUp” is the name of the function or interrupt service routine (ISR) that is going to be called when the external pin interrupt occurs. In line 850 to line 853, we have the wakeUp() ISR but didn’t put any statement in there as it is not necessary and if ever you put something in there, the code inside should not take so much time. The third parameter is the mode. It defines when the interrupt should be triggered. In this case, I selected RISING because the output of the motion sensor goes from LOW to HIGH when motion is detected. The other three mode constants are LOW, CHANGE, and FALLING.

After executing line 833, the MCU enters into low power mode using the function in line 837LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);”. The powerDown() function puts the MCU into power down state. It’s the lowest current consumption state and as mentioned earlier, you can use an external pin interrupt to exit from this powerDown state. The powerDown() function also has three parameters. The first parameter is the period or duration of the low power mode. If you check the low power library and look for the powerDown details, you will see that the powerDown period parameter has 11 sleep options. We can select from 15ms sleep up to sleep forever and since we want to wake the MCU up by using an external pin interrupt, we selected the SLEEP_FOREVER option. The second and third parameters are basically for turning ON or OFF the ADC (Analog-to-Digital Converter) and the BOD (Brown Out Detector) modules, respectively. ADC_ON and BOD_ON to leave them in their default state or ADC_OFF and BOD_OFF to turn them OFF. Turning the ADC and the BOD OFF really helps in saving power.

// Disable external pin interrupt on wake up pin.

detachInterrupt(digitalPinToInterrupt(motionSensor));

digitalWrite(LEDpin, HIGH);

delay(500);

digitalWrite(LEDpin, LOW);

Serial.print(F("\n"));

Serial.println(F("Motion Detected!"));

}

void wakeUp()

{

// Just a handler for the pin interrupt.

}

Now, assuming that the motion sensor detects a motion, the MCU exits from low power mode and the program jumps to the wakeUp() ISR. But since we didn’t put anything there, it will proceed to line 840detachInterrupt(digitalPinToInterrupt(motionSensor));” to disable the external interrupt pin. Then in line 842 to line 844 the MCU turns ON the red LED for about 500ms indicating that the MCU woke up from sleep mode. For monitoring purposes, in line 846 to line 847, the MCU transmits the message “Motion Detected!” on the UART port in which we can see in a serial monitor.

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

StandbyTimeAfterStartup = 0;

display.clearDisplay();

display.display();

distancedisplay = distance;

buzzer_1();

delay(50);

buzzer_2();

get_Ambient_and_Object_Temperature();

display_Ambient_and_Object_Temperature_on_OLED();

ReadButtonMain:

delay(250);

display.clearDisplay();

delay(250);

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

Then the program exits the LowPower_powerDown(); function then returns to line 530 to reset the value of the “StandbyTimeAfterStartup” variable. In line 531, we have the instruction or code to go to the ReadButtonMain label which is in line 550. So the program jumps to line 550 then executes the next lines. The code in line 551 to line 560 will just display the message “Position Forehead” on the OLED display then the program will return again to the first line (line 502) or task of the loop() function. So we’re done discussing how the thermometer will operate if ever the distance measured by the ultrasonic sensor is greater than 7cm (or no one uses the thermometer and standby time exceeds 30-40 seconds) and have explained how we programmed it. Let’s check how the thermometer will operate if the distance measured is less than 7cm.

If the ultrasonic sensor measured a distance less than 7cm, this means that someone is using the thermometer now. As shown in the flowchart above, the MCU will first send a message through the UART port containing the value of the distance measured and then reset the value of the “StandbyTimeAfterStartup” variable. Before scanning the temperature, the buzzers will be turned ON first, producing short beep sounds to indicate that the MLX90614 will start scanning. After that, the MLX90614 will scan the temperature. When the temperature scanning is done, the buzzers will be turned ON again to indicate that the thermometer is done scanning the temperature. The MCU will then process the results and then display it on the OLED display.

After displaying the results on the OLED display, the ultrasonic sensor will measure the distance again. This is to check if there’s another person that will use the thermometer or if it will enter into low power mode if no one uses the thermometer after 30-40 seconds.

After the MCU displays the results on the OLED display, when the user moves his or her forehead away from the thermometer, he or she should still be able to check the results and the results should remain on the display until another user uses the thermometer. This should be the ideal operation of the thermometer. However, since the execution time between displaying the results and measuring the forehead distance again is so small, when the person moves his or her forehead away from the thermometer, his or her movement might give a false trigger to the thermometer. The result displayed on the OLED display might be cleared immediately even though the person has not yet checked the results or worse, the false trigger could even make the thermometer scan the temperature again. To avoid this, I have added another distance measurement task in the operation as shown in the flow chart.

This task will check if the forehead distance is still less than 7cm. If it is, then the program will not proceed to the next operation but will just keep on looping until the distance measured is no longer less than 7cm, which means that the user already moved away from the thermometer. This will give the user enough time to move his or her forehead away from the thermometer.

After the user moves his or her forehead away from the thermometer with a distance greater than 7cm, the MCU will trigger the ultrasonic sensor to measure the distance again. This is so that the MCU can decide whether it will enter into a while loop that makes the MCU enter into low power mode if the value of the “StandbyTimeAfterScan” variable reaches 2000 or if it will exit the while loop, resets the “StandbyTimeAfterScan” variable, displays “Position Forehead” on the OLED display, and then returns to the first line of the loop() function. The while loop here is just the same as the while loop we’ve discussed earlier (before temperature scanning). Their only difference is that the while loop here, when the MCU exits low power mode, the program will not return to the first line of the loop() function. Instead, it will go back to the part where the MCU displays the results on the OLED display. The reason for this is so that we can still check the previous temperature result before the MCU entered low power mode. Now, let’s check the code of the operation that we’ve just discussed.

void loop()

{

// put your main code here, to run repeatedly:

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterStartup++;

if ((distance > 1) && (distance < 7))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

Serial.print(F("\n"));

}

else{ }

if (StandbyTimeAfterStartup == 1)

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

}

else if (StandbyTimeAfterStartup == 2000) // more or less 30 seconds

{

Serial.print(F("After Startup Standby Time: "));

Serial.println(StandbyTimeAfterStartup);

LowPower_powerDown();

StandbyTimeAfterStartup = 0;

goto ReadButtonMain;

}

}

StandbyTimeAfterStartup = 0;

display.clearDisplay();

display.display();

distancedisplay = distance;

buzzer_1();

delay(50);

buzzer_2();

get_Ambient_and_Object_Temperature();

display_Ambient_and_Object_Temperature_on_OLED();

ReadButtonMain:

delay(250);

display.clearDisplay();

delay(250);

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

After initializing the hardware of the non-contact forehead IR thermometer, in line 502, the ultrasonic sensor measures the distance between the forehead and the MLX90614 sensor. If it’s less than 7cm, then the program will not execute line 510 to line 517. It will just proceed to line 535 without printing the value of the “distance” variable. However, since the execution time between line 502 and line 504 is extremely short, then most of the time the program will really enter the while loop. Now if the distance measured is less than 7cm, then the statements inside the if statement in line 510 will be executed. The value of the “distance” variable will be transmitted through the UART port, then the program will exit the while loop and proceed to line 535 to clear the value of the “StandbyTimeAfterStartup” variable.

StandbyTimeAfterStartup = 0;

display.clearDisplay();

display.display();

distancedisplay = distance;

buzzer_1();

delay(50);

buzzer_2();

get_Ambient_and_Object_Temperature();

display_Ambient_and_Object_Temperature_on_OLED();

In line 537 to line 538, the OLED display will be cleared since we have another logo to display during temperature scanning. In line 540, we store the value of the “distance” variable to the “distancedisplay” variable which we are going to use later when the MCU will display the results on the OLED display. In line 542 to line 544, this is the part where the MCU turns ON the buzzers to indicate that the temperature scanning process will start. Then in line 546, the MCU executes the get_Ambient_and_Object_Temperature() function.

void get_Ambient_and_Object_Temperature()

{

digitalWrite(LEDpin, HIGH);

Serial.print(F("Ambient"));

Serial.print(F("\t\t"));

Serial.println(F("Object"));

Serial.print(F("\n"));

for (int readTemp = 0; readTemp < 50; readTemp++)

{

// Call temp.read() to read object and ambient temperatures from the sensor.

if (temp.read()) // On success, read() will return 1, on fail 0.

{

// Use the object() and ambient() functions to grab the object and ambient

// temperatures.

// They'll be floats, calculated out to the unit you set with setUnit().

ambientTemp = temp.ambient();

objectTemp = temp.object();

Serial.print(ambientTemp);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.print(F("\t\t"));

Serial.print(objectTemp);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

ambientTempSum = ambientTempSum + ambientTemp;

objectTempSum = objectTempSum + objectTemp;

}

if (readTemp == 4)

{

display.clearDisplay(); // for Clearing the display

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 45;

for (int bar = 0; bar < 14; bar++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

else if (readTemp == 29)

{

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 32;

for (int bar = 0; bar < 14; bar++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

else if (readTemp == 49)

{

display.drawBitmap(0, 0, ScanningTemperatureLogo, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

display.display();

int progressbar = 19;

for (int progressbarloop = 0; progressbarloop < 12; progressbarloop++)

{

display.drawLine(102, progressbar, 106, progressbar, WHITE);

display.display();

delay(20);

progressbar--;

}

}

}

Serial.print(F("\n"));

Serial.println(F("Sum:"));

Serial.print(ambientTempSum);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.print(F("\t"));

Serial.print(objectTempSum);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

Serial.print(F("\n"));

ambientTempAvg = ambientTempSum/50;

Serial.println(F("Average:"));

Serial.print(ambientTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print("C");

Serial.print("\t\t");

ambientTempSum = 0;

objectTempAvg = objectTempSum/50;

Serial.print(objectTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

objectTempSum = 0;

Serial.print(F("\n"));

Serial.println(F("Rounding Off Ambient Temperature:"));

int int_ambientTempAvg = ambientTempAvg;

Serial.println(int_ambientTempAvg);

float decimal_ambientTemp = ambientTempAvg - int_ambientTempAvg;

Serial.println(decimal_ambientTemp);

float decimaltimes10_ambientTemp = decimal_ambientTemp * 10;

Serial.println(decimaltimes10_ambientTemp);

float roundOff_ambientTemp = round(decimaltimes10_ambientTemp);

Serial.println(roundOff_ambientTemp);

float backTodecimal_ambientTemp = roundOff_ambientTemp/10;

Serial.println(backTodecimal_ambientTemp);

roundOff_ambientTempAvg = int_ambientTempAvg + backTodecimal_ambientTemp;

Serial.print(roundOff_ambientTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.println(F("\n"));

Serial.println(F("Rounding Off Object Temperature:"));

int int_objectTempAvg = objectTempAvg;

Serial.println(int_objectTempAvg);

float decimal_objectTemp = objectTempAvg - int_objectTempAvg;

Serial.println(decimal_objectTemp);

float decimaltimes10_objectTemp = decimal_objectTemp * 10;

Serial.println(decimaltimes10_objectTemp);

float roundOff_objectTemp = round(decimaltimes10_objectTemp);

Serial.println(roundOff_objectTemp);

float backTodecimal_objectTemp = roundOff_objectTemp/10;

Serial.println(backTodecimal_objectTemp);

roundOff_objectTempAvg = int_objectTempAvg + backTodecimal_objectTemp;

Serial.print(roundOff_objectTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.print(F("C"));

Serial.println(F("\n"));

buzzer_1();

delay(50);

buzzer_2();

digitalWrite(LEDpin, LOW);

}

The get_Ambient_and_Object_Temperature() function starts at line 563 and ends at line 712. In line 565, we turn ON the red LED. When we discussed the flowchart earlier, we only mentioned turning ON the buzzers before temperature scanning starts. But in the code, we also turned ON the red LED to have an additional indication that the thermometer is now scanning the temperature. The serial.print code in line 567 to line 570 is just to monitor the temperature results in the Arduino IDE serial monitor.

The scanning of the temperature, displaying the ScanningTemperatureLogo, and the summing of the ambient temperatures and object temperatures happen inside the for loop in line 572 to line 640. Instead of just scanning the forehead temperature once like the other MLX90614 sample codes that we usually see, we use a for loop that scans the ambient and forehead temperature 50 times to have a better reading of the forehead temperature. We then just get the average of the temperature readings.

In line 576, the MLX90614 reads the ambient and object temperature by calling the temp.read() function. Assuming that the reading of the temperatures is successful, in line 582 and line 583, we calculate and store the ambient and object temperatures to the ambientTemp and objectTemp variables by calling the functions temp.ambient() and temp.object(), respectively. Again, the serial.print code in line 585 to line 594, is just to monitor the temperature results in the Arduino IDE serial monitor.

In line 596 and line 597, we add the value of the ambientTemp variable to the ambientTempSum variable and the value of the objectTemp to the objectTempSum variable. We are going to use the ambientTempSum and the objectTempSum variables later to get the average of the temperature readings. This scanning process will be repeated until the for loop variable “readTemp” reaches 50.

The code in line 600 to line 639 is for displaying the ScanningTemperatureLogo on the OLED display with a vertical progress bar animation. The vertical progress bar will be filled as the readTemp variable reaches 4, 29, and 49.

In line 642 to line 653, we use serial.print code again to monitor the value of the ambientTempSum and objectTempSum variables. In line 655 to line 669, we get the average of the ambient (line 655 - ambientTempAvg = ambientTempSum/50;) and object (line 663 - objectTempAvg = objectTempSum/50;) temperature readings and print them again on the Arduino IDE serial monitor using serial.print. To round off the results to one decimal place, we use the code in line 671 to line 705. In line 707 to line 709, we turn ON the buzzers again, then in line 711 turn OFF the red LED to indicate that the thermometer is done scanning the temperature. After this, the program returns to line 548 to execute the display_Ambient_and_Object_Temperature_on_OLED() function.

void display_Ambient_and_Object_Temperature_on_OLED()

{

DisplayResultsAgainAfterExitingLowPowerMode:

delay(200);

display.clearDisplay();

display.display();

delay(200);

display.clearDisplay();

delay(100);

display.setTextSize(1);

display.setCursor(0, 0);

display.print(F("A:"));

display.print(roundOff_ambientTempAvg, 1);

display.drawCircle(38, 1, 1, WHITE);

display.setCursor(41, 0);

display.print(F("C"));

display.setCursor(80, 0);

display.print(F("D:"));

display.print(distancedisplay);

display.print(F("cm"));

display.drawLine(0, 12, 128, 12, WHITE);

calibration_value = calibrate_objectTemp(roundOff_ambientTempAvg, distancedisplay);

Serial.print(F("Calibration Value: "));

Serial.println(calibration_value);

calibrated_objectTempAvg = roundOff_objectTempAvg + calibration_value;

Serial.print(F("Calibrated Object Temperature: "));

Serial.print(calibrated_objectTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

display.setTextSize(3);

display.setCursor(14, 27);

display.print(calibrated_objectTempAvg, 1);

display.drawCircle(92, 30, 3, WHITE);

display.setCursor(100, 27);

display.print(F("C"));

display.drawLine(0, 63, 128, 63, WHITE);

display.display();

display.setTextSize(1);

Serial.print(F("\n"));

HCSR04_Temperature = roundOff_ambientTempAvg;

Serial.print(F("HC-SR04 Ultrasonic Sensor Temperature: "));

Serial.print(roundOff_ambientTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance < 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(1000);

}

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterScan++;

if ((distance > 1) && (distance < 8))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

}

else{ }

if (StandbyTimeAfterScan == 1)

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

}

else if (StandbyTimeAfterScan == 2000) // more or less 30 seconds

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

LowPower_powerDown();

StandbyTimeAfterScan = 0;

goto DisplayResultsAgainAfterExitingLowPowerMode;

}

}

Serial.println(F("\n"));

StandbyTimeAfterScan = 0;

digitalWrite(LEDpin, HIGH);

display.clearDisplay();

delay(200);

digitalWrite(LEDpin, LOW);

}

The display_Ambient_and_Object_Temperature_on_OLED() function starts at line 714 and ends at line 822. In line 716, we see the label DisplayResultsAgainAfterExitingLowPowerMode. This is where the program returns after the MCU exits low power mode when the thermometer is not used for about 30-40 seconds after scanning forehead temperature. In line 717 to line 723, we clear the OLED display. In line 725 to line 731, we have the code to draw or print the ambient temperature in the upper left corner of the OLED display. In line 733 to line 736, we also have the code to draw or print the distance between the forehead and the MLX90614 in the upper right corner of the OLED display. Then in line 738, we draw a white line on the (0, 12, 128, 12) location of the OLED display.

Underneath the white line drawn on the (0, 12, 128, 12) location of the OLED display, we display the body temperature of the user using the code in line 753 to line 758. But before that, we need to calibrate first the object temperature (stored in the roundOff_objectTempAvg variable) measured by the MLX90614, because as mentioned earlier, there is a difference between the temperature that we measure in our ears and in our forehead, and also with the other parts of the body like our armpit. So in order to get an accurate or maybe a closer result to the real body temperature, we need to calibrate the object temperature (or forehead temperature) that the MLX90614 measured.

I calibrated the result by comparing the readings of the MLX90614 to the readings of a pencil type thermometer. I don’t have a tympanic thermometer, so instead I used a pencil type thermometer. I measured my forehead temperature using the MLX90614 and measured my armpit temperature using the pencil type thermometer several times. Then I calculated the average of the difference between the readings.

As you can see in line 740, we have the calibration_value = calibrate_objectTemp(roundOff_ambientTempAvg, distancedisplay); code there. This code calls the calibrate_objectTemp() function to get a calibration value and then store it on the calibration_value variable. If you notice, we have two parameters in the calibrate_objectTemp() function, roundOff_ambientTempAvg and distancedisplay. Since infrared thermometers can be affected by the changes in the ambient temperature, we included the ambient temperature reading (roundOff_ambientTempAvg) in the condition on what calibration value the MCU will select. We also include the distance (distancedisplay), since the reading of an infrared thermometer also depends on how close the infrared sensor is to the surface of the object.

float calibrate_objectTemp(float roundOff_ambientTempAvg, double distancedisplay)

{

Serial.print(F("Ambient Temperature: "));

Serial.print(roundOff_ambientTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

Serial.print(F("Distance: "));

Serial.print(distancedisplay);

Serial.println(F("cm"));

if (((roundOff_ambientTempAvg > 20) && (roundOff_ambientTempAvg < 25)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 4.2;

}

else if (((roundOff_ambientTempAvg > 25) && (roundOff_ambientTempAvg < 30)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 3.4;

}

else if (((roundOff_ambientTempAvg > 30) && (roundOff_ambientTempAvg < 32.5)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 2.9;

}

else if (((roundOff_ambientTempAvg > 32.5) && (roundOff_ambientTempAvg < 35)) && ((distancedisplay > 2) && (distancedisplay < 7)))

{

calibration_value = 2.2;

}

else

{

calibration_value = 0;

}

delay(50);

Serial.print(F("Calibration Value: "));

Serial.println(calibration_value);

return calibration_value;

}

The calibrate_objectTemp() function is at line 877. As you can see in the code above, the function consists only of serial.print codes to monitor the values of the variables on the Arduino IDE serial monitor and if/elseif/else statements to select the calibration value to return. At the end of the function, a calibration value (calibration_value) will be returned to the display_Ambient_and_Object_Temperature_on_OLED() function based on the value of the roundOff_ambientTempAvg and distancedisplay variables.

After returning the calibration value, the program will return to line 742 to print to the serial monitor the value of the calibration_value variable. Then in line 745, the value of the calibration_value variable will be added to the roundOff_objectTempAvg variable and the result will be stored to the calibrated_objectTempAvg variable. In line 747 to line 751, we print the value of the calibrated_objectTempAvg variable to the serial monitor and in line 753 to line 758, we display it on the OLED display as the body temperature of the user. Then in line 760, we draw a white line on the (0, 63, 128, 63) location of the OLED display or the bottom part.

calibration_value = calibrate_objectTemp(roundOff_ambientTempAvg, distancedisplay);

Serial.print(F("Calibration Value: "));

Serial.println(calibration_value);

calibrated_objectTempAvg = roundOff_objectTempAvg + calibration_value;

Serial.print(F("Calibrated Object Temperature: "));

Serial.print(calibrated_objectTempAvg, 1);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

display.setTextSize(3);

display.setCursor(14, 27);

display.print(calibrated_objectTempAvg, 1);

display.drawCircle(92, 30, 3, WHITE);

display.setCursor(100, 27);

display.print(F("C"));

display.drawLine(0, 63, 128, 63, WHITE);

display.display();

display.setTextSize(1);

In line 762, we use the code display.display(); because all the values that we want to display on the OLED display such as the ambient temperature, distance, body temperature, and white lines will not be displayed until we call the display() function of the SSD1306 library. Then in line 764, we revert the text size of the OLED display to 1 since we set it at 3 in line 753 to make the font size of the body temperature larger.

Serial.print(F("\n"));

HCSR04_Temperature = roundOff_ambientTempAvg;

Serial.print(F("HC-SR04 Ultrasonic Sensor Temperature: "));

Serial.print(roundOff_ambientTempAvg);

Serial.write(0xC2);

Serial.write(0xB0);

Serial.println(F("C"));

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance < 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(1000);

}

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

while ((distance < 1) || (distance > 7))

{

distance = distanceSensor.measureDistanceCm(HCSR04_Temperature);

delay(10);

StandbyTimeAfterScan++;

if ((distance > 1) && (distance < 8))

{

Serial.print(F("\n"));

Serial.print(F("Distance: "));

Serial.print(distance);

Serial.println(F("cm"));

}

else{ }

if (StandbyTimeAfterScan == 1)

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

}

else if (StandbyTimeAfterScan == 2000) // more or less 30 seconds

{

Serial.print(F("After Scan Standby Time: "));

Serial.println(StandbyTimeAfterScan);

LowPower_powerDown();

StandbyTimeAfterScan = 0;

goto DisplayResultsAgainAfterExitingLowPowerMode;

}

}

Serial.println(F("\n"));

StandbyTimeAfterScan = 0;

digitalWrite(LEDpin, HIGH);

display.clearDisplay();

delay(200);

digitalWrite(LEDpin, LOW);

}

As ambient temperature changes frequently, we need to update the value of the ultrasonic sensor temperature variable (HCSR04_Temperature) in line 766 to line 772 to have a better reading of the distance.

The code in line 774 to line 812 is just similar to the code that we discussed earlier that makes the MCU return to the first line of the loop() function or enter low power mode. Their only difference is that instead of jumping into the ReadButtonMain label (line 550) to return to the first line of the loop() function after exiting low power mode, the code here will go to the DisplayResultsAgainAfterExitingLowPowerMode label (line 716) or first line of the display_Ambient_and_Object_Temperature_on_OLED() function. The purpose of this, as mentioned earlier, is so that when the MCU exits low power mode, we can still see the previous temperature results that the thermometer scanned. If the distance measured by the ultrasonic is not less than 7cm, the program will not exit the display_Ambient_and_Object_Temperature_on_OLED() function.

So after scanning the temperature, the only way to exit the display_Ambient_and_Object_Temperature_on_OLED() function is if someone uses the thermometer again. If this happens, the program will go to line 814, reset the StandbyTimeAfterScan variable in line 816, and turn ON and OFF the LED in line 818 to line 821. After that, the program will return to the loop() function line 549, then execute line 550 to line 560 to display “Position Forehead” again on the OLED display. Then the program will execute the loop() function again.

display_Ambient_and_Object_Temperature_on_OLED();

ReadButtonMain:

delay(250);

display.clearDisplay();

delay(250);

display.setTextSize(2);

display.setTextColor(WHITE);

display.setCursor(17, 10);

display.println(F("Position"));

display.setCursor(17, 35);

display.println(F("Forehead"));

display.display();

}

So this is how we programmed the operation of the non-contact forehead IR thermometer. I hope the explanation helped you understand how the thermometer works. In the next or last part of this project/tutorial, we are going to discuss how to program the MCU. We are also going to show the assembled non-contact forehead IR thermometer and show how it works.

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?