Montag, 4. September 2017

Alte Projekte: 8x8 LED-Cube

Hallo,

nach über zwei Monaten komme ich nun wieder einmal dazu, einen Eintrag zu verfassen. Aktuell arbeite ich an einem Forschungsprojekt für die Uni, welches ich hier jedoch (noch) nicht veröffentlichen möchte. Aus diesem Grund fehlt mir aktuell die Zeit, um an eigenen Projekten zu arbeiten.
Deswegen werde ich gelegentlich ein paar Worte über bereits durchgeführte Projekte verfassen. Den Anfang macht mein 8*8*8 LED-Cube, welchen ich 2015 gebaut habe.

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.


Sonntag, 28. Mai 2017

Plexiclock - Fertigstellung Teil 1

Hallo,
mittlerweile sind drei Wochen seit meinem letzten Eintrag zur Plexiclock vergangen. Seit dem hat sich viel getan, die Uhr ist nahezu fertig.

Zunächst hatte ich mich die Verkabelung der verbleibenden drei Segmente gemacht, über welche ich in meinem letzten Eintrag berichtet hatte. Daran angeschlossen hat sich die Verbindung der Low-Side Arrays (R,G,B). Dabei habe ich auch gleich noch die mittlere LED für den Doppelpunkt eingearbeitet.


Die High-Side der LED ist dabei mit an einem der mittleren Segmente angeschlossen. Diese benötigen ansonsten nur sieben der acht Anschlüsse.

Als nächstes ging es an die Verkabelung der Schieberegister/Treiber-Platinen. Diese besitzen jeweils sieben Anschlusspins: Masse, Versorgungsspannung, Output-Enable, Data-In, Data-Out, Register-Clock & Storage-Clock. Die meisten davon sind einfach parallel von Platine zu Platine verbunden. Eine Ausnahme davon stellen Data-In/-Out dar, da darüber die Schieberegister verbunden sind. Daher ist jeweils Data-In einer Platine mit Data-Out der vorhergehenden Platine verbunden. Außerdem besitzt jede Platine eine eigene Ader für die Versorgungsspannung. Dies ist darin begründet, dass dort ein relativ hoher Strom fließt, welcher über die Treiber-ICs die LEDs antreibt. Der Strom fließt anschließend über die R,G&B Low-Side der LEDs über jeweils einen Widerstand ab. Die Verkabelung einer einzelnen Platine ist in folgendem Bild dargestellt.


Die Leitungen enden auf einer Seite der Uhr an der Steuerplatine. Diese habe ich natürlich auch erst entworfen und zusammengelötet. Als Intelligenz der Uhr ist ein ESP8266 eingebaut. Zu diesem komme ich später noch einmal. Er übergibt jedenfalls über zwei Signalleitungen Steuerdaten an einen AtTiny85, welcher die PWM-Signale für die Farbkanäle erzeugt. Diese werden anschließend über drei n-Kanal Mosfets verstärkt. Die Ausgänge der Mosfets erzeugen direkt die Spannung, welche über die Widerstände an die LEDs geht. Neben der Farbsteuerung befinden sich auf der Steuerplatine Elemente zur Bedienung, Spannungsstabilisierung, etc. Der Schaltplan und das finale Aussehen sind unterhalb abgebildet.



Da die Steuerplatine den letzten elektrischen Part darstellt, konnte mit ihrer Fertigstellung die Programmierung beginnen. Zunächst habe ich den AtTiny85 programmiert, welcher die Farben steuert. Als Programmiersprache habe ich AVR-C innerhalb von Atmel Studio verwendet. Wer sich für den Code interessiert, kann ihn sich durch drücken des Knopfes unten ansehen. Das Programm nimmt über zwei Eingänge (Data, Clock) Befehle entgegen und übernimmt die empfangenen Daten nach Beendigung der Übertragung in die integrierten PWM-Controller. Die Übertragung findet über eine eigens erdachte Schnittstelle statt, welche aber einfach zu verstehen ist. Für die Übertragung eines Bits wird zunächst Clock auf high gesetzt. Data muss nun einen Sprung von Low auf High (Bit=1) oder High auf Low (Bit=0) durchführen. Zum Abschluss muss nun Clock wieder auf Low gezogen werden. Der Vergleich von Data bei den beiden Änderungen von Clock ergibt also den Wert des Bits. Was mit Data passiert, während Clock auf Low ist, wird nicht beachtet. Dadurch können die benötigten Ausgangszustände auf Data erreicht werden. Findet in einem Clock-Intervall keine Änderung von Data statt, so wird die Übertragung als beendet angenommen.



