Inhaltsverzeichnis

Flug ESC an Autofernsteuerung

Author: Dipl.-Ing. Wilfried Klaas

Board: Arduino Uno, Arduino Leonardo, ATTiny45/85

Einleitung

Gerade im Rennbootsektor wird gerne mit einer Pistolenfernsteuerung und Brushless-Flugreglern gearbeitet. Leider haben viele Regler dann das Problem den Nullpunkt der Pistolensteuerung (der ja in der Mitte liegt) nicht oder nicht richtig erkennen zu können. Bei teureren Sendern kann man das manchmal in der Software korrigieren. Den günstigeren Sendern fehlt meisten eine solche Funktion.
Weiterhin ist es sehr ärgerlich, daß viele Servos zwar mit z.B. 50° angegeben werden, diese aber an der eigenen Anlage dann doch nicht schaffen. Das 2. Programm erweitert den Weg des Servos. Hierbei ist jedoch Vorsicht geboten, denn schnell kann der Servo an die mechanischen Grenzen stossen…
Beide Programme werden per Jumper umgeschaltet.

Vergleich

Hier mal eine kurze Tabelle mit den Auswirkungen der Servo-Funktion.

GT-2 von Conrad

Servo ohne Modul mit Modul
HK16178 90° 135°
Graupner C 508 95° 150°
Blue Bird BMS 706-MG 90° -
MP Tiger Digi 4 95° 140°

GT-3

Servo ohne Modul mit Modul
TGY 9018MG 90° 160°

Spektrum DX3S mit SR3000

Servo ohne Modul mit Modul
HK16178 70° 135°
Graupner C 508 85° 125°
Blue Bird BMS 706-MG 75° 115°
MP Tiger Digi 4 80° 120°
Servo ohne Modul mit Modul
Conrad S-811MG 70° 120°
Acmos AS-12 90° 150°

Die angegebenen Werte sind natürlich ohne Gewähr. Sie unterscheiden sich auch von RC Anlage zu RC Anlage.

Programm

Aber wir können ja programieren. Also flux ein kleines Programm geschrieben, was den eingeschränkten Bereich des Abzugsknüppel auf den erweiterten Bereich des Reglers abbildet. Nebenbei habe ich auch gleich ein 2. Programm untergebracht, welches den Servoweg vergrößert. Der Teil ist aber mit Vorsicht zu behandeln. Denn nicht jeder Servo verträgt ein Signal von 700..2200us. Manche Servos können dadurch mechanisch an ihre Grenzen stossen. Also das ganze ist auf eigene Gefahr.
Die Software selber macht nur minimal Gebrauch von ext. Bibliotheken und passt sogar in eine Tiny45.
Wir benutzen selber auch keinen Timer oder Interrupt. Der Servo muss aber alle 20ms ein Signal bekommen. Das machen wir dadurch, daß wir einfach das Empfängersignal als Zeitbasis verwenden. Dabei machen wir folgende Annahmen.
Das Empfängersignal kommt alle 20ms und ist max. 2ms lang. (Ein Servoimpuls) Diesen werten wir mit der pulsIn (poll in der RCReceiver Bibliothek) Funktion aus. Danach berechnen wir den aktuellen Servowert. Dann setzen wir den Servopin auf 1, warten mit der delayMicroseconds() die angegebene Zeit und setzen dann den Pin wieder auf 0. Weil gerade das Setzen des Pin Zeitkritisch ist, verwende ich direkte Portzugriffe und schalte beim Servo den Interrupt aus. (PORTB …)
Das ganze läuft bei mir auf einem ATTiny 85 mit internen 16MHz.
Und wie immer, bei Fragen, einfach fragen.
UPDATE 18.11.2013: Hier jetzt die getestetet Version, die auch an meine Betatester raus gegangen ist. Platinen, mit oder ohne programmierte Controller, Bausätze und evt. auch fertige Module gibt's bei mir zum Selbstkostenpreis. Das ganze sieht so aus:
Anleitung rev 1 Anleitung zur rev 2

FESC_Small.ino
#include <makros.h>
#include <RCReceive.h>
 
