Neues vom Strandbeest

Der Strandbeest Bausatz ist jetzt schon eine Weile im Umlauf, und auf verschiedenen Kanälen habe ich Rückmeldungen über Nachbauten erhalten. Schwierigkeiten gab es insgesamt wenige – wenn, dann war jeder Fall ein „Einzelschicksal“ mit einem individuellen Problem. Zum Glück bisher keine systematischen Fehler :-)

Viel Zeit für Bugfixing musste ich also nicht aufwenden, daher habe ich mir den Mittelteil des Strandbeestes mal vorgenommen und die Servos gegen zwei (sogar günstigere) Getriebemotoren ausgetauscht. Siehe da: Das macht gleich noch etwas mehr Spaß, denn man geht instinktiv einen Schritt zurück, wenn die neue Version angetrappelt kommt.

Sieht gut aus? Finde ich auch :) Folgende Änderungen sind notwendig: zwei neue Trägerplatten für die Motoren ausschneiden. Acht Scheiben aus MDF für die Kopplung der Motorwelle an die Gewindestange müssen auch neu ausgelasert werden. Leider sind die Gewindestangen für die neue Variante zu kurz, also neu zuschneiden. Zum Einsatz kommen zwei „gelbe Motoren“ und ein L298 Motorregler, der auch gleich die 5V für den Arduino Nano und den RC Receiver macht. IMG_3881 Dafür entfallen zwei Servos und der Spannungsregler. Alles in allem käme man mit der neuen Version etwas günstiger weg, wenn die alte Version nicht bereits zusammengebaut herum stünde. Bei den Schrauben tut sich auch etwas: 4 8 Stück M3*10 M3*12 fallen weg, dafür kommen 4 Stück M3*30 dazu, außerdem werden noch 4 Muttern M4 und zwei passende Scheiben benötigt. IMG_3887Oben nicht abgebildet: die beiden Trägerplatten, in die die Servos eingeschraubt sind, die passen natürlich auch nicht mehr.

Wie wird das ganze nun zusammengebaut? Aus den Scheiben und Ringen wird wieder ein Sandwich zusammengeklebt, im Inneren befindet sich die Einschlagmutter, die vorher einzupressen ist. Die oberste Scheibe des Stapels hat einen passenden Ausschnitt, der ohne allzu viel Spiel auf die Motorwelle passt. Angeschraubt werden kann da nichts – die Motorwelle bietet nicht so viel Widerstand oder Platz, als das da viel befestigt werden könnte.
IMG_3884Auf die Welle (Gewindestange) werden zwei Muttern aufgeschraubt, die gestützt von einer Scheibe die Gewinderstange hinter der ersten Bein-Trägerplatte festhalten (Foto folgt) und so verhindern, dass das Sandwich von der Motorwelle abrutscht. So viel zum mechanischen Umbau – die Änderungen an der Verdrahtung und der Software folgen in den nächsten Tagen hier im Blog. Achso: Files mit den neuen Teilen folgen dann auch.

Auf der Maker Faire 2016 in Hannover gibt es das neue Modell natürlich auch zu sehen!

Weiter oben steht es schon geschrieben: Der L298D hat auch einen 5V Teil, so dass sich die folgende neue Spannungsversorgung ergibt:

Bisher brauchten die Servos zwei Pins am Arduino Nano. Der Motorregler braucht gleich mal sechs, so dass der Nano fast komplett belegt ist:

Der Aufmerksame Leser findet in den beiden Abbildungen noch Hinweise auf ein Display – das habe ich zum Debuggen noch mit auf dem Strandbeest montiert. Es handelt sich um das hier beschriebene OLED.

Was fehlt noch? Ach ja, der Quelltext:

// Strandbeest RC V1.2 -- 2016-05-22
// LED Statusanzeige, Modellumschaltung, Heartbeat, Display
// Adaptive Geschwindigkeitseinstellung
// FlySky Receiver

#include <Wire.h>
#include <FastLED.h>
#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);  // I2C / TWI 

#define BAUDRATE 19200

//RGB Leds
#define Anzahl 3
#define OutputPin 3
CRGB LEDs[Anzahl];

// motor one
int enA = 10;
int in1 = 9;
int in2 = 8;
// motor two
int enB = 5;
int in3 = 7;
int in4 = 6;

