Feb 22, 2012

Easy Driver With Arduino

 
Part I              (Maximum Stepper Speed)
Go to Part II   (Adjust max Stepper Current)
Go to Part III (Sleep an Enable)
 
 
 
Click Here to see and download the design of a PCB implementing this circuit.
 







An important thing to knwo about your stepper, is the maximum speed at witch it can be driven.
To know this, you can follow this LINK;

you need to knwo the specs of your stepper.
For my stepper:

  Voltage:                                  12v
  Inductance:                             46+-20% mH           then i take 50
  Steps per revolution:              Step Angle = 1.8º     360 / 1.8 = 200 Steps/rev
  Current:                                  0.33 A                       i take 135 mA because with easy
                                                                                   driver you can adjust the maximun current
                                                                                   with the potenciometer
                                                                                   (i will explain this in a future post)

then following the calculs from the previous link:

  T = 50 * 0.135 * 2 / 12 = 1.125 ms for a single Step

The default configuration of EasyDriver is eighth stepping then:

  T = 1.125 / 8 = 0.140 ms = 140 us for a single Step

given this we take a prescaler of 128 for the Timer2 (see coments in the code)

  128 / 16000000 = 0.000008 s-1 = 8 us-1         ( the frequency of the processor in the
                                                                              Arduino Uno is 16 MHz)

  Timer2 actulizes its counter every 8 us then, 140 / 8 = 17.5

then we have to program the counter for the Timer2 to 18 ( 18 = ceil(17.5) ), because of that we have a success of Timer2 every 18 * 8us = 144 us

and the speed for our stepper will be: 144 us * 1600 Steps/Revolution = 0.2304 s/Revolution

that is 1 / 0.2304 = 4.34 Revolutions / s , wich is an aproximation to the real speed, because there is an extra delay between steps introduced by the code itself  ( maybe 20 us extra delay ).


To see this working copy the following code to Arduino IDE, set the proper value for  this variables
      microSteping
      stepsPerRev
      l
      v
      iMax
 and run it.

This program also implements acceleration-desacceleration. You can change the amount of acceleration, changing the value of  'accelDuring' variable ( for example it accelerates during 10 steps the run at constant speed and finaly it desaccelerates during 10 steps ).
If you don't want acceleration set it to 0 ( accepted values are 0 - 255 ).



