Monatliches Archiv: Januar 2015

FHEM: Zufällige Zeit-Offsets

Möchte man sein Haus automatisieren, stehen einem ein ganzer Zoo von verschiedenen Systemen zur Verfügung: HomeMatic, FS20, EnOcean, Peha, KNX und viele mehr. Leider haben die Steuerzentralen der einzelenen Systeme eins gemeinsam: Sie können nur Komponenten des eigenen Systems ansprechen und lassen sich schlecht erweitern.

Daher hat Rudolf Koenig mit einigen anderen Entwicklern eine universelle Hausautomatisierungszentrale entwickelt: FHEM. Hiermit lassen sich, wenn man entsprechende Gateways auf die jeweiligen Funksysteme hat, beliebige Systeme anbinden.

Möchte man Rollos automatisieren, so bietet die Sunset-Funktion einen einfachen Weg, bei Sonnenuntergang die Rollos herunterzufahren. So sorgt der Befehl


define Zimmer1.Rollo.Herunter at *{sunset(0,"17:00","22:00")} set Zimmer1.Rollo off

dafür, dass das Rollo Kueche.Rollo bei Sonnenuntergang heruntergefahren wird, jedoch nicht vor 17:00 und nicht nach 22:00.

Macht man das für alle Rollos auf diesem Weg, ergibt sich jedoch ein Problem. Das ganze sieht ziemlich automatisiert aus und taugt für keine 5 Pfennig (ähh…Cent) als Anwesenheitssimulation. Man würde schließlich wenn man anwesend ist, auch nicht alle Rollos gleichzeitig herunterfahren können. Praktisch wäre ein zufälliger Offset. FHEM bietet diese Funktion leider nicht von Haus aus, aber man kann sich schnell so eine Funktion selber bauen:

# Zufälligen Offset auf Zeitstamp addieren/subtrahieren
sub
time_random_offset
{
  # Eingangs-Zeitstempel in Variable holen
  my ($timestamp) = shift;

  # Maximalen Zeitoffset (in Sekunden) in Variable speichern
  my ($maxoffset) = shift;

  # Zeitstempel in das interne Format konvertieren: 17:30:09 ==> 17.5025
  # Stunden werden als ganze Zahlen gespeichert, minuten als 1/60 und sekunden als 1/3600
  my ($t) = hms2h($timestamp);

  $t += rand()  * $maxoffset / 3600;

  # Zurück in HH:MM:SS konvertieren.
Weiter lesen

Speicher beim STM32 (Cortex-M3/Cortex-M0) sparen

In der letzten Zeit programmiere ich recht viel mit dem STM32. Dabei handelt es sich um einen Microcontroller mit ARM-v7 Kern (Cortex-M0, Cortex-M3, Cortex-M4), der sich grob im Preisniveau von AVRs bewegt, aber deutlich performanter ist. Seitens des Herstellers (ST Microelectronics) wird eine angenehm nutzbare Firmware-Library (Standard Peripherals Library) mitgeliefert, sodass man eigentlich direkt starten kann. Achsoo, GCC gibts natürlich auch als Compiler: . Das ganze wird dann unter Eclipse programmiert und mit OpenOCD debuggt.

Jetzt aber zum eigentlichen Thema. Wenn man nämlich wie von ST in den Beispielen der Firmware-Library die Peripherie initialisiert, dann sieht das in etwa so aus:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_Level_1;
gpio.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOC, &gpio);

Zum einen ist dieser Code recht unübersichtlich, zum anderen passiert aber hier etwas, was man eigentlich gar nicht möchte. Zuerst wird nämlich auf dem Stack Speicher für die Struktur reserviert, dann im Programm Feld für Feld mit Werten gefüllt und danach an die Init-Funktion übergeben. Im Assembler sieht das übrigens so aus:

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
 800899a:	2380      	movs	r3, #128	; 0x80
 800899c:	031b      	lsls	r3, r3, #12
 800899e:	1c18      	adds	r0, r3, #0
 80089a0:	2101      	movs	r1, #1
 80089a2:	f7fa f919 	bl	8002bd8 <RCC_AHBPeriphClockCmd>

  GPIO_InitTypeDef gpio;
  gpio.GPIO_Mode = GPIO_Mode_OUT;
 80089a6:	1c3b      	adds	r3, r7, #0
 80089a8:	2201      	movs	r2, #1
 80089aa:	711a      	strb	r2, [r3, #4]
  gpio.GPIO_OType = GPIO_OType_PP;
 80089ac:	1c3b      	adds	r3, r7, #0
 80089ae:	2200      	movs	r2, #0
 80089b0:	719a      	strb	r2, [r3, #6]
  gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
 80089b2:	1c3b      	adds	r3, r7, #0
 80089b4:	2200      	movs	r2, #0
 80089b6:	71da      	strb	r2, [r3, #7]
  gpio.GPIO_Speed = GPIO_Speed_Level_1;
 80089b8:	1c3b      	adds	r3, r7, #0
 80089ba:	2200      	movs	r2, #0
 80089bc:	715a      	strb	r2, [r3, #5]
  gpio.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
 80089be:	1c3b      	adds	r3, r7, #0
 80089c0:	22c0      	movs	r2, #192	; 0xc0
 80089c2:	0212      	lsls	r2, r2, #8
 80089c4:	601a      	str	r2, [r3, #0]
  GPIO_Init(GPIOC, &gpio);
 80089c6:	4a04      	ldr	r2, [pc, #16]	; (80089d8 <DIR_Config+0x44>)
 80089c8:	1c3b      	adds	r3, r7, #0
 80089ca:	1c10      	adds	r0, r2, #0
 80089cc:	1c19      	adds	r1, r3, #0
 80089ce:	f7f9 fddb 	bl	8002588 <GPIO_Init>

Ziemlich aufwendig für ein wenig Initialisierung, die sich nie ändert, gell?… Weiter lesen