Code share: some of my util classes

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.

Libraries.zip (6.98 KB)

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 ?

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 :wink:

I've created these classes as an exercise (in Arduino dependency management :wink: ) 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. :smiley:

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.

BTW: there is a method enableInternalPullup on the DigitalInput class

Sorry, I missed it in the quick look

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.

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.

robtillaart:
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.

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 :stuck_out_tongue_winking_eye: I'll have a look at the libraries ASAP.

obiwanjacobi:
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).

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

:astonished:

Here's main.cpp from Arduino 1.0:

#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.

Its not complete or done, by any means, I'm just playing around with this...

Debug.h

#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... :wink:

usage:

#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.

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

#include <WProgram.h>

int main(void)
{
	init();

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

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

Thank you for sharing your codes

Thanx! I hope it will lead to new ideas :wink:

mromani:
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 =(

BitArray.h

#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:

#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);
}

Attached is a 'port' of the MIDI library 3.2 by Francois Best where I've split up the Midi class into a MidiInPort and a MidiOutPort. I haven't included the midi thru functionality but the rest is as much copy-paste as possible.

The MidiOutPort takes a Stream in the constructor thus allowing a much broader range of output devices (not tied to Hardware Serial anymore). The MidiInPort still uses the USE_SERIAL_PORT macro (because there is no virtual serial device base class I couldn't get rid of the call to Serial.begin(MIDI_BAUDRATE)).

Now I can receive midi on the hardware serial and send midi on the software serial and to multiple outputs.

Here is an example:

#include <SoftwareSerial.h>

#include "MidiInPort.h"
#include "MidiOutPort.h"

SoftwareSerial softSerial(2, 4);
MidiInPort midiIn;
MidiOutPort midiOut(&softSerial);

void OnNoteOn(byte channel, byte note, byte velocity)
{
	if(velocity == 0)
	{
		// note off
		midiOut.sendNoteOn(channel, note, 0);
	}
	else
	{
		velocity = map(velocity, 1, 127, 60, 127);
		midiOut.sendNoteOn(note, velocity, channel);
	}
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
	midiOut.sendNoteOff(note, velocity, channel);
}

void setup()
{
	midiIn.setHandleNoteOn(OnNoteOn);
	midiIn.setHandleNoteOff(OnNoteOff);

	midiIn.begin(MIDI_CHANNEL_OMNI);
	
	softSerial.begin(MIDI_BAUDRATE);
}

void loop()
{
	midiIn.read();
}

PS: I could not find any license for the Midi library so I hope this is ok. If not, please delete this posting.

MidiPort.zip (10.8 KB)

seems you are very productive !

I once had the idea to make a "midi -> arduino -> switch my keyboard light" sketch, with this lib it becomes easy.

robtillaart:
seems you are very productive !

I once had the idea to make a "midi -> arduino -> switch my keyboard light" sketch, with this lib it becomes easy.

Thanx! And I haven't even shown you all the code. It seems I am slowly building up a new template library ... XD

Not entirely sure what you mean by 'switch my keyboard light' but I've found that even if you use the standard MIDI library on Hardware serial and only use the input, you're still able to output text to the COM port on the PC calling the normal Serial.Xxxx methods.

So go for it! :wink:

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

:astonished:

Here's the linker map of an empty program. Much of it are the interrupt vectors. Also, even an empty program still calls main() and init() which set a few things up, and so those are always linked.

.text           0x0000000000000000      0x1bc
 *(.vectors)
 .vectors       0x0000000000000000       0x68 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/../../../../avr/lib/avr5/crtm328p.o
                0x0000000000000000                __vectors
                0x0000000000000000                __vector_default
 *(.vectors)
 *(.progmem.gcc*)
 *(.progmem*)
                0x0000000000000068                . = ALIGN (0x2)
                0x0000000000000068                __trampolines_start = .
 *(.trampolines)
 .trampolines   0x0000000000000068        0x0 linker stubs
 *(.trampolines*)
                0x0000000000000068                __trampolines_end = .
 *(.jumptables)
 *(.jumptables*)
 *(.lowtext)
 *(.lowtext*)
                0x0000000000000068                __ctors_start = .
 *(.ctors)
                0x0000000000000068                __ctors_end = .
                0x0000000000000068                __dtors_start = .
 *(.dtors)
                0x0000000000000068                __dtors_end = .
 SORT(*)(.ctors)
 SORT(*)(.dtors)
 *(.init0)
 .init0         0x0000000000000068        0x0 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/../../../../avr/lib/avr5/crtm328p.o
                0x0000000000000068                __init
 *(.init0)
 *(.init1)
 *(.init1)
 *(.init2)
 .init2         0x0000000000000068        0xc /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/../../../../avr/lib/avr5/crtm328p.o
 *(.init2)
 *(.init3)
 *(.init3)
 *(.init4)
 .init4         0x0000000000000074       0x10 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/avr5/libgcc.a(_clear_bss.o)
                0x0000000000000074                __do_clear_bss
 *(.init4)
 *(.init5)
 *(.init5)
 *(.init6)
 *(.init6)
 *(.init7)
 *(.init7)
 *(.init8)
 *(.init8)
 *(.init9)
 .init9         0x0000000000000084        0x8 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/../../../../avr/lib/avr5/crtm328p.o
 *(.init9)
 *(.text)
 .text          0x000000000000008c        0x4 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/../../../../avr/lib/avr5/crtm328p.o
                0x000000000000008c                __vector_22
                0x000000000000008c                __vector_1
                0x000000000000008c                __vector_24
                0x000000000000008c                __vector_12
                0x000000000000008c                __bad_interrupt
                0x000000000000008c                __vector_6
                0x000000000000008c                __vector_3
                0x000000000000008c                __vector_23
                0x000000000000008c                __vector_25
                0x000000000000008c                __vector_11
                0x000000000000008c                __vector_13
                0x000000000000008c                __vector_17
                0x000000000000008c                __vector_19
                0x000000000000008c                __vector_7
                0x000000000000008c                __vector_5
                0x000000000000008c                __vector_4
                0x000000000000008c                __vector_9
                0x000000000000008c                __vector_2
                0x000000000000008c                __vector_21
                0x000000000000008c                __vector_15
                0x000000000000008c                __vector_8
                0x000000000000008c                __vector_14
                0x000000000000008c                __vector_10
                0x000000000000008c                __vector_18
                0x000000000000008c                __vector_20
                0x0000000000000090                . = ALIGN (0x2)
 *(.text.*)
 .text.setup    0x0000000000000090        0x2 16000000/simple.o
                0x0000000000000090                setup
 .text.loop     0x0000000000000092        0x2 16000000/simple.o
                0x0000000000000092                loop
 .text.startup.main
                0x0000000000000094       0x1a 16000000/core.a(main.o)
                0x0000000000000094                main
 .text.__vector_16
                0x00000000000000ae       0x94 16000000/core.a(wiring.o)
                0x00000000000000ae                __vector_16
 .text.init     0x0000000000000142       0x76 16000000/core.a(wiring.o)
                0x0000000000000142                init
                0x00000000000001b8                . = ALIGN (0x2)
 *(.fini9)
 .fini9         0x00000000000001b8        0x0 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/avr5/libgcc.a(_exit.o)
                0x00000000000001b8                exit
                0x00000000000001b8                _exit
 *(.fini9)
 *(.fini8)
 *(.fini8)
 *(.fini7)
 *(.fini7)
 *(.fini6)
 *(.fini6)
 *(.fini5)
 *(.fini5)
 *(.fini4)
 *(.fini4)
 *(.fini3)
 *(.fini3)
 *(.fini2)
 *(.fini2)
 *(.fini1)
 *(.fini1)
 *(.fini0)
 .fini0         0x00000000000001b8        0x4 /home/maniacbug/bin/avr/lib/gcc/avr/4.7.0/avr5/libgcc.a(_exit.o)
 *(.fini0)
                0x00000000000001bc                _etext = .

.data           0x0000000000800100        0x0 load address 0x00000000000001bc
                0x0000000000800100                PROVIDE (__data_start, .)
 *(.data)
 *(.data*)
 *(.rodata)
 *(.rodata*)
 *(.gnu.linkonce.d*)
                0x0000000000800100                . = ALIGN (0x2)
                0x0000000000800100                _edata = .
                0x0000000000800100                PROVIDE (__data_end, .)

.bss            0x0000000000800060        0x9
                0x0000000000800060                PROVIDE (__bss_start, .)
 *(.bss)
 *(.bss*)
 .bss.timer0_millis
                0x0000000000800060        0x4 16000000/core.a(wiring.o)
                0x0000000000800060                timer0_millis
 .bss.timer0_overflow_count
                0x0000000000800064        0x4 16000000/core.a(wiring.o)
                0x0000000000800064                timer0_overflow_count
 .bss.timer0_fract
                0x0000000000800068        0x1 16000000/core.a(wiring.o)
 *(COMMON)
                0x0000000000800069                PROVIDE (__bss_end, .)
                0x00000000000001bc                __data_load_start = LOADADDR (.data)
                0x00000000000001bc                __data_load_end = (__data_load_start + SIZEOF (.data))

.noinit         0x0000000000800069        0x0
                0x0000000000800069                PROVIDE (__noinit_start, .)
 *(.noinit*)
                0x0000000000800069                PROVIDE (__noinit_end, .)
                0x0000000000800069                _end = .
                0x0000000000800069                PROVIDE (__heap_start, .)

obiwanjacobi:
[...]
(because there is no virtual serial device base class I couldn't get rid of the call to Serial.begin(MIDI_BAUDRATE)).
[...]

You can use the magic of OOP and have an overloaded constructor which takes a HardwareSerial*, so that the usage could be either this:

MidiOutPort midiOut(&softSerial);

or this:

MidiOutPort midiOut(&Serial);

completely transparent to the user. In the library implementation, the easiest way to do this is to encapsulate the write() function, forex:

void write(uint8_t b) {
  if (m_hws != NULL) { // set to null in the softwareserial constructor
    m_hws->write(b);
  } else { // not using hardware serial
    m_ss->write(b);
  }
}