Go Down

Topic: Controlador de Temperatura [Arduino + Ethernet Shield + Relays + MySQL + PHP] (Read 14244 times) previous topic - next topic

Como primer proyecto me dispuse a hacer un control simple de temperatura que activa 2 relays en base a la temperatura obtenida y a su vez guarda en MySQL la temperatura para futuro procesamiento... es decir todo el control de setpoint's, horarios y demas puede ser realizado en PHP para asi dejar libre al Arduino de espacio.

Esta simple y espero a alguno de ustedes le ayude en algo.

El codigo esta hecho para un sensor DS18B20 (http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf)

Requerimientos:
- Arduino c/ Ethernet Shield
- Sensor DS18B20 (Pero lo puedes adaptar a tus necesidades) y resistencia de 4.7k
- 2 Relays de 5v de operación
- Servidor PHP y MySQL
- Tener la libreria OneWire - http://playground.arduino.cc/Learning/OneWire


Conexiones:
- Sensor de Temperatura - Digital Pin 2
- Relay 1 - Digital Pin 3
- Relay 2 - Digital Pin 4



Sketch de Arduino
Code: [Select]

/*************************************************************
Project: Temperature Controller
Author: Agustin Marmolejo (Twitter @eljamz)
Date: 18-December-2012
Description: A temperature controller who save's all the data into MySQL
and from there it can activate some relays.
*************************************************************/
#include <Ethernet.h>
#include <SPI.h>
#include <OneWire.h>

//Ethernet Configuration
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xEE}; // Ethernet Shield MAC
byte ip[] = { 192,168,1,249 }; // Ethernet Shield IP
byte server[] = { 192,168,1,136 }; // Server IP

EthernetClient client;
float value;

//Settings for Relays
String location = "http://192.168.1.136/arduino_sensor.php HTTP/1.0";
char inString[32]; // string for incoming serial data
int stringPos = 0; // string index counter
boolean startRead = false; // is reading?

//Declare Relay Pinouts
int rel1Pin = 3;  // Setup Realy 1 pin
int rel2Pin = 4; // Setup Realy 2 pin

//Define where to read the Sensor
int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2

//Temperature chip i/o
OneWire ds(DS18S20_Pin); // on digital pin 2

//void setup
void setup(void) {
 Serial.begin(9600);
 Ethernet.begin(mac, ip); // Init Ethernet Shield
 pinMode(rel1Pin, OUTPUT); // Make relPin and OUTPUT
 pinMode(rel2Pin, OUTPUT); // Make relPin and OUTPUT
 delay(5000); // Wait 5 seconds to Init Program
}


//void loop
void loop(void) {
float temperature = getTemp(); // Get the temperature from sensor and INSERT to MySQL.
Serial.println(temperature); // print out temperature.
delay(500); //just here to slow down the output so it is easier to read
String pageValue = connectAndRead(); //Connect to server and get action to follow!.
Serial.println(pageValue); //print out the findings.
delay(500); //just here to slow down the output so it is easier to read
}


/*
FUNCTION FOR RELAYS
*/
String connectAndRead(){
 //connect to the server

 Serial.println("connecting...");

 //port 80 is typical of a www page
 if (client.connect(server, 80)) {
   Serial.println("connected");
   client.print("GET ");
   client.println(location);
   client.println();

   //Connected - Read the page
   return readPage(); //go and read the output

 }else{
   return "connection failed";
 }

}



String readPage(){
 //read the page, and capture & return everything between '<' and '>'

 stringPos = 0;
 memset( &inString, 0, 32 ); //clear inString memory

 while(true){

   if (client.available()) {
     char c = client.read();

     if (c == '<' ) { //'<' is our begining character
       startRead = true; //Ready to start reading the part
     }else if(startRead){

       if(c != '>'){ //'>' is our ending character
         inString[stringPos] = c;
         stringPos ++;
       }else{
         //got what we need here! We can disconnect now
         startRead = false;
         client.stop();
         client.flush();
         Serial.println("disconnecting.");
         
         int inRel = atoi (inString);
         if (inRel == 1) {
           digitalWrite(rel1Pin, HIGH);
           digitalWrite(rel2Pin, LOW);
         } else if (inRel == 2) {
           digitalWrite(rel1Pin, LOW);
           digitalWrite(rel2Pin, HIGH);
         }
         return inString;

       }

     }
   }

 }

}



