Go Down

Topic: Code share: some of my util classes (Read 2 times) previous topic - next topic

obiwanjacobi

I am a C# programmer and am used to work with classes and Object Orientation (OO).
So I created some simple classes to encapsulate some essentials.

I've attached a .zip file called Libraries. Unzip it in your Arduino folder in My Documents. It contains 3 mini libs: AnalogInput, DigitalInput and DigitalOutput.

Please read the header files for more information on the specific classes.

I find them useful and hope others will too.

Enjoy,
Marc Jacobi
Netherlands.

robtillaart

I like the idea of a flag that the value has changed.

idea:
I am missing code to enable the internal pull up resistor

Maybe you can explain the rationale behind the details of these classes ?

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

obiwanjacobi

It is a work in progress. I build what I need but make sure I can extend later.
BTW: there is a method enableInternalPullup on the DigitalInput class ;)

I've created these classes as an exercise (in Arduino dependency management ;-) ) and because I don't like all the static and sequential stuff that seems to be the norm. I understand that it is convenient for beginners, but I am an OO programmer 8) and like it when I don't have to keep all the low level details in my head  XD I truly believe that you get better code if you use OO responsibly. But I also understand that it is not the only variable on a platform like the Arduino. That is why I want to learn a bit more on code optimization first, before getting too far ahead of myself. :D

