Dienstag, 20. Juni 2017

Mikroprozessorprogrammierung mittels ISP

Hallo,
auf Grund bestehender Nachfrage seitens eines Freundes möchte ich heute zeigen, wie man Atmel-Mikroprozessoren (Atmega, Attiny, etc.) mittels ISP programmieren kann.

Einführung

Zunächst einmal, was bedeutet "über ISP programmieren"? Es geht hier nicht um die Erstellung des Programmcodes, sondern wie man den fertigen Code auf den Mikroprozessor (µC) hochlädt (Upload).
Beginnen wir mit einem Beispiel am Arduino UNO. Die meisten Nutzer werden zum Upload den UNO über USB anstecken und in der Arduino IDE auf "Hochladen" drücken.
Was passiert dabei? Nach dem Compilieren des Codes wird versucht, sich über USB zum Arduino zu verbinden. Die Verbindung wird jedoch nicht zum eigentlichen µC (Atmega 328p) aufgebaut, sondern zu einem sich ebenfalls auf der Platine befindenden Atmega 8U2 oder Atmega16U2. Dieser dient als USB-Interface zwischen Computer und µC. Der 8/16U2 muss nun den vom Computer eingehenden Code zum 328p übertragen. Dafür besitzt der 328p einen Bootloader, welcher als aller erstes nach dem Start ausgeführt wird. Der Bootloader schaut, ob der 8/16U2 ein Programm einspielen möchte und startet ansonsten das eigentliche Programm. Stellt der Bootloader fest, dass der 8/16U2 neuen Code übertragen möchte, so wird das neue Programm aufgespielt.
Der Bootloader sorgt also dafür, dass einfach neuer Code eingespielt werden kann, verursacht damit aber auch gleich zwei Probleme. Ersterns verzögert sich der Programmstart um die Zeit, welche der Bootloader benötigt. Zweitens benötigt der Bootloader ebenfalls Speicherplatz, welcher entsprechend nicht für das Programm zur Verfügung steht.


Nun zu einem anderen Anwendungsfall. Möchte man sein Projekt weiterentwickeln, so ist es oft notwendig Platz einzusparen. Das Board des Arduino ist dabei störend. Den µC 328p ist dafür auf vielen Boards als DIP-28 Package ausgeführt. Man kann den Chip also vom Board entfernen und mit wenigen Drähten oder Leiterbahnen einzeln verwenden. Dabei verliert man allerdings die Möglichkeit Programme über USB zu übertragen. Man kann natürlich den Chip jedes mal erneut auf den Arduino stecken und so programmieren. Muss man aber nicht.

Easy-Mode: Arduino

Für alle genannten Probleme ist der Upload über ISP eine Lösung. "ISP" steht für "In-System-Programming". Es wird dazu genutzt, um µC direkt im Einsatzsystem zu programmieren. Der erforderliche Hardwareaufwand im Zielsystem ist entsprechend niedrig. Schauen wir uns einmal die ISP-Programmierung beim Arduino UNO an. Dort befindet sich am Ende der Platine ein 6 poliger "ICSP"-Header. "ICSP" bedeuted "In-Circuit Serial Programming" und ist praktisch das gleiche wie ISP.  Zur Programmierung über diese Schnittstelle benötigt man als aller erstes einen ISP-Programmer. Diese erhält man von allen möglichen Händlern, die auch Arduino-Zubehör anbieten. Ich selbst nutze einen "USB-TinyISP".


Dieser macht praktisch nichts anderes, als die USB-Signale zu verarbeiten und den ISP-Input des zu programmierenden µC anzusteuern. Dazu benötigt man allerdings Treiber für den USB-TinyISP, welche beispielsweise [hier] zu finden sind. Nach der Installation kann man im Arduino-Menü "Werkzeuge" den Programmer auswählen.


Als nächstes kann der Arduino an den Programmer angeschlossen werden. Da der Header nicht verdrehsicher ist, muss man auf die Ausrichtung des Steckers achten. Im Normalfall sollte die Nase am Stecker in Richtung des Boards zeigen. Wird der Stecker verkehrt herum eingesteckt, besteht die Gefahr, den Chip zu zerstören. Im Allgemeinen hält der Chip jedoch stand. Nun kann der Code hochgeladen werden. Im Unterschied zum USB-Upload muss man aber nun "Hochladen mit Programmer" im Menü auswählen.


