Schlagwort-Archive: Arduino Uno

Datenknoten mit Arduino

Loading

Leider sind die Abstände, in denen ich ein wenig Zeit finde, einen neuen Beitrag für den Blog zu schreiben, nicht kürzer geworden. Aber einen Beitrag pro Monat zu posten, halte ich ein… 🙂

Dieses Mal ist es kein Retro Bastelprojekt aus den heimischen Gefilden oder eine Restauration eines alten Gerätes, sondern wieder etwas zum Thema Arduino. Die Idee – es soll ein Sensor gebaut werden, der wie immer, eine physikalische Größe in ein elektrisches Signal umwandelt. Das ist jetzt nichts Besonderes und um welche Art von Sensor es sich handeln wird, werde ich vorerst noch nicht beschreiben. Aber es soll nicht ein Sensorboard geben, sondern viele. Und diese Sensorboard kurz „Sensoren“ sollen in einer zweidimensionalen Matrix miteinander vernetzt werden. Man kann sich das in etwa vorstellen wie ein Schachbrett, wobei jedes der Schachbrettfelder einen Sensor darstellt. Dieses Netzwerk an Sensoren – also Sensorknoten – soll dann über eine Übergabestelle mit einem Rechner verbunden sein und die Sensordaten des jeweiligen Feldes ausgeben. Es soll dann auch möglich sein, einzelne Felder aus dem Netzwerk zu entfernen ohne dass das verbleibende Netzwerk seine Funktion verliert.

Das ganze System soll möglichst einfach und günstig aufgebaut werden. Und so ist schnell ein Systemkonzept entstanden, in dem die Knoten über den I²C Bus kommunizieren und ihre Daten zu einem Master senden. Das folgende Diagramm soll das verdeutlichen.

Dieses Konzept, so dachte ich mir, lässt sich am einfachsten mit einem Atmega Microcontroller realisieren. Der hat genügend IO´s, einen I²C Bus und UART onboard, ebenso auch analoge Eingänge und benötigt wenig Bauteilperipherie, um ihn in einem eigenen Layout zum Leben zu erwecken. Und es gibt nichts schnelleres, so einen Testaufbau eines solchen Knotennetzwerks zu realisieren, als die gut bekannten Arduino Development Boards zu benutzen. Ich habe die günstigste Variante für einen Testaufbau gewählt -> den Chinanachbau vom Arduino Uno (Joy-IT UNO) mit dem Atmga328 im gesockelten DIL Gehäuse.

Joy-It Uno Boards

Im Bild sind zehn Stück dieser Microcontrollerboards zu sehen. Von denen soll einer als Bus-Master und neun Stück als Slaves eingesetzt werden. Jeder dieser Slaves hat natürlich eine eindeutige Bus-Adresse, die im System nur einmal vorkommt. Im Testaufbau wird diese Busadresse über den Programmcode fest vergeben, da ohnehin jeder Arduino einmal mit dem Rechner verbunden werden muss, um den Programm-Upload durchzuführen. Das soll natürlich später anders aussehen. Denn der Arduino wird auf den Atmega328 Chip, seinen Quarz und die paar Widerstände reduziert auf dem Sensorboard mit gelayoutet. Programmiert soll der Chip dann über die ISP Pins werden. Da bei vielen Boards natürlich nicht jedes Mal der Programmcode individuell angepasst wird und alle das gleiche Flashfile erhalten sollen, will ich die Sensoradresse mit einem 7Bit Dipschalter einstellen. Ein 4021 Cmos Static Shift Register soll die Bits nach dem Einschalten des Controllers auslesen und seriell in den Controller schieben. Der daraus resultierende Wert steht dann in einer Variablen als Busadresse zu Verfügung.

Jeder dieser Slaves mit seiner individuellen Busadresse wird nun vom Masterknoten der Reihe nach abgefragt, welchen Zustand er hat und ob er einen Ausgang schalten soll, oder nicht. Das bedeutet, der Knoten hat lediglich einen DO (Digitalen Ausgang) mit dem er beispielsweise eine LED aus- und einschalten kann und einen oder mehrere DI (Digitalen Eingang) der einen Zustand, zum Beispiel eines einfachen Schalters abfragt. Diese Funktionen werden in 2 Bits eines Bytes gespeichert. Ein weiteres Byte dient zur Übertragung der Busadresse. Es werden also zwei Bytes über den Bus geschickt. Das untenstehende Bild zeigt den Testaufbau mit den „UNO-Boards“

Alle Arduinos sind mit I²C Datenbus und Spannungsversorgung verbunden

Der Ablauf läuft wie folgt:

MASTER:
Der Masterknoten sendet nach der Reihe an alle Slave-Adressen eine Anfrage und einen Schaltbefehl (der kommt für alle Knoten von einem TEST-Taster Eingang am Master) für den LED-Ausgang des Knotens und sieht ob eine Antwort zurückkommt oder nicht. Wenn keine Antwort kommt, ist der Knoten nicht im Netz oder defekt. Kommt eine Antwort, so besteht diese aus der Adresse des Knotens und seinem Statusbyte. Diese Informationen werden über ein RS232 Terminal an den, am Master angeschlossenen Rechner übertragen. So kann dort beispielsweise über eine Visualisierung mittels (NI LabVIEW, oder Matlab o.ä.) der Schaltzustand jedes einzelnen Knotens am Bildschirm angezeigt werden. Mit einer Anpassung des Master Programmes ist es auch möglich, die LED-Ausgänge der Slaves über den Rechner zu schalten.

