Radar com Ping e Processing



Neste post estou mostrando minha adaptação do trabalho do Luckylarry para uso com o sensor ping ao invés do SRF-05.



Ao Lado  o sensor da Seeedstudio que é uma versão do sensor ping da Parallax, este sensor funciona baseado na emissão e captação de sinais sonoros.

O pricípio de funcionamento é a relação de tempo entre a emissão e a captação do som, baseado no fato de que o som tem uma velocidade constante é possível calcular a distância quando o intervalo entre a emissão e a captação é conhecido.



A montagem do sistema utiliza as seguintes portas do arduino:

 - Digital 7 para o sensor ping
 - Digital 9 (pmw) para o servo

Os dados recebidos no arduino na porta 7 são pulsos eletrícos que são convertidos em medidas de distância por uma formula simples e enviados para a porta serial para processamento do Processing.

A fórmula de conversão é baseada no fato de que  velocidade do som é de 340 m/s ou 29 microssegundos por centímetro, logo se o sinal do sensor (pulsos em microssegundos) for dividido por 29 e depois novamente dividido por 2 ( o som é emitido e volta por isso temos que dividir por 2) teremos a distância em centimetros: microseconds / 29 / 2.









Foram necessárias poucas mudanças no código do arduino, o do processing é igual.

---------------------------------------------------------------------------------------------------------

Código Arduino:

/*
ARDUINO - RADAR COM PING E PROCESSING
ADAPTADO POR CLÁUDIO MIKLOS - WWW.MIKLOS.BLOG.BR - PARA USO COM SENSOR PING
CERDITOS ORIGINAIS:
luckylarry.co.uk
Radar Screen Visualisation for SRF-05
Sends sensor readings for every degree moved by the servo
values sent to serial port to be picked up by Processing
*/

#include <Servo.h>             // include the standard servo library
Servo leftRightServo;         // set a variable to map the servo
int leftRightPos = 0;         // set a variable to store the servo position
const int numReadings = 10;   // set a variable for the number of readings to take
int index = 0;                // the index of the current reading
int total = 0;                // the total of all readings
int average = 0;              // the average
int pingPin = 7;              // the PING pin
unsigned long duration = 0;  // variable for reading the pulse
unsigned long distance = 0;   // variable for storing distance
 
/* setup the pins, servo and serial port */
void setup() {
  leftRightServo.attach(9);
  Serial.begin(9600);
}
 
/* begin rotating the servo and getting sensor values */
void loop() {
  for(leftRightPos = 0; leftRightPos < 180; leftRightPos++) {  // going left to right.
    leftRightServo.write(leftRightPos);
      for (index = 0; index<=numReadings;index++) {            // take x number of readings from the sensor and average them
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
        distance = duration / 29 / 2;                            // convert to centimetres
        total = total + distance;                              // update total
        delay(10);
      }
    average = total/numReadings;                               // create average reading
 
    if (index >= numReadings)  {                               // reset the counts when at the last item of the array
      index = 0;
      total = 0;
    }
    Serial.print("X");                                         // print leading X to mark the following value as degrees
    Serial.print(leftRightPos);                                // current servo position
    Serial.print("V");                                         // preceeding character to separate values
    Serial.println(average);                                   // average of sensor readings
  }
  /*
  start going right to left after we got to 180 degrees
  same code as above
  */
  for(leftRightPos = 180; leftRightPos > 0; leftRightPos--) {  // going right to left
    leftRightServo.write(leftRightPos);
    for (index = 0; index<=numReadings;index++) {
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
        distance = duration / 29 / 2;                            // convert to centimetres
        total = total + distance;                              // update total
      delay(10);
    }
    average = total/numReadings;
    if (index >= numReadings)  {
      index = 0;
      total = 0;
    }
    Serial.print("X");
    Serial.print(leftRightPos);
    Serial.print("V");
    Serial.println(average);

   }

}

------------------------------------------------------------------------
Código Processing
/*
ARDUINO - RADAR COM PING E PROCESSING
ADAPTADO POR CLÁUDIO MIKLOS - WWW.MIKLOS.BLOG.BR - PARA USO COM SENSOR PING
CERDITOS ORIGINAIS:
luckylarry.co.uk
Radar Screen Visualisation for SRF-05
Maps out an area of what the SRF-05 sees from a top down view.
Takes and displays 2 readings, one left to right and one right to left.
Displays an average of the 2 readings
Displays motion alert if there is a large difference between the 2 values.
*/
import processing.serial.*;     // import serial library
Serial arduinoport;                  // declare a serial port
float x, y;                       // variable to store x and y co-ordinates for vertices   
int radius = 350;               // set the radius of objects
int w = 300;                    // set an arbitary width value
int degree = 0;                 // servo position in degrees
int value = 0;                  // value from sensor
int motion = 0;                 // value to store which way the servo is panning
int[] newValue = new int[181];  // create an array to store each new sensor value for each servo position
int[] oldValue = new int[181];  // create an array to store the previous values.
PFont myFont;                   // setup fonts in Processing
int radarDist = 0;              // set value to configure Radar distance labels
int firstRun = 0;               // value to ignore triggering motion on the first 2 servo sweeps

