C Polymorphismus / Panorama Roboter Steuerung

Hallo,

ich beschäftige mich nun seit ca 3 Monaten mit der Arduino-Plattform. Mein aktuelles Projekt ist ein Panorama - Roboter. Die Steuerung des Roboters übernimmt ein Arduino Mini Pro 328 gekoppelt mit einem 16x2 LCD und einem Rotary Encoder. Die Hardware funktioniert soweit, nur die Software will nicht so wie ich will. Die Steuerung habe ich “polymorph” aufgebaut. Als Eingabe der Parameter des Roboters dient die “Param” Basisklasse. Von dieser sind verschiedene Parameter (z.B. Range, Dictionary, Void, Boolean) abgeleitet. Mittlererweile hat das Sketch über 1500 Zeilen Code.

Folgende BUGS tauchen auf.

  • beschädigte “Strings”, anstatt “1-5” escheint z.B. “1m5”
  • zufälliges unvorhersehbares “Benehmen” des Arduino

Ich denke, es ist ein Memory-Problem

Sktech hat ca 15KB, memoryFree() ca 900 bytes.

Am meisten Probleme ergeben sich in diesem Abschnitt

char *Param::toText(int value)
{
      static char buffer[7];
      //itoa(value, buffer, 10);
      sprintf(buffer, "%i", value);
      return buffer;
}

Param.h

#ifndef Param_h
#define Param_h

#include <WProgram.h>
#include <inttypes.h>

class Param;
typedef void (*OnChange)(Param*);
typedef void (*OnRender)(Param*);
typedef void (*OnSelect)(Param*);

//**************************************************************
// Param BaseClass
//**************************************************************

class Param 
{

      public:

            Param();
      
            // Events
            
            OnChange       onChangeHandler;
            OnSelect      onSelectHandler;
            OnRender      onRenderHandler;
      
            // Virtual
      
            virtual void value(int){};
            virtual int value(){ return 0;}
            virtual const char *text();
            virtual void inc(uint8_t){};
            virtual void dec(uint8_t){};
      
            // Static helper
            
            static char *toText(int);
            static int llimit(int, int, int);
            static int ulimit(int, int, int);
            
            void select();
            void render();
            const char *name;
      
};

//**************************************************************
// RangeParam
//**************************************************************

class RangeParam : public Param 
{
      public:
            RangeParam(const char*, int, int, int, int);
            RangeParam(const char*, int, int, int);      
            int value();
            const char *text();
            void inc(uint8_t);
            void dec(uint8_t);
            void value(int);
      private:
            int _step;
            int _value;
            int _from;
            int _to;
};

//**************************************************************
// VoidParam
//**************************************************************

class VoidParam : public Param 
{
      public:
            VoidParam();
            int value();
            const char *text();
};

//**************************************************************
// InfoParam
//**************************************************************

class InfoParam : public Param 
{
      public:
            InfoParam(const char*, const char*, int);
            int value();
            const char *text();            
      private:
            int _value;
            const char *_msg;
};

//**************************************************************
// DicItem
//**************************************************************

class DicItem 
{
      public:
            DicItem();
            DicItem(const char*, int);
            const char *name;
            int value;            
};

//**************************************************************
// DicParam
//**************************************************************

class DicParam : public Param
{
      public:
            DicParam();
            DicParam(char*, DicItem*, uint8_t);
            DicParam(char*, DicItem*, uint8_t, uint8_t);
            int value();
            const char *text();
            void inc(uint8_t);
            void dec(uint8_t);
            void value(uint8_t);
            uint8_t index();
      protected:
            uint8_t _index;
            uint8_t _size;
            DicItem *_items;
};

#endif

Param.cpp

#include "Param.h"

//**************************************************************
// Param BaseClass
//**************************************************************

Param::Param()
{
      onChangeHandler = NULL;
      onSelectHandler = NULL;
      onRenderHandler = NULL;
}

char *Param::toText(int value)
{
      static char buffer[7];
      //itoa(value, buffer, 10); 
      sprintf(buffer, "%i", value);
      return buffer;
}

int Param::llimit(int value, int limit, int step)
{            
      int tmp = value - step;
      return (tmp >= limit) ? tmp : limit;
}

int Param::ulimit(int value, int limit, int step)
{
      int tmp = value + step;
      return (tmp <= limit) ?tmp : limit;
}

void Param::select()
{
      if (onSelectHandler)
            (onSelectHandler)(this);
}

void Param::render()
{
      if (onRenderHandler)
            (onRenderHandler)(this);
}

//**************************************************************
// RangeParam
//**************************************************************

RangeParam::RangeParam(const char *n, int from, int to, int step): _from(from), _to(to), _step(step)
{
      _value = _from;
      name = n;
}

RangeParam::RangeParam(const char *n, int from, int to, int step, int value): _from(from), _to(to), _step(step), _value(value)
{
      name = n;
}

int RangeParam::value()
{
      return _value;
}

const char *RangeParam::text()
{
      return toText(_value);
}

void RangeParam::inc(uint8_t c)
{ 
      int tmp = _value;
      _value = ulimit(_value, _to, _step * c);
      if (onChangeHandler && _value != tmp)
            (onChangeHandler)(this); //OnChange ChangeHandler = onChangeHandler;
}

void RangeParam::dec(uint8_t c)
{
      int tmp = _value;
      _value = llimit(_value, _from, _step * c);
      if (onChangeHandler && _value != tmp)
            (onChangeHandler)(this); //OnChange ChangeHandler = onChangeHandler;
}

