reginald looAden
Published © GPL3+

SMART Temperature Monitoring for Schools

Automated temperature taking and attendance logging for students.

BeginnerFull instructions provided2 days3,251

Things used in this project

Hardware components

SiPy
Pycom SiPy
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Arduino Nano R3
Arduino Nano R3
×1
SparkFun Logic Level Converter - Bi-Directional
SparkFun Logic Level Converter - Bi-Directional
×1
NFC RFID-RC522
×1
Sharp GP2Y0A41SK0F (4cm - 30cm) - Analog distance sensor
×1
MLX90614-DCI I2C - Temperature sensor
×1
I2C 16x2 Arduino LCD Display Module
DFRobot I2C 16x2 Arduino LCD Display Module
×1
LED (generic)
LED (generic)
×1
Resistor 330 ohm
Resistor 330 ohm
×2
Buzzer
Buzzer
×1

Software apps and online services

Arduino IDE
Arduino IDE
Visual Studio Code (Pymakr)
ThingSpeak API
ThingSpeak API
IFTTT
Putty
IDLE IDE

Story

Read more

Schematics

Overall Connections

How everything is connected together

Distance Sensor Connections

Connections for Distance Sensor for Arduino

IR Temperature Sensor Connections

Connections for Temperature Sensor

LED

Connections for LED for Arduino

RFID

Connections for RFID for Arduino

LCD

Connections for LCD for Arduino

Buzzer Connections

Connections for Buzzer for Arduino

Code

[C] ARDUINO NANO

Arduino
This code is used for checking if there's a RFID card tap, if there is, there will be temperature taking and also handshaking between the Raspberry Pi to send data over like the card's ID, temperature and ready/standby signal and more.

It also uses LCD to show informative information.

If you're interested in knowing more, please read the section on "HOW IT WORKS: RaspberryPi & Arduino"
/*
90% is coded from scratch by Aden,
http://www.astero.me

the other 10% consists of open sourced libraries(Credits found below) used, like the RFID & temperature module.

*/

/*
 * Initial Author: ryand1011 (https://github.com/ryand1011)
 *
 * Reads data written by a program such as "rfid_write_personal_data.ino"
 *
 * See: https://github.com/miguelbalboa/rfid/tree/master/examples/rfid_write_personal_data
 *
 * Uses MIFARE RFID card using RFID-RC522 reader
 * Uses MFRC522 - Library
 * -----------------------------------------------------------------------------------------
 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
 *             Reader/PCD   Uno/101       Mega      Nano v3    Leonardo/Micro   Pro Micro
 * Signal      Pin          Pin           Pin       Pin        Pin              Pin
 * -----------------------------------------------------------------------------------------
 * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
 * SPI SS      SDA(SS)      10            53        D10        10               10
 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
*/
/***************** 
  This is a library example for the MLX90614 Temp Sensor
  Designed specifically to work with the MLX90614 sensors in the
  adafruit shop
  ----> https://www.adafruit.com/products/1748
  ----> https://www.adafruit.com/products/1749
  These sensors use I2C to communicate, 2 pins are required to  
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!
  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ******************/
 
// INCLUDE NECESSARY HEADER FILES
 
#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <Adafruit_MLX90614.h>


// DEFINITION OF PINS

#define RST_PIN         9           // Configurable, see typical pin layout above
#define SS_PIN          10          // Configurable, see typical pin layout above
#define BUZZER_PIN      7           //Pin define for Buzzer
#define LED_PIN         6           //Pin define for LED 
#define dist_sensePin A0

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.

LiquidCrystal_I2C lcd(0x27,16,2);   // Calls the LiquidCrystal class and creats an instance for the LCD.
// set the LCD address to 0x27 with 16 chars and 2 line display

Adafruit_MLX90614 mlx = Adafruit_MLX90614();  // Calls the Adafruit MLX90614 instance.
SoftwareSerial unoSerial(2, 3); // RX, TX	  // Calls the SoftwareSerial instance (for Pycom communication)


// GLOBAL VARIABLES

boolean tapped = true; 
boolean confirmation = false;

String GetUID(){
  String card_UID= "";
  String card_UID_no_space = "";
   for (byte i = 0; i < mfrc522.uid.size; i++) 
  {
     card_UID.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
     card_UID.concat(String(mfrc522.uid.uidByte[i], HEX));
  }
  
  card_UID_no_space.concat(card_UID.charAt(1));
  card_UID_no_space.concat(card_UID.charAt(2));
  card_UID_no_space.concat(card_UID.charAt(4));
  card_UID_no_space.concat(card_UID.charAt(5));
  card_UID_no_space.concat(card_UID.charAt(7));
  card_UID_no_space.concat(card_UID.charAt(8));
  card_UID_no_space.concat(card_UID.charAt(10));
  card_UID_no_space.concat(card_UID.charAt(11));  
  //Serial.println(card_UID_no_space);  
  return card_UID_no_space;
}

