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.
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.
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:
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 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).
#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.
I'm still struggling to get the FILE and LINE to work...
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.
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).
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 =(
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.
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.
obiwanjacobi:
I dont get why an empty program -with just an empty setup and loop method- is still 466 bytes long!?
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);
 }
}