/*
 * RGBController.c
 *
 * Created: 09.05.2017 11:28:11
 *  Author: Markus

 * PINS:
 * 1-> Reset
 * 2-> Data Connection PB3 / PCINT3
 * 3-> PB4, Color 3
 * 4-> GND
 * 5-> PB0, Color 1
 * 6-> PB1, Color 2
 * 7-> Data Connection PB2 / INT0 / PCINT2
 * 8-> VCC
 */

#ifndef F_CPU
#define F_CPU 1000000UL     /* Quarz mit 1.000.000 hz */
#endif

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <util/delay.h>

void SetValue(int Colour, char Value);

//DutyRegisters
volatile uint8_t* Port[] = {&OCR0A, &OCR0B, &OCR1B};

long int values=0;

//used in both regular programm and interrupts
volatile char SomethingHappened=0;

int main(void)
{
    //Set Output
    DDRB = 0x13;    //B0,B1,B4

    PRR|= _BV(PRADC)||_BV(PRUSI); //Disable ADC and USI (not needed, less power)

    // Timer 0, A side
    TCCR0A = _BV (WGM00) | _BV (WGM01) | _BV (COM0A1); // fast PWM, clear OC0A on compare
    TCCR0B = _BV (CS00);           // fast PWM, top at 0xFF, no prescaler
   
    // Timer 0, B side
    TCCR0A |= _BV (COM0B1);        // clear OC0B on compare
   
    // Timer 1
    TCCR1 = _BV (CS10);           // no prescaler
    GTCCR = _BV (COM1B1) | _BV (PWM1B);  //  clear OC1B on compare
    OCR1C = 255;                  // count to 255

    SetValue(0,128);
    SetValue(1,128);
    SetValue(2,128);
   
    MCUCR |= _BV(ISC00);    //Any logical change on INT0 generates an interrupt
    GIMSK |= _BV(INT0);   

    sei();                    //Enable interrupts

    set_sleep_mode(SLEEP_MODE_IDLE);
    while(1)
    {
        sleep_mode();        // in den Schlafmodus wechseln
        SomethingHappened=1;
        while(SomethingHappened){
            //while something happens, stay active
            _delay_ms(100);
            SomethingHappened=0;
        }
    }
}

void SetValue(int Colour, char Value){
    *Port[Colour] = Value;
}


char StartValue=0;
ISR(INT0_vect){
    SomethingHappened=1;
    char EdgeType = (PINB&_BV(PINB2))== _BV(PINB2);
    char CurrentValue = (PINB&_BV(PINB3))== _BV(PINB3);
    if(EdgeType){
        //Rising Edge
        StartValue = CurrentValue;
    }else{
        //Falling Edge
        if(CurrentValue==StartValue){
            //End of Data --> Apply values
            //B-Value was sent last, is most right
            char applyValue = values&255;
            SetValue(2,applyValue);
            applyValue = (values>>8)&255;
            SetValue(1,applyValue);
            //R-Value was sent first, is most left
            applyValue = (values>>16)&255;
            SetValue(0,applyValue);
            //Reset received values    (not needed?)
            values = 0;
        }else{
            //Bit received --> Add to existing values
            values=values<<1;
            if(CurrentValue){
                values|=1;
            }
        }
    }
}

