sabato 24 dicembre 2016

Very low cost 2 axis precision plotter with arduino mega

I decided to construct a very low cost (less than 100 euros) 2 axis plotter controlled with arduino in order to draw on paper using any standard pencil/pen and I will show you the result, so you could find something useful for your projects.
We used metal profiles connected each others in order to construct a frame of about 1 meter x 1 meter.

Main view of the plotter:


As you can see there are 2 step motors, one for the X axis and on for the Y axis. In order to move the head of the plotter I used 4 pulley and two timing belt.
Due to the fact the pulley is 20 tooth and has a diameter of about 13 mm and the step motors have 200 steps it is simple to calculate the drawing precision of the plotter.

The minimum movement "delta X" and "delta Y" is:
circumference of the pulley / steps of the motor = 3.14 * 13 mm / 200 = 0.2 mm (approximately)

That is good enough for my purpose.

Detail of the connection of the step motor on the frame:


 Detail of the frame:


Detail of the head of the plotter:


As you can see there is a servo motor used to lift the pen/pencil. The servo is connected to the hardware by using an old spiral cable.

In order to avoid damages I used 4 contact switch. I do not control them via software because it is useless. Do the the fact they intervene in the operations only as a result of a problem, they are conneted in series in order to open ther power circuit of the step motors: very simple and really safe.

The hardware was built using the following items:

- 2 step motors NEMA 17 200 steps
- 2 step motors controllers based on L298N, very simple and reliable to use
- 1 arduino MEGA
- 1 power supply 12 V 10 A
- 1 SD card holder

Hardware controller:


There is also a pause button that is useful to stop the plotter and change the pen/pencil during the task:


On the side of the box you can see the SD card holder where you can put the card containing the coordinates of the drawing read by the arduino. There is also the arduino's usb connector used to view the coordinates directly on the pc monitor.
There are several cables: 

- 2 cables with a plug connectors for the 2 step motors (4 wires x 2)
- 4 cables with plug connectors for the contact switches (2 wires x 4)
- 1 cable with plug for the servo motor (3 wires)
- 1 power cable 220 V


The following picture is the general connection scheme of the plotter:

Into the SD card the coordinates have to be written like this:

x1
y1
x2
y2
......

The coordinates are written on a txt file on a single column. Every new line (\n) correspond to a single number with the following format in millimeters:

nnn.n

For example if you want to draw a line from the point (x1=10, y1=20.8) to the point (x2=50.2, y2=200.4) you have to put on the SD card a file "coordinates.txt" with the following content:

010.0
020.8
050.2
200.4

If the plotter is not on pause it automatically raises the head, goes to the first coordinate  (x1=10, y1=20.8), lowers the head and the goes to the second coordinate (x2=50.2, y2=200.4).

About the code

Due to the fact I made a specific routine in scilab to convert a black and white image into the format described above and I wrote several different codes for arduino, I'll just explain arduino code generically by focusing on the most important issues.

In the setup I wrote:

#include <Stepper.h>
#define STEPS 200

Stepper steppera(200,30,32,34,36);
Stepper stepperb(200,31,33,35,37);

float deltal=0.2;

char buffer[] = {' ',' ',' ',' ',' ',' ',' '}; // Receive up to 7 bytes

#include <SD.h>
#include <SPI.h>
File myFile;

You can see I included the stepper library that is very useful to control the motors. The numbers correspond to the pins on the arduino MEGA I used. The float "deltal" is the parameter used to calculate the distances as described above.

The buffer is used only if you want to send the coordinates to the arduino MEGA by using the keyboard instead of the SD card but I will not describe it because I used it only during the debug.
I also included the SD library and the SPI in order to read data correctly from the SD card.
"myFile" is used to open the txt file on the SD card.About the code

Due to the fact I made a specific routine in scilab to convert a black and white image into the format described above and I wrote several different codes for arduino, I'll just explain arduino code generically by focusing on the most important issues.

In the setup I wrote:

#include <Stepper.h>
#define STEPS 200

Stepper steppera(200,30,32,34,36);
Stepper stepperb(200,31,33,35,37);

float deltal=0.2;

char buffer[] = {' ',' ',' ',' ',' ',' ',' '}; // Receive up to 7 bytes

#include <SD.h>
#include <SPI.h>
File myFile;

You can see I included the stepper library that is very useful to control the motors. The numbers correspond to the pins on the arduino MEGA I used. The float "deltal" is the parameter used to calculate the distances as described above.