/*
FUNCTION FOR TEMPERATURE.
*/
float getTemp(){
//returns the temperature from one DS18S20 in DEG Celsius

byte data[12];
byte addr[8];

if ( !ds.search(addr)) {
  //no more sensors on chain, reset search
  ds.reset_search();
  return -1000;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
  Serial.println("CRC is not valid!");
  return -1000;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
  Serial.print("Device is not recognized");
  return -1000;
}

ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end

byte present = ds.reset();
ds.select(addr);  
ds.write(0xBE); // Read Scratchpad


for (int i = 0; i < 9; i++) { // we need 9 bytes
 data[i] = ds.read();
}

ds.reset_search();

byte MSB = data[1];
byte LSB = data[0];

float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;

//Display in Serial Monitor
Serial.print(TemperatureSum); //Return temperature to Monitor
Serial.println(" Celsius");

//Save Temperature to MySQL
 Serial.println("Connecting...");

 if (client.connect(server, 80)>0) {  // Connect to server
   client.print("GET /arduino_sensor.php?type=Celsius&value="); // Send the DATA with GET
   client.print(TemperatureSum);
   client.println(" HTTP/1.0");
   client.println("User-Agent: Arduino 1.0");
   client.println();
   Serial.println("Connected");
 }
 else
 {
   Serial.println("Connection FAIL");
 }
 if (client.connected()) {}
 else {
   Serial.println("Disconnected!");
 }
 client.stop();
 client.flush();
}


arduino_sensor.php
Code: [Select]

<?php
include('db.php');
//Get parameter sended by Arduino.
$type $_GET["type"];
$value $_GET["value"];


// Validate parameters if any and INSERT.
if (($type != "") && ($value != "")) {
mysql_query("INSERT INTO sensor (date, type, value) values (NOW(),'$type','$value')");
} else {

//If no parameters run script for RELAYS.
$result mysql_query("SELECT value FROM sensor ORDER BY date DESC LIMIT 1");
while(
$row mysql_fetch_array($result))
  {
$value $row['value'];
$value = (int)$value;
  }
}

if (
$value >= 28) { //this could be changed to anything, if you return <1> start Relay 1 in PIN3 and <2> start your second Relay in PIN 4.
echo "<1>";
} else {
echo "<2>";
}


mysql_close($con);
?>



db.php
Code: [Select]

<?php

$con 
mysql_connect("localhost","root","");
if (!$con){
die('Connection fail: ' mysql_error());
}
mysql_select_db("arduino"$con);
?>


MySQL Create Code
Code: [Select]