void setup() {
  Serial.begin(9600);                                           // Initialize serial communications with the PC
  
  

  while(!confirmation)
  {
    Serial.println("ARDUINO");
    confirmation = Serial.readString() == "RAPI";
    delay(1000);
  }
  
  SPI.begin();                                                  // Init SPI bus
  mfrc522.PCD_Init();                                           // Init MFRC522 card
 
  mlx.begin();  												// Initiates Adafruit MLX90614
  unoSerial.begin(9600);										// Initiates SoftwareSerrial with 9600 baud rate.


  // Setting which pins are supposed to be outputs.
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  
  lcd.init();													// Initiates the LCD screen.
  lcd.backlight();												// Turns on the backlight for the LCD.

}

void loop() {
  // put your main code here, to run repeatedly:

  boolean readySignal = Serial.readString() == "READY";			// Creates a boolean that checks if the "ready signal" is received.


   
 
  if(readySignal) // If it's received, 
  {
    tapped = true; // Set tapped to true.
    Serial.println("RECEIVED"); // And return "RECEIVED" through Serial Communication.
  

  }


  if(tapped) // If it's ready to be tapped, 
  {
	
    digitalWrite(BUZZER_PIN, 0);
    digitalWrite(LED_PIN, 0);
    
    lcd.clear();
  	// Clear the Buzzer, LED & LCD.
  
    lcd.setCursor(0,0);
    lcd.print("   Please tap");
    lcd.setCursor(0,1);
    lcd.print("   your card.");
	
	// And later set a message on the LCD screen to prompt the user to tap the card.


    delay(500); // Delay for 500 ms.
    tapped = false; // Set the tapped boolean back to false so no taps can be done in this phase.
  }
  else
  {
    String card_UID= "";
  
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
      return;
    }
  
    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
      return;
    }
  
    card_UID = GetUID();
    card_UID.toUpperCase();
    Serial.print(card_UID);
    Serial.print("\n");
  
    
    String rawData = Serial.readString(); // Read the incoming String and puts it into a String variable.
    int commaPos = rawData.indexOf(','); // Finds the position of the comma in the string read above.
  
    String userInitial = rawData.substring(0, commaPos); // Sub strings the rawData string so it only reads any words that is before the comma's position.
    int userID = rawData.substring(commaPos + 1).toInt(); // After wards, save what's behind the comma, which is supposed to be an integer.
   
  
    lcd.clear(); // Clear the LCD screen.
  
    if(userID != 0 && userInitial != "0") // Check if it's actually a valid user tapping (validated by the RaspberryPi.)
    {
      
      lcd.setCursor(0,0);
      lcd.print("Welcome, ");
      
      lcd.setCursor(0,1);
      lcd.print(userInitial);

	
	  // And later set a message, welcoming the user.
	
      String sMeasTemp = "";
  
      int dist_senseValue = analogRead(dist_sensePin); // Checks the analog value of the distance module.
      byte temp_Hbyte = 0; // Creates a byte variable that initialises to 0.
      byte temp_Lbyte = 0; // Creates a byte variable that initialises to 0.
   
      float meas_temp = 0.0; // Creates a float variable that initialises to 0.
	  

      delay(2000); // Delay for 2 seconds.

      while((dist_senseValue < 170) || (dist_senseValue > 200)) // Check if there is actually a valid user infront of the machine. If no, it'll keep looping what's under.
      { 
        
        dist_senseValue = analogRead(dist_sensePin); // Keep checking until there's a person that comes in the distance sensor's range.

        lcd.setCursor(0,0);
        lcd.print("Please take");
        lcd.setCursor(0,1);
        lcd.print("your temperature.");
		
		// Prompt the user to take their temperature by standing infront of the machine.
        
      }

      
      digitalWrite(BUZZER_PIN, 1);
      digitalWrite(LED_PIN, 1);

      lcd.clear();
	  // Turns on the Buzzer and LED, at the same time, clear the LCD too.

      
      meas_temp = (float)mlx.readObjectTempC();
  
      temp_Hbyte = floor(meas_temp);
      temp_Lbyte = (meas_temp - temp_Hbyte) * 100;
      
      sMeasTemp += userID; 
      sMeasTemp += ",";
      sMeasTemp += temp_Hbyte;
      sMeasTemp += ",";
      sMeasTemp += temp_Lbyte;
	  
	  // Collect the temperature data that is taken.
	  
      Serial.println("TEMP");
      Serial.println(meas_temp); // Transfer the temperature data through Serial Communication to the RaspberryPi.
	  
	 

      if((meas_temp >= 28) && (meas_temp <=42)) // Check if it's a valid temperature.
        unoSerial.print(sMeasTemp); // If it is, send to the sigfox module for cloud storage.

      lcd.setCursor(0,0);
      lcd.print("Temperature: ");
      lcd.setCursor(0,1);
      lcd.print("   " + String(meas_temp));
	  
	  // Shows the temperature of the user through the LCD screen.
    }
    else // If it's an invalid user,
    {
      lcd.setCursor(0,0);
      lcd.print("Error!");
      lcd.setCursor(0,1);
      lcd.print("Invalid User.");
	  
	  // Tells the user that he's not a valid user and he is unable to proceed to the temperature taking phase.
	  
	  
    }

    rawData = Serial.readString();
    commaPos = rawData.indexOf(',');
    userInitial = rawData.substring(0, commaPos);
	
	// Above reads the incoming rawData again and converts into a userInitial variable.
    
    if(userInitial == "0") // If the user initial sent back is 0, it means that it's an invalid temperature because the RaspberryPi is unable to process it.
    {
      lcd.clear();

      
      lcd.setCursor(0,0);
      lcd.print("Error!");
      lcd.setCursor(0,1);
      lcd.print("Invalid Temperature.");
	  
	  // Prompts the user it's an invalid temperature range.
    }
  
   Serial.println("STANDBY");
   // Sends Serial that the Arduino is still processing data. 
      
    delay(1000); // Delay for 1 second.
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
  }
 }

