PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tutorial: Erstellen einer Arduino-Bibliothek



Sisor
05.07.2014, 21:00
Dieses Tutorial beschäftigt sich mit dem Erstellen einer Arduino-Bibliothek


1. Vorwort


Bibliotheken sind ja für viele etwas, was man reinladen muß, damit irgendein Sensor, Motor, Display etc angesprochen werden kann. Bibliotheken sind aber kein Hexenwerk. Im Gegenteil, sie sind sehr nützlich, um Programme übersichtlicher zu machen. Außerdem kann man sie leicht mit anderen teilen, sodass nicht jeder Programierer ein bestimmtes Problem neu enträtseln muss. Darum werden wir hier mal ein bischen genauer hinschauen.

Dazu nehmen wir ein einfaches Arduino-Programm und lagern die Funktion in mehreren Schritten in eine Bibliothek aus. Diese sind nicht unbedingt alle nötig, dienen aber der Verauschaulichung, was zu tun ist und warum.

Wer die Programmbeispiele am eigenen Rechner nachvollziehen will, benötigt eine installierte Arduino-IDE ab Version 1.


2. Das Programm


Programmbeschreibung:
Das Programm steuert eine RGB-Led. Diese Led kann man sich vorstellen wie 3 verschiedenfarige Leds in einem Gehäuse. Sie besitzt 4 Anschlüsse: Masse und 3 Anschlüsse für die einzelnen Steuerspannungen, mit denen die Helligkeit der jeweiligen Farbe (Rot, Grün, Blau) gesteuert wird.

RGB_LED.ino:

#define RED_PIN 4
#define GRN_PIN 3
#define BLU_PIN 2

void setRGB(byte r, byte g, byte b) {
analogWrite(RED_PIN, r);
analogWrite(GRN_PIN, g);
analogWrite(BLU_PIN, b);
}

void blink(char color, byte times, unsigned int ms) {
byte r=0, g=0, b=0;
if(color == 'r') r = 255;
else if(color == 'g') g = 255;
else if(color == 'b') b = 255;
else return ;

for (byte i = 0; i < times; i++) {
setRGB(r, g, b);
delay(ms);
setRGB(0, 0, 0);
delay(ms);
}
}

void setup() {}

void loop() {
blink('r', 5, 500); // rot, 5 Widerholungen, jeweils 500 Millisekunden an
blink('b', 10, 250);
blink('g', 15, 167);
}

'setRGB' setzt die einzelnen Steuerspannungen, und damit Farbe und Helligkeit der RGB-Led. Sie nimmt je Farbe Werte zwischen 0 (aus) und 255 (volle Helligkeit) entgegen.

'blink' lässt die Led naja blinken. Farbe der Led, Anzahl der Blinkwiderholungen und Dauer eines An-Aus-Intervalls sind der Funktion zu übergeben.

'loop' lässt die RGB-Led nun in erst rot und langsam, dann blau und schneller, dann grün und schnell blinken. Dann beginnt es von vorn.



3. Auslagern der Funktionalität in ein Modul


Motivation:
- Die Übersichtlichkeit wird verbessert. Im Hauptprogramm sind nur noch die Funtionsaufrufe sichtbar.
- Die Funktionalität kann so sehr einfach in andere Programme integriert werden. Dazu muss nur das Modul geladen werden.

Dazu wird im Sketch-Ordner eine zusätzliche Datei erstellt. Wir nennen sie 'RGBLed.h'.
'.h' identifiziert die Datei als Header-Datei. Hier werden nun die Funtionen eingefügt. Dem vorangestellt steht die Zeile: #include <Arduino.h>. Die ist erforderlich, weil dem Linker die Arduino-Befehlsumgebung zu diesem Zeitpunkt noch nicht bekannt ist. (Der Linker ist Programm, dass die einzelnen Programmteile zu eine einem Gesamtprogramm zusammensetzt, damit es vom Compiler in einen vom Arduino verständlichen Maschinencode übersetzt werden kann).

RGBLed.h

#include <Arduino.h>

void setRGB(byte r, byte g, byte b) {
analogWrite(RED_PIN, r);
analogWrite(GRN_PIN, g);
analogWrite(BLU_PIN, b);
}

void blink(char color, byte times, unsigned int ms) {
byte r=0, g=0, b=0;
if(color == 'r') r = 255;
else if(color == 'g') g = 255;
else if(color == 'b') b = 255;
else return ;

for (byte i = 0; i < times; i++) {
setRGB(r, g, b);
delay(ms);
setRGB(0, 0, 0);
delay(ms);
}
}
Im Hauptprogramm muß dann dem Kompiler angezeigt werden, dass wir die frisch erzeugte Datei verwenden wollen. Dies macht die Anweisung #include "RGBLed.h".

RGB_LED.ino


#define RED_PIN 4
#define GRN_PIN 3
#define BLU_PIN 2

#include "RGBLed.h"

void setup() {}

void loop() {
blink('r', 5 , 500);
blink('b', 10, 250);
blink('g', 15, 167);
}



4. Objekt bauen


Motivation:
- Objekte fassen Funtionalität und Daten unter einem Dach zusammen
- bessere Lesbarkeit des Codes
- Kapselung, Sicherheit

Bis hierhin haben wir reinen C-Code verwendet. Es ist jetzt an der Zeit einen der Vorzüge von C++ zu verwenden: Objekt-Orientierung. Objekte werden nach einem Bauplan erstellt. Dieser Bauplan wird allgemein als Klasse bezeichnet. Die Beschreibung einer Klasse geschieht in einem class-Block. Unsere bekommt den Namen RGBLed.
Das Schlüsselwort public gibt an, dass die Funktionen (die in der Objekt -Orientierung auch Methoden genannt werden) ausserhalb der Klasse ansprechbar sind. Dies wollen wir, da ja später aus dem Hauptprogramm diese Methoden aufrufen werden sollen.

