Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Masahiro MizunoTakayuki Saito
Published © GPL3+

REFLO Air XY Table

REFLO Air is a compact, open, and smart PCB reflow machine. XY table is option tool for REFLO Air.

IntermediateFull instructions provided5 hours928
REFLO Air XY Table

Story

Read more

Custom parts and enclosures

XY USB gerber

XY STEPM gerber

Schematics

Schematic

Schematic#2

Code

REFLO XY code

Arduino
// USB STATION 328
// ============================================
// USB STATION [ATMEGA328p 32kRAM, 2kROM]
//  Language Reference http://arduino.cc/en/Reference/HomePage
//  https://www.circuitsathome.com/arduino_usb_host_shield_projects/
// ============================================
//  INCLUDE FILES
// ============================================
  #include <EEPROM.h>
  #include <SoftwareSerial.h>    // UART
  #include <SPI.h>               // MAX3421E
  #include "FastLED.h"           // RGB LED  
  #include <hidboot.h>           // usb      
// ============================================
//  VARIABLEs
// ============================================ 
  CRGB leds[1];                  // RGB LED buffer  
  SoftwareSerial monit(5, 6);    // softUART pin setup for Serial Monitor  6:Tx 5:Rx 
  #define SSS 6                  // Sensitivity
  #define MEMS 450               // 450 words
  #define SPD 16                 // 4msx4phase=16ms
  #define BTT 50                 // IO time 20-30ms+α  
  #define MAXxP 571              // 399 >> 499
  #define MAXyP 571              // 399
  #include "para.h"              //  
// ============================================
//  CLASS
// ============================================
class MouseRptParser : public MouseReportParser {
protected:
	void OnMouseMove	(MOUSEINFO *mi);
	void OnLeftButtonUp	(MOUSEINFO *mi);
	void OnLeftButtonDown	(MOUSEINFO *mi);
	void OnRightButtonUp	(MOUSEINFO *mi);
	void OnRightButtonDown	(MOUSEINFO *mi);
	void OnMiddleButtonUp	(MOUSEINFO *mi);
	void OnMiddleButtonDown	(MOUSEINFO *mi);
  void OnWheelMove  (MOUSEINFO *mi);  // for wheel
};
USB     Usb;
HIDBoot<USB_HID_PROTOCOL_MOUSE>  HidMouse(&Usb);
MouseRptParser                   Prs;
// ============================================
//  SETUP
// ============================================
void setup() {
  monit.begin(19200);                // Tx:6/Rx:5 Serial Monitor  
  pinMode(A3, INPUT_PULLUP);         // IR
  pinMode( 2, INPUT_PULLUP);         // TACT SW  
  pinMode( 4, INPUT);                // SERIAL IF
  pinMode( 5, OUTPUT);               // LED 
  USBON();                           // USB Power ON
  FastLED.addLeds<NEOPIXEL, A0>(leds, 1);            // FastLED A0 
  leds[0] = CRGB(0, 2, 0); FastLED.show();           // RGB set
  
  pinMode( 7, OUTPUT);               // MAX3421E RESET 
  digitalWrite(7, 0); delay(10); digitalWrite(7, 1); // reset MAX3421E      
  if (Usb.Init() == -1) {            // USB HOST Initialize
    tone(A2, 880, 250); monit.println("OSCOKIRQ failed to assert"); 
    leds[0] = CRGB(2, 0, 0); FastLED.show(); delay(1000);
  } delay( 200 );  
  HidMouse.SetReportParser(0, &Prs); // USB Host Parser setup 

  REZERO();
  digitalWrite(5, StartStop);
}
void USBON() { pinMode(A1, OUTPUT); digitalWrite(A1, 1); delay(300); }  // A1:USB ON
// ============================================
//  SYSTEM
  long itime, itimeX, itimeY, itimeRP;                   // event timer   
  int LtimeX = 250; int LtimeY = 250; int LtimeP = 800;  // timer interval
  byte MODE = 0;                                         // normal/set points/replay  
  boolean MBf; long MBt; boolean MBm;                    // Middle button flag, timer
  long Systim;                                           // System sychronize timer (900ms interval)
  int Sreq = 0;                                          // Start Stop reqest   
  