/* create background and serial buffer */
void setup(){
// setup the background size, colour and font.
size(750, 450);
background (0); // 0 = black
myFont = createFont("verdana", 12);
textFont(myFont);
// setup the serial port and buffer
arduinoport = new Serial(this, Serial.list()[0], 9600);

}

/* draw the screen */
void draw(){
fill(0);                              // set the following shapes to be black
noStroke();                           // set the following shapes to have no outline
ellipse(radius, radius, 750, 750);    // draw a circle with a width/ height = 750 with its center position (x and y) set by the radius
rectMode(CENTER);                     // set the following rectangle to be drawn around its center
rect(350,402,800,100);                // draw rectangle (x, y, width, height)
if (degree >= 179) {                  // if at the far right then set motion = 1/ true we're about to go right to left
motion = 1;                         // this changes the animation to run right to left
}
if (degree <= 1) {                    // if servo at 0 degrees then we're about to go left to right
motion = 0;                         // this sets the animation to run left to right
}
/* setup the radar sweep */
/* 
We use trigonmetry to create points around a circle.
So the radius plus the cosine of the servo position converted to radians
Since radians 0 start at 90 degrees we add 180 to make it start from the left
Adding +1 (i) each time through the loops to move 1 degree matching the one degree of servo movement
cos is for the x left to right value and sin calculates the y value
since its a circle we plot our lines and vertices around the start point for everything will always be the center.
*/
strokeWeight(7);                      // set the thickness of the lines
if (motion == 0) {                    // if going left to right
for (int i = 0; i <= 20; i++) {     // draw 20 lines with fading colour each 1 degree further round than the last
stroke(0, (10*i), 0);             // set the stroke colour (Red, Green, Blue) base it on the the value of i
line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w); // line(start x, start y, end x, end y)
}
} else {                              // if going right to left
for (int i = 20; i >= 0; i--) {     // draw 20 lines with fading colour
stroke(0,200-(10*i), 0);          // using standard RGB values, each between 0 and 255
line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w);
}
}
/* Setup the shapes made from the sensor values */
noStroke();                           // no outline
/* first sweep */
fill(0,50,0);                         // set the fill colour of the shape (Red, Green, Blue)
beginShape();                         // start drawing shape
for (int i = 0; i < 180; i++) {     // for each degree in the array
x = radius + cos(radians((180+i)))*((oldValue[i])); // create x coordinate
y = radius + sin(radians((180+i)))*((oldValue[i])); // create y coordinate
vertex(x, y);                     // plot vertices
}
endShape();                           // end shape
/* second sweep */
fill(0,110,0);
beginShape();
for (int i = 0; i < 180; i++) {
x = radius + cos(radians((180+i)))*(newValue[i]);
y = radius + sin(radians((180+i)))*(newValue[i]);
vertex(x, y);
}
endShape();
/* average */
fill(0,170,0);
beginShape();
for (int i = 0; i < 180; i++) {
x = radius + cos(radians((180+i)))*((newValue[i]+oldValue[i])/2); // create average
y = radius + sin(radians((180+i)))*((newValue[i]+oldValue[i])/2);
vertex(x, y);
}
endShape();
/* if after first 2 sweeps, highlight motion with red circle*/
if (firstRun >= 360) {
stroke(150,0,0);
strokeWeight(1);
noFill();
for (int i = 0; i < 180; i++) {
if (oldValue[i] - newValue[i] > 35 || newValue[i] - oldValue[i] > 35) {
x = radius + cos(radians((180+i)))*(newValue[i]);
y = radius + sin(radians((180+i)))*(newValue[i]);
ellipse(x, y, 10, 10); 
}
}
}
/* set the radar distance rings and out put their values, 50, 100, 150 etc.. */
for (int i = 0; i <=6; i++){
noFill();
strokeWeight(1);
stroke(0, 255-(30*i), 0);
ellipse(radius, radius, (100*i), (100*i)); 
fill(0, 100, 0);
noStroke();
text(Integer.toString(radarDist+50), 380, (305-radarDist), 50, 50);
radarDist+=50;
}
radarDist = 0;
/* draw the grid lines on the radar every 30 degrees and write their values 180, 210, 240 etc.. */
for (int i = 0; i <= 6; i++) {
strokeWeight(1);
stroke(0, 55, 0);
line(radius, radius, radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w);
fill(0, 55, 0);
noStroke();
if (180+(30*i) >= 300) {
text(Integer.toString(180+(30*i)), (radius+10) + cos(radians(180+(30*i)))*(w+10), (radius+10) + sin(radians(180+(30*i)))*(w+10), 25,50);
} else {
text(Integer.toString(180+(30*i)), radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w, 60,40);
}
}
/* Write information text and values. */
noStroke();
fill(0);
rect(350,402,800,100);
fill(0, 100, 0);
text("Degrees: "+Integer.toString(degree), 100, 380, 100, 50);         // use Integet.toString to convert numeric to string as text() only outputs strings
text("Distance: "+Integer.toString(value), 100, 400, 100, 50);         // text(string, x, y, width, height)
text("Radar screen code at luckylarry.co.uk", 540, 380, 250, 50);
fill(0);
rect(70,60,150,100);
fill(0, 100, 0); 
text("Screen Key:", 100, 50, 150, 50);
fill(0,50,0);
rect(30,53,10,10);
text("First sweep", 115, 70, 150, 50);
fill(0,110,0);
rect(30,73,10,10);
text("Second sweep", 115, 90, 150, 50);
fill(0,170,0);
rect(30,93,10,10);
text("Average", 115, 110, 150, 50);
noFill();
stroke(150,0,0);
strokeWeight(1);
ellipse(29, 113, 10, 10); 
fill(150,0,0);
text("Motion", 115, 130, 150, 50);
}