RGBLed.h

#include <Arduino.h>

class RGBLed {
public:
void setRGB(byte r, byte g, byte b) {
analogWrite(RED_PIN, r);
analogWrite(GRN_PIN, g);
analogWrite(BLU_PIN, b);
}

void blink(char color, byte times, unsigned int ms) {
byte r=0, g=0, b=0;
if(color == 'r') r = 255;
else if(color == 'g') g = 255;
else if(color == 'b') b = 255;
else return ;

for (byte i = 0; i < times; i++) {
setRGB(r, g, b);
delay(ms);
setRGB(0, 0, 0);
delay(ms);
}

};

Im Hauptprogramm erzeugen wir zuerst ein RGBLed-Objekt mit dem Namen 'rgb'. Die Methoden des Objekts werden über den '.'-Operator angesprochen.

RGB_LED.ino


#define RED_PIN 4
#define GRN_PIN 3
#define BLU_PIN 2
#include "RGBLed.h"
RGBLed rgb;

void setup() {}

void loop() {
rgb.blink('r', 5 , 500);
rgb.blink('b', 10, 250);
rgb.blink('g', 15, 167);
}



5. Weitere Bearbeitung


- verhindern, dass die Bibliothek mehrfach geladen werden kann (und so zu Compiler-Fehlermeldungen führt)
Dies wird mit folgendem Code erreicht:

#ifndef __RGB_LED__
#define __RGB_LED__
//...
#endif

- Daten im Objekt speichern
Dazu benutzen wir einen Konstruktor: RGBLed (byte redPin, byte greenPin, byte bluePin)
Dieser soll die PIN-Nummern, an denen die RGB-Led angeschlossen wird, beim sofort beim Erstellen des Objekts speichern.
Das Schlüsselwort 'private' gibt an, dass die Variablen nur innerhalb des Objekts angesprochen werden können.

RGBLed.h:


#ifndef __RGB_LED__
#define __RGB_LED__

#include <Arduino.h>

class RGBLed {
private:
byte redPin;
byte greenPin;
byte bluePin;

public:
RGBLed (byte redPin, byte greenPin, byte bluePin){
this->redPin = redPin;
this->greenPin = greenPin;
this->bluePin = bluePin;
}
void setRGB(byte r, byte g, byte b) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
void blink(char color, byte times, unsigned int ms) {
byte r=0, g=0, b=0;
if(color == 'r') r = 255;
else if(color == 'g') g = 255;
else if(color == 'b') b = 255;
else return ;

for (byte i = 0; i < times; i++) {
setRGB(r, g, b);
delay(ms);
setRGB(0, 0, 0);
delay(ms);
}
}
};

#endif

Im Hauptprogramm benutzen wir nun den neuen Konstruktor.

RGB_LED.ino

#include "RGBLed.h"
RGBLed rgb(4, 3, 2); //Pins: red =4, green =3, blue =2

void setup() {}

void loop() {
rgb.blink('r', 5 , 500);
rgb.blink('b', 10, 250);
rgb.blink('g', 15, 167);
}



6. Finale Bearbeitung


Jetzt werden wir eine weitere Datei hinzufügen: RGBLed.cpp
Diese cpp-Datei wird die gesamte Funktionalität beinhalten, während wir diese aus der h-Datei verbannen. Dort werden nur noch Deklarationen stehen. Dies dient wiederum der Übersichtlichkeit. Falls ein fremder Programmieren sich den Code anschaut, kann er in der h-Datei sehen, was die Bibliothek kann. In der cpp-Datei kann er sehen, wie die Bibliothek das umsetzt.

Außdem werden wir nun einen neuen Ordner im Ordner '..Arduino/libraries' erstellen. Diesen nennen wir 'RGBLed' und packen unsere h-Datei und cpp-Datei dort hinein.

RGBLed.h:

#ifndef __RGB_LED__
#define __RGB_LED__

#include <Arduino.h>

class RGBLed {
private:
byte redPin;
byte greenPin;
byte bluePin;

public:
RGBLed (byte redPin, byte greenPin, byte bluePin);

void setRGB(byte r, byte g, byte b) ;
void blink(char color, byte times, unsigned int ms);
};

#endif

'RGBLed::' ordnet die in der cpp-Datei definierten Funktionen der Klasse 'RGBLed' zu.

RGBLed.cpp:

#include "RGBLed.h"

RGBLed::RGBLed (byte redPin, byte greenPin, byte bluePin) {
this->redPin = redPin;
this->greenPin = greenPin;
this->bluePin = bluePin;
}

void RGBLed::setRGB(int r, int g, int b) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}

void RGBLed::blink(char color, byte times, unsigned int ms) {
byte r=0, g=0, b=0;
if(color == 'r') r = 255;
else if(color == 'g') g = 255;
else if(color == 'b') b = 255;
else return ;

for (byte i = 0; i < times; i++) {
setRGB(r, g, b);
delay(ms);
setRGB(0, 0, 0);
delay(ms);
}
}

Das Hauptprogramm hat nur eine Änderung:
#include <RGBLed.h> statt #include "RGBLed.h"
Diese Änderung trägt der Migration in den Ordner '..Arduino/libraries' Rechnung. Dort sucht die Arduino-IDE beim Programmstart automatisch nach Bibliotheken.

RGB_LED.ino

#include <RGBLed.h>
RGBLed rgb(4, 3, 2); //Pins: red =4, green =3, blue =2

void setup() {}

