Manuel Jasch

Alles rund um Internet & Technik

Tea-Boy - James

Dez 202016

undefined

Für ein Weihnachtsgeschenk kam mir die spontane Idee eines Tea Boys. Dieser soll den Tee überwachen, nach abgelaufener Zeit Bescheid geben, ob der Tee fertig ist und den Teebeutel aus der Tasse ziehen.

Über einen Potentiometer soll dabei die Teezeit eingestellt werden und mit einem Taster der Tee-Beutel abgelassen werden. Nach abgelaufener Zeit soll der Beutel wieder aus der Tasse gezogen werden und mit einem Piezo darauf aufmerksam gemacht werden. Eine LED zeigt dabei die Status an.

Was ich für den Tea Boy benötige:

  • ATtiny
  • LED
  • Widerstände: 150 Ohm, 22 kOhm
  • 2 Micro Taster
  • Mini Servo
  • Potentiometer
  • Piezo

Zum Programmieren des ATtiny benötige ich:

  • Kondensator 10 uF, 16 V
  • Arduino Uno Board

Der ATtiny wird mithilfe eines in-system programmer (ISP) und eines Arduino Uno Board programmiert. Dafür wird der ATtiny, wie hier beschrieben, an das Arduino Uno Board angeschlossen. Der Kondensator muss dabei zwischen Masse und Reset gesteckt werden um zu verhindern, dass der Arduino reseted wird. Dann noch ein ISP Programm auf den Arduino installieren, das richtige Board auswählen und schon kann der ATtiny programmiert werden. Über die Entwicklungssoftware von Arduino kann noch die Frequenz des ATtinys ausgewählt werden. Ich habe bei diesem Projekt 8 Mhz gewählt. Nicht vergessen den Bootloader über die Arduino Software zu brennen. Dieser brennt nicht wirklich einen Bootloader sondern dieser setzt die Fuse-Bits, welche den internen Oszillator auf 8 Mhz wählt.

Der nächste Schritt ist die Programmierung. Dabei habe ich mich von dieser Seite inspirieren lassen, um einen Energiesparmodus zu ermöglichen. Der Autor verspricht dabei 0,12 µA im Standby, welcher aber nur durch einen Reset des Mikrocontrollers beendet werden kann. Nachgemessen wurde es nicht. Der Reset Pin wird dafür über einen 22 kOhm Widerstand auf die Versorgungsspannung und gleichzeitig über einen Taster auf Masse gelegt. Nun führt ein Tastendruck zum Reset des Mikrocontrollers.

Der zweite Taster, der den Tee-Vorgang starten soll, wird an A2 und an die Versorgungsspannung gelegt. Ein zusätzlicher Widerstand zieht den Pin auf Masse, falls der Taster nicht gedrückt ist. Der Tastendruck löst einen Interrupt aus, welcher den Potentiometer abfragt, den Servo-Arm herunter fährt und die angegebene Zeit verweilt.

Wenn der Tee fertig ist soll ein Piezo ertönen. Dafür habe ich mich für die hier beschriebene Methode entschieden. Eine andere Melodie ist dabei ohne Probleme möglich und kann in einem Array angegeben werden. Ich entschied mich für eine einfache Tonfolge.

Die Bauteile werden an die folgenden Pins des ATtiny angeschlossen:

undefined

LED D0 (0)
Servo D1 (1)
Reset Reset
Start A2 (4)
Poti A3 (3)
Piezo A1 (2)

undefined

Und das Programm natürlich:

#include <SoftwareServo.h>
#include <avr/sleep.h>           
#include <avr/interrupt.h>       

#define PIN_POTI  3 //A3
#define PIN_START 4 //A2
#define PIN_SERVO 1 //D1
#define PIN_LED   0 //D0
#define PIN_SPEAKER 2 //A1

#define SERVO_MIN 45
#define SERVO_MAX 135
#define SERVO_POSITIONING_TIME 1600  //in ms

#define MAX_TEA_TIME 780000 // 780 000 = 13 min ; 600 000 ms = 10 min
#define SLEEP_AFTER 20000   //go to sleep after this time (in ms 30 000 ms = 30 s)

#define WAKE_UP_BLINK 2 //wake up or reset
#define WAKE_UP_FREQUENCY 400 //in ms

#define TEA_START_BLINK 3
#define TEA_START_FREQUENCY 300

#define END_BREWING_SHAKING 3 //number of shaking the teabag
#define END_BREWING_SHAKE_DISTANCE 10 //in degree
#define END_BREWING_SHAKE_POSITIONING_TIME 300 //in ms

