Selbstbau-Arduino-Sensor mit wiederaufladbaren Akkus

Vor einiger Zeit habe ich mir zwei DIY-Sensoren von technikkram.net bestellt. Technikkram schickt alle Teile, nur löten muss man noch. Obwohl ich im Löten nicht sonderlich geübt bin, hat der Aufbau und die Inbetriebnahme nach dem zusätzlichen Einstellen der Sendefrequenz funktioniert.

Kein Betrieb mit Akkus möglich

Für den Betrieb meiner Homematic-Sensoren verwende ich ausschliesslich wiederaufladbare eneloop-Akkus. Leider hat der technikkram-Sensor nach wenigen Tagen mit den Akkus den Betrieb eingestellt, wohingegen der Sensor mit Alkaline-Batterien mehr als ein halbes Jahr ohne Problem gelaufen ist und wohl auch noch wesentlich länger gelaufen wäre.

Schaut man sich die Entladekurve der eneloops an, wird auch sofort klar, warum das so ist: Die Spannung eines einzelnen, voll-geladenen Akkus fällt sehr schnell von 1,45 V ab, um sich dann auf ca. 1,2 V für längere Zeit zu stabilisieren. Bei zwei Zellen in Serie ergibt das eine Spannung von 2,4 V. Der Arduino Mini Pro kommt standardmässig mit einer Brown-Out-Detection daher, welche bei 2,7 V eingestellt ist. Die BOD soll ein instabiles Verhalten bei zu geringer Versorgungsspannung verhindern und schaltet den Pro Mini bei Unterschreitung einfach ab, was mit den eneloops innerhalb weniger Tage passiert. Alkaline-Batterie hingegen starten mit einer höheren Spannung als die NiMH und bleiben auch sehr lange über 1,35 V, so dass die BOD erst viel später zuschlägt.

Lösung: Brown-Out-Detection (BOD) einstellen

Glücklicherweise kann man die BOD-Spannung konfigurieren (Schwellen: 4,3 V - 2,7 V - 1,8 V - off). Die BOD und noch andere Einstellungen werden mittels sogenannter Fuses (Sicherungen) gesetzt. Das geht leider nicht über das normalerweise für die Programmierung verwendete serielle Interface, sondern nur über einen Programmer. Ich verwende einen sehr günstigen (ca. 3 US-$) USB ISP AVR Programmer, den man z.B. hier bestellen kann. Es ist zu beachten, dass der Programmer auch 3,3 V beherrscht und nicht nur 5V, da der Arduino Pro Mini ebenfalls in der 3,3V-Ausgabe auf dem Sensor sitzt. Die Spannung des Programmers kann meist über einen Jumper eingestellt werden.

ISP-Programmer anschliessen

Da ich den Arduino Pro Mini schon eingelötet hatte und zu faul war, die Stiftleiste auf dem Board nachträglich aufzulöten, habe ich es "fliegend" mit einem Breadboard und E-Z Hooks gemacht. Eine gute Dokumentation der Verkabelung ist hier zu finden.

Fuses auslesen

Fuses auslesen und neu setzen geht einfach mit avrdude. Man kann das Tool herunterladen, oder wie bei Ubuntu direkt über den Packagemanager Apt installieren.

$> sudo apt install avrdude libusb-1.0-0

Als nächstes sollten die Batterien entfernt und der Programmer an den USB-Port angeschlossen werden. Man kann nun die korrekte Funktionsweise mit avrdude überprüfen:

$> avrdude -v -p m328p -c usbasp
avrdude: Version 6.3
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/alison/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : usb
         Using Programmer              : usbasp
         AVR Part                      : ATmega328P
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PC2
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
           flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
           lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : usbasp
         Description     : USBasp, http://www.fischl.de/usbasp/

avrdude: auto set sck period (because given equals null)
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: safemode: hfuse reads as DA
avrdude: safemode: efuse reads as FD

avrdude: safemode: hfuse reads as DA
avrdude: safemode: efuse reads as FD
avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF)

avrdude done.  Thank you.