SLAVE:
Wenn der Masterknoten vom Slave Daten anfordert, so sendet der Slave zwei Bytes zurück. Wobei Byte0 wieder die Slave ID (also Busadresse ist) und Byte1 die Daten. Byte1 besteht eigentlich aus nur zwei Bit, die wie folgt kodiert sind (in dezimaler Darstellung):
 0 = LED aus | Sensor nicht ausgelöst
 1 = LED ein | Sensor nicht ausgelöst
 2 = LED aus | Sensor ausgelöst
 3 = LED ein | Sensor ausgelöst

Der Programmcode als Beispiel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// I2C Slave Code
// 16.05.2018 
// ver 1.3
#include <Wire.h>
#define ADDRESS 2     // adresse des slave knotens
#define PAYLOAD_SIZE 2 // anzahl der bytes  die vom masterknoten zu erwarten sind
int LED=12;            // indicator led an pin D12
int SENSOR = 8;        // sensor input an pin D8
bool actionState=0;      // sensor zustand
int busstatus;  // statusvariable 
                       // 0 = LED aus | sensor nicht belegt
                       // 1 = LED ein | sensor nicht belegt
                       // 2 = LED aus | sensor belegt
                       // 3 = LED ein | sensor belegt
 
bool sensled=0;          // sensor LED
byte nodePayload[PAYLOAD_SIZE];

void setup()
{
  pinMode(LED, OUTPUT);         //sensorLED
  pinMode(SENSOR, INPUT);       //Sensor 
  Wire.begin(ADDRESS);          // Activate I2C network
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent); // auf master anforderung warten
                      //  // debug interface
                      //  Serial.begin(9600); 
}

// *********************************mainloop****************************************************
void loop()
{ 
  delay(5);
  
   if(sensled){digitalWrite(LED, HIGH);}
         else{digitalWrite(LED, LOW);}

  actionState = digitalRead(SENSOR);  //Sensoreingang abfragen        
   if((actionState==1)&&(sensled==1)) {busstatus=3;}
   else if ((actionState==0)&&(sensled==1)) {busstatus=1;}
   else if ((actionState==1)&&(sensled==0)) {busstatus=2;}
   else if ((actionState==0)&&(sensled==0)) {busstatus=0;}

                      //  Serial.println("######################");
                      //  Serial.print("busstatus neu setzen ");
                      //  Serial.println(busstatus);
                      //  Serial.print("sensled LED            ");
                      //  Serial.println(sensled);
                      //  Serial.print("actionState           ");
                      //  Serial.println(actionState);
                      //  Serial.println("######################");
  nodePayload[0] = ADDRESS;                  // Adresse in byte0 zurücksenden.  
  nodePayload[1] = busstatus;                //byte 1 ist die statusinfo der LED
}



// *********************************************************************************************
void requestEvent()
{ Wire.write(nodePayload,PAYLOAD_SIZE);  
  Serial.println("bytes status schreiben");
  Serial.println(nodePayload[0]);
  Serial.println(nodePayload[1]);
  delay(5);
}

// *********************************************************************************************
void receiveEvent(int bytes)  //einen wert vom I2C lesen
      
{
  
  busstatus = Wire.read(); //If the value received was true turn the led on, otherwise turn it off  
                              //  Serial.println("status empfangen");
                              //  Serial.println(busstatus);
  if((busstatus==1)||(busstatus==3)){sensled = 1;}
                                else{sensled = 0;}
                                              
}

 

Die Busadresse ist in diesem Slave-Code noch individuell einzugeben. In der nächsten Version ist dann der vorherbeschriebene „Serializer“ der parallelen Dip-Schaltervariante implementiert. Das folgende Codebeispiel ist von Masterknoten, der die Slaves ausliest und mittel Prüftaster ein LED Muster an die Sensor Slaves sendet:

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// I2C masterknoten 
// 16.05.2018 
// ver 1.2
// changes abfrage wenn kein knoten am bus dann 255 ausgeben
#include <Wire.h>

#define busbytes 2          // wievele byte vom I2C knoten zu erwarten sind
#define maxKNOTEN  10       // anzahl der zu scannenden slaves
#define startKNOTEN 2       // startadresse der slaves
#define DELAY 5             // einfach ein delay ....

int i; int j=0;
int buttonPin = 12;
int testbut = 0; int anim = 0;
int buttonState = 0;
int DATEN[busbytes];
int adresse; int busstatus;  
byte sensorbelegt; byte ledsensoron;

                       // 0 = LED aus | Sensor nicht belegt
                       // 1 = LED ein | Sensor nicht belegt
                       // 2 = LED aus | Sensor belegt
                       // 3 = LED ein | Sensor belegt

int leddat1[] = {1,1,1,1,0,1,1,1,1}; // -
int leddat2[] = {0,0,0,0,1,0,0,0,0}; // |

