Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
M.V.P.
Published © GPL3+

Automatic-watch WINDER with ATtiny85

Mechanical automatic watches have 2-3 days autonomy. Now ATtiny85 can keep them ready, whenever you want!

BeginnerFull instructions provided6 hours1,001
Automatic-watch WINDER with ATtiny85

Things used in this project

Hardware components

ATtiny85
Microchip ATtiny85
×1
BOOST-DRV8711 DRV8711 Stepper Motor Driver BoosterPack
Texas Instruments BOOST-DRV8711 DRV8711 Stepper Motor Driver BoosterPack
×1
DC motor (generic)
×1
Linear Regulator with Adjustable Output
Linear Regulator with Adjustable Output
×1
Resistor 220 ohm
Resistor 220 ohm
×2
Through Hole Resistor, 680 ohm
Through Hole Resistor, 680 ohm
×1
LED (generic)
LED (generic)
×2

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Banggood: Double-Side Prototype PCB Universal Printed Circuit Board - 20x80mm

Story

Read more

Schematics

Automatic watch winder by ATtiny85

Code

ATTiny_Automatic-watch-winderV6.ino

Arduino
/*  ATtiny85 module controls using driver DRV8871, a DC motor (9V) to wind up to 4 automatic watches.
 *   The motor accelerates, rotates at Pwmax speed in one sense, deccelerates and stops. 
 *   Then changes the sense an repeats the process.
 *   The cycles are repeated maxrun times. Then the whole process resumes after a psec (sec.) pause.
 *   Author: M.V. https://www.hackster.io/M-V-P
 *  Sketch for ATtiny85. Based on the Digispark (Use Digispark Default 16.5 MHz), no port select.
 *  Compile, Upload and then connect ATtiny85 module to USB cable.
 *  Programming ATtiny85 chip placed on Mini Usb MCU Development Board:
 *  https://www.banggood.com/ATTINY85-Mini-Usb-MCU-Development-Board-For-Arduino-p-971122.html?rmmds=myorder&cur_warehouse=CN
 *  - Adapter 6 pins to 8 pins (home made!)
 *  - USBASP 2.0 USB to SPI adapter: 
 *  https://www.optimusdigital.ro/ro/programatoare/143-programator-avr-usbasp.html?search_query=programator&results=71
 * Select board: Digispark (default 16.5 MHz) or ATtiny/processor Attiny85/clock 8 MHz (internal)
 * Select programmer: USBasp
 *     Sketck/Upload Using Programmer (CTRL+SHIFT+U)
 *     it works, but a warning appears:
 *    avrdude: warning: cannot set sck period. please check for usbasp firmware update. 
 * BETTER option: -Tools/Burn bootloader for ATtiny/processor Attiny85/clock 8 MHz (internal)
 *         - change the board: Digispark (default 16.5 MHz)
 *         - Sketck/Upload Using Programmer (CTRL+SHIFT+U)
 *  a) Using the ATtiny85 module: At POWER ON, the motor start running at full speed, until
 *  ATtiny85 module takes control. This means the first run has a different period than the next ones.
 *  All next cycles start when ATtiny85 wakes up after ~12H with progressive speed-up!
 *  b) Using ATtiny85 alone, the boot period is absent and MCU takes control from POWER-UP, slowly!
 * {ATtiny85 alone pins: 1=PB5, 2=PB3,ADC3, 3=PB4,ADC2, 4=GND, 5=PB0,MOSI,SDA, 6=PB1,MISO, 7=PB2,SCK,SCL, 8=VCC}
 * 
 * ATtiny85 module (9V VIN)    DRV8871 Brushed DC Motor Driver (6.5-35V)
 * PB3                       = INT1 
 * PB4                       = INT2
 *  ATtiny PB4 = Power for the driver (pin: 20mA = sufficient) 
 * 
 *  This work is licensed under the Creative Commons Attribution-       *
 * ShareAlike 3.0 Unported License. To view a copy of this license,     *
 * visit http://creativecommons.org/licenses/by-sa/3.0/ or send a       *
 * letter to Creative Commons, 171 Second Street, Suite 300,            *
 * San Francisco, California, 94105, USA.                               *
 * v6: adjusted watches rewind time
 * Sketch uses 3046 bytes (50%) of program storage space. Maximum is 6012 bytes.
 * Global variables use 86 bytes of dynamic memory.
 */
 