Der Code wird nun compiliert und hochgeladen. Man beachte jedoch, dass nach einem Upload über einen Programmer der Bootloader überschrieben wird. Dies kann im Menü "Werkzeuge" unter "Bootloader brennen" rückgängig gemacht werden, jedoch ist dafür zwingend ein ISP-Programmer notwendig.

Hard-Mode: AtTiny25/45/85

Nun gibt es Mikroprozessoren, welche über kein Breakout-Board wie den Arduino verfügen. Bei diesen besteht die USB-Upload Möglichkeit also garnicht. Man muss sich also entweder ein Steckbrett vorbereiten, oder selbst ein Breakout-Board zurecht machen. Zweiteres ist zu empfehlen, wenn vorherzusehen ist, dass man diesen Chiptyp öfters bespielen wird. Bei einmaligen Uploads oder oft wechselndem Chiptyp empfiehlt sich entsprechend ein Steckbrett. Die Vorgehensweise ist für beides die gleiche, ich gehe hier direkt auf die Variante mit Breakout-Board ein.

Zunächst benötigt man zwei Dinge. Erstens, das Pinlayout des ISP-Headers und zweitens das Datenblatt des Mikroprozessors. Das Pinlayout findet man über eine einfache Websuche nach "ISP-Header". Das Datenblatt zum µC wird vom Hersteller zur Verfügung gestellt. [Hier] beispielsweise das Datenblatt zum AtTiny25/45/85. Diese drei µC sind prinzipiell gleich, unterscheiden sich jedoch in der Speichergröße. Direkt auf Seite Zwei findet sich der Abschnitt "Pin Configurations". Dieser gibt an, welchem Pin welche Funktionen zugeordnet werden können. Für uns wichtig sind die Funktionen "RST", "MISO", "MOSI" und "SCK", sowie natürlich Vcc und GND.


Die genannten Pins erfüllen folgende Funktionen
  • "MISO" > "Master-in Slave-out" > Datenübertragung Programmer zu µC
  • "MOSI" > "Master-out Slave-in" > Datenübertragung µC zu Programmer
  • "SCK" > "Serial Clock" > Taktsignal zur Datenübertragung
  • "RST" > Reset  > Bewirkt ein Rücksetzen des µC
  • "GND" > Ground/Masse
  • Vcc > Versorgungsspannung
Die Pins der µC müssen nun mit den ensprechenden Pins am Header verbunden werden. Ein ISP-Programmierboard für den AtTiny25/45/85 sieht dann beispielsweise so aus.


Auf der Oberseite habe ich am Sockel für den AtTiny eine Kerbe markiert. Diese zeigt, wie gedreht der µC eingesteckt werden muss. Der AtTiny hat auf diese Seite entweder eine ebensolche Kerbe, oder einen Punkt ins Gehäuse auf der Oberseite eingedrückt.
Auf der Unterseite habe ich mit Rot die Pins markiert und mit grün die Drahtbrücken. Man beachte, dass die Nase des ISP-Header hier nach außen zeigen sollte, wie auf der Unterseite rot markiert ist.

Entsprechend meiner Vorlage kann nun auch jeder anderer, per ISP programmierbare µC programmiert werden. Ich möchte noch folgende Hinweise geben:
  • Die zum USB-TinyISP mitgelieferten Kabel sind manchmal falsch gebaut. Diese Kabel sollten eine 1:1 Zuordnung der beiden Stecker haben. Meines war jedoch um 180° gedreht
  • Eine Integration des ISP-Headers in die fertige Schaltung ist möglich, es ist jedoch zu bedenken, dass der zur Verfügung stehende Strom, welcher per USB bereit gestellt wird begrenzt ist und dass der USB-TinyISP auch Signale auf die Anschlüsse legt, welcher die Schaltung beeinflussen können
  • Zum Programmieren von beispielsweise AtTinys per ArduinoIDE existiert ein Board-Paket, siehe dazu auch [hier]
  • Es gibt auch 10-polige ISP-Header, diese habe ich aber praktisch noch nirgendwo gesehen