Nun zur Programmierung des ESP8266. Ich möchte auf diese nicht so detailliert eingehen, wie auf die des Attiny, da hier eine ganze Reihe an Aufgaben durchgeführt wird. Diese möchte ich jedoch zumindest anführen:
  • Aufbau und Aufrechterhaltung der WLAN-Verbindung
  • Kommunikation mit dem lokalen Homeautomation-System
  • Abrufen der Uhrzeit über externe NTP-Server (alle 15 Minuten)
  • Mitzählen der Uhrzeit und ständiges Aktualisieren der Anzeige
  • Kommunikation mit dem RGB-Controller
  • Beachtung von Eingabe über die integrierten Taster
Nach, beziehungsweise während der Programmierung habe ich natürlich auch schon Anzeigetests durchgeführt. Ich muss zugeben, dass ich währenddessen nicht sonderlich zufrieden mit dem angezeitden Resultat war. Die folgenden Bilder zeigen, warum...



Weder die 12:24, noch die 12:32 sind intuitiv ablesbar. Auch im Dunkeln wurde das Ganze nicht sonderlich besser...



Die obere (rote) Anzeige sollte hier 09:26 und die untere 88:88 darstellen. Auch wenn 88:88 keine sinnvolle Uhrzeit ist, so zeigt es zumindest, welche Elemente gut und welche schlecht bis garnicht funktionieren.

In ein paar Tagen geht es [hier] zu Teil 2...

Samstag, 13. Mai 2017

IoT-System - Logikstrukturen

Hallo,

heute möchte ich einen Einblick in die Logikverarbeitung meines IoT-Systems geben. Grund dafür ist, dass ich gestern einige Änderungen durchgeführt habe, auf welche ich weiter unten eingehen werde.

Wie ich bereits in einem vorherigen Post beschrieben habe, besteht die Logikverarbeitung aus Logikvorlagen "Pattern" und Logikinstanzen "Instances". Die eigentliche Logik ist innerhalb der Vorlage beschrieben. Eine Vorlage besitzt drei prinzipielle Komponenten: Eingänge, Ausgänge und Logikelemente. Die Logikelemente wiederum besitzen abhängig vom Typ des Elements wieder Ein- und Ausgänge.


Der Screenshot aus meiner Editor-Software zeigt eine Logikvorlage für einen einfachen Lichtschalter. Links sind die Ausgänge, rechts die Ausgänge und dazwischen die Logikelemente angeordnet. Wie zu erkennen ist, haben sämtliche Ein- und Ausgänge eine Farbe. Diese gibt deren Typ an. Grün stellt beispielsweise den Typ "Boolean" dar. Wird in den Eingang einer Logikstruktur ein Wert gegeben, so werden alle daran angekoppelte Elemente neu berechnet. Ändert sich dadurch ihr Ausgang, so werden daran angekoppelte Elemente ebenfalls neu berechnet. Dies wird fortgeführt, bis keine Änderungen mehr auftreten. Kommt es dazu, dass einer der Ausgänge auf der rechten Seite erreicht wird, so wird dessen Wert ebenfalls aktualisiert.

Nun stellt sich die Frage, was an die Ein- und Ausgänge der Struktur gekoppelt werden kann. Dafür sind die Logikinstanzen verantwortlich. Die Instanz erstellt eine Kopie der Struktur und verbindet deren Ein- und Ausgänge jeweils mit hinterlegten Ein- und Ausgängen von realen Geräten.


Das Bild zeigt die Instanzverwaltung einer Instanz, welche die oben gezeigte Vorlage verwendet. Im unteren Bereich ist es möglich, die Verbindungen von Ein- und Ausgängen festzulegen.

Nun bestehen Logikvorlagen nicht nur aus einem Logikelement, sondern aus vielen. Folgende Struktur verwende ich beispielsweise für eine RGB-LED-Leise unter meinem Bett, welche den Boden beleuchtet. Nachts soll sie bei Bewegung weiß leuchten und ansonsten optional ein Farbspiel durchführen. (Zum Vergrößern anklicken)


