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;
}
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? 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.
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;
}
}
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
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);
}
}
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.