//http://www.daycounter.com/Calculators/Stepper-Motor-Calculator.phtml
//Rev/sec = V/(L*2*Imax)/(steps/rev)  (Imax en A, L en mili Herns) p.e. 12/(46*2*0.33)/200
//T = L*Imax*2/V (T miliSegons/step)
  
  #define DIR_PIN 7
  #define STEP_PIN 6
  #define sbi(sfr,bit)  ( sfr |=   _BV(bit)  )         // a l'antiga manera. cbi() i sbi() son funcions obsoletes
  #define cbi(sfr,bit)  ( sfr &= ~(_BV(bit)) )  
  //  MS1  MS2  Resolution
  //  L    L    00 Full step (2 phase)
  //  H    L    10 Half step
  //  L    H    01 Quarter step
  //  H    H    11 Eighth step
  //
  // per defecte a la easy driver MS1 = MS2 = HIGH
  
  float microStepping = 8;                             // Valor establert a la easyDriver ( MS1, MS2 )
  long stepsPerRev = 200;                              // característica del motor
  float l = 50;                                        // = 46 +- 20% (el pitjor dels casos es 55.2) característica del motor
  float v = 12;                                        // característica del motor
  float iMax = 0.135;                                  // mesurat amb el tester (cal dividir el resultat per 2 (hi ha 2 bobines))
                                                       // amb la EasyDriver podem controlar iMax abm el potenciòmetre
                                                       
  float ocr2a;                                         // valor de OCR2A ( que depèn de la velocitat màxima del Stepper )
  int ocr2aIni;                                        // valor inicial de OCR2A donada l'acceleració
  
  int accelDuring = 10;                                // Accelera i desaccelera durant 10 Steps
                                                       
  int downCounter = accelDuring;                       // el nombre d'Steps que falten fins acabar la desacceleració
  int upCounter = accelDuring;                         // el nombre d'Steps que falten fins acabar l'acceleració
  int accelDelta;                                      // valor en que varia OCR2A durant l'acceleració 
                                                       // ( p.e. si ha de pasar de 255 a 19 en 10 steps decremeta de 23 en 23 i começa a 249 )
                                                       
  long stepsLeft = 0;                                  // nombre d'Steps que manque per acabar
  boolean running = false;                             // motor is running?
  
  
  
  void setup() {
    unsigned int usDelay;                              // temps d'espera entre Steps en us
      
    Serial.begin(115200);
    pinMode(DIR_PIN, OUTPUT);
    pinMode(STEP_PIN, OUTPUT);
  
    usDelay = l * 2000 * iMax / v / microStepping;     // temps d'espera entre steps en microsegons  ( T= L*Imax*2/V (T miliSegons/step) )
    ocr2a = ceil((float)usDelay / 8.0);                // l'enter mes proper donat l'interval del Timer2 ( 8 us )
  
    Serial.println(ocr2a);
  
    if(accelDuring > 0){
      accelDelta = (255 - int(ocr2a) ) / accelDuring;  // OCR2A pasarà del enter mes proper a 255 fins a ocr2a
                                                       // el valor que hem calculat per la velocitat màxima
    Serial.println(accelDelta);
      
      if(accelDelta<=0){
        if(ocr2a < 255){                               // la durada de l'acceleració no pot ser tan gran com esperem
          accelDelta =1;
          ocr2aIni = 255;
          accelDuring = 255 - ocr2a;                   // durada màxima de l'acceleració permesa
          
        }else{                                         // ocr2a = 255, no hi pot haver acceleració
          accelDelta = 0;
          accelDuring = 0;
          ocr2aIni = ocr2a;
        }
        
      }else                                            // acceleració segons la definició
        ocr2aIni = ocr2a + accelDelta * accelDuring;   // calcula el OCR2A inicial donada l'acceleració
        
    }else                                              // no hi ha acceleració
      ocr2aIni = ocr2a;
  
    Serial.println(ocr2aIni);
  
    setTimer2(ocr2aIni);
  
    Serial.print("Inter Step Delay: ");
    Serial.print(usDelay);
    Serial.println(" us");
    Serial.print("Max Speed: ");
    Serial.print( 60000000.0 / ((float)stepsPerRev * microStepping * (usDelay)) );
    Serial.println(" RPM");
    Serial.print("Inter Step Delay programat a Timer2: ");
    Serial.println((int( ceil( (float)usDelay / 8.0 ) ))*8);
    Serial.print("Max Speed Real: ");
    Serial.print( 60000000.0 / ((float)stepsPerRev * microStepping * ((int( ceil( (float)usDelay / 8.0 ) ))*8)) );
  }//// 
  
  
  
  long nVoltes = 1;
  long nSteps = nVoltes * stepsPerRev * microStepping;
  

  void loop(){
    if(!running){
 delay(1000);
      driveStepper(nSteps);
      nSteps = -nSteps;
    }
  }////
  
  
  
  
  
  
  void driveStepper(long lnSteps){
    int dir = (lnSteps > 0)? HIGH:LOW;
    digitalWrite(DIR_PIN,dir);
  
    lnSteps = abs(lnSteps);
  
    OCR2A = ocr2aIni;                // reinicialitza el contador (permet acceleració)
    upCounter = accelDuring;
    downCounter = accelDuring;
    running = true;
    stepsLeft = lnSteps;
  }////
  
  
  
  void setTimer2(int lusDelay) {
  
    //http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
    // The ATmega328P has three timers known as Timer 0, Timer 1, and Timer 2
    // Each timer has two output compare registers 
    // Each of the timers has a prescaler that generates the timer clock by dividing
    //         the system clock by a prescale factor such as 1, 8, 64, 256, or 1024
    //         Note that Timer 2 has a different set of prescale values from the other timers
    // The Arduino has a system clock of 16MHz and the timer clock frequency will be
    //         the system clock frequency divided by the prescale factor
    // The timers can also generate interrupts on overflow and/or match against either output compare register
    //
    // TCCRnA and TCCRnB. The Timer/Counter Control Registers hold the main control bits
    //            for the timer. (Note that TCCRnA and TCCRnB do not correspond to the outputs A and B.) 
    // TCCRnA and TCCRnB hold several groups of bits: 
    //     Waveform Generation Mode bits (WGM): these control the overall mode of the timer.
    //         (These bits are split between TCCRnA and TCCRnB.) 
    //     Clock Select bits (CS): these control the clock prescaler 
    //     Compare Match Output A Mode bits (COMnA): these enable/disable/invert output A 
    //     Compare Match Output B Mode bits (COMnB): these enable/disable/invert output B 
    //
    // OCRnA and OCRnB. The Output Compare Registers set the levels at which outputs A and B will be affected
    //
    // TIMSK2 – Timer2 Interrupt Mask Register
    //     OCIE2B: Timer2 Output Compare Match B Interrupt Enable
    //     OCIE2A: Timer2 Output Compare Match A Interrupt Enable
    //     TOIE2:  Timer2 Overflow Interrupt Enable
    //
    // Timer 1 is a 16-bit timer and has additional modes. Timer 2 has different prescaler values
    //
    // The Arduino uses Timer 0 internally for the millis() and delay() functions
    // OC0A pin  6   OC0B pin  5
    // OC1A pin  9   OC1B pin 10
    // OC2A pin 11   OC2B pin  3 
  
    cli();                           // plana 10. Bit 7 Clear interrupts
  
    cbi(TCCR2A, COM2A0 );            // plana 158. Table 17-2. Normal port operation, OC0A disconnected. 
    cbi(TCCR2A, COM2A1 );            // 00 deconecta el pin A del timer2
  
    cbi(TCCR2A, WGM20  );            // TCCR2A Regitre A de control del timer 2
    sbi(TCCR2A, WGM21  );       // plana 160 i 149. Clear Timer on Compare
    cbi(TCCR2B, WGM22  );            // WGM2 = 010 ==> mode of operation Clear Timer on Compare (CTC)
                                     // The counter value TCNT2 increases until TCNT2 == OCR2A, 
                                     // and then counter (TCNT2) is cleared
                                     // repeteix el cilce 0 --> valor ( valor clocks )
  
    sbi(TCCR2B, CS22   );            // TCCR2B Regitre B de control del timer 2
    cbi(TCCR2B, CS21   );       // Plana 162
    sbi(TCCR2B, CS20   );            // CS2 = 101 prescaler = 128 ==> 128/16.000.000 = 0.000008 s-1 = 8us-1
                                     // màxim temps que es pot contar = 8 * 255 = 2040 us
  
    sbi(TIMSK2, OCIE2A  );           // Timer2 Output Compare Match A Interrupt Enable. plana 163
  
    cbi(ASSR, AS2);              // AS2 = 0 clocked from the I/O clock
                                     // plana 164. Asynchronous Status Register
                                     // When AS2 is written to zero, Timer/Counter2 is clocked from the I/O clock, clkI/O.
                                     // When AS2 is written to one, Timer/Counter2 is clocked from a crystal Oscillator
                                     // connected to the Timer Oscillator 1 (TOSC1) pin.    
  
    OCR2A = lusDelay;                //  si p.e. OCR2A = 21 --> 21*0.000008 = 168us-1  . cridem la funcio un cop cada 168 us. plana 162
  
    TCNT2 = 0;                       // reset Timer2 counter
  
    sei();                           // Enable Interrupts
  }////
  
  
  
  void stopTimer2() {
    cbi(TIMSK2, OCIE2A  );           // Timer2 Output Compare Match A Interrupt Disable. plana 163
  }////
  
  
  
                                     
  ISR(TIMER2_COMPA_vect) {           // Timer2 Output Compare Match A Interrupt
                                     // definició dels vectors a Arduino\hardware\tools\avr\avr\include\iom328p.h     
                                     // definitions for ATmega328P
    if(stepsLeft > 0){
      if(stepsLeft == downCounter){  // ha de desaccelerar
        downCounter--;
        OCR2A += accelDelta;         // incrementa el temps entre Steps en CS22:0 us (el proper Step es produirà 8 us mes tard)
      }
      else if(upCounter >0){         // ha d'accelerar
        upCounter--;
        OCR2A -= accelDelta;         // decrementa el temps entre Steps en CS22:0 us (el proper Step es produirà 8 us abans)
      }
      digitalWrite(STEP_PIN, HIGH);  // envia el pols a la EasyDriver per avançar 1 Step
      digitalWrite(STEP_PIN, LOW);   // A low-to-high transition on the STEP input sequences the translator 
                                     // and advances the motor one increment.       
                                     // Minimum STEP Pulse Width 1.0 μs (plana 7 de 'A3967.pdf')
                                     // la intruccio NOP triga 1 Clock ( 0,0625 us = 62.5 ns ) per tant
                                     // la instrucció digitalWrite ha de trigar més de 16 Clocks
                                     // mirar el codi a 'wiring_digital.c'
                                     // segons els forums 'digitalWrite()' sembla trigar entre 6 i 9 us
                                     // depenent de si el port es PWM o no
                                     // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230286016
      stepsLeft--;
    }
    else                             // para el motor
      running = false;
  }////
  
  