void loop() {
rgb.blink('r', 5 , 500);
rgb.blink('b', 10, 250);
rgb.blink('g', 15, 167);
}

Als letztes fügen wir in unseren Ordner eine weitere Datei:

keywords.txt

RGBLed KEYWORD1
setRGB KEYWORD1
blink KEYWORD1

Diese sorgt für ein buntes Hervorheben unserer Schlüsselwörter und dient somit der nocheinmal der besseren Lesbarkeit.

--
Viel Spass beim Schreiben (und Veröffentlichen;)) eurer eigenen Arduino-Bibliotheken!!!

Sisor

Chypsylon
06.07.2014, 12:45
Die Methoden des Objekts werden über den '.'-Operator angesprochen, wie man es von z.B. Java oder C# kennt. (Dies ist eine Besonderheit der Arduino-IDE. C++-Programierer wundern sich: Normalerweise ist hier doch ein Pfeiloperator?)

C++ verwendet genauso den '.'-Operator. Der '->'-Pfeil ist nur eine Abkürzung um sich das derefernzieren eines Pointers zu ersparen.



Foo *myFoo = new Foo();

(*myFoo).foobar();
//ist das gleiche wie
myFoo->foobar();

Sisor
06.07.2014, 13:17
Danke für den Hinweis, da hatte ich im Kopf mächtig was verdreht.
In meinem Beispiel wird eben nicht mit 'new' und Referenz gearbeitet. Daher fällt natürlich auch das Dereferenzieren weg.

Foo myFoo();
myFoo.foobar();

// ohne Anwendung, nur zur Veranschaulichung:
Foo* myFooPtr = &myFoo; // Referenz erzeugen
myFooPtr->foobar(); // indirekter Zugriff
Habs korrigiert.

cdk
09.07.2014, 21:51
Hey Sisor, und wieder herzlichen Dank für das Tutorial! Ich werde es nutzen um meine rudimentären C-Kenntnisse endlich auf rudimentäre C++-Kenntnisse :) aufzustocken wenn ich mal wieder Zeit für den Arduino hab'...

Sisor
10.07.2014, 14:10
Kein Problem, ich hatte mich mit dem Thema gerade beschäftigt und dachte, ich fass das Gelernte mal für mich und andere zusammen. Interessant ist, dass jedes der 5 Programme (Schritt 2 - 6) die gleiche Funktionalität aufweist. Je komplexer ein Programm wird, desto mehr macht eine modulare, objektorientierte Herangehensweise Sinn.

Unregistriert
16.09.2015, 10:07
D:\Eigene Dateien\Arduino\libraries\RGBLed\RGBLed.cpp:9:6: error: prototype for 'void RGBLed::setRGB(int, int, int)' does not match any in class 'RGBLed'
void RGBLed::setRGB(int r, int g, int b) {
^
In file included from D:\Eigene Dateien\Arduino\libraries\RGBLed\RGBLed.cpp:1:0:
D:\Eigene Dateien\Arduino\libraries\RGBLed\RGBLed.h:15:7: error: candidate is: void RGBLed::setRGB(byte, byte, byte)
void setRGB(byte r, byte g, byte b) ;
^
Fehler beim Kompilieren.

Chypsylon
16.09.2015, 10:39
Der Compiler sagt dir doch eh schon wo das Problem liegt ;)

Im header File (.h) ist setRGB(byte r, byte g, byte b); mit bytes definiert. Im .cpp ist gibts die Funktion aber nur mit ints als Parameter ausprogrammiert. Das ist vermutlcih einfach ein Copy&Paste Fehler des Autors, ändere die ints zu bytes und es sollte funktionieren.

Unregistriert
16.09.2015, 10:56
Vielen Dank für die Rückantwort.0
Habe das bereits geändert so ´dass der Funktionsrumpf und die Implementierung über Byte arbeiten.
Wollte gerne noch bei meinem Projekt in die CPP-Datei die IRremote.h einbinden.
Wo kann man dazu noch was finden?

Derzeit kommt das dabei raus:
--------------
IRControl.cpp:1:22: fatal error: IRremote.h: No such file or directory
#include <IRremote.h>
^
compilation terminated.
Fehler beim Kompilieren.
--------------

Meine Klasse soll IRControl heißen und Steuercodes auf den Seriellen Monitor bzw. einen INT-Wert zurückgeben.
Hatte da schon ein Programm fertig, welches ich jetzt mit diesen Tutoriell in eine Klasse stecken möchte.

Chypsylon
16.09.2015, 11:38
IRControl.cpp:1:22: fatal error: IRremote.h: No such file or directory

Der Compiler meckert hier das er diese Datei nicht finden kann, bist du dir sicher das du die Library richtig installiert hast? Funktioniert das einbinden bei anderen Programmen?

Unregistriert
16.09.2015, 13:08
Danke nochmal für die Rückantwort.

Das Einbinden über <IRremote.h> hat nicht geklappt.

Habe die IRremote.h und die CPP sowie die zugehörigen Dateien in mein Library-Verzeichnis "IRControl" kopiert.
In der IRControl.cpp habe ich dann oben gleich diese direkt so eingebunden:

#include "IRControl.h"
#include "IRremote.h"
#include<Arduino.h>

Dann war es noch wichtig alle weiteren Dateien über Drag&Drop auf die Arduino-IDE zu ziehen damit die gefunden werden.
Jetzt läuft es soweit.

Das Einzige was mich noch etwas nervt, ist das im Hauptprogramm das Objekt sofort
instanziiert wird. In Java kann man es immer erst deklarieren und danach mit 'new' instanziieren.
Habe es jetzt mit static in den Loop() gesetzt. Da sollte es nur einmal erzeugt werden und ist verfügbar.
Vorerst ist es aber mal soweit ok. Hatte gegen 2009 aufgehört C++ zu programmieren.