void setup()
{
  Serial.begin(9600);  
  Serial.println("MASTER-KNOTEN");
  Serial.print("Maximum Slaveknoten: ");
  Serial.println(maxKNOTEN);
  Serial.print("Datengroesse in byte: ");
  Serial.println(busbytes);
  Serial.println("***********************");
  
  Wire.begin();                 // Activate I2C link
  pinMode(buttonPin, INPUT);    // test-tastereingang festlegen
}


//#####################################################################################################
void loop()
    
{
  for (int Knotenadresse = startKNOTEN;         //alle knoten scannen
           Knotenadresse <= maxKNOTEN; 
           Knotenadresse++) 

        
    //################################################################################################       
    { 
     // testbut = 0;  
     anim = 0;   
    Wire.requestFrom(Knotenadresse, busbytes);        // daten vom jeweiligen knoten anfordern
                                                 
           DATEN[0]=255; DATEN[1]=255;   // wenn kein knoten dann auf 255 setzen    
          if(Wire.available() == busbytes) {                                    // wenn knoten und daten dann
            for (i = 0; i < busbytes; i++) DATEN[i] = Wire.read();          // daten holen (zuerst busID, dann daten)
           // for (j = 0; j < busbytes; j++) Serial.println(DATEN[j]);        // daten an rs232 ausgeben   
          }            

//            Serial.println(Knotenadresse);
//            Serial.println(DATEN[0]);
//            Serial.println(DATEN[1]);
//            Serial.println(" ");
           
            adresse=DATEN[0]; 
            busstatus=DATEN[1];
           
            if(busstatus == 0)       {sensorbelegt=false;  ledsensoron=false;}
            else if (busstatus == 1) {sensorbelegt=false;  ledsensoron=true;}
            else if (busstatus == 2) {sensorbelegt=true;  ledsensoron=false;}
            else if (busstatus == 3) {sensorbelegt=true;  ledsensoron=true;}
      
         //################################################################################################
         //Testbutton Status lesen und variable testbut entsprechend setzen
       
          buttonState = digitalRead(buttonPin);               //tastereingang einlesen
          if(buttonState == HIGH){                            //wenn taster aktiv dann variable anim setzen
          anim = 1;
          //delay(5); 
          }
            
//            //debug debuginfo tasterstatus auf rs232 ausgeben
//            Serial.println("#######################");
//            Serial.print("Knoten Adresse    :");
//            Serial.println(adresse);
//            Serial.print("Busstatus         :");
//            Serial.println(busstatus);
//            
//            Serial.println("----------------");
//            Serial.print("Fliese belegt    :");
//            Serial.println(sensorbelegt);
//            Serial.print("LED Fliese       :");
//            Serial.println(ledsensoron);
//            Serial.print("#######################");
//            Serial.println(" ");
      
          //################################################################################################
          //Testbutton Status an jeweiligen knoten senden
      
          Wire.beginTransmission(Knotenadresse);           // transmit to device actual in for loop
          //anim schreiben
                    
           if (anim==0) {testbut=leddat1[j]; j++;}
                   else {testbut=leddat2[j]; j++;}
           if (j>8){j=0;}
          
          Wire.write(testbut);                             // senden des tasterstatus
          Wire.endTransmission();                          // ende gelände mit uerbertragung
       
          delay(DELAY);
          

    }
   
}

 

Mit dieser Anordnung ist es jetzt möglich alle Arduinos und deren Eingang auszulesen bzw. die LED über den Bus zu steuern. Im nächsten Schritt wird ein „Sensor“ gebaut, eine Platine gelayoutet und der Arduino Uno auf seinen Microcontroller reduziert. Darüber werde ich in einem der nächsten Posts berichten…

 

 

 

Feuchtesensor am Arduino

Loading

DSC_2268In den letzten Blog-Einträgen habe ich mit Hilfe des Arduino Uno – Experimentierboards auf unterschiedliche Weise einen NTC-Widerstand zur Messung der Temperatur eingesetzt. Aus einem anderen Projekt habe ich auch noch einen Feuchte/Temperatursensor der Firma IST (Innovative Sensor Technologie) zur Verfügung, der mit einem Raspberry und in Python ausgelesen wurde. Es handelt sich um den digitalen Sensor HYT939, der über den I²C Bus ausgelesen wird. Er zeichnet sich laut Datenblatt mit folgenden Merkmalen aus:

  • DSC_2269chemisch sehr resistent
  • sehr weiter Temperatur- und Feuchtigkeitsmessbereich (-40°C .. +125°C, 0% ..100% RH)
  • mechanisch robuste Bauform
  • kalibriert und temperaturkompensiert
  • sehr geringer Drift
  • einsetzbar bis zu einem Umgebungsdruck von 16bar
  • Versorgungsspannung von 2.7 bis 5.5V
  • Auflösung von +/- 0.02% RH und 0.015°C
  • Genauigkeit von +/- 1.8% RH bei +23°C und +/-0.2K

Also wollte ich diesen Sensor auch mit dem Arduino betreiben und vielleicht in weiterer Folge auch den NTC parallel auslesen und die Ergebnisse vergleichen. Aber zurerst einmal wird der HYT an den Arduino angeschlossen.

hytpinDas Bild zeigt das Pinout des HYT in der Ansicht von unten. Die Belegung der Pins lautet:

  • 1…SCL
  • 2…VCC
  • 3…GND
  • 4…SDA

