boxtec Forum
Microcontroller => Arduino General => : MathiasW September 07, 2012, 04:25:44 PM
-
Salut,
inspiriert von der Android App "Handbag" (welche leider nicht auf meinem Mobile installierbar ist), hatte ich die Idee, ein kleines Program zu schreiben, welches ein Sammlung an Controls anbietet die über das Arduino Sketch kontrolliert werden. Was dabei herausgekommen ist, kann man sich auf meiner Webpage anschauen : http://www.mathias-wilhelm.de/arduino/projects/arduino-dashboard/ (http://www.mathias-wilhelm.de/arduino/projects/arduino-dashboard/)
Auf der gleichen Idee basiert auch eine Ansteuerung des Adafruit motor shields und der servos eines Roboterarms: http://www.mathias-wilhelm.de/arduino/projects/arduino-control/ (http://www.mathias-wilhelm.de/arduino/projects/arduino-control/)
Der Code ist lange noch nicht fertig aber mich würde Euer Feedback interessieren.
Ciao, Mathias
-
Hallo Mathias,
Sehr interessant, ich bin grad an etwas ähnlichem zur Steuerung von zwei RGB Kanälen (z.B.) RGB LED Strips (http://shop.boxtec.ch/strip-505030m-p-40840.html) über seriell und Drehencoder mit Taster (http://shop.boxtec.ch/12mm-rotary-encoder-schalter-taster-p-40247.html):
(http://forum.boxtec.ch/index.php?action=dlattach;topic=1071.0;attach=468)
Dabei bedanke ich mich vor allem für den Hinweis auf serialEvent(), ich hab nicht gewusst, dass die neue Serial Library diesen Event schon registriert hat und standardmässig genannte Funktion aufruft. Absolut genial, danke für den Code, ich konnte mir da ein paar nette Tricks abschauen.
Viele Grüsse,
Christoph
-
Salut,
ich habe ein kleines Problem entdeckt: In meinem Sketch werden alle seriellen Daten über den Interrupt SerielEvent abgearbeitet. Im loop() geschiet eigentlich nichts. Nun habe ich im loop folgendes geschrieben:
if (readAnalog) {
Serial.println(analogRead(A0));
}
Die boolsche Variable readAnalog wird im SerialEvent auf true oder false gesetzt, je nach Kommando, welches ich von aussen schicke.
Das Ganze funktioniert, aber es dauert bis zu 10 Sekunden, bis der Wechsel des Wertes von readAnalog in loop erkannt wird.
Hat jemand eine Idee?
Ciao, Mathias
-
Salut,
da ich mit Interrupts arbeite, dachte ich mir, dass ich die Ausleseaktion von loop() in eine subrountine verschiebe und diese über den timer ausführen lasse:
#include <MsTimer2.h>
....
void setup()
{
Serial.begin(115200);
for (int i=2;i<14;i++) {pinMode(i,OUTPUT);}
MsTimer2::set(500, flash); // 500ms period
MsTimer2::start();
}
void flash()
{
int led_pin = 13;
if (readPort) {
digitalWrite(led_pin, !digitalRead(led_pin));
if (dbOpen) {
Serial.print("SLBV001.");
Serial.print(analogRead(A0));
Serial.println("E");
}
}
}
Mit diesem Konstrukt wird die Änderung in der variablen readPort sofort erkannt ...
Damit habe ich eine gute Lösung, aber ich würde das gerne verstehen ... Sobald ich ein Serial.println Statement in loop() einbaue, braucht es wieder bis zu 10 Sekunden, bis der Wechsel von readPort erkannt wird
Ciao, Mathias
-
Hallo Mathias,
Soweit ich den Code verstanden habe, wartest Du im serialEvent bis Du einen Charakter 13 kriegst, mit dem Du wohl die Kommandosequenz aus Deinem VB GUI terminierst. Ich kann mir daher vorstellen, dass der serialEvent die Ausführung des Codes in Loop blockiert, sprich der Loop wird tatsächlich erst nach 10s wieder durchlaufen weil solange immer der serialEvent blockt.
Ist aber ein wilder Schuss ins Blaue ohne den genauen Code gesehen zu haben.
Ev. kannst Du ein Stück Testcode zeigen, mit dem sich das Verhalten replizieren lässt.
Viele Grüsse & schönen Restsonntag,
Christoph
-
Salut,
da der Code nicht sonderlich lange ist, hier das vollständige Listing:
#include <MsTimer2.h>
int bufferCount; // Anzahl der eingelesenen Zeichen
char buffer[80]; // Serial Input-Buffer
char temp[80];
char lastpin=2;
int blinkdelay;
boolean dbOpen = false;
static boolean readPort = false;
void setup()
{
Serial.begin(115200);
for (int i=2;i<14;i++) {pinMode(i,OUTPUT);}
MsTimer2::set(500, flash); // 500ms period
MsTimer2::start();
}
void loop()
{
/*
if (readPort) {
digitalWrite(led_pin, !digitalRead(led_pin));
if (dbOpen) {
Serial.print("SLBV001.");
Serial.print(analogRead(A0));
Serial.println("E");
}
}
*/
}
void serialEvent(){
char ch = Serial.read();
buffer[bufferCount] = ch;
bufferCount++;
if(ch == 13){
evalSerialData();
}
}
void evalSerialData()
{
int ptr;
int val;
strcpy(temp,"");
if ((buffer[0]=='S')&&(buffer[bufferCount-2]=='E')) {
strncat(temp, buffer, bufferCount-1);
if (strcmp(temp,"SQVERSIONE")==0){
strcpy(temp,"AC 1.0.0");
}
if (strcmp(temp,"SINITE")==0){
// setup the Dashboard
Serial.println("STTL1Dashboard DemoE");
Serial.println("SBTN001.0020.0040.0080.0025.Click MeE");
Serial.println("SCKB001.0105.0040.Led 5E");
Serial.println("SSLD001.0020.0070.0002.0013.0001.Led SwiperE");
Serial.println("SSLD011.0020.0150.0000.0250.0025.Led FaderE");
Serial.println("SCBO001.0020.0240.0080.0025E");
Serial.println("SCBA001.Port A0E");
Serial.println("SCBA001.Port A1E");
Serial.println("SCBA001.Port A2E");
Serial.println("SCBA001.Port A3E");
Serial.println("SCBA001.Port A4E");
Serial.println("SCBA001.Port A5E");
Serial.println("SCBS001.0002E");
Serial.println("SLBL001.0250.0040.0080.0030.0.00E");
Serial.println("SCKB011.0335.0040.Show Port DataE");
dbOpen = true;
}
if (strcmp(temp,"SGoodByeE")==0){
// stop the Dashboard
dbOpen = false;
}
if ((buffer[1]=='C')&&(buffer[2]=='K')&&(buffer[3]=='B')){
ptr = (buffer[4]-48)*10 + (buffer[5]-48);
if (ptr==0) {digitalWrite(5,((buffer[6]-48)==1));}
if (ptr==1) {readPort = (buffer[6]-48)==1;}
}
if ((buffer[1]=='B')&&(buffer[2]=='T')&&(buffer[3]=='N')){
ptr = (buffer[4]-48)*10 + (buffer[5]-48);
for (int i=2;i<14;i++) {digitalWrite(i,!(digitalRead(i)));}
}
if ((buffer[1]=='S')&&(buffer[2]=='L')&&(buffer[3]=='V')){
ptr = (buffer[4]-48)*10 + (buffer[5]-48);
val = (buffer[6]-48)*1000 + (buffer[7]-48)*100 + (buffer[8]-48)*10 + (buffer[9]-48);
if (ptr==0) {
digitalWrite(lastpin,LOW);
lastpin=val;
digitalWrite(lastpin,HIGH);
}
if (ptr==1) {
analogWrite(10,val);
}
}
} else {
strcpy(temp,"BadCmd: ");
strncat(temp, buffer, bufferCount);
Serial.println(temp);
}
bufferCount=0;
}
void flash()
{
int led_pin = 13;
if (readPort) {
digitalWrite(led_pin, !digitalRead(led_pin));
if (dbOpen) {
Serial.print("SLBV001.");
Serial.print(analogRead(A0));
Serial.println("E");
}
}
}
Ich dachte auch, dass der SerialEvent den Prozessor blockt, aber dann müsste er das auch für die getimte Routine machen. Ausserdem schcike ich in der Zeit nichts sondern lese eldiglich auf der PC Seite die geschickten Werte und stelle sie in einem Graph dar.
Das Ganze dient übrigens als Plotroutine, so dass ich im Dashboard mit nur einem Kommando ein Graph-Control anfordern kann und dann Daten vom Arduino geplottet werden. Das habe ich immer vermisst, wenn ich z.B. den Input an A0-A5 betrachten will
Inzwischen ist das Dashboard mit den Controls Button, CheckBox, Combobox, Label und Slider fertig
Ciao, Mathias
-
Guten Morgen Matthias,
Nur eine kurze und vrmtl. dumme Frage: Warum deklarierst Du readPort als static im globalen Kontext ? Soweit ich mich da errinnere erreichst Du damit, dass die Variable auf file scope reduziert wird. Ob das das Problem beseitigt weiss ich nicht, aber es scheint mir einen Versuch wert, die Variable ohne static zu deklarieren.
Grüsse,
Christoph
-
Salut Christoph,
ich hatte die variable ursprünglich ohne static deklariert und dachte, dass es funktioniert, wenn ich sie static deklariere - kein Effekt
Mich wundert das Verhalten, da ich erwarte, dass die variable eine Speicherstelle belegt und in der Interruptroutine verändert wird. SObald dies im Memory geschrieben ist, sollte dies doch in der loop() routine sichtbar sein...
Da ich nach dem Befehle, die Variable auf false zu setzen auch nichts mehr über die Serielle Schnittstelle schicke, sollte auch keine Blockade durch das Handling der Seriellen Schnittstelle da sein, zumal 10 Sekunden sehr lange sind...
Ciao, Mathias
-
Salut Christoph,
welches IDE verwendest Du für den Ambientino? Ich bin am überlegen, das Arduino Dashboard auch auf Linux zu protieren
Ciao, Mathias
-
Guten Tag Mathias,
Entschuldige meinen späten Follow-Up. Ich wollte auch nochmal in Dein Serial Problem reinsehen da es mich selber sehr interessiert hab aber einfach die Zeit noch nicht gefunden.
Ich verwende unter Linux mit grosser Vorliebe PythonCard (http://pythoncard.sourceforge.net/) welches einen sehr einfachen Wrapper um wxPython (http://www.wxpython.org/) bietet und somit auch auf Windows läuft wenn wx.Python vorhanden ist oder es mit einem Packer zu einer exe gepackt wurde welche alle Libraries enthält.
Sobald man etwas wildere UIs bauen will kommt man um den direkten Zugriff via wxPython nicht rum, aber für schnelle und einfache Hacks ist PythonCard mein absoluter Liebling. So gut wie die gesamte innere Logistik und Lagerverwaltung bei Boxtec wird mit einer mittlerweile recht umfangreichen PythonCard Applikation gehandelt. Ich finde sogar mit dem pySerial Modul gibt es nichts trivialeres um Daten von einem Arduino seriell auszuwerten. Hier nur zum Appetit anregen die nötigen Zeilen um den Befehl "P1" seriell zu senden:
#!/usr/bin/python
import serial
serial_obj = serial.Serial('/dev/ttyACM0', 9600)
serial_obj.write("P1")
print "das wars auch schon"
# 2 bytes lesen und ausgeben
print serial.read(2)
# der saubere Handwerker schliesst der Port, passiert aber am Ende des Script eh:
serial_obj.close()
</Python-Lobpreisung> ;-)
Viele Grüsse,
Christoph
-
Salut Christoph,
klingt interessant und ich werde mir das mal anschauen. Was ich auf der Hompage gesehen habe, ist recht vielversprechend.
Zwischenzeitlich habe ich die erste Version des Arduino Dashboards fertig gestellt:
(http://www.mathias-wilhelm.de/arduino/assets/Arduino-VB/_resampled/resizedimage600427-db003.png)(http://www.mathias-wilhelm.de/arduino/assets/Arduino-VB/_resampled/resizedimage600427-db005.png)
Der Democode liest den in der Combobox angewählten anlog Port aus und stellt ihn im Histogram dar, im zweiten Tab werden alle 6 analog Ports dargestellt. Die beiden Zeitpanels schicken entweder die aktuelle PC-Zeit oder eine frei wählbar Zeit als Ticket an den arduino (der Sketch ist noch in Arbeit)
Ciao, Mathias
-
Salut,
das Problem mit der verzögerten Antwort für eine logische Variable, welche in der Handlingroutine des SerialEvents() gesetzt wird, habe ich gelöst:
Die Variable muss als "Volatile" deklariert werden (siehe attachInterrupt() Dokumentation)
Spannend wird es, wenn man versucht, Encodermotoren über die serielle Schnittstelle zu steuern. Da man für die Auslese der Encoder externe Interrupts verwendet, stört das die SerialEvent Routine (wie auch dort in der Dokumentation beschrieben). Es sieht aber so aus, als könne man das mit etwas mehr Protokollaufwand abfangen.
Ciao, Mathias