Unregistriert
16.09.2015, 16:51
in RGBLed.h:
---------------

#define COL_black 0x000000
#define COL_silver 0xC0C0C0
#define COL_gray 0x808080
#define COL_white 0xFFFFFF
#define COL_maroon 0x800000
#define COL_red 0xFF0000
#define COL_purple 0x800080
#define COL_pink 0x9B000A
#define COL_fuchsia 0xFF00FF
#define COL_green 0x008000
#define COL_lime 0x00FF00
#define COL_olive 0x808000
#define COL_yellow 0xFFFF00
#define COL_navy 0x000080
#define COL_blue 0x0000FF
#define COL_teal 0x000015
#define COL_aqua 0x00FFFF



public:

void setRGBhex(unsigned long col) ;

---------------

in RGBLed.cpp

void RGBLed::setRGBhex(unsigned long col)
{

byte r=0, g=0, b=0;
b = (byte)(col & 0x0000FF);
g = (byte)((col>>8) & 0x0000FF);
r = (byte)((col>>16) & 0x0000FF);

analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}

Ulrich H
20.09.2015, 18:32
Hallo zusammen,
ich hab da mal 2 grundsätzliche Fragen:
wofür sind die beiden ersten Zeilen der h Datei und welche Bedeutung haben die Unterstriche vorn und hinten?
Wie bekomme ich einen oder mehrere Rückgabewerte aus der Bibliothek in die ino Datei?

Chypsylon
20.09.2015, 20:46
Ich nehme mal an du meinst diese Zeilen:



#ifndef __RGB_LED__
#define __RGB_LED__
...
#endif

Die Dinger mit einem # voran heissen Preprozessor-Makros und werden vor dem eigentlichen Compilervorgang bearbeitet. #include file.h heisst ja nichts anderes als das vor dem Compilervorgang der ganze Inhalt des einzubindenden Files an dieser Stelle eingefügt wird. In der dieser Skizze wäre der Inhalt von header1.h im Endeffekt 2 mal eingefügt -> mag der Compiler nicht.

30701

Mit diesen so gennanten "Include Guards" wird das ganze verhindert.

Mit dem ersten #ifndef __RGB_LED__ wird abgefragt ob __RGB_LED__ bereits irgendwo #definiert wurde (if not defined=wenn noch nicht definiert). Da dies beim ersten Mal noch nicht der Fall ist wird der nachfolgende Code ebenfalls "angeschaut" und __RGB_LED__ in der nächsten Zeile "definiert" und der Code dann ganz normal weiter behandelt. Beim nächsten Einbindevorgang ist __RGB_LED__ aber schon "definiert" und der Code zwischen #ifndef und #endif wird einfach übersprungen.


Wie bekomme ich einen oder mehrere Rückgabewerte aus der Bibliothek in die ino Datei?

Wie meinst du das? Die Funktionen aus der Biliothek kannst du ja ganz normal aufrufen und auch Werte z.b. mit return zurückgeben.

Ulrich H
21.09.2015, 11:03
Hallo Chypsylon,
erstmal herzlichen Dank für das superschnelle Feedback.
Die Rückgabe habe ich hinbekommen, aber wofür ist denn diese Makrodefinition, es geht doch auch ohne. Gehören die Unterstriche denn zum Namen?
Meine h Datei sieht jetzt so aus und klappt:

//#ifndef __uliTesttLib_h__
//#define __uliTesttLib_h__

#include <Arduino.h>

class uliTestLib{
private:
byte pin1;
byte pin2;
byte pin3;
public:
uliTestLib(byte pin1, byte pin2, byte pin3);
void setRGB(byte r, byte g, byte b);
void blinken(char color, byte times, unsigned int ms);
int zurueck();
};
//#endif

Chypsylon
21.09.2015, 11:39
Die Unterstriche sind nur Konvention, damit gleich klar ist das es ein Includeguard ist.

Klar geht es jetzt auch ohne, du includest dein .h file vermutlich auch nur einmal. Aber wenn du jetzt z.b. eine zweites seperate Klasse "LedControl" mit eigenem Header-File machst dann könntest du dort #include uliTesttLib.h machen und die setRGB() Funktion von dort verwenden. Wenn du diese zweite Bibliothek dann aber ebenfalls in deinem main-file (.ino) einbindest (#include) hast du über diese zweite Klasse dann nochmals deine uliTesttLib.h eingebunden und dadurch wäre der Code dann zweimal vorhanden.

Diese Includeguards in Header-Files einzubauen gehört einfach zum guten Stil und man erspart sich dann selbst später viel Arbeit bei der Fehlersuche. "Richtige" IDEs und gute Editoren fügen Includeguards (und anderes Standardzeugs) automatisch ein, so dass man sich nicht darum kümmern muss.

Ulrich H
21.09.2015, 11:51
aha

Danke

Unregistriert
12.11.2015, 14:24
Bei mir kann ich es zwar rüberladen, passiert aber nichts...

inka
04.03.2016, 08:43
hallo Sisor,

ich habe mehrere sketches, die alle mit steppermotoren (customstepper lib) zu tun haben und wo ich die gleichen "funktionen" verwende, wie z.b.