//  int P1X, P1Y, P2X, P2Y; int cPX, cPY;                  // auto point/current point
//  int Rstp = 0;  int Pstp = 0;                           // Record step/Play step
// ============================================
int  DTchkS() { // |||| 0--100............   750""800 |||| 0.....  dead time chk skip
  int nownow = (millis() - Systim) % 900; 
  if(nownow < 100 || nownow > 750) return 0; else return 1; // time at execution
}
void SStartStop() { // Start/Stop by Middle button
  if(Sreq) {  
      // avoid racing skip //////////////////////////
      if(DTchkS()) { Sreq = 0;
        if(!StartStop)          { Senddata(0x03, 0x55, 0x0c); StartStop = 1; TTemp = 160; } // to pre heat
        else if(StartStop == 1) { Senddata(0x03, 0x55, 0x0c); StartStop = 2; TTemp = 240; } // to solder
        else if(StartStop == 2) { Senddata(0x03, 0xaa, 0x0c); StartStop = 0; } // to stop
      } //////////////////////////////////////////////
  }
  if(MB && !LB && !RB) {
           if(!MBf) { MBt = millis(); MBf = true; }                            // leading set
           if(millis() - MBt > 200 && MBf) { MBm = true; }                     //
  } else { MBf = false; if(MBm) { Sreq = 1; MBm = false; } }
}
void REZERO() { 
  leds[0] = CRGB( 20, 0, 0); FastLED.show(); MODE = 0;
  movedirect(MAXxP, 0, 1);         Rflash(10);
  movedirect(MAXxP/2, MAXyP/2, 1); Bflash(10);
}
void Bflash(int nn) {
  for(int n = 0; n < nn; n++) {
    leds[0] = CRGB( 0, 0,20); FastLED.show(); delay(200);
    leds[0] = CRGB( 0, 0, 0); FastLED.show(); delay(200);   
  }
}
void Rflash(int nn) {
  for(int n = 0; n < nn; n++) {
    leds[0] = CRGB(20, 0, 0); FastLED.show(); delay(500);
    leds[0] = CRGB( 0, 0, 0); FastLED.show(); delay(500);   
  }
}
// ============================================
//  LOOP
// ============================================
void loop() {
  SStartStop(); // middle button start/stop
  IR();         // Control by IR Remote
  SIrcv();      // Parameter sync with Control Pannel
  Usb.Task();   // USB task
  if(millis() - itimeX > LtimeX && LB) { itimeX = millis();     // send X axis command
      if(CXaxis == TXaxis) { LtimeX = 50; } else {              // monit.print("X:"); monit.println(TXaxis);  
        Xgogo(max(MAXxP - TXaxis, 1), 0);                       // Xaxis invert
        LtimeX = BTT+SPD*abs(CXaxis - TXaxis); CXaxis = TXaxis; // monit.print(" t:"); monit.println(LtimeX); 
      } 
  }
  if(millis() - itimeY > LtimeY && LB) { itimeY = millis();     // send Y axis command
      if(CYaxis == TYaxis) { LtimeY = 50; } else {              // monit.print("  Y:"); monit.println(TYaxis); 
        Ygogo(max(TYaxis, 1), 0);
        LtimeY = BTT+SPD*abs(CYaxis - TYaxis); CYaxis = TYaxis; // monit.print(" t:"); monit.println(LtimeY);  
      } 
  }  
  if(millis() - itime > 250) { itime = millis();                //send TTemp, send TAirflow
      if(CTemp  != TTemp)  { CTemp  = TTemp;  Senddata(0x00, TTemp-100, 0x0c); }   // Temp 
      if(CAflow != TAflow) { CAflow = TAflow; Senddata(0x01, TAflow/2,  0x0c); }   // Air Flow 
      digitalWrite(5, StartStop);     
  } 
  /// MODE CHANGE ////
  if(LB && RB) { LR = true; rezc++;
     leds[0] = CRGB(0, 2, 0); FastLED.show(); itimeRP = millis(); delay(50);
     if(rezc > 8) { REZERO(); } // REZERO
  } else if(LR && !LB && !RB) { // MODE CHANGE
     LR = false; if(rezc < 8) 
     MODE++; if(MODE > 2) MODE = 0; itimeRP += 1000; rezc = 0;
  } 
  AutoDrive(); // XY table auto driving
}

