Arduino based Laser Control

A forum for the Arduino based Mach4/Darwin Laser Control Panel
Post Reply
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

yes if one button is disconnected it makes the other buttons work wrong.

yes its the code, it does not matter if using all the switches

if using same set up as you its not a problem. its a good way to tall if any of the buttons don't work.

I have added a serial section in the code for using when connected everything up to check that all buttons, pot and leds are working ok. rejig of console
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

hi all have been doing some more playing I have found you cant have serial.print running in the code if you use a screen you have to have one or the other.

I have changed the code a little, I have removed all the yun parts from the code it still runs fine.

added a serial.print section for checking that the buttons and pot work and what order they are in, has to be commented out when using a screen

one problem you get with serial running buttons change order by there self at any random time.

I have got everything connected with out a laser using a led to see if the output is working properly.

on a side note you can test the outputs in Darwin after the run time has expired but no motor movement
ArtF
Global Moderator
Global Moderator
Posts: 4557
Joined: Sun Sep 05, 2010 5:14 pm
Contact:

Re: Arduino based Laser Control

Post by ArtF »

Hi Dan:

I knew the serial could interfere.. I find now with the latest version I can engrave at about 9000mm/min with no real noise issues, so
mine is working better all the time.. I guess next time Ill have to use a processor with more oomph...

Art
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

yer a faster board will be a full rewrite had a look at doing at my self but its way above what I can do at this time its bad enough trying to work out Lua
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

hi all I think I have everything working have not connected a laser up yet just using a arduino Oscilloscope it seems to be working ok.

I have added my version of the code it has a couple of different bits in for testing only they are commented where they are.

don't use serial when using for laser work it makes it go mad testing only.

if anyone makes changes please re name or change the T1 to T2 or whatever


Code: Select all

//Arts_laser_control_Leonardo_T1
#include <Wire.h>
#include <LiquidCrystal.h>
#include <PWM.h>

//Setup for Diags..
#define DIAGS   0
#define CNCMODE 1
#define NGRAVE  2
 
//frequency and pins used.
int32_t basefrequency = 5000; //5khz base freq
int8_t  PulsePin = 9;         //Pin 9 will control the laser. 
int8_t  BoardLED = 13;
int8_t  FireLED  = 12;
int8_t  PowerLED  = 5;
int8_t  Pot  = 4;
int8_t  Buttons  = 8;        //analogue and digital are same numbers..be carefull if you use other pins..
int8_t  Buttons1 = 10;
int8_t  CurButton = 0;       //currently pressed button
int8_t  CountMode = 0;       //the mode of the up/down counter..
bool    WaitButtonZero = false;
int8_t  OneMicro = 0;
int8_t  Spindle = 6; //interrupt, its on pin7
int8_t  OpMode = 0;
volatile unsigned short SerialIn = 0;
volatile unsigned short LastSerial = 0;

volatile unsigned long SpinOnCnt = 0;
volatile unsigned long SpinOffCnt = 0;
volatile bool SpindleOn = false; //remote spindle command..
volatile bool SpindlePWM = false; //remote spindle command current state..

volatile unsigned int OutPower[101];

//vars for control
bool      PowerOn = false;
bool      Tickle = false; // On Off Safety watchdog .. monitor the tickle for laser health
volatile int16_t Duty = OneMicro; //this is the pulse to pulse firepower during a shot.
volatile int32_t cnt = 0;  //this is a global interrupt counter..
volatile bool FIRE = false;

//initialize the lcd..its on a0-a6 lines in my leonardo..
LiquidCrystal  lcd( 23,22,21,20,19,18);  // set the LCD address to 0x27 for a 20 chars and 4 line display

// the main control variable for diags mode..
int16_t   Power =  50; //0 - 100%
unsigned long  PWMPower = 0;//0- 100% for the incoming PWM..
int16_t   Freq = 5000; // frequency of pulses..
int8_t    PotMode = 0;

volatile int32_t   FireCount = 0;   //number of pulses to fire in diags 
volatile int16_t   ReLoad = 1;