Somit lässt sich der Sensorchip ganz einfach an den Arduino anschließen, wobei die Pins SDA auf den Arduino Pin A4 und SCL auf den Pin A5 gelegt sind. Die Ausgabe der ausgelesenen Werte soll wieder wie beim NTC auf dem LC-Display stattfinden. Nachstehend ist der Code gelistet:

 

/*
 &nbsp; HYT939 bei Arduino UNO an:
 &nbsp; SDA pin A4
 &nbsp; SCL pin A5
 &nbsp; HYT939 bei MEGA2560 an:
 &nbsp; SDA pin 20
 &nbsp; SCL pin 21
 &nbsp; LCDisplay
 &nbsp; Pinzuordnungen allgemein für LCD
 &nbsp; RS to digital 12
 &nbsp; EN to digital 11
 &nbsp; D4 to digital 5
 &nbsp; D5 to digital 4
 &nbsp; D6 to digital 3
 &nbsp; D7 to digital 2
 &nbsp; R/W to ground
 &nbsp; VSS to ground
 */
 //I2C Addresse festlegen
 #define ADDR 0x28
 
 //Variablen und Datentypen festlegen
 double temp;
 double hum;
 unsigned int tempraw;
 unsigned int humraw;
 int x;
 unsigned char buffer[4];
 
 //Libraries laden
 #include <Wire.h>
 #include <LiquidCrystal.h>
 
 // interfacepins initialisieren
 LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
 &nbsp; void setup()
 &nbsp; {
 &nbsp; //I2C und LCD Interface initialisieren,
 &nbsp; Wire.begin();
 &nbsp; lcd.begin(20, 4);
 &nbsp;&nbsp;
 &nbsp; Serial.begin(9600);
 &nbsp; lcd.println("HYT939-Sensor");
 &nbsp; }
 
 void loop()
 &nbsp; {
 &nbsp; //I2C auselesen
 &nbsp; Wire.beginTransmission(ADDR);&nbsp;
 &nbsp; Wire.endTransmission();
 &nbsp; delay(200);
 
 &nbsp; //4 Bytes auslesen
 &nbsp; Wire.requestFrom(ADDR, 4,true);
 &nbsp; x=0;
 &nbsp; while(Wire.available())&nbsp;
 &nbsp; { char c = Wire.read(); buffer[x]=c; x++; }
 
 //Rohdaten aus Puffer lesen
 tempraw=buffer[2]*256+buffer[3];
 humraw=buffer[0]*256+buffer[1];
 
 //Daten laut Datenblatt maskieren
 tempraw&=0xfffc;
 humraw&=0x3fff;
 tempraw=tempraw/4;
 
 //Rohdaten in Ausgabeformat umrechnen
 temp=(double)tempraw/99.2909; &nbsp;//skalieren laut datasheet
 temp=temp-40.0;
 hum=(double)humraw/163.83;
 
 //Daten auf LCD schreiben
 &nbsp; lcd.setCursor(0, 2); &nbsp;
 &nbsp; lcd.print("Temperatur =");
 &nbsp; lcd.setCursor(11, 2);
 &nbsp; lcd.print(temp);
 &nbsp;&nbsp;
 &nbsp; lcd.setCursor(0, 3); &nbsp;
 &nbsp; lcd.print("Humidity =");
 &nbsp; lcd.setCursor(14, 3);
 &nbsp; lcd.print(hum);
 //lcd.setCursor(0, 2);
 //lcd.print('Buffer0 =');
 //lcd.setCursor(11, 2);
 //lcd.print(buffer[2]);
 //lcd.setCursor(0, 3);
 //lcd.print('Buffer1 =');
 //lcd.setCursor(11, 3);
 //lcd.print(buffer[3]);
 }

 

Arduino mit Matlab … Der Sensor

Loading

Nachdem ich im letzten Teil die Installation der Arduino-Matlab Packages zum Laufen bekommen habe, kann es nun weitergehen. Ziel dieses kleinen Beispiels ist es ja, an einen Arduino Uno einen NTC-Widerstand anzuschließen. Dieser soll als analoger Temperatursensor dienen (was er ja ist 😀 ) und über einen der A/D – Eingänge des Arduino eingelesen werden. Matlab soll dann schlussendlich die eingelesenen A/D- Werte  in Temperaturwerte umrechnen. Das Ganze soll über eine gewisse Zeit laufen, wobei alle paar n-Sekunden ein Wert eingelesen wird. Am Ende soll ein wunderschöner Temperaturplot mit Temperatur über Zeit erstellt werden.