/*
 FESC_Small.ino - Version 0.1.0
 Copyright (c) 2013 Wilfried Klaas.  All right reserved.
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
/*
  Steuerung eines Flug ESC mittels Car Fernsteuerng für den ATTiny x5. 
 Der Empfänger wird vom Pin 2 gelesen, 
 der Ausgang ist auf PB0 was in Arduino Pin 0 entspricht.
 Die Nullpunkte ist auf 1500us festgelegt.
 Es gibt 2 Modi, die über einen Jumper zwischen 3 und 4 (Pin 2/3 am Controller) angewählt werden können.
 Mode 1: FlugESC an CarRc
 Bei Vorwärtsfahrt (1,5ms-2ms) wird das Motorsignal gedehnt, also in den Bereich 1ms-2ms gebracht, 
 und an den Regler geschickt. 
 Bei Rückwärtsfahrt wird immer ein 1ms breites Signal an den Regler geschickt.
 Ebenso als Failsafe bei Empfängersignalverlust. Zus. blinkt dann auch noch die LED.
 
 Mode 2: Servowegerweiterung
 Das Empfängersignal (1ms-2ms) wird, ähnlich wie das Motorsignal, gedehnt, also in den Bereich 0,5ms-2,5ms gebracht, 
 und an den Servo geschickt. 
 Failsafe bei Empfängersignalverlust ist hier 1,5ms. Zus. blinkt dann auch hier die LED.
 */
 
// Definitionen
// Empfängeranschluss
const byte PIN_RC = 2;
 
// Servoausgänge
const byte SERVO0 = 0;
const byte SERVO1 = 1;
 
// freie Ein/Ausgänge
const byte AUX1 = 3;
const byte AUX2 = 4;
 
// Ein paar schöne Makros.
#ifndef between
#define between(a,x,y) ((a >=x) && (a <= y))
#endif
 
#define AUXOn(aux) \
PORTB |= _BV(aux)
// A: digitalWrite(aux, 1)
 
#define AUXOff(aux) \
PORTB &= ~_BV(aux)
// A: digitalWrite(aux, 0)
 
#define LEDOn() \
PORTB |= _BV(AUX1)
// A: digitalWrite(LED, 1)
 
#define LEDOff() \
PORTB &= ~_BV(AUX1)
// A: digitalWrite(LED, 0)
 
#define ServoOn() \
PORTB |= _BV(SERVO0)
// A: digitalWrite(SERVO0, 1)
 
#define ServoOff() \
PORTB &= ~_BV(SERVO0)
// A: digitalWrite(SERVO0, 0)
 
#define Blink() \
if ((millis() % blinkconst) > (blinkconst / 2)) { \
      LEDOn(); \
    } \
    else { \
      LEDOff(); \
    } \

// ----------------------------------------
// Hier geht's los
// ----------------------------------------
 
boolean servomode = false;
word blinkconst = 1000;
RCReceive rcReceiver;
 
void setup() {
  // Einstellung der Ports
  // 0 und 1 sind Ausgänge für Servos
  // 2,3 und 4 sind die Eingänge
  DDRB = _BV(AUX1) | _BV(SERVO0) | _BV(SERVO1);
  PORTB |= _BV(AUX2);  
 
  // Arduinoersatz
  /* A: 
   pinMode(SERVO0, OUTPUT);
   pinMode(SERVO1, OUTPUT);
   pinMode(AUX1, OUTPUT);
   pinMode(AUX2, INPUT_PULLUP);
  */
  delay(500);
 
  // Testen, ob Servo- oder FlugMode
  AUXOff(AUX1);
  // A: digitalWRite(AUX1, 0)
  servomode = ((PINB & _BV(AUX2)) == 0);
  // A: servomode = (digitalRead(AUX2) == 0);
  blinkconst = servomode ? 500 : 1000;  
  rcReceiver.attach(PIN_RC);
  delay(500);
}
 
// ----------------------------------------
// Hauptprogramm
// ----------------------------------------
 
word value = 0;
byte valueCount = 0;
 
void loop() {
  // Aktuellen RC-Wert lesen
  rcReceiver.poll();
  if (!rcReceiver.hasError() && rcReceiver.hasNP()) {
    LEDOff();
    doWork();
  } else if (rcReceiver.hasError()) {
    // Fehlerbehandlung failsafe oder sowas...
    Blink();
    servomode ? value = 1500 : value = 1000;
    cli();
    ServoOn();
    delayMicroseconds(value);
    ServoOff();
    sei();
  }
}
 