The buffer is used only if you want to send the coordinates to the arduino MEGA by using the keyboard instead of the SD card but I will not describe it because I used it only during the debug.
I also included the SD library and the SPI in order to read data correctly from the SD card.
"myFile" is used to open the txt file on the SD card.

About step motors control

With arduino is not possible (or at least I don't know how to do it) to control two different step motors at the same time if they are not sincronized so, in order to move from the first point (x1, y1) to the second one (x2, y2), I divided the path in two different movements. The X movement and the Y movement. For example if the points are (100, 100) and (150, 200) you have to move the head of about 50 mm in the X direction and 100 mm in Y direction. The code, in this case, gives 1 step to the X motor every 2 steps of the Y motor, and it continues to activate the two motors alternately until the final point is reached. Even if it is not a fluid movement, it is striclty connected with step motors nature and the precision of the drawing is related to the precision (deltal=0.2 mm) we stated before. The routine who take cares of control the two motors is called "azionamotori()":

void azionamotori()
{
if (na>=nb){
if (nb>0){
rapporto=(float)na / (float)nb;
for (int i=1;i<=na;i++) 
{
  contax=contax+1;
  ifloat=(float) i;
  k1=ifloat/rapporto;
  parteintera1=(int) k1;
  k2=(ifloat+1)/rapporto;
  parteintera2=(int) k2;
  if(parteintera1!=parteintera2) {valore=1; contay=contay+1;}
  else 
  {valore=0;}
if (deltax>0) {
steppera.step(+1);}
if (deltax<0) {
steppera.step(-1);}
if (deltay>0 && valore==1) {
stepperb.step(+1);}
if (deltay<0 && valore==1) {
stepperb.step(-1);}
}
else if (nb==0)
{
for (int i=1;i<=na;i++) 
{
 if (deltax>0) {
steppera.step(+1);}
if (deltax<0) {
steppera.step(-1);}
    }
}
 } 
else if (na<nb){
temp=na;
na=nb;
nb=temp;
  if (nb>0){
rapporto=(float)na / (float)nb;
for (int i=1;i<=na;i++) 
{
  contax=contax+1;
  ifloat=(float) i;
  k1=ifloat/rapporto;
  parteintera1=(int) k1;
  k2=(ifloat+1)/rapporto;
  parteintera2=(int) k2;
  if(parteintera1!=parteintera2) {valore=1; contay=contay+1;}
  else 
  {valore=0;}
if (deltay>0) {
stepperb.step(+1);}
if (deltay<0) {
stepperb.step(-1);}
if (deltax>0 && valore==1) {
steppera.step(+1);}
if (deltax<0 && valore==1) {
steppera.step(-1);}
}
else if (nb==0)
{
for (int i=1;i<=na;i++) 
{
if (deltay>0) {
//Serial.println("+y x=0"); 
stepperb.step(+1);}
if (deltay<0) {
//Serial.println("-y x=0"); 
stepperb.step(-1);}
    }
}
 }   return;
}

How to read data from the SD card

       centinaia=(myFile.read()-48);
       decine=(myFile.read()-48);
       unita=(myFile.read()-48);

       punto=(myFile.read()-48);

       decimale=(myFile.read()-48);

       invio=(myFile.read()-48);
       acapo=(myFile.read()-48);

       number=centinaia*100+decine*10+unita+decimale/10;
       Serial.print("y1: ");
       Serial.print(number);

I simply read 7 characters (4 numeric digits, 1 decimal point, one "return" character, one "new line" character) and then I calcuate the correspondent numeric value due to the ascii value.
The coordinate in millimeters is equal to (centinaia*100+decine*10+unita+decimale/10).
After having calculated this number is it possible to subtract it to the corresponding previous one, dividing the result by "deltal" that is 0.2 mm and you can find the number of steps you have to send to the step motor.

// calculate increment na (X axis) and nb (Y axis)
void calcola_na_nb()
{
deltax=x-xo;
deltay=y-yo;
na=abs(deltax/deltal);
nb=abs(deltay/deltal);

if (deltax>=0){xo=xo+na*deltal;} 
else if (deltax<0){xo=xo-na*deltal;}

if (deltay>=0){yo=yo+nb*deltal;} 
else if (deltay<0){yo=yo-nb*deltal;}

return;
}




martedì 4 ottobre 2016

A simple low cost GSM alarm with arduino


A simple low budget GSM alarm with arduino.

I wanted to construct a simple alarm spending less than 30 euros that was capable of sending an sms in case of:

- infraction
- low battery level
- power supply fault

and I am gonna to explait it to you in case it could be useful for you projects.

This alarm contains the following electrinic parts:

- 1 gsm gprs module used to send sms to one or more mobile numbers
- 2 infrared sensors
- 1 battery pack (in this version I did not use rechargeable batteries but it is very simple to modify the scheme)
- 1 arduino UNO board

In order to avoid false alarms, I used two separate infrared sensors, so the alarm is detected only if both the sensors give a positive signal. The are also 2 different buttons. The first one is used to start the initial countdown so you have the necessary time to exit the room, the second one is the reset.
The are also 3 leds on the front of the alarm. The first one indicates the presence of the electrical power, the second one indicates the countdown and the activation of the alarm.

The main scheme is indicated in the following picture:


About the code, this one is just one version and it is not optimized but it works:

#include <SoftwareSerial.h>
#define SIM800_TX_PIN 8
#define SIM800_RX_PIN 9
SoftwareSerial serialSIM800(SIM800_TX_PIN,SIM800_RX_PIN);
// numero massimo di allarmi da mandare per topologia
// sensore l'allarme si conta solo quando si attiva mentre per la rete si segnala quando va via e quando torna. per la batteria quando scende oltre un certo livello.
int max_sensore=6;
int max_rete=6;
int max_batteria=6;
int n_al_sensore=0;
int n_al_rete=0;
int n_al_batteria=0;
int secondi_partenza=30;

// PIN DEI SENSORI
int sensore = A0;    // SENSORE DI PRESENZA
int sensore2 = A2;    // SENSORE DI PRESENZA
int rete = A1; // TENSIONE DI RETE DA ALLARME PER TENSIONE DI ALIMENTAZIONE MANCANTE
int batteria = A5;  // TENSIONE BATTERIA DEVE ESSERE 9V O SUPERIORE E DARE ALLARME SE INFERIORE A 6V

int lettura_sensore = 0; 
int lettura_sensore2 = 0; 
int lettura_rete = 0; 
int lettura_batteria = 0; 

byte allarme_sensore=0;
byte allarme_rete=0;
byte allarme_batteria=0;

unsigned long tempo1=0;
unsigned long tempo2=0;

int nmisure;

long totale_sensore,totale_sensore2,totale_rete,totale_batteria;
int media_sensore=0;
int media_sensore2=0;
int media_rete=1000;
int media_batteria=1000;
int attivazione=7; // PIN CHE ATTIVA L'ALLARME HIGH=ATTIVO

String stringa[20];

void setup() {

while(!Serial);
serialSIM800.begin(9600);
delay(1000);
Serial.println("Setup GSM Complete!");
  
  pinMode(sensore, INPUT);
    pinMode(sensore2, INPUT);
  pinMode(rete, INPUT);
  pinMode(batteria, INPUT);

// interruttore con l'attivazione dell'allarme
  pinMode(attivazione, INPUT); 

  pinMode(2, OUTPUT); // sensore con il led di allarme
  pinMode(3, OUTPUT); // sensore con il led dell'SMS

  Serial.begin(9600);
lampeggia_verde();
Serial.println("In attesa di premere il pulsante rosso per attivare l'allarme...");
while (digitalRead(attivazione)==LOW) {} // attende "secondi_partenza" secondi per iniziare a funzionare
Serial.println("PULSANTE ROSSO PREMUTO...");  
lampeggia_partenza();
  delay(secondi_partenza*1000);
Serial.println("Allarme attivato...");  //INTERRUTTORE PREMUTO

digitalWrite(3,LOW); // led SMS
}

void loop() {

// LEGGE I PARAMETRI IN INGRESSO
rilevato();

// INOLTRA GLI ALLARMI
verifica_allarmi();
}

void print_parametri()
{
Serial.println("-------------------------PARAMETRI---------------------------------------"); 
Serial.print("media sensore: "); Serial.println(media_sensore); 
Serial.print("media sensore 2: "); Serial.println(media_sensore2)
Serial.print("media tensione rete: "); Serial.println(media_rete); 
Serial.print("media tensione batteria: "); Serial.println(media_batteria*9.79/695); 
Serial.println("-------------------------PARAMETRI---------------------------------------"); 
}

void manda_sms()
{Serial.println("SMS SMS SMS SMS SMS SMS SMS SMS SMS SMS ");

/* PARTE DA INSERIRE PER FAR PARTIRE L'SMS
serialSIM800.write("AT+CMGF=1\r\n");
delay(1000);
//Send new SMS command and message number
serialSIM800.write("AT+CMGS=\"??????????\"\r\n");
delay(1000);
//Send SMS content
serialSIM800.write("ALLARME PRESENZA");
delay(1000);
//Send Ctrl+Z / ESC to denote SMS message is complete
serialSIM800.write((char)26);
delay(1000);
Serial.println("SMS Sent!");

*/

digitalWrite(3,HIGH);
delay(250);
digitalWrite(3,LOW);
}
  
void verifica_allarmi()  {

if ((media_sensore < 200)&& (allarme_sensore==1))
{allarme_sensore=0; Serial.print("*** SENSORE TORNATO OFF ***    ");
  digitalWrite(2,LOW); // spegniamo il led all'uscita 2
// n_al_sensore=n_al_sensore+1; Serial.println(n_al_sensore);
// manda_sms();
}

if ((media_rete > 200) && (allarme_rete==1))
{allarme_rete=0; Serial.print("*** RETE TORNATA ON ***    ");
n_al_rete=n_al_rete+1; Serial.println(n_al_rete);
stringa[7]="RETE ON";
manda_sms();
}

if ((media_batteria > 200)&& (allarme_batteria==1))
{allarme_batteria=0; Serial.print("*** BATTERIA TORNATA ON ***");
n_al_batteria=n_al_batteria+1; Serial.println(n_al_batteria);
manda_sms();
}

  if ((media_sensore > 200) && (media_sensore2 >200))
{
  // SE IL SENSORE NON ERA GIA' IN ALLARME MANDA UN ALLARME
    if (allarme_sensore==0){
    if(n_al_sensore<max_sensore){
      stringa[16]="ALLARME PRESENZA";
    manda_sms();
    n_al_sensore=n_al_sensore+1;}
     // ALLARME PRESENZA
  Serial.println("<<<<<<<<<<<<<<<<<<<<<  ALLARME PRESENZA"); 
  digitalWrite(2,HIGH); // accendiamo il led all'uscita 2
  
    allarme_sensore=1;}

  }

  if (media_rete < 200)
{

  // SE IL SENSORE NON ERA GIA' IN ALLARME MANDA UN ALLARME
    if (allarme_rete==0){
   if (n_al_rete<max_rete){
     stringa[16]="RETE OFF";
    manda_sms();
    n_al_rete=n_al_rete+1;}
     // ALLARME PRESENZA
  Serial.println("<<<<<<<<<<<<<<<<<<<<< ALLARME RETE");
  Serial.print("MEDIA RETE: "); Serial.println(media_rete); 
    allarme_rete=1;}
}

  if (media_batteria < 200)
{
  // SE IL SENSORE NON ERA GIA' IN ALLARME MANDA UN ALLARME
    if (allarme_batteria==0){
    if(n_al_batteria<max_batteria){
 // TOGLIERE IL COMMENTO DALLA RIGA SUCCESSIVA
 // stringa[20]="ALLARME BATTERIA";
 //   manda_sms();
    n_al_batteria=n_al_batteria+1;}
     // ALLARME PRESENZA
  Serial.println("<<<<<<<<<<<<<<<<<<<<< ALLARME BATTERIA"); 
    allarme_batteria=1;}
}

  }

void rilevato() {
tempo1=millis();
tempo2=tempo1;
nmisure=0;
totale_sensore=0;
totale_sensore2=0;
totale_rete=0;
totale_batteria=0;

while((tempo2-tempo1) <= 1000){
lettura_sensore = analogRead(sensore);
lettura_sensore2 = analogRead(sensore2);
lettura_rete = analogRead(rete);
lettura_batteria = analogRead(batteria);

nmisure=nmisure+1;
totale_sensore=totale_sensore+lettura_sensore;
totale_sensore2=totale_sensore2+lettura_sensore2;
totale_rete=totale_rete+lettura_rete;
totale_batteria=totale_batteria+lettura_batteria;

  tempo2=millis();
}
media_sensore=totale_sensore/nmisure;
media_sensore2=totale_sensore2/nmisure;
media_rete=totale_rete/nmisure;
media_batteria=totale_batteria/nmisure;

}

void suona_sirena()

{}

void lampeggia_verde()
{digitalWrite(3,HIGH); delay(300);
 digitalWrite(3,LOW); delay(300);
 }


void lampeggia_partenza()
{digitalWrite(2,HIGH); delay(300);
 digitalWrite(2,LOW); delay(300);
 }


It is also very simple to introduce some magnetic switches: it is only necessary to connect them to an arduino's input and verify if the corresponding circuit changes its state.