Feb 6, 2012

Trheading a function with parameters in Processing

In Processing if you want that a function is executed in a diferent thread, you have just to use the following command:

  thread("myFunc");       // this command creates a new thread and executes myFunc in that thread

  void myFunc(){ ..... }

  The restriction is that 'myFunc' must be void, with no parameters and it could not be declared in a subclass in the sketch (it must be a public function in the pApplet class).

With the following code you could thread a public function inside any class, with any number of parameters:

public void thread(final Metode metode, final Object... parametres) {                 // és el mateix que el thread de Processing, però amb paràmetres
  Thread later = new Thread() {  public void run() {  metode.run(parametres);  }  };  // i a més el Mètode pot pertanyer a qualsevol Clase
  later.start();
}

to use this alternative version of 'thread', you have to do the following:

 Metode resend = new Metode(myObject, "myFunc", byte.class, int.class);
 thread( resend, toSend, mill );
where 'myObject'  is an object of the cass that contains myFunc  (if the function is in in the same class were you declare 'resend' you can use 'this' instead of 'myObject'):
  class myClass{
    .....
    public void myFunc(byte toSend, int mill){ ..... }
    .....
  }

  myClass myObject = new myClass();

you can find 'Metode' class in this post: Metode Class

Nov 18, 2011

Demo 123D Catch