Samstag, 17. Juni 2017

Plexiclock - Fertigstellung Teil 2

Hallo,
gestern habe ich es endlich geschafft, die Plexiclock an die Wand zu bringen und das Projekt damit abzuschließen. Der erste des Blogeintrags zur Fertigstellung findet sich [hier]. Diesen habe ich damit beendet, dass die Uhr funktioniert, die Zahlen jedoch schlecht ablesbar sind.

Eine der U(h)rsachen dafür liegt darin, dass alle Segmentescheiben jeweils eine weitere Scheibe vor und nach sich haben, bis auf die vorderste und hinterste. Das Licht kann somit nicht reflektiert werden und das Segment erscheint dunkler. Um das Problem zumindest für die hintere Scheibe zu lösen, habe ich die Segmentmodule von hinten mit Papier beklebt. Dadurch kann das Licht nicht nach hinten verschwinden. Als Resultat davon ist die Uhr zumindest erst einmal ablesbar.


Eine weitere Fehlerquelle beruht auf meiner eigenen Unaufmerksamkeit. Die Helligkeit von LEDs steigt mit mit dem anliegenden PWM-Tastverhältnis. Diese Steigerung ist jedoch nicht linear, sondern exponentiell. Das heist, dass für eine Verdoppplung der Helligkeit das Tastverhältnis vervierfacht werden muss. Um einen linearen Farbverlauf zu gewährleisten, wird der eingegebene lineare Farbwert daher über eine Lookup-Table in ein nichtlinearen Wert für das Tastverhältnis übertragen. Da ich den Code dafür aus einem anderen Projekt übernommen habe, in welchem ein ESP8266 direkt das PWM steuert, war der Wert für 10Bit ausgelegt. Hier dient jedoch der AtTiny85 als PWM-Controller. Dieser unterstützt jedoch nur 8Bit-PWM. Alles in allem hat dies dafür gesorgt, dass die Helligkeit nur ein Viertel der Maximalhelligkeit betragen hat. Nach dem Beheben dieses Fehlers ist die Anzeige wesentlich besser lesbar.


Als nächster Schritt stand die Programmierung der Zeitsynchronisation an. Dazu verbindet sich die Uhr immer kurz nach Um, Viertel, Halb und Dreiviertel mit einem NTP-Zeitserver. Anhand dessen Antwort wird die interne Zeit neu gesetzt. Zwischen den Synchronisationen wird über einen 1-Sekunden-Timer die Zeit mitgezählt.

Da die Uhr nun ersteinmal grundlegend Funktioniert, war der nächste Schritt die Wandhalterung. Dafür habe ich zunächst aus einem Aluminiumprofil (30*30*3 [mm]) Winkel zurechtgesägt. Die Winkel habe ich mit jeweils zwei 3mm Bohrungen je Schenkel versehen. Anschließend habe ich entsprechend der Bohrungen an den Winkeln weitere Bohrungen an der Plexiglas-Bodenplatte gesetzt, an welche die Winkel letztendlich angeschraubt wurden. Auf dem oben stehenden Bild sind die Winkel links und rechts des Doppelpunktes sichtbar.
Diesen habe ich im übrigen im gleichen Zeitraum hinzugefügt. Dafür habe ich einen 3mm Plexiglasstreifen von 30*500 [mm] Größe genutzt, welchen ich damals bei der Bestellung der Scheiben gleich mitbestellt hatte. Den Streifen habe ich auf die Höhe der Segmente zersägt, wobei ich zwei Millimeter zugegeben habe. Diese habe ich genutzt, um die Endfläche abfeilen zu können. Um eine glatte Oberfläche zu erhalten, wurde die Fläche am Schluss noch mit dem Cuttermesser abgerieben. Das Resultat ist ausreichend gut. Der Laserschnitt der anderen Kanten ist natürlich besser, aber die Qualität reicht, zumal die Fläche kaum sichtbar sein wird.
Die Gravur der Scheibe habe ich wieder selbst vorgenommen, wobei ich dafür erst wieder eine Schablone entwerfen musste. Dafür habe ich die Vorlage der Segmente genutzt, um ähnliche Geometrien zu erzeugen.

