No. I want to start a stopwatch when the button is pressed and reset it when the button is pressed a second time.
Is it to display all along, just updating every second?
Yes.
Then you will have to establish another variable or two and keep track of that vs. millis()
mark event millis(), print a '0',
has millis() advanced 1000 yet?
I'm having trouble finding a place to start the timer when the button is pushed. this is what I've got so far:
#include <Adafruit_LiquidCrystal.h>
unsigned long startTime = 0;
unsigned long currentTime = 0;
int indicator = 3;
const long count = 1000;
unsigned long previousTime = 0;
unsigned long markTime;
unsigned long elapsedTime;
unsigned long lockoutmark; // pb-related
const unsigned long debounceTime = 250; // debounceTime, used w/ lockout
const byte T1 = 8; // onboard
const unsigned long testTime = 1500; // time on
boolean lockout; // if true then PB doesn't get checked
boolean tripped; // pb hit, prep Update for new pointers etc.
const byte pb1pin = A0;
byte pb1state;
boolean engaged;
Adafruit_LiquidCrystal lcd(0);
void setup ()
{
Serial.begin(9600);
pinMode(pb1pin,INPUT_PULLUP);
pinMode(T1, OUTPUT);
lockout = false;
tripped = true;
engaged = false;
markTime = millis();
lcd.begin(16, 2);
lcd.clear();
}
void loop ()
{
elapsedTime = millis();
if(!lockout) // ok to read
{
pb1state = digitalRead(pb1pin);
if (!pb1state) // pb made active
{
lockoutmark = millis(); // record millis when pb went LOW
lockout = true;
tripped = true;
if (engaged)
{
digitalWrite(T1, LOW);
engaged = false;
}
else
{
digitalWrite(T1, HIGH);
markTime = millis();
engaged = true;
}
}
}
elapsedTime = millis();
if ((lockout) && ((elapsedTime - lockoutmark) >= debounceTime))
{
lockout = false; // lockout done, threshold met
tripped = false;
}
if (engaged)
{
elapsedTime = millis();
indicator++;
if((elapsedTime - markTime) >= testTime) // determine whether an Update() is due
{
digitalWrite(T1,LOW);
engaged = false;
}
}
if ((indicator & 2) == 0) {
currentTime = millis();
lcd.setCursor(0,0);
lcd.print((currentTime - )/1000);
lcd.print(':');
lcd.print((currentTime- ) / 100);
}
}
My stopwatch is here at the bottom.
Here is a reduced and modified version with a program-structure listing
/* Programm-structure Overview
* loop
* - calls GetToggleSwitchState()
* - calls TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
*
* - calls resetDigitsToZero();
*
* - calls CountUp();
* - calls TimePeriodIsOver(MyStopWatchTimer,100)
*
* - calls printStopWatchTime();
* - calls TimePeriodIsOver(MyPrintTimer, 50)
* - calls clearSerialMonitor();
*/
It has a special serial monitor-printing where the output seems to be steady at the bottom of the serial monitor-window
In reality the code prints so many empty lines than the prevoius lines are scrolled out of sight very fast
This gives an impression of a steady display
The print-intervall is 50 milliseconds to show even 10th-seconds
it can be changed in this line of code
If the serial monitor is flieckering reduce the applications that run on your computer
void printStopWatchTime() {
if ( TimePeriodIsOver(MyPrintTimer, 50) ) { // <<== change here to 1000
so here is the complete sketch
/* Programm-structure Overview
* loop
* - calls GetToggleSwitchState()
* - calls TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
*
* - calls resetDigitsToZero();
*
* - calls CountUp();
* - calls TimePeriodIsOver(MyStopWatchTimer,100)
*
* - calls printStopWatchTime();
* - calls TimePeriodIsOver(MyPrintTimer, 50)
* - calls clearSerialMonitor();
*/
#define ProjectName "Stopwatch demonstration"
// define IO-states for inputs with pull-up-resistors
// pull-up-resistors invert the logig
#define unPressed HIGH
#define pressed LOW
const byte ToggleButtonPin = 7;
bool activationMode = false;
unsigned long myCounter = 0;
unsigned long MyStopWatchTimer = 0;
unsigned long MyPrintTimer = 0;
boolean stopWatchRunning = false;
byte StopWatch10thSecCounter = 0;
byte SW_Sec = 0;
byte SW_Min = 0;
byte SW_Hour = 0;
void setup() {
Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
Serial.println( F("Setup-Start") );
Serial.println( F("Press button connected to IO-pin 7") );
Serial.println( F("to Start / Stop stopwatch") );
pinMode (LED_BUILTIN, OUTPUT); // used for indicating logging active or not
digitalWrite(LED_BUILTIN, LOW);
// wire button between IO-pin and GND
// Pull-up-resistor inverts the logic
// unpressed: IO-pin detects HIGH
// pressed: IO-Pin detects LOW
pinMode(ToggleButtonPin, INPUT_PULLUP);
MyStopWatchTimer = millis();
}
void loop () {
activationMode = GetToggleSwitchState(); // must be executed all the time
if (activationMode) { // if button pressed to start stopwatch
//printStopWatchTime();
if (!stopWatchRunning) { // if stopwatch not yet running = it is really starting
resetDigitsToZero();
stopWatchRunning = true; // set flag to true
}
CountUp();
printStopWatchTime();
}
else {
if (stopWatchRunning) {
stopWatchRunning = false;
}
}
}
void resetDigitsToZero() {
StopWatch10thSecCounter = 0;
SW_Sec = 0;
SW_Min = 0;
SW_Hour = 0;
}
void clearSerialMonitor() {
for (byte i = 0; i < 30; i++) {
Serial.print("\n");
}
}
void printStopWatchTime() {
if ( TimePeriodIsOver(MyPrintTimer, 50) ) { // change here to 1000
clearSerialMonitor();
Serial.println("Stopwatch is running");
Serial.println("Counting up");
Serial.print(SW_Hour);
Serial.print(":");
Serial.print(SW_Min);
Serial.print(":");
Serial.print(SW_Sec);
Serial.print(",");
Serial.println(StopWatch10thSecCounter);
}
}
void CountUp() {
if ( TimePeriodIsOver(MyStopWatchTimer, 100) ) { // check if 0,1 seconds of time have passed by
StopWatch10thSecCounter++;
if (StopWatch10thSecCounter == 10) { // if 10 x 0,1 seconds
SW_Sec++; // increment seconds
StopWatch10thSecCounter = 0;
}
if (SW_Sec == 60) { // check if seconds reached 60
SW_Min++; // if yes increment minutes
SW_Sec = 0; // set seconds back to 0
}
if (SW_Min == 60) { // check if minutes reached 60
SW_Hour++; // if yes increment hours
SW_Min = 0; // set minutes to 0
}
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN) ); // read state of LED-output and set to invertetd state
}
}
bool GetToggleSwitchState() {
// "static" makes variables persistant over function calls
static bool toggleState = false;
static bool lastToggleState = false;
static byte buttonStateOld = unPressed;
static unsigned long buttonScanStarted = 0;
unsigned long buttonDebounceTime = 20;
static unsigned long buttonDebounceTimer;
byte buttonStateNew;
if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
// if more time than buttonDebounceTime has passed by
// this means let pass by some time until
// bouncing of the button is over
buttonStateNew = digitalRead(ToggleButtonPin);
if (buttonStateNew != buttonStateOld) {
// if button-state has changed
buttonStateOld = buttonStateNew;
if (buttonStateNew == unPressed) {
// if button is released
toggleState = !toggleState; // toggle state-variable
} // the attention-mark is the NOT operator
} // which simply inverts the boolean state
} // !true = false NOT true is false
// !false = true NOT false is true
return toggleState;
}
// ignore at first
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
//startOfPeriod = currentMillis; // a new period starts right here so set new starttime
startOfPeriod += TimePeriod;
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
best regards Stefan
Okay, I've got what I want mostly worked out, but I don't know how to get the timer to display tenths and hundredths of seconds for more granularity. Also, I'm not sure why.. but sometimes when I hit the button nothing happens.
#include <Adafruit_LiquidCrystal.h>
bool indicator = 1;
unsigned long startTime = 0;
unsigned long currentTime = 0;
const long count = 1000;
unsigned long previousTime = 0;
unsigned long markTime;
unsigned long elapsedTime;
unsigned long lockoutmark; // pb-related
const unsigned long debounceTime = 250; // debounceTime, used w/ lockout
const byte T1 = 8; // onboard
const unsigned long testTime = 3000; // time on
boolean lockout; // if true then PB doesn't get checked
boolean tripped; // pb hit, prep Update for new pointers etc.
const byte pb1pin = A0;
byte pb1state;
boolean engaged;
const byte powerPin = 9;
Adafruit_LiquidCrystal lcd(0);
void setup ()
{
pinMode(powerPin, OUTPUT);
Serial.begin(9600);
pinMode(pb1pin,INPUT_PULLUP);
pinMode(T1, OUTPUT);
lockout = false;
tripped = true;
engaged = false;
markTime = millis();
lcd.begin(16, 2);
lcd.clear();
digitalWrite(powerPin, HIGH);
delay(500);
digitalWrite(powerPin, LOW);
}
void loop ()
{
elapsedTime = millis();
if(!lockout) // ok to read
{
pb1state = digitalRead(pb1pin);
if (!pb1state) // pb made active
{
indicator = !indicator;
lockoutmark = millis(); // record millis when pb went LOW
lockout = true;
tripped = true;
if (engaged)
{
digitalWrite(T1, LOW);
engaged = false;
}
else
{
digitalWrite(T1, HIGH);
markTime = millis();
engaged = true;
}
}
}
elapsedTime = millis();
if ((lockout) && ((elapsedTime - lockoutmark) >= debounceTime))
{
lockout = false; // lockout done, threshold met
tripped = false;
}
if (engaged)
{
startTime = millis();
elapsedTime = millis();
if (((elapsedTime - markTime) < testTime) && indicator == 0)
{
lcd.setCursor(0,0);
lcd.clear();
lcd.print("Preinfusion");
}
if((elapsedTime - markTime) >= testTime) // determine whether an Update() is due
{
digitalWrite(T1,LOW);
engaged = false;
}
}
Serial.print(indicator);
if (indicator == 0 && engaged == false) {
currentTime = millis();
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("Time: ");
lcd.print(((currentTime - startTime ) + 3000) /1000);
}
else {
lcd.setCursor(0,0);
lcd.print(" ");
}
}
How will you ever achieve a 10 milliseconds resolution when your debounce time is 25 times longer?
const unsigned long debounceTime = 250; // debounceTime, used w/ lockout
1/100th seconds = 10 ms resolution is not achievable if you print out at 9600 baud and additionally send data to an I2C-LCD
This line of code rounds to seconds
lcd.print(((currentTime - startTime ) + 3000) /1000);
you will have to modify this.
printing a single character at 9600 baud needs 1 ms so after printing 10 characters you are above 10 ms
The debounce time filters out the mechanical rattling in the pushbutton.
If it's not responding to a click, I speculate that it may be during the debounce time. You can dial back on that, but at some point you will encounter the rattling-effect inside the switch (mentioned before) - or it's caught up in something otherwise time-consuming.
You can only get as much 'granularity' as you're willing to check for - and at some point have no time for anything else.
Or look into (dare I mention it?) Interrupts
I'm not sure why you say that. It runs fine at 9600.
if the word "that" in this case means not achieving a finer granularity.
If you would like to "see" 1/100the seconds count up on the display
1,01
1,02
1,03
1,04
....
the baudrate is too slow for updating the display so fast.
It will be something like this example
1,01
1,07
1,15
1,23
1/100th seconds can't be seen with the human eye.
1/10th is possible
Ah. Okay, I get it. Yes, im seeing a big blur. My screen can go up to 57600. Not sure if thats fast enough?
Run it up.
what is "your screen" ? I thought you want to display the upcounting time on a I2C-display?
57600 baud means 57600 bits per second
a character is 10 bits (8 databits a startbit a stopbit)
So each character needs 1/57600 * 10 = 174 µS
1/100th second = 10.000 µS / 174 = 57 characters
I haven't checked how long datastransmision to a I2C-display needs.
Again depends on how many bytes and how many commands
This is the screen I am using:
I am building everything in an emulator and the 12C was the only thing the emulator had.
What emulator ist it?
Does this emulator have a serial monitor?
How fast can theis serial monitor print?
I mean if you print 50 lines are the 50 lines run through in less than 0,1 second or is it pretty slow?
It's the tinkercad emulator. I don't know all of the details about it.
I'm having Issues with the PID now. No matter what I do, the temperature keeps going up. Any advice?
#include "Arduino.h"
#if defined(ARDUINO_ARCH_SAMD) || defined(__SAM3X8E__)
// use pin 18 with Due, pin 1 with Zero or M0 Pro
#define lcd Serial1
#else
#include <SoftwareSerial.h>
// Create a software serial port!
SoftwareSerial lcd = SoftwareSerial(0,2);
#endif
// PID Libraries
#include <PID_v1.h>
// Libraries for the DS18B20 sensor
#include <OneWire.h>
#include <DallasTemperature.h>
// ************************************************
// Pins We'll Use
// ************************************************
// Pin for Temperature control pot
#define TMP_CNTRL A1
#define TMP_PWR A2
#define TMP_GND A3
// Pin for Brew Relay
#define BREW_RELAY 8
// Pin for Brew Button
#define BREW_BUTTON A0
// Pin for Power Relay
#define POWER_RELAY 9
// DS18B20 on PIN 6 on the Arduino
#define ONE_WIRE_BUS 6
#define ONE_WIRE_PWR 3
#define ONE_WIRE_GND 4
//Solid state relay on PIN 5 on the Arduino
#define COIL_RELAY 5
// ************************************************
// PID Definitions
// ************************************************
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
double Kp=2, Ki=0.5, Kd=2;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
int WindowSize = 1000;
unsigned long windowStartTime;
// ************************************************
// Definitions for Brew Button & Timer
// ************************************************
bool indicator = 1;
unsigned long startTime = 0;
unsigned long currentTime = 0;
const long count = 1000;
unsigned long previousTime = 0;
unsigned long markTime;
unsigned long elapsedTime;
unsigned long lockoutmark; // pb-related
const unsigned long debounceTime = 500; // debounceTime, used w/ lockout
const unsigned long endBrew = 500;
const unsigned long preinfusionTime = 3000; // time on
boolean lockout; // if true then PB doesn't get checked
boolean tripped; // pb hit, prep Update for new pointers etc.
byte pb1state;
boolean engaged;
void setup ()
{
// ************************************************
// Setup LCD Screen
// ************************************************
lcd.begin(9600);
// set the size of the display if it isn't 16x2 (you only have to do this once)
lcd.write(0xFE);
lcd.write(0xD1);
lcd.write(16); // 16 columns
lcd.write(2); // 2 rows
delay(10);
//Set RGB Color to Blue
lcd.write(0xFE);
lcd.write(0xD0);
lcd.write(0x01);
lcd.write(0x01);
lcd.write(0xFF);
// set the contrast
lcd.write(0xFE);
lcd.write(0x50);
lcd.write(200);
delay(10);
// set the brightness
lcd.write(0xFE);
lcd.write(0x99);
lcd.write(255);
delay(10);
lcd.print(0xFE);
lcd.print(0x52);
//change Splash Screen
lcd.write(0xFE);
lcd.write(0x40);
lcd.write("Coffeeduino_v1.0 ");
delay(10);
// ************************************************
// Setup Brew Button & Timer
// ************************************************
pinMode(TMP_PWR, OUTPUT);
digitalWrite(TMP_PWR, HIGH);
pinMode(TMP_GND, OUTPUT);
digitalWrite(TMP_GND, LOW);
pinMode(POWER_RELAY, OUTPUT);
pinMode(BREW_BUTTON,INPUT_PULLUP);
pinMode(BREW_RELAY, OUTPUT);
lockout = false;
tripped = true;
engaged = false;
markTime = millis();
// ************************************************
// Setup Temperature Sensor
// ************************************************
pinMode(ONE_WIRE_GND, OUTPUT);
digitalWrite(ONE_WIRE_GND, LOW);
pinMode(ONE_WIRE_PWR, OUTPUT);
digitalWrite(ONE_WIRE_PWR, HIGH);
// ************************************************
// Setup PID
// ************************************************
pinMode(COIL_RELAY, OUTPUT);
digitalWrite(COIL_RELAY, LOW);
windowStartTime = millis();
//initialize the variables we're linked to
Setpoint = 40;
//tell the PID to range between 0 and the full window size
myPID.SetOutputLimits(0, WindowSize);
//turn the PID on
myPID.SetMode(AUTOMATIC);
// ************************************************
// Power on Coffee machine
// ************************************************
digitalWrite(POWER_RELAY, HIGH);
delay(500);
digitalWrite(POWER_RELAY, LOW);
}
void loop ()
{
//Allow TMP_CNTRL to adjust Setpoint
int value = analogRead(TMP_CNTRL);
int increment = value / 13;
Setpoint = 40 + increment;
//Brew Controls
int set = Setpoint;
int temp = floor(sensors.getTempCByIndex(0));
elapsedTime = millis();
Brewstart(elapsedTime, set, temp);
//PID Controls
sensors.requestTemperatures();
Input = sensors.getTempCByIndex(0);
myPID.Compute();
/************************************************
* turn the output pin on/off based on pid output
************************************************/
/* if (millis() - windowStartTime > WindowSize)
{
//time to shift the Relay Window
windowStartTime += WindowSize;
}*/
if (Output < millis() - windowStartTime)
digitalWrite(COIL_RELAY, HIGH);
else
digitalWrite(COIL_RELAY, LOW);
}
void Brewstart( unsigned long x, int y, int z) {
if(!lockout) // ok to read
{
pb1state = digitalRead(BREW_BUTTON);
if (!pb1state) // pb made active
{
indicator = !indicator;
lockoutmark = millis(); // record millis when pb went LOW
lockout = true;
tripped = true;
if (engaged)
{
digitalWrite(BREW_RELAY, LOW);
engaged = false;
}
else
{
digitalWrite(BREW_RELAY, HIGH);
markTime = millis();
engaged = true;
}
}
}
elapsedTime = millis();
if ((lockout) && ((elapsedTime - lockoutmark) >= debounceTime))
{
lockout = false; // lockout done, threshold met
tripped = false;
}
if (engaged)
{
startTime = millis();
elapsedTime = millis();
if (((elapsedTime - markTime) < preinfusionTime) && indicator == 0)
{
// go 'home'
lcd.write(0xFE);
lcd.write(0x48);
lcd.print("Preinfusion ");
}
if((elapsedTime - markTime) >= preinfusionTime && indicator == 0) // determine whether an Update() is due
{
digitalWrite(BREW_RELAY,LOW);
engaged = false;
}
if((elapsedTime - markTime) >= endBrew && indicator == 1) // determine whether an Update() is due
{
digitalWrite(BREW_RELAY,LOW);
engaged = false;
}
}
if (indicator == 0 && engaged == false) {
currentTime = millis();
// go 'home'
lcd.write(0xFE);
lcd.write(0x48);
// clear screen
lcd.write(0xFE);
lcd.write(0x58);
// Setup screen
lcd.print("Time: ");
// Set cursor position
lcd.write(0xFE);
lcd.write(0x47);
// Column 1, Row 2
lcd.write(0x01);
lcd.write(0x02);
lcd.print("Temp S:");
lcd.print(y);
lcd.print("A:");
lcd.print(z);
//Set cursor position
lcd.write(0xFE);
lcd.write(0x47);
// Column 7, Row 1
lcd.write(0x07);
lcd.write(0x01);
lcd.print((currentTime - startTime + 3000) /1000);
lcd.print(':');
lcd.print((currentTime - startTime + 3000) % 1000);
}
if (indicator == 1 && engaged == false) {
// go 'home'
lcd.write(0xFE);
lcd.write(0x48);
// clear screen
lcd.write(0xFE);
lcd.write(0x58);
// Setup screen
lcd.print("Time: ");
// Set cursor position
lcd.write(0xFE);
lcd.write(0x47);
// Column 1, Row 2
lcd.write(0x01);
lcd.write(0x02);
lcd.print("Temp S:");
lcd.print(y);
lcd.print("A:");
lcd.print(z);
}
}
If output-variable is smaller than the difference
between milliseconds since power-on and milliseconds short after power-on
then switch on relay?
with example numbers
lets say your code needs 2000 milliseconds to reach the line
void setup ()
{
// ************************************************
// Setup LCD Screen
// ************************************************
lcd.begin(9600);
// set the size of the display if it isn't 16x2 (you only have to do this once)
lcd.write(0xFE);
lcd.write(0xD1);
....
pinMode(COIL_RELAY, OUTPUT);
digitalWrite(COIL_RELAY, LOW);
windowStartTime = millis(); // <<<<<=== THIS LINE millis() = 2000
3 seconds later millis() has value 5000
so your if-condition is
if (Output < millis() - windowStartTime)
with the example numbers
if (output < (5000 - 2000) {
after one minute since power up
if (output < (60000 - 2000) {
This makes no sense at all.
You involved timing in the if-condition so I guess you want to do something with timing.
It seems the functionality you want is not just
If temperature is below setpoint-temperature heating on
If temperature is at or above setpoint-temperature heating OFF
You want timing be involved.
So please describe the wanted functionality with normal words
not with code.