/* get values from serial port */
void serialEvent (Serial arduinoport) {
String xString = arduinoport.readStringUntil('\n');  // read the serial port until a new line
if (xString != null) {  // if theres data in between the new lines
xString = trim(xString); // get rid of any whitespace just in case
String getX = xString.substring(1, xString.indexOf("V")); // get the value of the servo position
String getV = xString.substring(xString.indexOf("V")+1, xString.length()); // get the value of the sensor reading
degree = Integer.parseInt(getX); // set the values to variables
value = Integer.parseInt(getV);
oldValue[degree] = newValue[degree]; // store the values in the arrays.
newValue[degree] = value;  
/* sets a counter to allow for the first 2 sweeps of the servo */
firstRun++;
if (firstRun > 360) {
firstRun = 360; // keep the value at 360 
}
}
}

Most Useless Machine - Caixa Teimosa



Neste post estou apresentando um projeto simples e ao mesmo tempo muito divertido, uma caixa que se desliga sozinha. Este projeto ensina alguns princípios básicos como o uso de chave DPDT e microswitches e especialmente os conceitos de montagem mecânica que são sempre o mais interessante quando se quer uma atividade que prenda a atenção das crianças (no meu caso, minha filha de 11 anos que criou a pintura com cara de monstro para a caixa).

Existem centenas de vídeos e páginas com este projeto e até mesmo kits prontos para montar como é o caso deste da solarbotics.

Os materiais para montagem são:

1. Uma caixa de qualquer material rígido e fácil de furar, comprei uma de madeira em uma loja de artesanato por R$ 4,00;
2. Um microswitch e um switch DPDT que podem ser encontrado em qualquer loja de componentes eletrônicos;
3. Um motor, eu utilizei um de 33 rpm com alimentação de 6 volts, testei alguns mais rápidos mas a caixa saiu de controle :-);
4. Um poco de arame e criatividade para montar e adaptar o braço mecanico;
5. Uma fonte de alimentação, no meu caso foi uma bateria de 6 v chumbo ácido que eu tinha aqui mas pode ser com pilhas ou outra fonte.

O esquema de montagem é o seguinte:



Segue um vídeo com o funcionamento interno:



Quando liga-se o switch, o braço move-se na direção para desligá-lo, quando o braço desliga o switch a direção do motor é invertida.
Quando o braço chega ao fim do curso, aciona o microswitch que desliga todo o sistema. Divirta-se!

Som na Caixa!

Estou pesquisando uma forma de incluir som quando o botão da caixa é ligado.

seguem alguns links para minha pesquisa com o chip LM386:

http://josepino.com/audio/mini_amplifier_lm386

http://www.instructables.com/id/Audio-Amplifier

http://www.instructables.com/id/Know-Your-IC-LM386/step4/Project-layout/



Ethernet Shield enviando dados para web



Neste post estou usando o ethernet shield do arduino para enviar as leituras de temperatura de um sensor LM35 para o site  ThingSpeak e exibir estas leituras em forma de gráfico no blog. O site ThingSpeak permite que o usuário envie diversos tipos de dados e acesse estes dados de diversas formas com uma api para inclusão em outros meios como web, twitter ou o que mais você quiser.

