Coin Operated Television

Hello, I'm trying to learn to code for a project to make my television coin operated. I have a coin mech programmed to take a token. I plan for each token to be worth five minutes of television. When the time runs out I will have it turn off a relay. I have a seven segment led from Adafruit to display the time remaining.

So far I have it recognizing the coin, adding the value of the coins, and doing a countdown, but I can't figure out how to have it stop at the end of the countdown. It always starts over because the elapsed time doesn't subtract from the start time.

If anyone has a minute to help me out I would be very grateful.

/******************************************************************
*  using tokens to operate a relay for a set amount of time as a
*  way to control how much television we are watching.
*  Power with 12v
*  Coin Mech gets 12v and the Digital 2 pin
*  7-segment display gets 5v, pin A5 -> C, pin A4 -> D
*  
*******************************************************************/
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_LEDBackpack.h"
Adafruit_7segment matrix = Adafruit_7segment();

//  ***  Coin Parameters  ***  //
const int coinInt = 0;            //Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.
volatile float coinsValue = 0.00; //Set the coinsValue to a Volatile float. Volatile as this variable changes any time the Interrupt is triggered
int coinsChange = 0;              //A Coin has been inserted flag

//  ***  Timer Parameters  *** //
unsigned long previousSecondMillis = 0UL;
long oneSecond = 1000UL;   // milliseconds per second
volatile int minutes = coinsValue;  // Set the value of the timer to the value of the coins deposited
int seconds = 0;

void setup()
{
 matrix.begin(0x70);                               //Starts communication with the display
 matrix.setBrightness(5);                          //Choose a value between 0 - 15. Larger # is brighter display
 attachInterrupt(coinInt, coinInserted, RISING);   //If coinInt goes HIGH (a Pulse), call the coinInserted function. An attachInterrupt will always trigger, even if your using delays
 matrix.print(coinsValue);                         //Sets the initial value of the display to the value of the coins
 matrix.writeDisplay();                            //Writes to the display for the first time.
}

void coinInserted()    
//The function that is called every time it recieves a pulse
{
 coinsValue = coinsValue + 5;  
 //As we set the Pulse to represent 5p or 5c we add this to the coinsValue
 coinsChange = 1;                           
 //Flag that there has been a coin inserted
}

void loop()
{
 if(coinsChange == 1)          
   //Check if a coin has been Inserted
 {
   coinsChange = 0;              
   //unflag that a coin has been inserted

   matrix.print(coinsValue);
   matrix.writeDisplay();
 }
 if(millis() - previousSecondMillis >= oneSecond)  {
   matrix.writeDigitNum(0, (minutes / 10));
   matrix.writeDigitNum(1, (minutes % 10));
   matrix.writeDigitNum(3, (seconds / 10));
   matrix.writeDigitNum(4, (seconds % 10));
   matrix.writeDisplay();

   if (seconds-- == 0)  {
     if (minutes == 0) {
       minutes = (coinsValue);
       seconds = 0;
       delay(1000);
     } 
     else {
       minutes -= 1;
       seconds = 59;
     }
   }
   previousSecondMillis += oneSecond;
 }
 x = 0;
}

Sounds like something I could use at my house! :slight_smile:

I'd suggest a bit of refactoring. I think you're getting into trouble, in part, because you've intermingled distinct pieces of logic.

  • coinInserted() should do as little as possible. Just have it set 'coinsChange' and be done.
  • In loop(), when you see coinsChange==1, that's when you should increment 'minutes' by 5.
  • The stuff under 'if(millis() - prev...)' should be purely concerned with decrementing 'seconds' and 'minutes' if they're not both already zero. Get the coinsValue and delay() stuff out of there.
  • Add a new variable called something like 'updateDisplay' which is initialized to true. At the bottom of loop(), check for this and do all your matrix.writeDigitNum() stuff if it's true, then set 'updateDisplay' to false. 'updateDisplay' should be set to true any time coinsChange==1 is detected, or when 'seconds' or 'minutes' are decremented. Get all the 'matrix.writeDigitNum()' stuff out of your timing code.
  • The first time 'if(millis() - prev...)' evaluates to true should be when the first second has elapsed.
  • ...so when a coin is inserted (when coinsChange==1 is true), if 'minutes' and 'seconds' are both 0, then you should set previousSecondsMillis equal to millis(). That way, the first second will actually elapse one second after the first coin is inserted. You don't want to be cheating anybody by a fraction of a second, right? :slight_smile: Also, if 'minutes' and 'seconds' are both zero at this time, this is when you turn the TV on.
  • ...and that means you'd want to do --seconds instead of seconds--.
  • When you decrement 'minutes' and it reaches zero, that's when you turn the TV off.
  • Don't set x to 0. Because there's no such variable as 'x' declared anywhere, as far as I can tell.

Or there are some ideas, anyway.

Thanks for the help. I'm so code illiterate I haven't had success yet, but I'm still working on it. This is where things stand now.

/******************************************************************
 *  using tokens to operate a relay for a set amount of time as a
 *  way to control how much television we are watching.
 *  Power with 12v
 *  Coin Mech gets 12v and the Digital 2 pin
 *  7-segment display gets 5v, pin A5 -> C, pin A4 -> D
 *  
 *******************************************************************/
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_LEDBackpack.h"
Adafruit_7segment matrix = Adafruit_7segment();