const uint8_t logo_bitmap[] PROGMEM = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x78
,0x13,0xff,0xc3,0xff,0xf0,0xff,0xff,0xc8
,0x13,0xff,0xc3,0xff,0xf0,0xff,0xff,0xc8
,0x1e,0x00,0xc3,0x00,0x30,0xc0,0x00,0x78
,0x00,0x00,0xc3,0x00,0x30,0xc0,0x00,0x00
,0x00,0x00,0xc3,0x00,0x30,0xc0,0x00,0x00
,0x1e,0x00,0xc3,0x1e,0x30,0xc0,0x00,0x78
,0x13,0xf0,0xc3,0x12,0x30,0xc3,0xff,0xc8
,0x13,0xf0,0xc3,0x12,0x30,0xc3,0xff,0xc8
,0x1e,0x30,0xc3,0x1e,0x30,0xc3,0x00,0x78
,0x00,0x30,0xc3,0x0c,0x30,0xc3,0x00,0x00
,0x00,0x30,0xc3,0x0c,0x30,0xc3,0x00,0x00
,0x0c,0x78,0xc3,0x0c,0x30,0xc3,0x1e,0x78
,0x12,0x48,0xc3,0x0c,0x30,0xc3,0x12,0x48
,0x12,0x48,0xc3,0x0c,0x30,0xc3,0x12,0x48
,0x16,0x78,0xc3,0x0c,0x30,0xc3,0x1e,0x78
,0x0c,0x30,0xc3,0x0c,0x30,0xc3,0x0c,0x00
,0x00,0x30,0xc3,0x0c,0x30,0xc3,0x0c,0x00
,0x1e,0x30,0xc3,0x0c,0x31,0xe7,0x8c,0x00
,0x13,0xf0,0xc3,0x0c,0x31,0x24,0x8f,0xf0
,0x13,0xf0,0xc3,0x0c,0x31,0x24,0x8f,0xf0
,0x1e,0x00,0xc3,0x0c,0x31,0xe7,0x8e,0x30
,0x00,0x00,0xc3,0x0c,0x30,0x00,0x0e,0x30
,0x00,0x00,0xc3,0x0c,0x48,0x00,0x12,0x30
,0x1e,0x00,0xc3,0x0c,0x48,0x00,0x12,0x30
,0x13,0xff,0xc3,0x0c,0x78,0xff,0x1e,0x30
,0x13,0xff,0xc3,0x0c,0x31,0xff,0x8c,0x30
,0x1e,0x00,0xc3,0x0c,0x03,0xc1,0xc0,0x30
,0x00,0x00,0xc3,0x0c,0x07,0x00,0xe0,0x30
,0x00,0x00,0xc3,0x0c,0x0e,0x00,0x70,0x30
,0x1c,0x78,0xc3,0x0c,0x1c,0x00,0x3e,0x30
,0x12,0x48,0xc3,0x0c,0x38,0x00,0x12,0x30
,0x12,0x48,0xc3,0x0c,0x30,0x00,0x12,0x30
,0x1e,0x78,0xc3,0x0c,0x30,0x00,0x1e,0x30
,0x0c,0x30,0xc3,0x0c,0x30,0x00,0x0c,0x30
,0x0c,0x30,0xff,0x0c,0x30,0x00,0x0c,0x30
,0x0c,0x30,0xff,0x0c,0x30,0x00,0x0c,0x38
,0x0c,0x30,0x00,0x0c,0x30,0x00,0x0c,0x48
,0x0c,0x38,0x00,0x1c,0x38,0x00,0x1c,0x48
,0x0c,0x1c,0x00,0x38,0x1c,0x00,0x38,0x78
,0x0c,0x0e,0x00,0x70,0x0e,0x00,0x70,0x10
,0x0c,0x07,0x00,0xe0,0x07,0x00,0xe0,0x00
,0x0c,0x03,0x81,0xc3,0x83,0x81,0xc0,0x38
,0x0c,0x01,0xff,0x82,0x41,0xff,0x80,0x48
,0x0c,0x00,0xff,0x02,0x40,0xff,0x00,0x48
,0x0c,0x00,0x00,0x03,0xc0,0x00,0x00,0x78
,0x0c,0x00,0x00,0x01,0x80,0x00,0x00,0x30
,0x0c,0x00,0x00,0x01,0x80,0x00,0x00,0x30
,0x0c,0x00,0x00,0x01,0x80,0x00,0x00,0x30
,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf0
,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf0
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

};

#define RCchA 11  //Channel 2, rechter Stick vor/rueck auf der FlySky
#define RCchB 12  //Channel 1, rechter Stick rechts/links auf der FlySky 
#define RCchE 2   //Channel 5, SWA auf der FlySky
#define RCchF 4   //Channel 6, SWB auf der FlySky

