Pull to refresh

Печатаем картинки с помощью Arduino

Reading time9 min
Views52K


Очень давно хотелось сделать какой-нибудь проект на Arduino, да такой, чтобы всё в нём двигалось и работало. И, спустя пару месяцев проектирования, программирования и сборки получился вот такой мини-принтер, с помощью которого можно печатать разнообразные картинки и текст на самых обычных стикерах. Если после просмотра видео Вы загорелись необъятным желанием сделать нечто подобное — прошу под кат.

Что нам понадобится


  • Arduino Uno. Для этого проекта я использовал оригинальную итальянскую плату Arduino Uno. Работу на других платах я гарантировать не могу.
  • Сервоприводы sg-90 — 3 штуки. Это самые дешёвые и маленькие сервоприводы из ныне существующих.
  • Любой стержень. Я использовал для этого алюминиевый пруток 5 мм, который можно купить в любом строительном магазине
  • Клей. В этом проекте я использовал 3 вида клея: столярный для фанеры, супер-клей для соединения шестерней с осями, также мне понадобился термический клей для крепления сервоприводов. Можно так сильно не запариваться и собрать все на какой-нибудь универсальный клей «Момент».
  • Наждачная бумага. Даже при очень точном изготовлении деталей им потребуется подгонка, поэтому без шлифовки — никуда.
  • Станок для лазерной резки(опционально). Если за Вас все детали сделает машина — это хорошо, но вполне реально сделать этот проект дома на коленке, используя обычный ручной лобзик. Первую версию принтера я делал именно так. Для лазерной резки использовалось оборудование 1-го Московского образовательного комплекса.
  • Фломастер. Можно использовать несколько фломастеров для печати цветных картинок
  • Пружинка и оси с маленьким диаметром. Для этого идеально подойдет «щелкающая» ручка, из которой можно достать пружинку и использовать её стержень в качестве оси
  • Фанера 4 мм. Можно найти в строительном или хобби-магазине. Возможно, подойдут и другие варианты, например, оргстекло.
  • Чертежи. Вышлю бесплатно и по первому требованию, если напишите сюда.


Собственно реализация


Изготовление деталей


Этот этап зависит от вашего желания и ваших возможностей. Можете вырезать на станке или выпиливать все детали вручную. Главное, что здесь надо отметить — это то, что качество и работоспособность этого проекта напрямую будет зависеть от вашей аккуратности. Также надо учитывать такой момент: некоторые детали, которые Вы увидите на фотографиях могут немного отличаться от тех, что будут на чертежах. Сразу надо отметить, что детали, показанные на фотографиях могут немного отличаться от тех, что прилагаются в чертежах.



Для тех, кто планирует всё делать вручную
Для того, чтобы изготовить все детали, понадобится: лобзик (лобзиковый станок), дрель (сверлильный станок), принтер, наждачная бумага и надфили.

Самое сложное здесь — это пазо-шиповое соединение деталей и изготовление шестеренок.

Алгоритм здесь такой. распечатываете чертежи, наклеиваете их на фанеру. Затем сверлите все необходимые отверстия в местах внутренних контуров. Контуры пазов следует выпиливать, отступая немного места внутрь, чтобы потом шипы (после небольшой обработка надфилем) вошли в них внатяг. После этого выпиливаем внутренние контуры, отрываем/сошкуриваем бумагу, которую наклеили и шлифуем. Ничего сложного в этом нет, главное — работать аккуратно и не торопиться.

Чтобы этот текст не казался таким сухим, разбавлю его этапами изготовления шестеренки:






Собираем боковые стенки


Пока всё просто: берете детали и склеиваете их. Следите за перпендикулярностью и за тем, чтобы на деталях не оставалось лишнего клея.


Приклеиваем ножки к основанию


Эти ножки представляют только эстетическую ценность. Если вы хотите сэкономить немного времени, то этот этап можно пропустить


Собираем корпус воедино


Текст на передней и задней табличках можете изменить на свой вкус.





Делаем площадку для бумаги


Этот этап требует особой аккуратности и тщательности выполнения работы. Особую сложность представляют «Г»-образные рельсы, на которых и будет держаться вся эта площадка.

Вам нужно аккуратно приклеить тонкие полоски точно по рискам, указанным в чертеже. Для лучшего эффекта я использовал такие необычные «прищепки», но можно спокойно обойтись и без них. Главное — убрать лишний клей и следить за тем, чтобы полоски никуда не съехали. Дальше на них сверху приклеиваем полоски пошире — технология здесь такая же.