// ============================================
//  AUTO
  int P1X, P1Y, P2X, P2Y; int cPX, cPY;                  // auto point/current point
  int Rstp = 0;  int Pstp = 0;                           // Record step/Play step
  int sp;
// ============================================
void AutoDrive() {
  if(millis() - itimeRP > LtimeP && DTchkS()) { itimeRP = millis();  // Record and Replay
     switch(MODE) {
       case 0: leds[0] = CRGB( 0, 2, 0); Pstp = 0; break; // NORMAL
       case 1: leds[0] = CRGB( 0, 0, 2+tgll2*20); 
           if(RB) {
              if(!Rstp) { P1X = CXaxis; P1Y = CYaxis; 
                          monit.print("on::"); monit.print(CXaxis); monit.print("/"); monit.println(CYaxis); } // CXaxis
              else      { P2X = CXaxis; P2Y = CYaxis; 
                          monit.print("en::"); monit.print(CXaxis); monit.print("/"); monit.println(CYaxis); } // CYaxis
              P1X = 300; P1Y = 110;
              P2X = 570; P2Y = 360;            
              Rstp = !Rstp;
           }
       break; // SET POINT
       case 2: leds[0] = CRGB( 2+tgll2*20, 0, 0);
           switch (Pstp) {            
                case 0: cPX  = P1X; cPY  = P1Y; Pstp++; LtimeP = 4000; sp = 1;  break; // home position
                case 1: cPX += 25;              Pstp++; LtimeP =  700; sp = 0;  break; // >> X+
                case 2:             cPY += 25;  Pstp++;                  break; //  V Y+
                case 3: cPX -= 25;              Pstp++;                  break; // << X-
                case 4: cPX += 25;  cPY -= 25;          LtimeP =  700;          // ^> Y-,X+
                        if(cPX < P2X) { Pstp = 1; }                             // repeat circle
                        else { cPY += 20; Pstp++; }                      break; // >> X+
                case 5: cPX -= 25;              Pstp++; LtimeP =  700;   break; // <<  
                case 6:             cPY += 25;  Pstp++;                  break; // V
                case 7: cPX += 25;              Pstp++;                  break; // >>
                case 8: cPX -= 25;  cPY -= 25;          LtimeP =  700;          // <^
                        if(cPX > P1X) { Pstp = 5; }                             // repeat circle
                        else { 
                          if(cPY > P2Y) {   Pstp = 0; }
                          else { cPY += 25; Pstp = 1; }
                        }    
              default: break;
           }
           movedirect(min(max(cPX, 1), MAXxP - 1), min(max(cPY, 1), MAXyP - 1), sp); // move move
       break; // AUTO MOVE
     } tgll2 = !tgll2; FastLED.show(); 
  }     
}

// ============================================
//  MOUSE PARSER roll
// ============================================ 
void MouseRptParser::OnWheelMove(MOUSEINFO *mi) {
  if(RB) { TTemp  += mi->dZ; if(TTemp > 350) { TTemp = 350; } if(TTemp < 100) { TTemp = 100; } } // TEMP
  if(LB) { TAflow += mi->dZ; if(TAflow > 16) { TAflow = 16; } if(TAflow <  0) { TAflow =  0; } } // FAN
}; 
void MouseRptParser::OnMouseMove(MOUSEINFO *mi) {
  if(LB) { // with left button
    Xaxis += mi->dX; if(Xaxis > MAXxP*SSS) { Xaxis = MAXxP*SSS; } if(Xaxis < 0) { Xaxis = 0; } TXaxis = Xaxis/SSS; 
    Yaxis += mi->dY; if(Yaxis > MAXyP*SSS) { Yaxis = MAXyP*SSS; } if(Yaxis < 0) { Yaxis = 0; } TYaxis = Yaxis/SSS;  
  }
};
// ============================================
//  MOUSE PARSER button
  #define Lft 1
  #define Mid 2
  #define Rgt 3  