long chA;
long chB;
long chE;
long chF; //not in use

unsigned long maxfwd=1900;
unsigned long maxbck=1300;
unsigned long maxlft=1300;
unsigned long maxrgt=1900;

int mySpeed=0;

int huecnt=0;
int redcnt=0;
int reddir=0;
int myStatus=0;

int displaycnt=0;

void setup()
{
  //Logo aufs Display
  u8g.firstPage();  
  while(u8g.nextPage()==1)
  { //x,y,number of bytes per line,anzahl zeilen, wo stehts
    u8g.drawBitmapP( 32, 1, 8, 64, logo_bitmap);
  }
  
  pinMode(RCchA, INPUT);
  pinMode(RCchB, INPUT);
  pinMode(RCchE, INPUT);
  pinMode(RCchF, INPUT); //Not in Use
  
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  
  FastLED.addLeds <NEOPIXEL, OutputPin> (LEDs, Anzahl);

  LEDs[0]=0x004000;
  LEDs[1]=0x000000;
  LEDs[2]=0x000000;
  FastLED.show();
  delay(500);
  LEDs[1]=0x004000;
  FastLED.show();
  delay(500);
  LEDs[2]=0x004000;
  FastLED.show();
  delay(500);
   
  //ready - blau
  LEDs[0]=0x000040;
  LEDs[1]=0x000040;
  LEDs[2]=0x000040;
  FastLED.show();
}