[PYTHON] RaspberryPi

Python
This code is mainly used for updating the MySQL database and also to handshake with the Arduino via Serial Communication. It plays a vital role with the Arduino in this project

If you're interested in knowing more, please read the section on "HOW IT WORKS: RaspberryPi & Arduino".
'''

Coded from scratch by Aden; for the Hackster.io Sigfox competition.

http://www.astero.me

'''

# IMPORTS
import mysql.connector
import serial
import os
import time

# METHODS
def queryConnection(): 
  cursor = conn.cursor(buffered=True)
  cursor.execute("USE `" + databaseName + "`") # Use the userInfo database,
  
  return cursor
  
# DATABASE INFORMATION
host = "localhost"
user = "root"
password = "password"

databaseName = "smartTemp"
conn = mysql.connector.connect(host=host, user=user,passwd=password)



# Serial Communication
comPort = 'ttyUSB0'
baudRate = 9600
ser = serial.Serial('/dev/' + comPort, baudRate, timeout=0) # Set the timeout as 0 so if it doesn't read a serial, it skips.

print("Confirming USB Serial Port now, please wait")
  
connected = False


# Below, is to ensure that the RaPi receives the correct COM port of the Arduino board.
for x in range(2):
  time.sleep(2)
  ser.write("RAPI") # Basically, here, we're writing to the Arduino to confirm communication.
  
  if connected == False:
    serialConfirmation = ser.readline().strip() == "ARDUINO" # Check if the RaPi is reading the correct COM port, whether it can read the Arduino's serial communications.
  print(serialConfirmation)
  
  
  
  
  if(serialConfirmation == False):
    comPort = 'ttyUSB1'
    connected = True
    
  else:
    comPort = 'ttyUSB0'
    connected = True
  
  print(comPort)
  
  ser = serial.Serial('/dev/' + comPort, baudRate) # Re-initiates this variable again, this time without a Serial Communication timeout. Meaning, it'll try to communicate with the Arduino w/o a timeout.


  
print("COM PORT confirmed, using " + comPort)  # Print to let you know which PORT the Arduino is on.




# File Creation
countFile = open("count.txt", "a++") # Creates a count file if it doesn't exist.

# GLOBAL VARIABLES
tapRFID = False

cursor = queryConnection()



if(os.stat("count.txt").st_size == 0): # Checks if the count.txt is empty.
  print("Count has not been set, setting to 1 now.")
  count = 1
else:  
  for countNumber in countFile:
    count = int(countNumber) # Set the count to the last counted count.
    
    
    print("Count has been restored from previous session: " + str(count))  


