1/2-Kanal Schalter
Author: Dipl.-Ing. Wilfried Klaas
Board: Arduino Duemilanove, ATTiny85
Hier mein eigenes Projekt. Ein einfacher 1/2Kanalschalter. Die Software ist geschrieben auf dem Arduino, läuft aber auch auf einem ATTiny85.
Platinen für das Projekt gibt's bei mir. Genauso wie auch fertig programmierte Chips. Aber hier wollen wir ja was lernen.
Zum Testen würde ich dsan ganze erstmal auf einer Steckplatine aufbauen und zwar so:
Der Empfänger kommt an Pin 2, die beiden LED's (an Pin 0 und 1) sind stellvertretend für die späteren Relais.
Mit den beiden Schaltern kann man jeden Kanal zwischen Toggle und Dauerbetrieb umschalten.
Impulsbetrieb heisst, einmal Hebel bewegen schaltet ein, nochmal bewegen schaltet aus.
Schaltbetrieb heisst, solange der Hebel ja nach Kanal in die eine oder andere Richtung steht, ist die LED an.
Zunächst ein paar Konstanten. Sowas wie die verwendeten Pins oder auch die Konstanten für die RC Anbindung.
Im setup()
gibt's nix besonderes. Die Ein/Ausgänge werden entsprechend konfiguriert.
Dann die loop()
. Zunächst werden mit digitalRead die Einstellungen für Dauer/Imüulsbetrieb der Kanäle gelesen. Dann wird der aktuelle Wert vom Empfänger gelesen. Befindet sich dieser innerhalb der gültigen Werte kommt der Wert in einen Ringspeicher. DAnn lese ich den aktuellen gemittelten Wert aus dem Ringspeicher.
Zunächst müsssen wir noch den Nullpunkt bestimmen, dseswegen werden gleich nach dem Start und nachdem die Empfängersignale gültig sind, einfach erstmal 10 Werte gelesen. Daraus wird der Mittelwert bestimmt und das gibt den Nullpunkt.
Erst nach dem Lesen der 10 Werte ist der Schalter einsatzbereit. Im Else Zweig der if (initNP) geht's dann zur Ausgabe.
Beim Schaltbetrieb wird einfach der RC-Wert mit dem Nullpunkt verglichen und der entsprechende Kanal aktiviert.
Beim Impulsbetrieb wird nach den Flanken geschaut. Wird eine positive (oder negative Flanke ja nach Kanal) entdeckt, dann wird der entsprechende Ausgang getoggelt, d.h. umgeschaltet.
Eine Besonderheit ist die Mittelwertbildung der RC-Signale und die Fehlerbehandlung. Kommen mehr als 3 fehlerhafte Impulse vom Empfänger (oder auch garkeine) dann werden beide Kanäle bageschaltet. (Failsafe)
DIe Mittelwertbildung geht über den Ringpuffer, der höchste und der niedrigste Wert werden ignoriert und der Mittelwert wird von den restilichen von 8 Werten gebildet. Das Verzögert zwar den Schaltprozess, aber gelegentliche Ausreisser, die immer mal wieder vorkommen, werden so sicher unterdrückt. Insgesamt hat der Schalter dann eine Verzögerungszeit von max. 200ms. Das ist für die meisten Einsatzbereiche tolerierbar.
Mit leichten Modifikationen kann das Programm auch auf einem normalen Arduino laufen. Einfach nur die Pins anpassen.
Hier der Quellcode.
- 12Switch.ino
/* Schaltprogramm für den ATTiny 85. Der Empfänger wird vom Pin 2 gelesen, die Ausgänge sind auf Pin 0 und 1. Über den Pin 3/4 wird gelesen, ob der jeweilige Kanal im Impulsbetrieb oder Dauerbetrieb geschaltet werden soll. Impulsbetrieb: einmaliges Umlegen des Hebels schaltet ein bzw. aus Dauerbetrieb: das Relais ist solange geschlossen, wie der Hebel in die entsprechende Richtung gelegt wird. Progbrammablauf: Der Nullpunkt wird zunächst durch interpolation der ersten 10 Werte vom Empfänger festgestellt. Fehlerhafte Werte, also Werte ausserhalb von 900-2100 werden ignoriert. Werden mehr als 3 fehlerhafte Werte hintereinander gefunden, werden die Ausgänge auf null geschaltet. (Failsafe) Die Schaltschwelle für den normalbetrieb ist auf 250ms festgelegt. */ //const byte RC_INT = 0; // das ist INT 0 aber Pin 2!!!! const byte PIN_RC = 2; // das ist INT 0 aber Pin 2!!!! // Ausgänge const byte CAN_1 = 0; const byte CAN_2 = 1; // Eingänge const byte PIN_IMPULS_1 = 3; const byte PIN_IMPULS_2 = 4; // Maximale Anzahl der Fehler const byte MAX_ERRORS = 3; // Konstanten für die RC Erkennung const int MIN_RC_VALUE = 900; const int MAX_RC_VALUE = 2100; /* Konstante für die Erkennung des Schaltbefehls. Bei Kanal 1 wäre das Nullpunkt + SWITCH_STEP bei Kanal 2 entsprechend Nullpunkt - SWITCH_STEP */ const int SWITCH_STEP = 250; // letzter Wert vom Empfänger volatile unsigned long RcValue; // Anzahl der tolerierten Fehler byte error = MAX_ERRORS; // Größe des Pufferspeichers const byte stackSize = 10; // Ringspeicher mit den letzten 10 Werten vom Empfänger (Fehlerwerte werden direkt ausgeblendet.) int stack[stackSize]; // Laufvariable des Ringspeichers byte stackIndex = 0; // Impulseinstellung pro Kanal boolean impuls_1; boolean impuls_2; // Speicher für alte Werte pro Kanal boolean oldValue_can1 = 0; boolean oldValue_can2 = 0; // Zwischenspeicher int myRcValue; int maxSwitch; int minSwitch; // Nullpunktbestimmung boolean initNP; void setup() { // Kanäle auf Ausgang, und dann deaktivieren pinMode(CAN_1, OUTPUT); pinMode(CAN_2, OUTPUT); digitalWrite(CAN_1, LOW); digitalWrite(CAN_2, LOW); // Eingang für Impuls 1 pinMode(PIN_IMPULS_1, INPUT); digitalWrite(PIN_IMPULS_1, HIGH); // Eingang für Impuls 2 pinMode(PIN_IMPULS_2, INPUT); digitalWrite(PIN_IMPULS_2, HIGH); // Eingang für RC pinMode(PIN_RC, INPUT); digitalWrite(PIN_RC, HIGH); initNP = true; error = MAX_ERRORS; } void loop() { // Lesen der aktuellen Impuls-Einstellungen von den Kanälen impuls_1 = digitalRead(PIN_IMPULS_1) == 0; impuls_2 = digitalRead(PIN_IMPULS_2) == 0; // Aktuellen RC-Wert lesen RcValue = pulseIn(PIN_RC, HIGH, 100000); // Wert innerhalb der Toleranzgrenze if ((RcValue > MIN_RC_VALUE) && (RcValue < MAX_RC_VALUE)) { // Fehlerspeicher zurück setzen error = MAX_ERRORS; // Wert in den Ringpuffer schreiben stack[stackIndex%stackSize] = RcValue; stackIndex++; // den aktiven RC Wert holen myRcValue = getRcValue(); // Nullpunktsbestimmung ? if (initNP) { // Schon alle Werte gelesen? if (stackIndex == stackSize) { // die ersten 10 Werte dienen der Nullpunkt bestimmung int nullpoint = myRcValue; initNP = false; // Schaltschwellen definieren maxSwitch = nullpoint + SWITCH_STEP; minSwitch = nullpoint - SWITCH_STEP; } } else { // Kanal 1 auf Impuls? if (impuls_1) { doImpuls1(); } else { // Ausgang schalten. digitalWrite(CAN_1, myRcValue > maxSwitch); } // Kanal 2 auf Impuls? if (impuls_2) { doImpuls2(); } else { // direkt schalten digitalWrite(CAN_2, myRcValue < minSwitch); } // ein bisschen verzögern delay(100); } } else { // Fehlerfall if (error == 0) { // failsafe ... digitalWrite(CAN_1, LOW); digitalWrite(CAN_2, LOW); } else { // Fehler zählen error--; } } } // Impulsschalten für Kanal 1 void doImpuls1() { // Änderung am Empfängerwert ? boolean newValue = myRcValue > maxSwitch; if (oldValue_can1 != newValue) { // neuen Wert schreiben, aber nur an der positiven Flanke if (newValue) { digitalWrite(CAN_1, !digitalRead(CAN_1 )); } oldValue_can1 = newValue; } } // Impulsschalten für Kanal 2 void doImpuls2() { // Änderung am Empfängerwert boolean newValue = myRcValue < minSwitch; if (oldValue_can2 != newValue) { // Nur Schalten bei negativer Flanke if (newValue) { digitalWrite(CAN_2, !digitalRead(CAN_2 )); } oldValue_can2 = newValue; } } /* Bestimmung des durchschmittlichen RC-Wertes. Es werden zunächst alle Werte aufsummiert. Der höchste und der Niedrigste werden jedoch ignoriert. Dann wird der Mittelwert gebildet. */ int getRcValue() { int all = 0; int minValue = stack[0]; int maxValue = stack[0]; all = stack[0]; for (int i = 1; i < stackSize; i++) { int value = stack[i]; // Neuer Höchstwert ? if (value > maxValue) { // neuen speichern maxValue = value; } // Neuer Niedrigstwert? if (value < minValue) { // neuen speichern minValue = value; } all += value; } // Höchst und Niedrigstwert wieder abziehen all -= minValue; all -= maxValue; // Mittelwert bilden all = all / (stackSize - 2); return all; }
Diskussion
ich habe sehr lange nach einer Memory Schaltung für den Andruino gesucht und bin hier fündig geworden.
Es ist genau das was ich gesucht habe , ich bastle gerade an einem Feuerwehr Fahrzeug und möchten diese Schalter zum schalten von Sound und Licht.
Dieser Sketch funktionierte direkt auf Anhieb und bin begeistert .
Kann man diesen Sketch erweitern auf 6 bzw 8 Empfängerkanäle ,
falls Pins zu wenig vorhanden wäre würde ich auf die Impuls Schaltung verzichten
dann würden die pins für die Schalter wegfallen ich bräuchte nur Memory Schaltung .
Ich bin überhaupt nicht fit beim Programmieren ich mache eher Handwerk .
Einen Obulus würde ich dem Schreiber gerne schicken ,
soll ja nicht umsonst die ganze Arbeit sein...
lieben gruss
Dario Fava
( durchgeschaltet ist ) Ich beschäftige mich erst seit kurzem mit den Arduino,
ist es möglich das Programm so zu ändern das bei Knüppel Nullstellung kein
Kanal geschaltet ist und nur bei Betätigung des Knüppel's der entsprechenden
Ausgang geschaltet wird (drei Wegeschalter)
MfG Bernd
Danke!
Gruß
Willi
ich habe mir erst vor Kurzem den Arduino Uno zugelegt und etliches hat schon gut funktioniert. Hier bei Deinem Sketch stehe ich allerdings auf dem Schlauch, weil ich mit dem Fehlerhinweis nichts anfangen kann. Oder kommt der Fehler daher, dass ich den Sketch unverändert auf den Arduino laden wollte?
In dem Sketch ist folgende Zeile markiert: const byte CAN_1 = 0;
weiter unten steht rot unterlegt "expected `,`or`,`before `const ´"
Gruß
Willi