#define TEA_FINISHED_BLINK 7

#define BODS 7                   //BOD Sleep bit in MCUCR
#define BODSE 2                  //BOD Sleep enable bit in MCUCR

// DEFINITION OF TONES  ==========================================
//       note, period, &  frequency. 
#define  c     3830    // 261 Hz 
#define  d     3400    // 294 Hz 
#define  e     3038    // 329 Hz 
#define  f     2864    // 349 Hz 
#define  g     2550    // 392 Hz 
#define  a     2272    // 440 Hz 
#define  b     2028    // 493 Hz 
#define  C     1912    // 523 Hz 
#define  R     0       // to represent a rest
int melody[] = {  c, R, c, R};
int beats[]  = { 32, 32, 32, 128}; // 32 => 320ms

int MAX_COUNT = sizeof(melody) / 2; // Melody length, for looping. (2 byte)
long tempo = 10000; // Set overall tempo
int pause = 2000; // // Set length of pause between notes
// Loop variable to increase Rest length
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES
// Initialize core variables
int tone_ = 0;
int beat = 0;
long duration  = 0;

//GLOBAL VARIABLES ==============================================
float tea_time, elapsed_time;
bool start, tea_brewing;
uint32_t start_time; 
uint8_t mcucr1, mcucr2, led_brightness, i;
SoftwareServo servo;

// PLAY TONE  ===================================================
void playTone() {
  long elapsed_time = 0;
  if (tone_ > 0) { // if this isn't a Rest beat, while the tone has 
    //  played less long than 'duration', pulse speaker HIGH and LOW
    while (elapsed_time < duration) {
      digitalWrite(PIN_SPEAKER,HIGH);
      delayMicroseconds(tone_ / 2);
      digitalWrite(PIN_SPEAKER, LOW);
      delayMicroseconds(tone_ / 2);
      // Keep track of how long we pulsed
      elapsed_time += (tone_);
    } 
  }
  else { // Rest beat; loop times delay
    for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
      delayMicroseconds(duration);  
    }                                
  }                                 
}

// PLAY SONG ===================================================
bool toggle = false;
void playSong() {
  for (int i=0; i<MAX_COUNT; i++) {
    tone_ = melody[i];
    beat = beats[i];
    duration = beat * tempo; // Set up timing
    // Blink LED
    digitalWrite(PIN_LED, toggle);
    toggle = !toggle;
    
    playTone(); 
    delayMicroseconds(pause);
  }
}

// GO TO DEEP SLEEP ==============================================
void goToSleep(void) {
    // GIMSK |= _BV(INT0);                    //enable INT0
    // MCUCR &= ~(_BV(ISC01) | _BV(ISC00));   //INT0 on low level
    ACSR |= _BV(ACD);                         //disable the analog comparator
    ADCSRA &= ~_BV(ADEN);                     //disable ADC
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    //turn off the brown-out detector.
    //must have an ATtiny45 or ATtiny85 rev C or later for software to be able to disable the BOD.
    //current while sleeping will be <0.5uA if BOD is disabled, <25uA if not.
    cli();
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
    mcucr2 = mcucr1 & ~_BV(BODSE);
    MCUCR = mcucr1;
    MCUCR = mcucr2;
    sei();                         //ensure interrupts enabled so we can wake up again
    sleep_cpu();                   //go to sleep
    cli();                         //wake up here, disable interrupts
    // GIMSK = 0x00;               //disable INT0
    sleep_disable();               
    sei();                         //enable interrupts again (but INT0 is disabled from above)
}

// WRITE SERVO ANGLE  ============================================
int last_angle = 0;
void setServo(uint8_t degree, uint32_t duration) {
  int diff = degree - last_angle;
  diff = abs(diff);
  int w = (float)duration / (float)diff * 0.5 * 1000.0;
  if(last_angle < degree) {
    for (float k = last_angle; k < degree; k+=0.5) {
      servo.write(k); 
      delayMicroseconds(w);
      SoftwareServo::refresh(); 
    }
  } else if (last_angle > degree) {
    for (float k = last_angle; k > degree; k-=0.5) {
      servo.write(k);
      delayMicroseconds(w); 
      SoftwareServo::refresh(); 
    }
  }
  last_angle = degree;
}

// BLINK STATUS LED ==============================================
void blink_led(int num, uint16_t frequency) {
  for(i = 0; i<num; i++){
    digitalWrite(PIN_LED, HIGH);
    delay(frequency);
    digitalWrite(PIN_LED, LOW);
    delay(frequency);
  }
}