void alle_stepper_vorwaerts(void)
{
if (start_ping == true) ping_distanz();

if (hindernis == true)
{
Serial1.print(hindernis);
Serial1.println(" hindernis - true - fahre rückwärts - US- abfrage in alle stepper vorwärts");
Serial.print(hindernis);
Serial.println(" hindernis - true - fahre rückwärts - US- abfrage in alle stepper vorwärts");
hindernis = false;


for (idx = stepper_VL; idx < stepper_MAX; idx++) //alle stepper rückwärts
{
stepper[idx].setRPM(12);
stepper[idx].setSPR(4075.7728395);
stepper[idx].setDirection(CCW);
stepper[idx].rotateDegrees(10); //rotate(1)
}
fahrt_ausfuehren();
}
else
{
hindernis = false;

Serial.print(hindernis);
Serial1.println(" freie fahrt - alle stepper vorwärts");
Serial.print(hindernis);
Serial.println(" freie fahrt - alle stepper vorwärts");

for (idx = stepper_VL; idx < stepper_MAX; idx++)//alle stepper vorwärts
{
stepper[idx].setRPM(12);
stepper[idx].setSPR(4075.7728395);
stepper[idx].setDirection(CW);
stepper[idx].rotateDegrees(5);//rotate(1)
}
fahrt_ausfuehren();
}
}
}

ich habe nun versucht nach Deinem tutorial daraus eine lib zu machen, allerdings bin ich über das erste stadium - in dem die funktionen in einer "vier_stepper.h" im gleichen verzeichnis ausgegliedert werden - nicht hinausgekommen. Das reicht mir nicht, weil ich ja immer noch dutzende von "vier_stepper.h" habe...

vom RP6 kenne ich diese möglichkeit:
eine standard.c (könnte bei arduino auch standard.cpp heissen)

#include "RP6ControlLib.h"
#include "RP6I2CmasterTWI.h"
#include "RP6Control_MultiIOLib.h"
#include "RP6Control_I2CMasterLib.h"
#include "RP6Control_LFSBumperLib.h"
#include "RP6ControlServoLib.h"
#include "RP6Control_OrientationLib.h"
#include "RP6Stopwatch0Lib.h"
#include "standard.h"


/***************************variablen************** ****************/


uint8_t RP6data[32];
uint8_t i, j, t;
int16_t x, y, z;
uint16_t anfang, ende;


uint16_t lfs_l, lfs_m, lfs_r;
uint8_t nord; //nord gefunden
uint8_t IR_wert[1];


uint8_t ch;
char item[12];
char dir[3];
int32_t new_dir; //neue richtung
int32_t old_dir; //gemessene richtung
int32_t temp; //berechnung korrektur richtung
int16_t dev, rot;// i;//berechnung rotation
char rdir;


/********************* ULTRASCHALL & HC-SR-04 ******************************/


double distanz;
volatile uint16_t zeit;
volatile uint16_t timestamp_last;


/****************************** STANDARD *********************************/




/*******************I2C Error handler*********************/


void I2C_transmissionError(uint8_t errorState)
{
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");
writeInteger(errorState, HEX);
writeChar('\n');
}




/******* The I2C_requestedDataReady Event Handler*********/
void I2C_requestedDataReady(uint8_t dataRequestID)
{
checkRP6Status(dataRequestID);
}


/**** Write a floating point number to the LCD or terminal. ****/


void writeDoubleLCD(double number, uint8_t width, uint8_t prec)
{
char buffer[width + 1];
dtostrf(number, width, prec, &buffer[0]);
writeStringLCD(&buffer[0]);
}


void writeDouble(double number, uint8_t width, uint8_t prec)
{
char buffer[width + 1];
dtostrf(number, width, prec, &buffer[0]);
writeString(&buffer[0]);
}


/***********************accuspannungsanzeige******* ***********/


void accuspannung(void) // accuspannung ausgeben
{


multiio_init();
LTC2990_measure();
setCursorPosLCD(0, 0);
writeStringLCD(" accu: ");
writeDoubleLCD(vbat, 4, 2);
writeStringLCD( " V");
}


/************************acculadungsanzeige******** *********/


void acculadung(void)
{


clearLCD();
setCursorPosLCD(0, 0);
writeStringLCD(" ADC2: ");
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2
setCursorPosLCD(0, 9);
writeIntegerLCD(adc2, DEC);
if (adc2 > 650)
{
setCursorPosLCD(2, 0);
writeStringLCD(" ladespannung ok ");
setMultiIOLED1(1);


}
}


/************************accuzustand*************** ********/


void accuzustand(void) // accuspannung abfragen und signalisieren
{


LTC2990_measure();
if (vbat < 6.0)
{
buzzer(330);
mSleep(200);
buzzer(330);
mSleep(200);
//bake_suche();
}


}


/***********************watchdog_request*********** *******/


void watchDogRequest(void)
{
static uint8_t heartbeat2 = false;
if(heartbeat2)
{
clearPosLCD(1, 14, 1);
heartbeat2 = false;
}
else
{
setCursorPosLCD(1, 14);
writeStringLCD_P("#");
heartbeat2 = true;
}
}




/******************* Heartbeat function********************/


void task_LCDHeartbeat(void)
{
if(getStopwatch1() > 500)
{
static uint8_t heartbeat = false;
if(heartbeat)
{
clearPosLCD(0, 15, 1);
heartbeat = false;
}
else
{
setCursorPosLCD(0, 15);
writeStringLCD_P("*");
heartbeat = true;
}
setStopwatch1(0);
}
}




/*******************fahr_bis_schwarz*************** *********/
void fahr_bis_schwarz (void)


