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!