A partir de fotografies el 123D Catch crea un model 3D. Simplement impresionant.

Nov 4, 2011

Send a method / function as a parameter in Processing

To send a method or a function as a parameter you can use the folloing code.
This code is also usable in Android-Processing and Java.

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

///////////////////////////////// Metode class ////////////////////////////// 
class Metode{
  Object obj;             // objecte que conté el mètode que HA de ser PUBLIC
  Method method;
  
  Metode(Object lobj, String lnomMetode, Class... parametres) {
    obj = lobj;
    
    try {
      method = lobj.getClass().getMethod(lnomMetode, parametres);     //parametres = new Class[] {  }  ||  new Class[] { int.class, float.class }
    } catch (NoSuchMethodException nsme) {
      System.err.println("There is no " + lnomMetode + "() method " + "in the class " + obj.getClass().getName());
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }////////
 

 
  Object run(Object... parametres) {
    try {
      return(method.invoke(obj, parametres));                  //parametres = new Object[] {  }  ||  new object[] { 1, 5.1 }
    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.getTargetException().printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return(null);
  }////////  
  
}//end class Metode


///////////////////////////// Example //////////////////////////

A aObj = new A();
C cObj = new C();

void setup(){
  Metode a = new Metode(  aObj, "metA", int.class, float.class  );
  a.run(3, 5.1);  // prints '3 5.1'
  
  rebMetode(  new Metode(  this, "metB")  );  // prints 'hola'
  
  Metode c = new Metode(  cObj, "metC", int.class  );
  int res = (Integer)c.run(2);  // prints 'Has dit: 2 i jo et dic: 12' 
  println(res);  // prints '12'
}


void rebMetode(Metode metode){  metode.run();  }


public void metB(){  println("hola");  }

class A{  A(){}  public void metA(int i, float f){  println(i+" "+f);                                                   }  }
class C{  C(){}  public int  metC(int i)         {  println("Has dit: " + i + " i jo et dic: "+(i+10));  return(i+10);  }  }

////////////////////////////// Example End ///////////////////////////////////



Oct 7, 2011

Ringtone with Android-Processing

If you want to get a ring tone here you have the code:

//http://developer.android.com/reference/android/media/RingtoneManager.html

import android.media.Ringtone;
import android.media.RingtoneManager;
import android.database.Cursor;

RingtoneManager ringManager;
Ringtone ring;
Cursor cursor;

void setup() {
  size(screenWidth, screenHeight, A2D);
  ringManager = new RingtoneManager(this);
  cursor = ringManager.getCursor();
  maxN = cursor.getCount();  
  ring = ringManager.getRingtone(0);  
}


int maxN;
int n = 1;

void draw() {  ;  }


void mouseReleased(){
  if(ring.isPlaying()){
    ring.stop();
    ring = ringManager.getRingtone(n);
    n = (n < maxN-1)? n+1 : 0;
  }else{ 
    ring.play();
  }
}

Enjoy it.

Oct 4, 2011

Set Window (Screen) Bright in Processing-Android

To Set the Window Bright in Processing for Android simply add the following code to your sketch:

void setup(){...}
void draw() {...}

//www.akeric.com/blog/?p=1313

//-----------------------------------------------------------------------------------------
// Override the parent (super) Activity class:
// States onCreate(), onStart(), and onStop() aren't called by the sketch.  Processing is entered at
// the 'onResume()' state, and exits at the 'onPause()' state, so just override them:

void onResume() {
  super.onResume();

  setWindowBright();

  println("RESUMED! (Sketch Entered...)");
}

import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;



void setWindowBright(){
  getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON | LayoutParams.FLAG_TURN_SCREEN_ON);

// to set a diferent bright level (other than default)
//  WindowManager.LayoutParams layoutParams = getWindow().getAttributes();   
//  layoutParams.screenBrightness = 0.8f;                    
//  getWindow().setAttributes(layoutParams);
}

That's all.

Sep 14, 2011

Translucent window en Processing


Per aconseguir una finestra transparent o translúcida en processing:



Aqui teniu el codi:


//http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/
import com.sun.awt.AWTUtilities;  // no necessària amb jre7
import java.awt.*;
import javax.swing.JFrame;

JFrame topFrame = null;
PGraphics pg3D;
int framePosX = 800;
int framePosY = 100;
int frameWidth = 300;
int frameHeight = 300;
float opacitatTopFrame = 1.0f;

public void init(){
  frame.removeNotify();
  frame.setUndecorated(true);
  AWTUtilities.setWindowOpaque(frame, false);
  AWTUtilities.setWindowOpacity(frame, 0.0f);  
  //frame.setOpacity(0.0f);  // amb jre7
  frame.setBackground(new Color(0.0f,0.0f,0.0f,0.0f));                
  frame.setVisible(false);
  frame.setLayout( null );
  frame.addNotify();
  
  GraphicsConfiguration translucencyCapableGC;
  translucencyCapableGC = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();

  topFrame = new JFrame(translucencyCapableGC);
  topFrame.setUndecorated(true);
  //topFrame.setOpacity(opacitatTopFrame); // amb jre7
  AWTUtilities.setWindowOpaque(topFrame, false);
  AWTUtilities.setWindowOpacity(topFrame, opacitatTopFrame);    
  topFrame.setAlwaysOnTop(false);
  topFrame.setLocationRelativeTo(null);
  topFrame.setLocation(framePosX, framePosY);
  topFrame.setSize(frameWidth, frameHeight);
  topFrame.setBackground(new Color(0,0,0,0));
  topFrame.setVisible(true);
  topFrame.setTitle( frame == null? "":frame.getTitle() );
  topFrame.setIconImage( frame.getIconImage() );
  topFrame.setLayout( null ); 
  topFrame.addNotify();
  super.init();
  g.format = ARGB;
  g.setPrimary(false);
}


void setup() {
  size(frameWidth, frameHeight);
  colorMode(RGB,255,255,255,255);
  pg3D = createGraphics(frameWidth, frameHeight,P3D);
  pg3D.colorMode(RGB,255,255,255,255);
}


float angle = 0;

void draw() {
  
   background(0,0,255,135);
   fill(0,255,0,55);
   int mX = MouseInfo.getPointerInfo().getLocation().x-framePosX;
   int mY = MouseInfo.getPointerInfo().getLocation().y-framePosY;
   rectMode(CENTER);
   rect(mX, mY,50,50);
   
   pg3D.beginDraw();
     pg3D.background(0,0,0,0);
     pg3D.stroke(0); 
     pg3D.fill(255,0,0,255);
     pg3D.translate(100,100);   pg3D.rotateZ(angle);   pg3D.rotateX(angle);   pg3D.rotateY(angle);
     pg3D.rectMode(CENTER);
     pg3D.rect(0,0,50,50);
   pg3D.endDraw();
   
   image(pg3D,0,0);
   frame.setVisible(false);
   topFrame.add(this);
   
   angle+=0.02;
}

Sep 7, 2011

Jul 21, 2011

Simple controlador d'un 'Stepper' amb Arduino

Amb els xips L293D i 7400 podem fer un cotrolador sumple d'un motor pas a pas.

Funciona bé, però només podem controlar el motor en 'full step', motiu per el qual a velocitats baixes es produeix 'torque ripple' cosa la qual calenta el motor, augmenta el soroll i en el meu cas, fa que algunes parts de la màquina entrin en resonància (cambiaré aquest controlador per un que suporti 'micro stepping').

l'esquema és el següent:



un sencill programa per Arduino que controla el(s) motor(s):

/*
http://www.tigoe.net/pcomp/code/category/arduinowiring/51
http://www.bricogeek.com/shop/upload/datasheets/arduino/Motor_control_v3.0_sch.pdf
http://arduino.cc/en/Tutorial/MotorKnob
*/

class StepperMotor{
  private:
  ;
  public:
    byte motorPin1;
    byte motorPin2;
    byte topePin1;          // Tope -
    byte topePin2;          // Tope +
    int motorSteps;
    int pasosPermm;
    int actualStep;
    int direccio;
    unsigned long stepsLeft;
    boolean running;
    