void setup()   
{
  ///////////////////////////////////////////////////////////
  //serial for testing switches comment out when not used
  
  //Serial.begin (1200); 
 // while the serial stream is not open, do nothing
  //while (!Serial);
  lcd.begin(20,4); // initialize the lcd 
  lcd.setCursor(3,0);   lcd.print("Gearotic Laser");
  lcd.setCursor(0,2);   lcd.print("Waiting for Unix.");
 
  InitTimers();    //start up the timers.
  bool timeron = SetPinFrequency( PulsePin, basefrequency ); //set pulsepin to 5Khz
  TIMSK1 |= (1 << TOIE1);  // enable timer overflow interrupt, we'll use this one for fastpwm
   
  attachInterrupt( 0, DUMMY, RISING ); //pulse external monitor 
  attachInterrupt( 4, DUMMY, CHANGE ); //sets the routine to call..    

  pwmWrite( PulsePin, 0); //set for 1us for laser tickle 
  if( timeron ) //if the timercall worked..
  {
    pinMode( BoardLED, INPUT); // board led life
	pinMode( FireLED,  OUTPUT); // fire button led
	pinMode( PowerLED, OUTPUT); //power control led & control

	pinMode( 2,   INPUT); //inputs from breakout board
	digitalWrite( 2, LOW );

	pinMode( 3,   INPUT); //inputs from breakout board
	digitalWrite( 3, LOW );

	pinMode( 11,    INPUT); //inputs from breakout board
	digitalWrite( 11, HIGH );

	pinMode( 6,    INPUT); //inputs from breakout board
	digitalWrite( 6, HIGH );

	pinMode( Spindle, INPUT); //inputs from breakout board

	 
	digitalWrite( PowerLED, PowerOn); //turn off led for power at start
	pinMode( Pot, INPUT); //pot for variable control
    digitalWrite(4, HIGH); //set for a pullup resistor
	pinMode( Buttons, INPUT); //Jog buttons as analogue input
    digitalWrite(Buttons, HIGH); 
	pinMode( Buttons1, INPUT); //Rest of buttons
	digitalWrite(Buttons1, HIGH); 
 
  } 
  SetDisplay( true ); //and draw the display mask
 
}

//main idle loop..

void loop() 
{
 /* 
  //diags text for testing and debugging..
  //uses if only needed comment out if not used

  int16_t  d1 = 0, d2 = 0, d3 = 0;
  if( cnt > 2000)
  {
     cnt = 0;
     d1 = analogRead(Pot + 2);
	 d2 = analogRead(8);
	 d3 = analogRead(10);

    Serial.print("pin 6,8,10" );
	Serial.print(" "); 
	Serial.print( d1 );
	Serial.print(" "); 
	Serial.print( d2 );
	Serial.print(" "); 
	Serial.print( d3 );
	Serial.print(" "); 
        Serial.print("current button");
	Serial.print(" ");
        Serial.print( CurButton );
        Serial.print(" ");
        Serial.println();
   
 /////////////////////////////////////////////////////////////////////////////////
   // /*pwm power to serial monitor use for setting pwm power
      //comment out if not needed 
    
    
        Serial.print("pwm");
        Serial.print(" ");
        Serial.print( PWMPower );
        Serial.print(" ");
	Serial.println();  
  }
*/
////////////////////////////////////////////////////////////////////////////
 
  HeartBeat(); //flash the board led if fire isnt flashing..
  FlashFire(); // flash the fire button if we are going to fire..
  WatchDog();  // ensure all is safe.
  //DiagsOut();  //display test text if demanded..not used
  PotControl(); //use the pot for values..
  GetButton();   
  if( OpMode == CNCMODE) SpindleCalc(); //only for CNC cut/profile op's..
  if( OpMode == DIAGS )  DiagFire();
  //if( OpMode == NGRAVE)  NGraveFire();
  LaserOnOff();
  DoButtonCount();  // make freq or firecount set by button if needed.
  //set power and other vars, then display..
  SetDisplay( false );
}

