Sonntag, 28. Januar 2018

Umbau meines Fahrrads zum Pedelec

nach nunmehr fast 4 Monaten und über 1200km mit meinem zum Pedelec umgebauten Fahrrad, möchte ich diesen Umbau hier einmal beschreiben.

Ich fand im Internet einen günstigen Umbausatz, bestehend aus einem eingespeichten Nabenmotor als Ersatz für das unmotorisierte Hinterrad.
Mitgeliefert wurde praktischerweise gleich eine Ritzelkassette. Diese enthält einen Freilauf und ist kompatibel zu den alten Shimano-Kassetten mit integriertem Freilauf. Praktischerweise passten auch die Abstufung und die Abstände der Ritzel zueinander für meine Alivio 7 Gänge.
Außerdem im Set dabei: der Motorcontroller, ein Pedalsensor und 2 Bremshebel mit Schalter, 2 Griffe (der rechte ist als Gasgriff ausgeführt - wie bei einem Motorrad) sowie eine Tasche zum Anbinden der Elektrik an den Rahmen und einige Kabelbinder.
Leider musste ich für den Pedalsensor etwas Material der Tretkurbel abfeilen, der Sensor hätte sonst geklemmt und wäre beim Anziehen der Befestigungsschraube der Tretkurbel beschädigt worden.
Auf den Sensor kommt eine Scheibe mit Magneten. Der Sensor ist fest unter der linken Mutter des Tretlagers befestigt, die Scheibe ist auf den Vierkant der Tretlagerwelle aufgesteckt und dreht sich mit.


Da ich die kombinierte Brems-Schaltungs-Elemente der Alivio-Schaltung am Lenker weiterverwenden wollte, kamen die mitgelieferten Bremshebel mit Schalter nicht in Frage. Ich habe mich für Reed-Relais in Festo-Zylinderschaltern entschieden und diese mit Montagekleber auf den Block geklebt. Die Ansteuerung der Relais erfolgt über kleine Neodym-Magneten, die ich in die Bremshebel eingebohrt habe. Bisher hatte ich noch keine Probleme, obwohl mir das Rad bereits mehrmals umgefallen ist.
Die Griffe habe ich nicht benutzt, sondern stattdessen den Forumscontroller aus dem Pedelec-Forum auf einer Lochrasterplatine aufgebaut. Der Controller basiert auf einem Arduino und ist extrem flexibel an die eigenen Bedürfnisse anpassbar.


Die Platine habe ich in einen Ziplock-Beutel eingepackt, damit in die Tasche eindringender Regen keine Beschädigung der Elektronik hervorruft. Auch hier habe ich nach mehreren längeren Regenfahrten noch keinerlei Ausfälle zu beklagen.
Ich habe mich weitestgehend an die Schaltung der Version 1.4 gehalten und folgende Komponenten verbaut:
  • Arduino Nano
  • fertig aufgebauter Stromsensor, der als Zubehör für Arduino verkauft wird
  • fertig aufgebauter Spannungsregler zur Versorgung der Fahrbeleuchtung mit 6V
  • jede Menge Stocko-Pfostenverbinder für die Peripherie
  • BUZ10 zur Schalten der Beleuchtung
 Zur Anzeige und Einstellung der Parameter habe ich ein Nokia-Grafik-Display und ein Poti in ein 3D-selbstgedrucktes Kästchen, welches am Lenker befestigt ist.

Vervollständigt wird die Maschine noch durch ein Daumengas, ein abgezwickter Fahrradtacho-Sensor nebst Speichen-Magnet und natürlich ein Akku, der an den Befestigungslöchern der Trinkflaschenhalterung am unteren Rahmen hängt.






Der Akku hat 11Ah bei 36V. Das ergibt bei 250W Motorleistung theoretisch etwa 80-100km je Akkuladung. In der Praxis ist das natürlich stark von der Außentemperatur (der Lipo-Akku hat eine Spannungslage bei niediger Temperatur, die bereits bei halb entleerter Kapazität und mäßiger Last zur Notabschaltung zum Schutz des Akkus führt), der Belastung und dem Alter abhängig. Ich lade immer so zwischen 35 und 45km nach. Im Sommer komme ich da wahrscheinlich deutlich weiter.

Da ich im Winter überwiegend bei Dunkelheit unterwegs bin, habe ich mir noch eine anständige Beleuchtung gegönnt. Die Vorderlampe ist speziell für Pedelecs konstruiert und macht ordentlich Licht.

Ich bin nun, trotz legaler Dimensionierung von Motor-Leistung  auf max 250W und Begrenzung der elektrischen Unterstützung bis maximal 25km/h recht flott unterwegs. Die Felgenbremsen des Fahrraden schienen mir drastisch unterdimensioniert (das Gesamtgewicht des frisch gebackenen Pedelecs liegt bei etwa 22kg). Die bereits vor einigen Jahren nachgerüstete Federgabel hat bereits Befestigungslöcher für Scheibenbremsen. Ich Scheiben/Bremssättel gefunden, die mit Seizug angesteuert werden. Eingebaut im Vorderrad (das Ausfallende des Rahmens hat leider nicht genug Platz, um auch die hintere Bremse umzurüsten) entschärft es die Situation merklich.




