Arduino Gewächshaussteuerung

Themabewertung:
  • 1 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
27.06.2014, 20:25

Nachdem es heute beim Holz verräumen angefangen hatte zu regnen, hatte ich dann doch noch etwas Zeit, mir Gedanken über meine Tastenabfrage zu machen. Inzwischen kann ich schon mal sagen, dass ich KEINE Pin-Interrupt verwenden werde. Stattdessen werde ich die Pins, an denen die Taster hängen, über einen Timer-Interrupt abfragen. Das erscheint mir von der Steuerung einfacher zu sein.

Im Timer-Interrupt kann ich nämlich ALLE Tasten GLEICHZEITIG abfragen und entsprechend reagieren. Außerdem gehen mir garantiert keine Tastendrücke verloren, da ich ja nicht 3 Pin-Interrupts habe, die sich ggf. gegenseitig in die Quere kommen, sondern nur einen Interrupt. Den des Timers.

Die Umsetzung habe ich noch nicht genau validiert aber ich habe inzwischen schon eine Vorstellung, wie das laufen könnte. In der Timer-Interrupt-Routine werde ich dann auch die Tasten entprellen.

Gruß, Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
28.06.2014, 19:10

So Leute! :-)

Heute habe ich mir wieder etwas Zeit für meine Steuerung nehmen können. Und den aktuellen Stand möchte ich wieder mit Euch teilen. Ich habe die Tastenabfrage, wie schon angekündigt, mit einem Timer realisiert. Dabei habe ich dann zunächst mal feststellen dürfen, dass ich aus der Arduino-IDE heraus den Timer0 des µC nicht nutzen kann, weil dieser für den Befehl "millis()" reserviert ist.

Hat ein wenig gedauert, bis ich das raus hatte. Ich bin dann auf Timer 1 umgestiegen, das funktioniert einwandfrei.

So, hier jetzt mal der Codeausschnitt, mit dem die Tasten entprellt werden:

Erstmal ein paar Variable und Definitionen:
Code:
// ===============================================
// Variable für die Tastenabfrage + Entprellung
// ===============================================
#ifndef F_CPU
#define F_CPU           8000000                   // processor clock frequency
#warning kein F_CPU definiert
#endif

#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY0            0      // Pin 14
#define KEY1            1      // Pin 15
#define KEY2            2      // Pin 16
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)

#define REPEAT_MASK     0 // (1<<KEY1 | 1<<KEY2)  // repeat: key1, key2
#define REPEAT_START    50                        // nach 500ms
#define REPEAT_NEXT     20                        // alle 200ms

volatile uint8_t key_state;                       // Tastenstatus entprellt und umgekehrt: bit = 1: key pressed
volatile uint8_t key_press;                       // Tastendruck registriert
volatile uint8_t key_rpt;                         // Taste lange gedrückt und Wiederholung

Und dann der passende Code für den Timer mit entprellen und die Routinen für die Tastenabfrage:

Code:
// ===============================================
// Interrupt-Service Routine für TIMER1
// Es werden die Pins 14,15 und 16 abgefragt
// Atmega328 Port B (0-2)
//
// Die Routine fragt den Status der Pins ab und
// gibt den Status zurück
//
// Die Tasten werden bei der Abfrage entprellt
//
// Die Basis hierzu hat Peter Dannegger entwickelt
// ===============================================
ISR( TIMER1_OVF_vect )                            // alle 10 ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;

  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // 10ms Pause

  i = key_state ^ ~KEY_PIN;                       // Tastenstatus geändert? ?
  ct0 = ~( ct0 & i );                             // reset oder zählen ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset oder zählen ct1
  i &= ct0 & ct1;                                 // Zählen bis zum Überlauf ?
  key_state ^= i;                                 // Dann den Prellstatus ändern
  key_press |= key_state & i;                     // 0->1: Tastendruck registrieren

  if( (key_state & REPEAT_MASK) == 0 )            // Wiederholfunktion prüfen
    rpt = REPEAT_START;                          // Pause beginnen
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // Pause wiederholen
    key_rpt |= key_state & REPEAT_MASK;
  }
}

// ===============================================
// Prüfen ob eine Taste gedrückt wurde. Jeder Tastendruck
// wird nur EINMAL gemeldet.
// ===============================================
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}