Zum Abschluss habe ich mir im Baumarkt eine Grundplatte (140*500*5 [mm]) zurechtsägen lassen, welche hinter der Uhr montiert wird. Durch das Zurechtsägen im Baumarkt erhält man eine sehr hochwertige Schnittkante. Die Platte habe ich mit Löchern versehen, welche zu den Aluminiumwinkeln passen. An diesen wird die Uhr über 12 Schrauben M3*10 gehalten.



Um die Grundplatte nun noch an der Wand zu befestigen, habe ich die Grundplatte noch mit zwei 4mm Bohrungen ganz außen versehen. Durch diese werden zwei weiße Haken in die Wand gedreht, wo sie von Dübeln gehalten werden.




Das Verhalten der Uhr hinsichtlich Farbe und Helligkeit wird im übrigen von meinem IoT-System gesteuert. Normalerweise ist die Uhr aus. Wird über einen (extern virtuell angeschlossenen) PIR-Bewegungssensor eine Bewegung erkannt, so schaltet sich die Uhr ein. Tagsüber für 15 Minuten mit maximaler Helligkeit und nachts für 3 Minuten mit 30% Helligkeit. Weitere Bewegungen reseten den Timer natürlich. Die Farbe wird dazu über einen Zufallsgenerator erzeugt, welcher jede Minute eine neue Farbe generiert. Auf diese wird dann innerhalb von 1,5 Sekunden übergegangen.

Damit war es das erst einmal für diese Blogeintragsreihe, aber die nächsten Projekte kommen bestimmt!

Freitag, 16. Juni 2017

Theorycrafting - Mehrere Buttons an einem Analogeingang

Hallo,
im Rahmen eines studentischen Forschungspraktikums bin ich auf ein Problem gestoßen, welches ich hier darstellen und lösen möchte.

Das Problem besteht darin, eine Modul mit (4) Tastern mittels eines möglichst niedrigadrigem Kabel mit einem Mikroprozessor-Modul zu verbinden. Einen Lösungsansatz habe ich in der Arduino-Community gefunden. Im Forum hatte jemand das selbe Problem und wollte 6 Taster auslesen, davon aber jeweils maximal zwei gleichzeitig. Dafür hat er jeden Taster mit einem Widerstand verbunden, welcher aber für jeden Taster eine andere Größe besitzt. Durch intelligente Auslegung der Widerstände konnte er erreichen, dass beim gleichzeitigen Drücken von einem oder zwei Tastern immer eine andere Spannung am Ausgang der Schaltung anliegt. Diese wird mittels eines Analog-Digital-Converters (ADC) im Mikroprozessor (µC) ausgelesen. Daraus folgt ein digitaler Wert, an welchem sich feststellen lässt, welche Tasten gedrückt wurden.

Soweit zu dieser Lösung. Ich wollte diese nun weiterentwickeln und dazu bringen, dass die bei mir vorhandenen Taster in beliebiger Anzahl gedrückt werden können. Also 1, 2, 3 oder alle 4 Taster gleichzeitig. Die Verarbeitung des Signals soll mittels des im ESP8266 integrierten ADC durchgeführt werden. Beginnen wir also nun bei der Schaltung. Man betrachte dazu den Ausschnitt aus meinem Schaltplan.


Praktisch ist diese Schaltung nichts anderes als ein Spannungsteiler mit variablem R2. U_ges ist durch die Spannungsversorgung des ESP8266 (3,3V) vorgegeben. Ein wichtiger Punkt ist, dass der ADC des ESP8266 einen Eingangsbereich von 0..1V besitzt. Dieser Wert entspricht U1. U2 ergibt sich letztendlich aus der Differenz von U_ges und U1 und beträgt demnach 3,3...2,3V.
Somit sind alle Werte, bis auf R1 und R2 gegeben. Diese gilt es nun auszulegen.

Im ersten Schritt habe ich R1 einfach auf 1kOhm festgelegt. Daraus resultiert über die Formel des Spannungsteilers ein Wertebereich für R2 von 2,3kOhm bis 32kOhm für R2. Innerhalb dieser Werte wird sich U1 genau zwischen 0,1V und 1V befinden. Die 0,1V sind eine Einschränkung, damit auch bei keinem gedrückten Taster die dann anliegenden 0V erkannt werden können.