Doch zuerst zum Sensor. Ich verwende hier einen NTC-Widerstand von dem Hersteller VISHAY mit einem R25 von 2200 Ohm (s.Datenblatt). Das bedeutet, er hat bei 25°C einen ohmschen Widerstand von 2.2kOhm. Je wärmer es nun wird, umso kleiner wird der Widerstand und umgekehrt. Der analoge Eingang des Arduino hat eine Auflösung von 10Bit. Das bedeutet, er kann den Spannungshub am Eingang (eingestellt durch die ADC Referenz) in 210 , also 1024 Teile auflösen (zerlegen). Der Standard beim Arduino ist 0V bis 5V. Also 0V am Eingang bedeutet einen ADC-Wert von 0 und 5V bedeutet einen ADC-Wert von 1024. Die Auflösung, also die kleinste auflösbare Spannungsänderung  ist daher:
formelarduino01 (2)Gibt der ADC beispielsweise einen Wert von 558 aus, so entspricht dies einer Spannung am Eingang von 558*0.00488V = 2.72304V. Doch wie soll der NTC jetzt an den Eingang angeschlossen werden? Ganz einfach. Man nehme einen Spannungsteiler und dimensioniere ihn so, dass der Strom durch die beiden Widerstände nie so groß werden kann, dass er zum einen die Versorgung des Arduino gefährdet und zum anderen auch nie so groß werden kann, dass er den NTC selbst erwärmt. In der Skizze ist nun dargestellt wie der Spannungsteiler aufgebaut und angeschlossen ist. Jetzt gilt es noch herauszufinden, wie aus der Spannung am ADC der gemessene Widerstandswert berechnet werden kann. Hier hilft die Spannungsteilerregel:
formel2
In diesem Beispiel hier besteht der Spannungsteiler aus dem NTC (2k2 @ 25°C) und einem 2k2 Festwiderstand. Wenn der NTC null Ohm haben sollte (nur rein theoretisch), so fließt ein maximaler Strom von 0.002A durch die Widerstände (5V/2200Ohm). Wir lesen jetzt in Matlab den Spannungsabfall am NTC ein und können uns über den Spannungsteiler den Widerstandswert berechnen. Jetzt fehlt nur noch die Formel zur Berechnung der Temperatur. Und die ist, samt den notwendigen Konstanten A1, B1, C1 und D1, im Datenblatt angegeben:formel3Das Ergebnis dieser Berechnung ist die Temperatur in Kelvin. Um die Temperatur aber in °Celsius angezeigt zu bekommen, rechnet man: °C = °K-273,15. Mit all diesen Informationen kann man jetzt ein Matlab-Script schreiben, das dann in etwa so aussieht:
 
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Beispielscript um mit ArduinoUno einen Temperaturverlauf aufzuzeichnen
% 02/2016 by I.Bihlo
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
&nbsp;
%a = arduino('com5','Uno');
&nbsp; a = arduino('com5','Uno','TraceOn', true)
&nbsp; analogpin=0;
&nbsp; analog=0;
&nbsp;
&nbsp; %ein paar konstanten für die weiteren berechnungen
&nbsp; %
&nbsp; r=2200;&nbsp; %Spannungsteilerwiderstand
&nbsp; rt=0; %das wird der errechnete widerstand des NTC
&nbsp; urt=0; %das wird der errechnete Spannungsabfall am NTC
&nbsp;
&nbsp; % konstanten für berechnung der Temperatur aus NTC Widerstand
&nbsp; % B25=3977K
&nbsp; a1=3.354016E-03;
&nbsp; b1=3.2569850E-04;
&nbsp; c1=2.61013E-06;
&nbsp; d1=6.38309e-08;
&nbsp; rref=2200;
&nbsp;
&nbsp;
&nbsp;
&nbsp; %Lesen eines Temperaturabhängigen Widerstandes am Analog Eingang A0
&nbsp; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
&nbsp;
&nbsp; for x=1:50
&nbsp; analog(x)=readVoltage(a, analogpin) %Liest den AnalogIn von A0 und gibt in Volt aus
&nbsp; urt(x)=5-analog(x); %spannungsabfall am NTC
&nbsp; rt(x)=(r/analog(x))*urt(x); %widerstand des NTC
&nbsp;
&nbsp; %berechung der temperatur
&nbsp; rvsrref(x)=rt(x)/rref;
&nbsp; temp(x)=1/(a1+(b1*log(rvsrref(x)))+(c1*(log(rvsrref(x)^2)))+(d1*(log(rvsrref(x)^3))));
&nbsp; tempc(x)=temp(x)-273.15 %Kelin in Celsius umrechnen
&nbsp; pause(2);
&nbsp;
&nbsp; end
&nbsp;
&nbsp; clear a;
&nbsp;
&nbsp; %plot den mist
&nbsp; figure(1);
&nbsp; time=1:50;
&nbsp; plot(time,analog,'b');
&nbsp; grid on; hold on;
&nbsp; plot(time,rt/1000,'r');
&nbsp; plot(time,urt,'g');
&nbsp; plot(time,tempc,'m');
&nbsp; legend('Analogspannung des ADC','Widerstand des NTC in kOhm','Spannung am NTC','Temperatur [°C]'); 
&nbsp; %ende

 

 
temperaturgraph
So sieht der Graph mit dem Temperaturlog danach aus…

Arduino mit Matlab … Startschwierigkeiten

Loading

arduino01 (4)Heute möchte ich ein Thema vorstellen, mit dem ich mich kurz im Rahmen einer Recherche beschäftigt habe. Viele (Techniker) kennen vielleicht die Software Matlab von der Firma Matworks. Das ist ein extrem umfangreiches Softwarepaket, das bei uns in der Lehre eingesetzt wird, um Studierende in allen möglichen technischen Bereichen zu unterrichten. Sei es Signalverarbeitung, Regelungstechnik, Bildverarbeitung, etc. – Matlab ist das Tool, um all das professionell umzusetzen. Auch wenn es um Messdatenerfassung geht, muß man nicht unbedingt auf Tools zurückgreifen, mit denen ein Projekt oder Programm „gezeichnet“ wird. Das ist zwar toll für den Einstieg, kann (und wird) in späterer Folge aber aufgrund des riesen Overheads und der benötigten PC Resourcen viel zu umständlich, wenn man schnell einmal über eine externe Hardware irgendwelche physikalischen Größen aufzeichnen und verarbeiten will.

