Kameraschlitten für Zeitrafferaufnahmen, LCD Keypad als User Interface

Guten Tag!

Ich bin dabei einen Kameraschlitten zu bauen. Einerseits sollen damit Kamerafahrten möglich sein, das ist der einfache Teil.
Andererseits soll es auch die Möglichkeit geben Zeitrafferaufnahmen zu machen.

Das Setup ist folgendes:

  • 1500mm Schiene, davon ca 1300mm Verfahrweg
  • Endschalter rechts/links
  • ein Schlitten auf der Schiene
  • ein Schrittmotor als Antrieb

Das Ansteuern des Schrittmotors habe ich bereits einigermaßen hinbekommen, auch das bauen der Hardware ist kein Problem.

Hacken tut es bei der Programmierung der Mensch-Maschine-Schnittstelle.

Ich nutze ein solches LCD Keypad Shield:
LCD Keypad Shield bei DFRobot

Damit möchte ich abfragen, wieviele Fotos (Schritte) auf welcher Distanz gemacht werden sollen. Ich gehe erstmal davon aus, dass der Schlitten auf einer Seite am Endschalter steht.

Zur Eingabe nutze ich die UP/DOWN/RIGHT/LEFT-Knöpfe und möchte damit eine Zahl nach der anderen eingeben. Wenn ich also 2134 eingeben möchte, drücke ich 2 mal UP, gehe mit RIGHT zu den Hundertern, drücke 1 mal UP (oder 3 mal UP und 2 mal DOWN), gehe wieder mit RIGHT zu den Zehnern.. etc.

Im Endeffekt frage ich also 4 Zahlen a,d,c,d ab und addiere danach zu einer gesamten e = (a1000)+(b100)+(c*10)+d und habe damit die Anzahl der Schritte als Integer.

Bisher habe ich folgenden Code um a zu bekommen (a kann maximal 2 sein, deswegen mit modulo 3):

//Sample using LiquidCrystal library
#include <LiquidCrystal.h>
 
// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
int a = 0;
int b = 0;
 
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;   
 return btnNONE;  
}
 
void setup()
{
 lcd.begin(16, 2);              // start the library
 lcd.setCursor(0,0);
 lcd.print("How many Steps?"); // print a simple message
}
  
