Willing to pay for a simple "single-held-button-time-counter"

Here's my simple project that's in need of better code:

I have:

  • A NodeMCU
  • A Simple ON/OFF Momentary Push Button
  • A 64x48 OLED Display

I already have everything wired and using this library the screen is correctly displaying this example sketch

What I need, is for the display to show 5 lines:

  • Total Time the board has been powered on, shown as: "UP: 99h:99m"
  • Total time the button has been held since power up, as: "ON: 99h:99m"
  • Total time button has been released since power up, as "OFF: 99h:99m"
  • Time since last button push/hold was released, as "IDLE: 99h:99m"
  • A Percentage of time button has been held out of total time unit has been on, as "99% ON"

I know this may seem simple, and I hope it is for you, but I'm a hardware guy and have no idea where to start. But I'm willing to PayPal someone ~$10 for this.

A few notes:

  • The unit will be powered on and "counting/timing" ~6 hours every day, and then unplugged for the night.
  • Displaying HR:MIN is a minimum, but HR:MIN:SEC would be nice.
  • Time doesn't have to be terribly precise, but at least -or+ 10min of drift over the day.
  • +$10 if you add the NodeMCU's wifi library and have its IP serve up the same 5 lines on a simple HTML page
  • +$5 if you can make same HTML page show previous 5 days final percentages (don't know if this will survive nightly power cycles...?)

Anywho, I hope I've explained that simply enough,
let me know if you need anymore information,
and thanks for any and all help in advance!

What does your code currently do?

The state change detection example shows how to determine when the switch has become pressed, or has become released, so timing how long the switch is pressed is quite straightforward.

Converting the value from millis(), up time in milliseconds, to hours, minutes, and seconds is 3rd grade math.

Should the ON time increment while the switch is pressed, or only when the switch becomes released?

Converting the value from millis() - lastRelease, idle time in milliseconds, to hours, minutes, and seconds is 3rd grade math.

PaulS:
What does your code currently do?

Lol I currently don't have any code =\

Should the ON time increment while the switch is pressed, or only when the switch becomes released?

Yes it should increment while the switch is pressed.

...quite straightforward...3rd grade math...

I know this may seem trivial, but I honestly have no clue how to pull this off,
and was really hoping someone would just paste code and I'd send them some cash,
perhaps I'm not offering enough money? Idk...
Heck I'd even pay the first two or three sketches submitted just to compare different implementations...

Here's my simple project that's in need of better code:

Lol I currently don't have any code =\

So, better means "anything more than nothing".

void setup()
{
}

void loop()
{
}

There you go. That's better than nothing. You owe me $10. 8)

void loop()
{
   unsigned long upTime = millis(); // up time is in milliseconds

   unsigned long upSeconds = upTime/1000; // up time is now in seconds

   unsigned long upHours = upSeconds / 3600; // get the up time in hours

   upSeconds -= upHours * 3600; // subtract whole hours

   unsigned long upMinutes = upSeconds / 60; // Get the minutes from the seconds

   upSeconds -= upMinutes * 60; // subtract whole minutes

   DisplayUpTime(upHours, upMinutes, upSeconds);

Now, all you have to do is write the DisplayUpTime() function to display the up time on the hardware you have, where you want it.

Create some global variables to hold when the switch becomes pressed and when it becomes released. Look at the state change detection example, and see if you can determine when the set each one. Using this loop() function snippet as a starting point, post your best attempt to properly populate those two global variables, and we'll show you how to get pressedTime and idleTime from them.

Welp,
I've read and read, pieced together some random snippets of code from examples,
and I've got the counting working correctly, along with button functionality, and even the display.

But I'm getting random freezes and restarts... =\

Oh, but the "button" is now a SW-420 Vibration sensor on an interrupt pin.
Could that cause the freezing?

My Sketch so far:

// Include Libraries for OLED
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 display(OLED_RESET);

#define led_pin 2
  char Upmsg[15] = "";
  char Onmsg[15] = "";
  char Offmsg[15] = "";
  char Idlemsg[15] = "";
  struct Time{
  String msg;
  unsigned char hours;
  unsigned char minutes;
  unsigned char seconds;
};


Time Up,On,Off,Idle;
unsigned char s, button, idle_flag;
unsigned int percentage;
void setup() {
  Up = {" ",0,0};
  On = {" ", 0,0};
  Off = {" ", 0,0};
  Idle = {" ", 0,0};
  s = 0;
  idle_flag = 0;
  Serial.begin(115200);
  pinMode(led_pin, OUTPUT);
  pinMode(button_pin,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(14), IntCallback, CHANGE);

    // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 64x48)
  // init done

  // Clear the buffer and Initialize Display.
  display.clearDisplay();
  display.display();  
  
  // text display setup
  display.setTextSize(1);
  display.setTextColor(WHITE);
}

void loop() {
//  if (digitalRead(button_pin)==1){button = 1;}
  delay(1000);
  s++;
//  if (digitalRead(button_pin)==1){button = 1;}
  s = 0;
  print_time();
  update_time();
}


void update_time(){
  Up.seconds = Up.seconds + 1; // Update after every minute
  if (Up.seconds>=60){
    Up.seconds = 0;
    Up.minutes = Up.minutes + 1;
  }
    if (Up.minutes>=60){
    Up.minutes = 0;
    Up.hours = Up.hours + 1;
  }
  
  // Updating on/off
  if(button == 1){
    digitalWrite(led_pin, LOW); //LED Shows button if button is pressed
    button = 0;
    idle_flag = 0;
    On.seconds = On.seconds + 1; // Update after every minute
    if (On.seconds>=60){
      On.seconds = 0;
      On.minutes = On.minutes + 1;
      }
      if (On.minutes>=60){
      On.minutes = 0;
      On.hours = On.hours + 1;
      }
  }
  else{
    digitalWrite(led_pin, HIGH);
    idle_flag  = 1; //
    Off.seconds = Off.seconds + 1; // Update after every minute
    if (Off.seconds>=60){
      Off.seconds = 0;
      Off.minutes = Off.minutes + 1;
      }
      if (Off.minutes>=60){
      Off.minutes = 0;
      Off.hours = Off.hours + 1;
      }
  }

  if(idle_flag == 1){
    Idle.seconds = Idle.seconds + 1; // Update after every minute
    if (Idle.seconds>=60){
      Idle.seconds = 0;
      Idle.minutes = Idle.minutes + 1;
      }
      if (Idle.minutes>=60){
      Idle.minutes = 0;
      Idle.hours = Idle.hours + 1;
      }
  }
  else{
    Idle.seconds = 0;
    Idle.minutes = 0;
    Idle.hours = 0;
  }
 
  sprintf(Upmsg, "UP %01d:%02d:%02d", Up.hours, Up.minutes, Up.seconds);
  sprintf(Onmsg, "ON %01d:%02d:%02d", On.hours, On.minutes, On.seconds);
  sprintf(Offmsg, "OF %01d:%02d:%02d", Off.hours, Off.minutes, Off.seconds);
  sprintf(Idlemsg, "ID %01d:%02d:%02d", Idle.hours, Idle.minutes, Idle.seconds);
  calculate_percentage();
}

void print_time(){
  display.clearDisplay(); //Display buffer is persistant so we must clear it each loop
  display.setCursor(0,0); //Return to top line
  display.println(Upmsg);
  display.println(Onmsg);
  display.println(Offmsg);
  display.println(Idlemsg);
  display.println();   //Drop down a line
  display.print(String(percentage)+" Percent");
  display.display();  //Draw buffer on screen

}

void calculate_percentage(){
  percentage = (unsigned int) (((On.minutes*60 + On.seconds)*100) / (Up.minutes*60+ Up.seconds));
}

void IntCallback(){
 button = 1;
}

megodinero:
But I'm getting random freezes and restarts... =\

NodeMCU - so let me guess: you getting wdt timeout errors?

Do allow for background processes to run - specifically it's networking stack. Any long routines must have yield() calls in them. Do read up on how to program the ESP8266, and get the "ESP exception decode" add-on for the Arduino IDE so you can actually make sense of those stack traces. That's another great help in tracking down the cause of crashes.

  sprintf(Upmsg, "UP %01d:%02d:%02d", Up.hours, Up.minutes, Up.seconds);

You want a one character field, with leading 0s as needed. Just how many leading 0s might be needed to pad the field to one character?

You use button in loop() and in the (stupidly named) IntCallback() function, which is an interrupt service routine. Variables shared between ISRs and other function MUST be declared volatile.

Your ISR is triggered when the pin changes state, and yet you don't care, in the ISR, what state the pin is now in.

What, EXACTLY, are you trying to measure?

PaulS:

  sprintf(Upmsg, "UP %01d:%02d:%02d", Up.hours, Up.minutes, Up.seconds);

You want a one character field, with leading 0s as needed. Just how many leading 0s might be needed to pad the field to one character?

I found this piece of code in a clock example and used it to fix my character formatting to show as h:mm:ss

PaulS:
You use button in loop() and in the (stupidly named) IntCallback() function, which is an interrupt service routine. Variables shared between ISRs and other function MUST be declared volatile.

Your ISR is triggered when the pin changes state, and yet you don't care, in the ISR, what state the pin is now in.

As stated above I pieced together some random snippets of code from examples, including one that used the IntCallback() function, but as I said in my first post I have no idea what I'm doing. So much so that, as I said before, I am even willing to pay a professional to do it for me correctly.

I'm kinda stumbling in the dark, reading for myself to try and grasp the concepts, while also pulling from other peoples examples on how to implement them. I'm trying lol, but also learning. And I think/hope I'm like 80% of the way there, but I'm also grateful for any guidance yourself or others can share along the way...

As for now I'm about to go look up what a volatile declaration is and how to use it properly lol.

PaulS:
What, EXACTLY, are you trying to measure?

Simply put; the total times for vibrations that periodically start and stop.

megodinero:
A few notes:

  • Time doesn't have to be terribly precise, but at least -or+ 10min of drift over the day.

This comes down to how accurate the NodeMCU oscillator is. It's probably quite a bit better than the resonator in the UNO, but you will still want to look at the spec sheet to be sure. 10 minutes over 24 hours is .6%, so the MCU will need to be more accurate than that.

If it isn't, then you'll need a RTC module of some sort, which will make the sketch quite a bit more complicated.

What, EXACTLY, are you trying to measure?

Simply put; the total times for vibrations that periodically start and stop.

Woah! So, is this button of yours going to vibrate? How fast? Because that changes things. Did you want to measure the total time that the button is down, or did you want to measure the overall time of vibration? That's two different things.