После того, как клей высохнет (минимум 20 минут), попробуйте вставить площадку в корпус. Если она зайдет туда, как по маслу и сможет совершать четкие движения вперед-назад, то вам сильно повезло — это именно то, что надо. В противном случае — берём в руки наждачную бумагу и шкурим те места рельсов площадки, на которых она у нас клинит.

С приклеиванием зубчатых реек проблем быть не должно. Следите за тем, чтобы они приклеились перпендикулярно.



Переходим к сборке механизма, удерживающего бумагу. Проблемы не должны возникнуть если собирать всё в такой последовательности. Сначала приклеиваем стойки, на них закрепляем рычажки. Потом к ним приклеиваем полоску фанеры, которая будет удерживать лис бумаги. А уже потом приклеиваем ту часть, в которую будут упираться пружинки.



Ещё немного фотографий, чтобы было попонятнее, как это всё собирать:



Если всё было сделано правильно, то Ваш принтер должен выглядеть примерно вот так:



Протяжный механизм


Вот мы наконец и добрались до шестеренок… Их нужно просто насадить на заранее подготовленный стержень длиной около 110 мм так, как показано на фото ниже и приклеить на супер-клей (его надо наносить равномерно на стенки центрового отверстия шестеренки). Следите за тем, чтобы зубья шестеренок подходили к зубчатой рейке.



После того, как клей высохнет, попробуйте аккуратно прокрутить стержень — площадка должна двигаться.

Устанавливаем первый сервопривод


Первым делом нужно подготовить шестеренку для моторчика. В комплекте с каждым серво идет несколько пластиковых качелек. Их надо обрезать и обточить так, как показано на рисунке ниже. А затем вклеить её в шестерню.



А теперь переходим к установке привода. Для начала задвиньте площадку для бумаги до конца в принтер и прокрутите Ваш серво против часовой стрелки до упора. Затем надо подобрать оптимальное положение привода, то есть закрепить сервопривод так, чтобы у шестеренок не было больших люфтов, но при этом они не упирались друг в друга.



А теперь можно проверить, всё ли у нас работает. Для этого подключите серво к Вашему Arduino через pin9 и загрузите скетч из Образцов: Servo > Sweep. Его код я, на всякий случай, привожу ниже.

Servo sweep
#include <Servo.h>

#define MIN_ANGLE 0
#define MAX_ANGLE 180

Servo servo;

void setup() { 
  servo.attach(9);
} 
 
void loop() { 
  for(int i=MIN_ANGLE; i<=MAX_ANGLE; ++i) {
    servo.write(i); 
    delay(15);
  } 
  for(int i=MAX_ANGLE; i>=MIN_ANGLE; --i) {                                
    servo.write(i);
    delay(15);
  } 
} 


Если всё заработало — замечательно. Если нет — попробуйте поменять положение привода или значения MIN_ANGLE и MAX_ANGLE в коде.

Устанавливаем второй сервопривод


Здесь всё делается аналогично с первым мотором. Единственное отличие — понадобится небольшая ось длиной ~12 мм, чтобы закрепить две шестерни.



Ставим печатающую каретку


Если Вы сумели добраться до этого этапа, то особых трудностей у вас здесь быть не должно. Самое сложное здесь — аккуратно собрать каретку(чтобы она не болталась и не клинила), возможно, придется поработать наждачной бумагой. Следующая возможная проблема — неправильно приклеенная зубчатая рейка. Она должна идти строго параллельно основанию корпуса, клеить ее лучше, когда каретка находится в самом дальнем от сервопривода положении.



Проверяем эту сборку с помощью Arduino и вышеупомянутого скетча, если всё работает, как надо, то переходи к следующему этапу. Если нет, то проблема скорее всего связана с шестеренками или неправильно приклеенной зубчатой рейкой.

Последний сервопривод


На этом этапе собираем рычажок для фломастера. Нужно суметь просверлить сбоку отверстие для шурупа, которым мы будем поджимать винт. Постарайтесь, чтобы в состоянии, когда фломастер опущен, серво имел положение 90 градусов, тогда Вам не придется делать лишних исправлений в коде.



После установки мотора наш принтер будет уже почти готов. Рекомендую заклеить поверхность площадки для бумаги малярным скотчем, в процессе отладки принтера она может серьёзно испачкаться.



Первый запуск и отладка