try: # Tries to create the database if it doesn't already exists.
    print("Creating " + databaseName + " database now..")
    cursor.execute("CREATE DATABASE " + databaseName) # Create a database.
    print("Database has been sucessfully created.")
    
except(mysql.connector.errors.DatabaseError): # If the database already exists, it excepts here.
    print(databaseName + " database is already created.") # If that database is found, run this block of code instead.


cursor.execute("CREATE TABLE IF NOT EXISTS `userInfo` (id int, cardID VARCHAR(9), userID VARCHAR(4), userInitial VARCHAR(8))")    # Create a userInfo table if it doesn't exist.
cursor.execute("CREATE TABLE IF NOT EXISTS `tempData` (id int, userID VARCHAR(4), dateMeas date, timeMeas time, tempMeasure decimal(5,2))") # Create a tempData table if it doesn't exist.


while True: # Endless loop.

  cursor = queryConnection() # Re-establishes the query connection using the queryConnection() method.
  cardID = str(ser.readline().strip()) # Reads the serial line and converts it to a String, stripping any empty spaces.
  
  cursor.execute("SELECT * FROM userInfo WHERE cardID = '" + cardID + "'") # Select the userInfo table with the inputted cardID from the Arduino.
  
  
  items = cursor.fetchone() # Fetch one line of the table.
 
  
  if(items != None): # If it's able to fetch (valid user)
    print(cardID) # Print out the cardID that it's currently fetching.
    
    
    
    userID = str(items[2]) # Gets the userID from the fetched line. 
    userDetails = str(items[3] + ", " + items[2]) # Get the userDetails from the fetched line.
    
    
    # Get the currentDate & currentTime.
    
    currentDate = str(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))[0:10]
    currentTime = str(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))[11:]
    
    print(items) # Print the whole item array that was fetched. (for debugging)
    ser.write(b'' + userDetails) # Serial communication to the Arudino. 
    
    countFile.close() # Closes the count file.
    countFile = open("count.txt", 'w') # Makes it so it overwrites the count file with the new saved count below.
    
    tempSignal = False
    
    while(tempSignal == False): # Keeps looping until it gets the temperature reading from the Arduino.
      print("Getting Temperature")
      tempSignal = ser.readline().strip() == "TEMP" # Read the Arudino incoming serial.
      tempData = float(ser.readline().strip()) # Read the Arduino incoming serial of the temperature data.
      
      
      
      
    print(tempData) # Prints the retrieved temperature data from the Arduino.
    
    if(tempData >= 28 and tempData <= 42): # Check if the temperature is of a valid range.
      cursor.execute("INSERT INTO `tempData`(`id`, `userID`, `dateMeas`, `timeMeas`, `tempMeasure`) VALUES (" + str(count) + ",'" + userID + "','" + currentDate + "','" + currentTime + "','" + str(tempData) + "')") # Write into the database the necessary information.
      conn.commit() # Commit the INSERT changes into the database.
      
      count = count + 1 # Ups the count.
    
      countFile.write(str(count)) # Update the count file with the new count.
    
    else: # If it's not a valid temperature.
      ser.write(b'' + "0, 0")  # Serial Communication to the Arduino informing that it's invalid temperature. 

    
    

      

  
 
  else: # If it's not a valid user.
    ser.write(b'' + ", 0") # Serial Communication to the Arduino informing that no valid cardID is found.
    print("Nothing found")
      
  readySignal = ser.readline().strip() == "STANDBY" # Waits for the Arduino to tell that it's ready to do it again, to read/validate data.
  
  while(readySignal == True): # If the Arduino is ready, loop until we're able to tell him back that we're also ready for another session.
    ser.write(b'' + "READY") # Serial communication basically to say we're ready too.
    print("sending")
    
    receivedSignal = ser.readline().strip() == "RECEIVED" 
    
    if(receivedSignal == True):
      readySignal = False
      print("received")    
	  

  cursor.close() # Close the query connection to prevent memory leaks.
     

conn.close() # Closes the connection to the MySQL.

[MicroPython] Sigfox (Pycom)

Python
This code is mainly used to read the serial communication from the Arduino and upload the received values to the Sigfox backend

If you're interested in knowing more, please read the section on "HOW IT WORKS: Sigfox (Pycom)"
'''
100% is coded by Reginald,


'''
from machine import UART # Tx and Rx (``P3`` and ``P4``)
from network import Sigfox # Import the sigfox library from the pycom controller
import binascii # Import the Binary to ASCII library for converting from Binary (1,0) to ASCII (readable characters)
import socket # Import socket module to enable socket connection for Sigfox
import time # Import time that can be used for delays
import pycom # Import pycom module that contains fucntions that controls the pycom device
import sys # Import sys module to enable exit from the pycom running program
pycom.heartbeat(False)