Ich habe die Struktur zur Erklärung in Bereiche mit Nummern unterteilt. Die Bereiche haben folgende Funktionen:
  1. Die Bewegungserkennung. Wird eine Bewegung erkannt, so wird der Ausgang eingeschalten. Ist die Bewegung beendet, so wird der Ausgang nach 15 Sekunden wieder abgeschalten.
  2. Dieser Bereich stellt fest, ob es aktuell zwischen 18 Uhr Abends und 7 Uhr Morgens ist. Dadurch wird die Bodenbeleuchtung bei Bewegung lediglich Nachts eingeschalten.
  3. Diese beiden Elemente stellen fest, ob das Farbspiel stattfinden soll. Praktisch könnte man die beiden Elemente weglassen und den Eingang direkt mit Bereich 5 verbinden. Ich habe sie denoch eingesetzt, da ich so weitere Bedingungen einfügen kann.
  4. Dieser große Bereich erzeugt alle 15 Sekunden eine zufällige Farbe. Die Multiplikationen am Ende sind notwendig, da das"HSV to RGB"-Element Werte von Null bis Eins erzeugt, die Ausgänge aber einen Wertebereich von Null bis 255 besitzen.
  5. Hier wird ermittelt, welcher Modus aktuell stattfinden soll. Als Modi stehen Aus, Farbwechsel und Ein zur Verfügung.
  6. Dieses Element legt fest, welche Farbe angezeigt werden soll, wenn weder Farbspiel noch Nachtbeleuchtung aktiv sind.
  7. Diese beiden Konstanten legen die Farbwechselzeiten fest. Das Nachtlicht soll innerhalb einer halben Sekunde (500ms) an und aus gehen. Das Lichtspiel dagegen darf fünf Sekunden (5000ms) beanspruchen.
  8. Hier wird die Farbe der Nachtbeleuchtung festgelegt. Die Werte habe ich über die Zeit immer wieder angepasst, um ein Licht zu erzeugen, welches im Dunkeln ausreichend hell ist, aber auch nicht blendet.
  9. In diesem Bereich wird mit Hilfe der Bedingungen aus Bereich fünf die Lichtwerte ausgewählt.
  10. Im letzten Bereich werden alle Lichtwerte (Rot, Grün, Blau, Wechselzeit) zu einem Bytearray zusammengeführt. Dies ist notwendig, da das verbundene Gerät somit alle Werte gleichzeitig erhält.
Nun komme ich darauf zurück, warum ich diesen Eintrag schreibe. Bisher hatte jedes Element für jeden Ein- und Ausgang einen eindeutigen Datentyp. Dies war einfach zur programmieren, hatte jedoch den Nachteil, dass für jeden Datentyp eigene Elemente nötig waren. Beispielsweise können Ganzzahlen und Gleitkommazahlen beide addiert werden, jede Operation benötigte dafür ein eigenes Element.


Die Änderung sieht nun vor, dass Ein- und Ausgänge von Elementen variabel funktionieren. Sie passen sich daher an den jeweils angeschlossenen Typ an. Dabei ist natürlich definiert, welche Datentypen angeschlossen werden können, beispielsweise kann eine Addition nicht mit boole'schen Daten umgehen.


Die Vorteile dieser Änderung liegen auf der Hand: Es werden weniger eigenständige Elemente benötigt, was die Auswahlpalette drastisch reduziert. Außerdem muss jedes Element einzeln programmiert werden. Soll ein Element mit jedem Datentyp umgehen können, so waren bisher sieben Elemente notwendig, welche alle programmiert werden mussten. Nun ist nurnoch die Programmierung eines Elementes notwendig, welche aber dafür ein wenig komplexer ist. Trotzdem ist der Gesamtaufwand wesentlich geringer als sieben einzelne Elemente zu programmieren.

Damit verabschiede ich mich für diesen Eintrag und wünsche ein schönes restliches Wochenende!

Sonntag, 7. Mai 2017