In diesem kleinen Projektchen möchte ich nun zeigen, wie einfach man mit Hilfe eines Arduino Uno Boards zusammen mit Matlab eine Temperaturmessung realisieren kann. Der Sensor soll hier ein ganz einfacher NTC Widerstand sein. Das bedeutet, der ohmsche Widerstand des Bauteils ändert sich mit der Temperatur, in dessen Umgebung sich das Bauteil befindet. Die Änderung passiert hier folgendermaßen: wenn die Temperatur steigt, so wird der ohmsche Widerstand kleiner. (Negativer Temperatur Koeffizient) (Coefficient engl. -> NTC). Einziger kleiner Haken – die Änderung passiert nicht linear wie zum Beispiel beim PT100. Das bedeutet der Verlauf  des Widerstands bei Änderung der Temperatur ist keine Gerade mit irgendeiner Steigung, sondern eine e-Funktion. Das wiederum bedeutet, dass, will man aus den gemessenen Widerstandwerten in eine Temperatur zurückrechnen, die Funktionsgleichung zum NTC gefunden werden muss. Glücklicherweise findet man diese aber fix und fertig in den Herstellerdatenblättern 😉 aber dazu später.

Man hat nun einen temperaturabhängigen Widerstand, ein Arduino Uno – Board und einen PC auf dem Matlab läuft. Wie geht´s nun weiter? Nehmen wir als Beispiel an, dass Matlab 2014b installiert ist. Nun gibt es in Matlab unter dem Tab „Home“ eine Rubrik, die sich „Add-Ons“ nennt. Klick man auf  diese, so klappt ein Menue auf, in dem man den Eintrag „Get Hardware Support Packages“ findet. Den klickt man an und es öffnet sich ein Fenster namens „Support Package Installer“.

Unter „Install from Internet“ und „Next>“ kommt man zu einem Auswahlmenue der verfügbaren Packages. Hier sucht man sich „Arduino“ aus und setzt im rechten Bereich des Fensters einen Haken – und zwar bei dem Paket „Aquire inputs and send outputs on Arduino Uno…“. Danach wieder auf „Next>“ klicken und die Installation vollenden.

Ist alles erledigt so kann man den Arduino am USB-Port anstecken. Windows sollte einen Treiber zuordnen. (Unter Systemsteuerung – Gerätemanger – Anschlüsse > sollte ein Arduino Uno (COMx) zu finden sein). Ist das der Fall, dann sollte es klappen 🙂

Jetzt kann in der Matlab Console folgender Befehl eingegeben werden:

arduino()

Folgende Antwort kommt nun zurück:

 arduino with properties:

Port: 'COM5'
Board: 'Uno'
AvailableAnalogPins: [0, 1, 2, 3, 4, 5]
AvailableDigitalPins: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Libraries: {'I2C', 'SPI', 'Servo'}

Der COM-Port unterscheidet sich natürlich und wird meist auf den nächsten freien COM gemappt. Hat das aber geklappt, so ist der erste Teil schon gewonnen. Matlab kommuniziert mit dem Arduinoboard. Es kann aber auch vorkommen, dass Fehlermeldungen auftauchen. Wie bei mir der Fall:

Cannot detect Arduino hardware. Make sure original Arduino hardware is properly plugged in. If
using unofficial(clone) hardware, specify port and board type. For more information, see the
arduino function reference page.

Das war die erste Meldung nach der Installation: Dafür gibt’s aber auch Abhilfe. Man gibt folgendes ein:

a = arduino('com5','Uno')

Wenn auch dann eine Fehlermeldung kommt, so wie natürlich bei mir, dann kann man mit folgendem commando auf Fehlersuche gehen:

 a = arduino('com5','Uno','TraceOn', true)

Jetzt kam bei mir die Meldung : Updating server code on Arduino Uno (COM5). Please wait.  Und danach gleich ein Errorfenster mit einem „avr-gcc.exe
Die Anwendung konnte nicht korrekt gestartet werden (0xc00000142)“ Fehler. Danach habe ich lange in diversen Foren gesucht, Tracelogs verglichen und nach den Fehlermeldungen gegoogelt. Hier gab es Tips von: die make.exe in den Tiefen des SupportPackages Ordner ist nicht kompatibel mit 64bit Systemen, mit Windows8.1 und Windows10 gibt es Zugriffsrechteprobleme und und und. Leider war aber nie ein Lösungsvorschlag dabei der bei mir funktionierte. Also habe ich auf einem zweiten Rechner eine jungfräuliche Installation von Matlab und dem Arduino Matlab Package durchgeführt. Und siehe da – es funktionierte. Aber wie jetzt nach dem Fehler suchen, oder ihn beheben. Also habe ich auf dem funktionierenden Rechner einen Tracelog durchgeführt, indem ich beim Aufrufen der Arduinofunktion einen Port angegeben habe, auf dem ein anderes NICHT Arudino Gerät angeschlossen ist. Und siehe da, der Errortracelog war seeehr lange und ausführlich. Man konnte ansehen was die Funktion alles aufruft. Und irgendwo ganz unten im Log las ich was von avrdude… Da kam mir die Idee. Auf meinem PC sind einige Entwicklungsumgebungen installiert. Unter anderem auch AVR-Studio und der WinAVR20100110 Compiler. Und genau der war das Problem. Den WinAVR gelöscht und schon klappte es.