void DoButtonCount()
{
            int8_t   Accel = 1; //this is the accel of button freq, adjust as needed.
   static   int16_t  Adder = Accel;
            int16_t  Freqmax  = 200;
   static   int32_t  Bres = Freqmax;  
   if( CurButton == 0)
   {
       Adder = Accel;
       Bres = Freqmax; 
	   return;
   }
   int16_t oldfire = FireCount;
   Bres += Adder;
   while( Bres > Freqmax ) //not very ellegant, fix later. just a timer for easier button press
   {
	   if( CurButton == 4 ) //left arrow, switch pot to count mode..
  	                CountMode--;
	   if( CurButton == 6 ) //left arrow, switch pot to dutycyle..
  	                CountMode++;
       if( CountMode < 0) CountMode = 2; 
       if( CountMode > 2) CountMode = 0;

	   Bres -= Freqmax;
	   if( CountMode == 0  && CurButton == 5) Freq++;
       if( CountMode == 2  && CurButton == 5) FireCount++;
       if( CountMode == 0  && CurButton == 7) Freq--;
       if( CountMode == 2  && CurButton == 7) FireCount--;
	   if( FireCount < 0 ) FireCount = 5000;
	   if( FireCount >  5000 ) FireCount = 0;
	   if( Freq <= 0 ) Freq = 1;
	   if( Freq > 5000 ) Freq = 5000;
	   int oldop = OpMode;
	   if( CountMode == 1 && CurButton == 5 ) OpMode++;
       if( CountMode == 1 && CurButton == 7 ) OpMode--;
	   if( OpMode > 2 ) OpMode = 0; //these two lines just make the opMode toggle up or down..
       if( OpMode < 0 ) OpMode = 2;

	   //reopen file if required.
	   if( OpMode == NGRAVE && oldop != NGRAVE)
	     {
           //detach the spindle interrupts.
		   detachInterrupt(4);
		   attachInterrupt( 1, DUMMY, RISING ); //reattach the spindle interrupt
		   
	     }
		 if( OpMode != NGRAVE && oldop == NGRAVE )
		 {
		    attachInterrupt( 4, DUMMY, CHANGE ); //reattach the spindle interrupt
			detachInterrupt(1);
		 }
	   

   }
   if( oldfire != FireCount ) ReLoad = FireCount;
   Adder += Accel;
   //faster..damnit..faster. :)
   if( Adder == 100 )   Adder *= 2;
   if( Adder == 400)    Adder *= 2;
    if( Adder == 1000)  Adder *= 2;
}

void DiagFire()
{
  if( CurButton == 2 ) FireCount = ReLoad;
  //FIRE!!!!
  if( CurButton == 1 && FireCount > 0 ) FIRE = true;
  if( CurButton == 0 && FIRE && OpMode == DIAGS ) FIRE = false; //turn it off if no button is pressed in Diags mode.
}


void SpindleCalc()
{
	if( OpMode == NGRAVE )
		return;
	if( !SpindleOn ) return;

	if( cnt - SpinOffCnt > 225 ) 
	{
		SpindleOn = false; //turn it off.
		PWMPower = 0;
		return;
	}
	if( SpinOffCnt < SpinOnCnt )
	{ //probably a on/off spindle control
		if( PWMPower == 0 ) PWMPower = Power;
  	    return;
	}
	int per = SpinOffCnt - SpinOnCnt;

    static int xc = 0;
	static int yc = 0;
	static int lastpwm = 0;
	 
	if( per > 98 ) per = 98;
	if( per < 5 ) per = 5;
	PWMPower = per;

	//switch to base pot power
    if( (cnt - SpinOnCnt) > 130  && digitalRead(7) )
  	    PWMPower = Power; //looks like no pwm, just a spindle on command
    if( (cnt - SpinOffCnt) > 130  && !digitalRead(7) )
 	    SpindleOn = false; //looks like no pwm, just a spindle on command

}

void LaserOnOff()
{
  //this controls Laser ON/OFF
  if( CurButton == 3 ) //power call , but only once per press
  { 
	if( !WaitButtonZero) PowerOn = !PowerOn; 
    digitalWrite( PowerLED, PowerOn); //Set Power condition;
    WaitButtonZero = true;
  }

}


void DUMMY()
{
  
}