// ============================================ 
void rgb(int num, int br) {
  itimeRP = millis();
  switch(num) {
    case 1:  leds[0] = CRGB( br,  br,  0);  break; // left 
    case 2:  leds[0] = CRGB( br,  0,   br); break; // mid
    case 3:  leds[0] = CRGB( 0,   br,  br); break; // right
    default: leds[0] = CRGB( 0,   2,   0);  break;
  } if(!LR) FastLED.show();
}
void MouseRptParser::OnLeftButtonUp     (MOUSEINFO *mi) { rgb(Lft,  2); LB = false; };
void MouseRptParser::OnLeftButtonDown   (MOUSEINFO *mi) { rgb(Lft, 12); LB = true;  };
void MouseRptParser::OnRightButtonUp    (MOUSEINFO *mi) { rgb(Rgt,  2); RB = false; };
void MouseRptParser::OnRightButtonDown  (MOUSEINFO *mi) { rgb(Rgt, 16); RB = true;  };
void MouseRptParser::OnMiddleButtonUp   (MOUSEINFO *mi) { rgb(Mid,  2); MB = false; };
void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi) { rgb(Mid, 16); MB = true;  };

// ============================================
//  IO assignment
// ============================================
// A0:RGB
// A1:USB ON
// A2:BUZZ
// A3:IR
// D2:SW
// D4:IF
// D5:LED
//  D7:RESET
//  D8:GPX
//  D9:INT >> D9/D3
//  D10:SS
//  D12:MISO
//  D13:SCK
// ============================================
//  VALIABLES
// ============================================  
  boolean tgll = true; boolean tgll2 = true;    // LED tgle/RGB LED tgle
  int StartStop = 0;                            // 
  int TXaxis, TYaxis, TTemp, TAflow;            // Target Value
  int CXaxis, CYaxis, CTemp, CAflow;            // Current Value
  int Xaxis, Yaxis;                             // 6times  
  boolean RB, LB, MB;                           // button status
  boolean LR = false;                           // RB and LB button mode
  byte rezc = 0;                                // REZERO count  
// ============================================
//  VALIABLES Serial IF
// ============================================  
  byte cnt = 0; byte ddd = 0; byte ccc = 0x0f;  // Serial IF parameters 
  byte myaddress = 0x00;                        // address C Multi Master
// ============================================
//  VALIABLES AUTO MODE
// ============================================ 
  int Rpointer = 0;          // buffer pointer
  unsigned int adata[MEMS];  // record buffer memory

Step motor control

Arduino
// ============================================
// ATTINY XY TABLE STTEPING MOTOR CONTROL
// ============================================
// MOMONGA SERVO CONTROLLER [ATTINY44 0.5kRAM, 4kROM]
// Language Reference http://arduino.cc/en/Reference/HomePage
// ============================================
//  Environment
//   http://make.kosakalab.com/make/electronic-work/arduino-ide/attiny-dev/
//   http://drazzy.com/package_drazzy.com_index.json  board manager
//   pin Mapping = clockwise/internal 8MHz
// ============================================
//  FILE INCLUDE
// ============================================
   #include "para.h"             // Parameters     
   #define SP 4000               // Speed ms
   //#define SP 3000               // Speed ms
   #define MGN 1                 // Zero Margin