Zum Schluss noch die Kosten, die ich für den Umbau investiert habe:

  • 165,99€ 26” Zoll 36V 250W Hinterrad E Bike Conversion Kit Elektro Fahrrad Umbausatz Kit 
  • 209,00€ Akku zum Anbau anstatt eines Flaschenhalters
  • 10,45€ Schaltwerk-Schutzbuegel-lang-Befestigung-am-Ausfallende-455216
  • 3,79€ 1 x Schwalbe Fahrrad-Felgenband PU Größen 16 / 18 / 20 / 24 / 26 / 28 Zoll
  • 3,95€ Stromsensor
  • 4,99€ LCD-Display
  • 4,00€ Neodym-Supermagnete Stäbe - 5x8mm
  • 19,99€ Gepäckträger
  • 18,90€ LED-Scheinwerfer 45 Lux Sport Pro E-Bike 6 - 48 Volt DC Fahrradlampe Pedelec
  • 8,99€ AXA BLUELINE mit Standlicht LED Rücklicht Fahrrad Rücklicht für Nabendynamo 50mm
  • 3,90€ 10 Stück Bremszüge 2000 mm Bremszug MTB Bowdenzug Fahrrad Innenzug Bremsseil
  • 24,95 Felt Vorderrad 26" Disc Moutainbike MTB Alex Hohlkammer Felge, VR Schwarz
  • 12,95€ Fahrrad Scheibenbremse mechanische Scheibe MTB Bremsscheibe Vorne+Hinten Set
  • 5,45€ Daumengas Gasgriff Gashebel für E-Bike Elektro Bike Elektrofahrrad E-Scooter Neu
macht eine Gesamtsumme von 497,30€. Das ist eiine Stange Geld, aber ich habe einen deutlichen Komfortgewinn und das Fahrrad stark aufgewertet. Wenn ich nun die Investition durch eingesparten Sprit meines Autos darstelle, würde ich bei 8L/100km (Diesel, Kurzstrecke) und einem derzeitigen Preis von 1,19€/Liter nach etwa 4200km den Umbau finanziert haben. Das ist bei meinen derzeit ca. 60km/Woche (täglich zur Arbeit und zurück) binnen eines Jahres der Fall. Natürlich nur, wenn das System solange durchhält...

Sonntag, 20. August 2017

JouleThief XXL V2.1 - noch ein Update

der Betrieb der V2.0 ist schon sehr komfortabel. Die Spannung der Batterien wird zuverlässig angezeigt und es kommt eine Meldung, wenn deren Spannung zu weit absinkt.
Allerdings ist der Kontrast des Displays noch nicht optimal, da dessen Versorgungsspannung abhängig der Leere der Batterien im Bereich von 5V bis unter 3V schwankt. Da muss eine Kontrast-Automatik her! In Anlehnung an diese Webseite, habe ich die Schaltung erweitert: Ich habe noch 2 Analogeingänge frei, mit der ich sowohl Versorgungsspannung des Displays, als auch die Kontrastspannung messen kann. Über einen PWM-Ausgang mit nachgeschaltetem Spannungsteiler, sowie Glättungs-Schaltung kann ich nun den Kontrast immer im optimalen Bereich halten.



















In diesem Zusammenhang habe ich noch ein paar kleine Bugs im Programm beseitigt:




// ---------------------------------------------------------
// Batteriemessger�t
// es wird die Spannung von 6 Batterien gemessen und gleichzeitig
// auf einem LCD-Display dargestellt.
// ---------------------------------------------------------

#include

#define DEBUG
#include "log.h"

struct _Batterie
{
    float spannung;  // die gemessene Spannung
    bool ausgetastet;  // wird die Spannung gerade angezeigt, oder ausgetastet
    float abschaltspannung;  // darunter ist die Batterie leer
};

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // initialize the library with the numbers of the interface pins
int AnalogBatPin1 = A0;
int AnalogBatPin2 = A1;
int AnalogBatPin3 = A2;
int AnalogBatPin4 = A3;
int AnalogBatPin5 = A4;
int AnalogBatPin6 = A5;
int AnalogKontrastPin = A6;  // hier wird die Kontrastspannung gemessen
int AnalogVCCPin = A7; // hier wird die VCC gemessen (für die Kontrastspannungsberechnung)
int KontrastPWM = 6;
int TasterPin = 10;
int SummerPin = 9;
int LEDPin =13;
bool Pieps; // w�hrenddessen wird gepiepst
int Slot; // z�hlt immer durch
struct _Batterie Batterien[6];
bool SekundenBlinkbit; // toggle jede Sekunde
bool HalbsekundenBlinkbit; // toggle jede halbe Sekunde
bool ViertelsekundenBlinkbit; // toggle jede viertel Sekunde

#define NEIN 0  // Taste nicht gedr�ckt
#define KURZ 1  // Taste kurz gedr�ckt
#define LANG 2  // Taste lang gedr�ckt