CREATE TABLE `sensor` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`date` DATETIME NOT NULL,
`type` VARCHAR(100) NOT NULL COLLATE 'latin1_spanish_ci',
`value` FLOAT NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id` (`id`)
)
COLLATE='latin1_spanish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=0;



Espero les sirva, saludos!
You can't control what you can't measure!

Muy buen trabajo!

Me gustaría hacerte una recomendación en el código arduino_sensor.php, ya que es vulnerable a una inyección SQL y un graciosillo que encuentre la pagina puede fastidiarte la base de datos.

Simplemente es añadir mysql_real_escape_string() a las variables que van a interactuar con la base de datos para evitar una posible intrusión.

Quote
mysql_query("INSERT INTO sensor (date, type, value) values (NOW(),'mysql_real_escape_string($type)','mysql_real_escape_string($value)')");



Un saludo  ;)

Sergegsx

estupendo aporte !!

hace tiempo que tengo este mismo post pendiente de compartir con la comunidad, me lo has ahorrado jeje

luego lo pruebo y comento si algo va mal pero tiene muy buena pinta.

Gracias por sus comentarios, con respecto al Inyección SQL, Se me ocurre agregarle a las cadenas un user y una pwd, y validar de esta manera, digo ya para que sepan también el usuario y contraseña estará medio difícil supongo!

Esto además de poner este comando para limpiar las variables...


Saludos!
You can't control what you can't measure!

Si quieres seguridad a tope usa md5 en el servidor arduino y en el script php. Es como funcionan las API simple y muy eficaz.

Por ejemplo en el servidor arduino que te genere el md5 de la string de temp seguido de tu key (tempKEY). Se lo envías al php como .php?temp=22&md5=(lo que genere arduino), entonces con la misma key que la de arduino y la temp que recibes en el php vuelves a generar el md5 y los comparas, que son iguales sigues adelante que no puede ser una intrusión.

Un saludo!

jray

Buen trabajo, este me lo guardo en la carpeta de recortes.
Y buen apunte de JRodrigo.


Muy buen trabajo!

Me gustaría hacerte una recomendación en el código arduino_sensor.php, ya que es vulnerable a una inyección SQL y un graciosillo que encuentre la pagina puede fastidiarte la base de datos.

Simplemente es añadir mysql_real_escape_string() a las variables que van a interactuar con la base de datos para evitar una posible intrusión.

Quote
mysql_query("INSERT INTO sensor (date, type, value) values (NOW(),'mysql_real_escape_string($type)','mysql_real_escape_string($value)')");



Un saludo  ;)


Si quieres seguridad a tope usa md5 en el servidor arduino y en el script php. Es como funcionan las API simple y muy eficaz.

Por ejemplo en el servidor arduino que te genere el md5 de la string de temp seguido de tu key (tempKEY). Se lo envías al php como .php?temp=22&md5=(lo que genere arduino), entonces con la misma key que la de arduino y la temp que recibes en el php vuelves a generar el md5 y los comparas, que son iguales sigues adelante que no puede ser una intrusión.

Un saludo!


Creo que con el puro md5(tempKEY); es suficiente no?, digo a final de cuentas la idea es que no se sepan la tempkey... y el valor de la temperatura es irrelevante una vez que adivinen el (tempKEY)



You can't control what you can't measure!

Igor R

He añadido el post en http://playground.arduino.cc//Es/FAQ

Saludos y gracias,


Igor

Creo que con el puro md5(tempKEY); es suficiente no?, digo a final de cuentas la idea es que no se sepan la tempkey... y el valor de la temperatura es irrelevante una vez que adivinen el (tempKEY)


Lo del md5 era una idea por si te podía interesar, es también valida tu idea de utilizar un usuario con su contraseña, ya que pienso que nadie se va a dedicar a meterte datos falsos (alguien que se aburra mucho puede que si jajjaja), la única brecha de seguridad bastante grave era al introducir los datos en la DB pero lo arreglas con la función mysql_real_escape_string() y listo!


He añadido el post en http://playground.arduino.cc//Es/FAQ

Saludos y gracias,


Igor

Gracias a ti! es un honor aparecer ahi!



Lo del md5 era una idea por si te podía interesar, es también valida tu idea de utilizar un usuario con su contraseña, ya que pienso que nadie se va a dedicar a meterte datos falsos (alguien que se aburra mucho puede que si jajjaja), la única brecha de seguridad bastante grave era al introducir los datos en la DB pero lo arreglas con la función mysql_real_escape_string() y listo!

De hecho tienes razon, pero lo del MD5 tempkey tambien es una "buena" practica, lo ideal seria utilizar una session, y esto es posible pero nos quitaria mucho espacio... lo que podemos hacer es:

Como ya sabemos la direccion IP del arduino, podemos desde PHP saber el IP del cliente, y entonces limitar a que solo el arduino tenga acceso...

Agregar esto en "arduino_sensor.php"
Code: [Select]

<?php
$arduino_ip 
"192.168.1.1";
$client_ip = @$REMOTE_ADDR

if (
$arduino_ip == $client_ip) {

EL CODIGO AQUI

} else {
echo 
"No estas autorizado para usar este sitio";
}
?>

You can't control what you can't measure!

Sergegsx

creo que deberia de ser
Code: [Select]
$_SERVER["REMOTE_ADDR"]
al menos en mi servidor remoto.

Lo malo es que si se reinicia el router y cambia la ip, ya no funcionara.

Por supuesto me refiero si el servidor esta fuera de la red de casa.

Justo eso quise decir
Red Local:
Code: [Select]

$ip = $_SERVER["REMOTE_ADDR"];

Si estas dentro de la red el IP esta definido, y si estas trabajando sobre internet puedes utilizar un "No-IP.org" o "DynDNS" asi tu ip publica si cambia el PHP la resuelve en automatico...

Code: [Select]

$ip = gethostbyname("user.no-ip.org");


Saludos!
You can't control what you can't measure!

Jossema106


Justo eso quise decir
Red Local:
Code: [Select]

$ip = $_SERVER["REMOTE_ADDR"];

Si estas dentro de la red el IP esta definido, y si estas trabajando sobre internet puedes utilizar un "No-IP.org" o "DynDNS" asi tu ip publica si cambia el PHP la resuelve en automatico...

Code: [Select]

$ip = gethostbyname("user.no-ip.org");



Es mas, algunos routers tienen la opcion de añadirle la cuenta y que sea el propio router el que la actualice automaticamente.
Saludos!
Proyecto:
http://duinoblog.blogspot.com.es/


Es mas, algunos routers tienen la opcion de añadirle la cuenta y que sea el propio router el que la actualice automaticamente.
Saludos!


Si! creo que ya la mayoria lo tiene como "default" solo busquen DynDNS y ahi apareceran varios servicios y ya lo configuran, esto para poder actualizar el IP en automatico.
You can't control what you can't measure!


Go Up