Also steht dem weiteren Aufbau zur Realisierung des Projektchens nichts mehr im Weg. Aber darüber scheibe ich beim nächsten Mal in Teil 2…

 

Arduino als USB-Joystick

Loading

IMAG1861
Arduino als USB-Joystickinterface

Die große Welt der kleinen Microcontroller und vor allem der sehr günstigen Microcontroller und deren Vielfältigkeit, hat mich beim folgenden ‚Projektchen‘ wieder motiviert sie zu verwenden. Für die eher weniger outdoorlastigen kalten Wintertage habe ich mir ein Projekt vorgenommen, dass das Thema Retrocomputer betrifft. Zum einen habe geplant, die Website um die Rubrik der alten Computer zu erweitern (zumindest sollen die paar wenigen aus meiner Sammlung vorgestellt und in Betrieb genommen werden…) und zum anderen will ich mir eine Arcade-Station bauen, deren Kern das neue RaspBerry2 Modell ist . Auf dem soll die Emulatorplattform Retropie aus dem gleichnamigen Projekt zu Einsatz kommen. Die ersten Versuche mit den Images aus dem Projekt sehen sehr gut aus. Die alten 8-Bit und 16-Bit Computer laufen in der Emulation ausgezeichnet (C64, Atari, Amiga usw…). Was die Anbindung von Eingabegeräten betrifft, kann man natürlich Maus und Keyboard vom PC an den Raspberry anschliessen und alles steuern. Die Jungs aus dem Projekt haben auch selbst einige Boards entwickelt, die die Anbindung von Joysticks und co an des Raspberry ermöglichen. (zB. den GPIO Adapter). Aber da ich ein paar Arduino Unos herumliegen habe, dachte ich mir, warum nicht diese verwenden. Da es beim Arduino möglich ist, den Mega 16U2 zu flashen (der Chip wird als Programmer für den Atmega 328 auf dem Uno – Board verwendet) und aus dem Uno-Board so zum Beispiel ein USB-HID (Human Interface Device) zu machen -sprich Keyboard, Mouse, was auch immer, bot sich der als ideale Plattform an.

Tutorials, den Arduino zu einem HID-Interface zu flashen, gibt es mittlerweile schon sehr viele. Zum Beispiel findet man hier eine schöne Anleitung. Je nach dem welche Arduino-Uno Boardversion man hat, kann man sich den zusätzlichen Widerstand fürs DFU-Flashen sparen. (z.Bsp. bei meinen Boardversionen R3 ist der Widerstand nicht nötig.) Auf den neuen Boards ist auch nicht mehr der Atmega 8U2 verbaut, sondern der 16U2. Man muß einfach im Atmega Flash-Tool „ATMEL-FLIP“ den entsprechenden Chip auswählen. Die entsprechenden Firmwarefiles findet man im Netz…

Arduino-keyboard-0.3.hex
Arduino-mouse-0.1.hex

Ist der Arduino dann geflasht, so wird er beim Anschluss an den PC eben als Keyboard oder Maus erkannt. Jetzt braucht man nur mehr den gewünschten Code in den Atmega328 zu schreiben und die Arduino UNO Eingänge führen z.Bsp. Tastaturbefehle aus. Um die Firmware jetzt nicht jedes Mal zwischen Programmer und USB-HID zu ändern, verwende ich einfach einen zweiten UNO der als Programmer dient und stecke einfach den geflashten Chip immer um (ist viel einfacher beim Testen).

Da das Interface jetzt soweit passt, habe ich mich mit dem HID-Codetable beschäftigt und die entsprechenden Tasten, die die Joystickbewegungen an den PC senden sollen herausgesucht.

Hier der sehr einfache Code der momentan die alten Commodore Joystickbewegungen umsetzt:)