Nun gilt es den errechneten Wertebereich für R2 so auf die Einzelwiderstände R2n zu verteilen, dass bei jeder Kombination von Tastendrücken ein anderes U1 resultiert. Dafür habe ich zunächst eine Formel aufgestellt, welche U1 abhängig von R2, bzw. R2n liefert. Ohne jetzt groß auf die Rechnung eingehen zu wollen, ergibt sich folgende Formel.


Daraus lässt sich nun ein Excel-Arbeitsblatt erstellen, in welches man die vier Einzelwiderstände eingeben kann. Daraus werden die jeweils resultierenden Gesamtwiderstände und Ausgangsspannungen aller Taster-Kombinationen errechnet. Abschließend werden diese aufsteigend sortiert und bei der Spannung die Differenz zum nächst kleineren Wert errechnet. Zusätzlich werden alle Werte in Diagrammen dargestellt.

Durch Einsetzen und Probieren erhält man somit eine Rückmeldung, wie optimal die Eingabe ist. Als Beispiel möchte ich die letztendlich von mir gefundene Lösung präsentieren.


Zusätzlich zu Widerstand und Spannung taucht hier ein ADC-Wert auf. Dieser Wert gibt an, welcher Wert aus dem ADC ausgelesen werden kann, bzw. werden sollte. Auf den Graphen der sortierten Werte ist gut erkennbar, wie optimal die Lösung ist. Die optimalste Lösung würde eine Grade zeigen, die bei 0,1V beginnt und bei 1V aufhört. Dadurch wäre der Abstand zwischen den einzelnen Punkten möglichst konstant. Würden zwei Werte zu nah aneinander liegen, so könnte der µC nichtmehr unterscheiden, um welche Tastenkombination es sich nun handelt.

Bei der von mir gefundenen Lösung ergeben sich die Werte 4.3KOhm, 9.1kOhm, 20kOhm und 31,6kOhm. Dabei ist zu beachten, dass ich nur Werte verwendet habe, welche es so auch zu kaufen gibt.

Wer selbst einmal rechnen (lassen möchte), für den habe ich mein Excel-Dokument zum Download zur Verfügung gestellt.

Im nächsten Blogeintrag wird es dann endlich um die Fertigstellung meiner Plexiclock gehen, welche es heute endlich an die Wand geschafft hat.

Sonntag, 4. Juni 2017

IoTSystem - Patchday!

Hallo,

über Pfingsten habe ich mich entschlossen ein schon länger geplantes Feature umzusetzen. Sicher hat der eine oder andere schonmal von "OTA-Updates" gehört. "OTA" steht hier für "Over the air", es werden also Updates drahtlos übertragen. Ein Anwendungsbeispiel ist die Aktualisierung von Android-Smartphones, welche neue Software über WLAN erhalten.

Da der Großteil meiner bisher angebundenen Geräte auf ESP8266 basiert, habe ich mit diesen Geräten begonnen. Glücklicherweise beinhaltet die ESP8266-Arduino-Bibliothek direkt eine Möglichkeit zu OTA-Updates. Die zugehörige Dokumentation beschreibt dafür drei Wege.

Weg 1 - Arduino IDE

Um OTA-Updates über die Arduino IDE verteilen zu können, muss zunächst erst einmal Python installiert sein. Der Upload erfolgt dann über einen virtuellen Serialport analog zum Upload über einen USB-Programmer. Allerdings muss jedes Gerät einzeln aktualisiert werden, daher habe ich diesen Weg für mich ausgeschlossen.

Weg 2 - Webbrowser

Die Verteilung von Updates über den Webbrowser funktioniert, indem der ESP8266 als Webserver dient, welchem man über einen POST-Request die neue Software unterschiebt. Dafür muss die installierte Software allerdings erst einen extra dafür vorhandenen Server starten, zu welchem man sich verbinden kann. Aber auch hier muss jedes Gerät einzeln aktualisiert werden.

Weg 3 - HTTP-Server