Для начала мы подключаем серво к нашему Arduino Uno, не обязательно на данном этапе помещать его внутрь принтера, так будет неудобно работать. Первый мотор подключаем к pin2, второй — к pin4, третий — к pin6. Далее загружаем вот этот скетч. Внимание! Данный код не работает на IDE, свежее 1.6.4(весь код я постарался подробно и понятно закомментировать, сильно в его подробности вдаваться не буду):

Разверни меня
#include <Servo.h>

/*
*  MiniPrinter
*  Designed in May 2015
*  By Alexandrow Yegor
*/

//Структура мотора, хранящая диапазон допустимых углов и экземпляр класса Servo
struct Motor {
  int min_angle, max_angle;
  Servo servo;
};

#define IMG_W 64 //Ширина изображения
#define IMG_H 64 //Высота изображения
int STEP = 2;    //Градус поворота серво на 1 пиксель
#define IDLING_MS 4 // Время, тербующееся на поворот в 1 градус
#define WRITING_MS 8

struct Motor motor_x, motor_y, motor_p;
byte img[IMG_W/8]; //Изображение передается построчно
                   //Один пиксель = один бит

struct Motor newMotor(int pin, int a1, int a2) { //Инициализация мотора
  struct Motor m;

  m.servo.attach(pin);
  m.min_angle = a1;
  m.max_angle = a2;

  return m;
}


void gotoStart() {
   motor_p.servo.write(motor_p.max_angle); //Поднятие фломастера
   delay(15);
   
   //Перемещение в точку (0; 0)
   motor_x.servo.write(motor_x.min_angle);
   motor_y.servo.write(motor_y.min_angle); 
   delay(IDLING_MS * STEP * IMG_W + 150);
}

void finish() { //Закрыть принтер
    motor_p.servo.write(motor_p.max_angle); 
    delay(15);
    motor_x.servo.write(motor_x.min_angle);
    motor_y.servo.write(motor_y.max_angle);
    delay(IDLING_MS * STEP * IMG_W + 150);
}

void setup() { //Инициализация
  Serial.begin(9600);
  motor_y = newMotor(2, 50, 180);
  motor_x = newMotor(4, 30, 160);
  motor_p = newMotor(6, 90, 99);
  gotoStart();
}

//Проверяет, есть ли в строке черные пиксели, которые еще не были напечатаны
boolean nextWayIsEmpty(int i) {
  for(; i<IMG_W; ++i) {
    if(!(img[i/8] & (1 << 7-(i%8)))) continue;
    else return false;
  }
  
  return true;
}

void printImg() {
  gotoStart();
  int x = 0;
  for(int y=0; y<IMG_H; ++y) {
      for(int i=0; i<IMG_W/8; ++i) img[i] = Serial.read();
      Serial.write(61);//Эта команда означает, что принтер принял строку с ПК и готов принимать новую
      
      for(int i=0; i<STEP; ++i) { //Каждый пиксель на бумаге имеет длину и ширину, равную STEP
          motor_x.servo.write(motor_x.min_angle); //Переход на новую строку
          motor_y.servo.write(motor_y.min_angle + y*STEP + i); 
          delay(IDLING_MS * STEP * x + 30);
          
          for(x=0; x<IMG_W; ++x) {
            
            if(nextWayIsEmpty(x)) {
              motor_p.servo.write(motor_p.max_angle); delay(IDLING_MS * (motor_p.max_angle-motor_p.min_angle));
              break;
            }
            
            //Определяет положение фломастера(нажат/поднят), исходя из цвеа пикселя
            motor_p.servo.write((img[x/8] & (1 << 7-(x%8))) ? motor_p.min_angle : motor_p.max_angle);
            delay(IDLING_MS * (motor_p.max_angle-motor_p.min_angle));
            
            //Продвижение печатающей каретки по оси X
            motor_x.servo.write(motor_x.min_angle + x*STEP);
            delay(((img[x/8] & (1 << 7-(x%8))) ? WRITING_MS : IDLING_MS) * STEP); 
        }
        
        //Поднять фломастер
        motor_p.servo.write(motor_p.max_angle); delay(IDLING_MS * (motor_p.max_angle-motor_p.min_angle));
      }
    
    //Поднять фломастер
    motor_p.servo.write(motor_p.max_angle); delay(IDLING_MS * (motor_p.max_angle-motor_p.min_angle));
  }
    
  gotoStart();
  Serial.flush();
}

