亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」

本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

基于Arduino的迷你CNC 2D绘图仪

发布时间:2021-04-16
分享到:

基于Arduino的迷你CNC 2D绘图仪

发布时间:2021-04-16
分享到:

该项目是基于Arduino的迷你CNC 2D绘图仪,通过2个马达来控制绘图仪的X轴和Y轴,用伺服电机和一个手工制作的笔架来控制Z轴。

项目演示:

所需硬件:

  • Arduino Mega 2560
  • 带有L293D的电机控制罩
  • 微型伺服EMAX ES08MA II  
  • 步进电机

原理图: 

代码:

//打开调试模式
// #define DEBUG

//伺服库
#include <Servo.h>

// ADAFRUIT MOTOR SHIELD库
#include <AFMotor.h>

//引脚
const int z_pin  =  9 ;

//位置
const int z_up  =  0 ;
const int z_dn  =  35 ;

//绘图设置
const char STEP  = INTERLEAVE ;
const float x_steps_per_mm  =  13 .4 ;
const float y_steps_per_mm  =  13 .4 ;

//延迟
const int step_delay_up  =  2 ;
const int step_delay_dn  =  5 ;
const int move_delay  =  100 ;
const int pen_delay  =  250 ;

//极限[ mm ]
const float x_min  =  0 ;
const float x_max  =  40 ;
const float y_min  =  0 ;
const float y_max  =  40 ;

// 读
const int line_size  =  80 ;

//步进马达( STEPS / REV,NUMBER )
AF_Stepper x_motor (48,1 );
AF_Stepper y_motor (48,2 );
伺服z_motor ;

// 位置

浮点数x  = x_min ;
浮点y  = y_min ;
int z  =  0 ;

// 读

int line_index  =  0 ;
字符行[ line_size ] ;
布尔忽略 =假;

// ------------------------------------------------ ---------------------------- //
//功能( SETUP )                                                           //
// ------------------------------------------------ ---------------------------- //

无效设置() {
    //初始化串行通讯
    Serial.begin (38400 );

    //重置位置
    重置();

    #ifdef调试
        Serial.println ();
        Serial.println (“ MiniPlotter还在运行!” );
        Serial.println ();
        Serial.print (“ X from” );
        Serial.print ( x_min );
        Serial.print (“到” );
        Serial.print ( x_max );
        Serial.println (“ mm” );
        Serial.print (“ Y from” );
        Serial.print ( y_min );
        Serial.print (“到” );
        Serial.print ( y_max );
        Serial.println (“ mm” );
        Serial.println ();
    #万一
}

void reset () {
    move_z (0 );
    move_z (1 );
    move_z (0 );
    move_xy ( x_min,y_min );
    move_xy ( x_max,y_max );
    move_z (1 );
    move_z (0 );
    move_xy ( x_min,y_min );
}

// ------------------------------------------------ ---------------------------- //
//功能( READ )                                                            //
// ------------------------------------------------ ---------------------------- //

无效的read_serial () {
    字符c ;

    而 ( Serial.available ()) {
        c  = Serial.read ();

        #ifdef调试
            Serial.print (“读取字符:” );
            Serial.println ( c );
        #万一

        如果 (c  ==  '\ n'  ||  c  ==  '\ r' ) {
            //到达行尾

            如果 ( line_index> 0 ) {
                行[ line_index ]  =  '\ 0' ;
                #ifdef调试
                    Serial.print (“读取行:” );
                    Serial.println (行);
                    Serial.println ();
                #万一
                process_line ( line,line_index );
                line_index  =  0 ;
            } 其他 {
                #ifdef调试
                    Serial.println (“空行” );
                    Serial.println ();
                #万一
            }

            忽略 =假;
        } 其他 {
            如果 (忽略) {
                如果 (c  ==  ')' ) {
                    //停止忽略行
                    忽略 =假;
                }
            } 其他 {
                如果 ( c < =  '' ) {
                    //丢掉白色空间和控制字符
                }  else  if  (c  ==  '/' ) {
                    //不支持块删除,忽略字符
                }  else  if  (c  ==  '(' ) {
                    //开始忽略行
                    忽略 = true ;
                }  else  if  (c  ==  ';' ) {
                    //不支持SEMICOLON,忽略行
                    忽略 = true ;
                } 否则, 如果 ( line_index> = line_size- 1 ) {
                    Serial.println (“错误:行大小超出” );
                    忽略 =假;
                } 否则, 如果 ( c> =  'a'  && c < =  'z' ) {
                    //到UPCASE
                    line [ line_index ++ ]  = c - 'a ' + 'A' ;
                } 其他 {
                    行[ line_index ++ ]  = c ;
                }
            }
        }
    }
}