const int coinInt = 0;            //Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.
volatile float coinsValue = 0.00; //Set the coinsValue to a Volatile float. Volatile as this variable changes any time the Interrupt is triggered
int coinsChange = 0;              //A Coin has been inserted flag
unsigned long previousSecondMillis = 0UL;
long oneSecond = 1000UL;         // milliseconds per second
volatile int minutes = coinsValue;  // Set the value of the timer to the value of the coins deposited
int seconds = 0;
boolean updateDisplay = true;

void setup()
{
  matrix.begin(0x70);                               //Starts communication with the display
  matrix.setBrightness(5);                          //Choose a value between 0 - 15. Larger # is brighter display
  attachInterrupt(coinInt, coinInserted, RISING);   //If coinInt goes HIGH (a Pulse), call the coinInserted function. An attachInterrupt will always trigger, even if your using delays
  matrix.print(coinsValue);                         //Sets the initial value of the display to the value of the coins
  matrix.writeDisplay();                            //Writes to the display for the first time.
}

void coinInserted()    
//The function that is called every time it recieves a pulse
{
  coinsChange = 1;                           
  //Flag that there has been a coin inserted
}

void loop()
{
  if(coinsChange == 1)          
    //Check if a coin has been Inserted
  {
    coinsChange = 0;              
    //unflag that a coin has been inserted
    coinsValue = minutes + 5; //Up the time by five minutes.
    matrix.print(coinsValue);
    matrix.writeDisplay();
    updateDisplay = true;
  }
  if(millis() - previousSecondMillis >= oneSecond)  {
    if (seconds-- == 0)  {
      if (minutes == 0) {
      } 
      else {
        minutes -= 1;
        seconds = 59;
      }
    }
    previousSecondMillis += oneSecond;
  }
  if (updateDisplay ==true){
    matrix.writeDigitNum(0, (minutes / 10));
    matrix.writeDigitNum(1, (minutes % 10));
    matrix.writeDigitNum(3, (seconds / 10));
    matrix.writeDigitNum(4, (seconds % 10));
    matrix.writeDisplay(); 
    updateDisplay = false;
  }
}

Can you make one so when you put in a coin it turns the damn thing off for 30 minutes. That would be a big seller, I suspect.

...R

I'm making my kids earn their screen time. If I can figure out the code.

Leave aside the business of displaying data on your screen and just get the TV control logic working with output to the Serial Monitor. When that is working then add in the display code.

When a coin is inserted you need to save the value of millis() and set a variable, perhaps called tvON = true. Then you continually check to see if the allowed time has elapsed

if (millis() - coinInMillis > allowedTimeMillis) {
   tvON = false;
}

Elsewhere in your program you will have code that allows the TV to work if the variable tvON == true.

...R
Planning and Implementing a Program

I believe I now have the logic running. I need to figure out how to format the remaining time for the led display.

/******************************************************************
 *  using tokens to operate a relay for a set amount of time as a
 *  way to control how much television we are watching.
 *  Power with 12v
 *  Coin Mech gets 12v and the Digital 2 pin
 *  7-segment display gets 5v, pin A5 -> C, pin A4 -> D
 *  
 *******************************************************************/

const int coinInt = 0;                     //Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.
volatile float runTime = 0.00;           //Set the coinsValue to a Volatile float. Volatile as this variable changes any time the Interrupt is triggered
int coinsChange = 0;                       //A Coin has been inserted flag
unsigned long previousSecondMillis = 0UL;  //Stores time
long oneSecond = 1000UL;                   //milliseconds per second
volatile int minutes = runTime;          //Set the value of the timer to the value of the coins deposited
int seconds = 0;                           //Set the initial number of seconds to 0
unsigned long startTime = 0;               //Time coin was deposited
boolean tvOn = false;
int coinValue = 2;
int remainingTime = 0;

void setup(){
  attachInterrupt(coinInt, coinInserted, RISING);   //If coinInt goes HIGH (a Pulse), call the coinInserted function. An attachInterrupt will always trigger, even if your using delays
  Serial.begin(9600);
}

void coinInserted()                                 //The function that is called every time it recieves a pulse
{
  coinsChange = 1;                                  //Flag that there has been a coin inserted
}

void loop(){
  if(coinsChange == 1){                 //Check if a coin has been Inserted
    coinsChange = 0;                    //unflag that a coin has been inserted
    startTime = millis();                //time coin was deposited
    runTime = startTime + (coinValue * 1000);     //Up the time by five minutes
    Serial.print("Start Time: ");
    Serial.println(startTime);  
  }
  if(millis() - startTime < runTime)  {
    tvOn = true;
    Serial.println("TV Is On!");
  }  
  else {
    tvOn = false;
    Serial.println("TV Is Off!");
    delay (1000);
  }
}

jimiMac:
I need to figure out how to format the remaining time for the led display.

I can't help with that.

I presume this is the raw calculation

remainingTime = runTime - (millis() - startTime);

...R

coinsChange is changed in the interrupt routine so it should be declared volatile. I don't see that your other uses of volatile are necessary (yet). coinsChange is just used as a flag, so why not use a boolean instead?

Do you intend to allow users to enter several coins at once? As you have it, a second coin will reset the start time and the user will lose any remaining time they were due from the first coin.