Blink.ino
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
SimpleLed.ino
class Led {
private:
uint8_t _pin;
public:
Led(const uint8_t pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
void on() {
digitalWrite(_pin, HIGH);
}
void off() {
digitalWrite(_pin, LOW);
}
};
Led statusLed(LED_BUILTIN);
void setup() {}
void loop() {
statusLed.on();
delay(1000);
statusLed.off();
delay(1000);
}
Led.h
class Led {
private:
uint8_t _pin;
public:
Led(const uint8_t pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
void on() {
digitalWrite(_pin, HIGH);
}
void off() {
digitalWrite(_pin, LOW);
}
};
Led.cpp
#include "Led.h"
Led::Led(const uint8_t pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
void Led::on() {
digitalWrite(_pin, HIGH);
}
void Led::off() {
digitalWrite(_pin, LOW);
}
Led2.h
#ifndef OO_LED
#define OO_LED
#include <Arduino.h>
class Led {
private:
uint8_t _pin;
public:
Led(const uint8_t pin);
void on();
void off();
};
#endif
Blink.ino
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
BlinkRaw.ino
void setup() {
DDRB = 0b00100000;
}
void loop() {
PORTB = 0b00100000;
delay(1000);
PORTB = 0b00000000;
delay(1000);
}
BlinkBits.ino
void setup() {
DDRB |= 0b00100000;
}
void loop() {
PORTB |= 0b00100000;
delay(1000);
PORTB &= 0b11011111;
delay(1000);
}
BlinkBV.ino
void setup() {
DDRB |= _BV(5);
}
void loop() {
PORTB |= _BV(5);
delay(1000);
PORTB &= ~_BV(5);
delay(1000);
}
BlinkMain.ino
#include <avr/io.h>
#include <util/delay.h>
int main() {
DDRB |= _BV(DDB5);
while (true) {
PORTB |= _BV(PORTB5);
_delay_ms(1000);
PORTB &= ~_BV(PORTB5);
_delay_ms(1000);
}
}
Button.ino
const uint8_t BUTTON = 2;
uint8_t buttonState = 0;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(BUTTON, INPUT);
}
void loop() {
buttonState = digitalRead(BUTTON);
if (buttonState == HIGH) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
ButtonMitInterrupt.ino
const uint8_t BUTTON = 2;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(BUTTON, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON),:.switchPressed, CHANGE);
}
void loop(){}
void switchPressed() {
if (digitalRead(BUTTON) == HIGH)
digitalWrite(LED_BUILTIN, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
}
BlinkMitInterruptVolatile.ino
const uint8_t BUTTON = 2;
volatile uint8_t buttonStatus = LOW;
void setup () {
pinMode(LED_BUILTIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON), switchPressed, :.CHANGE);
}
void loop (){
digitalWrite(LED_BUILTIN, buttonStatus);
}
void switchPressed() {
buttonStatus = digitalRead(BUTTON);
}
BlinkMitTimer.ino
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
OCR1A = 15624;
TCCR1B |= _BV(WGM12);
TCCR1B |= _BV(CS10);
TCCR1B |= _BV(CS12);
TIMSK1 |= _BV(OCIE1A);
interrupts();
}
ISR(TIMER1_COMPA_vect) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void loop() {
Serial.println("Hallo.");
delay(1000);
}
BlinkMitTimerOverflow.ino
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 65535 - 15624;
TCCR1B |= _BV(CS10);
TCCR1B |= _BV(CS12);
TIMSK1 |= _BV(TOIE1);
interrupts();
}
ISR(TIMER1_OVF_vect) {
TCNT1 = 65535 - 15624;
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void loop() {
Serial.println("Hallo.");
delay(1000);
}
BlinkAVR.ino
#include <avr/io.h>
int main() {
DDRB |= _BV(DDB5);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
OCR1A = 15624;
TCCR1B |= _BV(WGM12);
TCCR1B |= _BV(CS10);
TCCR1B |= _BV(CS12);
TIMSK1 |= _BV(OCIE1A);
interrupts();
while (true) ;
}
ISR(TIMER1_COMPA_vect) {
PORTB ^= _BV(PORTB5);
}
Shields und Module mit SD-Karten-Slots gibt es reichlich und in jeder Preisklasse.
Sechs Pins reichen, um ein SD-Modul (inklusive Pegelwandler) mit dem Arduino zu verbinden.
Die obige Abbildung ist nur exemplarisch und variiert von Modul zu Modul.
FAT32 DateiSystem
Um Daten auf Massenspeichern strukturiert und wiederauffindbar ablegen zu können, bedarf es eines Dateisystems.
Dazu gehört mindestens ein Verzeichnis, das Informationen zu abgelegten Daten
aufnimmt, beispielsweise ein Zuordnungsname (vulgo Dateiname) so wie
Daten über den Ort, wo die Datei auf dem Datenträger beginnt. Das Dateisystem dient als Schnittstelle zwischen
Massenspeicher und Anwendungen.
Die Anwendung bittet etwa das Dateisystem, eine Datei bestimmten Namens zu öffnen, um lesend darauf zuzugreifen.
Das Dateisystem initialisiert einen internen Zeiger auf den Anfang der Datei, liefert das erste Byte zurück und erhöht
den Zeiger um 1.
Durch weitere Aufrufe erhält die Anwendungen sequenziell alle weiteren Daten.
Ein Schreibvorgang funktioniert analog dazu.
Um die interne Organisation des Massenspeichers und ob etwa eine Festplatte mit runden Scheiben, eine SSD, ein
USB-Stick oder eine SD-Karte mit NANDFlash oder eine DVD-RW benutzt wird,
muss man sich keinerlei Gedanken mehr machen.
Stattdessen kümmert sich das Dateisystem um die Größe von Blöcken, Clustern, Sektoren und Partionen.
Diverse Dateisysteme weisen unterschiedliche Eigenschaften und damit je nach Einsatzzweck verschiedene
Vor- und Nachteile auf. Eines der am weitesten verbreiteten ist FAT32, mit
dem auch viele Datenträger vorformatiert sind.
Die SD-Bibliothek
Der Arduino lernt den Umgang mit FAT32.
Karteninfo.ino
#include <SD.h>
const uint16_t BAUD_RATE = 9600;
const uint8_t CHIP_SELECT_PIN = 10;
void setup() {
Serial.begin(BAUD_RATE);
Serial.print("SD-Karte wird initialisiert... ");
Sd2Card card;
if (!card.init(SPI_HALF_SPEED, CHIP_SELECT_PIN)) {
Serial.println("Die Karte konnte nicht :.initialisiert werden.");
return;
} else {
Serial.println("Alles prima!");
}
Serial.print("\nKarten-Typ: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD V1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD V2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unbekannt");
}
SdVolume volume;
if (!volume.init(card)) {
Serial.println("Es konnte keine :.FAT16/FAT32-Partition gefunden werden.");
return;
}
Serial.print("Dateisystem: FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();
uint64_t kapazitaet = volume.blocksPerCluster();
kapazitaet *= volume.clusterCount();
kapazitaet *= 512;
Serial.print("Kapazität (Bytes): ");
char buffer[100];
sprintf(buffer, "%0ld", kapazitaet / 1000000L);
Serial.print(buffer);
sprintf(buffer, "%0ld", kapazitaet % 1000000L);
Serial.println(buffer);
Serial.print("Kapazität (KBytes): ");
uint32_t kapazitaet_kb = kapazitaet /= 1024;
Serial.println(kapazitaet_kb);
Serial.print("Kapazität (MBytes): ");
uint32_t kapazitaet_mb = kapazitaet_kb /= 1024;
Serial.println(kapazitaet_mb);
Serial.println("\nDie Karte enthält die folgenden :.Dateien: ");
SdFile root;
root.openRoot(volume);
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop() {}
DataLogger.ino
#include <SD.h>
const uint8_t CHIP_SELECT_PIN = 4;
const uint16_t BAUD_RATE = 9600;
File logDatei;
void setup() {
Serial.begin(BAUD_RATE);
if (!SD.begin(CHIP_SELECT_PIN)) {
Serial.println("SD-Karte konnte nicht initialisiert werden.");
return;
}
logDatei = SD.open("datalog.csv", FILE_WRITE);
if (!logDatei) {
Serial.println("Die Log-Datei konnte nicht geöffnet werden.");
return;
}
}
void loop() {
const uint8_t MAX_PIN = 3;
String sensorDaten = "";
for (uint8_t analogPin = 0; analogPin < MAX_PIN; analogPin++) {
uint16_t sensor = analogRead(analogPin);
sensorDaten += String(sensor);
if (analogPin < MAX_PIN - 1) {
sensorDaten += ",";
}
}
Serial.println(sensorDaten);
logDatei.println(sensorDaten);
logDatei.flush();
delay(500);
}
Format für Daten-Logger
Wer Daten auf eine SD-Karte schreibt, muss zuvor ein Datenformat festlegen. Die Auswahl ist groß und hängt davon ab, mit
welcher Anwendung die gesammelten Daten weiterverarbeitet werden.
Als Faustregel gilt, dass Binärdaten nur in Ausnahmefällen in Frage kommen. Eine etwaige Platzersparnis durch die kompaktere Speicherung
rechtfertigt in den seltensten Fällen die damit einhergehenden Nachteile. Binärdaten können nicht mit einem Text-Editor
inspiziert werden, sondern bedürfen spezieller Software, um sie anzusehen. Sie sind oft nicht portabel, weil sie auf verschiedenen
CPU-Architekturen unterschiedlich interpretiert werden.
Text-Formate haben diese Nachteile nicht und lassen sich einfach erzeugen und lesen. Besonders beliebt ist das CSV-Format (Character-
Separated Values), das so gut wie alle Tabellenkalkulationsprogramme und Datenbanken importieren kann.
In einer CSV-Datei werden Daten zeilenweise gespeichert und jede Zeile enthält dieselbe Anzahl an Werten. Benachbarte Werte
werden durch dasselbe Zeichen (meistens ein Komma, Semikolon oder Tabulator-Zeichen) getrennt.
Selbst vermeintlich einfache Formate wie CSV stecken jedoch voller Tücken.
Auf dem Arduino beginnt das mit dem Dezimalpunkt bei Fließkommazahlen.
Die print()-Funktionen verwenden als Dezimal-Trennzeichen nämlich einen Punkt und kein Komma.
Das ist vorteilhaft, wenn man ein Komma als CSV-Trennzeichen verwendet.
Es führt aber zu Problemen, wenn die Landes-Einstellungen der Tabellenkalkulation ein Komma als Dezimal-Trennzeichen erwarten.
Weitere Alternativen sind XML (eXtensible Markup Language) und JSON (JavaScript Object Notation). Beides sind ebenfalls Text-Formate, orientieren sich aber nicht an Zeilen, sondern an Dokumenten.
Um XML- oder JSON-Dokumente zu erzeugen, muss man Daten daher oft vorm Schreiben puffern, und wenn mal irgendetwas schiefgeht, sind die Ergebnis-Dokumente mit hoher Wahrscheinlichkeit kaputt.
Diese Nachteile sind allesamt bekannt und so gibt es mittlerweile sogar eine zeilenorientierte Version von JSON
http://jsonlines.org/
DataLoggerZeitstempel.ino
#include
#include "RTClib.h"
const uint8_t CHIP_SELECT_PIN = 10;
const uint16_t BAUD_RATE = 9600;
File logDatei;
RTC_DS1307 rtc;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(BAUD_RATE);
if (!rtc.begin()) {
Serial.println("Konnte keine Uhr finden.");
return;
}
if (!rtc.isrunning()) {
Serial.println("Die Uhr läuft nicht.");
}
SdFile::dateTimeCallback(ermittle_zeitstempel);
if (!SD.begin(CHIP_SELECT_PIN)) {
Serial.println("SD-Karte konnte nicht initialisiert werden.");
return;
}
logDatei = SD.open("datalog.csv", FILE_WRITE);
if (!logDatei) {
Serial.println("Die Log-Datei konnte nicht geöffnet werden.");
return;
}
}
void loop() {
DateTime now = rtc.now();
char buffer[11];
sprintf(buffer, "%4d-%02d-%02d", now.year(), now.month(), now.day());
logDatei.print(buffer);
logDatei.print(",");
sprintf(buffer, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
logDatei.print(buffer);
logDatei.print(",");
String sensorDaten = ermittle_sensor_daten();
Serial.println(sensorDaten);
digitalWrite(LED_BUILTIN, HIGH);
logDatei.println(sensorDaten);
logDatei.flush();
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
String ermittle_sensor_daten() {
const uint8_t MAX_PIN = 3;
String sensorDaten = "";
for (uint8_t analogPin = 0; analogPin < MAX_PIN; analogPin++) {
uint16_t sensor = analogRead(analogPin);
sensorDaten += String(sensor);
if (analogPin < MAX_PIN - 1) {
sensorDaten += ",";
}
}
return sensorDaten;
}
void ermittle_zeitstempel(uint16_t* datum, uint16_t* uhrzeit) {
DateTime now = rtc.now();
*datum = FAT_DATE(now.year(), now.month(), now.day());
*uhrzeit = FAT_TIME(now.hour(), now.minute(), now.second());
}