glennedi
Published © GPL3+

Rate of Perceived Exertion Logger

Record your exertions on the Borg scale.

BeginnerFull instructions provided2,182
Rate of Perceived Exertion Logger

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Deek Robot data logging shield v1.0
×1
LCD shield with analog keypad
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

RPE logger upgrade code file

C/C++
//RPE logger code file

// Updated December 2021 - Remove bug which gave extra writes when on rpe 20 and pressed increment and on rpe 6 when pressed decrement
//  "Somehwat hard" altered to "Somewhat hard"

//CHECK IF LCD SHIELD USES DIGITAL PIN 10 AS THIS IS USED AS CS FOR DATA LOGGING SHIELD
//CHECK TO SEE IF LCD IS ONE OF THE FAULTY ONES
//REFERENCE - https://forum.arduino.cc/index.php?topic=96747.0 (Retrieved 7/June/2019)

/*
 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 10 for deek robot data logging shield v1.0
 * 
 * RTC connections (I2C):
 * gnd to gnd
 * + to +5V
 * SDA to A4
 * SCL to A5
 * 
 * LCD shield:
 * Backlight - pin 10 removed
 * B4 to D4
 * B5 to D5
 * B6 to D6
 * B7 to D7 
 * RS to D8
 * E to D9
 * 
 * LCD shield buttons:
 * Analog 0
 * 
 */

//#define my_test  //uncomment this line to send data to serial console instead of SD card
 
#include <SPI.h>
#include <SD.h>

const int chipSelect = 10;//CS for deek robot data logging shield v1.0

//for real time clock
#include "RTClib.h"

RTC_DS3231 rtc;

//for LCD shield
#include <LiquidCrystal.h>

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// these have been checked with http://www.robotshop.com/content/PDF/dfrobot-lcd-keypad-shield-schematic.pdf


File myFile;

//required to debounce switches
#define MAX_CHECKS 10
volatile uint8_t Debounced_State=0;//accessed by isr and main loop code
uint8_t State[MAX_CHECKS]={0};
uint8_t Index=0;


//rpe
const int rpe_max=20;
const int rpe_min=6;


//where we hold the filename
const String my_filename="data.txt";

void setup()
{

    lcd.begin(16, 2); // start the library
    lcd.setCursor(0,0);

    pinMode(A0,INPUT);//Analog ladder for buttons
          
    // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 625;              // compare match register  16MHX/256/100HZ 
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();


  if (!SD.begin(chipSelect)) {
    lcd.print("Not Initialized");
    delay(5000);
    return;
  }


  if (!SD.exists(my_filename)) {
    lcd.print("No File");;
    delay(5000); 
  }

  lcd.setCursor(4,0);
  lcd.print("Stopped");
  lcd.setCursor(4,1);
  lcd.print("RPE ");
  lcd.print(rpe_min);

#ifdef my_test
  Serial.begin(9600);
  #endif
 
}