// -------------------------------
void setup()
{
    SERIAL_BEGIN;
    lcd.begin(16, 2);    // set up the LCD's number of columns and rows:
    analogReference(INTERNAL);
    pinMode(TasterPin, INPUT);
    pinMode(SummerPin, OUTPUT);
    pinMode(LEDPin, OUTPUT);
    InitVariablen();
}

// -------------------------------
void InitVariablen(void)
{
    int i;

    for(i=0; i<6 br="" i="">        Batterien[i].abschaltspannung = 0.02F;
}

// -------------------------------
void MesseBatterien(void)
{
    int aval;
    float offset[6] = { 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F };
    float zaehl[6] = { 1.24F, 1.24F, 1.24F, 1.24F, 1.24F, 1.24F };
    float nenner[6] = { 0.83F, 0.83F, 0.83F, 0.83F, 0.83F, 0.83F };

    aval = analogRead(AnalogBatPin1);
    Batterien[0].spannung = (float)aval *zaehl[0] / nenner[0] / 1000.0F + offset[0];
    aval = analogRead(AnalogBatPin2);
    Batterien[1].spannung =
        (float)aval *2.0F * zaehl[1] / nenner[1] / 1000.0F + offset[1];
    Batterien[1].spannung -= Batterien[0].spannung;
    aval = analogRead(AnalogBatPin3);
    Batterien[2].spannung =
        (float)aval *3.0F * zaehl[2] / nenner[2] / 1000.0F + offset[2];
    Batterien[2].spannung -= Batterien[0].spannung;
    Batterien[2].spannung -= Batterien[1].spannung;
    aval = analogRead(AnalogBatPin4);
    Batterien[3].spannung =
        (float)aval *4.0F * zaehl[3] / nenner[3] / 1000.0F + offset[3];
    Batterien[3].spannung -= Batterien[0].spannung;
    Batterien[3].spannung -= Batterien[1].spannung;
    Batterien[3].spannung -= Batterien[2].spannung;
    aval = analogRead(AnalogBatPin5);
    Batterien[4].spannung =
        (float)aval *5.0F * zaehl[4] / nenner[4] / 1000.0F + offset[4];
    Batterien[4].spannung -= Batterien[0].spannung;
    Batterien[4].spannung -= Batterien[1].spannung;
    Batterien[4].spannung -= Batterien[2].spannung;
    Batterien[4].spannung -= Batterien[3].spannung;
    aval = analogRead(AnalogBatPin6);
    Batterien[5].spannung =
        (float)aval *6.0F * zaehl[5] / nenner[5] / 1000.0F + offset[5];
    Batterien[5].spannung -= Batterien[0].spannung;
    Batterien[5].spannung -= Batterien[1].spannung;
    Batterien[5].spannung -= Batterien[2].spannung;
    Batterien[5].spannung -= Batterien[3].spannung;
    Batterien[5].spannung -= Batterien[4].spannung;
}

// -------------------------------
void AusgabeLCD(void)
{
    char buffer1[40], buffer2[40];
    char t1[10], t2[10], t3[10], t4[10], t5[10], t6[10];

    if(Batterien[0].ausgetastet)
        strcpy(t1, "    ");
    else
        dtostrf(Batterien[0].spannung, 4, 2, t1);
    if(Batterien[1].ausgetastet)
        strcpy(t2, "    ");
    else
        dtostrf(Batterien[1].spannung, 4, 2, t2);
    if(Batterien[2].ausgetastet)
        strcpy(t3, "    ");
    else
        dtostrf(Batterien[2].spannung, 4, 2, t3);
    sprintf(buffer1, "%s %s %s  ", t1, t2, t3);

    if(Batterien[3].ausgetastet)
        strcpy(t4, "    ");
    else
        dtostrf(Batterien[3].spannung, 4, 2, t4);
    if(Batterien[4].ausgetastet)
        strcpy(t5, "    ");
    else
        dtostrf(Batterien[4].spannung, 4, 2, t5);
    if(Batterien[5].ausgetastet)
        strcpy(t6, "    ");
    else
    dtostrf(Batterien[5].spannung, 4, 2, t6);
    sprintf(buffer2, "%s %s %s  ", t4, t5, t6);

    lcd.setCursor(0, 0);
    lcd.print(buffer1);
    lcd.setCursor(0, 1);
    lcd.print(buffer2);
}

// -------------------------------
int LeseTaster(void)
{
    int tasterGedrueckt; // und hier ist die Taste
    static long int anfang; // hier merken wir uns, seit wann der Taster gedr�ckt ist
    long int jetzt=millis(); // Millisekunden
    long int drueckdauer; // so lange ist der Taster bereits gedr�ckt
    bool gedrueckt;
    static bool letztes_gedrueckt=0;

    gedrueckt = digitalRead(TasterPin);
    digitalWrite(LEDPin, gedrueckt); // visuelle R�ckmeldung

    if (gedrueckt)
    {
        drueckdauer = jetzt-anfang;
        if (drueckdauer > 1000)
            tasterGedrueckt = LANG;
        else
        {
            if (drueckdauer > 500)
                tasterGedrueckt = KURZ;
            else
                tasterGedrueckt = NEIN;
        }
        if (letztes_gedrueckt == 0)
            anfang = jetzt;
    }
    else
        anfang = jetzt; // damit die Tasten-Dr�ck-Dauer kurz ist

    letztes_gedrueckt = gedrueckt;

//    SERIAL_PRINT(anfang);
//    SERIAL_PRINT(" ");
//    SERIAL_PRINT(jetzt);
//    SERIAL_PRINT(" ");
//    SERIAL_PRINTLN(tasterGedrueckt);

    return tasterGedrueckt;
}

// -------------------------------
void PruefeBatterien(void)
{
    int i;
    int leereBatterie = -1;

    for(i=0; i<6 br="" i="">    {
        if (Batterien[i].spannung < Batterien[i].abschaltspannung)
            leereBatterie = i+1;           
    }
    // SERIAL_PRINTLN(leereBatterie);
   
    if (leereBatterie > Slot)
    {
        Pieps = true;
        //SERIAL_PRINT("Slot ");
        //SERIAL_PRINTLN(Slot);
    }
    else
        Pieps = false;
   
}

// -------------------------------
void StelleKontrast(void)
{
    int kont, vcc;
    static int kontrastwert=0;  // der PWM-Stellwert für den Kontrast
   
    kont = analogRead(AnalogKontrastPin);
    SERIAL_PRINT("Kin=");
    SERIAL_PRINT(kont);

    vcc = analogRead(AnalogVCCPin);
    SERIAL_PRINT("VCC=");
    SERIAL_PRINTLN(vcc);  // 1000bit = 5V

    kontrastwert += ((800*(vcc/1000))-kont)/7;
    if (kontrastwert<0 br="">        kontrastwert = 0;
    if (kontrastwert>255)
        kontrastwert = 255;

    SERIAL_PRINT(" Kout=");
    SERIAL_PRINTLN(kontrastwert);

    analogWrite(KontrastPWM, kontrastwert);
}

// -------------------------------
void loop()
{
    long int jetzt;
    int taste;
    static long int jedeSekunde=0; // f�r den Timer jeder Sekunde
    static long int jedeHalbeSekunde=0; // f�r den Timer jeder halben Sekunde
    static long int jedeViertelSekunde=0; // f�r den Timer jeder viertel Sekunde

    jetzt = millis();
    taste = LeseTaster();
    PruefeBatterien();

    if ((jetzt-jedeSekunde) > 1000)
    {   // was hier steht, wird zyklisch jede Sekunde ausgef�hrt
        MesseBatterien();
        StelleKontrast();

        SekundenBlinkbit = ~SekundenBlinkbit;
        jedeSekunde = jetzt;
    }

    if ((jetzt-jedeHalbeSekunde) > 500)
    {   // was hier steht, wird zyklisch jede halbe Sekunde ausgef�hrt
        AusgabeLCD();

        Slot++;
        if (Slot>5)
            Slot=0;

        SERIAL_PRINTLN(Slot);

        HalbsekundenBlinkbit = ~HalbsekundenBlinkbit;
        jedeHalbeSekunde = jetzt;
    }

    if ((jetzt-jedeViertelSekunde) > 250)
    {   // was hier steht, wird zyklisch jede viertel Sekunde ausgef�hrt
        static bool letzte; // nur maximal 1/4 Sek. piepsen
        if (Pieps & !letzte)
        {
            letzte = true;
            digitalWrite(SummerPin, HIGH);
        }
        else
        {
            letzte = false;
            digitalWrite(SummerPin, LOW);
        }
        ViertelsekundenBlinkbit = ~ViertelsekundenBlinkbit;
        jedeViertelSekunde = jetzt;
    }
}

Dienstag, 1. August 2017

Joule-Thief XXL V2.0

Ich habe nun schon einige Batterien leer gesaugt, jedoch immer wieder das Problem, dass Manche einfach auslaufen oder gar ihre ätzende Flüssigkeit versprühen. Woran liegt das?
Die Antwort ist einfach: da die Zellen in Reihe verdrahtet sind, fließt der Strom durch alle Zellen. Wenn deren Ladung unterschiedlich ist, ist deren Spannung unter Last natürlich auch unterschiedlich. Ist eine Zelle nun leer, so fließt ja weiterhin Strom durch diese Zelle und sie wird negativ aufgeladen. Das quitieren die Batterien mit Auslaufen, je nach Stromstärke mit mehr oder weniger Getöse. Was her muss, ist eine einfache Möglichkeit die Spannung jeder einzelnen Batterie zu überwachen und bevor sie Schaden anrichten kann, muss eine Meldung kommen.

Der Joule-Thief XXL V2.0 ist geboren!
Ich habe einen meiner Arduinos mit einem LCD-Display versehen und die Spannung jeder einzelnen Zelle über ein Widerstandsnetzwerk gemessen und angezeigt. Fällt nun die Spannung einer Zelle unter +0.01V, so wird ein Piepston ausgegeben, der die Position der Zelle im Verbund angibt. So ist sichergestellt, dass auch dann, wenn die Gesamtspannung zu niedrig ist um eine vernünftige Anzeige zu erzeugen, die leere Zelle gemeldet wird.


Zur Verdeutlichung habe ich die Schaltung  mal im Eagle gemalt:

Die Spannungen werden über die Potis abgegriffen und dann so eingestellt, dass die Eingangsspannung des Arduino auf dessen Analog-Pins nicht überschritten wird. Das bedeutet dann, dass die Spannung an A0 bei 0-1.5V, an A1 zwischen 0-3.0V, an A2 zwischen 0-4.5V usw. liegt. Die Messgenauigkeit wird bei jeder weiteren Zelle schlechter, aber für meine Anforderungen reicht das vollkommen.



Und so sieht das Programm aus:
// ---------------------------------------------------------
// Batteriemessgerät
// es wird die Spannung von 6 Batterien gemessen und gleichzeitig
// auf einem LCD-Display dargestellt.
// ---------------------------------------------------------

#include

#define DEBUG
#include "log.h"

struct _Batterie
{
    float spannung;  // die gemessene Spannung
    bool ausgetastet;  // wird die Spannung gerade angezeigt, oder ausgetastet
    float abschaltspannung;  // darunter ist die Batterie leer
};

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // initialize the library with the numbers of the interface pins
int AnalogBatPin1 = A0;
int AnalogBatPin2 = A1;
int AnalogBatPin3 = A2;
int AnalogBatPin4 = A3;
int AnalogBatPin5 = A4;
int AnalogBatPin6 = A5;
int TasterPin = 10;
int SummerPin = 9;
int LEDPin =13;
bool Pieps; // währenddessen wird gepiepst
int Slot; // zählt immer durch
struct _Batterie Batterien[6];
bool SekundenBlinkbit; // toggle jede Sekunde
bool HalbsekundenBlinkbit; // toggle jede halbe Sekunde
bool ViertelsekundenBlinkbit; // toggle jede viertel Sekunde

#define NEIN 0  // Taste nicht gedrückt
#define KURZ 1  // Taste kurz gedrückt
#define LANG 2  // Taste lang gedrückt

// -------------------------------
void setup()
{
    SERIAL_BEGIN;
    lcd.begin(16, 2);    // set up the LCD's number of columns and rows:
    analogReference(INTERNAL);
    pinMode(TasterPin, INPUT);
    pinMode(SummerPin, OUTPUT);
    pinMode(LEDPin, OUTPUT);
    InitVariablen();
}

// -------------------------------
void InitVariablen(void)
{
    int i;

    for(i=0; i<6 br="" i="">        Batterien[i].abschaltspannung = 0.1F;
}

// -------------------------------
void MesseBatterien(void)
{
    int aval;
    float offset[6] = { 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F };
    float zaehl[6] = { 1.24F, 1.24F, 1.24F, 1.24F, 1.24F, 1.24F };
    float nenner[6] = { 0.83F, 0.83F, 0.83F, 0.83F, 0.83F, 0.83F };

    aval = analogRead(AnalogBatPin1);
    Batterien[0].spannung = (float)aval *zaehl[0] / nenner[0] / 1000.0F + offset[0];
    aval = analogRead(AnalogBatPin2);
    Batterien[1].spannung =
        (float)aval *2.0F * zaehl[1] / nenner[1] / 1000.0F + offset[1];
    Batterien[1].spannung -= Batterien[0].spannung;
    aval = analogRead(AnalogBatPin3);
    Batterien[2].spannung =
        (float)aval *3.0F * zaehl[2] / nenner[2] / 1000.0F + offset[2];
    Batterien[2].spannung -= Batterien[0].spannung;
    Batterien[2].spannung -= Batterien[1].spannung;
    aval = analogRead(AnalogBatPin4);
    Batterien[3].spannung =
        (float)aval *4.0F * zaehl[3] / nenner[3] / 1000.0F + offset[3];
    Batterien[3].spannung -= Batterien[0].spannung;
    Batterien[3].spannung -= Batterien[1].spannung;
    Batterien[3].spannung -= Batterien[2].spannung;
    aval = analogRead(AnalogBatPin5);
    Batterien[4].spannung =
        (float)aval *5.0F * zaehl[4] / nenner[4] / 1000.0F + offset[4];
    Batterien[4].spannung -= Batterien[0].spannung;
    Batterien[4].spannung -= Batterien[1].spannung;
    Batterien[4].spannung -= Batterien[2].spannung;
    Batterien[4].spannung -= Batterien[3].spannung;
    aval = analogRead(AnalogBatPin6);
    Batterien[5].spannung =
        (float)aval *6.0F * zaehl[5] / nenner[5] / 1000.0F + offset[5];
    Batterien[5].spannung -= Batterien[0].spannung;
    Batterien[5].spannung -= Batterien[1].spannung;
    Batterien[5].spannung -= Batterien[2].spannung;
    Batterien[5].spannung -= Batterien[3].spannung;
    Batterien[5].spannung -= Batterien[4].spannung;
}

// -------------------------------
void AusgabeLCD(void)
{
    char buffer1[40], buffer2[40];
    char t1[10], t2[10], t3[10], t4[10], t5[10], t6[10];

    if(Batterien[0].ausgetastet)
        strcpy(t1, "    ");
    else
        dtostrf(Batterien[0].spannung, 4, 2, t1);
    if(Batterien[1].ausgetastet)
        strcpy(t2, "    ");
    else
        dtostrf(Batterien[1].spannung, 4, 2, t2);
    if(Batterien[2].ausgetastet)
        strcpy(t3, "    ");
    else
        dtostrf(Batterien[2].spannung, 4, 2, t3);
    sprintf(buffer1, "%s %s %s  ", t1, t2, t3);

    if(Batterien[3].ausgetastet)
        strcpy(t4, "    ");
    else
        dtostrf(Batterien[3].spannung, 4, 2, t4);
    if(Batterien[4].ausgetastet)
        strcpy(t5, "    ");
    else
        dtostrf(Batterien[4].spannung, 4, 2, t5);
    if(Batterien[5].ausgetastet)
        strcpy(t6, "    ");
    else
    dtostrf(Batterien[5].spannung, 4, 2, t6);
    sprintf(buffer2, "%s %s %s  ", t4, t5, t6);

    lcd.setCursor(0, 0);
    lcd.print(buffer1);
    lcd.setCursor(0, 1);
    lcd.print(buffer2);
}

// -------------------------------
int LeseTaster(void)
{
    int tasterGedrueckt; // und hier ist die Taste
    static long int anfang; // hier merken wir uns, seit wann der Taster gedrückt ist
    long int jetzt=millis(); // Millisekunden
    long int drueckdauer; // so lange ist der Taster bereits gedrückt
    bool gedrueckt;
    static bool letztes_gedrueckt=0;

    gedrueckt = digitalRead(TasterPin);
    digitalWrite(LEDPin, gedrueckt); // visuelle Rückmeldung

    if (gedrueckt)
    {
        drueckdauer = jetzt-anfang;
        if (drueckdauer > 1000)
            tasterGedrueckt = LANG;
        else
        {
            if (drueckdauer > 500)
                tasterGedrueckt = KURZ;
            else
                tasterGedrueckt = NEIN;
        }
        if (letztes_gedrueckt == 0)
            anfang = jetzt;
    }
    else
        anfang = jetzt; // damit die Tasten-Drück-Dauer kurz ist

    letztes_gedrueckt = gedrueckt;

//    SERIAL_PRINT(anfang);
//    SERIAL_PRINT(" ");
//    SERIAL_PRINT(jetzt);
//    SERIAL_PRINT(" ");
//    SERIAL_PRINTLN(tasterGedrueckt);

    return tasterGedrueckt;
}

// -------------------------------
void PruefeBatterien(void)
{
    int i;
    int leereBatterie = -1;

    for(i=0; i<6 br="" i="">    {
        if (Batterien[i].spannung < Batterien[i].abschaltspannung)
            leereBatterie = i+1;           
    }
    // SERIAL_PRINTLN(leereBatterie);
   
    if ((leereBatterie > Slot) && ViertelsekundenBlinkbit)
    {
        Pieps = true;
        //SERIAL_PRINT("Slot ");
        //SERIAL_PRINTLN(Slot);
    }
    else
        Pieps = false;
   
}

// -------------------------------
void loop()
{
    long int jetzt;
    int taste;
    static long int jedeSekunde=0; // für den Timer jeder Sekunde
    static long int jedeHalbeSekunde=0; // für den Timer jeder halben Sekunde
    static long int jedeViertelSekunde=0; // für den Timer jeder viertel Sekunde

    jetzt = millis();
    taste = LeseTaster();
    PruefeBatterien();

    if ((jetzt-jedeSekunde) > 1000)
    {   // was hier steht, wird zyklisch jede Sekunde ausgeführt
        MesseBatterien();

        SekundenBlinkbit = ~SekundenBlinkbit;
        jedeSekunde = jetzt;
    }

    if ((jetzt-jedeHalbeSekunde) > 500)
    {   // was hier steht, wird zyklisch jede halbe Sekunde ausgeführt
        AusgabeLCD();

        Slot++;
        if (Slot>5)
            Slot=0;

        SERIAL_PRINTLN(Slot);

        HalbsekundenBlinkbit = ~HalbsekundenBlinkbit;
        jedeHalbeSekunde = jetzt;
    }

    if ((jetzt-jedeViertelSekunde) > 250)
    {   // was hier steht, wird zyklisch jede viertel Sekunde ausgeführt
        if (Pieps)
            digitalWrite(SummerPin, HIGH);
        else
            digitalWrite(SummerPin, LOW);

        ViertelsekundenBlinkbit = ~ViertelsekundenBlinkbit;
        jedeViertelSekunde = jetzt;
    }
}

Nicht schön, aber der Code funktioniert. Wie man sieht, habe ich auch noch eine Taste mit eingebaut, damit die Maschine auch noch parametriert werden könnte. Platz für V2.1...

Zu guter Letzt habe ich noch Alles auf einer Lochrasterplatine zusammengelötet:




Montag, 30. Januar 2017

Joule-Thief XXL

Im vorigen Post habe ich die Verwertung von leeren Batterien durch einen Joule-Thief thematisiert. Da die von mir vorgestellte Taschenlampe für den Alltag nicht wirklich taugt, habe ich noch etwas weiter experimentiert und eine weitere Idee - für den Alltag durchaus praktikabel.

Ich habe jede Menge leerer Batterien, die in keinem Gerät mehr verwendbar sind. Viele Geräte können bei 1.2V schon nicht mehr richtig arbeiten, das schließt sogar den Betrieb an NiMH-Akkus aus. Quartz-Uhrwerke laufen noch etwas länger, aber auch hier ist häufig bei 1.0V der Ofen aus. Und eine NiMH ist da schon tiefentladen, also eigentlich hier gar nicht sinnvoll einsetzbar:

  • ich habe mehrere dieser leeren Primärzellen hintereinander gehängt
  • die resultierende Spannung mit einem DC/DC-Wandler auf etwas über 15V transformiert
  • das Ganze über einen Vorwiderstand und eine Diode an einen Blei-Akku gehängt
  • und damit die leeren Batterien so lange leergesaugt, bis sie ohne Last unter 0.1V hatten
  • hintereinander diese, dann wirklich leeren Batterien, gegen etwas Bessere getauscht
  • und dieses Spiel solange wiederholt, bis ich alle Batterien so leer hatte, dass der Spannungswandler nicht mehr anlief.
  • diesen nun aus Abfall gefüllten Bleiakku  schloss ich über einen weiteren Spannungswandler
  • an eine USB-Powerbank
  • und kann damit mindestens 2x mein Smartphone laden
So habe ich dimensioniert:
  • 8 leere AA-Batterien in Reihe. Ich habe da eine ausgemusterte Batteriehalterung mit einem Tamiya Stecker dran.
  • das ergibt selbst bei nur noch 1V pro Zelle immerhin 8V! Unter Last sind's grad noch 4V.
  • als DC/DC-Aufwärts-Wandler habe ich den MT3608 gewählt. Bei ebay gibt es haufenweise Händler, die damit bestückte Platinen für wenige Euro anbieten. Die Ausgangsspannung kann man mit einem Poti stufenlos einstellen.
  • Einstellen auf 15.0V, damit der Vorwiderstand für den Bleiakku den Strom begrenzt. Ich habe etwa 50mA Ladestrom.
  • Wenn ich den Regler direkt anschließe (also ohne den Vorwiderstand), wird der Strom in den Blei-Akku so groß, dass die Spannung an den leeren Batterien gleich so weit zusammenbricht, dass der Spannungswandler wieder aus geht.
  • Am Akku habe ich einen Abwärtswandler, eingestellt auf 5.1V für USB
  • und daran ein abgeschnittenes USB-Verlängerungskabel, um USB-Geräte einstecken zu können


(Die Skale des Messgerätes im Bild hat die falsche Beschriftung. Der Endausschlag ist bei 60mA)

Der DC/DC-Abwärts-Wandler arbeitet mit einem LM2596. Auch hier gibt es für kleines Geld eine Menge Händler (auch aus Deutschland).


Messungen mit dieser Anordnung ergaben, dass ich noch mindestens 200mAh aus den, normalerweise bereits als Müll bezeichneten, Batterien ziehen kann. Man muss nur aufpassen, dass die so leer gesaugten Batterien gerne mal auslaufen, also nach der Benutzung nicht in der Halterung lassen und fachgerecht entsorgen - diesmal aber wirklich leer!

Samstag, 14. Januar 2017

sinnvolle Verwendung von leeren Batterien, Recycling einmal anders

jedesmal wenn ich in einem Gerät eine Batterie austauschen muss, ärgere ich mich, dass eigentlich noch reichlich Energie in der Primärzelle ist, die ich nun wegwerfen muss.
Vor einiger Zeit fand ich eine interessante Idee zu diesem Thema: Der Joule-Thief. Das ist eine kleine Schaltung, die mit Hilfe einer selbst gewickelten Spule und einem Transistor aus der eigentlich schon ratzeputz leeren Batterie auch noch mit weniger als 1V eine Spannung von über 3V erzeugt. Das reicht sogar für den Betrieb einer weißen LED.

Ich hatte damals etwas rumexperimentiert und die Bauteile in einen leeren Klebestift eingebaut.


 Die Batterie im Bild ist übrigens eine AAA mit 2 selbstgedruckten Ringen als Adapter zu AA.


Die Batterie hat eine Spannung von nur noch 0.85V und die Lampe leuchtet noch!
Eigentlich nur Spielzeug, eine Power-Taschenlampe ist das definitiv nicht. Aber das Experiment beweist, dass die Batterie noch Potential hat.

Dienstag, 17. November 2015

aktuelles Wetter aus der eigenen Wetterstation im Internet mit dem Raspberry Pi

Seit einiger Zeit läuft bei mir eine Wetterstation WS2800IT von Technoline. Die Station misst die Außentemperatur, die Luftfeuchtigkeit, die Wind-Stärke und -Richtung, sowie die Regenmenge. Die zum Teil solarbetriebenen Sensoren stehen auf dem Dach. Alle gemessenen Daten werden übersichtlich auf einem Drahtlos angebundenen Display angezeigt, welches bei mir im Wohnzimmer steht.














Das sieht nicht nur schick aus, sondern ist auch praktisch, da kein Drahtverhau rumliegt. Ein weiteres Feature ist die ebenfalls drahtlose Kommunikation über einen USB-Stick zu einer PC-Software zur Anzeige der von der Station gesammelten historischen Daten (maximal 1700 Messwerte, gespeichert jede 5 Minuten).
Hier müsste ich jedoch regelmäßig den PC anwerfen, um die Daten auszulesen, denn für mehr als eine Woche reicht der Speicher der Station nicht. Auf der Suche nach einer besseren Lösung, (die die Daten kontinuierlich in eine SQL-Datenbank schreiben kann) bin ich auf den Raspberry Pi gestoßen. Da ich ohnehin mal ein Projekt mit diesem kleinen Computer machen wollte, drängte sich die Wetteraufzeichnung geradezu auf.
Ich habe ein Starterpaket mit einem Raspberry Pi B erstanden, die Rechenleistung und Ausstattung reicht vollkommen.













Der Pi wurde mit einer SD-Karte geliefert, die NOOBS enthielt. Das ist ein grafisches Installationsprogramm für verschiedene Betriebssysteme des Pi. Also Maus, Tastatur und TV (über HDMI) angeschlossen und los geht's. Ich wählte Raspbian Jessie, eine aktuelle Debian-Distribution. Da ich für den späteren Betrieb keinen Bildschirm, Tastatur oder Maus benötige (Headless), muss Telnet und SSH laufen. Das ist standardmäßig aktiviert, also habe ich jetzt nur noch folgende Komponenten:
- ein Steckernetzteil
- der Raspberry Pi im Gehäuse
- ein Netzwerkkabel zur Fritzbox
- der USB-Stick der Wetterstation
Nun kommt die Installation des eigentlichen Wetterdaten-Abholprogramms. Ich habe mich für weewx entschieden, weil es schöne Übersichtsgrafiken für Tag, Woche und Jahr erzeugen kann.

Die Installation startete ich über die Anleitung von  Byte Insight, bin dann aber auf die Überholspur gewechselt und habe mir das vorgefertigte Debian-Paket vom weewx-Server gezogen. die Datei weewx_3.2.1-1_all.deb funktionierte tadellos und hinterließ ein funktionsfähiges System, das ohne angemeldeten Benutzer läuft.
Leider gibt es beim Raspberry Pi noch einen Fallstrick. Da die Hardware keine Echtzeituhr besitzt, ist nach dem Einschalten erst mal die falsche Uhrzeit im System. Die Installation von NTP reicht noch nicht, denn Weewx startet, bevor Ntp die aktuelle Uhrzeit hat. Weewxs Rasberry-Pi-Dokumentation geht darauf ein: einfach warten, bis die Uhrzeit aktuell ist. Der dort beschriebene Code muss nach /usr/share/weewx/user/extensions.py
Zur Bereitstellung als Website brauchen wir noch einen Webserver. Das erledigt lighttpd mit einer Portfreigabe von meiner dynamischen Internetadresse rainerbahr.no-ip.biz an den Pi.
Da die Wetterstation die Luftdruckdaten als relativen Luftdruck und nicht, wie von weewx erwartet absoluten Luftdruck sendet, ist dessen Anzeige schlicht falsch. Abhilfe bringt hier eine Änderung in der Konfiguration in /etc/weewx/weewx.conf:

[StdConvert]
target_unit = METRIC
[StdCalibrate]
pressure = pressure - 53

Dienstag, 21. Juli 2015

Freifunk - freies WLAN für Alle

neulich bin ich über ein interessantes Mitmach-Projekt gestolpert: Freifunk
Hier geht es um freie WLAN-Hotspots für Jeden. Die Knoten bieten einen unverschlüsselten Internetzugang, an den sich Jedermann anmelden kann.
In Deutschland ist solch ein Projekt rechtlich nicht einfach durchzuführen, denn jeder, der einen Internetzugang anbietet, haftet für das, was darüber an Daten ausgetauscht wird. Die sogenannte Störerhaftung greift auch dann, wenn man selbst nicht einmal weiß, dass illegale Daten über sein Netz gingen. Abmahnungen und Anzeigen wegen unerlaubtem Anbieten von illegalen Dateien sind wahrscheinlich.
Im Freifunk wird das umgangen, indem jeder Knoten die Daten, die darüber laufen, über eine verschlüsselte VPN-Verbindung zum Freifunk-Backbone leitet und dieser dann als Provider auftritt. Provider sind ausdrücklich von der Haftung ausgeschlossen, sonst wären ja auch T-Online und Konsorten in der Haftung. Durch diesen Trick bekommt derjenige, der sich am Hotspot anmeldet, eine IP-Adresse aus dem Pool des Freifunks, der eigentliche, physikalisch anbietende Knoten erscheint nicht in den Logs.
Bei solchen Projekten muss ich einfach mitmachen! Ihr findet meinen Knoten in der Freifunk-Gruppe Stuttgart als Knoten ffs-rutesheim02. Also, wenn Ihr mal in der Nähe seid, bedient Euch.