//Проверка входящего сообщения
//ПК может отправлять на принтер команды, состоящие из двух байтов
//Некоторые команды требуют дополнительных параметров

void checkMessage() {
    if(Serial.available()) {
      delay(10);
      byte msg[] = {Serial.read(), Serial.read()};
      
      if(msg[0] == 'P' && msg[1] == 'R') printImg(); //Печать
      if(msg[0] == 'S' && msg[1] == 'T') gotoStart(); //Перейти в точку (0; 0)
      if(msg[0] == 'C' && msg[1] == 'L') finish(); //"Закрыть принтер"
      if(msg[0] == 'S' && msg[1] == 'Z') STEP = Serial.read(); //Определить размер пикселя на бумаге
      if(msg[0] == 'P' && msg[1] == 'T') { // Определить высоту поднятия фломастера (PT+1 - на градус выше, PT-2 на 2 градуса ниже)
        if(Serial.read() == '+') {
          byte b = Serial.read();
          motor_p.min_angle += b-'0';
          motor_p.max_angle += b-'0';
        } else {
          byte b = Serial.read();
          motor_p.min_angle -= b-'0';
          motor_p.max_angle -= b-'0';
        }
      }  
    }
}

void loop() {
  checkMessage();
}



Если Вы до этого момента уже протестировали и отладили все системы, то проблем быть не должно: площадка для бумаги должна слегка дернуться, а печатающая каретка должна занять крайнее левое положение. Если что-то из вышеперечисленного не доходит до краев, или, напротив, упирается в стенки, попробуйте подобрать параметры в функции setup().

Если Вы столкнулись с проблемами посерьёзнее, рекомендую перечитать внимательно весь туториал еще раз, и проверить, все ли Вы сделали правильно. А еще будет неплохо в таком случае проверить каждый моторчик еще раз с помощью первого листинга(Servo sweep).

Далее мы подключаем наш принтер к компьютеру и запускаем вот эту программу (нужна установленная java).

Сначала мы выбираем тот последовательный порт, к которому подключен принтер и нажимаем кнопку «OK», после чего принтер должен слегка подёрнуть площадкой для бумаги. Затем нажимаем кнопку «Log». У нас открылось окно терминала. Далее вводим команду CL — принтер должен задвинуть площадку внутрь. Если ввести ST, то принтер вернется в исходное положение. Также можно отправлять несколько команд подряд, например: CLSTCL. Главное не использовать пробелов и других символов между командами.



Если с эти справились, идём дальше — устанавливаем рычажок с фломастером. Для начала закрепим фломастер на рычаге. расстояние от его кончика до нижней грани рычага должно составлять около 25 мм. После чего нужно вкрутить шуруп так, чтобы фломастер закрепился и никуда не делся. Теперь просто надеваем этот рычаг на ось сервопривода — расстояние от кончика фломастера до бумаги должно составлять около 7 мм. Более точно высоту можно отрегулировать с помощью команд PT+1 и PT-1. Они регулируют угол подъема фломастера, вместо 1 может быть любое число от 0 до 9.

А теперь настал самый ответственный момент — печать первого рисунка. Маленькие картинки разрешения 64x64 подходят лучше всего. Я рекомендую использовать вот этого Марио:


Скачать

Сначала нужно открыть изображение:



С помощью ползунков справа можно настроить контрастность изображения на свой вкус. Стоит помнить, что чем больше чёрных пикселей — тем дольше будет печататься картинка.



Это всё. Потом Вы просто нажимаете кнопку «Print» и любуетесь тем, как работает ваше творение. Единственная возможная проблема на этом этапе — нажатия фломастера. Он будет либо давить слишком сильно, либо не давить совсем. Помимо управления высотой фломастера через терминал, можно попробовать подстроить высоту вручную.

Также вы можете на свой страх и риск поменять значения глобальных переменных в скетче: поиграть со скоростью и размерами печати.

Как только вы будете довольны качеством получаемой картинки, можете смело помещать Arduino в корпус принтера, прикрутив её винтами — на этом Ваш проект можно считать завершенным, можете хвастаться им перед всеми друзьями!

Заключение


На этом всё, надеюсь, вам был интересен этот проект. В нём, конечно есть свои недостатки особенности реализации, так что ещё есть куда стремиться, что совершенствовать и что развивать. Буду рад любым советам, вопросам и пожеланиям в комментариях.
Tags:
Hubs:
Total votes 33: ↑32 and ↓1+31
Comments17

Articles