// ===============================================
// Prüfen ob eine Taste solange gedrückt wurde, dass
// die Tastenwiederholfunktion aktiviert wird
// Es wird sozusagen simuliert, dass der Benutzer
// die Taste mehrfach drückt und wieder loslässt.
// ===============================================
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}

// ===============================================
// Prüfen ob eine Taste gerade JETZT gedrückt wurde.
// Jeder Tastendruck wird nur EINMAL gemeldet.
// ===============================================
uint8_t get_key_state( uint8_t key_mask )
{
  key_mask &= key_state;
  return key_mask;
}

// ===============================================
// Prüfen ob eine Taste KURZ gedrückt wurde.
// ===============================================
uint8_t get_key_short( uint8_t key_mask )
{
  cli();   // Interrupt unterdrücken
  return get_key_press( ~key_state & key_mask );
}

// ===============================================
// Prüfen ob eine Taste LANG gedrückt wurde.
// ===============================================
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}


Und so wird eine Taste dann abgefragt (mal beispielhaft):

Code:
if( get_key_press( 1<<KEY0 ))
  {
    setMenuScr();
  }

Ich weiß, ich habe vieles eher im C-Style umgesetzt und die Arduino-Befehle, die es zu diesem Zweck auch gibt, nicht genutzt. Ich persönlich finde es aber besser so.

Am Ende möchte ich das Programm sogar komplett auf C und später vielleicht auf Assembler umstellen. Aber das ist die Zukunft.

Um das Ganze mal zu komplettieren, habe ich meinen aktuellen (nicht optimierten) Sketch mal angehängt:


.ino   Gewaechshausbewaesserung.ino (Größe: 29,84 KB / Downloads: 414)

Gruß, Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
28.06.2014, 20:24

Ööhm....

ich habe festgestellt, dass die Codefragmente im Text falsch dargestellt werden. Da werden einige Dinge einfach unterschlagen.

Also nicht die Codefragmente aus dem Text verwenden sondern den Teil dann lieber aus dem Sketch entnehmen.

Gruß, Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
tesacrep
Administrator
*******

Beiträge: 500
Registriert seit: Dec 2013
Bewertung: 2
28.06.2014, 22:40

Gut das du das Thema mal wieder anleierst, das erinnert mich auch daran an dem Code-higliter zu arbeiten.
Mein arduino ist leider noch immer nicht da Mad mal sehen wie sich die sache noch entwickelt. Ansosnten sieht das mit den Tastern sehr gut aus, ich denke ich werde davon einiges für meine übernehmen, sollte ich das selbe problem haben wie du mit dem entprellen.
Ich schaue das ich morgen mal mein neues Projekt mit euch teile.

Mfg, tesacrep
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
28.06.2014, 22:50

Ich könnte dir einen robo-eek von mir geben. Das teil dürfte zu deinen arduinos kompatibel sein, vermute ich.
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
29.06.2014, 19:37

Heute habe ich wieder am Programm gearbeitet.

Das Programm ist nun soweit fertig. Alle Menüs sind implementiert und funktionieren auch. Alle Voreinstellungen können gemacht werden (Einschaltintervall für jedes Ventil in Minuten, maximal 1440 Minuten = 24 Stunden, Einschaltdauer für jedes Ventil in Sekunden, maximal 120 Sekunden = 2 Minuten).

Die aktuelle Uhrzeit lässt sich natürlich auch einstellen (Stunden und Minuten, auf Sekunden habe ich verzichtet).

Die Tastenabfrage und -Entprellung funktioniert einwandfrei.

Nun möchte ich meine Voreinstellungen natürlich speichern und dazu das EEProm nutzen. Dazu habe ich EEProm.h inkludiert und versuche mit EEProm.write() und EEProm.read() die Daten zu schreiben und zu lesen. Das will aber leider so gar nicht funktionieren. Ich habe ein paar Stündchen gekämpft und jetzt erstmal aufgegeben.

Meinen Sketch hänge ich mal wieder an diesen Beitrag, vielleicht hat ja jemand eine Idee, warum es nicht klappt.

Gruß, Uni


.ino   Gewaechshausbewaesserung.ino (Größe: 36,31 KB / Downloads: 463)
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
02.07.2014, 20:06

Hallo zusammen,

kurzer Zwischenstand zur Programmierung. Ich habe meinen Sketch komplett nach C portiert und alle "Besonderheiten" von Arduino eliminiert.

