This is what I am trying to achieve:
User inputs time using a rotary encoder.
The Arduino Serial Monitor must display real-time values of time as the user keeps rotating the encoder.
The user then hits a physical switch (push switch) to initiate the countdown.
Initially, my code worked perfectly with delay() function.
But my application also requires me to run a motor as long as the timer lasts. For this, I need a non-blocking delay. I am having a hard time with that. Please help. Here is my code:
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change:
const long interval = 1000; // interval at which to blink (milliseconds)
#define outputA 6
#define outputB 7
int i;
int button=5;
int counter = 0;
int aState;
int aLastState;
void setup() {
pinMode (outputA,INPUT);
pinMode (outputB,INPUT);
pinMode (button,INPUT);
Serial.begin (9600);
// Reads the initial state of the outputA
aLastState = digitalRead(outputA);
}
void loop() {
// here is where you'd put code that needs to be running all the time.
aState = digitalRead(outputA); // Reads the "current" state of the outputA
// If the previous and the current state of the outputA are different, that means a Pulse has occured
if (aState != aLastState) {
// If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
if (digitalRead(outputB) != aState) {
counter = counter+1;
} else {
counter = counter-1;
}
Serial.print("Time (secs): ");
Serial.println(counter);
i = counter;
if (digitalRead(button) == HIGH) {
while (i != 0) {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis == interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
i--;
Serial.println(i);
}
}
}
aLastState = aState;
}
}
If I understand well you are decrementing i only when entering if (currentMillis - previousMillis == interval). It means you are decrementing very slowly, and not millisecond by milliseconds…
To correct this I'm suggesting:
unsigned long startMillis = millis();
while (millis() - startMillis() < counter){ //check if your time has elapsed
unsigned long currentMillis = millis();
if (currentMillis - previousMillis == interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
Serial.println((int) counter - (millis() - startMillis()));
}
}
Hope it helps!
Related
I bought the same boards used in this video on Youtube (the componts are THIS geiger counter (that I am pretty sure its working, the tiks on the speacker are coerent) and THIS esp (I don't know if is here the problem or in the code), I loaded the arduino code into the esp32 board, but the display remains stuck on "Measuring". It connects correctly to the ThingSpeak channel, but always linearly increasing measurements. The number grows continuously (view the attached image). I tried to insert some currentMillis and previousMillis println. Both variables are always 0 in the serial monitor.
So I tried adding currentMillis = millis (); at the beginning of the loop (), at this point the display has finally shown "radioactivity" with the usual increasing numbers. Even if the card is disconnected from the geiger counter, the data are the same and always increasing. How can I solve it?
#define PRINT_DEBUG_MESSAGES
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <IFTTTMaker.h>
#include <ThingSpeak.h>
#include <SSD1306.h>
//#include <credentials.h> // or define mySSID and myPASSWORD and THINGSPEAK_API_KEY
#define LOG_PERIOD 20000 //Logging period in milliseconds
#define MINUTE_PERIOD 60000
#define WIFI_TIMEOUT_DEF 30
#define PERIODE_THINKSPEAK 20000
#ifndef CREDENTIALS
// WLAN
#define mySSID ""
#define myPASSWORD ""
//IFTT
#define IFTTT_KEY "......."
// Thingspeak
#define SECRET_CH_ID 000000 // replace 0000000 with your channel number
#define SECRET_WRITE_APIKEY "XYZ" // replace XYZ with your channel write API Key
#endif
// IFTTT
#define EVENT_NAME "Radioactivity" // Name of your event name, set when you are creating the applet
IPAddress ip;
WiFiClient client;
WiFiClientSecure secure_client;
IFTTTMaker ifttt(IFTTT_KEY, secure_client);
SSD1306 display(0x3c, 5, 4);
volatile unsigned long counts = 0; // Tube events
unsigned long cpm = 0; // CPM
unsigned long previousMillis; // Time measurement
const int inputPin = 7;
unsigned int thirds = 0;
unsigned long minutes = 1;
unsigned long start = 0;
unsigned long entryThingspeak;
unsigned long currentMillis = millis();
unsigned long myChannelNumber = SECRET_CH_ID;
const char * myWriteAPIKey = SECRET_WRITE_APIKEY;
#define LOG_PERIOD 20000 //Logging period in milliseconds
#define MINUTE_PERIOD 60000
void ISR_impulse() { // Captures count of events from Geiger counter board
counts++;
}
void displayInit() {
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_24);
}
void displayInt(int dispInt, int x, int y) {
display.setColor(WHITE);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(x, y, String(dispInt));
display.setFont(ArialMT_Plain_24);
display.display();
}
void displayString(String dispString, int x, int y) {
display.setColor(WHITE);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(x, y, dispString);
display.setFont(ArialMT_Plain_24);
display.display();
}
/****reset***/
void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
{
Serial.print("resetting");
esp_restart();
}
void IFTTT(String event, int postValue) {
if (ifttt.triggerEvent(EVENT_NAME, String(postValue))) {
Serial.println("Successfully sent to IFTTT");
} else
{
Serial.println("IFTTT failed!");
}
}
void postThingspeak( int value) {
// Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
// pieces of information in a channel. Here, we write to field 1.
int x = ThingSpeak.writeField(myChannelNumber, 1, value, myWriteAPIKey);
if (x == 200) {
Serial.println("Channel update successful.");
}
else {
Serial.println("Problem updating channel. HTTP error code " + String(x));
}
}
void setup() {
Serial.begin(115200);
displayInit();
ThingSpeak.begin(client); // Initialize ThingSpeak
displayString("Welcome", 64, 15);
Serial.println("Connecting to Wi-Fi");
WiFi.begin(mySSID, myPASSWORD);
int wifi_loops = 0;
int wifi_timeout = WIFI_TIMEOUT_DEF;
while (WiFi.status() != WL_CONNECTED) {
wifi_loops++;
Serial.print(".");
delay(500);
if (wifi_loops > wifi_timeout)
{
software_Reset();
}
}
Serial.println();
Serial.println("Wi-Fi Connected");
display.clear();
displayString("Measuring", 64, 15);
pinMode(inputPin, INPUT); // Set pin for capturing Tube events
interrupts(); // Enable interrupts
attachInterrupt(digitalPinToInterrupt(inputPin), ISR_impulse, FALLING); // Define interrupt on falling edge
unsigned long clock1 = millis();
start = clock1;
}
void loop() {
if (WiFi.status() != WL_CONNECTED)
{
software_Reset();
}
if (currentMillis - previousMillis > LOG_PERIOD) {
previousMillis = currentMillis;
cpm = counts * MINUTE_PERIOD / LOG_PERIOD;
//cpm=105;
counts = 0;
display.clear();
displayString("Radioactivity", 64, 0);
displayInt(cpm, 64, 30);
if (cpm > 100 ) IFTTT( EVENT_NAME, cpm);
}
// Serial.print("minutes: ");
// Serial.println(String(minutes));
//cpm = counts * MINUTE_PERIOD / LOG_PERIOD; this is just counts times 3 so:
cpm = counts / minutes;
if (millis() - entryThingspeak > PERIODE_THINKSPEAK) {
Serial.print("Total clicks since start: ");
Serial.println(String(counts));
Serial.print("Rolling CPM: ");
Serial.println(String(cpm));
postThingspeak(cpm);
entryThingspeak = millis();
}
// if ( thirds > 2) {
// counts = 0;
// thirds = 0;
// }
}
This is based on this work on GITHUB
Your code never updates the value of currentMillis. You set it equal to millis at global scope, likely before init is called and before millis even has a value and then you just leave it with that value. You're not telling that variable to call millis every time, you're just giving it the value of millis at that one instant. You need a line in loop to update that variable.
Because of that, this section never runs:
if (currentMillis - previousMillis > LOG_PERIOD) {
previousMillis = currentMillis;
cpm = counts * MINUTE_PERIOD / LOG_PERIOD;
//cpm=105;
counts = 0;
display.clear();
displayString("Radioactivity", 64, 0);
displayInt(cpm, 64, 30);
if (cpm > 100 ) IFTTT( EVENT_NAME, cpm);
}
and counts never gets set back to 0. That's why you see an ever increasing number.
You're also using this minutes variable as if it will count something, but you never add to it. So it just stays at 1 forever as far as I can tell.
I have a simple code with delays.
I'd like to know how to convert this code to millis? Is there a function to do so?
long revers = 1000;
void setup() {
pinMode(D1, OUTPUT);
pinMode(D2, OUTPUT);
}
void loop() {
digitalWrite(D1, LOW);
delay(revers);
digitalWrite(D2, HIGH);
delay(revers);
digitalWrite(D2, LOW);
delay(revers);
digitalWrite(D1, HIGH);
delay(revers);
}
The basic concept is this: record the millis() at a given moment in a variable - say 'starttime'. Now, during every loop(), check the time that has elapsed by subtracting millis() from 'starttime'. If the elapsed time is greater than the delaytime you set, execute code. Reset the starttime to create a repeating pattern.
That may be too short of an explanation for you, so before you dive into the code, I highly advise you to read this introduction about using millis() for timing. It's lengthy, but it explains the principle extensively. This will help you understand the code below.
Lastly, there are several libraries written to make the use of timing easier. For instance the SimpleTimer-library, but you can google "arduino timer library" for others. I've included an example below.
1 second on, 3 seconds off:
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 1000; //the value is a number of milliseconds
int fase; //value used to determine what action to perform
void setup() {
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
startMillis = millis(); //initial start time
fase = 0;
}
void loop() {
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
if (currentMillis - startMillis >= period) //test whether the period has elapsed
{
if (fase == 0)
{
digitalWrite(8, LOW);
startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
fase = 1; //increment fase, so next action will be different
}
else if (fase == 1)
{
digitalWrite(7, HIGH);
startMillis = currentMillis;
fase = 2;
}
else if (fase == 2)
{
digitalWrite(7, LOW);
startMillis = currentMillis;
fase = 3;
}
else if (fase == 3)
{
digitalWrite(8, HIGH);
fase = 0;
startMillis = currentMillis;
}
}
}
Example of a flashing led using the SimpleTimer library
#include <SimpleTimer.h>
// the timer object
SimpleTimer timer;
int ledPin = 13;
// a function to be executed periodically
void repeatMe() {
digitalWrite(ledPin, !digitalRead(ledPin));
}
void setup() {
pinMode(ledPin, OUTPUT);
timer.setInterval(1000, repeatMe);
}
void loop() {
timer.run();
}
I'm trying to see if I can combine tasks that are currently being handled by two separate Arduino Unos onto a single Uno. I was shown the Adafruit guide to "multi-tasking" on the Arduino (Link) and thought I would give it a try.
I feel like I'm missing something really obvious, but my code is just not working...
I'm controlling a series of solenoids. They need to each act based on individual timing. I created a Solenoid class that handles telling the solenoid when to start and then monitor when to close it. For the life of me I can't see where my error is, but I never wind up satisfying my end condition.
class Solenoid {
//Class Variables
int solenoidPin;
long chargeTime;
long ventTime;
bool started = false;
unsigned long startMillis;
unsigned long endMillis;
unsigned long previousMillis;
int solenoidState;
//Constructor
public:
Solenoid(int pin, long charge, long vent) {
solenoidPin = pin;
pinMode(solenoidPin, OUTPUT);
chargeTime = charge;
ventTime = vent;
solenoidState = LOW;
previousMillis = 0;
}
void Start() {
Serial.println("Start called");
started = true;
}
void Update() {
//Check to see if it is time to change the state of the solenoid
unsigned long currentMillis = millis();
Serial.println("Update");
if((started == true) && (currentMillis-previousMillis <= chargeTime)) {
//Run
Serial.print("Run: Charge Time=");
Serial.print(chargeTime);
Serial.print(" current-previous=");
Serial.println(currentMillis-previousMillis);
previousMillis = currentMillis;
} else if ((started == true) && (currentMillis-previousMillis >= chargeTime)){
//Stop
Serial.println("Stop");
}
}
};
//Setup the solenoids
Solenoid solenoid1(13, 70, 70);
void setup() {
Serial.begin(115200);
Serial.println("Ready");
solenoid1.Start();
solenoid1.Update();
solenoid1.Update();
solenoid1.Update();
solenoid1.Update();
solenoid1.Update();
}
I'm just running everything in setup so I can see a few runs.
Can you help me with my clearer stupid mistake?
It's possible that the entire setup() void is executing faster than 70ms, meaning you never get to you end position before the 5 calls to solenoid1.Update() complete.
Try this:
class Solenoid {
//Class Variables
int solenoidPin;
long chargeTime;
long ventTime;
bool started = false;
unsigned long startMillis;
unsigned long endMillis;
unsigned long previousMillis;
int solenoidState;
//Constructor
public:
Solenoid(int pin, long charge, long vent) {
solenoidPin = pin;
pinMode(solenoidPin, OUTPUT);
chargeTime = charge;
ventTime = vent;
solenoidState = LOW;
previousMillis = 0;
}
void Start() {
Serial.println("Start called");
started = true;
}
void Update() {
//Check to see if it is time to change the state of the solenoid
unsigned long currentMillis = millis();
Serial.println("Update");
if((started == true) && (currentMillis-previousMillis <= chargeTime)) {
//Run
Serial.print("Run: Charge Time=");
Serial.print(chargeTime);
Serial.print(" current-previous=");
Serial.println(currentMillis-previousMillis);
previousMillis = currentMillis;
} else if ((started == true) && (currentMillis-previousMillis >= chargeTime)){
//Stop
Serial.println("Stop");
}
}
};
//Setup the solenoids
Solenoid solenoid1(13, 70, 70);
void setup() {
Serial.begin(115200);
Serial.println("Ready");
solenoid1.Start();
}
void loop(){
solenoid1.update()
}
The rest looks fine to me; if you still have issues try using explicit variable declarations instead of your constructor to troubleshoot if that's the issue.
Turns out I was erasing my own counter by setting previousMillis = currentMillis. Once I killed that, it started working. I added some other functionality since then, like a delayed start, but this is primarily the same code.
void Update() {
//Check to see if it is time to change the state of the solenoid
unsigned long currentMillis = millis();
if((started == true) && (currentMillis - startMillis <= startDelay)) {
//keep waiting
Serial.println("Waiting");
} else if((started == true) && (currentMillis - startMillis < (chargeTime + startDelay) )) {
//Run
Serial.print("Run ");
Serial.println(currentMillis - startMillis);
digitalWrite(solenoidPin, HIGH);
} else if ((started == true) && (currentMillis - startMillis >= (chargeTime + startDelay) )){
//Stop
Serial.print("Stop ");
Serial.println(currentMillis - startMillis);
digitalWrite(solenoidPin, LOW);
started = false;
}
}
I am working on this code and I cant figure out how to implement a loop reset.
I want the program to return to idle if anytime during the initial 10 second countdown the beam is reconnected. So in other words here is what I want to tell the Arduino: if at any time during the 10 second
count down you see beamPin high (beam connected) reset the loop.
There has to be a simple way to do this.
I am trying to use this code but it is not working like I want it to.
if (state == PAUSE_BEFORE_ON) {
//Wait in tight loop while counting down 10 second pause...
while (millis() - start_time >= PAUSE_BEFORE_ON_ms) {
//Check if BEAM is reconnected...
//If it is, reset to IDLE mode
if ( !beamIsBroken() ) {
state = IDLE;
}
Here is the entire code
//Diesels High Tech Pet beam break sensor
//This sketch activates and deactivates the "hold Up" function which is build into the HIGH TECH pet door
//By imitating pressing and holding "ON BUTTON" while pressing "OPEN BUTTON" and then releasing both buttons
enum State {
IDLE, PAUSE_BEFORE_ON, ON_DELAY, OPEN_DELAY, WAIT_BEAM, PAUSE_BEFORE_CLOSE, OFF_DELAY
} state = IDLE;
uint32_t start_time;
const uint32_t PAUSE_BEFORE_ON_ms = 10L * 1000L; //pause for 10 seconds before initiating sequence
const uint32_t ON_DELAY_ms = 500L; // pause for half a second before sending ON signal
const uint32_t OPEN_DELAY_ms = 500L; // pause for half a second before sending OPEN signal
const uint32_t PAUSE_BEFORE_CLOSE_ms = 2L * 1000L; // pause for 2 seconds before sending ground signal to openPin which deactivates "hold Up" funtion
const uint32_t OFF_DELAY_ms = 250L;// pause for quarter of a second
const byte onPin = 2; // On button
const byte openPin = 3; // Open button
const byte beamPin = 4; // QT50CM yellow wire of receiver
void activateButton(byte pin) {
// to activate a button, we ground the pin
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
void deactivateButton(byte pin) {
// to deactivate a button, we float the pin
pinMode(pin, INPUT);
digitalWrite(pin, LOW);
}
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
deactivateButton(onPin);
deactivateButton(openPin);
pinMode(beamPin, INPUT_PULLUP); // turn on internal 5.1k pullup resistor
state = IDLE;
start_time = millis();
}
boolean beamIsBroken() {
//'broken beam' grounds the output
return digitalRead(beamPin) == LOW;
}
/**
--wait for beam to break (IDLE)
--beam breaks
--wait 10 seconds (PAUSE_BEFORE_ON)
--send signal to ON
--wait half a second (ON_DELAY)
--send signal to OPEN
--wait half a second (OPEN_DELAY)
--release/turn off/float both signals
--wait for beam to reconnect (WAIT_BEAM)
--wait another 2 seconds (PAUSE_BEFORE_CLOSE)
--send ground signal to ON(which deactivates function)
--wait 1/4 of a second (OFF_DELAY)
--turn off "hold Up funtion" by sending ground signal to openPin
--wait for beam to break again (IDLE)
*/
void loop() {
switch (state) {
case IDLE:
if (beamIsBroken()) {
state = PAUSE_BEFORE_ON;
start_time = millis();
}
break;
case PAUSE_BEFORE_ON:
if (millis() - start_time >= PAUSE_BEFORE_ON_ms) {
activateButton(onPin);
state = ON_DELAY;
start_time = millis();
}
break;
case ON_DELAY:
if (millis() - start_time >= ON_DELAY_ms) {
activateButton(openPin);
state = OPEN_DELAY;
start_time = millis();
}
break;
case OPEN_DELAY:
if (millis() - start_time >= OPEN_DELAY_ms) {
deactivateButton(openPin);
deactivateButton(onPin);
state = WAIT_BEAM;
start_time = millis();
}
break;
case WAIT_BEAM:
if (!beamIsBroken()) {
state = PAUSE_BEFORE_CLOSE;
start_time = millis();
}
break;
case PAUSE_BEFORE_CLOSE:
if (beamIsBroken()) {
state = WAIT_BEAM;
start_time = millis();
}
else if (millis() - start_time >= PAUSE_BEFORE_CLOSE_ms) {
activateButton(openPin);
state = OFF_DELAY;
start_time = millis();
}
break;
case OFF_DELAY:
if (millis() - start_time >= OFF_DELAY_ms) {
deactivateButton(openPin);
state = IDLE;
start_time = millis();
}
break;
}
}
Try adding a second if statement to route the state machine back to the IDLE state in the PAUSE_BEFORE_ON state.
case PAUSE_BEFORE_ON:
if (millis() - start_time >= PAUSE_BEFORE_ON_ms) {
activateButton(onPin);
state = ON_DELAY;
start_time = millis();
}
if ( !beamIsBroken() ) {
state = IDLE;
}
break;
I am making a project that has to sense ambient light with a LDR. The idea is, that when the value of the LDR is low for 3 seconds, the led turns on. Also when the value of that LDR gets higher again, and stays high for 3 seconds, the led should turn of. This is so I can avoid that just a cloud or somebody waving over the sensor turns the led on immediately.
I know that I can use the mills() function here like in the blink without delay tutorial. But it doesn't seem to work....
this is my code so far:
#define ledPin 2
#define ldrPin A0
int daylight = 430;
int night = 150;
int ledState = 0;
int lightState = 0;
const long timeOut = 2000;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long tNow = 0;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT);
pinMode(ldrPin, INPUT);
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
tNow = millis();
int value = analogRead(ldrPin);
switch (lightState) {
case 0:
ledState = 0;
if (value <= 200 && (tNow - previousMillis) >= timeOut)
{
previousMillis = tNow;
lightState = 1;
}
break;
case 1:
ledState = 1;
if (value >= 300 && (tNow - previousMillis2) >= timeOut)
{
previousMillis2 = tNow;
lightState = 0;
}
break;
}
switch (ledState) {
case 0:
digitalWrite(ledPin, LOW);
break;
case 1:
digitalWrite(ledPin, HIGH);
break;
}
Serial.println(value);
Serial.println(ledState);
}
You could try using smoothing to read a running average from the sensor. That way you'll have a smoothed average instead of an immediate value, so a short spike (like a hand) won't change the value if you keep the window long enough.
There is a tutorial on the arduino website explaining how to do this. Basically you store multiple previous values and keep track of the average.