{


moveAtSpeed(80, 80);


lfs_l = 0;
lfs_m = 0;
lfs_r = 0;




lfs_l = getLFS(CH_LFS_L);
lfs_m = getLFS(CH_LFS_M);
lfs_r = getLFS(CH_LFS_R);


if(lfs_l && lfs_m && lfs_r <300) //stop();//break;
{
stop();


setCursorPosLCD(0, 0);
writeStringLCD(" li mi re ");
setCursorPosLCD(1, 0);
lfs_l = getLFS(CH_LFS_L);
writeIntegerLCD(lfs_l, DEC);
setCursorPosLCD(1, 8);
lfs_m = getLFS(CH_LFS_M);
writeIntegerLCD(lfs_m, DEC);
setCursorPosLCD(1, 16);
lfs_r = getLFS(CH_LFS_R);
writeIntegerLCD(lfs_r, DEC);




writeIntegerLength(lfs_l, DEC, 4);
writeString(" ");
writeIntegerLength(lfs_m, DEC, 4);
writeString(" ");
writeIntegerLength(lfs_r, DEC, 4);
writeString(" ");
writeChar('\n');




mSleep(2000);
}


}


/*********************************** IR-bake***************************************/




/********************readAllRegister*************** ****/


void readAllRegisters(void)
{
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);


writeString_P("\nREADING ALL RP6 REGISTERS:");
uint8_t i = 0;
for(i = 0; i < 31; i++)
{
if(i % 8 == 0)
writeChar('\n');
else
writeString_P(" | ");
writeChar('#');
writeIntegerLength(i,DEC,2);
writeChar(':');
writeIntegerLength(RP6data[i],DEC,3);
}
writeChar('\n');
}


/***************print_register_30****************** **/


void print_Register_30(void)
{


writeString_P("\nPRINTING REGISTER 30:");
uint8_t i = 0;
for(i = 0; i < 31; i++)
{
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);
if(i % 8 == 0)
writeChar('\n');
else
writeString_P(" | ");
writeChar('#');
writeIntegerLength(i,DEC,2);
writeChar(':');
writeIntegerLength(IR_wert[0],DEC,3);
}
writeChar('\n');
}


/******************read_register_30**************** ****/


void read_Register_30(void)
{
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);
}










/*******************bake_suche********************* *****/
void bake_suche(void)
{


setLEDs(0b0100);


writeString_P("bake_suche_1 \n");
writeChar('\n');
initRP6Control();
initLCD();
startStopwatch3();
t=0;


do
{
if(getStopwatch3() > 50)
{


read_Register_30();


if (IR_wert[0] !=0)
{
setMultiIOLED1(1);
setMultiIOLED1(0);
rotate(80, RIGHT, 5, false);
read_Register_30();


if (IR_wert[0] == 0) stop();


}
read_Register_30();


if (IR_wert[0] == 0)
{
x = getStopwatch3();
setMultiIOLED3(1);
setMultiIOLED3(0);
if (t<10)
{
t++;


if (t == 10)
{
y = getStopwatch3();
z = y-x;


writeInteger(x, DEC);
writeChar('\n');
writeInteger(y, DEC);
writeChar('\n');
writeInteger(z, DEC);
writeChar('\n');


t=0;
setStopwatch3(0);
if (z< 600)
{
move(100, FWD, DIST_MM(100), false);
setStopwatch3(0);
t=0;
mSleep(400);
}


}


}




}
}


}
while(!bumper_left && !bumper_right);
stop();


}




/*************************** GYRO & NORDSUCHE ******************************************/


/**********************test rdir**************************/
void test_rdir(void)
{
if (rot < 0)
rdir = LEFT;
else rdir = RIGHT;
}


/************korrekrur richtung*************************/


void korrekrur_richtung(void)
// Wertebereich new_dir und old_dir: 0..359
// Ergebnis in rot! Positiv: Rechtsdrehung, negativ: Linksdrehung!
{
dev = new_dir - old_dir;
rot = dev;
if (abs(dev) > 180)
{
{
if (dev < 0)
{
rot = 360 + dev;
}
else
{
rot = -360 + dev;
}
}
}
}


/******************sensorwerte_holen*************** *****/


void sensorwerte_holen(void)
{
task_I2CTWI();
readLSM303DLHC_M(); // Get sensor values
task_I2CTWI();
normalizeLSM303DLHC_M(); // Normalize data
headingm = headingLSM303DLHC_M(); // Calculate heading
calculateDetailDir(dir, headingm);
}


/*********************nordsuche******************** ****/


void nordsuche (void)
{
nord=0;
// while (true)
// {
new_dir = 360;
sensorwerte_holen();
old_dir = headingm;
korrekrur_richtung();


test_rdir();
rotate(60, rdir, ((abs(rot)/2)), true);


if isMovementComplete()
{
for (i= 0; i<9; i++);


{
sensorwerte_holen();
old_dir = headingm;
korrekrur_richtung();


test_rdir();
rotate(60, rdir, ((abs(rot)/2)), true);


if (headingm <= 5 || headingm >= 355)


{
nord=1;
// if (nord==1) break;


}
task_I2CTWI();


}
}
}
//}
/*********************nordsuche links************************/


void nordsuche_links (void)
{


clearLCD();


new_dir = 360;
sensorwerte_holen();
old_dir = headingm;
// korrekrur_richtung();


// test_rdir();
rotate(60, LEFT, ((abs(old_dir)/2)), true); //0


for (i= 0; i<9; i++);
{
sensorwerte_holen();
old_dir = headingm;
// korrekrur_richtung();


// test_rdir();
rotate(60, LEFT, ((abs(old_dir)/2)), true); //0
// }
if (abs(old_dir) < 003) stop();
}
// break;


// task_I2CTWI();


}