// SETUP =========================================================
void setup() {  
  tea_time = 0;
  start = false;
  tea_brewing = false;
  elapsed_time = 0;

  pinMode(PIN_POTI, INPUT);
  pinMode(PIN_START, INPUT);
  pinMode(PIN_SERVO, OUTPUT);
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_SPEAKER, OUTPUT);
  
  servo.attach(PIN_SERVO);
  servo.setMaximumPulse(2100);
  servo.setMinimumPulse(550);
  servo.write(SERVO_MAX);              
  for(int i=0; i<600/20; i++) { //stellzeit
    SoftwareServo::refresh();  
    delay(20); 
  }
  last_angle = SERVO_MAX;
  
  start_time = millis();
  blink_led(WAKE_UP_BLINK, WAKE_UP_FREQUENCY);
  digitalWrite(PIN_LED, HIGH);
  //cli(); 
  //General Interrupt Mask Register, External Interrupts: 0b01000000; Pin Change Interrupts: 0b00100000
  GIMSK = 0b00100000;    // turns on pin change interrupts
  //Pin Change Mask Register 
  PCMSK = 0b00010000;    // turn on interrupts on pin PB4
  sei();                 // enables interrupts (bzw. _SEI();)
}

void loop() {
  if (tea_brewing) {
    elapsed_time = millis() - start_time;
    analogWrite(PIN_LED, 60);
    // Check if tea is finished
    if (elapsed_time > tea_time) {
      endRoutine();    
      digitalWrite(PIN_LED, LOW);  
      goToSleep(); 
    }
  } else {
    if (millis() - start_time > SLEEP_AFTER) {
      digitalWrite(PIN_LED, LOW); // TODO auskommentieren? müsste auch ohne aus gehen?! geht nicht aus!
      goToSleep(); 
    }
  }

  if (start) {
    blink_led(TEA_START_BLINK, TEA_START_FREQUENCY);
    startRoutine();
    start = false;
  }
}

//Start the tea procedure
void startRoutine() {
  tea_brewing = true; //set tea making flag
  //get current time value time in ms
  tea_time = (float)MAX_TEA_TIME - (float)MAX_TEA_TIME * analogRead(PIN_POTI) / 1023.0;
  setServo(SERVO_MIN, SERVO_POSITIONING_TIME);
  start_time = millis();  //start time measure
}

//End tea procedure
void endRoutine() {
  tea_brewing = false;
  setServo(SERVO_MAX, SERVO_POSITIONING_TIME);
  //shake arm
  for (i = 0; i < END_BREWING_SHAKING; i++) {
    setServo(SERVO_MAX, END_BREWING_SHAKE_POSITIONING_TIME);
    setServo(SERVO_MAX - END_BREWING_SHAKE_DISTANCE, END_BREWING_SHAKE_POSITIONING_TIME);
  }
  setServo(SERVO_MAX, END_BREWING_SHAKE_POSITIONING_TIME);
  for(int l = 0; l<TEA_FINISHED_BLINK; l++) {
    playSong();
  }
}

//Interrupt Service Routine
ISR(PCINT0_vect)
{
  if (digitalRead(PIN_START)) {  // is start pressed?
    start = true;
  }
}

Das Programm erfüllt seinen Zweck und ist lange nicht optimiert aber es musste schnell gehen damit das Weihnachtsgeschenk rechtzeitig fertig wird :)

Und so sieht das ganze fertig aus.. mit dem Kopf lässt sich die Minutenzahl einstellen. Dabei habe ich darauf geachtet das bei sechs Minuten die Mittelstellung erreicht ist. Das Video wurde gekürzt um die Wartezeit zu verringern.

Quellen:

Energiesparmodus: http://www.arduino-hausautomation.de/2014/emils-ampel-attiny45-im-tiefschlaf/

ATtiny programmieren: https://www.frag-duino.de/index.php/maker-faq/37-atmel-attiny-85-mit-arduino-arduinoisp-flashen-und-programmieren

Farbcodierung bei Widerständen: http://www.calculino.com/de/elektronik/ohmscher-widerstand_rechner.html

Vorwiderstand LED: http://www.elektronik-kompendium.de/sites/bau/1109111.htm

Piezo Ansteuerung: https://www.arduino.cc/en/Tutorial/PlayMelody

  • Permalink
  • Kommentare

Thailand, Laos und Cambodscha Teil 2

Okt 052015

Weiter ging es zu den 4000 Inseln am Mekong. Die letzten Flussdelfine, Wasserfälle und die atemberaubende Natur bewundert.

undefined

