Control a RepStrap With Processing
by marc.cryan in Circuits > USB
62129 Views, 102 Favorites, 0 Comments
Control a RepStrap With Processing
More specifically this intructable shows how to draw the path of a bouncing ball with a CNC machine. But this is primarly an example of this open source toolchain.
The first few steps of this intstructable describe the basic setup I am using and includes links to additional information. This is followed by more specific intructions and code to creaste the drawings.
Here is a breif outline of the setup I am using.
Setup:
Hardware:
1 - RepStrap McWire Carestion Bot
2 - Stepper drivers with firmware from RepRap
3 - PC power supply
4 - Arduino Duemilanove
Software:
1 - Arduino IDE on a computer
2 - Processing software on a computer
Here are some good books on Arduino and Processing:
Programming Arduino Getting Started with Sketches
Processing: A Programming Handbook for Visual Designers and Artists
**NOTE** 7/2013 -- 3D printing is now pretty popular -- there is now an amazon 3D printing store here Amazon 3D printing store
The Setup
From left to right.
RepStrap
This is "McWire Cartesian Bot v1.2" - It is a version of Tom McGuire's CNC machine which has been adapted by The RepRap group for 3D printing. For simplicity, this Instructable is only using the X-Y axis. You should be able to use any X-Y stage that is driven with stepper motors.
If you would like to build this exact machine, detailed instructions can be found here (http://reprap.org/bin/view/Main/McWire_Cartesian_Bot_1_2) I believe these were posted by Zach Smith of the RepRap team.
Tom McGurie's original instructable is here (https://www.instructables.com/id/Easy-to-Build-Desk-Top-3-Axis-CNC-Milling-Machine/)
"Stepper Drivers"
These driver boards are also the work of the RepRap team. They are "Stepper Motor Driver V1.2" Designed by Zach Hoeken. Full instructions can be found at http://reprap.org/bin/view/Main/Stepper_Motor_Driver_1_2. These include ports for optical endstops. This is a very nice feature, but I am not using it here.
They have new version - Stepper Motor Driver v2.3 which is availble here (http://store.makerbot.com/featured-products/stepper-motor-driver-v2-3-kit.html).
SparkFun is stocking a good looking driver as well (http://www.sparkfun.com/commerce/product_info.php?products_id=9402).
"Power"
The power to the drivers is from a desktop computer power supply. RepRap will also tell you how to do this!
http://dev.www.reprap.org/bin/view/Main/PCPowerSupply
"Arduino with breakout"
This setup is using an Arduino Duemilanove with an Atmega 168 chip. Everything should work with other Arduino's and clones.
This breakout is also from RepRap. It is "Arduino Breakout v1.4" also Designed by Zach Hoeken. And available here. http://store.makerbot.com/electronics/pcbs/arduino-breakout-v1-4-pcb.html
Spark fun is stocking a nice screw sheild as well (http://www.sparkfun.com/commerce/product_info.php?products_id=9282)
"USB cable to PC"
The Arduino is connected to a desktop computer with a USB cable.
"Software on the PC"
I'm running an Ubunto distribution of Linux on my PC, the operating system shouldn't really matter since this project using Arduino and Processing software which is designed to run on most platforms (Linux, Mac, Windows).
If you don't already have them, You will need to load the Arduino and Processing software packages.
http://www.arduino.cc/
http://processing.org/
Build a Pen Holder
To build a quick Sharpie holder. Drill a 1/2" hole in through one end of a strip of wood or plastic (approx 1/4?X1.5"X6"). Wrap a bit of duct tape around the Sharpie for traction. Then stick the sharpie through the hold and clip it inplace with two binder clips, as shown below. This holds reasonalby will and can be quickly adjusted. Once you are ready to start "printint" you just clamp the strip of wood to the fram of the Z-axis, or whatever is handy.
Arduino Wiring:
It is important to have stepper motor drivers wired to the stepper motor correctly. In this case, 'correclty' means that the Arduino to Stepper driver connections match the pins called out in the Arduino and Processing Code that follows.
Wire the stepper motor to the Arduino like this:
X - Driver "Arduino" "What it does"
Pin 3 2 Step
Pin 4 3 Direction
Y - Driver "Arduino" "What it does"
Pin 3 10 Step
Pin 4 7 Direction
This is the same setup used by RepRap.org (http://make.rrf.org/electronics-2.0). So if you have a half built RapRap you are all set!
Arduino Code
This project requires to peices of code. 'Firmware' that is loaded onto the Arduino microcontroller. And 'Software' that is run by the Processing program on the PC.
The chain of command goes like this - Processing -> Ardcuino Board -> Steppr drivers -> stepper motors -> X-Y stages.
Here is a little more detail of the communication that will be happening, based on my understanding of the way things work....
The Processing software will send commands to the Arduino board over the USB cable. T The Arduino board will take the command and set the specifed output pins to high or low logic states (5V or 0V). The stepper drivers are wired to the Arduino board outputs. So when the drivers see the high and low signals, they send power to the approriate coils in the stepper motors, making them go.
The code below sets up the Arduino board to listen for the commands sent from Processing and then take actions.
You can cut and paste the code in the Arduino IDE. Then verify the code by clicking the 'Play" button. If there are no errors you can then upload it to the board by pressing the upload button - which looks like a right arrow.
// Arduino code:
// Read data from the serial and turn ON or OFF a light depending on the value
//and control stepper motor on RepStrap
char val; // Data received from the serial port
int ledPin = 13; // Set the pin to digital I/O 13
#define XstepPin 10
#define XdirPin 7
#define YstepPin 2
#define YdirPin 3
void setup() {
pinMode(ledPin, OUTPUT); // Set pin as OUTPUT
pinMode(XstepPin, OUTPUT);
pinMode(XdirPin, OUTPUT);
pinMode(YstepPin, OUTPUT);
pinMode(YdirPin, OUTPUT);
Serial.begin(9600); // Start serial communication at 9600 bps
}
void loop() {
if (Serial.available()) { // If data is available to read,
val = Serial.read(); // read it and store it in val
}
if (val == 'H') { // If H was received
digitalWrite(ledPin, HIGH); // turn the LED on
digitalWrite(XdirPin,HIGH);
digitalWrite(XstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
delayMicroseconds(2);
}
if(val == 'h') {
digitalWrite(ledPin, HIGH); // turn the LED on
digitalWrite(YdirPin,HIGH);
digitalWrite(YstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(YstepPin,LOW);
delayMicroseconds(2);
}
if (val == 'L'){
digitalWrite(ledPin, LOW); // Otherwise turn it OFF
digitalWrite(XdirPin,LOW);
digitalWrite(XstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
delayMicroseconds(2);
}
if (val == 'l'){
digitalWrite(ledPin, LOW); // Otherwise turn it OFF
digitalWrite(YdirPin,LOW);
digitalWrite(YstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(YstepPin,LOW);
delayMicroseconds(2);
}
delayMicroseconds(1000); // <<<<<< USE TO CHANGE SPEED <<<<<<<<
}
Processing Code
Here is the Processing code. Open Processing and cut and paste the code.
This code is built on an example from the Processing text book written by Casey Reas and Ben Fry. (http://www.amazon.com/Processing-Programming-Handbook-Designers-Artists/dp/0262182629)
//This outputs the motion of the bouncing ball to the RepRap X and Y axis
import processing.serial.*;
Serial myPort; // Create object from Serial class
int val; // Data received from the serial port
float x= 50.0;
float y = 50.0;
float speedX = 1.0;
float speedY= .4;
float radius = 15.0;
int timeDelay = 10; //10 ms delay between direction prints gives rocky step
int directionX = 1;
int directionY = 1;
void setup(){
size(100, 100);
smooth();
noStroke();
ellipseMode(RADIUS);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}
void draw(){
fill(0,12);
rect(0,0,width, height);
fill(255);
ellipse(x, y, radius, radius);
x += speedX * directionX;
if (directionX == 1){ //if the direction is up, the motor goes one way
myPort.write('H');
print('H');
delay(100);
}
else { // if the direction is down, the motor goes the other way
myPort.write('L'); // send an L otherwise
print('L');
delay(100);
}
if((x>width-radius) || (x < radius)) {
directionX = -directionX;
}
y += speedY * directionY;
if (directionY == 1){ //if the direction is up, the motor goes one way
myPort.write('h');
print('h');
delay(100);
}
else { // if the direction is down, the motor goes the other way
myPort.write('l'); // send an L otherwise
print('l');
delay(100);
}
if((y>height-radius) || (y < radius)) {
directionY = -directionY;
}
}
Run the Processing Code
Confirm that power is OFF to the stepper motors.
Run the Processing code by clicking the 'Play' button.
You should see:
1 - A display window on the computer monitor showing the ball slowly bouncing around.
2 - The LEDs on the Arduino and stepper boards blinking.
The H,h,L,l outputs are printed on the bottom of the screen, they are also sent over the USB cable to the Aduino board.
Draw!
Now that the path of the bouncing ball is being sent to the Arduino Board, it is time to set up the paper and pen.
- Tape a peice of paper to the X-Y platform.
- Clip a pen to the Z-axis frame.
-Check that the ball is still bouning and the lights are still blining.
-Turn on power to the stepper motors!
-Now the X-Y platform will move about following the path of the bouncing ball. Yay!
Make Changes
Now you can mess around with the code to change the behavior of the ball, which will change the comands to the robot and it will draw somehting different.
Changing the size of the box will make a bigger drawing.
You can also:
Change speed
Change the X or Y bounce
You will probably notice that the steppers are not running very smoothly. I'll post some new code once this is fixed.
Alternatives and ideas:
-You can do almost this exact instructable using the Arduino "Frimata" library. I don't know if there is much advantage either way.
-Ultimately would like to print 3D objects that are generated from Processing. I'd like to print 3D fractals, but I am a long way off for now!
Making It a Little Better
As I make improvements I am going to add steps to the end of this instructable.
10-11-09
The previus code tracks the ball pretty well but the steppers are driven in a very rocky way.
In this updated code, the Proccessing sketch only sends a signal to the Arduino when there is a change in direction. So the stepper motors just run in whatever direction they were last set to, until they are told to change. This allows the motors to run alot faster and smoother.
But there is still somthing a bit off. The Arduino seems to miss some of the signals, so the RepStrap ends up with the X-Y stage pushed all the way to one side.
I will try to fix this.
The Arduino and Processing code is below. I have added more comments to help keep things clear.
/////////////////////////////////////////////////
// Arduino code:
// Read data from the serial
//Use data to determine direction of X and Y steppers
#define XstepPin 10
#define XdirPin 7
#define YstepPin 2
#define YdirPin 3
int val; // Data received from the serial port
void setup() {
pinMode(XstepPin, OUTPUT);
pinMode(XdirPin, OUTPUT);
pinMode(YstepPin, OUTPUT);
pinMode(YdirPin, OUTPUT);
Serial.begin(9600); // Start serial communication at 9600 bps
}
void loop() {
int dirX;
int dirY;
if (Serial.available()) { // If data is available to read,
val = Serial.read(); // then read it and store value in val
}
if (val == 'H'){ //set the direction of X and Y based on data sent from Processing
dirX = HIGH;
}
if (val == 'L'){
dirX = LOW;
}
if (val == 'h'){
dirY = HIGH;
}
if (val == 'l'){
dirY = LOW;
}
digitalWrite(XdirPin,dirX); //set X direction
digitalWrite(YdirPin,dirY); //set Y direction
digitalWrite(XstepPin,HIGH); //take steps
digitalWrite(YstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
digitalWrite(YstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE SPEED <<<<<<<<
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Processing Code:
//This outputs the motion of the bouncing ball to the RepRap X and Y axis
import processing.serial.*;
Serial myPort; // Create object from Serial class
int val;
float x= 50.0;
float y = 50.0;
float speedX = 1.0;
float speedY= .4;
float radius = 15.0;
int directionX = 1;
int directionY = 1;
int old_directionX = 1;
int old_directionY = 1;
void setup(){
size(100, 100);
smooth();
noStroke();
ellipseMode(RADIUS);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}
void draw(){
fill(0,12);
rect(0,0,width, height);
fill(255);
ellipse(x, y, radius, radius);
x += speedX * directionX;
if((x>width-radius) || (x < radius)) { //change X direction if the ball hits the side of the box
directionX = -directionX;
}
y += speedY * directionY;
if((y>height-radius) || (y < radius)) { //change Y direction if the ball hits the side of the box
directionY = -directionY;
}
if ((directionX != old_directionX) && (directionX == 1)){ //if X direction changed print H
myPort.write('H');
print('H');
delay(100);
}
if ((directionX != old_directionX) && (directionX == -1)){ //if X direction changed print h
myPort.write('h');
print('h');
delay(100);
}
if ((directionY != old_directionY) && (directionY == 1)){ //if Y direction changed print L
myPort.write('L');
print('L');
delay(100);
}
if ((directionY != old_directionY) && (directionY == -1)){ //if Y direction changed print l
myPort.write('l');
print('l');
delay(100);
}
old_directionX = directionX ; //stores the directions we just used, as the old direction
old_directionY= directionY;
//delay(100); //uncomment to slow the whole thing down for troubleshooting
}
Doing It With Firmata
You can learn more about it here:
http://arduino.cc/en/Reference/Firmata
and here:
http://firmata.org/wiki/Main_Page
I don't understand the details. But, from my experience so far, I do know that is does a good job of handling the communication between the PC and the Arduino. So, I am happy to ignore the details for now.
I have started by modifying an example that is included in theArduinoIDE called "SimpleDigitalFirmata". Once this code is loaded onto the Arduino, You can communicate with the digital pins on the Arduino board directly from Processing.
In processing I am working from an example that turns on and off an LED on the Arduino board when you move a mouse over a square dawn of the monitor of the PC.
To make Processing control the LED and a Stepper motor, you need to modiify the code for the Arduino and Processing.
On the Arduino side - I've defined the stepper pins at the top of the sketch. Then I jammed in code to drive the stepper at the end of the main loop. I put this outside of the code that reads the serial port. So the stepper will just go.
The direction of the stepper is set directly from Processing. The example code turned on and off an LED on pin13. So, I just made the step direction pin do the same thing as the LED.
Here is the code. This only controls the X stepper.
///////////////// ARDUINO //////////////////////
//Supports as many digital inputs and outputs as possible.
//
#include
#define XstepPin 10 //Step pin
#define XdirPin 7 //Direction pin
byte previousPIN[2]; // PIN means PORT for input
byte previousPORT[2];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if(previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
void setPinModeCallback(byte pin, int mode) {
if(pin > 1) { // don't touch RxTx pins (0,1)
pinMode(pin, mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if(value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
outputPort(0, PIND &~ B00000011); // pins 0-7, ignoring Rx/Tx pins (0/1)
outputPort(1, PINB); // pins 8-13
while(Firmata.available()) {
Firmata.processInput();
}
//added stepper sequence to firmata code
digitalWrite(XstepPin,HIGH); //take steps
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE STEPPER SPEED <<<<<<<<
}
//////////////////////////////////Processing/////////////////////////////////
/**
* Simple Write. < modified to control stepper direction
*
* Check if the mouse is over a rectangle and writes the status to the serial port.
* This example works with the Wiring / Arduino program that follows below.
*/
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
//Serial myPort; // Create object from Serial class
// Data received from the serial port
int ledPin = 13; // pin for LED
int XdirPin = 7; // pin for stepper X direction
int XstepPin = 10; //pin for X step
void setup()
{
size(200, 200);
// I know that the first port in the serial list on my mac
// is always my FTDI adaptor, so I open Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you're using.
// String portName = Serial.list()[0];
//myPort = new Serial(this, portName, 9600);
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(ledPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
}
void draw() {
background(255);
if (mouseOverRect() == true) { // If mouse is over square,
fill(204); // change color and
arduino.digitalWrite(ledPin, Arduino.HIGH); // LED on
arduino.digitalWrite(XdirPin, Arduino.HIGH); // Stepper direction this way
}
else { // If mouse is not over square,
fill(0); // change color and
arduino.digitalWrite(ledPin, Arduino.LOW); // LED off
arduino.digitalWrite(XdirPin, Arduino.LOW); //Stepper direction the other way
}
rect(50, 50, 100, 100); // Draw a square
}
boolean mouseOverRect() { // Test if mouse is over square
return ((mouseX >= 50) && (mouseX <= 150) && (mouseY >= 50) && (mouseY <= 150));
}
Correction to Firmata....
The code below works - but doesn't have as many comments.
//////////////////////////Arduino//////////////////////////
// Wiring/Arduino code:// simmple digital firmata
//Supports as many digital inputs and outputs as possible.
//This example code is in the public domain.
#include <Firmata.h>
#define XstepPin 10
#define XdirPin 7
#define YstepPin 2
#define YdirPin 3
byte previousPIN[2]; // PIN means PORT for input
byte previousPORT[2];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if(previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
void setPinModeCallback(byte pin, int mode) {
if(pin > 1) { // don't touch RxTx pins (0,1)
pinMode(pin, mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if(value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
outputPort(0, PIND &~ B00000011); // pins 0-7, ignoring Rx/Tx pins (0/1)
outputPort(1, PINB); // pins 8-13
while(Firmata.available()) {
Firmata.processInput();
}
//added stepper sequence to firmata code
digitalWrite(XstepPin,HIGH); //take steps
digitalWrite(YstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
digitalWrite(YstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE STEPPER SPEED???? <<<<<<<<
}
/////////////////////////////PROCESSING//////////////////////////////////////
/**
* Simple Write.
*
* Check if the mouse is over a rectangle and writes the status to the serial port.
* This example works with the Wiring / Arduino program that follows below.
*/
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
//Serial myPort; // Create object from Serial class
// Data received from the serial port
int ledPin = 13;
int XstepPin = 10;
int XdirPin = 7;
void setup()
{
size(200, 200);
// I know that the first port in the serial list on my mac
// is always my FTDI adaptor, so I open Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you're using.
// String portName = Serial.list()[0];
//myPort = new Serial(this, portName, 9600);
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(ledPin, Arduino.OUTPUT);
arduino.pinMode(XstepPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
}
void draw() {
background(255);
if (mouseOverRect() == true) { // If mouse is over square,
fill(204); // change color and
arduino.digitalWrite(ledPin, Arduino.HIGH); // send an H to indicate mouse is over square
arduino.digitalWrite(XdirPin, Arduino.HIGH);
//arduino.digitalWrite(XstepPin, Arduino.HIGH);
//delay(1);
//arduino.digitalWrite(XstepPin, Arduino.LOW);
//delay(1);
}
else { // If mouse is not over square,
fill(0); // change color and
arduino.digitalWrite(ledPin, Arduino.LOW); // send an L otherwise
arduino.digitalWrite(XdirPin, Arduino.LOW);
//arduino.digitalWrite(XstepPin, Arduino.HIGH);
//delay(1);
//arduino.digitalWrite(XstepPin,Arduino.LOW);
//delay(1);
}
rect(50, 50, 100, 100); // Draw a square
}
boolean mouseOverRect() { // Test if mouse is over square
return ((mouseX >= 50) && (mouseX <= 150) && (mouseY >= 50) && (mouseY <= 150));
}
Corrected and Commented
This should work and includes comments.
///////////////// ARDUINO //////////////////////
*<modified to drive a stepper motor
* This example code is in the public domain.
*/
#include <Firmata.h>
#define XstepPin 10 //Step pin
#define XdirPin 7 //Direction pin
byte previousPIN[2]; // PIN means PORT for input
byte previousPORT[2];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if(previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
void setPinModeCallback(byte pin, int mode) {
if(pin > 1) { // don't touch RxTx pins (0,1)
pinMode(pin, mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if(value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
outputPort(0, PIND &~ B00000011); // pins 0-7, ignoring Rx/Tx pins (0/1)
outputPort(1, PINB); // pins 8-13
while(Firmata.available()) {
Firmata.processInput();
}
//added stepper sequence to firmata code
//digitalWrite(XdirPin,HIGH);
digitalWrite(XstepPin,HIGH); //take steps
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE STEPPER SPEED <<<<<<<<
}
//////////////////////PROCESSING///////////////////////////////////
//
// Simple Write. < modified to control stepper direction
//
//Check if the mouse is over a rectangle and writes the status to the serial port.
//This example works with the Wiring / Arduino program that follows below.
//
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
//Serial myPort; // Create object from Serial class
// Data received from the serial port
int ledPin = 13; // pin for LED
int XdirPin = 7; // pin for stepper X direction
int XstepPin = 10;
void setup()
{
size(200, 200);
// I know that the first port in the serial list on my mac
// is always my FTDI adaptor, so I open Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you're using.
// String portName = Serial.list()[0];
//myPort = new Serial(this, portName, 9600);
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(ledPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
}
void draw() {
background(255);
if (mouseOverRect() == true) { // If mouse is over square,
fill(204); // change color and
arduino.digitalWrite(ledPin, Arduino.HIGH); // LED on
arduino.digitalWrite(XdirPin, Arduino.HIGH); // Stepper direction this way
}
else { // If mouse is not over square,
fill(0); // change color and
arduino.digitalWrite(ledPin, Arduino.LOW); // LED off
arduino.digitalWrite(XdirPin, Arduino.LOW); //Stepper direction the other way
}
rect(50, 50, 100, 100); // Draw a square
}
boolean mouseOverRect() { // Test if mouse is over square
return ((mouseX >= 50) && (mouseX <= 150) && (mouseY >= 50) && (mouseY <= 150));
}
Follow the Bouncing Ball With Firmata! (this Works Good)
Now I've taken the bouncing ball sketch and combined it with the Firmata one.
Now the stepper motors smoothly follow the path of the bouncing ball!!
Now we are getting somewhere!
//////////////////////////Arduino//////////////////////////
// Wiring/Arduino code:// simmple digital firmata
//Supports as many digital inputs and outputs as possible.
//This example code is in the public domain.
#include <Firmata.h>
#define XstepPin 10
#define XdirPin 7
#define YstepPin 2
#define YdirPin 3
byte previousPIN[2]; // PIN means PORT for input
byte previousPORT[2];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if(previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
void setPinModeCallback(byte pin, int mode) {
if(pin > 1) { // don't touch RxTx pins (0,1)
pinMode(pin, mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if(value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
outputPort(0, PIND &~ B00000011); // pins 0-7, ignoring Rx/Tx pins (0/1)
outputPort(1, PINB); // pins 8-13
while(Firmata.available()) {
Firmata.processInput();
}
//added stepper sequence to firmata code
digitalWrite(XstepPin,HIGH); //take steps
digitalWrite(YstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
digitalWrite(YstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE STEPPER SPEED???? <<<<<<<<
}
///////////////////PROCESSING//////////////////////////////
/**
* Simple Write.
*
* Check if the mouse is over a rectangle and writes the status to the serial port.
* This example works with the Wiring / Arduino program that follows below.
*/
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
//Serial myPort; // Create object from Serial class
// Data received from the serial port
int ledPin = 13;
int XstepPin = 10;
int XdirPin = 7;
int YstepPin = 2;
int YdirPin = 3;
//parameters for the ball
float x= 50.0;
float y = 50.0;
float speedX = 1.0;
float speedY= .4;
float radius = 15.0;
int directionX = 1;
int directionY = 1;
void setup()
{
//setup the drawing
size(100, 100);
smooth();
noStroke();
ellipseMode(RADIUS);
//setup communication with Arduino
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(ledPin, Arduino.OUTPUT);
arduino.pinMode(XstepPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
arduino.pinMode(YstepPin, Arduino.OUTPUT);
arduino.pinMode(YdirPin, Arduino.OUTPUT);
}
void draw() {
fill(0,12);
rect(0,0,width, height);
fill(255);
ellipse(x, y, radius, radius);
x += speedX * directionX;
if((x>width-radius) || (x < radius)) { //change X direction if the ball hits the side of the box
directionX = -directionX;
}
y += speedY * directionY;
if((y>height-radius) || (y < radius)) { //change Y direction if the ball hits the side of the box
directionY = -directionY;
}
if (directionX >= 0){
arduino.digitalWrite(ledPin, Arduino.HIGH);
arduino.digitalWrite(XdirPin, Arduino.HIGH);
}
else{
arduino.digitalWrite(ledPin, Arduino.LOW);
arduino.digitalWrite(XdirPin, Arduino.LOW);
}
if (directionY >= 0){
//arduino.digitalWrite(ledPin, Arduino.HIGH);
arduino.digitalWrite(YdirPin, Arduino.HIGH);
}
else{
//arduino.digitalWrite(ledPin, Arduino.LOW);
arduino.digitalWrite(YdirPin, Arduino.LOW);
}
}
Working Towards a 3D Print
The RepRap builds up 3D objects with melted plastic. Each layer is laid down as a plastic mesh. There is softwar/firmware availalbe to generate this mesh for any 3D object. But I am interested in making a program that will print the shapes directly from Processing.
I'm starting by modifiying the bouncing ball sketch to draw a mesh that will cover a 2D shape.
The next step will be to rotated the mesh 90deg and repeat (to print the next layer).
Here is what I have so far - this is only in Processing and does not include any communitation with the Arduino.
///////////////////Processing/////////////
/**
*ball traces back and forth across the window
*/
int size = 10; // Width of the shape
float xpos, ypos; // Starting position of shape
float xspeed = 3; // Speed of the shape
float yspeed = 0;// Speed of the shape
int xdirection = 1; // Left or Right
int ydirection = 1; // Top to Bottom
void setup()
{
size(200, 200);
noStroke();
frameRate(30);
smooth();
// Set the starting position of the shape
xpos = 0;
ypos = 0;
background(102);
}
void draw()
{
//background(102);
// Update the position of the shape
xpos = xpos + ( xspeed * xdirection );
ypos = ypos + ( yspeed * ydirection );
// change direction when the ball hits the edge - to draw a square
//starts at left and moves towards right
//move down 10 steps
if (xpos > width-size && ypos== 0) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==20){
xdirection = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==20) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if(xpos < 0 && ypos ==40){
xdirection = 1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos > width-size && ypos ==40) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==60){
xdirection = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==60) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 200X edge
if(xpos < 0 && ypos ==80){
xdirection = 1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos > width-size && ypos ==80) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==100){
xdirection = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==100) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if (xpos < 0 && ypos ==120) { //down
xdirection=1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > width-size && ypos ==120) {
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 0X edge
if (xpos > width-size && ypos ==140) {
xdirection=-1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos < 0 && ypos ==140) { //down
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if (xpos < 0 && ypos ==160) { //down
xdirection=1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > width-size && ypos ==160) {
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 200X edge
if (xpos > width-size && ypos ==180) {
xdirection=-1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > 0 && ypos ==180) {
ydirection=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 200x edge
if (xpos > 0 && ypos ==200) {
xdirection=1;
yspeed =0;
xspeed=2;
}
// Draw the shape
ellipse(xpos+size/2, ypos+size/2, size, size);
}
Working Towards a 3D Print ....
The code is step 14 needs to be tweaked a bit in order to the stepper motors. The main change that whenever the balls speed is zero - the corresponding stepper needs to be disabled.
Here is the processing code. This only runs one layer, many layers need to be stacked to make a 3D print.
///////////////////Processing/////////////
/**
*ball traces back and forth across the window in a mesh that will be useful for plastic printing
*/
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
int ledPin = 13;
int XstepPin = 2;
int XdirPin = 3;
int XenablePin = 4;
int YstepPin = 7;
int YdirPin = 10;
int YenablePin = 8;
int size = 10; // Width of the shape
float xpos, ypos; // Starting position of shape
float xspeed = 3; // Speed of the shape
float yspeed = 0;// Speed of the shape
int directionX = 1; // Left or Right
int directionY = 1; // Top to Bottom
void setup()
{
size(200, 200);
noStroke();
frameRate(30);
smooth();
// Set the starting position of the shape
xpos = 0;
ypos = 0;
background(102);
//setup communication with Arduino
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(XstepPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
arduino.pinMode(YstepPin, Arduino.OUTPUT);
arduino.pinMode(YdirPin, Arduino.OUTPUT);
arduino.pinMode(EstepPin, Arduino.OUTPUT);
arduino.pinMode(EdirPin, Arduino.OUTPUT);
arduino.pinMode(EenablePin, Arduino.OUTPUT);
}
void draw()
{
//background(102);
// Update the position of the shape
xpos = xpos + ( xspeed * directionX );
ypos = ypos + ( yspeed * directionY );
// change direction when the ball hits the edge - to draw a square
//starts at left and moves towards right
//move down 10 steps
if (xpos > width-size && ypos== 0) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==20){
directionX = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==20) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if(xpos < 0 && ypos ==40){
directionX = 1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos > width-size && ypos ==40) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==60){
directionX = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==60) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 200X edge
if(xpos < 0 && ypos ==80){
directionX = 1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos > width-size && ypos ==80) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hits the 0X edge
if(xpos>width-size && ypos ==100){
directionX = -1;
yspeed = 0;
xspeed =2;
}
//move down 10 steps
if (xpos < 0 && ypos ==100) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if (xpos < 0 && ypos ==120) { //down
directionX=1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > width-size && ypos ==120) {
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 0X edge
if (xpos > width-size && ypos ==140) {
directionX=-1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos < 0 && ypos ==140) { //down
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse X direction until it hiths the 200X edge
if (xpos < 0 && ypos ==160) { //down
directionX=1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > width-size && ypos ==160) {
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 200X edge
if (xpos > width-size && ypos ==180) {
directionX=-1;
yspeed =0;
xspeed=2;
}
//move down 10 steps
if (xpos > 0 && ypos ==180) {
directionY=1;
yspeed =2;
xspeed=0;
}
//reverse direction until it hits teh 200x edge
if (xpos > 0 && ypos ==200) {
directionX=1;
yspeed =0;
xspeed=2;
}
//TELL ARDUINO WHICH WAY TO SEND MOTORS
if (directionX >= 0){
arduino.digitalWrite(ledPin, Arduino.HIGH);
arduino.digitalWrite(XdirPin, Arduino.HIGH);
}
else{
arduino.digitalWrite(ledPin, Arduino.LOW);
arduino.digitalWrite(XdirPin, Arduino.LOW);
}
if (directionY >= 0){
//arduino.digitalWrite(ledPin, Arduino.HIGH);
arduino.digitalWrite(YdirPin, Arduino.HIGH);
}
else{
//arduino.digitalWrite(ledPin, Arduino.LOW);
arduino.digitalWrite(YdirPin, Arduino.LOW);
}
if (xspeed == 0) {
arduino.digitalWrite(XenablePin, Arduino.HIGH);
}
else{
arduino.digitalWrite(XenablePin, Arduino.LOW);
}
if (yspeed == 0) {
arduino.digitalWrite(YenablePin, Arduino.HIGH);
}
else{
arduino.digitalWrite(YenablePin, Arduino.LOW);
}
// Draw the shape
ellipse(xpos+size/2, ypos+size/2, size, size);
}
Pinch Wheel Extruder
This includes a stepper motor. I am driving it with an "EasyDriver" I bought from SparkFun. It is EasyDriver V4 co-designed with Brian Schmalz. It has breakouts for microstepping and does not get too hot.
http://www.sparkfun.com/commerce/product_info.php?products_id=9402.
Here are some images of the extruder. If anyone is intersted I can post a template for the layout.
Extruder Temp Control and Code
To get a usable stream of melted plastic out of the extruder, the tempurature has to be hot enough to melt the plastic, but not so hot that it starts to smoke and burn. You also only want to drive the stock material while the temp is within this range.
Arduino code (this is just a modifed version of some RepRap code). This allows you to set a temperature range and then only runs the extruder motor when the heater within range.
As shown below, the Raw tempurate reading is used. Actual temp can also be used.
//////////////////Arduino Code///////////////////
//this example keeps temp above melting point of HDPE - but not too hot
//turns off stepper if temp is too low [NOTE: using EasyDriver board, pull enable pin HIGH to turn off
//turn heat off if too hot
//turn heat way up if too cold
//turn heat to medium if
//webpage http://dev.www.reprap.org/bin/view/Main/Temperature_Sensor_1_1
// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)
// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)
// ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=4066 --max-adc=1023
#define THERMISTOR_PIN 0
#define heatPin 11 //heat level
#define EstepPin 5 //set extruder stepper to pin12
#define EdirPin 6 //set step direction to stepper pin11
#define EenablePin 12 //
byte lowTemp = 245; // >>> if below lowTemp then extruder is disabled - degF
byte minTemp = 245; //>>>> SET MIN TEMP HERE >>>>>> - degF
byte maxTemp = 250; //>>>> SET MAX TEMP HERE >>>>>> - degF
byte maxRaw = 125;
byte minRaw = 130;
byte cool = 0; // heater off
byte warm = 255; //medium heat
byte hot = 255; //heat up pretty high >> max would be 255, but I don't want to burn out the heater
// r0: 100000
// t0: 25
// r1: 0
// r2: 4700
// beta: 4066
// max adc: 1023
#define NUMTEMPS 20
short temptable[NUMTEMPS][2] = {
{1, 841},
{54, 255},
{107, 209},
{160, 184},
{213, 166},
{266, 153},
{319, 142},
{372, 132},
{425, 124},
{478, 116},
{531, 108},
{584, 101},
{637, 93},
{690, 86},
{743, 78},
{796, 70},
{849, 61},
{902, 50},
{955, 34},
{1008, 3}
};
void setup()
{
Serial.begin(9600);
Serial.println("Starting temperature exerciser.");
pinMode(heatPin, OUTPUT);
pinMode(EstepPin, OUTPUT);
pinMode(EdirPin, OUTPUT);
pinMode(EenablePin, OUTPUT);
}
void loop()
{
int rawvalue = analogRead(THERMISTOR_PIN);
int celsius = read_temp();
int fahrenheit = (((celsius * 9) / 5) + 32);
Serial.print("Current temp: ");
Serial.print(celsius);
Serial.print("C / ");
Serial.print(fahrenheit);
Serial.println("F");
Serial.print("Raw value: ");
Serial.println(rawvalue);
Serial.println(" ");
//
//control the extruder heater and stepper motor based on the temperature
//
if (rawvalue >= minRaw){ //if temp too low - turn stepper off
analogWrite(heatPin, hot); //if temp too low- turn heat to max
digitalWrite(EenablePin,HIGH); //if temp too low - turn off the stepper
//??? Stop everything if temp too low ???
}
// if((fahrenheit >=lowTemp) && (fahrenheit <=minTemp)){ // if temp below range- LED slow blink
// analogWrite(heatPin, hot); //if temp below range - turn extruder up to max
// digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
// }
// if ((fahrenheit >= minTemp) && (fahrenheit <=maxTemp)){ //if temp in range - LED ON
// analogWrite(heatPin, warm); //if temp is in range - keep extruder warm
// digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
// }
if ((rawvalue <= minRaw)){ //if temp in range - LED ON
analogWrite(heatPin, warm); //if temp is in range - keep extruder warm
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
if(rawvalue <=maxRaw){ //if temp above range -LED fast blink
analogWrite(heatPin, cool); //if temp too high - turn heater off
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
//run extruder stepper motor
digitalWrite(EdirPin,LOW);
digitalWrite(EstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(EstepPin,LOW);
delay(15);
// delay(1000);
}
int read_temp()
{
int rawtemp = analogRead(THERMISTOR_PIN);
int current_celsius = 0;
byte i;
for (i=1; i if (temptable[i][0] > rawtemp)
{
int realtemp = temptable[i-1][1] + (rawtemp - temptable[i-1][0]) * (temptable[i][1] - temptable[i-1][1]) / (temptable[i][0] - temptable[i-1][0]);
if (realtemp > 255)
realtemp = 255;
current_celsius = realtemp;
break;
}
}
// Overflow: We just clamp to 0 degrees celsius
if (i == NUMTEMPS)
current_celsius = 0;
return current_celsius;
}
Second Round of Extruder Code
Here is a second pass at the extruder code - If the first doesn't work, then try this.
///////////// Arduino Only///////////////////////
//////////////////Arduino Code///////////////////
//this example keeps temp above melting point of HDPE - but not too hot
//turns off stepper if temp is too low [NOTE: using EasyDriver board, pull enable pin HIGH to turn off
//turn heat off if too hot
//turn heat way up if too cold
//turn heat to medium if
//webpage http://dev.www.reprap.org/bin/view/Main/Temperature_Sensor_1_1
// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)
// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)
// ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=4066 --max-adc=1023
#define THERMISTOR_PIN 0
#define heatPin 11 //heat level
#define EstepPin 5 //set extruder stepper to pin12
#define EdirPin 6 //set step direction to stepper pin11
#define EenablePin 12 //
byte lowTemp = 175; // >>> if below lowTemp then extruder is disabled - degF
byte minTemp = 180; //>>>> SET MIN TEMP HERE >>>>>> - degF
byte maxTemp = 185; //>>>> SET MAX TEMP HERE >>>>>> - degF
byte maxRaw = 110;
byte minRaw = 120;
byte cool = 0; // heater off
byte warm = 255; //medium heat
byte hot = 255; //heat up pretty high >> max would be 255, but I don't want to burn out the heater
// r0: 100000
// t0: 25
// r1: 0
// r2: 4700
// beta: 4066
// max adc: 1023
#define NUMTEMPS 20
short temptable[NUMTEMPS][2] = {
{1, 841},
{54, 255},
{107, 209},
{160, 184},
{213, 166},
{266, 153},
{319, 142},
{372, 132},
{425, 124},
{478, 116},
{531, 108},
{584, 101},
{637, 93},
{690, 86},
{743, 78},
{796, 70},
{849, 61},
{902, 50},
{955, 34},
{1008, 3}
};
void setup()
{
Serial.begin(9600);
Serial.println("Starting temperature exerciser.");
pinMode(heatPin, OUTPUT);
pinMode(EstepPin, OUTPUT);
pinMode(EdirPin, OUTPUT);
pinMode(EenablePin, OUTPUT);
}
void loop()
{
int rawvalue = analogRead(THERMISTOR_PIN);
int celsius = read_temp();
int fahrenheit = (((celsius * 9) / 5) + 32);
Serial.print("Current temp: ");
Serial.print(celsius);
Serial.print("C / ");
Serial.print(fahrenheit);
Serial.println("F");
Serial.print("Raw value: ");
Serial.println(rawvalue);
Serial.println(" ");
//
//control the extruder heater and stepper motor based on the temperature
//
if (rawvalue >= minRaw){ //if temp too low - turn stepper off
analogWrite(heatPin, hot); //if temp too low- turn heat to max
digitalWrite(EenablePin,HIGH); //if temp too low - turn off the stepper
//??? Stop everything if temp too low ???
}
// if((fahrenheit >=lowTemp) && (fahrenheit <=minTemp)){ // if temp below range- LED slow blink
// analogWrite(heatPin, hot); //if temp below range - turn extruder up to max
// digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
// }
// if ((fahrenheit >= minTemp) && (fahrenheit <=maxTemp)){ //if temp in range - LED ON
// analogWrite(heatPin, warm); //if temp is in range - keep extruder warm
// digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
// }
if ((rawvalue <= minRaw)){ //if temp in range - LED ON
analogWrite(heatPin, warm); //if temp is in range - keep extruder warm
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
if(rawvalue<=maxRaw){ //if temp above range -LED fast blink
analogWrite(heatPin, cool); //if temp too high - turn heater off
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
//run extruder stepper motor
digitalWrite(EdirPin,LOW);
digitalWrite(EstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(EstepPin,LOW);
delay(1);
// delay(1000);
}
int read_temp()
{
int rawtemp = analogRead(THERMISTOR_PIN);
int current_celsius = 0;
byte i;
for (i=1; i <NUMTEMPS; i++) {
if (temptable[i][0] > rawtemp)
{
int realtemp = temptable[i-1][1] + (rawtemp - temptable[i-1][0]) * (temptable[i][1] - temptable[i-1][1]) / (temptable[i][0] - temptable[i-1][0]);
if (realtemp > 255)
realtemp = 255;
current_celsius = realtemp;
break;
}
}
// Overflow: We just clamp to 0 degrees celsius
if (i == NUMTEMPS)
current_celsius = 0;
return current_celsius;
}
Third Round of Extruder Code!
This bit of extruder conrol is working well with polylactic acid (PLA).
You can by some here if you are intersted: https://ultimachine.com/
//////////////////Arduino Code///////////////////
//
/* EXTRUDER CONTROL
WHAT IT DOES:
-REGULATE TEMP VIA PWM
-RUN STEPPER MOTOR OF PINCH WHEEL EXTRUDER WHEN IT IS WITHIN PROPER TEMP RANGE
-RUNNING STEPPER FROM EASYDRIVER BOARD
*/
//Turns off stepper if temp is too low [NOTE: using EasyDriver board, pull enable pin HIGH to turn off
//turn heat off if too hot
//turn heat way up if too cold
//turn heat to medium if
//THIS IS BASED ON CODE AND DESIGNS FROM
//webpage http://dev.www.reprap.org/bin/view/Main/Temperature_Sensor_1_1
// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)
// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)
// ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=4066 --max-adc=1023
#define THERMISTOR_PIN 0
#define heatPin 11 //heat level
#define EstepPin 5 //set extruder stepper to pin12
#define EdirPin 6 //set step direction to stepper pin11
#define EenablePin 12 //
#define LED 13
byte lowTemp = 95; // >>> if below lowTemp then extruder is disabled - degF
byte minTemp = 100; //>>>> SET MIN TEMP HERE >>>>>> - degF
byte maxTemp = 105; //>>>> SET MAX TEMP HERE >>>>>> - degF
//byte maxRaw = 690;
//byte minRaw = 700;
byte cool = 0; // heater off
byte warm = 255; //medium heat
byte hot = 255; //heat up pretty high >> max would be 255, but I don't want to burn out the heater
// r0: 100000
// t0: 25
// r1: 0
// r2: 4700
// beta: 4066
// max adc: 1023
#define NUMTEMPS 20
short temptable[NUMTEMPS][2] = {
{1, 841},
{54, 255},
{107, 209},
{160, 184},
{213, 166},
{266, 153},
{319, 142},
{372, 132},
{425, 124},
{478, 116},
{531, 108},
{584, 101},
{637, 93},
{690, 86},
{743, 78},
{796, 70},
{849, 61},
{902, 50},
{955, 34},
{1008, 3}
};
void setup()
{
Serial.begin(9600);
Serial.println("Starting temperature exerciser.");
pinMode(heatPin, OUTPUT);
pinMode(EstepPin, OUTPUT);
pinMode(EdirPin, OUTPUT);
pinMode(EenablePin, OUTPUT);
}
void loop()
{
int rawvalue = analogRead(THERMISTOR_PIN);
int celsius = read_temp();
int fahrenheit = (((celsius * 9) / 5) + 32);
Serial.print("Current temp: ");
Serial.print(celsius);
Serial.print("C / ");
Serial.print(fahrenheit);
Serial.println("F");
Serial.print("Raw value: ");
Serial.println(rawvalue);
Serial.println(" ");
//
//control the extruder heater and stepper motor based on the temperature
//
if (celsius <= minTemp){ //if temp too low - turn stepper off
analogWrite(heatPin, hot); //if temp too low- turn heat to max
digitalWrite(EenablePin,HIGH); //if temp too low - turn off the stepper
digitalWrite(LED,HIGH);
//??? Stop everything if temp too low ???
}
if((celsius >=lowTemp) && (celsius <=minTemp)){ // if temp below range- LED slow blink
analogWrite(heatPin, hot); //if temp below range - turn extruder up to max
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
if ((celsius >= minTemp) && (celsius<=maxTemp)){ //if temp in range - LED ON
analogWrite(heatPin, warm); //if temp is in range - keep extruder warm
digitalWrite(EenablePin,LOW); //if hot enough then turn on stepper
}
//run extruder stepper motor
digitalWrite(EdirPin,LOW);
digitalWrite(EstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(EstepPin,LOW);
delay(1);
// delay(1000);
}
int read_temp()
{
int rawtemp = analogRead(THERMISTOR_PIN);
int current_celsius = 0;
byte i;
for (i=1; i if (temptable[i][0] > rawtemp)
{
int realtemp = temptable[i-1][1] + (rawtemp - temptable[i-1][0]) * (temptable[i][1] - temptable[i-1][1]) / (temptable[i][0] - temptable[i-1][0]);
if (realtemp > 255)
realtemp = 255;
current_celsius = realtemp;
break;
}
}
// Overflow: We just clamp to 0 degrees celsius
if (i == NUMTEMPS)
current_celsius = 0;
return current_celsius;
}
3D Bouncing Ball - Processing
Here is some Processing code that shows a ball bouncing around in a 3D cube.
This is useful because it contains information for the X,Y and Z axiz.
It will be combined with the Firmata code in order to send the movements of all 3-axis to the Arduino, which in turn controlls the stepper motors, that move the platforms in each axis. That code will then be combined with the "mesh" pattern from step 14 to create a 3D mesh.
As in step 6 - I've built this on some sample code that came packaged with Processing. It is under Examples->Topics->Motion->Bounce
/////////////////// *********PROCESSING CODE ONLY*********
/**
* Bounce_3D_0
*
* When the shape hits the edge of the window, it reverses its direction.
*
*Modified for 3D
NOTE: 0,0,0 - top left corner (facing the computer monitor)
negative Z axis points 'into' the computer monitor
positive Y axis points down
positive X axis points to the right
*/
int size = 40; // Width of the shape
float xpos, ypos, zpos; // Starting position of shape
float depth;
float xspeed = 2.5; // Speed of the shape
float yspeed = 2; // Speed of the shape
float zspeed = 3;
int xdirection = 1; // Left or Right
int ydirection = 1; // Top to Bottom
int zdirection = 1; //front or back
void setup()
{
size(400, 400, P3D);
noStroke();
frameRate(30);
smooth();
// Set the starting position of the shape
xpos = width/2;
ypos = height/2;
zpos = -height/2; //note that Zaxis goes 'into' the screen
depth = -height;
}
void draw()
{
background(102);
lights();
//glass box //note: line(x1, y1, z1, x2, y2, z2)
stroke(255);
//back
line(0,0,depth, width,0,depth);
line(0,height,depth, width,height,depth);
line(0,0,depth, 0,height,depth);
line(width,0,depth, width,height,depth);
//corners
line(0,0,0, 0,0,depth);
line(0,height,0, 0,height,depth);
line(width,0,0, width,0,depth);
line(width,height,0, width,height,depth);
// Update the position of the shape
xpos = xpos + ( xspeed * xdirection );
ypos = ypos + ( yspeed * ydirection );
zpos = zpos + ( zspeed * zdirection );
// Test to see if the shape exceeds the boundaries of the screen
// If it does, reverse its direction by multiplying by -1
if (xpos > width-size || xpos < 0) {
xdirection *= -1;
}
if (ypos > height-size || ypos < 0) {
ydirection *= -1;
}
if (zpos < -height-size || zpos > 0) { //note that Zaxis goes 'into' the screen
zdirection *= -1;
}
// Draw the shape
lights();
translate(xpos, ypos, zpos);
sphere(size);
}
3D - Follow the Bouncing Ball (this Includes Firmata)
Here is the 3D bouncing ball Processing and Adruino code.
This will run X,Y,Z stepper motors.
The Arduino code follows, this is the code from Step 13 with the Z axis added.
///////////////////////////////PROCESSING /////////////////////////////
/////////////////// *********PROCESSING with FIRMATA*********
/**
* Bounce_3D_0
*
* When the shape hits a boundry it reverses its direction.
There are X,Y, Z bounds
Direction is outputed -
Processsing -> Firmata -> Arduino -> Drivers -> Steppers
*
*Modified for 3D
NOTE: 0,0,0 - top left corner (facing the computer monitor)
negative Z axis points 'into' the computer monitor
positive Y axis points down
positive X axis points to the right
*/
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
//Serial myPort; // Create object from Serial class
// Data received from the serial port
int ledPin = 13;
int XstepPin = 10;
int XdirPin = 7;
int YstepPin = 2;
int YdirPin = 3;
int ZstepPin = 19;
int ZdirPin = 18;
int size = 40; // Width of the shape
float xpos, ypos, zpos; // Starting position of shape
float depth;
float xspeed = 2.5; // Speed of the shape
float yspeed = 2; // Speed of the shape
float zspeed = 3;
int xdirection = 1; // Left or Right
int ydirection = 1; // Top to Bottom
int zdirection = 1; //front or back
void setup()
{
size(200, 200, P3D);
noStroke();
frameRate(30);
smooth();
// Set the starting position of the shape
xpos = width/2;
ypos = height/2;
zpos = -height/2; //note that Zaxis goes 'into' the screen
depth = -height;
//setup communication with Arduino
arduino = new Arduino(this, Arduino.list()[0], 57600); // v1
arduino.pinMode(ledPin, Arduino.OUTPUT);
arduino.pinMode(XstepPin, Arduino.OUTPUT);
arduino.pinMode(XdirPin, Arduino.OUTPUT);
arduino.pinMode(YstepPin, Arduino.OUTPUT);
arduino.pinMode(YdirPin, Arduino.OUTPUT);
arduino.pinMode(ZstepPin, Arduino.OUTPUT);
arduino.pinMode(ZdirPin, Arduino.OUTPUT);
}
void draw()
{
background(102);
lights();
//glass box //note: line(x1, y1, z1, x2, y2, z2)
stroke(255);
//back
line(0,0,depth, width,0,depth);
line(0,height,depth, width,height,depth);
line(0,0,depth, 0,height,depth);
line(width,0,depth, width,height,depth);
//corners
line(0,0,0, 0,0,depth);
line(0,height,0, 0,height,depth);
line(width,0,0, width,0,depth);
line(width,height,0, width,height,depth);
// Update the position of the shape
xpos = xpos + ( xspeed * xdirection );
ypos = ypos + ( yspeed * ydirection );
zpos = zpos + ( zspeed * zdirection );
// Test to see if the shape exceeds the boundaries of the screen
// If it does, reverse its direction by multiplying by -1
if (xpos > width-size || xpos < 0) {
xdirection *= -1;
}
if (ypos > height-size || ypos < 0) {
ydirection *= -1;
}
if (zpos < -height-size || zpos > 0) { //note that Zaxis goes 'into' the screen
zdirection *= -1;
}
// Draw the shape
lights();
translate(xpos, ypos, zpos);
sphere(size);
// CONTROLL THE STEPPER MOTORS
// X
if (xdirection >= 0){
arduino.digitalWrite(ledPin, Arduino.HIGH);
arduino.digitalWrite(XdirPin, Arduino.HIGH);
arduino.digitalWrite(ZdirPin, Arduino.HIGH);
}
else{
arduino.digitalWrite(ledPin, Arduino.LOW);
arduino.digitalWrite(XdirPin, Arduino.LOW);
arduino.digitalWrite(ZdirPin, Arduino.LOW);
}
// Y
if (ydirection >= 0){
arduino.digitalWrite(YdirPin, Arduino.HIGH);
}
else{
arduino.digitalWrite(YdirPin, Arduino.LOW);
}
// Z
if (zdirection >= 0){
arduino.digitalWrite(ZdirPin, Arduino.HIGH);
}
else{
arduino.digitalWrite(ZdirPin, Arduino.LOW);
}
}
/////////////////////////////ARDUINO////////////////////////////////////////
// Wiring/Arduino code:// simmple digital firmata
//Supports as many digital inputs and outputs as possible.
//This example code is in the public domain.
#include
#define XstepPin 10
#define XdirPin 7
#define YstepPin 2
#define YdirPin 3
#define ZstepPin 19
#define ZdirPin 18
byte previousPIN[2]; // PIN means PORT for input
byte previousPORT[2];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if(previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
void setPinModeCallback(byte pin, int mode) {
if(pin > 1) { // don't touch RxTx pins (0,1)
pinMode(pin, mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if(value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
outputPort(0, PIND &~ B00000011); // pins 0-7, ignoring Rx/Tx pins (0/1)
outputPort(1, PINB); // pins 8-13
while(Firmata.available()) {
Firmata.processInput();
}
//added stepper sequence to firmata code
digitalWrite(XstepPin,HIGH); //take steps
digitalWrite(YstepPin,HIGH);
digitalWrite(ZstepPin,HIGH);
delayMicroseconds(2);
digitalWrite(XstepPin,LOW);
digitalWrite(YstepPin,LOW);
digitalWrite(ZstepPin,LOW);
delayMicroseconds(2);
delayMicroseconds(1000); // <<<<<< USE TO CHANGE STEPPER SPEED???? <<<<<<<<
}
Doing to Things at Once
1: to controll the X,Y,Z axis
2: to control the extruder temperature and motor
The trick now, is to run both at once. The problem is that the extruder motor must be run at a much slower rate then the X,Y,Z motors. In order to run the steppers at different speeds you need a timer or counter, or something. I'm going to make it so that for every so many X,Y,Z steps, the extruder motor takes one step.
Here is a very simple bit of code that does this. This is based on an example that was included in the Processing dowload. The orgingal code ran a line across the screen, moving one position for every loop through the program. I've added a parameter called "timer" that coundt up to 4, then changes teh background to white, then resets to 0 and starts counting again.
So what you get is two things running at once at a rate of 4:1.
Here is the processing code:
///////////////////////////PROCESSING ONLY//////////////////////////////////
void setup() {
frameRate(4);
}
int pos = 0;
int timer= 0;
void draw() {
background(204);
pos++;
line(pos, 20, pos, 80);
if(pos > width) {
pos = 0;
}
timer++; //INCREMENT TIMER
if (timer ==4){ // DO SOMETHING AFTER 4 COUNTS
background(255); //SOMETHING
timer = 0; //RESET THE TIMER
}
}
Better Mesh (in Progress)
here is mesh drawing code, that used less specific coordinates
///////////////////Processing/////////////
/**
*Point moves back and forth accross the screen - includes movement in the X direction
****uses different variables to indicate that this is in progress
(and because I forgot what variables I was using)
*/
int res = 50; //print resolution
int a = 100;
int b = 1;
int dir_a = 1;
int dir_b = 0;
int step_a = 1;
int step_b = 0;
int old_b = 0;
int next_b = res;
void setup()
{
size(640, 200);
stroke(255);
background(50);
}
void draw()
{
a = a + dir_a*step_a;
b = b + dir_b*step_b;
if ((a < 10 || a > height-10) && b < next_b) {
dir_b = 1;
step_a = 0;
step_b = 1;
dir_a = (-1)*dir_a;
old_b = b;
}
else {
step_a=1;
step_b=0;
next_b = old_b+res;
}
point(b, a); // Syntax point(x1, y1)
print(b);
print(',');
println(a);
}
Continous Mesh Rotating 90deg Each Layer
This shows a point continously tracing mesh pattern across an X,Y plane.
The mesh is rotatated 90deg after each layer is traced.
This can be used to output X,Y directions to the stepper motors.
Z axis control can be added to this.
///////////////////Processing/////////////
/**
*Point moves back and forth accross the screen - includes movement in the X and Y direction
Repeats over and over, rotating 90deg every time a layer is complete
takes some odd steps at the begining
****uses different variables to indicate that this is in progress
(and because I forgot what variables I was using)
*/
int res = 50; //print resolution
int a = 10;
int b = 10;
int dir_a = 1;
int dir_b = 1;
int step_a = 1;
int step_b = 0;
int old_step_a = 1;
int old_step_b =0;
int old_dir_a = 1;
int old_dir_b = 0;
int dir_switch = 0;
int old_dir_switch = 0;
int old_a = 0;
int old_b = 0;
int next_b = res;
int next_a = res;
void setup()
{
size(200, 200);
stroke(255);
background(50);
}
void draw()
{
background(50);
if ((a < 10 || a > height - 10) && old_step_b ==0){
dir_a = -1 * dir_a;
next_b = b+res;
dir_switch = 1;
}
if ((old_dir_switch != dir_switch) && b < next_b){
step_a = 0;
step_b = 1;
dir_switch = 0;
}
if (b > next_b){
step_a = 1;
step_b = 0;
next_b = b+res;
dir_switch = 0;
}
if ((b < 10 || b > width - 10) && old_step_a ==0){
dir_b = -1 * dir_b;
next_a = a + res;
dir_switch = 1;
}
if ((old_dir_switch != dir_switch) && a < next_a){
step_b = 0;
step_a = 1;
dir_switch = 0;
}
if (a > next_a){
step_b = 1;
step_a = 0;
next_a = a + res;
dir_switch = 0;
}
// calculate postion
a = a + dir_a*step_a;
b = b + dir_b*step_b;
//display point
point(b, a); // Syntax point(x1, y1)
//store some values as old for comparison
old_a = a;
old_b = b;
old_step_a = step_a;
old_step_b = step_b;
old_dir_a = dir_a;
old_dir_b = dir_b;
old_dir_switch = old_dir_switch + dir_switch;
print(b);
print(',');
println(a);
}