/***********************calculate_dir************** ********/
void calculateDir(char* dir, uint16_t heading) //setzt headingwerte grob in himmelsrichtungen um
{
dir[1] = ' ';
dir[2] = '\0';
if ((heading <= 22) || (heading >=338)) dir[0] = 'N';
if ((heading >= 23) && (heading <= 67))
{
dir[0] = 'N';
dir[1] = 'E';
}
if ((heading >= 68) && (heading <= 112)) dir[0] = 'E';
if ((heading >= 113) && (heading <= 157))
{
dir[0] = 'S';
dir[1] = 'E';
}
if ((heading >= 158) && (heading <= 202)) dir[0] = 'S';
if ((heading >= 203) && (heading <= 247))
{
dir[0] = 'S';
dir[1] = 'W';
}
if ((heading >= 248) && (heading <= 292)) dir[0] = 'W';
if ((heading >= 293) && (heading <= 337))
{
dir[0] = 'N';
dir[1] = 'W';
}
}


/************************calculateDetailDir******** *****************************/


void calculateDetailDir(char *dir, uint16_t heading)
{
dir[1] = ' ';
dir[2] = '\0';
dir[3] = '\0';


if ((heading <= 11) || (heading >=349)) dir[0] = 'N';
if ((heading >= 12) && (heading <= 33))
{
dir[0] = 'N';
dir[1] = 'N';
dir[2] = 'E';
}
if ((heading >= 34) && (heading <= 56))
{
dir[0] = 'N';
dir[1] = 'E';
}
if ((heading >= 57) && (heading <= 78))
{
dir[0] = 'E';
dir[1] = 'N';
dir[2] = 'E';
}
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';
if ((heading >= 102) && (heading <= 123))
{
dir[0] = 'E';
dir[1] = 'S';
dir[2] = 'E';
}
if ((heading >= 124) && (heading <= 146))
{
dir[0] = 'S';
dir[1] = 'E';
}
if ((heading >= 147) && (heading <= 168))
{
dir[0] = 'S';
dir[1] = 'S';
dir[2] = 'E';
}
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';
if ((heading >= 192) && (heading <= 213))
{
dir[0] = 'S';
dir[1] = 'S';
dir[2] = 'W';
}
if ((heading >= 214) && (heading <= 236))
{
dir[0] = 'S';
dir[1] = 'W';
}
if ((heading >= 237) && (heading <= 258))
{
dir[0] = 'W';
dir[1] = 'S';
dir[2] = 'W';
}
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';
if ((heading >= 282) && (heading <= 303))
{
dir[0] = 'W';
dir[1] = 'N';
dir[2] = 'N';
}
if ((heading >= 304) && (heading <= 326))
{
dir[0] = 'N';
dir[1] = 'W';
}
if ((heading >= 327) && (heading <= 348))
{
dir[0] = 'N';
dir[1] = 'N';
dir[2] = 'W';
}
}


/********************* ULTRASCHALL & HC-SR-04 ******************************/


//uint16_t distanz;
//volatile uint16_t zeit;
//volatile uint16_t timestamp_last;
volatile uint16_t dauer;




ISR(TIMER1_CAPT_vect)
{
//Wenn steigende Flanke
if(TCCR1B & (1<<ICES1))
{
//Flankenerkennung auf fallend
TCCR1B ^= (1<<ICES1);
//aktuelen timer-wert speichern
timestamp_last = ICR1;
}
//fallende Flanke
else
{
//Flankenerkennung auf steigend
TCCR1B ^= (1<<ICES1);
//Laufzeit = aktueller timerwert - vorheriger timerwert
zeit = ICR1 - timestamp_last;
}


}






/*************** trig ***************************/


void trig(void)
{
PORTC |= (1<<PC6);//Trig high
_delay_us(12);
PORTC &= ~(1<<PC6);//TRIG auf low
}




/*********************trig PC6**********************/


void trig_PC6(void)
{
PORTC |= (1<<PC6);//Trig high
_delay_us(12);
PORTC &= ~(1<<PC6);//TRIG auf low
}


/********************read_PC5**********************/


void read_PC5(void)
{
loop_until_bit_is_set(PINC, PC5);
setStopwatch02(0);




loop_until_bit_is_clear(PINC, PC5);
dauer = getStopwatch02();


//distanz_schleife = (dauer*34.3)/2;
distanz = ((dauer/2)*3.43);
}










/***************** messung_SR_04 ********************/


void messung_SR_04 (void)
{
DDRC |= (1 << PC6);//Trig als Ausgang
PORTC &= ~(1<<PC6);//TRIG auf low


DDRD &= ~(1<<PD6);//Echo als Eingang
PORTD &= ~(1<<PD6);//ECHO pullup AUS




//Timer konfigurieren
TCCR1A = 0; // normal mode, keine PWM Ausgänge
//Noise Canceler aktivieren, Flankenerkennung auf steigende, Prescaler auf 64
TCCR1B |= (1<<ICNC1) | (1<<ICES1) | (1<<CS11) |(1<<CS10);


//ICP Interrupt aktivieren
TIMSK |= (1<<TICIE1);


//Globale Interrupts aktivieren
sei();
distanz = (zeit*4)/58;

}


und eine standard.h" in dieser form:

#ifndef STANDARD_H_
#define STANDARD_H_


/************** standard *************************/


void I2C_transmissionError(uint8_t errorState);


void I2C_requestedDataReady(uint8_t dataRequestID);


void writeDoubleLCD(double number, uint8_t width, uint8_t prec);


void writeDouble(double number, uint8_t width, uint8_t prec);


void accuspannung(void);


void acculadung(void);


void accuzustand(void);


void watchDogRequest(void);


void task_LCDHeartbeat(void);


void fahr_bis_schwarz (void);


/************** IR-bake **************************/


void readAllRegisters(void);


void print_Register_30(void);


void read_Register_30(void);


void bake_suche(void);


/******************* gyro & nordsuche ************/


void test_rdir(void);


void korrekrur_richtung(void);