Wenn obiger Output erscheint, hat die Kommunikation mit dem Arduino funktioniert. Wichtig ist die vorletzte Zeile mit der Ausgabe der Fuses, E für Extended, H für High, L für Low. Wenn man noch nicht an den Fuses geschraubt hat, kann man direkt zu "Fuses einstellen" springen. Ansonsten muss ruft man den Engbedded Atmel AVR® Fuse Calculator auf, wählt als "AVR part name" den "ATmega328P" aus und gibt die ausgelesenen Fuses unter "Current Settings" ein. Danach kann man unter "Feature configuration" das Brown Out Detection level auf 1.8V setzen und bekommt unten die korrekte Fuse-Configuration für den avrdude angezeigt.

Fuses einstellen

Um die neu berechneten Fuses auf den Pro Mini zu übertragen, wird avrdude mit den Fuse-Werten aus dem Fuse Calculator aufgerufen. Wer bisher keine Anpassungen an den Fuses vorgenommen hat, kann die Kommandozeile unverändert übernehmen.

$> avrdude -p m328p -c usbasp -U lfuse:w:0xff:m -U hfuse:w:0xda:m -U efuse:w:0xfe:m 

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "0xff"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.00s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xff:
avrdude: load data lfuse data from input file 0xff:
avrdude: input file 0xff contains 1 bytes
avrdude: reading on-chip lfuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: reading input file "0xda"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.00s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xda:
avrdude: load data hfuse data from input file 0xda:
avrdude: input file 0xda contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xfe"
avrdude: writing efuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0xfe:
avrdude: load data efuse data from input file 0xfe:
avrdude: input file 0xfe contains 1 bytes
avrdude: reading on-chip efuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of efuse verified

avrdude: safemode: Fuses OK (E:FE, H:DA, L:FF)

avrdude done.  Thank you.

Das wars. Bei mir läuft der technikkram-Sensor nun seit einigen Wochen mit eneloops.

Tiefentladung verhindern

Die eneloops haben eine Entladeschluss-Spannung von 1,0 V. Werden sie unter diese Schwelle entladen, nehmen sie Schaden. Durch die Verschiebung der BOD auf 1,8 V würden der Pro Mini rechnerisch bei nur 0,9V pro Zelle ausschalten, also unterhalb der Entladeschluss-Spannung. Die BOD von 1,8 V nützt uns also nichts. Daher kann man die BOD ganz ausschalten:

 $> avrdude -p m328p -c usbasp -U lfuse:w:0xff:m -U hfuse:w:0xda:m -U efuse:w:0xff:m

und die Abschaltung des Arduino bei zu geringer im Programm erledigen. Dafür muss im Programm (HM-WDS40-TH-I-BME280.ino) die function loop um die Abschaltung ergänzt werden, so dass sie wie folgt aussieht:

void loop() {
  bool worked = hal.runready();
  bool poll = sdev.pollRadio();
  if ( worked == false && poll == false ) {
    // deep discharge protection
    if( hal.battery.critical() ) {
      // halt the arduino
      hal.activity.sleepForever(hal);
    }
    hal.activity.savePower<Sleep<>>(hal);
  }  
}

Zusätzlich sollte natürlich auch die kritische Batteriespannung konfiguriert werden. Der Prozessor des Pro Mini (ATmega328P) kann die eigene Versorgungsspannung messen in dem sie, einfach gesagt, gegen eine Referenzspannung verglichen wird (zum Nachlesen z.B. hier). Diese Referenzspannung ist jedoch sehr ungenau (+/- 10%), was dazu führt, dass Vcc ebenfalls ungenau gemessen wird. Konfiguriere ich bei meinen Sensoren 2,1V als kritische Spannung, schaltet der Arduino erst bei 2,02 oder 2,03 V ab. Das kann bei anderen Boards durchaus verschieden sein.

Die kritische Batteriespannung wird hiermit auf 2.1 V gesetzt: battery.critical(21):

class Hal : public BaseHal {
  public:
    void init (const HMID& id) {
      BaseHal::init(id);
      // measure battery every 1h
      battery.init(seconds2ticks(60UL * 60), sysclock);
      battery.low(23);
      battery.critical(21);
    }

    bool runready () {
      return sysclock.runready() || BaseHal::runready();
    }
} hal;

Sollte der Arduino jedoch zu früh abschalten, dann kann man diesen Wert noch nach unten korrigiren.

Kompilieren und Hochladen auf den Arduino Pro Mini nicht vergessen!