Project tutorial
GymGeneral Retooled

GymGeneral Retooled © LGPL

Almost no wiring!

  • 877 views
  • 0 comments
  • 0 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
Deek Robot data logging shield v1.0
×1
LCD Keypad shield
×1
Piezo sounder element
I used Murata PKM13EPYH4002
×1
0.1 Inch Right angle pins
×2
0.1 Inch Jumper
×1

Apps and online services

About this project

Story:

I had bought myself an LCD shield with keypad and while I was doing some projects with it I took the opportunity to redo my GymGeneral sport timer [1] (see that article for the full details of the project).

My original idea was to build a gym interval timer featuring easy routine set up via text files.

Hardware description:

The hardware consists of an Arduino UNO, a Deek robot data logging shield v1.0 and an LCD shield with buttons.

The UNO, datalogging shield and LCD shield are layered together like a cake.

I have removed pin 10 from my LCD shield this pin is used for the LCD backlight but has been implemented incorrectly on some shields [2], such as the one I bought, this is no loss in the context of this project as the datalogging shield uses this pin as chip select.

I soldered the piezo sounder to the logging shield using the spare contact pads provided adding in a couple of pins for a jumper so that it could be disconnected when I use the shield in other projects.

The connections for the sounder are:

+ To Digital pin3

GND to GND via2*0.1 inch pins with jumper

All other connections are made by plugging the shields into each other.

(See Schematics section below for the wiring diagram and photo's)

Software description:

The software is almost identical to that in my original project the only change was to alter the button code to take input from the LCD shield analog buttons.

Libraries used:

SPI

SD

LiquidCrystal

Functioning:

Prepare one or more routines in text file format and copy them to the SD card.

The format for the text file is:

<Repeats>, <Tone>, <On time in tenth seconds>, <Off time in tenth seconds>,

Tone uses 1 for low frequency and 2 for the high frequency.

Example:

20, 2, 1, 9, = 20repeats of high frequency, on for 1 tenth second, off for 9 tenths second.

Turn on the device, cycle through the routines using the down button, once you have found the routine you want press select to start.

Note that there is no stop option or error trapping!

References:

[1] Link to original project https://create.arduino.cc/projecthub/Mr_Glenn/gymgeneral-a-sports-interval-timer-7a63f8?ref=search&ref_id=Mr_Glenn&offset=2 (Checked 29/March/2020)

[2] LCD shield errata link https://forum.arduino.cc/index.php?topic=96747.0 (Checked 29/March/2020)

Code

GymGeneral software retooled for LCD shield with buttonsC/C++
//Sports interval timer using SD card to hold intervals data

//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 27/March/2020)

/*
 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) - Clock not used in this project:
 * 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
 * 
 * Buzzer 
 * GND to GND
 * + to D3 
 */
 
#include <SPI.h>
#include <SD.h>

const int chipSelect=10;

//my_aliases
const int start_screen=1;
const int initialization_screen=2;
const int file_menu_screen=3;
const int file_wont_open_screen=4;

//my_boolean variables
volatile bool count_updated=true;
volatile bool enter_pressed=false;
volatile bool beep_file_contents=false;
volatile bool do_update=true;

File root;
File myFile;

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

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

//required to debounce switch
#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;

//my_global_variables
int screen_number=start_screen;
bool initialization_fail=false;
bool file_exists=false;
static String my_filename="";

const int sounder_pin=3;// the pin we attach our sounder to
const int duty_cycle=128;//50% duty cycle for analogWrite()

void setup() {

//initial lcd setup
  lcd.begin(16,2);    // initialize the lcd for 16 chars 2 lines    
  lcd.clear();

  update_display();
  delay(5000);
  screen_number=initialization_screen;

//try to initialize sd card
  if (!SD.begin(chipSelect)) {
    initialization_fail=true;
    update_display();
    return;
  }
  initialization_fail=false;
  update_display();
  delay(5000);
  
  
  // initialize digital pin as an output.
  pinMode(sounder_pin, OUTPUT);


      // 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();


}/*end of setup*/

void loop() {
  
  root = SD.open("/");

  int repeats=0;
  int frequency=0;
  int on_time=0;
  int off_time=0;
  int index=0;
 
while(1){

static bool button_up=false;
static bool chord_ready=false;
static int Stored_State=0;
static int Action_Me=0;

    noInterrupts();           // disable all interrupts    
if (Debounced_State>Stored_State){Stored_State=Debounced_State;chord_ready=true;}//store chord and flag as ready
if (Debounced_State<Stored_State){if(chord_ready){button_up=true;Action_Me=Stored_State;}Stored_State=Debounced_State;} 
    interrupts();             // enable all interrupts

if(button_up && chord_ready)
{
chord_ready=false;
button_up=false;

if (Action_Me&1<<1){   switch(screen_number){
                          case file_menu_screen:beep_file_contents=true;break;
                          case file_wont_open_screen:screen_number=file_menu_screen;do_update=true;break;}}//select button
                          
if (Action_Me&1<<0){if (screen_number==file_menu_screen){count_updated=true;}}//down button

}

  if (do_update){do_update=false;update_display();}
  
  if (count_updated){


  //cycle through the existing files on the SD card
  count_updated=false;

  File entry =  root.openNextFile();
    if (! entry) {
      // no more files -> so go back to beginning
      entry.close();
      root.rewindDirectory();
      my_filename="No more";
      update_display();   
    }else
    {
    my_filename=entry.name(); 
    screen_number=file_menu_screen;
    update_display();
    entry.close();}                   
                    }

//beep out the intervals from the chosed file
if (beep_file_contents){
  
  beep_file_contents=false;

  if (SD.exists(my_filename)) {file_exists=true;}else{file_exists=false;}
  
  update_display();

  //read data from file
  // re-open the file for reading:
  myFile = SD.open(my_filename, FILE_READ);
  if (myFile) {

    // read from the file until there's nothing else in it:
    while (myFile.available()) 
    {
      repeats=myFile.parseInt();
      frequency=myFile.parseInt();
      on_time=myFile.parseInt();
      off_time=myFile.parseInt();

      for (index=0;index<repeats;index++)
      {
        if (frequency==1){TCCR2B = (TCCR2B & 0b11111000) | 0x04;}else{TCCR2B = (TCCR2B & 0b11111000) | 0x02;}

        analogWrite(sounder_pin, duty_cycle);
        delay(on_time*100);
        analogWrite(sounder_pin,0);
        delay(off_time*100);
        }     
    }

    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    screen_number=file_wont_open_screen;
    update_display();
  }

}//end beep out file
                    
}
  
}/*end of loop*/

//my_functions
ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{

//read buttons
  uint8_t temp=0x00;
  
 if (analogRead(0) > 200 && analogRead(0) < 400){temp|=1<<0;} //btnDOWN  
 if (analogRead(0) > 600 && analogRead(0) < 800){temp|=1<<1;} //btnSELECT
 
//debounce
  uint8_t i,j;
  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;} 
}

