This is my first post after lurking and googling around for several months and being tempted to ask for help on more than one occasion but resisted and researched and tried things until I have some working code.
My project is to make an automatic shutdown for my dashcam with a variable delay that will fit in a car 12v usb adapter.
I have previously made a "hardware" system using a voltage sensing relay and 12v to 5v buck converters. It works OK but how long the camera runs after the ignition is switched off (for parking protection) is unpredictable due to the state of battery charge, short runs, cold temperatures etc and of course it runs down the car's battery -camera draws 600mA when running, base current of the hardware system is about 60mA.
In order to realize my project I will be using an attiny85 for the final deployment but testing was done on an UNO R3. A voltage divider, a MOSFET, a buck converter, an LED and a button make up the rest of the hardware.
The program works by sensing the external voltage and triggering the MOSFET when the target voltage is achieved or exceeded, the LED is lit. When the external voltage drops below the lower limit the MOSFET remains ON until the timer has expired. The timer length is determined by the number of button pushes (4 max) multiplied by the delay. When the delay has timed out the MOSFET goes OFF and set the counter to 1.
The code below works but I feel there may be a better way of doing it so I would appreciate any comments or suggestions for improvement.
const int buttonPin = 2; // the pin that the pushbutton is attached to
const int MOSFETPin = 8; // the pin that the LED is attached to
float denominator;
int resistor1 = 990;
int resistor2 = 326;
// Variables that will change:
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
void setup() {
Serial.begin(9600);
pinMode(buttonPin, INPUT); // initialize the button pin as a input:
pinMode(MOSFETPin, OUTPUT); // initialize the MOSFET as an output:
denominator = (float)resistor2 / (resistor1 + resistor2);
pinMode(1, OUTPUT);
}
void loop() {
buttonState = digitalRead(buttonPin); // read the pushbutton input pin:
if (buttonState != lastButtonState) { // compare the buttonState to its previous state
if (buttonState == HIGH) { // if the state has changed, increment the counter
buttonPushCounter++; // if the current state is HIGH then the button went from off to on:
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
if (buttonPushCounter == 5) {
buttonPushCounter = 1; // reset the counter added by me to limit time delay options
}
} else {
Serial.println("off");
}
delay(50); // Delay a little bit to avoid bouncing
}
lastButtonState = buttonState; // save the current state as the last state, for next time through the loop
float voltage;
voltage = analogRead(A3); //Obtain RAW voltage data
voltage = (voltage / 1024) * 5.0; //Convert to actual voltage (0 - 5 Vdc)
//Convert to voltage before divider
// Divide by divider = multiply
// Divide by 1/3 = multiply by 3
voltage = voltage / denominator;
//Output to serial
Serial.print("Volts: ");
Serial.println(voltage);
//Delay to make serial out readable
delay(1000);
if (voltage >= 3.9) digitalWrite(MOSFETPin, HIGH);
if (voltage <= 3.8)
{
delay(buttonPushCounter * 1000UL * 10UL);
digitalWrite(MOSFETPin, LOW);
buttonPushCounter = 1;
}
}
The comment should read "//Delay to make button unusable"
You would have to hold the button down for about 1 second because the code spends a second in this delay, not looking at the button. It also delays your system's response to the low-voltage condition because it's stuck in this delay.
Look at the tutorial on using millis() for timing. (I think it may be in the "Programming" sub-forum.) I can see three places you need to use millis:
Restricting the Serial output
Debouncing the button (look up "debounce" on your favourite search engine)
Timing the off-timer
I'll give you the solution for #1. You need to do some more reading to understand what choices are available to you for solving the other timing problems.
The problem of restricting Serial output is very common. I have a simple little block that I drop in anywhere that I need to add some Serial output to a process which is running at high speed. This code below should replace everything in your code from //Output to Serial down to the delay.
static unsigned long lastSerialPrint;
const unsigned long serialPrintInterval = 1000; //milliseconds
if(millis() - lastSerialPrint > serialPrintInterval) {
lastSerialPrint = millis();
//Output to serial
Serial.print("Volts: ");
Serial.println(voltage);
}
The keyword "static" is important. By declaring a variable here, it's local to the function. In this case, the loop() function. Normally local variables are forgotten after the function ends. But a static variable is stored and keeps its value even when the function is not running.
Thank you MorganS for taking the time to reply. Once the code is ported to the Attiny85 all the references to serial will be removed but your guidance will be most useful for my other projects. I will investigate the timing issue and substitute millis()
Depending how long it takes for your dashcam to start recording when powered up, maybe you could just cycle it every few seconds to greatly extend the coverage time.
MorganS:
This part is wrong...
The comment should read "//Delay to make button unusable"
You would have to hold the button down for about 1 second because the code spends a second in this delay, not looking at the button. It also delays your system's response to the low-voltage condition because it's stuck in this delay.
Look at the tutorial on using millis() for timing. (I think it may be in the "Programming" sub-forum.) I can see three places you need to use millis:
Restricting the Serial output
Debouncing the button (look up "debounce" on your favourite search engine)
Timing the off-timer
I'll give you the solution for #1. You need to do some more reading to understand what choices are available to you for solving the other timing problems.
The problem of restricting Serial output is very common. I have a simple little block that I drop in anywhere that I need to add some Serial output to a process which is running at high speed. This code below should replace everything in your code from //Output to Serial down to the delay.
//Output to serial
Serial.print("Volts: ");
Serial.println(voltage);
}
The keyword "static" is important. By declaring a variable here, it's local to the function. In this case, the loop() function. Normally local variables are forgotten after the function ends. But a `static` variable is stored and keeps its value even when the function is not running.
I have done a lot of looking into how I could replace the MOSFET delay off-timer with millis() but I keep running into the same problem, the off-timer is varied by the number of button presses. do I make 4 timers and call the value depending upon the buton presses ?
Been some time since I posted an update and the "tough love" in this group pays off. We must find our own solutions and hence learn. Here is the very different code I ended up with after a lot of looking and trying. I will remove all the serial code lines and rename LED to MOSFET and change the voltage thresholds before I upload this to my Attiny85.
Thanks to MorganS and the others who guided me
//Global Variables
const byte buttonPin = 2; // our button pin
const byte LED = 12; // LED
const int voltPin = A3; //Voltage divider input
// Variables will change:
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
unsigned long buttonPushedMillis; // when button was released
unsigned long ledTurnedOnAt; // when led was turned on
unsigned long turnOnDelay = 100; // wait to turn on LED
unsigned long turnOffDelay = 1000; // turn off LED after this time
unsigned long turnOffDelay_2 = 5000; // turn off LED after this time
unsigned long turnOffDelay_3 = 10000; // turn off LED after this time
bool ledReady = false; // flag for when button is let go
bool ledState = false; // for LED is on or not.
//Variables for voltage divider
float denominator;
int resistor1 = 990;
int resistor2 = 326;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
denominator = (float)resistor2 / (resistor1 + resistor2);
//Serial.begin(9600);
}
void loop() {
// get the time at the start of this loop()
unsigned long currentMillis = millis();
float voltage;
//Obtain RAW voltage data
voltage = analogRead(A3);
//Convert to actual voltage (0 - 5 Vdc)
voltage = (voltage / 1024) * 5.0;
//Convert to voltage before divider
// Divide by divider = multiply
// Divide by 1/3 = multiply by 3
voltage = voltage / denominator;
if (voltage >= 3.9) digitalWrite(LED, HIGH);
//---------------------
// read the pushbutton input pin:
buttonState = digitalRead(buttonPin);
//---------------------
// check the button
if (digitalRead(buttonPin) == LOW) {
// update the time when button was pushed
buttonPushedMillis = currentMillis;
ledReady = true;
}
//---------------------
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is HIGH then the button went from off to on:
buttonPushCounter++;
if (buttonPushCounter == 4) {
buttonPushCounter = 0; // reset the counter added by me to limit time delay options
}
// Serial.print("Volts: ");
// Serial.println(voltage);
// Serial.println("on");
// Serial.print("number of button pushes: ");
// Serial.println(buttonPushCounter);
} else {
// if the current state is LOW then the button went from on to off:
// Serial.println("off");
}
// Delay a little for serial print
//delay(50);
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
//---------------------
// make sure this code isn't checked until after button has been let go
if (ledReady)
{
//this is typical millis code here:
if ((unsigned long)(currentMillis - buttonPushedMillis) >= turnOnDelay) {
// okay, enough time has passed since the button was let go.
digitalWrite(LED, HIGH);
// setup our next "state"
ledState = true;
// save when the LED turned on
ledTurnedOnAt = currentMillis;
// wait for next button press
ledReady = false;
}
}
// see if we are watching for the time to turn off LED
if (ledState && buttonPushCounter == 1 && voltage <= 3.8) {
// okay, led on, check for now long
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay) {
ledState = false;
digitalWrite(LED, LOW);
}
}
else if (ledState && buttonPushCounter == 2 && voltage <= 3.8) {
// okay, led on, check for now long
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay_2) {
ledState = false;
digitalWrite(LED, LOW);
}
}
else if (ledState && buttonPushCounter == 3 && voltage <= 3.8) {
// okay, led on, check for now long
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay_3) {
ledState = false;
digitalWrite(LED, LOW);
}
}
else if (voltage <= 3.8)
{
digitalWrite(LED, LOW);
}
}