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

Dario Fava, 2018/01/07 12:34
Hallöchen liebe Gemeinde ,



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
Bernd Runge, 2017/11/05 13:57
Hallo ich habe eine Frage, ist es so gewollt das immer ein Kanal aktiv ist.
( 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
Sherzod, 2015/07/27 13:38
Hello friend. I have<a href="http://uekmtdcw.com"> alomst</a> the project done, but i have some doubts. In your arduino code i see that you have this: //board setup pinMode(boardInPin, INPUT); pinMode(boardOutPin, OUTPUT);Why one is input and the other are output?If in the multiplex are connected all the selector cables.How i send voltage to the matrix? I test the configuration on the breadborad connecting the independent inputs/outputs from the first multiplex one wire at 5v and other from the second multiplex to the 5v and the arduino recive the wires connected at the selector cables, but i have all multiplex connected as imput.what i have wrong?ThanksKing regards
Dipl.-Ing. Wilfried Klaas, 2015/08/21 10:17
Hello,
sorry for my late return. I just don't understand, because in this project there ist no line with pinMode(boardInPin, INPUT);
On which project are you working?
Lala, 2015/07/23 14:49
Udo I would just like to thank you for your interesting LED cdiong for examples, like the Knightrider with PWM. I am finding it useful as a learning exercise modifying your code and just using the eight LED's in a line that I tend to use for trying and modifying peoples code as an aid to developing my very basic cdiong skills. I just declared an eight element array so I could change the 20 in the setup for loop down to the 8 required for my minimal setup. It runs perfectly on my Windows XP system. Anyway thanks again and hopefully I'll see you on the Arduino forum.Peter Newman / Pedro147 on Arduino forum.
Willi Preugschat, 2014/11/11 18:13
... so ein kleiner Fehler und so viel Fehlertext Wieder was dazu gelernt.
Danke!

Gruß
Willi
Willi Preugschat, 2014/11/10 18:48
Hallo Willie,

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
Wilfried KLaas, 2014/11/11 08:59
Hallo Willi

da hat sich der Fehlerteufel eingeschlichen. In der Zeile davor muss es
const byte PIN_RC = 2;
heissen. Da fehlte ein Semikolon...
Willi Preugschat, 2014/11/11 18:18
... so ein kleiner Fehler und so viel Fehlertext Wieder was dazu gelernt.
Danke!

Gruß
Willi
Melden Sie sich an, um einen Kommentar zu erstellen.
arduino/modellbau/projekte/12-kanal-schalter.txt · Zuletzt geändert: 2018/11/04 11:51 (Externe Bearbeitung)
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0