//my_screen_functions
void update_display(void)
{
  switch (screen_number)
  {
    case start_screen:lcd.clear();
                      lcd.setCursor(3,0);
                      lcd.print("GymGeneral");
                      lcd.setCursor(3,1);
                      lcd.print("Gym timer");
                      break;    
    case initialization_screen:lcd.clear();
                               lcd.setCursor(0,0);
                               lcd.print("Init SD...");
                               if (!initialization_fail){lcd.print("done.");}else{lcd.print("fail!");}
                               break;
    case file_menu_screen:lcd.clear();
                          lcd.setCursor(2,0);
                          lcd.print("Select file");
                          lcd.setCursor(2,1);
                          lcd.print("            ");//clear any previous filename (8.3 character format, so 12 spaces)
                          lcd.setCursor(2,1);
                          lcd.print(my_filename);
                          break;                        
    case file_wont_open_screen:lcd.clear();
                               lcd.print("Can't open file");
                               break;          
    }
  }

Schematics

Piezo sounder connections
Buzzer for gymgeneral retooled ku58yzjdtg
Buzzer top view
20190616 160059 ud5krzbptq
Buzzer underside view with wiring annotations
Piezo sounder wiring   copy edited 3h0yz8vnbp
How everything stacks together
20190330 152725 sv6ywm67bn

Comments

Similar projects you might like

GymGeneral - A Sports Interval Timer

Project tutorial by Mr_Glenn

  • 3,744 views
  • 0 comments
  • 4 respects

Rate of Perceived Exertion Logger

Project tutorial by Mr_Glenn

  • 1,217 views
  • 0 comments
  • 6 respects

A Posture Detector Sending Bluetooth Data to a Cordova App

Project tutorial by Gini76

  • 6,119 views
  • 2 comments
  • 22 respects

HEARTBEAT SENSOR project

Project tutorial by SURYATEJA

  • 6,156 views
  • 0 comments
  • 0 respects

Stop Smoking Device

Project tutorial by Marc Uberstein

  • 5,568 views
  • 0 comments
  • 14 respects

Flowers - Arduino Nano, CrazyCircuits, DFRobot

Project tutorial by Kitty Yeung

  • 4,048 views
  • 6 comments
  • 28 respects
Add projectSign up / Login