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


亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」
该项目是基于Arduino的迷你CNC 2D绘图仪,通过2个马达来控制绘图仪的X轴和Y轴,用伺服电机和一个手工制作的笔架来控制Z轴。
项目演示:
所需硬件:
原理图:
代码:
//打开调试模式
// #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 ();
}
讨论