inline void doWork() {
  value = rcReceiver.getMsValue();
//  value = pulseIn(PIN_RC, HIGH, 25000); // Timeout 25ms
      if (servomode) {
        // Servo Modus
        // alle gültigen Signale werden verarbeitet
        if (between(value, 900, 2100)) {
          value = constrain(value, 1000, 2000);
//          value = ((value - 1000) * 2) + 500;
          // Im Arduino könnte man auch schreiben
          value = map(value, 1000, 2000, 700, 2300);
        } 
        else {
          // Failsafe
          value = 1500;
        }
      }
      else {
        // FlugEsc Modus
        // Nur die Vorwärtsfahrt (> 1500 us) wird berücksichtigt.
        if (between(value, 1500, 2200)) {
          value = constrain(value, 1500, 2000);
          value = ((value - 1500) * 2) + 1000;
          // Im Arduino könnte man auch schreiben
          // A: value = map(value, 1000, 2000, 500, 2500);
        } 
        else {
          // Failsafe
          value = 1000;
        }
    }
    cli();
    ServoOn();
    delayMicroseconds(value);
    ServoOff();
    sei();
}

Hardware

Ein großer Arduino war mir für dieses Miniprojekt wirklich zu groß, also hab ich das ganze Projekt nicht für einen normalen Arduino gemacht sondern für einen ATTiny85. Das ist eine der kleinstes AT Controllerversionen. Der Chip hat nur 8 Pins, davon kann man 5 (ohne Reset und ISP Möglichkeit 6) frei programmieren. Leider kann man auf dem kleinen nicht die normale Servo Bibliothek verwenden, da im Chip kein 16-Bit Timer vorhanden ist.
Aber wie kriegen wir nun dsa Programm in den Chip?
Das ist jetzt etwas tricky. Wir brauchen dazu

  1. einen ISP, den die Arduino IDE kennt. Ich hab sowas:
  2. einen kleinen Programmieradapter. Oder wir nehmen einfach unser (hoffentlich vorhandenes) Breadboard und verkabeln die 6 Anschlüsse des ISP mit dem Chip. (attiny_programmiersockel.zip (Target Format)
  3. brauchen wir natürlich noch ein Board. Platine bereite ich vor, wer es aber eilig hat, kann schon mal den Schaltplan auf Lochraster nachbauen.

So hier mal die neue Schaltung und das Platinenlayout. flightesc_rev1.pdf
Platinen, Bausätze gibt's bei mir auf Anfrage. Fertiggeräte gibt es wegen der WEEE leider nicht.

IDE vorbereiten

Zum Programmieren gibt es dann einen kleinen Trick.(Quelle: Programming an ATtiny w/ Arduino 1.0)
Zunächst stellt man das richtige Board ein, also bei uns einen Dem ATtiny85 (internal 16 MHz PLL; 2.7 V BOD).
Allerdings gibt's den nicht. Es gibt nur die 4.7V BOD Variante. Leider startet der COntroller dann auf unserem Board nciht, weil durch die Verpolungsschutzdiode nur 4,4V am Controller anliegen. Deswegen muss man den Typ selber in die boards.txt eintragen. Zu finden ist die Datei im hardware/attiny/boards.txt.
Den Text einfach zur vorhandenen Datei hinzufügen…

boards.txt
attiny85at162p.name=Dem ATtiny85 (internal 16 MHz PLL; 2.7 V BOD)
attiny85at162p.bootloader.low_fuses=0xC1
attiny85at162p.bootloader.high_fuses=0xD5
attiny85at162p.bootloader.extended_fuses=0xFF
attiny85at162p.upload.maximum_size=8192
attiny85at162p.build.mcu=attiny85
attiny85at162p.build.f_cpu=16000000L
attiny85at162p.build.core=arduino:arduino
attiny85at162p.build.variant=tiny8

Für die Arduino 1.5.x Umgebung lautet der Pfad hardeware/attiny/avr/boardts.txt

boards.txt
attiny.menu.clock.internal162=16 MHz BOD 2,7(internal)
attiny.menu.clock.internal162.bootloader.low_fuses=0xc1
attiny.menu.clock.internal162.bootloader.high_fuses=0xd5
attiny.menu.clock.internal162.bootloader.extended_fuses=0xff
attiny.menu.clock.internal162.build.f_cpu=16000000L

Dann wählt man den richtigen Programmer. (Man kann übrigens auch einen normalen Arduino als Programmer programmieren…)
Jetzt muss man zunächst einen Bootloader programmieren.
Ja, auch wenn tatsächlich keiner eingebrannt wird, weil wir keinen brauchen. Aber dadurch werden im Chip die richtigen Einstellungen gemacht. (Nennt man Fuses…)
Und jetzt kann man ganz normal das Programm mit der IDE programmieren.
Das ganze Projekt kann man dann direkt in die Leitung zum ESC stecken.

Viel Spass damit.
Willie