Plexiclock - Drähte über Drähte

Hallo!

Seit meinem letzten Eintrag sind 3 Wochen vergangen, in welchen ich natürlich auch weiter an der Plexiclock gearbeitet habe. Die meiste Zeit habe ich dafür benötigt, die 56 LEDs mit ihren 224 Drähten zu verdrahten.


In der Detailansicht sieht man, dass ich die jeweils zusammengehörigen LEDs auf der High-Side bereits verbunden habe.


Für ein Segment habe ich diese Drähte außerdem bereits  an ein Flachbandkabel mit Stiftleiste angeschlossen.


Diese Stiftleiste wird später an eine Platine angeschlossen, welche sich auf der Unterseite des Plexiglases befinden wird. Für jede Ziffer ist eine Platine vorgesehen. Die Platinen besitzen jeweils einen Schieberegister- und einen Treiber IC. Das Schieberegister IC vom Typ 74HC595N setzt serielle, also hintereinander gesendete, Binärdaten der Steuerung in parallele um. Dadurch wird nicht für jedes Segment ein eigener Anschluss am steuernden Mikroprozessor benötigt. Da die Schieberegister hintereinander geschalten werden können, sinkt die Anzahl der Anschlüsse weiter. Am Ende können unter Nutzung von 4 Datenpins 32 Pins gesteuert werden, wovon hier 4*7 + 1, also 29 genutzt werden. Da die Schieberegister allerdings nur wenige Milliampere pro Pin ausgeben können, hier aber 6 LEDs mit einem Bedarf von maximal etwa 120 Milliampere pro Pin angeschlossen werden sollen, ist jedem Schieberegister ein Treiber IC vom Typ UDN2981AT nachgeschalten. Diese erhöhen den möglichen Strom exakt auf die benötigten 120 Milliampere.


Neben dem elektrischen Anschluss habe ich mich darum beschäftigt, weitere Papierstreifen für die Trennung der Segmentscheiben herzustellen.


Diese habe ich auch sogleich eingebaut und dabei die inneren Schutzfolien der Segmentblöcke entfernt.


Die nun folgenden Schritte werden sein, die verbleibenden drei Segmente mit Kabeln zu versehen und die Low-Side (R,G,B) zu verbinden. Außerdem benötigt der sich später in der Mitte befindende Doppelpunkt noch eine LED mit Widerständen.
Daran wird sich der Bau der Steuerplatine und die Programmierung des Mikroprozessors anschließen.

Bis dahin, Schönen Sonntag!

Samstag, 15. April 2017

Plexiclock - Vorbereitung Bodenplatte und LED-Verkabelung

Grüße!

In der letzten Woche habe ich mich mit dem Aufbau der Trägerplatte beschäftigt. Diese wird die LEDs tragen und benötigt daher Durchführungen für die Verdrahtung. Da pro Ziffer sieben Segmente existieren und ich für jedes Segment zwei LEDs vorgesehen habe, ergibt sich bei vier Ziffern und vier Pins pro LED eine Gesamtanzahl an Bohrlöchern von 224.


Bei der vorhandenen Segmentstärke von 3mm und zwei Löchern, welche nebeneinander müssen, habe ich einen Bohrdurchmesser von 0.5mm gewählt. Somit verbleiben pro Segment 2mm an Material. Das Bohren der 224 Löcher dauerte mehrere Stunden, zumal die Platzierung vorher angezeichnet werden musste. Dabei sind auf Grund des geringen Durchmessers sechs Bohrer gebrochen.

Im Anschluss habe ich mit einer LED den Lichteffekt getestet, was in folgendem Bild zu sehen ist.


Dabei ist mir die geringe Lichtstärke und die starke Streuung auf umliegende Segmente aufgefallen, weswegen ich Papierstreifen angefertigt habe, welche die einzelnen Segmente trennen. Dadurch ist zumindest die Streuung verschwunden, die Lichtstärke ist nur etwas besser geworden. Allerdings werden ja später auch zwei LEDs ein Segment beleuchten.