Danach ging es weiter Richtung Cambodscha. Natürlich mussten wir zum Angkor Wat nach Siem Riep. Der beeindruckendste Tempel war derjenige in dem 2001, Lara Croft: Tomb Raider, gedreht wurde. Seit Jahrhunderten sind nun die Tempelanlagen verlassen und gigantische Bäume sind in dieser Zeit gewachsen.

undefined

Nach zwei weiteren Stopps in den Süden von Cambodscha, Sihanoukville und Battambang, sind wir wieder zurück nach Bangkok gekommen. Thai Boxing durfte dann natürlich auch nicht fehlen.

undefined

  • Permalink
  • Kommentare

Thailand, Laos und Cambodscha Teil 1

Okt 052015

Nach gut zwei Monaten sind wir wieder zurück. In Bangkok hat alles gestartet. Von dort ging es in den Norden Thailands wobei wir wunderschöne Momente hatten auf Bootstouren, beim Schnorcheln und beim erklimmen etlicher Aussichtspunkte. Unter anderem waren wir auf den Inseln Koh Samui, Koh Tao, Khao Ping Kan, Koh Phi Phi und viele weitere Inseln.undefined

undefined

Weiter ging es in das Kletterparadies Krabi und von dort mit dem Flugzeug in den Süden nach Chiang Mai.

undefined

Kochkurs und Trekking Tour standen dabei auf dem Programm. In Pai konnte man dagegen relaxen. Weiter ging es Richtung Laos über Chiang Rai mit den etwas anderen Tempeln. Kajaktouren, Tubing sowie Klettern war in Laos geboten. Auch gab es schöne Wasserfälle.

undefined

In Vang Vieng gab es sowohl sehr ruhige Plätzchen wie auch Party.

undefined 

undefined

  • Permalink
  • Kommentare

Mein 3D Drucker

Jul 222015

Nachdem ich die Adhäsion weitestgehend in den Griff bekommen habe folgt hier mal ein Video meines Druckers. Was noch fehlt ist ein Z-Endschalter welcher das Druckbett abtasten kann dafür habe ich noch keinen günstigen Sensor gefunden.

Jetzt geht es erst mal in den Urlaub für 2 Monate nach Thailand.

  • Permalink
  • Kommentare

Mein 3D Drucker - Druckversuche

Jun 072015

Kinematik Problem

Das Problem mit der Kinematik, welches dadurch zustande kommt das der Y-Schlitten von der Bewegung in Z-Richtung abhängt, konnte ich leider nicht perfekt lösen.
Ein Forumsbeitrag bei Repetier half mir etwas weiter. In der Repetier-Firmware gibt es die Header Datei motion.h in der bei der Schrittgenerierung, die Korrektur implementiert werden sollte.
In der Theorie sollte in der Funktion startZStep() die Schritte für die y-Achse gemacht werden. Versucht wurde es über ein Modulo-Operator alle 'X' Z-Schritte einen Y-Schritt zu machen.
Leider funktionierte es nicht wie gewollt. Wer noch eine Idee diesbezüglich hat bitte melden.

Schlussendlich wurde die Korrektur bei der GCode Interpretation implementiert.
Der einzige Nachteil liegt darin das bei Bewegungen welche durch M- oder G-Befehle getriggert werden die Korrektur nicht greift.
Wie zum Beispiel bei dem Befehl Homing aller Achsen G28. Dabei fährt der Drucker über den Endstop der Y-Achse falls die Z-Achse noch eines Resthöhe hat.

Weitere Testdrucke

Nun konnte ich endlich mit ein paar Testdrucken anfangen. Nach einigen erfolglosen Versuchen den Slic3r in den Griff zu bekommen versuchte ich es mit dem Cura-Slicer.
Der Cura-Slicer ist etwas schneller im Slicen und auch schneller beim Drucken bei gleichem Infill. Das kommt daher das er als Infill Überkreuzte Geraden extrudiert anstatt eine Wabenstruktur wie beim Slic3r. Dabei muss der Drucker nicht für jede Seitenwand einer Wabe beschleunigen und abbremsen. 

Im Folgenden Bild sieht man der Verlauf der Druckversuche:

undefined

Unten links waren die ersten Versuche, unten rechts wurde begonnen mit dem Infill zu experimentieren, oben rechts wurde auf Cura-Slicer umgestellt. Wie man sehen kann gibt es noch Probleme mit dem Durchfluss. Aktuell habe ich noch Probleme mit dem Füllgrad der obersten Schicht. Es kommt zu keiner Überlappung der einzelnen gedruckten Linien. Auch ist die Adhäsion der ersten Schicht noch ein Problem.

 

  • Permalink
  • Kommentare
k
← Ältere Posts