无效process_line ( char * line,int line_size ) {
    int line_index  =  0 ;
    字符缓冲区[ 50 ] ;

    #ifdef调试
        Serial.print (“处理行:” );
        Serial.println (行);
        Serial.println (“” );
    #万一

    而 ( line_index <line_size ) {
        切换( line [ line_index ++ ]) {
            案例 “ S”:
            {
                缓冲区[ 0 ]  =行[ line_index ++ ] ;
                缓冲区[ 1 ]  =行[ line_index ++ ] ;
                缓冲区[ 2 ]  =  '\ 0' ;
                浮动大小 = atof (缓冲区);

                而 ( line_index <line_size ) {
                    char c  =行[ line_index ++ ] ;
                    plot_char ( c,size );
                }
            }
            情况 “ G”:
            {
                缓冲区[ 0 ]  =行[ line_index ++ ] ;
                缓冲区[ 1 ]  =  '\ 0' ;

                开关( atoi (缓冲区)) {
                    情况 0:
                    {
                        char * z_index  = strchr ( line + line_index,'Z' );
                        int z_pos ;

                        如果 ( z_index < =  0 ) {
                            z_pos  = z ;
                        } 其他 {
                            z_pos  = atoi ( z_index + 1 );
                            z_index  =  '\ 0' ;
                        }

                        move_z ( z_pos );
                        休息;
                    }
                    情况 1:
                    {
                        char * x_index  = strchr ( line + line_index,'X' );
                        char * y_index  = strchr ( line + line_index,'Y' );
                        浮点数x_pos,y_pos ;

                        如果 ( y_index < =  0 ) {
                            x_pos  = atof ( x_index + 1 );
                            y_pos  = y ;
                        } 否则, 如果 ( x_index < =  0 ) {
                            Y_POS  = ATOF ( y_index + 1 );
                            x_pos  = x ;
                        } 其他 {
                            Y_POS  = ATOF ( y_index + 1 );
                            y_index  =  '\ 0' ;
                            x_pos  = atof ( x_index + 1 );
                        }

                        move_xy ( x_pos,y_pos );
                        休息;
                    }
                }
                休息;
            }
            案例 'M':
            {
                缓冲区[ 0 ]  =行[ line_index ++ ] ;
                缓冲区[ 1 ]  =行[ line_index ++ ] ;
                缓冲区[ 2 ]  =行[ line_index ++ ] ;
                缓冲区[ 3 ]  =  '\ 0' ;

                开关( atoi (缓冲区)) {
                    案例 300:
                    {
                        char * s_index  = strchr ( line + line_index,'S' );
                        浮s_pos  = ATOF ( s_index + 1 );

                        如果 (s_pos  ==  50 ) {
                            //补上
                            move_z (0 );
                        }

                        如果 (s_pos  ==  30 ) {
                            //下笔
                            move_z (1 );
                        }
                        休息;
                    }
                }
                休息;
            }
        }
    }
}

// ------------------------------------------------ ---------------------------- //
//功能( MOVE )                                                            //
// ------------------------------------------------ ---------------------------- //

void motors_attach_detach ( int模式) {
    // 模式:
    // 0  =附着
    // 1  = DETACH

    如果 (mode  ==  0 ) {
        z_motor.attach ( z_pin );
    } 其他 {
        z_motor.detach ();
    }
}

void move_z ( int z_pos ) {
    // Z_POS:Z POSITION
    // 0  =向下
    // 1  =向上

    #ifdef调试
        Serial.print (“ Move:Z” );
        Serial.print ( z );
        Serial.print (“ => Z” );
        Serial.println ( z_pos );
        Serial.println (“” );
    #万一

    // 移动
    如果 ( z_pos!= z ) {
        motors_attach_detach (0 );
        如果 (z_pos  ==  0 ) {
            z_motor.write ( z_up );
        } 其他 {
            z_motor.write ( z_dn );
        }
        延迟( pen_delay );
        motors_attach_detach (1 );
    }

    //更新位置
    z  = z_pos ;
}

void move_xy ( float x_pos,float y_pos ) {
    // X_POS,Y_POS:X和Y位置

    #ifdef调试
        Serial.print (“ Move:X” );
        Serial.print ( x );
        Serial.print (“ Y” );
        Serial.print ( y );
        Serial.print (“ => X” );
        Serial.print ( x_pos );
        Serial.print (“ Y” );
        Serial.println ( y_pos );
        Serial.println (“” );
    #万一

    //调整位置到极限
    如果 ( x_pos> = x_max ) {
        x_pos  = x_max ;
    }

    如果 ( x_pos < = x_min ) {
        x_pos  = x_min ;
    }

    如果 ( y_pos> = y_max ) {
        y_pos  = y_max ;
    }

    如果 ( y_pos < = y_min ) {
        y_pos  = y_min ;
    }

    //转换为步骤
    浮点数x_step  =  ( int )( x * x_steps_per_mm );
    浮点y_step  =  ( int )( y * y_steps_per_mm );
    浮点数x_pos_step  =  ( int )( x_pos * x_steps_per_mm );
    浮点数y_pos_step  =  ( int )( y_pos * y_steps_per_mm );

    //计算变化
    长dx  = abs ( x_pos_step-x_step );
    长dy  = abs ( y_pos_step-y_step );
    int sx  = x_step <x_pos_step吗?前进:前进;
    int sy  = y_step <y_pos_step吗?前进:前进;

    // 移动
    长超过 =  0 ;
    如果 ( dx> dy ) {
        对于 ( int i  =  0 ; i <dx ; ++ i ) {
            x_motor.onestep ( sx,STEP );
            超过 + = dy ;

            如果 (超过> = dx ) {
                超过- = dx ;
                y_motor.onestep ( sy,STEP );
            }

            如果 (z  ==  0 ) {
                延迟( step_delay_up );
            } 其他 {
                延迟( step_delay_dn );
            }
        }
    } 其他 {
        为 ( int i  =  0 ; i <dy ; ++ i ) {
            y_motor.onestep ( sy,STEP );
            超过 + = dx ;

            如果 (大于> = dy ) {
                超过- = dy ;
                x_motor.onestep ( sx,STEP );
            }

            如果 (z  ==  0 ) {
                延迟( step_delay_up );
            } 其他 {
                延迟( step_delay_dn );
            }
        }
    }
    延迟( move_delay );

    //更新位置
    x  = x_pos ;
    y  = y_pos ;
}