void loop()
{
  chA = pulseIn(RCchA, HIGH, 30000);
  if (chA==0)
  {
    //no rc
    chA = pulseIn(RCchA, HIGH, 30000);
    //Alles auf Null
    analogWrite(enA, 0);
    analogWrite(enB, 0);
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);  
    digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);  
    
    // HUE CYCLE
    LEDs[0] = CHSV( huecnt, 255, 128);
    LEDs[1] = CHSV( huecnt, 255, 128);
    LEDs[2] = CHSV( huecnt, 255, 128);
    FastLED.show();
    huecnt++;
    if (huecnt==256){huecnt=0;}
    myStatus=0;
  }
  else
  {
    myStatus=1;
    chE = pulseIn(RCchE, HIGH, 30000); //Check for Enable
    if (chE > 1700) //Schalter SWA = 1
    {
      //Kein Enable
      //Alles auf Null
      analogWrite(enA, 0);
      analogWrite(enB, 0);
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);  
      digitalWrite(in3, LOW);
      digitalWrite(in4, LOW); 
      
      //Neutral - r/l blau, mitte rot mit heartbeat
      LEDs[0]=0x000040;
      LEDs[1].r=redcnt;
      LEDs[1].g=0;
      LEDs[1].b=0;
      LEDs[2]=0x000040;
      FastLED.show();

      //heartbeat
      if (reddir==0) 
      {
        //count up
        redcnt++;
        if (redcnt==80)
        {
          reddir=1; //Zährichtung umschalten auf abwärts
        }
      }
      else
      {
        //count down
        redcnt--;
        if (redcnt==0)
        {
          reddir=0; //Zährichtung umschalten auf abwärts
        }
      }
    }
    else //Schalter SWA = 0
    {
      chB = pulseIn(RCchB, HIGH, 30000);

      if (chB < 1200) //Links
      {
        if ((chA > 1200) &&(chA < 1700)) //VorRück in etwa auf Mitte?
        {
          if (chB<maxlft){maxlft=chB;} //linksanschlag=kleine Werte
          if (maxlft==0){maxlft=1000;}
          mySpeed=map(chB,maxlft,1500,254,0);
          // turn on motor A
          digitalWrite(in1, HIGH);
          digitalWrite(in2, LOW);
          analogWrite(enA, mySpeed);
          // turn on motor B,
          digitalWrite(in3, LOW);
          digitalWrite(in4, HIGH);
          analogWrite(enB, mySpeed);
  
          LEDs[0]=0x400000;
          LEDs[1]=0x804000;
          LEDs[2]=0x004000;
          FastLED.show();
        }
      }
      else if  (chB > 1700) //rechts
      {
        if ((chA > 1200) &&(chA < 1700)) //VorRück in etwa auf Mitte?
        {
          if (chB>maxrgt){maxrgt=chB;} //rechtsanschlag=große Werte
          mySpeed=map(chB,1500,maxrgt,0,254);
          // turn on motor A
          digitalWrite(in1, LOW);
          digitalWrite(in2, HIGH);
          analogWrite(enA, mySpeed);
          // turn on motor B,
          digitalWrite(in3, HIGH);
          digitalWrite(in4, LOW);
          analogWrite(enB, mySpeed);
  
          LEDs[0]=0x004000;
          LEDs[1]=0x804000;
          LEDs[2]=0x400000;
          FastLED.show();
        }
      }
      else
      {
      if (chA > 1550) //Vor
      {
        if (chA>maxfwd){maxfwd=chA;} //anschlag oben=große Werte
        mySpeed=map(chA,1500,maxfwd,0,254);

        // turn on motor A
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);
        analogWrite(enA, mySpeed);
        // turn on motor B
        digitalWrite(in3, HIGH);
        digitalWrite(in4, LOW);
        analogWrite(enB, mySpeed);
        
        LEDs[0].r=0;
        LEDs[0].g=(mySpeed/4);
        LEDs[0].b=((255-mySpeed)/4);
        
        LEDs[1]=0x804000;
        
        LEDs[2].r=0;
        LEDs[2].g=(mySpeed/4);
        LEDs[2].b=((255-mySpeed)/4);

        FastLED.show();
      }
      else if  (chA < 1450) //Rueck
      {
        if (chA<maxbck){maxbck=chA;} //anschlag unten=kleine Werte
        mySpeed=map(chA,maxbck,1500,254,0);

        // turn on motor A
        digitalWrite(in1, LOW);
        digitalWrite(in2, HIGH);
        analogWrite(enA, mySpeed);
        // turn on motor B
        digitalWrite(in3, LOW);
        digitalWrite(in4, HIGH);
        analogWrite(enB, mySpeed);
        
        LEDs[0].g=0;
        LEDs[0].r=(mySpeed/4);
        LEDs[0].b=((255-mySpeed)/4);
        
        LEDs[1]=0x804000;
        
        LEDs[2].g=0;
        LEDs[2].r=(mySpeed/4);
        LEDs[2].b=((255-mySpeed)/4);
        FastLED.show();
      }
      else //
      {
        //Serial.println("Neutral");

        //Alles auf Null
        analogWrite(enA, 0);
        analogWrite(enB, 0);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);  
        digitalWrite(in3, LOW);
        digitalWrite(in4, LOW); 
        
        //Neutral - r/l blau, Mitte grün
        LEDs[0]=0x000040;
        LEDs[1]=0x804000; //orange
        LEDs[2]=0x000040;
        FastLED.show();
      }
    }
    }
  }
  
  displaycnt++;
  if (displaycnt>30)
  {
    if (myStatus==0) //norc
    {
      u8g.firstPage();  
      while(u8g.nextPage()==1)
      { //x,y,number of bytes per line,anzahl zeilen, wo stehts
        u8g.drawBitmapP( 32, 1, 8, 64, logo_bitmap);
      }
    }
    else //rc
    {
      u8g.firstPage();  
      while(u8g.nextPage()==1)
      { //x,y,number of bytes per line,anzahl zeilen, wo stehts
        String mr=String(maxrgt);
        String ml=String(maxlft);
        
        u8g.setFont(u8g_font_6x10);
        u8g.drawStr( 0, 30, "R");

        u8g.setPrintPos(10, 30); 
        u8g.print(maxrgt);
        
        u8g.drawStr( 0, 40, "L");
        
        u8g.setPrintPos(10, 40); 
        u8g.print(maxlft);
        
        //u8g.drawStrP( 10, 40, ml);
      }
    }
    displaycnt=0;
  }
}

Hier noch einmal das Display: und die in Innenraum festgeschnallten Komponenten:

Auch gut zu sehen: die drei LEDs. Grüner Draht links: Das ist die Datenleitung.

Countdown zur Maker Faire Hannover

icon_Hannover_dtDie Zeit läuft unerbittlich und schon sind es nur noch zehn Tage bis zur Maker Faire Hannover 2016. Die Liste der vorher zu erledigenden Sachen ist noch recht lang. Für meinen Tisch brauche ich zum Beispiel noch eine Kante, damit mir die Strandbeesten nicht in einem unbeaufsichtigten Moment stiften gehen. Ein Flipdot-Projekt soll noch fertig werden, die Beschilderung ist noch nicht gebaut, etwas Anschauungsmaterial fehlt und so weiter und so fort? Wo soll man da anfangen? Eigentlich egal, Hauptsache anfangen! Also geht es erstmal an die Schaustücke, damit es für die Besucher auch was zu sehen gibt.

Die Flipdot Uhr ist im Kasten

IMG_3844

