I have a simple program that is monitoring my home A/C system. I'm using millis() to calculate the amount of time that the A/C system is on (read through an Arduino pin). I calculate how many days/hours/minutes since Arduino reset and the amount of time that the Arduino pin is "on". I output both of these values, along with a calculated load percentage to a 2 line LCD. My end product looks something like:
A/C 1:04:24 20%
The above means I've had the arduino monitor for 5 days, 21 hours, 13 minutes and the A/C has been on for 1 day, etc and that means about 20% duty cycle. All works well.
Now, I'd like to also display the same A/C on information for the last 24 hours. Not since midnight (or some other time) the previous day - I think I could manage that, haha - but since 24 hours ago. So I don't need a "Tot[all]" line in my readout as that is obviously 24 hours. How do I do this? I think that it is a tough problem if I want it to be instantaneous - meaning I want a 24 hour window that is updated every second - but what if I want every minute or hour?
My thoughts so far --
I'm familiar with the sensor running average (averaging 10,20 of the last readings in array) code, but not sure if that's best here. For instance, if I wanna update my average every 1 hour, I'd need a 24 element array, but each element is not on/off because the a/c will cycle during that period so I'd need to have a a/c on period recorded for each hour, then save 24 hours, and average the array. That is complicated, but doable. But every minute? If I'm doing that it's 24*60 element array. I probably don't need to save the on time to the array (I can record that for every minute element the a/c is either on or off) and have an array of boolean values, but it's a big array. This method simplifies my recording because I just check whether the system is on or off at the beginning of every minute and place the value in the array.
Help - I am a newbie, I'd really like a nudge/start from someone but would enjoy completely solving/coding this myself.
You will not be able to store “per second” readings for the last 24 hours, “per minute” readings would require 180 bytes of memory - if you are familiar with bit manipulation.
As I see it, the task is simple and it requires the use of a “circular buffer”. It is basically an array with room for the last 24 hours worth of data and then you only need to know index and size of the buffer.
#define BUFFER_SIZE 100
uint8_t buffer_i = 0; //Index of next element
uint8_t buffer_n = 0; //Number of elements
void circular_buffer_put(uint8_t value)
//Store the value and increment index
buffer[buffer_i++] = value;
//Make sure that number of elements is not less than the index
if (buffer_n < buffer_i) buffer_n = buffer_i;
//If index is beyond end of buffer, restart from 0
if (buffer_i == BUFFER_SIZE) buffer_i = 0;
//Make sure datatype is large enough to hold maxval(uint8_t)*BUFFER_SIZE
uint16_t result = 0;
//Get the sum of the buffer
for (uint8_t i = 0; i < buffer_n; i++) result += buffer[i];
//Return the average
return result / buffer_n;
You could store the “per second” value in a different buffer and use the average of that buffer to determine whether the minute was more on than off and store that as “per minute” value.
Thanks so much for the reply. I’ve been thinking about this more but haven’t coded anything. I understand exactly your reply, but after thinking about this more, I think there is a better way. I know that there are a small (relative to the number of seconds) number of on/off cycles through the last 24 hours. Let’s say 5 per hour (which is still probably high). If I keep a record of all of those I’d have about 125 on and 125 off readings. Now I just look at my array and calculate those to determine my ‘on’ time in last 24 hours. I’d have to handle the situation of it already being on either at the beginning of the time or end, but that’s doable. Also, we wouldn’t know how many actual on or offs, so it’d work differently than the simpler smoothing method, but that’s what makes it a challenging problem. Does this make sense?
I don’t need you to put this into code for me, I’d just like someone to say - yeah, I see that can work or no, there’s something you’re not considering.
If you know that there are a maximum of X counts of on/off cycles per hour and that each cycle is exactely Y amount of time, then your method would work. If the cycle time varies, it will not be accurate.
Think about this for a minute. Do you really need up-to-the-millisecond data on your AC? What are you really trying to do here?
Write this the same way you have now to store data in 24 one hour blocks instead. So for any given hour you know how many minutes the AC was on. At the top of each hour you can take out one reading and replace it with a new one and have a rolling 24 hours worth of data.
If that's not granular enough then break it into like 15 minute blocks. If that's not granular enough to see whatever you want to see then you really need to work on the thermostat. You AC shouldn't be cycling that quickly.
Right, but you want the system to be accurate detailed enough so you see when it is not behaving well, so you kinda have to collect a bit more data than if was always working correctly. That’s kinda the point.
I like the hourly table, throw away the oldest hour which is also the circukar buffer idea.
When the AC goes on, start a timer. When it goes off, figure out and accumulate ON time, and increment the “number of cycles” counter. The time accumulator and cycle counter would be stored and cleared for the next period, here one hour. Raises some details about how to handle crossing the hour boundary.
Is there an RTC in this setup? You don’t need one unless you need one, I mean it can be a bit less than WWV accurate, and certainly you could figure out if you crystal was slow a bit or fast.
Delta_G gave the simplest solution to the problem, in partially pseudo code:
#define BUFFER_HOURS 24 //Number of hours to remember
uint8_t index = 0;
uint8_t minutes = 0;
uint16_t result = 0;
for (uint8_t i = 0; i < BUFFER_HOURS; i++) result += buffer[i];
memset(&buffer, 0, BUFFER_HOURS);
if (elapsed_since_last_reading >= one_minute)
buffer[index] += is_ac_on();
if (++minutes == 60)
minutes = 0;
if (++index == HOURS) index = 0;
display.print("24 hours = ");
Thanks for all of your input.
To answer your questions -
A/C is not too frequently cycling
I'm not concerned about getting precise data (I mean this isn't for a study or to really solve a problem). I am looking for an interesting challenge in logic/coding.
I really understand the suggestions and am awed/amazed that you guys are out there and happy to lend thoughts, ideas. Thanks.
I'm still thinking there's a different (there always is) way that could be simpler. I really just need to know if the unit was on exactly 24 hours ago. Then, I need to know if the unit is on right now. Every second I compare those two and either add a second to total, subtract a second from total, or leave the total the same. Isn't that right? Of course, I don't start doing that until I've been already been recording for 24 hours and have a total for the first 24 hour period. That sounds simple, I think the most complicated part is storing the on/offs so that I can know what my state was 24 hours ago. This method is only practical if there are a small (relative) number of on/off cycles to record.
All of this is to say - I think it's most practical (but not memory efficient, which doesn't matter in this case) to just store minute readings as per Danois suggestion, but think it might be fun to try a more memory efficient, and super accurate method (if mine will work).
Thanks again for suggestions, I think I'm ready to play around with info I have.
The only issue with that solution is that you need a time stamp for each time the unit turns on or off. Then when you calculate you just go through all the time stamps, throw out the ones that are too old, and calculate on time from the rest.
The problem is the question of how many time stamps you need. Will it be enough? Do you have enough memory to make it more?