// ============================================
//  SET UP 
// ============================================
void setup() { 
   pinMode( 0, INPUT_PULLUP);      // Wall check Hall Sensor
   pinMode( 3, INPUT_PULLUP);      // IF
   pinMode( 5, OUTPUT);            // LED
   pinMode( 9, INPUT_PULLUP);      // ADDRESS0   
   pinMode(10, INPUT_PULLUP);      // ADDRESS1

   pinMode( 4, OUTPUT);            // MOTORA PH
   pinMode( 8, OUTPUT);            // MOTORA EN
   pinMode( 7, OUTPUT);            // MOTORB PH
   pinMode( 6, OUTPUT);            // MOTORB EN

   myaddress = 0;                  // read my address
   if(!digitalRead(9))  { bitSet(myaddress, 0); } // x0:X
   if(!digitalRead(10)) { bitSet(myaddress, 1); } // x1:y
   // bit1: odd/even
   // bit2: 512
   CHOT();
   REZERO();
}
// ============================================
//  LOOP
   unsigned int bbb;   
   long itime;           // system timers    
   boolean tgll = true;  // led dimming direction
   // bit 0:XY bit 1:odd/even bit 2:512 bit3: fast mode 
// ============================================
void loop22() {
  if(digitalRead(0)) { digitalWrite(5, 1); } else { digitalWrite(5, 0); }; 
}
void loop() { 
  if(ReceiveCommand())  { bbb = ddd;
    switch(cnt) {
      case 2: TPos = bbb*2; if(bitRead(ccc, 1)) { TPos += 1; } // 8+1bit
              spp = 4000; if(!bitRead(ccc, 3)) spp = 7500;     // set speed ccc bit3:fast bit
              if(bitRead(ccc, 2)) TPos += 512;                 // extend move length
              DIFF = abs(TPos - CPos);                         // Target and Distance 0-399
              FWD = 0; if(TPos - CPos > 0) { FWD = 1; }        // Direction
              rotMON(); for(int n = 0; n < DIFF; n++) {        // Rotate
                  rotMOT(FWD, spp); if(!digitalRead(0)) { n = DIFF; ADJzero(MGN+TPos); }
              } CPos = TPos; rotMOFF(); 
              if(!TPos) { REZERO(); } break;                   // position
      default: break;
    } cnt = 9;
  }
  if(millis() - itime > 500) { itime = millis();
    if(tgll) { analogWrite(5, bbb); } else { analogWrite(5, 0); } tgll = !tgll;
  }  
}
// ============================================
//  HOME POSITION
// ============================================
void CHOT() {
   rotMON(); for(int n = 0; n < 8 ; n++) { rotMOT(1, 7000); }
}
void REZERO() {
   long TO = millis(); CPos = 0;
   rotMON();  // rev rotation end point search
   while( digitalRead(0) && millis() - TO < 15000) { rotMOT(0, SP); } // move to near(screw CW)
   ADJzero(MGN); rotMOFF();
}
void ADJzero(int margn) { // keep margin
   while(!digitalRead(0))  { rotMOT(1, SP); }
   for(int n = 0; n < margn ; n++) { rotMOT(1, SP); }     // zero detect     
}

// ============================================
//  Serial data rcv
//  _____start----    ____"0"----   ____"1"---//----  ____stop----
//    9ms     4ms     600us 600us    600us  1600us    600us           total 20 to 30ms 
//  0/1/2/3:address 5:all 4:talkie(wait serial command)
// ============================================
int ReceiveCommand() {
  long TStime; long sTStime; int lap[20]; cnt = 0; ddd = 0; ccc = 0x0f;  // initialize
  if(!digitalRead(3)) {
    sTStime = millis(); TStime = micros();                               // total/lap length check 
      delayMicroseconds(10); while(!digitalRead(3)) { if(millis() - sTStime > 50) return 0; } 
      if(micros() - TStime < 4000) return 0;                             // start carry "L" 9ms
      delayMicroseconds(10); while( digitalRead(3)) { if(millis() - sTStime > 50) return 0; } // start space "H" 4ms              
    for(int n = 0; n < 10 + 4; n++) {                                    // 10bit
      delayMicroseconds(10); while(!digitalRead(3)) { if(millis() - sTStime > 50) return 0; } // data carry 600us
      TStime = micros();
      delayMicroseconds(10); while( digitalRead(3)) { if(millis() - sTStime > 50) return 0; } 
      lap[n] = micros() - TStime;                                        // data space 600(H)/1600(H)us   
    } while(!digitalRead(3)) { if(millis() - sTStime > 50) return 0; }   // stop bit    
    for(int n = 0;   n < 2;     n++) { if(lap[n] > 1200) { bitSet(cnt, 1 - n);     } }   // command 2bit
    for(int n = 2;   n < 2+8;   n++) { if(lap[n] > 1200) { bitSet(ddd, 7+2 - n);   } }   // data 8bit
    for(int n = 2+8; n < 2+8+4; n++) { if(lap[n] < 1200) { bitClear(ccc, 7+2+4 - n); } } // address 4bit
    int cccc = ccc & 0x01; // axxa
    if(cccc == myaddress) { return 1; } 
  } return 0; 
}