/* HID Joystickinterface für Arduino UNO im HID Modus
 Jun2015 by I.Bihlo
 Die Tasten brauch i  Taste/Hexcode/Dec-Code  
  KEY_LEFT_CTRL    0x01  //01 
  KEY_LEFT_SHIFT    0x02  //02 
  KEY_LEFT_ALT    0x04  //04 
  KEY_LEFT_GUI    0x08  //08 
  KEY_RIGHT_CTRL    0x10  //16 
  KEY_RIGHT_SHIFT 0x20  //32 
  KEY_RIGHT_ALT   0x40  //64 
  KEY_RIGHT_GUI   0x80  //128 

  KEY_RIGHT_ARROW 0x4F  //79 
  KEY_LEFT_ARROW  0x50  //80 
  KEY_DOWN_ARROW  0x51  //81 
  KEY_UP_ARROW    0x52  //82 
  KEY_TAB         0x2B  //43 
  KEY_ENTER       0x28  //40 
  KEY_SPC         0x2C  //44 
 */ 
     uint8_t keyNone[8] = { 0, 0, 0, 0, 0, 0, 0 }; 
     uint8_t keyA[8] = { 0, 0, 4, 0, 0, 0, 0 };  //left 
     uint8_t keyD[8] = { 0, 0, 7, 0, 0, 0, 0 }; //right 
     uint8_t keyW[8] = { 0, 0, 26, 0, 0, 0, 0 }; //up 
     uint8_t keyS[8] = { 0, 0, 22, 0, 0, 0, 0 }; //down 
     uint8_t keySPACE[8] = { 0, 0, 44, 0, 0, 0, 0 }; //space 
      
     uint8_t keyLEFT[8] = { 0, 0, 80, 0, 0, 0, 0 };  //left 
     uint8_t keyRIGHT[8] = { 0, 0, 79, 0, 0, 0, 0 }; //right 
     uint8_t keyUP[8] = { 0, 0, 82, 0, 0, 0, 0 }; //up 
     uint8_t keyDOWN[8] = { 0, 0, 81, 0, 0, 0, 0 }; //down 
     uint8_t keyENTER[8] = { 0, 0, 40, 0, 0, 0, 0 }; //enter 
      
 // DEFINE inputs 
 const int UPA = 12; 
 const int DOWNA = 13; 
 const int LEFTA = 2; 
 const int RIGHTA = 3; 

 const int UPB = 4; 
 const int DOWNB = 5; 
 const int LEFTB = 6; 
 const int RIGHTB = 7; 

 int out=0; //fürs Testen am Serial Monitor im Programmermodus 

 void setup() { 
    
   // Die Pins als Eingang definieren (Intern PullUP setzen - ist bei mir nötig da actice Low geschaltet wird 
   pinMode(UPA, INPUT_PULLUP); pinMode(DOWNA, INPUT_PULLUP); pinMode(LEFTA, INPUT_PULLUP); pinMode(RIGHTA, INPUT_PULLUP);  
   pinMode(A0, INPUT); 
   pinMode(UPB, INPUT_PULLUP); pinMode(DOWNB, INPUT_PULLUP); pinMode(LEFTB, INPUT_PULLUP); pinMode(RIGHTB, INPUT_PULLUP);  
   pinMode(A1, INPUT);    
    
   Serial.begin(9600); 
    
 } 

  void loop() { 
     
    // Joystickbewegungen abfragen und senden 
    // Da das Interface einen Invert-Schmitt Trigger verpasst bekommen hat, werden die Ausgänge auf active LOW abgefragt - wenn nix passiert sind alles Eingänge HIGH 
     
    if (digitalRead(UPA)==LOW) {out=120; Serial.write(keyW, 8);}   
      
    if (digitalRead(DOWNA)==LOW) {out=121; Serial.write(keyS, 8);}   
      
    if (digitalRead(LEFTA)==LOW) {out=122; Serial.write(keyA, 8);}  
      
    if (digitalRead(RIGHTA)==LOW) {out=123; Serial.write(keyD, 8);}  
     
    if (digitalRead(A0)==LOW) {out=124; Serial.write(keySPACE, 8);}  
      
     
    if (digitalRead(UPB)==LOW) {out=130; Serial.write(keyUP, 8);}  
      
    if (digitalRead(DOWNB)==LOW) {out=131; Serial.write(keyDOWN, 8);}  
       
    if (digitalRead(LEFTB)==LOW) {out=132; Serial.write(keyLEFT, 8);}  
       
    if (digitalRead(RIGHTB)==LOW) {out=133; Serial.write(keyRIGHT, 8);}  
       
    if (digitalRead(A1)==LOW) {out=134; Serial.write(keyENTER, 8);}  
      
  delay(20);   
  //Serial.println(out); //des is nur fürs debuggen im serial monitor 
  Serial.write(keyNone, 8); // den Tastendruck beenden 

 delay(20); 
}
IMAG1858
Lochrasterplatine am Arduino

Als nächstes habe ich ein Interfaceboard (eine einfache Lochrasterplatine) mit zwei 9-poligen SUB-D-Buchsen für den Anschluss der Joysticks gebastelt. Da der Arduino genügend Ports besitzt, habe ich auf ein Multiplexen oder Matrixverschalten der insgesamt zehn Steuerleitungen verzichtet und jeden Joystickkontakt direkt auf einen Port gelegt. Das funktioniert schon einmal ganz gut. Aber das Prellen der Federkontakte und Microtaster in den Joysticks muss natürlich softwaremäßig entfernt werden. Um jetzt nicht in jeder Softwareversion einen debounce-code mitzuführen entschloss ich mich das Entprellen in der Hardware zu machen. Einfach ein RC-Glied (10k – 1uF) an einen Schmitt-Trigger und gut ist´s. Also schnell ein Board gelayoutet (das geht schneller als am Lochrasterprint zu löten), geätzt und bestückt… und heraus kam das Board hier:

IMAG1870
Board geätzt und gebohrt
IMAG1871
fertig bestückt am Arduino

Der Funktionstest verlief positiv. Die Bewegungen des Sticks werden sauber umgesetzt. Ein schneller Test mit WinVice ließ sofort das Zockergefühl von vor 30 Jahren aufkommen 😉