int16_t FireLaser()
{
	int16_t ret = OneMicro; //usually we'll return 1 microsecond.
	static int16_t Counter = basefrequency;
	if( FIRE )
	{   
		if( OpMode == DIAGS )
		  ret = Power << 5; //we'll return this pot power only if the counter says so..
		else
        if( OpMode == CNCMODE )
		  ret = PWMPower;
		else 
		  ret =  OutPower[PWMPower]; //in NGrave mode we return the duty cycle power, or ngrave power

		if( ret > 3191 ) ret = 3191; //set a maximum pulse wdith here for your cpu speed
		//now decide on a frequcny basis is this is a shot time..
        Counter += Freq;
		if( Counter >= basefrequency )
		{
			Counter -= basefrequency;
			if( SpindleOn && FireCount > 2000 ) FireCount = 2000;
    	    if( FireCount != 5000) FireCount--;
		    if( FireCount <= 0 ) FIRE = false;
		}
		else
			ret = OneMicro;
	
	}
	if( ret < OneMicro ) ret = OneMicro; //no msaller than 1 microsecnd
	return ret;
}
///*
///////////////////////////////////////////////////////////////////////////////////////////////////
//display the text
/////  Text Blockout

////////////////////////
//        Pwr         //
//Frq-hz  100  Pulses //
//  5000 <--->  5000  //
////////////////////////
void SetDisplay( bool clear )
{
  if( clear ) 
  {
	  lcd.clear(); //x,y
	  lcd.setCursor(8, 0);    lcd.print("Pwr");
	  lcd.setCursor(0, 1);    lcd.print("Frq-hz");
	  lcd.setCursor(14,1);    lcd.print("Pulses");
 }
  static int8_t slw = 0;
  slw++;
  if( slw % 8 )
  if( SpindleOn && OpMode == CNCMODE ) //display of remote power setting..
  {
	  lcd.setCursor(11, 0);    lcd.print("-Rem:");
	  lcd.setCursor(16, 0);    lcd.print("     ");
	  lcd.setCursor(16, 0);    lcd.print(PWMPower,10);
  }
  else
  if( OpMode == NGRAVE )
  {
      lcd.setCursor(11, 0);    lcd.print("-Rem:");
	  lcd.setCursor(16, 0);    lcd.print("     ");
	  lcd.setCursor(16, 0);    lcd.print(LastSerial,10);
  }
  else
  {   
	  lcd.setCursor(11, 0);   
      lcd.print("        ");
      lcd.setCursor(16, 0);   
	  lcd.print("     ");
  }


  //power
  lcd.setCursor(8,1);
  lcd.print( Power , 10 );
  lcd.print( "%  " );
  //frequency
  lcd.setCursor(3,3);
  lcd.print( Freq , 10 );      lcd.print("   ");
  //fire count
  lcd.setCursor(15,3);

  if( FireCount != 5000)
  { lcd.print( FireCount , 10 ); lcd.print("   "); }
  else
     lcd.print(" CW   ");
 
  //and count mode
  lcd.setCursor(8,3);
  switch( CountMode )
  {
  case (0) : lcd.print("<<---"); break;
  case (1) : lcd.print(" ^^^ "); break;
  case (2) : lcd.print("--->>"); break;
  }
   
  static int toggle = 0;
  static bool twinkle = true;
  toggle++;
  if( toggle > 20 )
  {
	  toggle = 0;
	  twinkle = !twinkle;
	  lcd.setCursor( 0,4 ); 
	  if( twinkle ) lcd.print("*"); else lcd.print("#");
  }

//erase the mopde line..
lcd.setCursor( 0,2 );
lcd.print("                    ");

if( SpindleOn && OpMode == DIAGS)
   OpMode = CNCMODE; //auto switch from Diags
 
  switch (OpMode)
  {
   case(0): lcd.setCursor( 8,2 );  lcd.print("Diag"); break;
   case(1): lcd.setCursor( 7,2 ); lcd.print("CNC-CW"); break;
   case(2): lcd.setCursor( 7,2 ); lcd.print("NGRAVE"); break;
  }
}

///////////////////////////////////////////////////////////////////////////////////////
//helper functions
 