// ------------------------------------------------ ---------------------------- //
//功能( PLOT )                                                            //
// ------------------------------------------------ ---------------------------- //

void plot_char ( char c,float height ) {
    // C:要绘制的字符[ A-Z0-9 ]
    //
    //高度:以MM为单位(宽度 =高度/ 2 )

    #ifdef调试
        Serial.print (“ Character =” );
        Serial.println ( c );
        Serial.println ();
    #万一

    浮标尺 =高度/ 12 ;
    浮点数x0  = x ;
    浮点y0  = y ;

    开关( c ) {
        情况 “ A”:
            move_z(1);
            move_xy(x0+0*scale, y0+10*scale);
            move_xy(x0+2*scale, y0+12*scale);
            move_xy(x0+4*scale, y0+12*scale);
            move_xy(x0+6*scale, y0+10*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+0*scale, y0+6*scale);
            move_z(1);
            move_xy(x0+6*scale, y0+6*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'B':
            // TBD
            break;
        case 'C':
            move_xy(x0+6*scale, y0+12*scale);
            move_z(1);
            move_xy(x0+2*scale, y0+12*scale);
            move_xy(x0+0*scale, y0+10*scale);
            move_xy(x0+0*scale, y0+2*scale);
            move_xy(x0+2*scale, y0+0*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'D':
            // TBD
            break;
        case 'E':
            move_xy(x0+6*scale, y0+6*scale);
            move_z(1);
            move_xy(x0+0*scale, y0+6*scale);
            move_z(0);
            move_xy(x0+6*scale, y0+12*scale);
            move_z(1);
            move_xy(x0+0*scale, y0+12*scale);
            move_xy(x0+0*scale, y0+0*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'F':
            // TBD
            break;
        case 'G':
            // TBD
            break;
        case 'H':
            // TBD
            break;
        case 'I':
            // TBD
            break;
        case 'J':
            // TBD
            break;
        case 'K':
            // TBD
            break;
        case 'L':
            move_xy(x0+0*scale, y0+12*scale);
            move_z(1);
            move_xy(x0+0*scale, y0+0*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'M':
            move_z(1);
            move_xy(x0+0*scale, y0+12*scale);
            move_xy(x0+3*scale, y0+6*scale);
            move_xy(x0+6*scale, y0+12*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'N':
            // TBD
            break;
        case 'O':
            move_xy(x0+0*scale, y0+2*scale);
            move_z(1);
            move_xy(x0+0*scale, y0+10*scale);
            move_xy(x0+2*scale, y0+12*scale);
            move_xy(x0+4*scale, y0+12*scale);
            move_xy(x0+6*scale, y0+10*scale);
            move_xy(x0+6*scale, y0+2*scale);
            move_xy(x0+4*scale, y0+0*scale);
            move_xy(x0+2*scale, y0+0*scale);
            move_xy(x0+0*scale, y0+2*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'P':
            // TBD
            break;
        case 'Q':
            // TBD
            break;
        case 'R':
            move_z(1);
            move_xy(x0,         y0+12*scale);
            move_xy(x0+4*scale, y0+12*scale);
            move_xy(x0+6*scale, y0+10*scale);
            move_xy(x0+6*scale, y0+8*scale);
            move_xy(x0+4*scale, y0+6*scale);
            move_xy(x0+0*scale, y0+6*scale);
            move_xy(x0+6*scale, y0+0*scale);
            move_z(0);
            move_xy(x0+8*scale, y0+0*scale);
            break;
        case 'S':
            // TBD
            break;
        case 'T':
            // TBD
            break;
        case 'U':
            // TBD
            break;
        case 'V':
            // TBD
            break;
        case 'X':
            // TBD
            break;
        case 'Y':
            // TBD
            break;
        情况 “ Z”:
            //待定
            休息;
    }
}

无效循环()
{
    read_serial ();
}

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论