// ============================================
//  STEPPINT MOTOR CONTROL
// 4phase x 5 = 1 rotation >> 0.5mm
// 400/50mm 0.125mm/4phase 400x4phase 4ms/1phase >> 16mx400=6.4sec
// 4phasex5/rot 80ms/rot 750rpm
// ============================================
// D0:SENSOR  D5:LED
// D4:PH1     D8:EN1
// D7:PH2     D6:EN2

// STEP MOTOR DRIVE POSITION CONTROL
// A""""""______""""""______""""""____________""""""______""""""___
// B__"""""""______""""""______""""""______""""""______""""""______
//  10 11 01 00 10 11 01 00 10 11 01 00 00 01 11 10 00 01 11
//   2  3  1  0  2  3  1  0  2  3  1  0  0  1  3  2  0  1  3
//           >>>>>>>>>>>                >>>>>>>>>>>
//           +++++++++++                -----------
// 20step(x5cycle)/rot
// 80mm/64 = (6bit) 1.25mm/bit
// 0.5mm/pitch(M3) 2rotで1mm
// X: address 0 Y: address 1
// 
void rotMON()  { digitalWrite(5, 1); digitalWrite(8, 1); digitalWrite(6, 1); } // MotorAB Power ON
void rotMOFF() { digitalWrite(4, 0); digitalWrite(7, 0);                       // MotorAB Power OFF 
                 digitalWrite(8, 0); digitalWrite(6, 0); digitalWrite(5, 0); }
void rotMOT(int dir, int del) {
                 digitalWrite(4, 0); digitalWrite(7, 0); delayMicroseconds(del);   // ACCW BCCW
      if(!dir) { digitalWrite(4, 1); digitalWrite(7, 0); delayMicroseconds(del); } // ACW  BCCW
      else     { digitalWrite(4, 0); digitalWrite(7, 1); delayMicroseconds(del); } // ACCW BCW
                 digitalWrite(4, 1); digitalWrite(7, 1); delayMicroseconds(del);   // ACW  BCW
      if(!dir) { digitalWrite(4, 0); digitalWrite(7, 1); delayMicroseconds(del); } // ACCW BCW
      else     { digitalWrite(4, 1); digitalWrite(7, 0); delayMicroseconds(del); } // ACW  BCCW 
}

// ============================================
// Pin Assignment
// ============================================
//   D0: Hall Sensor for Wall
//   D3: Serial IF
//   D5:LED
//   D4:PH1   D8:EN1 MOTORA
//   D7:PH2   D6:EN2 MOTORB
//   D4/D5/D6:SCK/MISO(Tx)/MOSI(Rx) (IDE connection)
//   D9/D10: ADDRESS

//   D7/D8:TIMER0(delay/mili..)
//   D5/D6:TIMER1(led(x))
// ============================================
//  VARIABLEs
// ============================================ 
   byte cnt = 0; byte ddd = 0; byte ccc = 0x0f;  // Serial IF parameters 
   int TPos, CPos, FWD, DIFF;    // Target/Current position/Motor Direction
   //int tstM = 0;                 // test mode
   byte myaddress = 0;           // leg address 0/1/2/3/4  
   int spp;

Credits

Masahiro Mizuno
20 projects • 33 followers
Chief Prototypist. 20+ years R&D experience in wide range of technology.
Contact
Takayuki Saito
3 projects • 2 followers
Mechanical Engineer/ Industrial Designer/BJJ Fighter
Contact

Comments

Please log in or sign up to comment.