Der letzte Weg für OTA-Updates beschreibt die selbstständige Aktualisierung über einen HTTP-Server. Die installierte Software muss dafür eine Anfrage an den Server stellen, welchem sie dabei Version mitteilen kann. Der Server schickt nun abhängig davon, ob eine Software mit höherer Versionsnummer bereitsteht oder nicht die neue Software, oder lehnt die Anfrage ab. Großer Vorteil dieser Variante ist, dass dem Server nur einmal eine neue Version übergeben werden muss, welche sich dann alle Geräte selbstständig abholen können.

Eben auf Grund dieses Umstandes habe ich mich für den dritten Weg entschieden. Es mussten nun also zwei Dinge getan werden. Einmal musste der HTTP-Server geschrieben werden, welche die Updates speichert und verteilt. Entsprechend dazu musste die Software der ESPs auch dazu gebracht werden, sich die Updates abzuholen.

Zur Abfrage der Updates habe ich den bei der Dokumentation enthaltenen Beispielcode verwendet, an welchen ich lediglich IP, Port und URL angepasst habe. Um meine verschiedenen Geräte unterscheiden zu können, verwende ich einen URL-Parameter. Die integrierte Softwareversion muss natürlich für jeden Build erhöht werden. Der gezeigte Codeabschnitt ist so eingebaut, dass er nach Anfrage durch den Systemserver behandelt wird.


const unsigned long int VAR_VERSION = 14;
const String VAR_OTAUPDATENAME = "RGBSTRIP";

String UpdatePath = "/espupdate?device=" + VAR_OTAUPDATENAME;
TurnOnStatusLED();
t_httpUpdate_return ret = ESPhttpUpdate.update(ServerIP, 4713, UpdatePath, String(VAR_VERSION));
  switch(ret) {
    case HTTP_UPDATE_FAILED:
      Serial.println(F("[update] Update failed."));
      break;
    case HTTP_UPDATE_NO_UPDATES:
      Serial.println(F("[update] Update no Update."));
      break;
    case HTTP_UPDATE_OK:
      Serial.println(F("[update] Update ok.")); // may not called we reboot the ESP
      break;
  }
TurnOffStatusLED();

Dies ist auf Geräteseite auch schon alles. Passend dazu habe ich meinen bereits vorhanden Http-Server erweitert. Bisher diente dieser nur als Kommunikationsschnittstelle zum User-Interface. Nun nimmt er auch die Anfragen der Geräte entgegen. Die Anfragen enthalten das anfragende Gerät, bzw. dessen Softwaretyp, in der URL und die mitgeschickte Softwareversion als Http-Parameter namens "x-ESP-version". Zusätzlich werden diverse Http-Parameter wie verwendeter, freier und vorhandener Speicher etc. mitgeschickt. Wer will kann diese auch zur Ermittlung der entsprechend zurückzusendenden Software verwenden, für meine Anwendung reichen jedoch die genannten Parameter.
Der Server sucht nun im Dateisystem nach einem entsprechend dem Gerätetyp benannten Ordner. Darin sollte sich eine "Version.txt" befinden, welche die Version der neuen Software enthält. Durch den Vergleich der Versionsnummer aus der Datei und der Versionsnummer ergibt sich, ob ein Update vorhanden ist oder eben nicht. Als letztes lädt der Server eine "Firmware.bin", welche sich ebenfalls im Ordner befinden sollte und schickt diese an das anfragende Gerät.


Tritt während des Prozesses ein Fehler auf, ist also eine Datei nicht vorhanden, fehlt ein benötigter Parameter etc., so wird ein entsprechender Statuscode  (400-BadRequest / 404-NotFound / 500-InternalServerError) zurückgeschickt, nach welchem das anfragende Gerät weiß, wie es zu verfahren hat.

Um nun ein Update zu starten, muss mit der Android-IDE (oder mit dem Plugin für Visual Studio) die Software kompiliert werden, wodurch im Ordner des Sketches eine Binärdatei mit der Endung ".bin" entsteht. Diese muss in das richtige Verzeichnis kopiert werden. Zudem muss die Versionsnummer in der "Version.txt" erhöht werden. Darauf hin kann über das aktualisierte Nutzerinterface das Update ausgelöst werden. Dieses zeigt nun auch den Gerätetyp und die aktuelle Versionsnummer an. Unterstützt das jeweilige Gerät keine OTA-Updates, dann stehen diese Möglichkeiten entsprechend auch nicht zur Verfügung.