void sensorwerte_holen(void);


void nordsuche (void);


void nordsuche_links (void);


void calculateDir(char *dir, uint16_t heading);


void calculateDetailDir(char *dir, uint16_t heading);




/************ ultraschall **** HC-SR-04 ***********************/


void trig(void);


void trig_PC6(void);


void messung_SR_04 (void);


void read_PC5(void);




#endif /*STANDARD_H_*/


ginge das?

Sisor
05.03.2016, 06:00
Folgendes verstehe ich inhaltlich nicht und kann daher deine Frage nicht genau nachvollziehen:
ich habe nun versucht nach Deinem tutorial daraus eine lib zu machen, allerdings bin ich über das erste stadium - in dem die funktionen in einer "vier_stepper.h" im gleichen verzeichnis ausgegliedert werden - nicht hinausgekommen. Das reicht mir nicht, weil ich ja immer noch dutzende von "vier_stepper.h" habe...

Mehrere Funktionen in einer Bibliothek zusammenzufassen, geht in C genau wie in C++. Eine .h-Datei enthält Deklarationen und eine .c bzw. .cpp-Datei definiert diese dann. Das ist auch die Standard-Vorgehensweise bei Arduino-Bibliotheken.

inka
05.03.2016, 09:01
ich möchte so eine arduino library erzeugen (es ist jetzt einfachheitshalber nur die funktion "alle_stepper_vorwaerts()" drin):


"vier_stepper.cpp":

#include "vier_stepper.h"


void alle_stepper_vorwaerts(void)
{
if (start_ping == true) ping_distanz();


if (hindernis == true)
{
Serial1.print(hindernis);
Serial1.println(" hindernis - true - fahre rückwärts - US- abfrage in alle stepper vorwärts");
Serial.print(hindernis);
Serial.println(" hindernis - true - fahre rückwärts - US- abfrage in alle stepper vorwärts");
hindernis = false;




for (idx = stepper_VL; idx < stepper_MAX; idx++) //alle stepper rückwärts
{
stepper[idx].setRPM(12);
stepper[idx].setSPR(4075.7728395);
stepper[idx].setDirection(CCW);
stepper[idx].rotateDegrees(10); //rotate(1)
}
fahrt_ausfuehren();
}
else
{
hindernis = false;


Serial.print(hindernis);
Serial1.println(" freie fahrt - alle stepper vorwärts");
Serial.print(hindernis);
Serial.println(" freie fahrt - alle stepper vorwärts");


for (idx = stepper_VL; idx < stepper_MAX; idx++)//alle stepper vorwärts
{
stepper[idx].setRPM(12);
stepper[idx].setSPR(4075.7728395);
stepper[idx].setDirection(CW);
stepper[idx].rotateDegrees(5);//rotate(1)
}
fahrt_ausfuehren();
}
}
}




und eine "vier_stepper.h":

#ifndef VIER_STEPPER_H_
#define VIER_STEPPER_H_

void alle_stepper_vorwaerts(void);

#endif /*VIER_STEPPER_H_*/



meine frage ging dahin, ob es so - zu einer "vier_stepper.zip"datei zusammengepackt und in der IDE als lib hinzugefügt - ohne all die stufen der bearbeitung die Du (wegen der objektorientierung, der kapselung, der übersichtlichkeit des codes und des besseren programierstils) im tutorial aufgeführt hast - auch ginge?

Sisor
05.03.2016, 09:27
Das geht, ist auch einfach.
Wenn Du Windows benutzt, wird standardmäßig im Ordner 'Dokumente' der Ordner 'Arduino' angelegt. Dort befindet sich der Ordner 'libraries'. Hier kannst du deinen eigenen Ordner erzeugen, z.B. 'VierStepper', und die 'vier_stepper.h' und 'vier_stepper.cpp' hineintun.
Dann die Arduino IDE starten. Unter 'Sketch' -> 'Bibliothek importieren' steht dir nun deine Bibliothek zur Verfügung.

inka
05.03.2016, 09:52
ok, danke, bei linux ist es ähnlich mit den ordnern...

inka
07.02.2017, 09:14
noch eine frage:

diese funktion wird in der "L293D_V2_fahren.cpp" so definiert:


void vorwaerts(void)
{
motor_hl->setSpeed(100);
motor_hl->run(FORWARD);
encoder_1_auslesen();
if (summe_1 >= 100)
{
summe_1 = 0;
vorwaerts_1 = 0;
motor_stop();
}
}

und ist so deklariert in der "L293D_V2_fahren.h:"


#ifndef L293D_V2_FAHREN_H_
#define L293D_V2_FAHREN_H_

void vorwaerts(void);

#endif /*L293D_V2_FAHREN_H_*/


sie wird ganz normal aufgerufen in einer *.ino datei mit "vorwaerts()" und der motor dreht sich so lange, bis die vorgegebene anzahl der ticks, die vom encoder mit "encoder_1_auslesen()" ausgelesen wird, auch erreicht wird. Das funktioniert so auch ganz gut...

Ich möchte das ganze nun etwas komfortabler, flexibler und auch praxisnäher machen, denn die anzahl der ticks, die zeit in sec. oder was auch immer (die jetzt hier starr vorgegeben sind) soll später von dem auslesen der entfernung zum hindernis (z.b.) abhängig an die funktion weiter gereicht werden...

Nach unzähligen versuchen und suche nach "wertübergabe an eine funktion" in unzähligen variationen hier die frage: Wie mache ich das? Es ist anders/komplizierter, als wenn die funktion in der *.ino datei selbst wäre und nicht in der lib ausgelagert, ich muss ja auch die deklaration der funktion in "L293D_V2_fahren.h" anpassen...