Also being a beginner in experimenting with the Arduino (although I had previous experience with other types of MCU's) I quickly found that I was repeating myself (in code). To me, that is always a sign 'something needs to be done'. This is my first pass at it.

I hope more people will see its value and find these classes useful.

robtillaart

Quote
BTW: there is a method enableInternalPullup on the DigitalInput class

Sorry, I missed it in the quick look

Quote
I truly believe that you get better code if you use OO responsibly

But allways remember that OO is a tool not the goal itself. And any technique should be used responsibly.

Quote
all the static and sequential stuff that seems to be the norm.

Not necessary the norm but sometimes the only way to get the functionality needed packed in the memory available.

That all said, keep posting your work as it is allways inspiring to see other peoples code.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

obiwanjacobi


Not necessary the norm but sometimes the only way to get the functionality needed packed in the memory available.


Can you elaborate on that? I have no idea (yet) on how efficient the compiler packs down the code and how smart it is in optimizing.
What I saw was staticly defined class instances just so the MyLib.someFunction (like SPI.write or whatever) syntax can be used. Is the compiler smart enough not to package up unused code? Otherwise if you don't use those statics (because you need more than one instance?) they will only take up  memory space.

mromani

This brings me to a question I had in my mind since I first posted SimpleTimer on the Playground... what's the best place to share some Arduino code ? Playground seems a bit too "official" sometimes, while libraries posted here in the forum are quickly buried to oblivion due to the sheer amount of activity going on.... The github idea is good IMHO, but it seems the staff doesn't have enough time to bring it forward.
I think some improvement is needed in this area.

I apologize with the OP for hijacking his thread ;-P I'll have a look at the libraries ASAP.

mromani


Is the compiler smart enough not to package up unused code?


I think unreferenced code is stripped by the linker. Therefore you get into the final .hex file only the code that is actually called (to be precise, that appears to be called at compile time - there's no run-time analysis of dead codepaths, of course).

obiwanjacobi

I dont get why an empty program -with just an empty setup and loop method- is still 466 bytes long!?

:smiley-eek:

mromani

Here's main.cpp from Arduino 1.0:

Code: [Select]
#include <Arduino.h>

int main(void)
{
init();

#if defined(USBCON)
USB.attach();
#endif

setup();
   
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
       
return 0;
}


At least setup() and init() are called, even on an "empry" sketch, therefore some code has to be compiled in.

obiwanjacobi

#9
Feb 23, 2012, 07:51 pm Last Edit: Feb 23, 2012, 07:54 pm by obiwanjacobi Reason: 1
Its not complete or done, by any means, I'm just playing around with this...

Debug.h
Code: [Select]

#ifndef _Debug_h_
#define _Debug_h_

#include <Stream.h>

#ifdef DEBUG

class Debug
{
public:
Debug(Stream* output) { _output = output; }
inline bool inDebug() { return true; }
inline void writeLine(const char* string) { _output->println(string); }
inline void assert(bool condition, const char* description)
{
if(condition) return;

_output->print("WARNING: Debug assertion failed: ");
_output->println(description);
_output->print(__FILE__);
_output->print(" (");
_output->print(__LINE__);
_output->print("):");
_output->println(__func__);
}
private:
Stream* _output;
};

#else

class Debug
{
public:
Debug(Stream* output) {}
inline bool inDebug() { return false; }
inline void writeLine(const char* string) {}
inline void assert(bool condition, const char* description) {}
};

#endif //DEBUG

#endif //_Debug_h_


I'm still struggling to get the __FILE__ and __LINE__ to work... ;)

usage:
Code: [Select]

#define DEBUG // comment out for Release build.

#include "Debug.h"

Debug debug(&Serial);

void setup()
{
if(debug.inDebug())
{
    Serial.begin(9600);
}
}

void loop()
{

    debug.writeLine("inside loop");
    delay(1000);

    debug.assert(false==true, "Test Assertion");

}


Constructive criticism is appreciated.
Hope you like it.

robtillaart


Quote
I dont get why an empty program -with just an empty setup and loop method- is still 466 bytes long!?


that are two functions, called from main() - C:\Program Files (x86)\arduino-0022\hardware\arduino\cores\arduino\main.cpp

Code: [Select]
#include <WProgram.h>

int main(void)
{
init();

setup();
   
for (;;)
loop();
       
return 0;
}


SO it does more than the eye can see (directly)


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

yadoo86


obiwanjacobi

Thanx! I hope it will lead to new ideas  ;)

obiwanjacobi


I think unreferenced code is stripped by the linker. Therefore you get into the final .hex file only the code that is actually called (to be precise, that appears to be called at compile time - there's no run-time analysis of dead codepaths, of course).


I was reading up on the underlying avr toolset and libraries and came across this page:
http://www.nongnu.org/avr-libc/user-manual/library.html

At the "How the linker works" section it describes how the linker uses complete object files to decide what to include and what not. So only if you write each function in a separate file, it cannot get optimized even when it is not in use. There goes the class file  =(

obiwanjacobi

#14
Mar 03, 2012, 03:00 pm Last Edit: Mar 03, 2012, 03:17 pm by obiwanjacobi Reason: 1
BitArray.h

Code: [Select]

#ifndef __BITARRAY_H__
#define __BITARRAY_H__

#include <Arduino.h>

// The BitArray class stores bits in one or more bytes.
// This class can be used for maintaining boolean flags in a memory effecient way.
// A normal boolean takes up a whole byte.
// T is an (unsigned) integer datatype.
template<typename T>
class BitArray
{
public:
BitArray(T initialValues)
{
_bits = initialValues;
}

bool Set(byte bitIndex, bool value)
{
if (bitIndex > getMaxBits()) return false;

T mask = 1 << bitIndex;
// clear bit
_bits &= ~mask;

if (value)
{
// set bit
_bits |= mask;
}

return true;
}

bool IsTrue(byte bitIndex)
{
if (bitIndex > getMaxBits()) return false;

T mask = 1 << bitIndex;
return ((_bits & mask) > 0);
}

bool IsFalse(byte bitIndex)
{
if (bitIndex > getMaxBits()) return false;

T mask = 1 << bitIndex;
return ((_bits & mask) == 0);
}

byte getMaxBits()
{
return (sizeof(T) * 8);
}

bool operator[] (byte bitIndex)
{
return IsTrue(bitIndex);
}

operator T()
{
return _bits;
}

private:
T _bits;
};

#endif //__BITARRAY_H__


usage:

Code: [Select]

#include <HardwareSerial.h>

#include "BitArray.h"

BitArray<byte> bits(0);

void setup()
{
Serial.begin(115200);

bits.Set(0, false);
bits.Set(1, false);
bits.Set(2, true);
bits.Set(3, false);
bits.Set(4, false);
bits.Set(5, false);
bits.Set(6, true);
bits.Set(7, true);
}

void loop()
{
Serial.print(bits.getMaxBits());
Serial.println(" bits");
Serial.print("value = ");
Serial.println(bits);

for (int i = 0; i < bits.getMaxBits(); i++)
{
Serial.print(i);
Serial.print(" = ");
Serial.println(bits.IsTrue(i) ? "true" : "false");
}
 
delay(2000);
}

Go Up