Das war mal eben eine Herausforderung, weil dann nette Befehle wie digitalWrite() nicht mehr existieren. Das muss man dann selbst programmieren. Allerdings lohnt sich das auch, denn das Programm belegt jetzt knapp 3 KB weniger Speicher im µC. Das, finde ich, ist schon eine Nummer!

Aktuell ist mein C-Programm noch ungetestet, ich kann es frühestens morgen mal auf den Chip packen. Aaaaber.... Im Debugger vom AVR-Studio siehts nicht schlecht aus. Was ich dort aber nicht testen kann, ist das Timing des Programms. Es kann also durchaus sein, dass der Compiler keine Fehler meldet, mein Programm logisch korrekt ist, aber trotzdem nix geht, weil das Timing Mist ist.

Aber das werde ich dann sehen.

Sobald ich den ganzen alten Code gelöscht habe (ich habe ihn bis jetzt nur auskommentiert), und ich die einzelnen Programmteile, die ich für eine bessere Übersichtlichkeit in unterschiedliche Dateien abgelegt hatte, wieder zusammen gefügt habe, stelle ich meine Version ins Forum.

Gruß, Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
02.07.2014, 22:50

Nun hat es doch noch geklappt. Ich habe mein Proggi mal auf den Chip gepackt. Bis auf die Abfrage DHT11 und ein kleines Problem mit dem Ansteuern der Leistungsports klappt alles.

Die Leistungsports scheinen nicht zu reagieren. Da muss ich mir meine Bit-Zauberei nochmal ansehen.

Und was den DHT11 anbelangt.... Da funktioniert die Abfrage vermutlich schon, jedoch klappt die Type-Conversion von String nach Int noch nicht korrekt, da es sich bei der DHT11-Lib um eine andere als die für den Arduino genutzte handelt und die Interna dort anders sind.

Muss ich mir also auch nochmal ansehen.

So... Das muss als Update genügen :-)

Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren
tesacrep
Administrator
*******

Beiträge: 500
Registriert seit: Dec 2013
Bewertung: 2
04.07.2014, 16:18

Wow, ich hatte ein wenig um die Ohren, deshalb habe ich in den letzten tagen nichts mehr geschrieben.
Den Konvertierungsfehler habe ich selbst heute noch drin, deshalb können bei mir nur gerade Temperaturen angezeigt bzw, die ungeraden werden auf gerade gerundet werden.
Wichtig war mir erstmal die Grundfunktion. Bei mir gibt es leider kein Update, ich warte immer noch auf den Arduino, und beschäftige mich derweil mit Anderen Projekten.

Mfg, tesacrep
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 726
Registriert seit: Apr 2014
Bewertung: 5
06.07.2014, 10:58

Ich habe jetzt Urlaub, also Zeit fürs Projekt! Big Grin

Ich möchte jetzt unbedingt mal produktiv gehen mit dem Projekt. Deshalb habe ich beschlossen, den Arduino-Sketch zunächst soweit zu schleifen, dass dieser 100%ig funktioniert.

Die C-Version hat doch noch ein paar Macken. Ich muss mich intensiv mit den Timern beschäftigen, da die Tastenabfrage unter C nicht rund läuft und der Timerinterrupt ja die Tastendrücke registriert und entprellt. Ohe die Funktion kann ich keine Einstellungen machen.

Außerdem läuft die Schaltung locker 10 mal schneller unter C. Ich muss somit mit der Taktrate spielen. In der Arduino-IDE wird ja über die Auswahl des Boards auch die Taktrate eingestellt. Das ist unter C natürlich anders (Ihr erinnert Euch: Ich arbeite mit dem nackten Chip).

Der DHT11 funktioniert unter C auch noch nicht richtig. Ich kann ja die Lib von Arduino nicht nutzen. Ich habe deshalb eine andere Lib eingesetzt. Da funktioniert die Abfrage auch anders. Das werde ich mal gesondert auf dem Breadboard stecken und programmieren um das nochmal zu testen.

Ihr seht: Jede Menge Arbeit

Heute werde ich anfangen ein Gehäuse zu bauen und das Display mit Kabel an die Schaltung verbindung. Außerdem baue ich den immer noch fehlenden Spannungsregler auf die Platine, damit ich eine saubere Spannung verfügbar habe.

Fortsetzung folgt... :-)
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
Zitieren


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 2 Gast/Gäste