Sieht so aus, als würden Lackfarbe und ich doch noch irgendwann Freunde. Meine bisherigen Lackierergebnisse waren nicht so berauschend, aber hier bei der Flip Dot Uhr hat es mal einigermaßen geklappt. So stecken die Module jetzt in einem matt schwarz lackierten Stück MDF das in einen IKEA Bilderrahmen passt.

Auf der Rückseite ist jetzt auch alles hübsch. Ein Abstandshalter verhindert, dass die Controller Platinen bis auf die Flipdots gedrückt werden können und Platz für mein Logo war auch noch ;-) Zum Abschluss kommt jetzt von der Rückseite auch noch mal ein Rahmen dagegen und dann ist die Uhr „fertig“. IMG_3838

Warm Up zur Maker Faire Hannover 2016

icon_Hannover_dtIn etwas weniger als drei Wochen ist es so weit: Die Maker Faire Hannover öffnet ihre Pforten. Als kleinen Vorgeschmack gibt Daniel Bachfeld (Chefredakteur Make Magazin) auf robotiklabor.tv einen knapp anderthalbstündigen Ausblick auf die Veranstaltung. Meine Strandbeesten werden auch mit dabei sein – in der Niedersachsenhalle am Stand Nummer 21.

Flipdot Clock Prototyp

IMG_3466Mittlerweile habe ich fünf Controllerplatinen bestückt, die jeweils als I2C Client ein 5×7 Modul treiben können. Die Module sind an der Ober- und Unterseite jeweils mit praktischen Rasten ausgestattet, so dass ich nur ein Stück MDF mit passendem Ausschnitt als Halter brauche. So sieht das Ganze im Rohbau aus:

Im Hintergrund werkeln 5 von meinen Controller-Platinen, ein DS3231 RTC Modul und ein weiterer Arduino Nano als Bus-Master. Die Abstandshalter sind noch nicht als final zu betrachten :)

IMG_3609Möglicherweise sieht das ein wenig nach „Overkill“ aus – aber hey – es funktioniert ;-)

Die Flipdot Platinen sind da!

IMG_3474Mit Spannung erwartet, nun sind sie da. Die ersten „selbst gemachten“ Platinen seit fast dreißig Jahren! Wobei das mit dem „selbst gemacht“ so eine Sache ist. Geliefert aus China, das Layout erzeugt von Urs aus der Arduino Hannover Gruppe, bestellt von Luca. Aber zumindest das zugrunde liegende Schaltbild ist von mir :-)
IMG_3475Zunächst werden aus der einen Platine zwei: Mit der Sollbruchstelle in der Mitte lassen sich Controller und Flipdotträger von einander trennen. Der Controller wird mit 8 Kondensatoren 100nF und zwei Elkos 100uF bestückt und bekommt außerdem zwei 74238 Adressdecoder, sechs L293D Motortreiber und einen Arduino Nano. Wer es modular mag, bestückt außerdem noch eine 20 polige Buchsenleiste, in die die Flipdot Platine eingesteckt wird.
IMG_3477Auf der Flipdot Platine werden dann noch 35 SMD Doppeldioden eingelötet, bevor die fünf Flipdot-Riegel (7*1) eingebaut werden. Mit einer Stiftleiste versehen können die beiden Module dann aufeinander gesteckt werden. Vier Lötpads sind vorhanden, um dem Modul seine Adresse auf dem I2C Bus vorzugeben. 16 Module lassen sich in der aktuellen Version anreihen. Nach dem Power On zeigt jedes Modul kurz seine Nummer und lauscht dann auf dem Bus.IMG_3482

Ach: und einen Jumper gibt es auf der Rückseite auch noch, mit dem lässt sich der Selbstest bzw. Demomodus aktivieren. So sieht das Modul dann aus:

 

Der 5*7 Flipdot Controller auf dem Weg zur Platine

Mein Flipdot-Controller war zunächst ja nur für meine bereits fertigen 5×7 Module gedacht. Dann kam Urs, unser Platinendesigner und erzeugte das folgende Platinenpaar, das neben dem Controller eine Trägerplatine für fünf 1×7 Flipdotstreifen mitbringt. Damit wird das Set „massentauglich“, denn ein paar von den 1×7 Streifen haben wir ja noch auf Lager.

fd1

So sieht das ganze in der Theorie aus. Eine erste Charge ist bestellt und soll demnächst ihren Weg von China nach Deutschland finden. Ich werde berichten :)
fd2

Die in der Abbildung horizontal angesetzte Buchsenleiste kommt da natürlich senkrecht rein, damit der Controller auch komplett hinter der Flipdot-Trägerplatine verschwindet.