I guess it's time to share

A friend of mine had this little hydroponic nursery that was being run by mechanical timers. For temperature monitoring he had a thermometer taped inside. One of his main problems was that the timers have only a 15 minute resolution. He wanted to run some things for shorter times. I figured it was a job for Arduino which I had no experience with but I had heard of because I'm an open source supporter.

So I did my research and ordered up a bunch of stuff from seeed studios. An Uno and a bunch of the Grove stuff including the Grove base shield. So this Uno is loaded up with; two switches, two DHT22 sensors, one analogue temp sensor, four relays, a serial LCD (16x2) display, an SD card shield and a real time clock with battery.

Like everyone else I had to walk before I could try running so I made the LED on pin 13 blink. Then I poked at each of my devices to get them working.

Inside the unit is the analogue temp and a DHT22. The other DHT22 sits outside for ambient air readings. The LCD displays the time and real time temps and humidity as well as the highest and lowest values recorded. Which are scrolled through with a button. The LCD backlight can be turned on and off with the other button and it also times out and auto shuts off. The LCD backlight blinks if an alarm condition is met.

The relays can have four different scheduled events to run during a day. They can only be programmed to a minute, which is fine for this purpose. Any relay can be overridden if needed such as for thermostatic control of the vent fan. I wanted to be able to program the relays via the LCD screen and switches but basically have no program space left. So the relay programming is uploaded with the sketch. As part of the function is to properly time relays it is possible to set the clock via the switches and this routine will time out and quit if their is no user input. That keeps it from being left part way through setting the clock and not being able to do anything waiting for user input. Hopefully the battery backed RTC means never having to do this.

The big advantage of this system is that it logs data so that he can get a good idea of what is really happening in there. Then we can fine tune the cooling and alarm system.

I did do battle with running out of RAM and it currently uses about 31k of program space. So it's pretty much full. I see a couple of places that need some tweaking like bounds checking in the function that programs the relays. When the hardware is all installed into its new home I'll get some pics.

I've attached the code for this as well as a utility that I wrote up that can turn relays on and off and dump the datafile via serial.

Hopefully someone can find something useful in there. I'd also be happy to hear some criticisms of my code. Bear in mind that I code as a fun hobby, not a living, so go easy on me.

sketchbook-2012-08-28.zip (12.9 KB)

Nice project.

As an exercise to regain memory, maybe you could try removing all the "OO" layering ?
I'm not sure how efficient the compiler does that, but if you don't define buttons and relays etc. as objects,
but simply as functions, it should spare memory space.

Or am I just an ignorant of modern C++ compilers ? (which I am :wink: )

In your button function/object, you don't need to check twice for "if(digitalRead(_pin1) == LOW)" and you don't need to repeat debounce calls (should be const BTW), but that won't spare much. Probably moving all stuff into buttons.ino will bring a sensible memory gain.

My 2 cents, I don't program much either.

Nice project indeed

tochinet:
As an exercise to regain memory, maybe you could try removing all the "OO" layering ?
I'm not sure how efficient the compiler does that, but if you don't define buttons and relays etc. as objects,
but simply as functions, it should spare memory space.

Or am I just an ignorant of modern C++ compilers ? (which I am :wink: )

