Hola, en esta nueva entrada voy a describir como crear un discriminador de llamadas telefónicas usando la capacidad de ciertos modems serie de hacer Caller ID o lo que es lo mismo, identificación de llamadas. Para ello usaremos un Arduino UNO R3 Atmega32, un modem serie U.S. Robotics 56K, un modulo serie, un cable serie modem nulo, un modulo SD YL-30 y un keypad shield LCD 16x2 (en la última versión del proyecto se usa un LCD 16x2 y una botonadura simple de 4 botones DIY). Como siempre el costo de los materiales es ridículo, si exceptuamos el módem, que es un cacharro que muchos tenemos en casa de la época del internet sin ADSL.
El sistema funciona del siguiente modo:
Al recibirse una llamada, el modem realiza un Caller ID y detecta el número llamante, para que se reciba esta información al menos deben recibirse dos RING, este es enviado al Arduino a través del cable modem nulo y el módulo serie. Arduino lee el número y lo compara con los números almacenados en un fichero de teléfonos bloqueados ya existente, si no lo encuentra entre los números del fichero, lo graba en un fichero de llamadas recibidas, si lo encuentra, le indica al modem que coja la llamada, evitando que nos fría a timbrazos. Luego también podremos leer el fichero de llamadas recibidas y bloquear los números que nos interesen, desde el propio Arduino, usando la botonadura del keypad y los números presentados en el LCD.
La conexiones a realizar entre los diferentes elementos y Arduino, son las siguientes:
- Modulo SD YL-30:
MOSI............11
MISO............12
SCK..............13
CS.................10
3,3v................3.3v
5v...................5v
GND..............GND
- Modulo Serie:
Es un módulo serie sencillo con entradas TX-RX y de voltaje, y una salida DB9 para poder conectarla al modem a través del cable serie.
Modulo: Arduino:
RX............................8
TX............................9
- Modulo Keypad shield LCD de 5 botones:
Conectaremos los pines del Keypad shield del modo siguiente:
Keypad: Arduino:
5v..........................................5v.
GND.....................................GND
4............................................4
5............................................5
6............................................6
7............................................7
8............................................2
9............................................3
A0.........................................A0
En el caso de usar un LCD 1602 bruto, hay que hacer una pequeña modificación para que sea usable en Arduino. Hay que soldarle una tira de 16 pines macho, y hay que soldar entre sí los pines 1 - 4 y 16 (GND) y entre sí los pines 2 y 15 (+5v.), así como unir los pines 1 y 3 con una resistencia de 3 K Ohm.
La correspondencia con Arduino es la siguiente:
LCD: Arduino:
1 ...................GND
2 ...................5v.
3.....................Unido a GND por resistencia 3 KOhm
4....................2
5....................GND
6.....................3
....
11.....................4
12.....................5
13.....................6
14.....................7
15.....................5v.
16 .....................GND
La botonadura DIY se hace de forma sencilla con cuatro botones y tres resistencias de 3 K Ohm. Como en la figura adjunta, pero uniendo un extremo al Pin analógico A0 y el otro a cualquier GND.
El cable serie de conexión entre el modulo serie y el modem se construye de la siguiente manera:
Se necesita un cable con tres polos para las líneas RX y TX, que deben cruzarse entre los dos conectores, y la linea de GND. En cada conector se deben unir entre si las patillas RTS-CTS (7 y 8) y las DTR-DSR (4 y 6). De esta manera el módem creerá que la negociación de datos que realiza es correcta y nos permitirá comunicarnos con el Arduino.
El sketch para hacer funcionar todo es el siguiente:
/* Proyecto de discriminador de llamadas telefonicas no deseadas, tras la llamada se identifica el
numero llamante y se compara con los que están anotados en un fichero almacenado en una tarjeta SD. Si
coincide con alguno, la llamada es bloqueada. Ademas graba automáticamente los números que han llamado
y que posteriormente pueden seleccionarse con la botonadura para ser bloqueados.
El hardware necesario es un Arduino Uno, un modem con CallerID (en mi caso US Robotics 56k), un modulo
SD YL-30, una tarjeta SD, un modulo Serie RS232, un cable serie modem nulo, un modulo LCD de 16x2 y
una botonadura tipo Keypad de resistencias (una botonadura simple con tres resistencias de 3K en serie
conectando GND a Pin A0, p.e.).
El cable serie modem nulo permite obviar el comportamiento del modem en respuesta a la lineas RTS-CTS,
DTR-DSR, las cuales se unen en los extremos de cada conector, cruzándose las lineas TX-RX y
uniendo las GND entre si.
Conexion pines modulo SD a Arduino:
modulo: GND 3.3v 5v. CS MOSI SCK MISO GND
Arduino:GND 3.3V 5V. 10 11 13 12 GND
Conexion pines modulo LCD 1602 a Arduino:
(X)En modulo unir 1 y 3 con Resitencia 3K.
modulo: 1 2 3 4 5 6 ....11 12 13 14 15 16
Arduino:GND 5v(X) 2 GND 3 ....4 5 6 7 5v GND
*/
//Sample using LiquidCrystal library
#include <LiquidCrystal.h>
#include <SD.h> //CS en pin 10
// select the pins used on the LCD panel
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
// define some values used by the panel and buttons
int lcd_key = 0;
int adc_key_in = 0;
#define btn3 0
#define btnUP 1
#define btn2 2
#define btnLEFT 3
#define btn1 4
#define btnNONE 5
// Guardamos en que entrada de arduino esta conectado el pin CS del modulo.
//const int chipSelect = 10;
char nmbrstring[ ] = "NMBR";
char numerodete[10];
char numerotst[10];
char numllam[10];
char charBuf[10];
char charBuf2[10];
char charThree[10];
int numblock = 50;//cantidad de lineas de numeros del fichero
int i2 = 0;
int llamadas = 0;// Contador de llamadas, debe ser 0. Otro valor es para pruebas.
File myFile; //fichero llamadas recibidas
String stringThree;
unsigned long tAntes = 0;
unsigned long tAntes2 = 0;
unsigned long tAntes3 = 0;
unsigned long tAntes4 = 0;
unsigned long tAntes5 = 0;
long time_now = 0;
void setup()
{
lcd.begin(16, 2); // start LCD library
pinMode(A0, INPUT_PULLUP); // sets analog pin for input
lcd.setCursor(0,0);
lcd.print("LCD Activado"); // print a simple messag
delay (2000);
// Configuramos el puerto serie (esta version NO usa Serie Virtual)
Serial.begin(9600);
while (!Serial) {
; // Espera para conexion puerto serie, solo necesario para leonardo
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Setup is Ready");
//Iniciamos la SD
//Serial.print("Iniciando SD...");
//Serial.print("\n");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Iniciando SD...");
// El pin CS por defecto de la placa arduino debe ser configurado como salida
// aunque no se use (10 en la mayoria de las placas, 53 en Arduino Mega).
pinMode(10, OUTPUT);
// Si ha habido error al leer la tarjeta informamos por el puerto serie.
if (!SD.begin(10)) {
//Serial.println("Error inicializar SD!");
lcd.setCursor(0,1);
lcd.print("Error ini.SD!");
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PULSE RESET");
pinMode(10, INPUT);
delay(1000);
return;
}
lcd.setCursor(0,1);
lcd.print("SD ini...OK");
delay(2000);
//REcreando fichero RECIB.TXT
File Archivo1;
Archivo1 = SD.open("RECIB.TXT", FILE_WRITE);
if (Archivo1) {
Archivo1.println("111111111");
Archivo1.close();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RECIB.TXT ini");
delay(2000);
lcd.setCursor(0,1);
lcd.print("OK");
delay(2000);
} else {
lcd.setCursor(0,1);
lcd.print("Error SD write");
delay(2000);
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Start Modem....");
// Iniciamos los comandos Hayes para configurar el modem
Serial.print("ATZ\r\n");
delay(4000);
lcd.setCursor(0,1);
lcd.print("4.");
//mySerial.write("AT+VCID=1\r\n");//Conceptronics CID ON
Serial.print("AT#CID=1\r\n");//USR Robotics CID ON
delay(2000);
lcd.setCursor(2,1);
lcd.print("3.");
Serial.print("ATS7=5\r\n");//USR Robotics Answer Wait 7 seg
delay(2000);
lcd.setCursor(4,1);
lcd.print("2.");
Serial.print("ATS0=0\r\n");//USR Robotics Auto Answer OFF
delay(2000);
lcd.setCursor(6,1);
lcd.print("OK");
delay(2000);
}
void loop()
{
static char buffer[20];
buttonselect();// Selector botones
if (readline(Serial.read(), buffer, 80) > 0)
{
char* numero1 = strchr(buffer, 'NMBR =');
char* prefinmbr = strtok(buffer, " =");
char* numero2 = strchr(numero1, ' ');
if(strcmp(prefinmbr, nmbrstring) == 0)//Detectamos la cadena NMBR que contiene el numero llamante
{
//Lector de numero
char* numerodete = numero2;//detecta numero con 1 espacio al inicio
String stringOne = numerodete;//convierte numero char a string
stringOne.trim();//elimina el espacio del inicio del número
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Num.llamante:");
lcd.setCursor(0,1);
lcd.print(stringOne);
stringOne.toCharArray(charBuf, 10);//convierte el numero string a char para strcmp
lcd.setCursor(0,1);
lcd.print(charBuf);
//Inicio codigo escritura llamadas recibidas
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("RECIB.TXT", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
myFile.println(stringOne);
myFile.close();// close the file:
llamadas++;// acumulado de numero de llamadas
lcd.setCursor(0,0);
lcd.print("Llamadas.....");
lcd.setCursor(13,0);
lcd.print(llamadas);
lcd.setCursor(12,1);
lcd.print("WCOK");
//Mantiene el tiempo a 0 mientras se pulsa boton lectura
long time_now = millis();
tAntes = time_now;
tAntes2 = time_now;
tAntes3 = time_now;
tAntes4 = time_now;
} else {
// if the file didn't open, print an error:
//Serial.println("error opening file");
lcd.setCursor(0,1);
lcd.print("error SD write");
}
// fin codigo escritura llamadas recibidas
//Inicio seccion lectura fichero numeros bloqueados.
char Ruta[9] = {'B', 'L', 'O', 'Q', '.', 'T', 'X', 'T', '\0'};//establece nombre fichero tlfs bloqueados
for(int i = 0; i < numblock; i++)// secuencialmente se lee cada una de los numeros del fichero
{
//Leemos linea de archivo en la SD
String numerotst = ReadFile(i,Ruta);
String stringTwo = numerotst;
stringTwo.toCharArray(charBuf2, 10);// convertimos string to char para strcmp
if(strcmp(charBuf, charBuf2) == 0)// comparamos el numero detectado con el leido en el fichero
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Numero detec.:");
lcd.setCursor(0,1);
lcd.print(charBuf); // Muestra el numero bloqueado
//Codigo contestar y colgar
Serial.print("ATA\r\n");//Coge el tlf
delay(2000);
Serial.print("ATH1\r\n");//Cuelga el tlf
delay (4000);
}// Fin deteccion numero bloqueado
}//Fin bucle lectura numeros bloqueados SD
}//Fin deteccion de llamada NMBR
}//Fin lectura buffer serie
}
// Codigo de lectura de los telefonos bloqueados en la SD
String ReadFile(int Linea,char Ruta[]){
int Lin=0;
String Resultado;
File myFile;
byte Bin;
myFile = SD.open(Ruta);;
if (myFile) {
while (myFile.available()) {
Bin=myFile.read();
if (Bin==13){Lin++;myFile.read();}
else
{
if (Lin==Linea){Resultado=Resultado+(char(Bin));}
if (Lin>Linea){myFile.close();return Resultado;}
}
}
myFile.close();return Resultado;
}
}
/// AÑADIDO CONTROL BOTONADURA Y GRABADO NUMEROS BLOQUEADOS SD
void buttonselect()
{
lcd.setCursor(0,1); // move to the begining of the second line
lcd_key = read_LCD_buttons(); // read the buttons
switch (lcd_key) // depending on which button was pushed, we perform an action
{
case btn3:
{
lcd.clear();
lcd.setCursor(0,1);
//lcd.print(adc_key_in);
lcd.print("RIGHT ");
delay(500);
break;
}
case btn2:
{
/*lcd.clear();
lcd.setCursor(0,1);
lcd.print(adc_key_in);
lcd.print("DOWN ");*/
leer();
break;
}
case btn1:
{
//lcd.setCursor(7,0);
//lcd.print("Bloquear");
bloquear();
/*lcd.clear();
lcd.setCursor(0,1);
lcd.print(adc_key_in);
lcd.print("SELECT");*/
break;
}
case btnNONE:
{
long time_now = millis();
if ((long)(time_now - tAntes) > 10000) // regulador de tiempo medida datos y presentacion lcd
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Waiting call...");
lcd.setCursor(0,1);
lcd.print("Llamadas.....");
lcd.setCursor(13,1);
lcd.print(llamadas);
tAntes = time_now;
}
if ((long)(time_now - tAntes2) > 1000)
{
lcd.setCursor(15,0);
lcd.print("*");
tAntes2 = time_now;
}
if ((long)(time_now - tAntes3) > 1500)
{
lcd.setCursor(15,0);
lcd.print(" ");
tAntes3 = time_now;
}
//Reponedor a 0 de lectura llamadas si inactividad en botones
if ((long)(time_now - tAntes4) > 20000)
{
i2 = 0;
tAntes4 = time_now;
}
if ((long)(time_now - tAntes5) > 300000) //RESETEO MODEM CADA 5 MINUTOS
{
modemreset();
tAntes5 = time_now;
}
break;
}
}
}
void leer()
{
char Ruta[10] = {'R', 'E', 'C', 'I', 'B', '.', 'T', 'X', 'T', '\0'};//establece nombre fichero tlfs bloqueados
String numllam = ReadFile(i2,Ruta);//Leemos linea de archivo como char en la SD
//Proceso conversion de string to char para poder grabar los datos leidos
String stringThree = numllam; //convertir char to string
stringThree.toCharArray(charThree, 10);// convertimos string to char
delay(500);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Numero Leido.....");
lcd.setCursor(15,0);
lcd.print((llamadas + 5)- i2);
//lcd.setCursor(0,1);
//lcd.print(" "); // Muestra el numero leido
lcd.setCursor(0,1);
lcd.print(charThree); // Muestra el numero leido
lcd.setCursor(12,1);
lcd.print(".."); // Muestra el numero leido
delay (500);
i2++;
//Mantiene el tiempo a 0 mientras se pulsa boton lectura
long time_now = millis();
tAntes = time_now;
tAntes2 = time_now;
tAntes3 = time_now;
tAntes4 = time_now;
if (i2 > (llamadas + 5))
{
i2 = 0;
llamadas = 0;
delay (1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Borrando las");
lcd.setCursor(0,1);
lcd.print("LLamadas...");
myFile.close();
delay(2000);
//Borrando fichero RECIB.TXT
SD.remove("RECIB.TXT");
delay(2000);
myFile.close();
//REcreando fichero RECIB.TXT
File Archivo1;
Archivo1 = SD.open("RECIB.TXT", FILE_WRITE);
if (Archivo1) {
Archivo1.println("111111111");
delay(200);
Archivo1.close();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RECIB.TXT ini");
delay(2000);
lcd.setCursor(0,1);
lcd.print("OK");
delay(2000);
} else {
lcd.setCursor(0,1);
lcd.print("Error SD write");
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
lcd.print("PULSE RESET");
delay(2000);
}
// fin recreo
}
}
//funcion para escribir el número leido de RECIB.TXT y grabarlo en
//el fichero de numeros bloqueados BLOQ.TXT
void bloquear()
{
myFile = SD.open("BLOQ.TXT", FILE_WRITE);
if (myFile){
myFile.println(charThree);
delay(200);
myFile.close();
} else {
lcd.setCursor(0,1);
lcd.print("error SD write");
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Numero bloqueado");
lcd.setCursor(0,1);
lcd.print(" "); // Limpia espacio numero
lcd.setCursor(0,1);
lcd.print(charThree); // Muestra el numero bloqueado
lcd.setCursor(12,1);
lcd.print("BQ");
//Mantiene el tiempo a 0 mientras se pulsa boton lectura
long time_now = millis();
tAntes = time_now;
tAntes2 = time_now;
tAntes3 = time_now;
tAntes4 = time_now;
delay(2000);
}
//Sistema de lectura del puerto serie
int readline(int readch, char *buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\n': // Ignore new-lines
break;
case '\r': // Return on CR
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len-1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -1.
return -1;
}
// fin programa lectura puerto serie
// read the buttons
int read_LCD_buttons()
{
adc_key_in = analogRead(0); // read the value from the sensor
//Valores para botonadura simple de 3 resistencias de 3k Ohm en serie
if (adc_key_in > 1000) return btnNONE; // Ningun boton pulsado
if (adc_key_in < 95 && adc_key_in > 90) return btn3;
//if (adc_key_in < 250) return btnUP;
if (adc_key_in < 200 && adc_key_in > 100) return btn2;
//if (adc_key_in < 650) return btnLEFT;
if (adc_key_in > 200 && adc_key_in < 300) return btn1;
return btnNONE; // when all others fail, return this...
}
// FIN READ BUTTONS
// Reseteo modem
void modemreset()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Reset Modem....");
// Iniciamos los comandos Hayes para configurar el modem
Serial.print("ATZ\r\n");
delay(4000);
lcd.setCursor(0,1);
lcd.print("4.");
//mySerial.write("AT+VCID=1\r\n");//Conceptronics CID ON
Serial.print("AT#CID=1\r\n");//USR Robotics CID ON
delay(2000);
lcd.setCursor(2,1);
lcd.print("3.");
Serial.print("ATS7=5\r\n");//USR Robotics Answer Wait 7 seg
delay(2000);
lcd.setCursor(4,1);
lcd.print("2.");
Serial.print("ATS0=0\r\n");//USR Robotics Auto Answer OFF
delay(2000);
lcd.setCursor(6,1);
lcd.print("OK");
delay(2000);
}
martes, 23 de septiembre de 2014
sábado, 28 de junio de 2014
Sistema de control de temperaturas via web
En esta entrada voy a describir la construcción de un sistema de control de las temperaturas de un lugar, por ejemplo un frigorífico con medicamentos, de tal manera que podamos ver vía web las temperaturas actuales, la máxima y la mínima registradas. Usaremos un Arduino Uno, una fuente de alimentación, un sensor DS18B20 y un módulo ethernet ENC28J60, el conjunto es ridículamente barato. También necesitaremos darnos de alta en una web de registro de datos, tranquilos es gratis, que nos permitirá almacenar los datos enviados vía web por nuestro sistema de registro funcionando en el Arduino. Vamos al lío:
- Construcción del hardware:
Las conexiones del módulo ENC28J60 a arduino son las siguientes:
CS...Pin digital 8
SI...Pin digital 11
SO...Pin digital 12
SCK..Pin digital 13
VCC.... 3.3v
GND
La conexión del sensor de temperatura DS18B20 se hace colocando primero una resistencia de 4K7 Ohm uniendo el polo de datos al VCC (3.3v) como muestra el dibujo:
Las conexiones del sensor a arduino son:
GND y VCC a GND de Arduino y DATA a Pin digital 2.
Ya tenemos construido nuestro hardware.
- Dar de alta el servidor de datos de Xively (antes llamado Pachube):
Entramos en Xyvely.com y nos damos de alta. Vamos a "Develop" y Pulsamos "+ Add a Device". Le damos un Nombre, una Descripción , pulsamos en la selección de "Public Device", una vez hecho esto, damos a "Save Changes". Una vez hecho esto aparecerá en la parte inferior de la pagina Web unos datos importantes para poder configurar el programa que hará funcionar nuestro recolector de temperaturas, estos son:
- Free ID
- API Key
Las copiamos y las guardamos para usarlas luego.
- Programa sketch para Arduino:
Sustituye Free ID y Api Key por los valores que guardaste al dar de alta tu cuenta Xively. Copia el programa siguiente, y pégalo en tu pantalla de Arduino Uploader. Comprueba que tienes las bibliotecas requeridas instaladas. Comprueba que tienes una IP libre (cambia la 192.168.1.32, por la que tu tengas) y que la puerta de enlace (dirección IP de tu router) es la adecuada (sustituye 192.168.1.1 por la adecuada).
*********************Inicio de programa - no copiar esto**************************
// Sistema de control de temperaturas usando modulo ethernet ENC28J60 y sensor DS18B20
// 2011-07-08 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
//PIN CONNECTION FOR ENC28J60 ETHERNET CARD
//CS...8
//SI...11
//SO...12
//SCK..13
//VCC...3.3v
//GND...GND
// Conexion del sensor DS18B20:
// Se ha elegido el modo de alimentación parásita que consiste en conectar VCC (+3,3v) y GND
//juntos y unir DATA a +3,3v con una resistencia de 4K7 Ohm.Este modo permite conectar los
//tres polos a un conector GND-DATA-VCC actuando como desequivocador, ya que DATA nunca cambia
//de posicion central.
// arduino +3.3v-----R 4K7Ohm----DATA------Pin Digital 2 Arduino
// ****************************DS18B20***************************
// Arduino GND------------------VCC-GND
#include <EtherCard.h>
#include <stdio.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(9, 10, 4, 5, 6, 7);
float tempmin = 2; //Variable donde guardamos la temperatura minima registrada
float tempmax = 8; //Varibale donde guardamos la temperatura maxima registrada
float teston = 0; //Variable del testeo de DNS ON
float teston1 = 0; //Variable de presentacion testeo OK
float temp01; //Varibale donde guardamos la temperatura actual sensor 01
float temp02; //Varibale donde guardamos la temperatura actual sensor 02
float temp03; //Varibale donde guardamos la temperatura actual sensor 02
//Timers
unsigned long tAntes = 0;
unsigned long tAntes1 = 0;
unsigned long tAntes2 = 0;
unsigned long tAntes3 = 0;
unsigned long tAntes4 = 120000;// Timer 4 minutos estabilizacion sensores
long time_now = 0;
// change these settings to match your own setup (son de mi cuenta Xively)
#define FEED "PONER AQUI VALOR DE FEED"
#define APIKEY "PONER AQUI VALOR APIKEY"
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// ethernet interface mac address, must be unique on the LAN
#define STATIC 1 // set to 1 to disable DHCP (adjust myip/gwip values below)
#if STATIC
// mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
// ethernet interface ip address
static byte myip[] = { 192,168,1,32 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };
// mask address
static byte mymask[] = { 255,255,255,0 };
// dnsip address
static byte dnsip[] = { 10,44,99,225 };
#endif
char website[] PROGMEM = "api.xively.com";
#define BUFFER_SIZE 500 //antes 400
byte Ethernet::buffer[BUFFER_SIZE];
BufferFiller bfill;
Stash stash;
void setup () {
lcd.begin(16, 2); // start the library
lcd.setCursor(0,0);
lcd.print("Starting...."); // print a simple message
lcd.setCursor(0,1);
lcd.print("Max/Min delay"); //Warning about 4 min delay in Max/Min
delay(2000);
// Start up the library
sensors.begin();
Serial.begin(38400);
Serial.println("Trying to get an IP...");
Serial.print("MAC: ");
for (byte i = 0; i < 6; ++i) {
Serial.print(mymac[i], HEX);
if (i < 5)
Serial.print(':');
}
Serial.println("\n[webClient]");
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Ethernet Start"); // print a simple message
delay(2000);
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
{
Serial.println( "Failed to access Ethernet controller");
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Ethernet Start");
lcd.setCursor(0,1);
lcd.print("Ethernet Error");
} else {
Serial.println( "Ethernet controller OK");
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Ethernet Start");
lcd.setCursor(0,1);
lcd.print("Ethernet OK");
}
delay(2000);
#if STATIC
Serial.println( "Getting static IP.");
if (!ether.staticSetup(myip, gwip)){
Serial.println( "could not get a static IP");
}
#else
Serial.println("Setting up DHCP");
if (!ether.dhcpSetup()){
Serial.println( "DHCP failed");
blinkLed(); // blink forever to indicate a problem
}
#endif
ether.printIp("IP: ", ether.myip);
ether.printIp("GW: ", ether.gwip);
ether.printIp("DNS: ", ether.dnsip);
ether.printIp("SRV: ", ether.hisip);
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Wait for DNS"); // print a simple message
if (!ether.dnsLookup(website)) {
Serial.println("DNS failed");
//lcd.begin(16, 2);
lcd.setCursor(0,1);
lcd.print("DNS Error");
//teston = 0;
} else {
Serial.println("DNS OK");
//lcd.begin(16, 2);
lcd.setCursor(0,1);
lcd.print("DNS OK");
}
delay(2000);
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("1 minute to"); //Inicio loop de publicacion en Xively
lcd.setCursor(0,1);
lcd.print("Start Publish.."); //Inicio loop de publicacion en Xively
delay(2000);
}
void loop () {
ether.packetLoop(ether.packetReceive());
long time_now = millis();
if ((long)(time_now - tAntes) > 30000) // regulador de tiempo medida datos y presentacion lcd
{
tAntes = time_now;
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print(" Requesting temperatures...\n");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
float temp01 = sensors.getTempCByIndex(0);// Why "byIndex"?
// You can have more than one IC on the same bus.
// 0 refers to the first IC on the wire
float temp02 = sensors.getTempCByIndex(1);
float temp03 = sensors.getTempCByIndex(2);
teston1 = teston / 4;// Convertir pasos de 15 min en 0.25 LCD
// Comparador temperaturas para saber Max y Min
if (temp01<tempmin)
tempmin=temp01;
if (temp01>tempmax)
tempmax=temp01;
//Timer para permitir estabilizarse la temperatura en el lugar donde colocar la sonda, para evitar
// falsos maximos y minimos
if ((long)(tAntes4 - time_now) > 1) // regulador de tiempo medida datos y presentacion lcd
{
tempmin=temp01;
tempmax=tempmin;
}
//Presentador datos terminal serie
Serial.print("Sensor 1: ");
Serial.print(temp01);
Serial.print("Min Sensor1: ");
Serial.print(tempmin);
Serial.print("Max Sensor1: ");
Serial.print(tempmax);
Serial.print("Sensor 2: ");
Serial.print(temp02);
Serial.print("Sensor 3: ");
Serial.print(temp03);
Serial.print("Test ON: ");
Serial.print(teston1);
Serial.print("\n");
// FIN SERIE
//Escribe datos temperatura en el LCD
if ((long)(time_now - tAntes2) > 10000) // regulador de tiempo presentacion lcd
{
tAntes2 = time_now;
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T.Sens2");
lcd.setCursor(10,0);
lcd.print(temp02);
lcd.setCursor(0,1);
lcd.print("T.Sens3");
lcd.setCursor(10,1);
lcd.print(temp03);
delay(4000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T.Ac.");
lcd.setCursor(5,0);
lcd.print(temp01);
lcd.setCursor(12,0);
lcd.print(teston1);
lcd.setCursor(0,1);
lcd.print("Min");
lcd.setCursor(3,1);
lcd.print(tempmin);
lcd.setCursor(8,1);
lcd.print("Max");
lcd.setCursor(11,1);
lcd.print(tempmax);
delay(4000);
}
// Reseteador temparaturas cada 24 horas
if (teston > 96){
tempmax=temp01;
tempmin=temp01;
teston = 0;
}
//Envio de datos a Xively
if ((long)(time_now - tAntes1) > 30000) // regulador de tiempo envio datos
{
tAntes1 = time_now;
// generate two fake values as payload - by using a separate stash,
// we can determine the size of the generated message ahead of time
byte sd = stash.create();
stash.print("Sensor_1_T_Max,");
stash.println(tempmax);
stash.print("Sensor_1_T_Min,");
stash.println(tempmin);
stash.print("Sensor_1_Actual,");
stash.println(temp01);
stash.print("Sensor_2_Actual,");
stash.println(temp02);
stash.print("Sensor_3_Actual,");
stash.println(temp03);
stash.print("Test_ON,");
stash.println(teston1);
stash.save();
// generate the header with payload - note that the stash size is used,
// and that a "stash descriptor" is passed in as argument using "$H"
Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n"
"Host: $F" "\r\n"
"X-PachubeApiKey: $F" "\r\n"
"Content-Length: $D" "\r\n"
"\r\n"
"$H"),
website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);
// send the packet - this also releases all stash buffers once done
ether.tcpSend();
}
// Testeador conexion etherner usando dnslookup
if ((long)(time_now - tAntes3) > 900000) // regulador de tiempo testeo 15 min.
{
tAntes3 = time_now;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Wait for DNS");
if (!ether.dnsLookup(website)) {
Serial.println("DNS failed");
lcd.setCursor(0,1);
lcd.print("Error");
teston = 0;
} else {
lcd.setCursor(0,1);
lcd.print("OK");
teston = teston + 1;//Indicador creciente testeo posiivo
}
}
}
}
*********************fin de programa - no copiar esto**************************
- Funcionamiento:
Tras encender el Arduino, veras que empieza a enviar datos de temperaturas. Si te conectas a la web de Xively y vas a "Develop", veras que aparece en "Development Devices" el que diste de alta anteriormente, si pulsas sobre el, accederás a las temperaturas que aparecerán como : Frigo_1, T_Max y T_Min. En la línea de la pantalla de datos donde pone "Channels", puedes escoger el modo de ver dichos datos, como un dato numérico, o como una gráfica de datos en el tiempo, junto con el valor actual, esto lo haces pulsando "Graphs". Verás que en cada gráfica puedes escoger el intervalo de tiempo de recogida de datos, pulsando en el selector de la base de cada gráfica. Si modificas en el programa de Arduino anterior los nombres de los datos (Frigo_1 por Salon, p.e.), aparecerá este nombre como una gráfica nueva. Si quieres reiniciar los datos o borrar gráficas con nombres antiguos, dale a "Delete" (dibujo de cubo de basura), tras esto, automáticamente se volverán a cargar las gráficas descritas en el programa Arduino.
- Adenda:
Este sistema nos permite controlar la temperatura de nuestro frigorífico, p.e., y nos permitirá darnos cuenta de si se ha averiado estando de vacaciones, p.e., pero no funcionará si se va la corriente de la casa, ya que el router e internet no funcionarán. Para solucionar esto, quizás sería interesante usar un módulo GRPS/3G para Arduino, junto con un sistema de SAI de energía para Arduino.El sistema SAI para Arduíno es bastante económico, ya que consiste en un "libro" de baterías recargables, que encuentras por internet por menos de 10 euros. El módulo de datos por GPRS/3G ya es algo más caro, pues implica una tarifa de datos sólo para el sistema mas el coste elevado del módulo. Existe la posibilidad de generar programas que lean los datos almacenados en Xively, de la misma manera que lo hace su página Web, incluso explotando esos datos, creando secuencias de alarma o aviso para unos determinados valores o su ausencia de actualización. Esto se escapa aún de mis conocimientos, pero espero que alguien lo pueda desarrollar, p.e. una applet para Android con un Widget de temperatura actual y una alarma de aviso configurable.
- Construcción del hardware:
Las conexiones del módulo ENC28J60 a arduino son las siguientes:
CS...Pin digital 8
SI...Pin digital 11
SO...Pin digital 12
SCK..Pin digital 13
VCC.... 3.3v
GND
La conexión del sensor de temperatura DS18B20 se hace colocando primero una resistencia de 4K7 Ohm uniendo el polo de datos al VCC (3.3v) como muestra el dibujo:
GND y VCC a GND de Arduino y DATA a Pin digital 2.
Ya tenemos construido nuestro hardware.
- Dar de alta el servidor de datos de Xively (antes llamado Pachube):
Entramos en Xyvely.com y nos damos de alta. Vamos a "Develop" y Pulsamos "+ Add a Device". Le damos un Nombre, una Descripción , pulsamos en la selección de "Public Device", una vez hecho esto, damos a "Save Changes". Una vez hecho esto aparecerá en la parte inferior de la pagina Web unos datos importantes para poder configurar el programa que hará funcionar nuestro recolector de temperaturas, estos son:
- Free ID
- API Key
Las copiamos y las guardamos para usarlas luego.
- Programa sketch para Arduino:
Sustituye Free ID y Api Key por los valores que guardaste al dar de alta tu cuenta Xively. Copia el programa siguiente, y pégalo en tu pantalla de Arduino Uploader. Comprueba que tienes las bibliotecas requeridas instaladas. Comprueba que tienes una IP libre (cambia la 192.168.1.32, por la que tu tengas) y que la puerta de enlace (dirección IP de tu router) es la adecuada (sustituye 192.168.1.1 por la adecuada).
*********************Inicio de programa - no copiar esto**************************
// Sistema de control de temperaturas usando modulo ethernet ENC28J60 y sensor DS18B20
// 2011-07-08 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
//PIN CONNECTION FOR ENC28J60 ETHERNET CARD
//CS...8
//SI...11
//SO...12
//SCK..13
//VCC...3.3v
//GND...GND
// Conexion del sensor DS18B20:
// Se ha elegido el modo de alimentación parásita que consiste en conectar VCC (+3,3v) y GND
//juntos y unir DATA a +3,3v con una resistencia de 4K7 Ohm.Este modo permite conectar los
//tres polos a un conector GND-DATA-VCC actuando como desequivocador, ya que DATA nunca cambia
//de posicion central.
// arduino +3.3v-----R 4K7Ohm----DATA------Pin Digital 2 Arduino
// ****************************DS18B20***************************
// Arduino GND------------------VCC-GND
#include <EtherCard.h>
#include <stdio.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(9, 10, 4, 5, 6, 7);
float tempmin = 2; //Variable donde guardamos la temperatura minima registrada
float tempmax = 8; //Varibale donde guardamos la temperatura maxima registrada
float teston = 0; //Variable del testeo de DNS ON
float teston1 = 0; //Variable de presentacion testeo OK
float temp01; //Varibale donde guardamos la temperatura actual sensor 01
float temp02; //Varibale donde guardamos la temperatura actual sensor 02
float temp03; //Varibale donde guardamos la temperatura actual sensor 02
//Timers
unsigned long tAntes = 0;
unsigned long tAntes1 = 0;
unsigned long tAntes2 = 0;
unsigned long tAntes3 = 0;
unsigned long tAntes4 = 120000;// Timer 4 minutos estabilizacion sensores
long time_now = 0;
// change these settings to match your own setup (son de mi cuenta Xively)
#define FEED "PONER AQUI VALOR DE FEED"
#define APIKEY "PONER AQUI VALOR APIKEY"
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// ethernet interface mac address, must be unique on the LAN
#define STATIC 1 // set to 1 to disable DHCP (adjust myip/gwip values below)
#if STATIC
// mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
// ethernet interface ip address
static byte myip[] = { 192,168,1,32 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };
// mask address
static byte mymask[] = { 255,255,255,0 };
// dnsip address
static byte dnsip[] = { 10,44,99,225 };
#endif
char website[] PROGMEM = "api.xively.com";
#define BUFFER_SIZE 500 //antes 400
byte Ethernet::buffer[BUFFER_SIZE];
BufferFiller bfill;
Stash stash;
void setup () {
lcd.begin(16, 2); // start the library
lcd.setCursor(0,0);
lcd.print("Starting...."); // print a simple message
lcd.setCursor(0,1);
lcd.print("Max/Min delay"); //Warning about 4 min delay in Max/Min
delay(2000);
// Start up the library
sensors.begin();
Serial.begin(38400);
Serial.println("Trying to get an IP...");
Serial.print("MAC: ");
for (byte i = 0; i < 6; ++i) {
Serial.print(mymac[i], HEX);
if (i < 5)
Serial.print(':');
}
Serial.println("\n[webClient]");
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Ethernet Start"); // print a simple message
delay(2000);
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
{
Serial.println( "Failed to access Ethernet controller");
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Ethernet Start");
lcd.setCursor(0,1);
lcd.print("Ethernet Error");
} else {
Serial.println( "Ethernet controller OK");
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Ethernet Start");
lcd.setCursor(0,1);
lcd.print("Ethernet OK");
}
delay(2000);
#if STATIC
Serial.println( "Getting static IP.");
if (!ether.staticSetup(myip, gwip)){
Serial.println( "could not get a static IP");
}
#else
Serial.println("Setting up DHCP");
if (!ether.dhcpSetup()){
Serial.println( "DHCP failed");
blinkLed(); // blink forever to indicate a problem
}
#endif
ether.printIp("IP: ", ether.myip);
ether.printIp("GW: ", ether.gwip);
ether.printIp("DNS: ", ether.dnsip);
ether.printIp("SRV: ", ether.hisip);
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Wait for DNS"); // print a simple message
if (!ether.dnsLookup(website)) {
Serial.println("DNS failed");
//lcd.begin(16, 2);
lcd.setCursor(0,1);
lcd.print("DNS Error");
//teston = 0;
} else {
Serial.println("DNS OK");
//lcd.begin(16, 2);
lcd.setCursor(0,1);
lcd.print("DNS OK");
}
delay(2000);
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("1 minute to"); //Inicio loop de publicacion en Xively
lcd.setCursor(0,1);
lcd.print("Start Publish.."); //Inicio loop de publicacion en Xively
delay(2000);
}
void loop () {
ether.packetLoop(ether.packetReceive());
long time_now = millis();
if ((long)(time_now - tAntes) > 30000) // regulador de tiempo medida datos y presentacion lcd
{
tAntes = time_now;
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print(" Requesting temperatures...\n");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
float temp01 = sensors.getTempCByIndex(0);// Why "byIndex"?
// You can have more than one IC on the same bus.
// 0 refers to the first IC on the wire
float temp02 = sensors.getTempCByIndex(1);
float temp03 = sensors.getTempCByIndex(2);
teston1 = teston / 4;// Convertir pasos de 15 min en 0.25 LCD
// Comparador temperaturas para saber Max y Min
if (temp01<tempmin)
tempmin=temp01;
if (temp01>tempmax)
tempmax=temp01;
//Timer para permitir estabilizarse la temperatura en el lugar donde colocar la sonda, para evitar
// falsos maximos y minimos
if ((long)(tAntes4 - time_now) > 1) // regulador de tiempo medida datos y presentacion lcd
{
tempmin=temp01;
tempmax=tempmin;
}
//Presentador datos terminal serie
Serial.print("Sensor 1: ");
Serial.print(temp01);
Serial.print("Min Sensor1: ");
Serial.print(tempmin);
Serial.print("Max Sensor1: ");
Serial.print(tempmax);
Serial.print("Sensor 2: ");
Serial.print(temp02);
Serial.print("Sensor 3: ");
Serial.print(temp03);
Serial.print("Test ON: ");
Serial.print(teston1);
Serial.print("\n");
// FIN SERIE
//Escribe datos temperatura en el LCD
if ((long)(time_now - tAntes2) > 10000) // regulador de tiempo presentacion lcd
{
tAntes2 = time_now;
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T.Sens2");
lcd.setCursor(10,0);
lcd.print(temp02);
lcd.setCursor(0,1);
lcd.print("T.Sens3");
lcd.setCursor(10,1);
lcd.print(temp03);
delay(4000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T.Ac.");
lcd.setCursor(5,0);
lcd.print(temp01);
lcd.setCursor(12,0);
lcd.print(teston1);
lcd.setCursor(0,1);
lcd.print("Min");
lcd.setCursor(3,1);
lcd.print(tempmin);
lcd.setCursor(8,1);
lcd.print("Max");
lcd.setCursor(11,1);
lcd.print(tempmax);
delay(4000);
}
// Reseteador temparaturas cada 24 horas
if (teston > 96){
tempmax=temp01;
tempmin=temp01;
teston = 0;
}
//Envio de datos a Xively
if ((long)(time_now - tAntes1) > 30000) // regulador de tiempo envio datos
{
tAntes1 = time_now;
// generate two fake values as payload - by using a separate stash,
// we can determine the size of the generated message ahead of time
byte sd = stash.create();
stash.print("Sensor_1_T_Max,");
stash.println(tempmax);
stash.print("Sensor_1_T_Min,");
stash.println(tempmin);
stash.print("Sensor_1_Actual,");
stash.println(temp01);
stash.print("Sensor_2_Actual,");
stash.println(temp02);
stash.print("Sensor_3_Actual,");
stash.println(temp03);
stash.print("Test_ON,");
stash.println(teston1);
stash.save();
// generate the header with payload - note that the stash size is used,
// and that a "stash descriptor" is passed in as argument using "$H"
Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n"
"Host: $F" "\r\n"
"X-PachubeApiKey: $F" "\r\n"
"Content-Length: $D" "\r\n"
"\r\n"
"$H"),
website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd);
// send the packet - this also releases all stash buffers once done
ether.tcpSend();
}
// Testeador conexion etherner usando dnslookup
if ((long)(time_now - tAntes3) > 900000) // regulador de tiempo testeo 15 min.
{
tAntes3 = time_now;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Wait for DNS");
if (!ether.dnsLookup(website)) {
Serial.println("DNS failed");
lcd.setCursor(0,1);
lcd.print("Error");
teston = 0;
} else {
lcd.setCursor(0,1);
lcd.print("OK");
teston = teston + 1;//Indicador creciente testeo posiivo
}
}
}
}
*********************fin de programa - no copiar esto**************************
- Funcionamiento:
Tras encender el Arduino, veras que empieza a enviar datos de temperaturas. Si te conectas a la web de Xively y vas a "Develop", veras que aparece en "Development Devices" el que diste de alta anteriormente, si pulsas sobre el, accederás a las temperaturas que aparecerán como : Frigo_1, T_Max y T_Min. En la línea de la pantalla de datos donde pone "Channels", puedes escoger el modo de ver dichos datos, como un dato numérico, o como una gráfica de datos en el tiempo, junto con el valor actual, esto lo haces pulsando "Graphs". Verás que en cada gráfica puedes escoger el intervalo de tiempo de recogida de datos, pulsando en el selector de la base de cada gráfica. Si modificas en el programa de Arduino anterior los nombres de los datos (Frigo_1 por Salon, p.e.), aparecerá este nombre como una gráfica nueva. Si quieres reiniciar los datos o borrar gráficas con nombres antiguos, dale a "Delete" (dibujo de cubo de basura), tras esto, automáticamente se volverán a cargar las gráficas descritas en el programa Arduino.
- Adenda:
Este sistema nos permite controlar la temperatura de nuestro frigorífico, p.e., y nos permitirá darnos cuenta de si se ha averiado estando de vacaciones, p.e., pero no funcionará si se va la corriente de la casa, ya que el router e internet no funcionarán. Para solucionar esto, quizás sería interesante usar un módulo GRPS/3G para Arduino, junto con un sistema de SAI de energía para Arduino.El sistema SAI para Arduíno es bastante económico, ya que consiste en un "libro" de baterías recargables, que encuentras por internet por menos de 10 euros. El módulo de datos por GPRS/3G ya es algo más caro, pues implica una tarifa de datos sólo para el sistema mas el coste elevado del módulo. Existe la posibilidad de generar programas que lean los datos almacenados en Xively, de la misma manera que lo hace su página Web, incluso explotando esos datos, creando secuencias de alarma o aviso para unos determinados valores o su ausencia de actualización. Esto se escapa aún de mis conocimientos, pero espero que alguien lo pueda desarrollar, p.e. una applet para Android con un Widget de temperatura actual y una alarma de aviso configurable.
Suscribirse a:
Entradas (Atom)