#init Sigfox for RCZ4 (Asia)
sigfox = Sigfox(mode=Sigfox.SIGFOX,rcz=Sigfox.RCZ4)

uart1 = UART(1, baudrate=9600) # To set the serial communcation parameters
uart1.init(9600,bits=8,parity=None, stop=1, pins = ('P3', 'P4'), timeout_chars=20) # To set the serial communcation parameters


while True:
    try:
        recv=uart1.readline() # It always reads the serial communcation for any messages 
        
        if recv != None:    '''If there is a message recieved from the serial communcation,
                            it will procced to establish the sigfox connection and process the message for sending'''
              
            #print Sigfox DeviceID
            print(binascii.hexlify(sigfox.id()))

            # print Sigfox PAC number 
            print(binascii.hexlify(sigfox.pac()))

            #create Sigfox socket
            sk1 = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW)

            #make thesocket blocking
            sk1.setblocking(False)

            #configure as uplink only
            sk1.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False)

            dataList = recv.decode("utf-8")      #decode the received message
            print("dataList : %s" %(dataList))
            split_val = dataList.split(",")      #split the listing based on commas
			
			# eg. if receive message is 8,35,60  ->   userID = 8,  temperature = 35.60 degree celsius
			
			userID = int(split_val[0])           # assign the 1st element in the listing to userID.
            temp_H = int(split_val[1])           # assign the 2nd element in the listing to the whole number of the temperature.
            temp_L = int(split_val[2])           # assign the 3rd element in the listing to the decimal numner of the temperature.
            print("userID : %d temp_H : %d temp_L : %d" % (userID, temp_H, temp_L))
            bData = [userID,temp_H,temp_L]       # create a list
            print(bData)

            meas_temp = temp_H + (temp_L*0.01)   # merge temperature values
            print("measure temperature : %.2f" %(meas_temp))  
				
		    sk1.send(bytes(bData))               #cast the data list to bytes and send to Sigfox backend. 
            sk1.close()                          #close Sigfox socket connection.
            time.sleep(5)                        #delay for 5 seconds.
 
    except KeyboardInterrupt:
        sys.exit()  

[ThingSpeak] MATLAB SCRIPT

MATLAB
This script focuses on combining the raw data that is sent up by the Sigfox to ThingSpeak and put them together into a presentable visual state, allowing users to properly visualize the information. It also works alongside with IFTTT by triggering a webhook URL when it reaches a fever temperature, alerting user(s) via Email.

If you're interested in learning more about this code, please read the section on "HOW IT WORKS: ThingSpeak".
%% Made from scratch by Aden; for the Hackster.io Sigfox Competition.
%% http://www.astero.me

readChannelID = 870479; %% Channel to read.

webhookTrigger = 'https://maker.ifttt.com/trigger/student_fever/with/key/h10MdSGwjSPtZQ43wH-AgoiKI0pwaljBNnGUEu4Yecn';

readAPIKey = 'QGHINBPNJQKULBH2'; %% Channel read API Key.


writeChannelID = 870482; %% Channel to write.

writeAPIKey = 'R6NJIM8NT5A42R9N'; %% Channel write API Key.


wholeNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 1); %% Read the value from field 1 and save it to a variable.

decimalNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 2)/100;
%% Read the value from field 2 and save it to a variable.

userID = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 3);
%% Read the value from field 3 and save it to a variable.

display(wholeNumber, 'BEFORE') %% Display value for debugging.


analyzedData = wholeNumber + decimalNumber; %% Converting the two into one whole number instead of two separate values.


display(analyzedData, 'AFTER')


display(userID, 'USERID')


%%thingSpeakWrite(writeChannelID, analyzedData, 'Fields', 1, 'WriteKey', writeAPIKey);
thingSpeakWrite(writeChannelID,'Fields',[1,2],'Values',{analyzedData,userID},'WriteKey', writeAPIKey)

if(analyzedData >= 38) %% Check if fever temperature.
    webwrite(webhookTrigger,'value1',analyzedData,'value2',userID);     %% If yes, trigger the webhook and send an email of the values.
end

Credits

reginald loo

reginald loo

2 projects • 1 follower
Aden

Aden

1 project • 1 follower

Comments