#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI
// https://github.com/digistump/DigistumpArduino/tree/master/digistump-avr/libraries/DigisparkTinySoftPwm : 
#include <TinySoftPwm.h>                
// Utility sleep macros
// https://gist.github.com/JChristensen/5616922 :
#include <avr/sleep.h>
#include <avr/wdt.h>                    //Needed to enable/disable watch dog timer
#define adc_disable() (ADCSRA &= ~(1<<ADEN)) // disable ADC (before power-off)
#define adc_enable()  (ADCSRA |=  (1<<ADEN)) // re-enable ADC

#define LED_BUILTIN PB1         // 
#define DRV1 PB3                // 
#define DRV2 PB4                // 
/*  DRV1    DRV2    = motor driver command pins
 *    0      0      => Coast (sleep after 1ms)
 *    0      1      => Reverse max speed  (PWM on DRV2)     
 *    1      0      => Forward max speed  (PWM on DRV1)
 *    1      1      => Brake, with low decay  
 */ 

unsigned int psec=43640;           // Period of the process = pause(~sec): 12h period (uint32_t<65535 !)
uint32_t StartStep=0, StartRot=0;  // Time counters
uint32_t runit, maxrun=8;          // rotate maxrun times  (10 => ~30 min)
int Pwm, Pwmin=60, Pwmax=255;      // Max speed from 255 possible
int  Dir, Sense;                   // Rotation direction: increasing/decreasing speed, and Sense: anti/clockwise
bool hold=false;

void setup()
{
  pinMode(DRV1,OUTPUT);     // Direction pin
  pinMode(DRV2,OUTPUT);     // Speed pin
 psec-=100*maxrun;        // Substract duration of rewind process (~100s: can be adjusted!)         
 Dir=-1;                  // 
 Sense=1;
 Pwm=Pwmin;
 runit=0;
 
  /***********************************************************/
  /* Call TinySoftPwm_process() with a period of 60 us       */
  /* The PWM frequency = 128 x 60 # 7.7 ms -> F # 130Hz      */
  /* 128 is the first argument passed to TinySoftPwm_begin() */
  /***********************************************************/
  TinySoftPwm_begin(128, 0); /* 128 x TinySoftPwm_process() calls before overlap (Frequency tuning), 0 = PWM init for all declared pins */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sleep mode is set here
  sleep_enable();                       // enables the sleep bit in the mcucr register, so sleep is possible
}


//================================================================================================
void loop(){
  static uint32_t StartUs=micros();
  static uint32_t StartMs=millis();
  /***********************************************************/
  /* Call TinySoftPwm_process() with a period of 60 us       */
  /* The PWM frequency = 128 x 60 # 7.7 ms -> F # 130Hz      */
  /* 128 is the first argument passed to TinySoftPwm_begin() */
  /***********************************************************/
  if((micros() - StartUs) >= 60)
  { StartUs=micros();
   /* This function shall be called periodically, like here, based on micros() */
    TinySoftPwm_process(); 
  }
   
  /*************************************************************/
  /* Send commands to the motor with a period of 100 ms and    */
  /* ~60s hold on max speed => ~ 100.5s cycle period                */  
  /*************************************************************/
  if((millis()-StartMs) >= 100)
  {
    digitalWrite(LED_BUILTIN, HIGH);// Automatically reset after ON!!
    StartMs=millis();               // reset timer    
    Pwm-=Dir; /* increment or decrement PWM depending of sign of Dir */


    if(Pwm > Pwmax) {               /*If max speed is surpassed: */
      Pwm=Pwmax;                    /* Keep the max speed        */
      Dir=-1;                       /* Hold motor as accelerating*/
      if (hold==false){             /* First time Pwm>Pwmax :    */
        StartRot=millis();          /*  mark the moment,         */
        hold=true;                  /*  set max speed indicator. */
      }
    }
    
    if (millis()-StartRot > 60000 && hold==true){ /*Keep rotating at max speed for 60 s */
      Dir=1;                        /* After 60s: start reducing speed                  */
      hold=false;                   /* reset the max. indicator                         */
    }

    if (Sense==1){
       TinySoftPwm_analogWrite(DRV1, Pwm); /* Update driver speed control */
    }
    else{
       TinySoftPwm_analogWrite(DRV2, Pwm); /* Update driver speed control */
    }

    if(Pwm<=Pwmin){        /* if PWM reaches the minimum: */
      Pwm=Pwmin;            
      Dir=-1;              /* start increasing speed      */
      Sense=-Sense;        /* and change rotation sense   */
      runit++;             /* Increase runs counter       */
      digitalWrite(LED_BUILTIN, LOW);
      waitsec(2);          /* A 2s break for the motor    */
    }
  }
  
  /* Keep runing maxrun times, then sleep: */
  if(runit>=maxrun){
    sleepsec(psec);          /* Have a pause/sleep of psec seconds */
    runit=0;                 /* Reset cycle counter                */
  }
   
}