void loop()
{
 lcd.setCursor(0,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons
 
 if (lcd_key == btnUP)          //if UP is pressed, increase a by 1
 {
   a = a+1;
   a = a%3;                     //modulo 3 because a can't be bigger than 2
   delay(500);
 
 }
 if (lcd_key == btnDOWN)        //if DOWN is pressed, decrease a by 1 
 {                              //if a < 0, make a 2
   a = a-1; 
   if ( a < 0)
   {
     a = 2;
   }
   
   delay(500);
 
 }
 
 lcd.print(a);
lcd.setCursor(1,1);
lcd.print(b);
}

Das funktioniert soweit schonmal super.
Nur wie gehe ich jetzt weiter zu b, c und d?

Da stehe ich komplett auf dem Schlauch und wäre für jeden Vorschlag dankbar!

Viele Grüße aus München
Roman

Hallo Roman,

kommt das deinem Wunsch näher ? http://arduino.cc/forum/index.php/topic,131281.0.html

Gruß Gerd

wie gehe ich jetzt weiter zu b, c und d?

Wenn du willst, genauso. Sonst wäre evtl. eine gemeinsame Funktion für btnUP/DOWN sehr elegant. ( nur der Tausender-Spezialfall ist dann komplizierter )

Fehlt noch:

int c; int d;
int zahl;

zahl = a*1000+b*100+c*10+d;

a,b,c,d sind übrigens byte , zahl ist unsigned int

Auch fehlt erstmal, dass du weisst wo du grade bist ( Tausender - Hunderter- Zehner - Einer )

  • eventuell auch in der Anzeige signalisieren. (Invers ? / Ein 'V' von links nach rechts wandern lassen ?)
  • soll auch eine Korrektur ( btnLEFT ) gehen ?
  • was passsiert beim 4. btnRIGHT ? ( Hängt in den Einern / Springt im Kreis zu den Tausendern / => btnSELECT )

Dein Problem hat ja grade nichts mit der Bewegungs-Hardware zu tun, richtig ?

Servus Gerd,

Den Thread kenne ich bereits, nur leider finde ich darin nichts zu meinem Problem.

Das Problem an sich ist die Eingabe der Zahlenwerte. Ich hätte gerne auf dem Display soetwas:

How many Steps?
2354

Wobei das gelb hinterlegte die ausgewählte Ziffer ist. Ich möchte nun mit RIGHT/LEFT zwischen den Ziffern wechseln und sie mit UP/DOWN erhöhen/erniedrigen.

Eventuell geht es ja mit einem Array aus den 4 Ziffern? Nur weiss ich dann nicht, wie ich die Bedingungen (Zahl darf maximal 2600 sein) da einbringe?

EDIT:

@Michael:

Genau so soll es werden! Beim 4. btnRIGHT gehts wieder zu den Tausendern. Korrektur wäre natürlich am besten!

Deshalb:
Wie invertiere (v hat kein Platz) ich die ausgewählte Ziffer? In Abhängigkeit von der Postion das jeweilige Feld samt Inhalt invertieren?

Die Hardware ist nicht das Problem, im Gegenteil, das macht richtig Spass!

Wieseo baust du dir nicht ein Byte PositionCursor den erhöhst um 1 du bei Taste Links und verringerst bei Taste Rechts. Dann kannst du ja die abfarge der anderen Psitionen machen als bei
PositionCursor1 = Einer Schritte veränden
PositionCursor2 = Zehner Schritte veränden
PositionCursor3 = Hunderter Schritte veränden
PositionCursor4 = Tausender Schritte veränden

 if (lcd_key == btnUP)          //if UP is pressed, increase a by 1
 {
switch (PositionCursor)
case 1:   
    a = a+1;
    a = a%3;                     //modulo 3 because a can't be bigger than 2
    delay(500);
    break; 
case 2:   
    b = b+1;
    delay(500);
    break; 
case 3;
    c = c+1;
    delay(500);
    break; 
case 4;
    d = d+1;
    delay(500);
    break; 
// und so weiter das gleich auch bei Down
}
 if (lcd_key == btnDOWN)        //if DOWN is pressed, decrease a by 1 
 {                              //if a < 0, make a 2
   a = a-1; 
   if ( a < 0)
   {
     a = 2;
   }
Steps=a*1000+b*100+c*10+d*1;

So etwas in dieser Richtung Display ausgabe bekommst du ja dann auch hin du muss nur den Cursor richtig Positioniern beim Print.

Gruß
Der Dani

Wie invertiere (v hat kein Platz) ich die ausgewählte Ziffer? In Abhängigkeit von der Postion das jeweilige Feld samt Inhalt invertieren?

Ich fürchte, das macht Probleme bei textbasierten 16x2 LCD Displays, hab mir aber die LiquidCrystal library nicht genau angesehen.
10 Sonderzeichen definieren ?

sonst:

Steps :  1234
          ^

Eventuell geht es ja mit einem Array aus den 4 Ziffern? Nur weiss ich dann nicht, wie ich die Bedingungen (Zahl darf maximal 2600 sein) da einbringe?

Ich denke, für dich einfacher ist es, die 4 Fälle einfach nacheinander hinzuschreiben. ( z.B. wie Dani's Vorschlag)
Dann brauchst du auch kein Array.

Die Hardware ist nicht das Problem, im Gegenteil, das macht richtig Spass!

Software macht manchen auch Spass. Wenn du die einfache Version fertig hast, kannst du dich ja mal an einer Funktion
byte setDigit( byte pos, byte max, byte & value); versuchen. Oder so...
Ich stelle mir pos = 0 ... 3 vor, max ist 2, 6 oder 9 in deinem Fall, value enthält (vorher und nachher) die eingestellte Ziffer.
Der Rückkehr-Wert zeigt an, welche Taste - nicht - bearbeitet wurde:
btnNONE : wenn keine oder btnUP / btnDOWN dran war.
btnRIGHT / btnLEFT : pos und max anpassen, Positionsanzeige
btnSELECT: fertig

Dein delay(500) ist in diesem Fall übrigens super. Man drückt eh so lange bis man eine Reaktion sieht, und hat sowohl Tasten-Entprellen wie Scrollen erledigt.

Bei herkömmlichen HD44780- und kompatiblen Displays kann man nur eine begrenzte Anzahl an eigenen Zeichen erstellen, 8 Stück um es genau zu sagen. Das reicht natürlich nicht aus, um 10 Ziffern invertiert darzustellen. Ich denke aber, dass dies gar nicht nötig ist. Warum blendet man nicht einfach den Cursor an der entsprechenden Position ein, dann blinkt da halt ein Balken, der die aktuelle Zahl unterstreicht.

So funktionierts:

void loop()
{
 lcd.setCursor(PositionCursor,1); 
delay(100); // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons
  if (lcd_key == btnRIGHT)          //if UP is pressed, increase a by 1
 {
   PositionCursor = PositionCursor+1;
   PositionCursor = PositionCursor%4;                     //modulo 4 because a can't be bigger than 3
   delay(300);
 
 }
 if (lcd_key == btnLEFT)        //if LEFT is pressed, decrease a by 1 
 {                              
   PositionCursor = PositionCursor-1; 
   if ( PositionCursor < 0)
   {
     PositionCursor = 3;
   }
   
   delay(300);
 
 }

if (lcd_key == btnUP)
{
  switch (PositionCursor)
  {
    case 0:
    a = a+1;
    a = a%3;
    delay(300);
    break;
    
    case 1:
    b = b+1;
    if (a == 2)
    {
      b = b%7;
    }
    else
    {
      b = b%10;
    }
    delay(300);
    break;
    
    case 2:
    c = c+1;
    c = c%10;
    delay(300);
    break;
    
    case 3:
    d = d+1;
    d = d%10;
    delay(300);
    break;
    
}
}
if (lcd_key == btnDOWN)
{
  switch (PositionCursor)
  {
    case 0:
    a = a-1;
    a = a%3;
    if(a < 0)
    {
      a = 2;
    }
    delay(300);
    break;
    
    case 1:
    b = b-1;
    if (b < 0)
    {
      b = 9;
    }
    if (a == 2 & b < 0)
    {
      b = 6;
      b = b%7;
    }
    else
    {
      b = b%10;
    }
    delay(300);
    break;
    
    case 2:
    c = c-1;
    if(c < 0);
    {
      c = 9;
    }
    c = c%10;
     delay(300);
     break;
    
    case 3:
    d = d-1;
    if(d < 0)
    {
      d = 9;
    }
    d = d%10;
      delay(300);
      break;
    
}
}
    if(a == 2 & b == 6)
    {
      c = 0;
      d = 0;
    }
    if( a == 2 & b > 6)
    {
      b = 6;
    }
    NumberSteps = (a*1000)+(b*100)+(c*10)+d;

Super! Vielen Dank für eure Hilfe!
Ick freu mir wahnsinnig :slight_smile:

Warum blendet man nicht einfach den Cursor an der entsprechenden Position ein, dann blinkt da halt ein Balken

Ja klar, dafür ist der Cursor gedacht!

Sorry, war in letzter Zeit auf Grafik LCD Module fixiert, da sind alle Zeichen mehr oder weniger "custom defined" und invertieren geht auch einfacher.
Dafür muss man für einen blinkenden Cursor Klimmzüge machen.

Nochmal Vielen Dank für eure Hilfe!

Momentan programmiere ich die ganzen Menü-Punkte einzeln, in der Hoffnung, sie später aneinanderfügen zu können...
Zum Beispiel eine Auswahlmöglichkeit zwischen zwei Möglichkeiten:

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

  
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
int Cursor_Position = 0;
int Cursor_Position2 = 1;


int read_LCD_buttons()
{
 adc_key_in = analogRead(0);
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;   
 return btnNONE;  
}


int Anzeigen()
{
  lcd.clear();
  lcd.setCursor(0, Cursor_Position);
  lcd.print(">");
  lcd.setCursor(1,0);
  lcd.print("Schrittweise o.");
  lcd.setCursor(1,1);
  lcd.print("kontinuierlich?");
  delay(300);
}
  

void setup() 
{
  lcd.begin(16, 2);
}
 
void loop() 
{
  lcd_key = read_LCD_buttons();
  if (lcd_key == btnUP)          
 {
   Cursor_Position = Cursor_Position+1;
   Cursor_Position = Cursor_Position%2; 
   delay(100);
 }
 if (lcd_key == btnDOWN)
 {                              
   Cursor_Position = Cursor_Position-1;
   if (Cursor_Position < 0)
   {
     Cursor_Position = 1;
   }   
   delay(100);
 }
  Anzeigen();
}

Das funktioniert soweit sehr gut, nur leider flackert die Schrift wegen dem lcd.clear() etwas. Das lcd.clear() brauche ich aber, weil sonst das ">" in den Zeilen stehenbleibt?!

Ich möchte jetzt mit btn_SELECT in den nächsten Menüpunkt (s. oben) kommen. Wie stelle ich das am einfachsten an? Wie bekomme ich den void loop beendet und wie komme ich dann in den nächsten Code?
Ist vielleicht etwas laienhaft ausgedrückt, aber ich weiss nicht wie ichs anders sagen soll..

Viele Grüße
Roman