Darauf habe ich weitere LEDs bedrahtet und an den Trägerplatten befestigt. Das unten stehende Bild zeigt auf der linken Seite die beiden Basisplatten von der Unterseite. Gut zu erkennen sind hier die Bohrlöcher. Die Obere Doppelreihe ist bereits mit LEDs bestückt, deren Drähte durch die Löcher geführt sind. Die rechte Seite zeigt die Oberansicht durch die Segmentscheiben.


Im Anschluss habe ich ersten Widerstandsarrays gebaut. Davon werden pro Ziffer sechs Stück benötigt. Drei werden jeweils zu einem Stapel zusammengefasst. Dafür habe ich Heißkleber verwendet, welche die Stapel auch an der Trägerplatte befestigt. Die Drähte der LEDs werden durch die Löcher der Trägerplatte geführt. Die jeweils drei Lowside-Anschlüsse werden an den Widerstandsstapeln angelötet. Jede Ebene der Stapel ist dabei jeweils für eine Farbe (Rot, Grün, Blau) zuständig. Die Highside-Anschlüsse steuern dagegen die Wahl der Segmente.



Als Abschluss habe ich noch ein paar Test mit dem angeschlossenen Segment durchgeführt. Das Segment stand hierbei auf dem Kopf, sodass sich die LEDs im oberen rechten Bereich befinden. Der Schimmer um die LEDs ist hier deswegen sichtbar, weil die Ziffer noch vorn und hinten mit einer Schutzfolie versehen ist.


In den nächsten Tagen werde ich die Verdrahtung der verbleibenden LEDs und deren Anschluss angehen. Dafür wird wohl einiges an Zeit draufgehen. Außerdem werde ich mich um die Highside-Logik kümmern, welche ich auch schon angefangen habe.

Darum wird es wohl dann im nächsten Post gehen.
Bis dahin!

Samstag, 8. April 2017

Plexiclock - Arbeit, Arbeit!

Hallo,

seit meinem letzten Post habe ich weiter an der Plexiclock gearbeitet. Zunächst habe ich die zuletzt bearbeiteten Plexigläser zu einem Segmentmodul zusammengefügt und über Bohrungen verbunden. Die Bohrungen der ersten Platte habe ich angesenkt, um Senkkopfschrauben verwenden zu können. Diese verbinden die Platten und halten am Ende noch Aluminiumwinkel, welche die Verbindung zur späteren Grundplatte herstellen werden.


Die Winkel haben Abmesseungen von 10*10mm² pro Seite. Da man solche Winkel nicht fertig kaufen kann - oder ich einfach keine gefunden habe - musste ich mir sie aus einem Aluminiumprofil aus dem Baumarkt selbst herstellen. Das Profil habe ich in entsprechende Stück gesägt und die Kanten abgeschliffen. Zudem habe ich eine Seite bereits jeweils mit einem Loch versehen.



Das erste Segment war somit fertig. In der Frontansicht ist mit viel Licht schon die Ziffer, trotz Schutzfolien, erkennbar. Im Anschluss an das erste Segmentmodul habe ich weitere drei nach dem gleichen Schema gebaut, was ein ganze Stück an Zeit verschlungen hat.


Zusätzlich zu den Segmenten habe ich mit einer Grundplatte begonnen. Diese besteht aus zwei Plexiglasstreifen von 30*500*3mm. Diese habe ich ebenfalls von Plattenzuschnitt24 bezogen, welche diese vergleichsweise günstig als Fertigteil anbieten. Eine der beiden Platten habe ich mit rechteckigen Aussparungen versehen, in welche die Segmente gesteckt werden. Die Aussparungen haben daher Abmessungen entsprechend der Segmentmodule. Die andere Platte wird sich später unterhalb der ersten Platte befinden und als Träger für die LEDs dienen.



Mit der genannten Trägerplatte werde ich mich in den nächsten Tagen beschäftigen.

Bis dahin war es das. :)