Im nachfolgenden Weekend Project möchte ich gerne aufzeigen wie man sich eine eigene, große 7 Segmentanzeige mit Servomotoren erstellen kann.
Ich habe dieses Projekt zuerst in einem Instagram Beitrag gesehen und fand dieses cool, daher wollte ich dieses gerne nachbauen und natürlich auf meinem Blog einen Beitrag dazu schreiben.
Was ist eine 7 Segmentanzeige?
Mit einer 7 Segmentanzeige kann man die Zahlen von 0 bis 9 und einfache Buchstaben anzeigen.
Ich habe bereits im Tutorial Arduino Lektion 62: 7 Segmentanzeige eine 7 Segmentanzeige vorgestellt.
Materialien & Werkzeuge
Materialien
Folgende Materialien werden benötigt:
- 2x 3 mm, DIN A4 Sperrholz,
- Holzleiste, 20 cm lang, 10 mm stark
- eventuell, Farbe für die Displayfläche,
Werkzeuge
- Laubsäge,
- Laubsägeblätter, fein
- Heißklebepistole,
- Holzleim,
- Papierkleber,
- kleiner, Kreuzschraubendreher
- kleiner, Schlitzschraubendreher
elektrische Bauelemente
- 1x Arduino Leonardo*,
- USB Kabel mit Micro USB Stecker,
- 7x Servo Motor 9g*,
- 1x PWMServoDriverShield – Typ “PCA9685”*,
- 6x Breadboardkabel, 20cm Lang, männlich – weiblich
Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!
Vorlage
Zunächst einmal habe ich mir eine Vorlage im Zeichenprogramm paint.net erzeugt und diese ausgedruckt dann auf das Sperrholz geklebt.
Ich habe dafür den Papierkleber der Firma “Pritt” benutzt.
Diese ist deutlich besser als die günstigen.
Die Vorlage kannst du dir unter folgendem Link herunterladen:
Aufbau der 7 Segmentanzeige mit Sperrholz
Im folgenden Video zeige ich, wie ich die 7 Segmentanzeige mit Sperrholz erstellt habe.
Zunächst einmal habe ich jedes Segment mit der Laubsäge ausgesägt und danach auf das korrekte Maß geschliffen. Danach wurde eine kleine 2 cm lange Holzleiste montiert, damit dieses besser hält, habe ich jeweils ein 2 mm Loch hineingebohrt und ein 3 mm x 12 mm Kreuzschraube inkl. Holzleim verwendet. Wenn alle 7 Segmente montiert wurden dann wurden die Servomotoren mit der Heißklebepistole festgeklebt und diese danach auf die übrige Sperrholzplatte montiert. Zum Schluss habe ich die Segmente angestrichen.
Anschließen der 7 Servomotoren
Für den Betrieb der Servos verwende ich das PWMServoDriverShield vom Typ “PCA9685“. Dieses kann man bei ebay.de für knapp 5 € inkl. Versandkosten erwerben.
PWMServoDriverShield – Typ “PCA9685”
Das Shield hat den Vorteil, dass man die Servos per I2C ansprechen kann und somit die digitalen Pins des Mikrocontrollers freihat. Ein weiterer großer Vorteil ist, dass die somit hat man die Möglichkeit nicht nur 16 Servos zu betreiben, sondern bis zu 6x 16 Stück = 96 Stück.
Auf der Rückseite des Moduls sind die maximalen Lasten für das Modul aufgedruckt.
Das Modul verfügt über 16 Steckplätze für Servomotoren, diese sind in der korrekten Pinanordnung sodass man den Stecker des Servos direkt drauf stecken kann.
Des Weiteren verfügt das Modul über 6 Pins welche mit dem Microcontroller verbunden werden.
Je nach verwendeten Mikrocontroller muss das Modul anders angeschlossen werden. Hier nun die 3 Möglichkeiten für die gängigen Mikrocontroller der Arduino Baureihe:
Arduino UNO
- VCC an 5V
- GND an GND
- SDA an analog A4
- SCL an analog A5
Arduino Mega 2560 R3
- VCC an 5V
- GND an GND
- SDA an digital 20
- SCL an digital 21
Arduino Leonardo
- VCC an 5V
- GND an GND
- SDA an SDA
- SCL an SCL
Spannungsversorgung der Servos
Die Servos werden über einen Separaten Anschluß auf dem Modul mit Spannung versorgt. Die beiden Pins VCC & GND dienen “nur” zum betrieb des Modules.
Programmieren des PAC9685 Servo Driver Shield
Damit wir das Modul verwenden können benötigen wir zusätzlich eine Bibliothek, diese können wir in der Arduino IDE bequem über den “Bibliotheksverwalter” installieren.
Die benötigte Bibliothek heißt “Adafruit PWM Servo Driver Library” und kann über das Eingabefeld mit der Zeichenkette “pwm servo” gefunden werden.
Wenn die Bibliothek installiert wurde kann es mit der Programmierung schon beginnen.
Einfaches Beispiel mit einem Servo
Als erstes wollen wir einen Servo betreiben und diesen von 0° bis 180° und zurück bewegen lassen.
#include <Wire.h> // OneWire Bibliothek #include <Adafruit_PWMServoDriver.h> //Bibliothek für das PAC9585 Modul //erzeugen einer Instanz für die Kommunikation Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // Minimum der Pulselänge const int SERVOMIN = 125; // Maximum der Pulselänge const int SERVOMAX = 575; //Port an welchem der Servo angeschlossen ist const int SERVO_PORT = 0; //Pause zwischen den einzelnen Schritten const int PAUSE = 25; void setup() { //beginn der Kommunikation pwm.begin(); //setzen der PWM Frequenz auf 60Hz pwm.setPWMFreq(60); } void loop() { //Schleife von 0..180 for(int angle = 0;angle<180;angle++){ //setzen des aktuellen Wertes von "angle" an dem Servo //dabei muss der aktuelle Winkel auf die Pulslänge gemappt werden. pwm.setPWM(SERVO_PORT, 0, map(angle,0, 180, SERVOMIN,SERVOMAX)); //eine kleine Pause delay(PAUSE); } //Schleife von 180..0 for(int angle = 180;angle>=0;angle--){ //setzen des aktuellen Wertes von "angle" an dem Servo //dabei muss der aktuelle Winkel auf die Pulslänge gemappt werden. pwm.setPWM(SERVO_PORT, 0, map(angle,0, 180, SERVOMIN,SERVOMAX)); //eine kleine Pause delay(PAUSE); } }
Video
Sketch für die 7 Segmentanzeige
interne Klasse “Segment”
Da ich bedingt durch die Künste nicht ganz gerade Sägen zu können etwas Toleranz in jedem Segment benötige habe ich mir die interne Klasse “Segment” erzeugt.
Diese Klasse erhält
- den Port an welchem der Servo angeschlossen ist,
- den Namen,
- den Wert “maxAngle” für den untersten Wert,
- den Wert “minAngle” für den obersten Wert
class Segment{ private: String name; int port; int maxAngle; int minAngle; public: Segment(String n, int p,int maxA, int minA){ name = n; port = p; maxAngle = maxA; minAngle = minA; } String getName(){ return name; } int getPort(){ return port; } int getMaxAngle(){ return maxAngle; } int getMinAngle(){ return minAngle; } };
Zunächst einmal werden wir also die Servos an den einzelnen Ports mit einer neuen Instanz der Klasse “Segment” definieren.
Segment segmentA = Segment("Segment A", 0, 180, 90); Segment segmentB = Segment("Segment B", 1, 180, 100); Segment segmentC = Segment("Segment C", 2, 160, 60); Segment segmentD = Segment("Segment D", 3, 160, 60); Segment segmentE = Segment("Segment E", 4, 170, 60); Segment segmentF = Segment("Segment F", 5, 140, 60); Segment segmentG = Segment("Segment G", 6, 160, 90);
Ich verwende hier die Beschriftung A-G diese ist für die Definition der einzelnen Segmente einer 7 Segmentanzeige üblich.
Nun müssen wir noch in der Funktion “setup” das PWM Modul “starten” und die Frequenz für die Kommunikation festlegen, bei den von mir verwendeten Servos liegt diese bei 60Hz.
void setup() { Serial.begin(9600); Serial.println("16 channel Servo test!"); pwm.begin(); pwm.setPWMFreq(60); }
In der Funktion “loop” werden nun die einzelnen Funktionen zum Anzeigen einer Zahl aufgerufen. Ich habe dieses einmal beispielhaft für die Zahlen 0 & 1 getan.
void loop() { displayNum0(); delay(PAUSE); displayNum1(); delay(PAUSE); }
In den Funktionen selber wird zunächst ein Reset ausgeführt, dieses sorgt dafür, dass alle Servos in den Zustand “nicht angezeigt” gehen (quasi im Winkel von 180°).
void reset(){ resetSegment(segmentA); delay(PAUSE_KL); resetSegment(segmentB); delay(PAUSE_KL); resetSegment(segmentC); delay(PAUSE_KL); resetSegment(segmentD); delay(PAUSE_KL); resetSegment(segmentE); delay(PAUSE_KL); resetSegment(segmentF); delay(PAUSE_KL); resetSegment(segmentG); }
Da ich doppelten Quellcode vermeiden möchte, habe ich mir wiederum eine extra Funktion geschrieben, in welcher ich den Port übergebe und diese mit dem Servo auf den benötigten Winkel setzt.
void resetSegment(Segment segment){ moveServo("reset ->",segment.getName(),segment.getPort(), angleToPulse(segment.getMaxAngle())); }
Hier kommt auch schon die nächste Funktion “moveServo” hat 3 Parameter,
- die Aktion, für die Ausgabe auf dem Seriellen Monitor der Arduino IDE,
- den Port, an welchem der Servo angeschlossen ist,
- der Winkel, in welchem Winkel der Servo versetzt werden soll
void moveServo(String action, String name, int port, int angle){ Serial.print(action); Serial.println(name); pwm.setPWM(port, 0, angleToPulse(angle)); }
Da das Modul PAC9685 mit Pulsen anstatt Winkeln arbeitet müssen wir zunächst den gewünschten Winkel in ein Pulse umwandeln, dazu nutzen wir die Funktion “map”. Wir wissen, dass bei Pulse “125” der minimale Wert (also 0°) und bei “575” der maximale Wert erreicht (also 180°) ist.
int angleToPulse(int ang){ return map(ang,0, 180, SERVOMIN,SERVOMAX); }
Nehmen wir einmal Beispielhaft die Zahl “0”, hier müssen alle Segmente aufgerichtet werden außer das Segment “G”. Wie erwähnt wird zunächst ein Reset ausgeführt, danach wird jedes einzelne Segment aufgerichtet. Zwischen den einzelnen Segmenten habe ich eine kleine Pause eingeführt, dieses musste ich machen da es wohl zeitliche Probleme gibt.
void displayNum0(){ reset(); moveUp(segmentA); delay(PAUSE_KL); moveUp(segmentB); delay(PAUSE_KL); moveUp(segmentC); delay(PAUSE_KL); moveUp(segmentD); delay(PAUSE_KL); moveUp(segmentE); delay(PAUSE_KL); moveUp(segmentF); }
Für die Zahl “1” würden dann nur das Segment “B” & “C” aktiviert usw.
kompletter Sketch
Hier nun der komplette Sketch. Im unteren Bereich dieses Tutorials findest du den Sketch zum bequemen Download.
#include <Wire.h> #include <Adafruit_PWMServoDriver.h> Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); #define SERVOMIN 125 // this is the 'minimum' pulse length count (out of 4096) #define SERVOMAX 575 // this is the 'maximum' pulse length count (out of 4096) const int PAUSE = 1000; const int PAUSE_KL = 250; class Segment{ private: String name; int port; int maxAngle; int minAngle; public: Segment(String n, int p,int maxA, int minA){ name = n; port = p; maxAngle = maxA; minAngle = minA; } String getName(){ return name; } int getPort(){ return port; } int getMaxAngle(){ return maxAngle; } int getMinAngle(){ return minAngle; } }; Segment segmentA = Segment("Segment A", 0, 180, 90); Segment segmentB = Segment("Segment B", 1, 180, 100); Segment segmentC = Segment("Segment C", 2, 160, 60); Segment segmentD = Segment("Segment D", 3, 160, 60); Segment segmentE = Segment("Segment E", 4, 170, 60); Segment segmentF = Segment("Segment F", 5, 140, 60); Segment segmentG = Segment("Segment G", 6, 160, 90); void setup() { Serial.begin(9600); Serial.println("16 channel Servo test!"); pwm.begin(); pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates } void loop() { displayNum0(); delay(PAUSE); displayNum1(); delay(PAUSE); } void displayNum0(){ reset(); moveUp(segmentA); delay(PAUSE_KL); moveUp(segmentB); delay(PAUSE_KL); moveUp(segmentC); delay(PAUSE_KL); moveUp(segmentD); delay(PAUSE_KL); moveUp(segmentE); delay(PAUSE_KL); moveUp(segmentF); } void displayNum1(){ reset(); moveUp(segmentB); delay(PAUSE_KL); moveUp(segmentC); } void displayNum2(){ reset(); moveUp(segmentA); delay(PAUSE_KL); moveUp(segmentB); delay(PAUSE_KL); moveUp(segmentG); delay(PAUSE_KL); moveUp(segmentE); delay(PAUSE_KL); moveUp(segmentD); delay(PAUSE_KL); } void displayNum3(){ reset(); moveUp(segmentA); moveUp(segmentB); moveUp(segmentG); moveUp(segmentC); moveUp(segmentD); } void displayNum4(){ reset(); moveUp(segmentF); moveUp(segmentG); moveUp(segmentB); moveUp(segmentC); } void displayNum5(){ reset(); moveUp(segmentA); moveUp(segmentF); moveUp(segmentG); moveUp(segmentC); moveUp(segmentD); } void displayNum6(){ reset(); moveUp(segmentA); moveUp(segmentF); moveUp(segmentG); moveUp(segmentE); moveUp(segmentD); moveUp(segmentC); } void displayNum7(){ reset(); moveUp(segmentA); moveUp(segmentB); moveUp(segmentC); } void displayNum8(){ reset(); moveUp(segmentA); moveUp(segmentB); moveUp(segmentC); moveUp(segmentD); moveUp(segmentE); moveUp(segmentF); moveUp(segmentG); } void displayNum9(){ reset(); moveUp(segmentA); moveUp(segmentB); moveUp(segmentC); moveUp(segmentG); moveUp(segmentF); } void reset(){ resetSegment(segmentA); delay(PAUSE_KL); resetSegment(segmentB); delay(PAUSE_KL); resetSegment(segmentC); delay(PAUSE_KL); resetSegment(segmentD); delay(PAUSE_KL); resetSegment(segmentE); delay(PAUSE_KL); resetSegment(segmentF); delay(PAUSE_KL); resetSegment(segmentG); } void resetSegment(Segment segment){ moveServo("reset ->",segment.getName(),segment.getPort(), segment.getMaxAngle()); } void moveUp(Segment segment){ moveServo("moveUp ->",segment.getName(),segment.getPort(), segment.getMinAngle()); } void moveDown(Segment segment){ moveServo("moveDown ->",segment.getName(),segment.getPort(), segment.getMaxAngle()); } void moveServo(String action, String name, int port, int angle){ Serial.print(action); Serial.println(name); pwm.setPWM(port, 0, angleToPulse(angle)); } int angleToPulse(int ang){ return map(ang,0, 180, SERVOMIN,SERVOMAX); }