    StepperMotor(byte lmotorPin1, byte lmotorPin2, byte ltopePin1, byte ltopePin2, int lmotorSteps, int lpasosPermm){
      motorPin1 = lmotorPin1;
      motorPin2 = lmotorPin2;
      topePin1 = ltopePin1;        // Tope -
      topePin2 = ltopePin2;        // Tope +      
      motorSteps = lmotorSteps;  // Steps per volta. NO Usat
      pasosPermm = lpasosPermm;
      actualStep = 0;
      running = false;
      
      pinMode(motorPin1,OUTPUT);
      pinMode(motorPin2,OUTPUT);
      pinMode(topePin1,INPUT);
      pinMode(topePin2,INPUT);    
    }
    
    void run(float lmm, int ldireccio){
      direccio = ldireccio;
      stepsLeft = lmm * (float)pasosPermm;
      running = true;
    }
    
    void playStep(){
      if(stepsLeft > 0 && pucAvansar(direccio)){
        switch (actualStep) {
          case 0: digitalWrite(motorPin1, LOW);  digitalWrite(motorPin2, HIGH);  break;  // 01
          case 1: digitalWrite(motorPin2, HIGH); digitalWrite(motorPin1, HIGH);  break;  // 11
          case 2: digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW);   break;  // 10
          case 3: digitalWrite(motorPin2, LOW);  digitalWrite(motorPin1, LOW);   break;  // 00
        }
        actualStep += direccio;
        if(actualStep < 0) actualStep = 3; else if(actualStep > 3) actualStep = 0;
        stepsLeft--;
        if(stepsLeft == 0) running = false;
      }
    }
    