void FlashFire() 
{
  static int8_t Flashtime = 0;
  static bool   fireledstate;
  if( FireCount == 0 ) 
    fireledstate = false;
  if( Flashtime++ > 10 ) //this 10 should be change for a pleasant flash tempo..
  {
	  digitalWrite( FireLED, fireledstate ); 
	  fireledstate = !fireledstate;
	  Flashtime = 0;
  }
}

//flash the board led to show we're alive
void HeartBeat() 
{
  static int8_t Beat = 0;
  static bool   beatstate;
  if( FireCount != 0 ) return; 
 
  if( Beat++ > 10 ) //this 10 should be change for a pleasant flash tempo..
  {
	  //digitalWrite( BoardLED, beatstate ); 
	  beatstate = !beatstate;
	  Beat  = 0;
  }
}

//watchdog function, kill laser power if tickle stops..
//or other checks all of which can be put here..
void WatchDog()
{
  static int lastcnt = cnt; //activity monitor
  if( lastcnt != cnt ) Tickle = true; else  Tickle = false;
  lastcnt = cnt;

  //set other checks here... too long a shot maybe...

  if( !Tickle )
  {
	  PowerOn = false;
	  digitalWrite( PowerLED, PowerOn); //turn off led for power at start
  }

}
//use of the pot for duty or frequency.
void PotControl()
{
    int32_t val = analogRead( Pot + 2 ); //the analogue for the pot pin
    static int32_t oldval = 0;
	//lets only  update the power tables if the pot has changed by 2 minimum..
	if( PotMode == 0 && abs(oldval - val) > 2 ) // duty cycle.. only mode at present..
	{
       Power =  val / 10.23;
	   if( Power < 1 ) Power = 1;
	   if( Power > 100) Power = 100;
	   Power = 101 - Power;
	   //fill power tables for NGraving
	   OutPower[ 0 ] = 0;
	   double scale = ((double)(31 * Power))/(double)100;
	   for( int x = 1; x <= 100; x++ )
	   {
		 OutPower[ x ] = (unsigned int) (scale * x);   
	   }
	}
}

//figure out a button press, only one button 
//at any time with noise rejection. This uses 2 analogue pins
//which multiplexes many buttons on each pin.
void GetButton()
{
  int8_t setbounce = 4; //set a debounce here..
  static int8_t debounce = setbounce;
  static int8_t LastButton = 0;
  int8_t thisbut = 0;

  int16_t val = analogRead(Buttons); //fire,reload and laser
  if( val < 900 )
  {
     
	  thisbut = 3 - (val >> 8);
  }
  val = analogRead(Buttons1); //ok, read the fire buttons then..
  if( val < 900 )
  {
      //figure out the button in the fire group
	  thisbut = (val >> 8) + 4;
  }

  if( LastButton == thisbut ) debounce--;
  else   debounce = setbounce;    
  if( debounce == 0 )
  {
	 CurButton = thisbut;
     debounce = setbounce;
  }

  if( WaitButtonZero && CurButton == 0)
		 WaitButtonZero = false;

  LastButton = thisbut;

}

//make the frerq and count registers count by button.

//this gets called at timer reset time..
ISR(TIMER1_OVF_vect)
{
  cnt++;
  Duty = FireLaser();   //find the next power level..    
  OCR1A = Duty;         
}
 
//these three interrupts are for step/dir and spindle pwm. 
//used as direct vectors to get the speed required..
//this interrupt is for the step pulses..any step pulses, to 
//set a firepulse..

ISR(INT0_vect) 
{
    //step interrupt...very fast
	if( (OpMode != DIAGS && SpindleOn)) 
	{
		FireCount = ReLoad;
		FIRE = true;
	}
 
}

//this is for recieving serial data power 
//for 16 slice sequences.
ISR(INT1_vect) 
{
    
	if( PINB & ( 1 << 7))
	     SerialIn |= 0x8000;
	if( (SerialIn & 0xf00f ) == 0xf00f)
	  {
       // LastSerial = SerialIn & 0x0ff0;
	   LastSerial = (SerialIn >> 4) & 0xff;
	   SerialIn = 0;
	   if( LastSerial < 100 )
	   {
	      static unsigned short prev = 0;
	     if( prev == LastSerial )  //must be a repeated power..
	          PWMPower = LastSerial;
	      prev = LastSerial;
	   }
	   //control the spindle
	   if( PWMPower != 0 ) SpindleOn = true;
	     else SpindleOn = false;  
	   
	  }
	SerialIn = SerialIn >>  1; //we got a clock, so shift it..
}