void loop()
{
//variables
static bool started=false;
static bool last_entry=false;
static bool do_update=false;
static bool button_up=false;
static bool button_stored=false;
static int Stored_State=0;
static int Decode_Me=0;
static int rpe_count=rpe_min;

    noInterrupts();           // disable all interrupts    
if (Debounced_State>Stored_State){Stored_State=Debounced_State;button_stored=true;}//store and flag as ready
if (Debounced_State<Stored_State){if(button_stored){button_up=true;Decode_Me=Stored_State;}Stored_State=Debounced_State;}//button has been released    
    interrupts();             // enable all interrupts

if(button_up && button_stored)
{
button_stored=false;
button_up=false;
do_update=true;

   if (Decode_Me==1){if(started){rpe_count--;if(rpe_count<rpe_min){rpe_count=rpe_min;do_update=false;}else{do_update=true;} }}//decrement rpe   
   if (Decode_Me==2){if(started){rpe_count++;if(rpe_count>rpe_max){rpe_count=rpe_max;do_update=false;}else{do_update=true;} }}//increment rpe
   if (Decode_Me==4){if(started){started=false;last_entry=true;}else{started=true;}rpe_count=rpe_min;do_update=true;}//start stop

}

  if (do_update){
    do_update=false;

  if(!started){lcd.setCursor(0,0);lcd.print  ("    Stopped         ");}
  if(rpe_count==6 && started){lcd.setCursor(0,0);lcd.print       ("  No exertion    ");}
  if(rpe_count>=7 && rpe_count < 9){lcd.setCursor(0,0);lcd.print (" Extremely light ");}
  if(rpe_count>=9 && rpe_count < 11){lcd.setCursor(0,0);lcd.print ("  Very light    ");}
  if(rpe_count>=11 && rpe_count < 13){lcd.setCursor(0,0);lcd.print("     Light     ");}
  if(rpe_count>=13 && rpe_count < 15){lcd.setCursor(0,0);lcd.print("  Somewhat hard ");}
  if(rpe_count>=15 && rpe_count < 17){lcd.setCursor(0,0);lcd.print("     Hard       ");}
  if(rpe_count>=17 && rpe_count < 19){lcd.setCursor(0,0);lcd.print("   Very hard    ");}
  if(rpe_count>=19 && rpe_count < 20){lcd.setCursor(0,0);lcd.print(" Extremely hard ");}
  if(rpe_count==20){lcd.setCursor(0,0);lcd.print("Maximum exertion");}

  lcd.setCursor(8,1);  
  lcd.print(rpe_count);
  if(rpe_count<10){lcd.print(" ");}

//In test mode we write to the serial console when not in test mode we write to the SD card.
#ifdef my_test  
  if(started){
  Serial.print(get_time());
  Serial.print('-');
  Serial.println(rpe_count);}
  else
  {if(last_entry){
  last_entry=false;
  Serial.print(get_time());
  Serial.print('-');
  Serial.println(0);}}
  #else
  if(started){append_file(my_filename,get_time(),rpe_count);}else{if(last_entry){last_entry=false;append_file(my_filename,get_time(),0);}}
  #endif
  
  
  }

}

//my_functions

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
//
//read buttons
  uint8_t temp=0x00;

  
 if (analogRead(0) > 60 && analogRead(0) < 200){temp|=1<<1;} //btnUP
 if (analogRead(0) > 200 && analogRead(0) < 400){temp|=1<<0;} //btnDOWN  
 if (analogRead(0) > 600 && analogRead(0) < 800){temp|=1<<2;} //btnSELECT
 
  
//debounce
  uint8_t i,j;

//for right hand
  State[Index]= temp;
  ++Index;
  j=0xFF;
  for (i=0;i<MAX_CHECKS;i++){j=j&State[i];}
  Debounced_State=j;
  if(Index>=MAX_CHECKS){Index=0;} 

}


//function to get time from clock and return it as a string
String get_time(void){
//check rtc available
  if (! rtc.begin()) {
    lcd.print("No RTC");
    delay(5000);
  }

  String dataString = "";
 
     DateTime now = rtc.now();
     
    dataString += String(now.year());
    dataString += ",";
    dataString += String(now.month());
    dataString += ",";
    dataString += String(now.day());
    dataString += ",";
    dataString += String(now.hour());
    dataString += ",";
    dataString += String(now.minute());
    dataString += ",";
    dataString += String(now.second());

    return dataString;
    }
    
//function to append the rpe value and time to the data file
void append_file(String filename,String dataString,int rpe)
{
   
//append rate perceived exertion value to time dataString    
    dataString += ',';
    dataString += rpe;
    dataString += ',';
    

//write to file

  myFile = SD.open(filename, FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    myFile.println(dataString);
    // close the file:
    myFile.close();
  } else {
    lcd.print("Can't open file");//error file didn't open when writing data
    delay(5000);
  }

return;
}
//End

Credits

glennedi
5 projects • 23 followers

Comments