No exemplo abaixo estou enviando estas leituras para um pequeno gráfico que pode ser visto em todas as páginas deste blog, esta mesma idéia pode ser usada para enviar outros tipos de leituras e existem ainda formas de fazer com que esta função tenha duas vias para controlar equipamentos e fazer automação doméstica.

Na imagem acima o sensor está conectado na porta analógica 0 e alimentado por 5V a partir da própria placa Ethernet. As leituras analógicas são convertidas para apresentar a temperatura em graus centígrados antes do envio.

(imagem estática de exemplo)

O código para exibição no blog é simples e pode ser encontrado no endereço https://www.thingspeak.com/channels/261 que é o endereço do canal destes dados no site ThingSpeak.

----------------------------------------------------------------------------------------------------


/*

 ThingSpeak Client to Update Channel Feeds
 
 The ThingSpeak Client sketch is designed for the Arduino + Ethernet Shield.
 This sketch updates a channel feed with an analog input reading via the
 ThingSpeak API (http://community.thingspeak.com/documentation/)
 using HTTP POST.
 
 Getting Started with ThingSpeak:
 
    * Sign Up for New User Account - https://www.thingspeak.com/users/new
    * Create a New Channel by selecting Channels and then Create New Channel
    * Enter the Write API Key in this sketch under "ThingSpeak Settings"
 
 Created: January 25, 2011 by Hans Scharler (http://www.iamshadowlord.com)
 
 Additional Credits: Example sketches from Tom Igoe and David A. Mellis
 
*/

#include <SPI.h>
#include <Ethernet.h>

int pin = 0; // analog pin
int tempc = 0,tempf=0; // temperature variables
int samples[8]; // variables to make a better precision
int maxi = -100,mini = 100; // to start max/min temperature
int i;

// Local Network Settings
byte mac[]     = { 0xD4, 0x28, 0xB2, 0xFF, 0xA0, 0xA1 }; // Must be unique on local network
byte ip[]      = { 192, 168,   1,  13 };                // Must be unique on local network
byte gateway[] = { 192, 168,   1,   1 };
byte subnet[]  = { 255, 255, 255,   0 };

// ThingSpeak Settings
byte server[]  = { 184, 106, 153, 149 }; // IP Address for the ThingSpeak API
String writeAPIKey = "sua chave vai aqui";    // Write API Key for a ThingSpeak Channel
const int updateInterval = 10000;        // Time interval in milliseconds to update ThingSpeak   
Client client(server, 80);

// Variable Setup
long lastConnectionTime = 0; 
boolean lastConnected = false;
int resetCounter = 0;

void setup()
{
  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.begin(9600);
  delay(1000);
}

void loop()
{
  
  tempc = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;

  String analogPin0 = String(tempc);
  
  // Print Update Response to Serial Monitor
  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }
  
  // Disconnect from ThingSpeak
  if (!client.connected() && lastConnected)
  {
    Serial.println();
    Serial.println("...disconnected.");
    Serial.println();
    
    client.stop();
  }
  
  // Update ThingSpeak
  if(!client.connected() && (millis() - lastConnectionTime > updateInterval))
  {
    updateThingSpeak("field1="+analogPin0);
  }
  
  lastConnected = client.connected();
}

void updateThingSpeak(String tsData)
{
  if (client.connect())
  { 
    Serial.println("Connected to ThingSpeak...");
    Serial.println();
        
    client.print("POST /update HTTP/1.1\n");
    client.print("Host: api.thingspeak.com\n");
    client.print("Connection: close\n");
    client.print("X-THINGSPEAKAPIKEY: "+writeAPIKey+"\n");
    client.print("Content-Type: application/x-www-form-urlencoded\n");
    client.print("Content-Length: ");
    client.print(tsData.length());
    client.print("\n\n");

    client.print(tsData);
    
    lastConnectionTime = millis();
    
    resetCounter = 0;
    
  }
  else
  {
    Serial.println("Connection Failed.");   
    Serial.println();
    
    resetCounter++;
    
    if (resetCounter >=5 ) {resetEthernetShield();}

    lastConnectionTime = millis(); 
  }
}

void resetEthernetShield()
{
  Serial.println("Resetting Ethernet Shield.");   
  Serial.println();
  
  client.stop();
  delay(1000);
  
  Ethernet.begin(mac, ip, gateway, subnet);
  delay(1000);
}

----------------------------------------------------------------------------

Internet of Things

LUX com arduino GPRS shield e sensor LDR.

/*
Graph: Feed 38642, Datastream lux
*/

Laboratórios, Lojas e Produtos

Blogs, Comunidades e Revistas