//================================================================================================
//This runs each time the watch dog wakes us up from sleep
ISR(WDT_vect) {
  // The MCU wakes-up after every 8 sec. Then goes again to sleep if necessary!
}

//================================================================================================
void sleepsec(unsigned int seconds){
  // Tested for max. 65635 sec. = 18.15 hours
  volatile uint8_t i, j, imx, jmx;
  unsigned int cycles, rems;
  seconds -= seconds/100;     // Correction for Led ON duration (here adjust for 12H period!)
  cycles = seconds/9;         // Each sleep cycle lasts ~8.8s
  rems = seconds % 9;         // Seconds after cycles*9
  waitsec(rems);              // Pause MCU for rems seconds
  imx=cycles/256;             // Keep it in blocks of 255
  jmx=cycles % 256;           // and reminder of these blocks
  // AtTiny85 will sleep for "seconds", but each cycle is about 8.8s
  for (j=0; j<jmx; j++){
      setup_watchdog(9);      //Setup watchdog to go off (code 9 -> 8s sec + code time)
      sleep_mode();           //Go to sleep! Wake up n sec later and work
      digitalWrite(LED_BUILTIN, HIGH); // Every 8s, wake-up and Led ON
      delay(80);
      digitalWrite(LED_BUILTIN, LOW);  
  }
  for (i=0; i<imx; i++){
    for (j=0; j<255; j++){
      setup_watchdog(9);      //Setup watchdog to go off (code 9 -> 8s sec + code time)
      sleep_mode();           //Go to sleep! Wake up n sec later and work
      digitalWrite(LED_BUILTIN, HIGH);
      delay(80);
      digitalWrite(LED_BUILTIN, LOW);  
    }
  }

}

//================================================================================================
void waitsec(int seconds){
  unsigned long startsec=millis()/1000;
  while (millis()/1000-startsec < seconds){
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    // Do nothing, just wait to pass "seconds".
  }
}

//================================================================================================
void setup_watchdog(int timerPrescaler) {
    //Sets the watchdog timer to wake up, but no reset, after:
    //0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
    //6=1sec, 7=2sec, 8=4sec, 9=8sec
    //From: http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
  if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings
  byte bb = timerPrescaler & 7; 
  if (timerPrescaler > 7) bb |= (1<<5); //Set the special 5th bit if necessary
  //This order of commands is important and cannot be combined
  MCUSR &= ~(1<<WDRF);            //Clear the watchdog reset
  WDTCR |= (1<<WDCE) | (1<<WDE);  //Set WD_change enable, set WD enable
  WDTCR = bb;                     //Set new watchdog timeout value
  WDTCR |= _BV(WDIE);             //Set the interrupt enable, this will keep unit from resetting after each int
}

Credits

M.V.P.

M.V.P.

7 projects • 46 followers
Arduino: Amazing, useful and rewarding hobby for some!

Comments