MX20, 6 Arduino progs.
Met de T-rex 550 wilde ik graag in het donker kunnen vliegen en ben me gaan verdiepen in de mogelijkheden. Uiteraard kun je kant-en-klaar modules kopen van bv.InnoSky maar die kosten ook wel wat ! Een goedkopere manier om de WS2812 led’s aan te sturen kun je vinden bij Arduino.
Volgende stap is het maken van een aantal programma’s in de Arduino die dan op de zender (MX20) te schakelen zijn. We hebben deze setup overigens ook bij een Futaba set geïnstalleerd en daar werkt het evengoed prima echter moeten de gemeten (PWM) tijden wat aangepast worden, of.. uiteraard de schakelpunten in de zender.
Om de 6 standen te kunnen kiezen heb ik gebruik gemaakt van SW6/7 (CTRL-10) en SW3. Geschakeld wordt kanaal 5 van de ontvanger.
‘Gebereinstellungen’ – Eing.5 – SW6/7 (G10). Tevens:
‘Gebereinstellungen’ – Eing.10 (bijvoorbeeld) – SW3.
Als je nu bij de ‘Servoanzeige’ gaat kijken kun je met SW6/7 kanaal 5 schakelen -100, 0, 100 en met SW3 kanaal 10 (-100, 100).
Om nu de twee schakelaars te combineren en in totaal 6 standen op kanaal 5 te verkrijgen gaan we nu een ‘Freie Mischer’ programmeren. Gebruik ‘M1’ en kies voor de vraagtekens: 10 -> 5. Tevens kijk je vervolgens bij de instellingen (>>) en selecteert ‘Mix Eing.’ op 10% en 10% en bij de ‘Offset’ ook +100% (SYM).
Als het goed is moet je nu bij ‘Servoanzeige’ kanaal 5 in zes standen kunnen schakelen (overigens blijft kanaal 10 ook gewoon schakelen van -100 naar 100);
- -120 Komt overeen met Graupner 1100 µs (Stand 1)
- -80 Komt overeen met Graupner 1180 µs (Stand 6)
- 0 Komt overeen met Graupner 1500 µs (Stand 2)
- 20 Komt overeen met Graupner 1580 µs (Stand 5)
- 100 Komt overeen met Graupner 1900 µs (Stand 3)
- 120 Komt overeen met Graupner 1980 µs (Stand 4)
Voor het meten van de puls lengte (PWM) die de ontvanger afgeeft (door de input van de zender) gaan we op de Arduino een interrupt gebruiken. Ik heb daarbij altijd zowel de signaal draad vanaf de ontvanger alsmede de massadraad aangesloten op de Arduino. Of dit per se nodig is weet ik niet, maar gezien het feit dat ik 2 spanningscircuits gebruik (een via de YEP20 en een via de BEC van de T-rex) heb ik het toch maar gedaan. De plus draad verbinden we niet om te voorkomen dat de Arduino via de ontvanger gevoed wordt.
Nu de ontvanger middels de correct geprogrammeerde zender de juiste output geeft wordt het tijd de Arduino van een programma te voorzien ! Het onderstaande programma is in grote lijnen van internet te halen en uiteraard naar eigen inzicht aan te passen.
//******************************************************
// * Arduino Nano for T-rex 550 *
// * ATmega 328 *
// * COM13 by M.Vossen *
// ******************************************************
#include // neopixel library.. moeten staan in de goede directory !
#include "WS2812_Definitions.h" // ws2812 library met kleuren.. moet in de goede directory staan !
// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum direction { FORWARD, REVERSE };
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
public:
// Member Variables:
pattern ActivePattern; // which pattern is running
direction Direction; // direction to run the pattern
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update of position
uint32_t Color1, Color2; // What colors are in use
uint16_t TotalSteps; // total number of steps in the pattern
uint16_t Index; // current step within the pattern
void (*OnComplete)(); // Callback on completion of pattern
// Constructor - calls base-class constructor to initialize strip
NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
:Adafruit_NeoPixel(pixels, pin, type)
{
OnComplete = callback;
}
// Update the pattern
void Update()
{
if((millis() - lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
switch(ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
break;
case THEATER_CHASE:
TheaterChaseUpdate();
break;
case COLOR_WIPE:
ColorWipeUpdate();
break;
case SCANNER:
ScannerUpdate();
break;
case FADE:
FadeUpdate();
break;
default:
break;
}
}
}
// Increment the Index and reset at the end
void Increment()
{
if (Direction == FORWARD)
{
Index++;
if (Index >= TotalSteps)
{
Index = 0;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
else // Direction == REVERSE
{
--Index;
if (Index <= 0)
{
Index = TotalSteps-1;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
}
// Reverse pattern direction
void Reverse()
{
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps-1;
}
else
{
Direction = FORWARD;
Index = 0;
}
}
// Initialize for a RainbowCycle
void RainbowCycle(uint8_t interval, direction dir = FORWARD)
{
ActivePattern = RAINBOW_CYCLE;
Interval = interval;
TotalSteps = 255;
Index = 0;
Direction = dir;
}
// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for(int i=0; i< numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
show();
Increment();
}
// Initialize for a Theater Chase
void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = THEATER_CHASE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}
// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for(int i=0; i< numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
setPixelColor(i, Color1);
}
else
{
setPixelColor(i, Color2);
}
}
show();
Increment();
}
// Initialize for a ColorWipe
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = COLOR_WIPE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
Direction = dir;
}
// Update the Color Wipe Pattern
void ColorWipeUpdate()
{
setPixelColor(Index, Color1);
show();
Increment();
}
// Initialize for a SCANNNER
void Scanner(uint32_t color1, uint8_t interval)
{
ActivePattern = SCANNER;
Interval = interval;
TotalSteps = (numPixels() - 1) * 2;
Color1 = color1;
Index = 0;
}
// Update the Scanner Pattern
void ScannerUpdate()
{
for (int i = 0; i < numPixels(); i++) { if (i == Index) // Scan Pixel to the right { setPixelColor(i, Color1); } else if (i == TotalSteps - Index) // Scan Pixel to the left { setPixelColor(i, Color1); } else // Fading tail { setPixelColor(i, DimColor(getPixelColor(i))); } } show(); Increment(); } // Initialize for a Fade void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD) { ActivePattern = FADE; Interval = interval; TotalSteps = steps; Color1 = color1; Color2 = color2; Index = 0; Direction = dir; } // Update the Fade Pattern void FadeUpdate() { // Calculate linear interpolation between Color1 and Color2 // Optimise order of operations to minimize truncation error uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps; uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps; uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps; ColorSet(Color(red, green, blue)); show(); Increment(); } // Calculate 50% dimmed version of a color (used by ScannerUpdate) uint32_t DimColor(uint32_t color) { // Shift R, G and B components one bit to the right uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}
// Set all pixels to a color (synchronously)
void ColorSet(uint32_t color)
{
for (int i = 0; i < numPixels(); i++) { setPixelColor(i, color); } show(); } // Returns the Red component of a 32-bit color uint8_t Red(uint32_t color) { return (color >> 16) & 0xFF;
}
// Returns the Green component of a 32-bit color
uint8_t Green(uint32_t color)
{
return (color >> 8) & 0xFF;
}
// Returns the Blue component of a 32-bit color
uint8_t Blue(uint32_t color)
{
return color & 0xFF;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos)
{
WheelPos = 255 - WheelPos;
if(WheelPos < 85)
{
return Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
}
};
// Define some NeoPatterns for the two rings and the stick
// as well as some completion routines
NeoPatterns Ring1(10, 5, NEO_GRB + NEO_KHZ800, &Ring1Complete); // Ogen op Canopy
NeoPatterns Ring2(17, 6, NEO_GRB + NEO_KHZ800, &Ring2Complete); // Neus en onderkanten
NeoPatterns Stick(58,10, NEO_GRB + NEO_KHZ800, &StickComplete); // Lange zijstrip tot achteren
volatile int pwm_value = 0;
volatile int prev_time = 0;
// Initialize everything and prepare to start
void setup()
{
Serial.begin(115200);
// Initialize all the pixelStrips
Ring1.begin();
Ring2.begin();
Stick.begin();
// Kick off a pattern
Ring1.TheaterChase(Ring1.Wheel(random(255)), Ring1.Color(0,0,50), 100);
Ring2.TheaterChase(Ring2.Color(255,255,0), Ring2.Color(0,0,50), 100);
Stick.TheaterChase(Stick.Color(255,255,0), Stick.Color(0,0,50), 100);
attachInterrupt(0, rising, RISING);
}
//--------------------------------------------------------------
// Main Loop; het feitelijke programma bij de 6 schakel standen
//--------------------------------------------------------------
void loop() {
// Update the rings.
Ring1.Update();
Ring2.Update();
// -----------------------------------------------------------
if (pwm_value == 1100) // STAND 1 ----------------------------
{
Ring1.setBrightness(4);
Ring2.setBrightness(4);
Stick.setBrightness(4);
Ring1.ActivePattern = NONE;
for (int i=0; i<10; i++) { // Alle lampjes UIT
Ring1.setPixelColor(i, 0); }
Ring1.setPixelColor(0, Ring1.Color(0,0,255));
Ring1.setPixelColor(10, Ring1.Color(255,0,0));
Ring2.ActivePattern = NONE;
for (int i=0; i<17; i++) { // Alle lampjes UIT
Ring2.setPixelColor(i, 0); }
Ring2.setPixelColor(16, Ring2.Color(255,0,0));
Ring2.setPixelColor(0, Ring2.Color(0,0,255));
Stick.ActivePattern = NONE;
for (int i=0; i<58; i++) { // Alle lampjes UIT
Stick.setPixelColor(i, 0); }
Stick.setPixelColor(57, Stick.Color(255,255,255));
Stick.setPixelColor(51, Stick.Color(255,0,0));
Stick.setPixelColor(19, Stick.Color(255,0,0));
Stick.setPixelColor(0, Stick.Color(0,0,255));
Stick.Update();
Stick.show();
Ring1.Update();
Ring1.show();
Ring2.Update();
Ring2.show();
}
// --------------------------------------------------------------
else if (pwm_value == 1180) // STAND 6 --------------------------
{
Ring1.setBrightness(240);
Ring2.setBrightness(240);
Stick.setBrightness(240);
Ring1.ActivePattern = SCANNER; // Ogen
Ring1.Interval = 60;
Ring1.TotalSteps = (12 - 1) * 2; // Zet deze waarde LAAG voor een dubbele scanner
Ring2.ActivePattern = COLOR_WIPE; // Lange strip
Ring2.TotalSteps = 50;
Ring2.Interval = 20;
Stick.ActivePattern = THEATER_CHASE; // Midden en onder
Stick.Interval = 90;
Stick.Update();
Ring1.Update();
Ring2.Update();
}
// ---------------------------------------------------------------
if (pwm_value == 1500) // STAND 2
{
Ring1.setBrightness(25);
Ring2.setBrightness(40);
Stick.setBrightness(55);
Ring1.ActivePattern = NONE;
for (int i=0; i<10; i++) { // Alle lampjes UIT
Ring1.setPixelColor(i, 0); }
Ring1.setPixelColor(0, Ring1.Color(0,0,255));
Ring1.setPixelColor(10, Ring1.Color(255,0,0));
Ring2.ActivePattern = NONE;
for (int i=0; i<17; i++) { // Alle lampjes UIT
Ring2.setPixelColor(i, 0); }
Ring2.setPixelColor(16, Ring2.Color(255,0,0));
Ring2.setPixelColor(0, Ring2.Color(0,0,255));
Stick.ActivePattern = SCANNER;
Stick.Color1 = Stick.Wheel(random(255));
Stick.TotalSteps = (58 - 1) * 2; // Zet deze waarde LAAG voor een dubbele scanner
Stick.Interval = 60;
Stick.Update();
Stick.show();
Ring1.Update();
Ring1.show();
Ring2.Update();
Ring2.show();
}
// -----------------------------------------------------
else if (pwm_value == 1580) // STAND 5
{
Ring1.setBrightness(180);
Ring2.setBrightness(80);
Stick.setBrightness(80);
Ring1.ActivePattern = SCANNER; // Ogen
Ring1.Interval = 60;
Ring1.TotalSteps = (12 - 1) * 2; // Zet deze waarde LAAG voor een dubbele scanner
Ring2.ActivePattern = COLOR_WIPE; // Lange strip
Ring2.TotalSteps = 50;
Ring2.Interval = 20;
Stick.ActivePattern = THEATER_CHASE; // Midden en onder
Stick.Interval = 90;
Stick.Update();
Ring1.Update();
Ring2.Update();
}
// --------------------------------------------------------------
else if (pwm_value == 1900) // STAND 3 -----------------------------------------
{
Ring1.setBrightness(70);
Ring2.setBrightness(70);
Stick.setBrightness(120);
Ring1.ActivePattern = THEATER_CHASE;
Ring1.Interval = 120;
Ring2.ActivePattern = RAINBOW_CYCLE;
Ring2.TotalSteps = 255;
Ring2.Interval = min(10, Ring2.Interval);
Stick.ActivePattern = SCANNER;
Stick.Color1 = Stick.Wheel(random(255));
Stick.TotalSteps = (58 - 1) * 2; // Zet deze waarde LAAG voor een dubbele scanner
Stick.Interval = 37;
Stick.Update();
Ring1.Update();
Ring2.Update();
}
// ------------------------------------------------------------------------
else if (pwm_value == 1980) // STAND 4
{
Ring1.setBrightness(140);
Ring2.setBrightness(140);
Stick.setBrightness(240);
Ring1.ActivePattern = THEATER_CHASE;
Ring1.Interval = 100;
Ring2.ActivePattern = RAINBOW_CYCLE;
Ring2.TotalSteps = 255;
Ring2.Interval = min(10, Ring2.Interval);
Stick.ActivePattern = SCANNER;
Stick.TotalSteps = (58 - 1) * 2; // Zet deze waarde LAAG voor een dubbele scanner
Stick.Interval = 30;
Stick.Update();
Ring1.Update();
Ring2.Update();
}
// ----------------------------------------------------------------
else if (pwm_value == LOW || pwm_value == HIGH) // Onbekende stand (zender uit, signaal weg)
{
Ring1.setBrightness(10);
Ring2.setBrightness(10);
Stick.setBrightness(10);
Ring1.ActivePattern = NONE;
for (int i=0; i<10; i++) { // Alle lampjes UIT
Ring1.setPixelColor(i, 0); }
Ring1.setPixelColor(0, Ring1.Color(255,0,0));
Ring1.setPixelColor(10, Ring1.Color(255,0,0));
Ring2.ActivePattern = NONE;
for (int i=0; i<17; i++) { // Alle lampjes UIT
Ring2.setPixelColor(i, 0); }
Ring2.setPixelColor(16, Ring2.Color(255,0,0));
Ring2.setPixelColor(0, Ring2.Color(255,0,0));
Stick.ActivePattern = NONE;
for (int i=0; i<58; i++) { // Alle lampjes UIT
Stick.setPixelColor(i, 0); }
Stick.setPixelColor(57, Stick.Color(255,0,0));
Stick.setPixelColor(51, Stick.Color(255,0,0));
Stick.setPixelColor(36, Stick.Color(255,0,0));
Stick.setPixelColor(20, Stick.Color(255,0,0));
Stick.setPixelColor(19, Stick.Color(255,0,0));
Stick.setPixelColor(0, Stick.Color(255,0,0));
Stick.Update();
Stick.show();
Ring1.Update();
Ring1.show();
Ring2.Update();
Ring2.show();
}
}
//----------------------------------------------------
//Completion Routines - get called on completion of a pattern
//----------------------------------------------------
// Ring1 Completion Callback OGEN
void Ring1Complete()
{
Ring1.Interval = 60;
Ring1.Color1 = Ring1.Wheel(random(255));
Ring2.Color1 = Ring2.Wheel(random(255));
Ring2.Interval = 20000;
}
// Ring 2 Completion Callback LANGE ZIJSTRIP
void Ring2Complete()
{
Ring2.Color1 = Ring2.Wheel(random(255));
Ring1.Color1 = Ring1.Wheel(random(255));
Ring1.Interval = 20000;
}
// Stick Completion Callback ONDERKANT en MIDDEN
void StickComplete()
{
Ring1.Interval = 10;
Stick.Color1 = Stick.Wheel(random(255));
Ring2.Interval = 20000;
}
void rising() { // ISR (interrupt Service Routine)
attachInterrupt(0, falling, FALLING);
prev_time = micros();
}
void falling() { // ISR (interrupt Service Routine)
attachInterrupt(0, rising, RISING);
pwm_value = micros()-prev_time;
Serial.println(pwm_value);
}