//and finally a pwm spindle interrupt, basically Reads incoming PWM signal at 50hz..
ISR(INT6_vect) 
{
	if( PINE & ( 1<<6))
	{
		SpinOnCnt = cnt;
		SpindleOn = true;
	}
	else
		SpinOffCnt = cnt;
	return;
 
}
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

Hi art just a couple of questions what Darwin signal do you set the pwm to is it all or just one

I have set

step flag to 25 Hz

serial data to 25Hz

serial clock to 25Hz

I am running it in PWM mode with 25 Hz as base freq

just making sure I am doing it correctly as I think I may have a problem with the switches or pot not to sure yet will be testing tomorrow.

I have a junk laser I am going to put on it to see what is happening as for some reason when in test mode I cant get the laser output to fire it will just be a setting the switches or pot playing up. when I was scoping it, it only went up to 3.8v. it also may be that I have a 10k pot on it not a 5k pot.will play with pot settings and see what is happens.
ArtF
Global Moderator
Global Moderator
Posts: 4557
Joined: Sun Sep 05, 2010 5:14 pm
Contact:

Re: Arduino based Laser Control

Post by ArtF »

Hi Dan:

  You can set all to zero for frequency, it shouldnt be used. The Stepflag signal pulses whenever a motor ( any motor ) steps. So just jogging
will show that signal toggle as a test. Serial data will be gogin out all the time when using Darwin in Laser mode. Serial clock will pulse at 40khz or whatever the maximum kernal rate is set to.

  Step Flag is used all the time, you can use it in PWM mode as well as laser mode, it just means the laser will fire only
when moving. In the most recent Darwins it will fire only whiel moving in G1 mode, no other mode will fire. I like this as it makes
it all much safer and easier to deal with.

  If you execute a G1X100 , does the laser PWM go higher than the 1us tickle.. and when it does, does the laser fire.. Thats the
thing that needs to be done... or figured out. :)

Art
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

its not doing anything no laser fire nothing
tried G1 x100 nothing tried with laser on an off

did a pick linked to Mach4 with M3 S1000 F600 nothing, M3 F600 S1000 nothing
put G1 X100 M3 S1000 F600 nothing, G1 X100 F600 M3 F600 nothing,

tried it in diag nothing.

tried setting the signals active low nothing.
I have set the 3 signals to output 0,1,2 in Mach4 the leds don`t light up on dig screen

any suggestions as what to do

I am wondering if its haveing a 10k pot not a 5k pot doing it will get a 5k and see what happens
ArtF
Global Moderator
Global Moderator
Posts: 4557
Joined: Sun Sep 05, 2010 5:14 pm
Contact:

Re: Arduino based Laser Control

Post by ArtF »

Dan:

Whoa.. slow down. Lets find whats working and what isnt and go from there..

OK, first, does the diags mode work. The Diags mode will not laser no matter
what Mach4 is doing. It will laser only when you dial up a count, and press the fire button.
Counter should run to zero, laser fires until zero. Pressing reload will relaod last count and fire will
flash again ready for the next shot. Does that all work?

So, use the buttons to get a count of 1000 , then press fire ( which should be flashing.. ). Does the laser fire?
Lets make sure that works , then we'll go to stage 2 and get PWM laser from the device in CNC Mode... then finally we'll
get NGrave mode running that does photo's..

Art
DanL
Site Admin
Posts: 357
Joined: Thu Sep 11, 2014 12:35 am

Re: Arduino based Laser Control

Post by DanL »

diags mode works I could see a pwm signal on my arduino scope.

I think it is the switches I am using sometime I can`t get a reading from them.

I am using a mb06 v2 from Homann Designs as the bob to run the laser that's working ok

the Leonardo and screen are fine I have tested them.

I have some switches I can change to so I will do that and try again
Post Reply