    boolean pucAvansar(int direccio){  // Retorna false si no es pot avançar en la dirreció actual
      switch (direccio){
        case -1:
          if(digitalRead(topePin1)) {running =false; return(false);} else return(true);
        case 1:
          if(digitalRead(topePin2)) {running =false; return(false);} else return(true);
      }
    }
    
    void goHome(){
      direccio = -1;
      while(pucAvansar(direccio)){
        switch (actualStep) {
          case 0: digitalWrite(motorPin1, LOW);  digitalWrite(motorPin2, HIGH);  break;  // 01
          case 1: digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, HIGH);  break;  // 11
          case 2: digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW);   break;  // 10
          case 3: digitalWrite(motorPin1, LOW);  digitalWrite(motorPin2, LOW);   break;  // 00
        }
        actualStep += direccio;
        if(actualStep < 0) actualStep = 3; else if(actualStep > 3) actualStep = 0;
        delayMicroseconds(1300);
      }
    }
};





#include <flexitimer2.h> //http://arduino.cc/playground/Main/FlexiTimer2

const int nombreMotors = 3;
StepperMotor motors[nombreMotors] = {StepperMotor( 6,  7, 14, 15, 200,201)
                                    ,StepperMotor( 8,  9, 16, 17, 200,201)
                                    ,StepperMotor(10, 11, 18, 19, 200,201)};

void setup() {
  Serial.begin(115200); // Initialize the Serial port:
  
 // allMotorsHome();
  FlexiTimer2::set(3, 1.0/2000, driveSteppers);  // equivalent a delayMicroseconds(1500);
  FlexiTimer2::start();
}


void loop() {
  char eix = '  ';
  int mm;

  mm = procesaComandaSerie(&eix);
  if(mm !=0){
    switch(eix){
      case 'x':  case 'X':
        motors[0].run(abs(mm), mm/abs(mm)); break;
      case 'y': case 'Y':
        motors[1].run(abs(mm), mm/abs(mm)); break;
      case 'z': case 'Z':
        motors[2].run(abs(mm), mm/abs(mm)); break;
    }
  }
}


void allMotorsHome(){
  
  for(int i = 0; i < nombreMotors; i++){
    motors[i].goHome();
  }
}


void driveSteppers(){
  for(int i = 0; i < nombreMotors; i++){
    motors[i].playStep();
  }
}


   


int procesaComandaSerie(char *eix){
  String comanda = "";
  int retorn = 0;
  
  comanda = getSerialCommand();
  if(comanda.length()>0){
    Serial.println("#"+comanda+"#");
    *eix = comanda.charAt(0);
    Serial.println(*eix);
    comanda = comanda.substring(1);
    retorn = strToInt(comanda);
  }
  return(retorn);
}

String getSerialCommand(){
  String retorn="";
  char llegit;
     
  if(Serial.available()){
    llegit = Serial.read();
    while (llegit != 32){      
      if(Serial.available()){
        retorn += llegit;
        llegit = Serial.read();        
      }
    }
  }
  return(retorn);
}


int strToInt(String sNum){
  int retorn = 0;
  int pes = 1;
  
  for(int n=sNum.length()-1; n>=0; n--){
    if(sNum.charAt(n) == '-'){
      retorn *= -1;
    }else{
      retorn += (byte(sNum.charAt(n))-48)*pes;
      pes *= 10;
    }
  }
  Serial.println(retorn);
  return(retorn);
}

aques petit programa acepta comandes des de la consola sèrie de la forma 'x100 y-20 z300 ' (els espais son imprescindibles) que fa avançar 100mm en X -20mm en Y i 300mm en Z. donada les caractrística de la màquina (en el meu cas 'pasosPermm = 201' pasos per mm).

Jul 6, 2011

Projecte de la meva CNC

Aquest es el meu projecte de màquina CNC.