Can you point out any official source which states that is the case? My google search "object oriented programming and memory usage" did not surface anything.
I ask because I know how a C++ compiler works and there is indeed a memory overhead (due to the indirection table of the object) when using virtual functions. However you will have a overhead as well when solving the same problem in a functional way.
I agree it is a bad way to use dynamic memory management on a small chip because there is simply not enough memory management capability on board.
But you can uses dynamic memory in both OO and functional programming. (In other words don't use new.)
Please point me to a good source that proves your point so I can educate myself.

Best regards
Jantje

Can you point out any official source which states that is the case? My google search "object oriented programming and memory usage" did not surface anything.

No. You probably know better than me. I don't want to "prove" anything, just to help. Maybe "OO overhead" would bring you better results ? (like "Measuring the Perceived Overhead Imposed by Object-Oriented Programming in a Real-time Embedded System")

Did you look at the project of Jimmy ? He defines a class myButtonClass with only one instance and one method check() that give 0-1-2-3 in function of key press, then he uses that in one place (buttons.ino) with a case: iteration.

My gut feeling tells me this is serious OO overkill, and it's repeated logic. Of course, function indirection also brings overhead both in PROGMEM and RAM. You can just test the buttons in loop() to spare even more memory, as most functions appear to be used only once.

Or will the compiler detect that ? Same thing for the "debounce", can the compiler see it's a const or do we need to tell ?

Thinking of it, replacing the functions by macros may give the same win without breaking the files structure. But I'm not sure that's easy to implement.

tochinet
I hadn't looked at the code. I took a quick look and my conclusion is as follows

  1. I don't know the intention of the mybutton class and how it is setup. It may be that it is reused or is foreseen for future expansion. From what I know there is no reason to call it good or bad.
  2. I see plenty of
Serial.println("# probably doesn't exist");

If you want to run out of RAM that is what you need to do.
replacing all these by

Serial.println(F("# probably doesn't exist"));

will have a far more impact on memory usage than any code change without any risk of regression.

Best regards
Jantje

@Jantje,

Sure strings can be used from Flash. And maybe other things can be PROGMEMed.
But will this help in this particular case, as the bottleneck is the 32K memory of the mega328 ?

Another direction is to question the real need for all libraries : the included libraries probably eat up already a large part of(program) memory : SD, Wire, SoftwareSerial, SerialLCD, floating math for temperatures, etc.

In all honestly I may have got carried away with the OO layers as it was new to me. I had only tackled classes for the first time during this project. So a library like two buttons was done partly as an experiment and partly to get another piece of working code out of the way. I actually noticed virtually no change in memory usage either way. I had it mostly working before I started using the OO approach and for the most part OO actually saved me a bunch of program space. That could just be that the code was more efficient. It could be apples to oranges. RAM is a non-issue now. It runs very stable and has been run continuously for many days for testing.

I did find while playing around that having stuff in a library or in the main sketch had no effect on program space as long as the code was the same.

The button reading may seem odd as it checks the other value each time. I had trouble with it reliably detecting both buttons being pushed otherwise. I should play with the debouncing. I'm not sure about it either, it looks funny. It works well though.

It's become obvious to me that the next version of this will be on a mega. We want to add a few more sensors and detectors.

Thanks for having a look. I really appreciate it.

@jantje: the official source is of course gcc. With regard to performance issues there is nothing like X always good, Y always bad. The only rule that stays is: look and see. That is you have to measure.

With regard to the OO overhead: IMHO the overhead is negligible on most "real" computers because of the improved abstraction. But then most of the time you want to use languages that are even further away from the hardware because you want the increased abstraction. But on the Arduino with its tight constraints the overhead is there and the impact is clear. If you do not believe it just implement whatever you want and then replace all Arduino out of the box OO constructs with their equivalents from avr-libc and compare the generated assembler code.

The most notorious example here is the string class.

tochinet
Just to make my point about moving data to program space I took all the Serial.print(ln) from the utility001.ino from the zip file from the first post and put them in the loop() of a sketch.
Which gives met this code (with the F)

void setup() {
}


void loop() {
  // put your main code here, to run repeatedly:
  Serial.print(F("# Initializing SD card..."));
    Serial.println(F("Card failed, or not present"));
  Serial.println(F("card initialized."));
    Serial.println(F("# Toggle Relay 1 - VENT"));
    Serial.println(F("# Toggle Relay 2 - LIGHT"));
    Serial.println(F("# Toggle Relay 3 - PUMP"));
    Serial.println(F("# Toggle Relay 4 - AERATE"));
    Serial.println(F("# Delete Data File"));
    Serial.println(F("# Dump Data File"));
    Serial.println(F("# error opening file"));
    Serial.println(F("# probably doesn't exist"));
Serial.println(F("# Removing your file..."));
}

compiling in GCC without F gives me

Program:    4814 bytes (14.7% Full)
(.text + .data + .bootloader)

Data:        433 bytes (16.9% Full)
(.data + .bss + .noinit)

Compiling with F gives me

Program:    4838 bytes (14.8% Full)
(.text + .data + .bootloader)

Data:        155 bytes (6.1% Full)
(.data + .bss + .noinit)

So using F in this small program saves roughly 10% of data space. You will have to do some really bad programming in OO before you will waist the same amount of Data space. That is and was my point.

Udo Klein
I think we are on the same page. As to the String class. I try to avoid it. It is really unwise to use it for 1 single line of code. But; when I'm building a string intensive application (for instance a web server) I prefer using the string class to writing lots of char array functions. I'm even convinced that "in real life" using the String class in such case may result in less data space usage. This because there will be less code when using the String class and as such less opportunities to waste Data space.

Jimmy60

Jimmy60:
I did find while playing around that having stuff in a library or in the main sketch had no effect on program space as long as the code was the same.

This is a 100% correct observation. From a compile perspective the Arduino IDE does not make a distinction between the code and a library. The only difference is that the code is compiled before the library to increase the chance of finding compilation errors soon.

Best regards
Jantje

I did a project similar to this and I would recommend you offload the logic (scheduling times, etc) and user interface (the LCD) to another machine.

Reduce the code you have to listen and send over serial and allow another computer to tell it what to do (get temperature, turn on vent relay, pump relay, lights, etc). Then you can get rid of the RTC and SD on top of the LCD. Since another machine could read, store, display and do all that.

For the other machine, I'd recommend checking out OpenWRT Linux which works very well with the Lua language and makes it easy to talk to an Arduino. Raspberry Pi is another option. The point there is you'll have much more control over the UI, you could plug it into the internet and then control all this remotely from a webpage or whatever; that would otherwise be very difficult on the Arduino by itself.

Unless you really really enjoy squeezing as much as you can out of the Arduino, I think it'll get old and frustrating quickly as you want to add more features.

If you want to avoid Linux though or a separate machine, you could use a more powerful ARM CPU. I'd highly recommend the mbed platform for this (http://mbed.org/) as it's very nice to use and easy to program, plus has a lot more flash storage for you to use.

That's an interesting idea but not that practical in this application. There is also no internet access at the location. I might go this way down the road though depending on what we think it may need.

It's actually working fine and has been doing it's job perfectly for the last week.

I have made some improvements and added some features. I was able to get rid of my DHTpro class which I never really liked. It felt like I reinvented the wheel and just stuck it to another wheel. What I did was modify the DHT library to my needs. Not sure why I didn't think of that before but it only took 15 minutes to adapt.

I was also able to add a feature to the relay library that allows me to protect a relay from bumping. I noticed that the vent fan was switching on and off as it went below it's temperature threshold. Now through a public variable a delay time can be set that will keep a relay from turning on too soon after turning off. To me, this was the real advantage of writing a class for my relays.

I was also able to include the utility to dump the data from the SD card using serial commands. I can delete the file as well.

For a guy complaining about running out of program space I managed to add quite a bit. XD

I added an overheat procedure that if it detects a certain threshold will turn off the light, run the fan and check for the temp dropping. If it isn't cooling after a certain time it throws an error message on the LCD and stops running.

One of the things that was troubling me and I was struggling with a solution was to protect against a hot start. If the power went out for several hours this is a non-issue but if it goes out for a few second or minutes it might try to hot start the light. The light doesn't really like that. I kept approaching from the angle of detecting if a power failure had occurred. Then I realized that "hot start" was the answer. I just had to read the temperature near the light during setup. If it's hot the power probably bumped. It then calls a procedure that cools it down or waits a certain time then continues on.

I would never avoid Linux, it's the other ones I avoid. XD

sketchbook.rar (20 KB)