void RangeParam::value(int v)
{
      _value = v;
}

//**************************************************************
// VoidParam
//**************************************************************

VoidParam::VoidParam(){}
int VoidParam::value(){ return 0;}
const char *VoidParam::text(){ return "";}

//**************************************************************
// InfoParam
//**************************************************************

InfoParam::InfoParam(const char *n, const char *msg, int value)
{
      name = n;
      _value = value;
      _msg = msg;
}

int InfoParam::value()
{
      return _value;
}

const char *InfoParam::text()
{
      return _msg;
}

//**************************************************************
// DicItem
//**************************************************************

DicItem::DicItem(){}
DicItem::DicItem(const char *n, int v)
{
      name = n;
      value = v;
}

//**************************************************************
// DicParam
//**************************************************************

DicParam::DicParam(){}

DicParam::DicParam(char *n, DicItem *items, uint8_t size) : _items(items), _size(size)
{
      _index = 0;
      name = n;
}

DicParam::DicParam(char *n, DicItem *items, uint8_t size, uint8_t index)
{
      name = n;
      _size = size;
      _index = index;
      _items = items;
}

void DicParam::value(uint8_t index)
{
      _index = index;
}

int DicParam::value()
{
      return _items[_index].value;
}

const char *DicParam::text()
{
      return _items[_index].name;
}

void DicParam::inc(uint8_t count)
{
      int tmp = _index;
      _index = ulimit(_index, _size -1, 1);
      if (onChangeHandler && _index != tmp)
            (onChangeHandler)(this);
}

void DicParam::dec(uint8_t count)
{
      int oldindex = _index;
      _index = llimit(_index, 0, 1);
      if (onChangeHandler && _index != oldindex)
            (onChangeHandler)(this);
}

uint8_t DicParam::index()
{
      return _index;
}

Nach soviel Text und Code bin ich dankbar für gefundene Fehler oder Vorschläge

p.s ist mein erstes C / C++ Projekt

Markus

Also da gibt es unendlich viele Dinge, die schief laufen können. Nicht weil Du sie falsch programmiert hättest oder so (habe das nicht gross geprüft), sondern weil der c++ Compiler schonmal Sorgen bereitet, besonders bei grösseren Projekten. Welche Arduino Version (Software) benutzt Du auf welchem Betriebssystem? Die wenigstens arbeiten hier mit c++, deswegen gibt es dazu kaum debug-Erfahrungen... sprintf etc. sind wirkliche Knaller für den kleinen Controller. Und die Print() Funktionen haben eh viele Bugs, darauf würde ich direkt verzichten. Wenn Memoryprobleme auftauchen, dann wegen Print(), zumindest bei mir.

So, habe letzte Nacht eines an Code geändert. Es funktioniert soweit so gut, solange ich nicht zuviele Param definiere. Als IDE kommt 0017 auf Windows 7 und Ubuntu 9.10 zum Einsatz.

Da nun alles fast zufriedenstellend funktioniert, ein paar Fakten zu “Param”

Ziel der Param-Bibliothek

Einfache Definition und Mutation / Verändern von Parametern

z.B.

Stunde eines Weckers

RangeParam wecker_stunde("Stunde [h]", 0, 23, 1, 12); // 0..23, Schrittweite 1, default 12

Blinkabstand einer LED

RangeParam lcd_blink("Blink delay", 200, 2000, 200, 1000); // 200..2000, Schrittweite 200, default 1000

Auswahl von “Strings”

DicItem montag("Montag",1);
DicItem dienstag("Dienstag",2);
...

DicItem *tage = {&montag, &dienstag, ...}
DicParam wochentage("Wochentage, tage, 7, 0); // Auswahl der Tage Montag .. Sonntag, 7 Wochentage, default Montag

Wieso Polymorph ?

Macht nur Sinn, wenn mehrere unterschiedliche Param in einer Liste sind, was ziemlich schnell der Fall ist. Somit kann ich, mittels gemeinsamer “Schnittstelle” / Methoden auf den Wert eines Param (value()) zugreifen.

Methoden:

inc() //erhöhe um Schrittweite
dec() //verringere um Schrittweite
char text() //Wert als char
int value() // Wert als int

Param unterstützt drei Events:

OnChange // Wert hat sich geändert
OnRender // Anzeige auf LCD
OnSelect // Auswahl, falls mehrere Param in einer Liste sind

Kleines Beispiel (PSEUDO-CODE)

#define LED_PIN 3
#define INC_BUTTON_PIN 4
#define DEC_BUTTON_PIN 5

RangeParam led("LED Helligkeit", 0, 255, 15, 0); // 0..255 Schrittweite 15, default = 0

void setup()
{

      // init der buttons inc und dec

      .....

      // onChangeHandler, mach etwas, wenn sich der Wert verändert hat

      led.onChangeHandler = &onChange;

}

void loop()
{

      // inc Button gedrückt = erhöhe Wert um 15

      if (inc_pressed)
            led.inc();

      // dec Button gedrückt = verringere Wert um 15

      if (dec_pressed)
            led.dec();
}

void onChange(Param *p)
{

      // LED Helligkeit = Wert des Param

      digitalWrite(LED_PIN, p -> value());
}

Zukunft ?

Als C / C++ Uebung. Einsatz generischer Datentypen (nicht nur int).

Falls Interesse besteht, werde ich “Param” hochladen. Mir jedenfalls hilft es enorm, wenn ich ein LCD + veränderbare Werte brauche, welche “Events” auslösen.

Markus