El model està fet amb Autodesk 123D (després d'haver patit multiples penjades i fins i tot la pèrdua d'un fitxer. espero que en futures Beta o en la verssió definitiva arreclin aquests problemes).

De moment tinc fets els controladors dels motors pas a pas i l'eix X i funcionen. Com a controlador uso una Arduino Uno convencional.



El següent pas serà l'eix Y, el Z i el software (tinc previst usar el 'ReplicatorG').

Quan funcionin els eixos Y i Z publicaré tota la documentació (ara mateix tinc dubtes fonamentats de que tal com els he dissenyat funcionin, però bé ja ho corregiré).

Mira la Nova verssió

Jun 27, 2011

Autdesk 123D, free 3D CAD

Autodesk 123D és un fantastic, simple i gratuit programa de disseny 3D. Segueix l'enllaç:

Exemple de peça creat en 5 minuts:


No és tant potent com Blender ni incorpora els refinaments de Meshlab, però el interface és extremament simple, intuitiu i precis (es nota que es d'Autodesk).

Pot exportar a format STL.

Apr 29, 2011

Fantastic i divertit exemple en Processing



Segueix l'enllaç:

Geotagging photos




En geoetiquetar les fotografies, magatzamem en la informació EXIF de cada fotografia les coordenades geogràfiques del lloc on es va fer la foto (s'ha acabat allò de, on caram varem fer aquesta foto).

Des del 2009 jo geoetiqueto totes les meves fotografies.

Actualment uso el i-gotU GT-600 (Pàgina oficial de i-gotU) de 37 g de pès.


El podeu comprar, per exemple a PIXmania per uns 60€  (busqueu i-gotu).

Aquest aparell funciona enregistrant les coodenades GPS i l'hora cada 30s (aquest interval es configurable). L'unic que hem de fer per geoetiquetar les fotografies es executar el software del i-gotU que simplement enparella les fotografies amb les coodenades GPS donada la hora en que es va fer la fotografia.

Amb aquest GPS jo he magatzemat les dades de una setmana sense problemes (Aquest es l'avantatge de la desconnexió automàtica quan no et mous).


Finalment la parella ideal per organitzar les fotografies geoetiquetades (per mi) es el Google Picasa.








Apr 14, 2011

BarCode reading and writing with Processing



Mira aquest enllaç, però si el compiles tu, tindràs una verssió mes actualitzada.


// http://code.google.com/p/zxing/wiki/GettingStarted
// http://blog.makezine.com/archive/2011/03/codebox-use-qr-codes-in-processing.html
// http://code.google.com/p/zxing/wiki/DeveloperNotes
// http://zxing.org/w/docs/javadoc/index.html
// http://code.google.com/p/zxing/source/browse/trunk#trunk%2Fcore%2Fsrc
// http://zxing.org/w/docs/javadoc/com/google/zxing/client/j2se/package-frame.html
// http://code.google.com/p/zxing/source/browse/trunk#trunk%2Fjavase%2Fsrc%2Fcom%2Fgoogle%2Fzxing%2Fclient%2Fj2se
// http://code.google.com/p/zxing/source/browse/trunk/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java




import com.google.zxing.*;
import java.awt.image.BufferedImage;


int SIZE = 400;


//com.google.zxing.Writer writer = new com.google.zxing.qrcode.QRCodeWriter(); 
//http://zxing.org/w/docs/javadoc/index.html
//com.google.zxing.Reader reader = new com.google.zxing.qrcode.QRCodeReader();
MultiFormatWriter writer = new MultiFormatWriter();
MultiFormatReader reader = new MultiFormatReader();
BitMatrix QRBitMatrix = new BitMatrix(SIZE, SIZE);
BarcodeFormat format = BarcodeFormat.QR_CODE;
// QR_CODE
// DATA_MATRIX
// UPC_E
// UPC_A
// EAN_8
// EAN_13
// CODE_128
// CODE_39       ATENCIÓ    Només Numèric
// ITF






void setup(){
size(SIZE, SIZE);


PImage img = new PImage(SIZE, SIZE);
String codi = "Hola";//println(codi.length());


img = codificaBarCcode(codi, format, SIZE, SIZE);


image(img, 0, 0);
img.save(savePath("data\\QR.jpg"));


println(decodificaBarCode(img));
}






PImage codificaBarCcode(String codi, BarcodeFormat format, int ample, int alt){
color negre = color(0), blanc = color(255);
PImage img = new PImage(ample, alt);
Hashtable hints = new Hashtable(1);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");

try {
QRBitMatrix = writer.encode(codi, format, ample, alt, hints);
} catch (Exception e) {println(e.toString());}

int width = QRBitMatrix.getWidth();
int height = QRBitMatrix.getHeight();

for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
if(QRBitMatrix.get(x, y)) img.set(x, y, negre); else img.set(x, y, blanc);
}
}
return(img);
}




String decodificaBarCode(PImage img){
String retorn = "";
Result result = null;

try {
LuminanceSource source = new BufferedImageLuminanceSource((BufferedImage)img.getImage());
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
result = reader.decode(bitmap);
retorn = result.getText();
if (retorn != null) {
//println(result.getText());
ResultPoint[] points = result.getResultPoints();

for (int i = 0; i < points.length; i++) {
fill(#ff8c00);
ellipse(points[i].getX(), points[i].getY(), 20,20);
}
}
} catch (Exception e) {println(e.toString());}

return(retorn);
}

Mar 18, 2011

Kinect's OpenNi Wrapper for Processing

SimpleOpenNI és una llibreria per Processing que permet usar la Kinect des de Processing.
Segueix l'enllaç:

Aquest programa (que es la modificació d'un dels exemples de SimpleOpenNI), es un prototipus per scanejar en color una imatge en 3D .


import processing.opengl.*;

import SimpleOpenNI.*;

SimpleOpenNI context;
float z =0.3f;
float ox = radians(180);
float oy = radians(0);

float rotx = radians(180), roty = radians(0), rotz = 0, incX = 0, incY = 0, incZ = 0;
final float RATE = 0.01; // Velocitat de rotació
int despX=14, despY=56; //24, 48 -22 +56
float VE=0.001;
PImage rgbTrans = new PImage(640,480);


void setup(){
size(1024,768,OPENGL);
context = new SimpleOpenNI(this);

context.setMirror(false);
context.enableDepth();
context.enableRGB();

smooth();
perspective(95, float(width)/float(height), 10, 150000);
//context.update();
}



void draw(){
int[] depthMap = context.depthMap();
int steps = 2; // to speed up the drawing, draw every third point
int index;
int i;
float x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4;

PVector realWorldPoint, projectivePoint = new PVector();

background(0,0,0); translate(width/2, height/2, 0); rotateX(rotx); rotateY(roty); scale(z);
translate(0,0,-1000); // set the rotation center of the scene 1000 infront of the camera
println(frameRate);

context.update();
rgbTrans.copy(context.rgbImage(),
                       despX,despY, context.rgbImage().width, context.rgbImage().height,
                               0,        0, context.rgbImage().width, context.rgbImage().height);

for(int y=0;y < context.depthHeight()-steps;y+=steps){
for(int x=0;x < context.depthWidth()-steps;x+=steps) {
beginShape(QUADS);
index = x + y * context.depthWidth();
if(depthMap[index] > 0){
fill(rgbTrans.pixels[index]); noStroke();

i = index; realWorldPoint = context.depthMapRealWorld()[i];
x1 = realWorldPoint.x; y1 = realWorldPoint.y; z1 = realWorldPoint.z;

i = index+steps; realWorldPoint = context.depthMapRealWorld()[i];
x2 = realWorldPoint.x; y2 = realWorldPoint.y; z2 = realWorldPoint.z;

i = x+steps + (y+steps) * context.depthWidth(); realWorldPoint = context.depthMapRealWorld()[i];
x3 = realWorldPoint.x; y3 = realWorldPoint.y; z3 = realWorldPoint.z;

i = x + (y+steps) * context.depthWidth(); realWorldPoint = context.depthMapRealWorld()[i];
x4 = realWorldPoint.x; y4 = realWorldPoint.y; z4 = realWorldPoint.z;

if(z1 >0 && z2 >0 && z3 >0 && z4 >0){
vertex(x1,y1,z1); vertex(x2,y2,z2); vertex(x3,y3,z3); vertex(x4,y4,z4);
}
// realWorldPoint = context.depthMapRealWorld()[index];
////context.convertRealWorldToProjective(realWorldPoint,projectivePoint);
////stroke(context.rgbImage().pixels[(int)(projectivePoint.x + projectivePoint.y * context.depthWidth())]);
// stroke(rgbTrans.pixels[index]);
// point(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z); // make realworld z negative, in the 3d
//drawing coordsystem +z points in the direction of the eye
}
endShape(CLOSE);
}
}
}


void mouseDragged() {
if(mouseButton == LEFT){
incX = (mouseY-pmouseY) * RATE;
incY = (mouseX-pmouseX) * RATE;
rotx -= incX;
roty -= incY;
}else{
incX = (mouseY-pmouseY) * RATE*100;
incY = (mouseX-pmouseX) * RATE*100;
despX +=incY;
despY +=incX;
println(despX+" "+despY);
}
}


void keyPressed(){
switch(keyCode) {
case UP:
z += VE;
break;
case DOWN:
z -= VE;
break;
}
}