Bagà. Omplir els dipòsits del rec
Contingut
Introducció
(TBD)
Desenvolupament
Hi ha 4 pins per a l'alimentació:
- 5V (els relés s'activen a 5V, s'agafa de la RPi. Es podria agafar de la font d'alimentació. La RPi s'alimenta de la font d'alimentació.)
- 3,3V. Les entrades GPIO van a 3,3V, i per tant l'alimentació dels sensors de nivell i de llum van a 3,3V
- 12V. És el secundari dels relés. La tensió ve d'una font d'alimentació de 12V.
- GND. És el comú de la RPi i de les fonts d'alimentació.
Sensor de nivell
El sensor de nivell és un interruptor, i per tant la manera de connectar-ho als pins GPIO és de la mateixa manera que amb les màquines Arcade.
______ _______
GPIO >----|__1K__|--+--|__10K__|----< +3V3
|
|
o |
|=#
o |
|
|
GND >--------------+
Sensor LDR de crepuscle
Per tal d'ajustar la sensibilitat de quan commuta, he de substituir la resistència de 1K per un potenciòmetre trimmer de 1K. A 1K per tal de què commuti quasi bé he de tapar la LDR. Si baixés a 500ohm no caldria tapar tant la LDR. És un divisor de tensió.
Raspberry i relés
El problema d'activar un relé amb un pin GPIO també interessa al projecte Interface_GPIO-relés_per_a_efectes_de_LEDs.
La idea és segui l'esquema de
El pin GPIO són 3.5V, i el coil primari del relé són 5V. Per tant, les dues senyals les agafo de la RPi. El secundari de la bobina seran 12V o 220V depenent del projecte.
Software
instal.lació de la llibreria wiringPi
$ sudo apt-get install git-core $ git clone git://git.drogon.net/wiringPi $ cd wiringPi $ ./build
Primera prova des de la línia de comandes a la RPi:
$ gpio mode 7 input $ gpio read 7 0 $ gpio read 7 1
$ mkdir rec_baga $ cd rec_baga
portàtil:
$ cd /home/joan/projectes/rec_baga $ scp rec_baga_v1.c pi@192.168.1.35:/home/pi/rec_baga
RPi:
$ gcc -o rec_baga -Wall -I/usr/local/include -L/usr/local/lib rec_baga_v1.c -lwiringPi $ sudo ./rec_baga
fitxer rec_baga_v1.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
#define SENSOR_NIVELL 7 //GPIO7, pin 7 del header
#define SENSOR_LDR 0 //GPIO0, pin 11 del header
#define RELE 2 //GPIO2, pin 13 del header
int estat_nivell;
int estat_ldr;
void setup (void)
{
if (geteuid () != 0)
{
fprintf (stderr, "prova1_gpio: Need to be root to run (sudo?)\n") ;
exit (0) ;
}
if (wiringPiSetup () == -1)
exit (1) ;
printf ("Setup ... ") ;
fflush (stdout) ;
//pinMode (0, OUTPUT) ;
//digitalWrite (0, 0) ;
pinMode (SENSOR_NIVELL, INPUT) ;
pinMode (SENSOR_LDR, INPUT) ;
pinMode (RELE, OUTPUT) ;
estat_nivell = digitalRead (SENSOR_NIVELL);
printf("Estat inicial del nivell: %d\n", estat_nivell);
estat_ldr = digitalRead (SENSOR_LDR);
printf("Estat inicial del LDR: %d\n", estat_ldr);
}
void waitCanviEstat (void)
{
if (digitalRead (SENSOR_NIVELL) != estat_nivell) {
estat_nivell = !estat_nivell;
printf("Estat del nivell: %d\n", estat_nivell);
digitalWrite (RELE, estat_nivell);
}
if (digitalRead (SENSOR_LDR) != estat_ldr) {
estat_ldr = !estat_ldr;
printf("Estat del LDR: %d\n", estat_ldr);
digitalWrite (RELE, estat_ldr);
}
delay(100);
}
int main (void)
{
setup () ;
for (;;)
{
waitCanviEstat () ;
}
}
fitxer rec_baga_v2_a.c:
// gcc -o rec_baga -Wall -I/usr/local/include -L/usr/local/lib rec_baga_v2_a.c -lwiringPi
// sudo ./rec_baga
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
//http://opensourceecology.org/w/images/e/e5/Rasp_pinout.png
#define SENSOR_NIVELL 7 //GPIO7, pin 7 del header
#define SENSOR_LDR 6 //pin22 //5 //pin 18 //0 //GPIO0, pin 11 del header
#define RELE1 2 //GPIO2, pin 13 del header. electrovalvula
#define RELE2 3 //GPIO3, pin 15 del header. electrovalvula
#define RELE3 4 //GPIO4, pin 16 del header. sensor LDR
int estat_nivell;
int estat_ldr;
void setup (void)
{
if (geteuid () != 0)
{
fprintf (stderr, "prova1_gpio: Need to be root to run (sudo?)\n") ;
exit (0) ;
}
if (wiringPiSetup () == -1)
exit (1) ;
printf ("Setup ... ") ;
fflush (stdout) ;
//pinMode (0, OUTPUT) ;
//digitalWrite (0, 0) ;
pinMode (SENSOR_NIVELL, INPUT);
pinMode (SENSOR_LDR, INPUT);
pinMode (RELE1, OUTPUT);
pinMode (RELE2, OUTPUT);
pinMode (RELE3, OUTPUT);
estat_nivell = digitalRead (SENSOR_NIVELL);
printf("Estat inicial del nivell: %d\n", estat_nivell);
estat_ldr = digitalRead (SENSOR_LDR);
printf("Estat inicial del LDR: %d", estat_ldr);
if (estat_ldr == 0) {
printf(" dia. apagar el vailet");
} else {
printf(" nit. encendre el vailet");
}
}
void waitCanviEstat (void)
{
if (digitalRead (SENSOR_NIVELL) != estat_nivell) {
estat_nivell = !estat_nivell;
printf("Estat del nivell: %d\n", estat_nivell);
if (estat_nivell == 0) {
digitalWrite (RELE1, !estat_nivell);
delay(1000);
digitalWrite (RELE1, estat_nivell);
} else {
digitalWrite (RELE2, estat_nivell);
delay(1000);
digitalWrite (RELE2, !estat_nivell);
}
}
if (digitalRead (SENSOR_LDR) != estat_ldr) {
estat_ldr = !estat_ldr;
printf("Estat del LDR: %d\n", estat_ldr);
if (estat_ldr == 0) {
printf(" dia. apagar el vailet");
} else {
printf(" nit. encendre el vailet");
}
digitalWrite (RELE3, estat_ldr);
}
delay(100);
}
int main (void)
{
setup () ;
for (;;)
{
waitCanviEstat () ;
}
}
versió 3
En la versiò 2 volia posar tot junt el sensor de llum/vailet i el sensor de nivell/electrovàlvula. La conseqüència és que hi ha molta incompatibiltat electromagnètica de l'alternador del vailet que ha generat molta inestabilitat en la RPi (model B) i al final s'han cascat els pints d'E/S.
Per tant, elimino tot el temap de sensor de llum/vailet i em centro amb el sensor de nivell per activar l'electrovàlvula. Ho faig amb una Raspberry Pi A+.
NOTA. Aquest projecte, tal com ha quedat, seria ideal per fer-ho amb un microcontrolador. És a dir, no caldria fer-lo amb la RPi. En definitiva és un interruptor que activen dos relés. També es pot fer més barato amb la plataforma que està sortint: C.H.I.P (https://www.kickstarter.com/projects/1598272670/chip-the-worlds-first-9-computer), que per 9$ tens el micro i tots els pins GPIO, i no cal xarxa ni HDMI...
En aquesta versió 3 he utilitzat dos relés DPDT (doble pol, double throw) que em pemeten tenir a la sortida els +12/-12V que buscava en la versió 2. Sense cap relé actiu la sortida en els bornes de l'electrovàlvula està flotant. Quan es tanca l'interruptor s'activa un relé apareix els +12V, i torno a obrir al cap d'un segon. Quan s'obre l'interruptor es tanca l'altre relé i apareixen els -12V, i es torna a obrir al cap d'un segon. Per tant, l'estat normal de l'electrovàlvula és que té els bornes flotant.
Per testejar els +/-12V amb un tester doncs ho faig directament amb el tester. Ara bé, el que no es pot fer és connectar un dels bornes de l'electrovàlvula al terra comú (!). Mirar l'esquema, els bornes de l'electrovàlvula han d'estar flotant.
NOTA. Una altra cosa que m'ha despistat és que fent proves amb la RPi és que en comptes d'utilitzar una font de 12V he utilitzat el pin 5V de la RPi (el pin 3 que també és de 12V). I en ficar el tester no m'apareixien els +/-12V.
La idea és que el circuit de la RPi i els sensors sigui TOTALMENT INDEPENDENT de la font d'alimentació de 12V.
rec_baga_v3.c
// gcc -o rec_baga -Wall -I/usr/local/include -L/usr/local/lib rec_baga_v3.c -lwiringPi
// sudo ./rec_baga
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
//raspberry pi A+
//http://pi4j.com/pins/model-a-plus.html
#define EV 1 //GPIO1, pin 12 del header
#define RELE1 0 //GPIO0, pin 11 del header. electrovalvula
#define RELE2 2 //GPIO2, pin 13 del header. electrovalvula
int estat_ev;
void setup (void)
{
if (geteuid () != 0)
{
fprintf (stderr, "prova1_gpio: Need to be root to run (sudo?)\n") ;
exit (0) ;
}
if (wiringPiSetup () == -1)
exit (1) ;
printf ("Setup ... ") ;
fflush (stdout) ;
pinMode (EV, INPUT);
pinMode (RELE1, OUTPUT);
pinMode (RELE2, OUTPUT);
estat_ev = digitalRead (EV); //normalment l'interruptor està obert, i el valor que es llegeix és un 1
printf("Estat inicial de l'electrovalvula: %d\n", estat_ev);
}
void waitCanviEstat (void)
{
if (digitalRead (EV) != estat_ev) {
estat_ev = !estat_ev;
printf("Estat de la electrovalvula: %d\n", estat_ev);
if (estat_ev == 0) {
printf("rele1\n");
digitalWrite (RELE1, !estat_ev);
delay(1000);
digitalWrite (RELE1, estat_ev);
delay(1000);
} else {
printf("rele2\n");
digitalWrite (RELE2, estat_ev);
delay(1000);
digitalWrite (RELE2, !estat_ev);
delay(1000);
}
}
delay(100);
}
int main (void)
{
setup () ;
for (;;)
{
waitCanviEstat () ;
}
}
Aprofitant aquest projecte també testejo l'interruptor del moneder (el del filferro), que funciona correctament. moneder_v3.c:
// gcc -o moneder -Wall -I/usr/local/include -L/usr/local/lib moneder_v3.c -lwiringPi
// sudo ./moneder
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
//raspberry pi A+
//http://pi4j.com/pins/model-a-plus.html
#define MONEDER 1 //GPIO1, pin 12 del header
#define RELE1 2 //GPIO0, pin 11 del header. electrovalvula
#define RELE2 3 //GPIO2, pin 13 del header. electrovalvula
int estat_moneder;
void setup (void)
{
if (geteuid () != 0)
{
fprintf (stderr, "prova1_gpio: Need to be root to run (sudo?)\n") ;
exit (0) ;
}
if (wiringPiSetup () == -1)
exit (1) ;
printf ("Setup ... ") ;
fflush (stdout) ;
pinMode (MONEDER, INPUT);
pinMode (RELE1, OUTPUT);
pinMode (RELE2, OUTPUT);
estat_moneder = digitalRead (MONEDER); //normalment l'interruptor està obert, i el valor que es llegeix és un 1
printf("Estat inicial del moneder: %d\n", estat_moneder);
}
void waitCanviEstat (void)
{
if (digitalRead (MONEDER) != estat_moneder) {
estat_moneder = !estat_moneder;
printf("Estat del moneder: %d\n", estat_moneder);
digitalWrite (RELE1, 1);
delay(1000);
digitalWrite (RELE1, 0);
delay(1000);
}
//delay(100);
}
int main (void)
{
setup () ;
for (;;)
{
waitCanviEstat () ;
}
}
Ara que ja tenim el sistema en funcionament, hem de millorar el software.
rec_baga_v6.c:
// gcc -o rec_baga -Wall -I/usr/local/include -L/usr/local/lib rec_baga_v6.c -lwiringPi
// sudo ./rec_baga
// funcionament de l'electrovàlvula: http://www.infojardin.com/foro/showthread.php?t=17559
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
//raspberry pi A+
//http://pi4j.com/pins/model-a-plus.html
#define EV 1 //GPIO1, pin 12 del header
#define RELE1 0 //GPIO0, pin 11 del header. electrovalvula
#define RELE2 2 //GPIO2, pin 13 del header. electrovalvula
int estat_ev;
int regant;
int esperant;
int esperant2;
int comptador;
int comptador2;
void setup (void)
{
if (geteuid () != 0)
{
fprintf (stderr, "prova1_gpio: Need to be root to run (sudo?)\n") ;
exit (0) ;
}
if (wiringPiSetup () == -1)
exit (1) ;
printf ("Setup ... ") ;
fflush (stdout) ;
pinMode (EV, INPUT);
pinMode (RELE1, OUTPUT);
pinMode (RELE2, OUTPUT);
estat_ev = digitalRead (EV); //normalment l'interruptor està obert, i el valor que es llegeix és un 1
printf("Estat inicial de l'electrovalvula: %d\n", estat_ev);
printf("en estat normal, boia a l'extrem, ha de valdre 0\n");
regant = 0;
esperant = 0;
comptador = 0;
comptador2 = 0;
}
//en la posició normal del sensor de nivell (sense aigua) l'interruptor està tancar (R=0) i el pin EV=0
void waitCanviEstat (void)
{
//arriba aigua al sensor de nivell, s'obre el sensor (R=inf) i s'ha d'obrir la vàlvula
if ( (digitalRead(EV)==1) && (regant==0) ) {
digitalWrite (RELE1, 1);
printf("rele1\n");
delay(5000);
digitalWrite (RELE1, 0);
delay(5000);
//i ara està passant aigua... Però això s'ha de comprovar tres vegades cada 5 min.
regant = 1;
esperant = 1;
esperant2 = 1; //estic regant
comptador = 0;
}
if ((regant == 1) && (esperant == 1)) {
if (comptador == 60) { //25 min: 60. test: 3
printf("hem arribat als 25min\n");
comptador = 0;
esperant = 0;
} else {
comptador++;
printf("rele1*\n");
digitalWrite (RELE1, 1);
delay(20000);
digitalWrite (RELE1, 0);
delay (5000);
}
}
if (esperant2 == 1) {
if (comptador2 <= 1440) { //4 hores: 1440. test: 15
delay (5000);
comptador2++;
} else { //s'ha omplert els dipòsits durant una hora i mitja, i ja podem tancar
printf("ja han passat 4 hores\n");
printf("rele2\n");
digitalWrite (RELE2, 1);
delay (5000);
digitalWrite (RELE2, 0);
delay (5000);
//ja no rego. Torno a començar el procés.
regant = 0;
esperant = 0;
esperant2 = 0;
comptador = 0;
comptador2 = 0;
}
}
delay (100);
}
int main (void)
{
setup () ;
for (;;)
{
waitCanviEstat () ;
}
}
En aquesta versió del hardware el vailet està totalment separat a nivell electrònic, però comparteixen la mateixa capsa. Sembla ser que l'alternador del vailet ha espatllat la RPi A+ (TBD). Més motiu per fer una nova versió de hardware feta amb microcontrolador, no pas a amb la RPi.
Versió 4 de hardware (ATMEGA168)
la versió 3 funcionava correctament. Ara bé, si he utilitzat la RPi (finalment una A+) és perquè últimament estava desenvolupant i programant la RPi i fent interfaces amb els pins GPIO. Però si ens posem a pensar amb perspectiva, és massa hardware per al projecte que es vol realitzar. El millor és utilitzar directament un microcontrolador com el ATMega168 que ja havia utilitzat. És millor econòmicament i com a solució tècnica. És al cap i a la fi com utilitzar un Arduino però sense l'arduino, fent una placa a mida de les necessitats, i gravant el software al microcontrolador). Ja havia fet un projecte amb el ATMega168 en la primera versió de la màquina de marcianitos, quan vaig fer una interface USB per fer un gamepad, i en aquell cas ja vaig documentar de com es grava el xip (sense el cable FTDI).
Al cap i a la fi en aquest projecte tenim un interruptor d'entrada i dos pins de sortida que han d'habilitar dos relés.
Fent un resum de diversos projectes basats en ATMega168 la configuració bàsica per fer començar a funcionar un petit projecte amb el ATMega168 (AVR): ATMega168 pin mapping:
- Entre Vcc i GND hi ha un electrolític de 0.1uF (hi ha d'altres possibilitats, com per exemple http://ba.protostack.com/2011/03/customer_clock_02_lrg.jpg). Una altra possibilitat és que sigui un ceràmic de 100nF. Però sembla ser que és important és que aquest condensador estigui a la vora dels pins Vcc i GND.
- AVcc va a Vcc. Aref pot anar a Vcc (https://petrockblog.files.wordpress.com/2012/07/wpid-photo-02-07-2012-0915.jpg)
- botó reset amb una resistència de 10K. Per veure com es connecta el reset, per exemple: http://s55.photobucket.com/user/icecreamterror/media/atmega8_schematiccopy-1.jpg.html. Si no es vol utilitzar un botó de reset el pin de reset ha d'anar connectat a Vcc a través d'una resistència com es veu a https://electrosome.com/wp-content/uploads/2013/05/Blinking-LED-using-Atmega32-AVR-Microcontroller-and-Atmel-Studio.jpg
- cristall de 16MHz i dos condensadors de 22-27-33pF
- La font d'alimentació ha de ser de 5V regulada. Moltes vegades es fa servir un 78L05. (Es pot connectar directament a un carregador de mòbil? És regulada?)
- Si en el pin de sortida vull connectar un led, s'ha de fer amb una resistència de 680 ohm connectada a terra (tal com es veu a https://electrosome.com/wp-content/uploads/2013/05/Blinking-LED-using-Atmega32-AVR-Microcontroller-and-Atmel-Studio.jpg)
- Per connectar un botó és tan fàcil com ficar el botó del pin a terra (doncs hi ha una resistència interna de pull-up que fa que el pin normalment està a 1 quan es configura el pin com a entrada) (tal com es mostra a: http://www.bristolwatch.com/arduino/img/arduino_ds1307.gif, http://d32zx1or0t1x0y.cloudfront.net/2009/08/atmega8_breadboard_24_lrg.jpg, http://ba.protostack.com/2011/03/customer_clock_02_lrg.jpg). Però també hi ha gent que fica resistències externes de pull-up i pull-down com faig amb els botons connectats a GPIO a la RPi (http://www.circuitstoday.com/wp-content/uploads/2012/02/Circuit1.jpg
- si vull un led que m'indiqui que el circuit està alimentat, fico un ressitència de 1K i un LED entre Vcc i GND (http://ba.protostack.com/2011/03/customer_clock_02_lrg.jpg)
Un cop tenim dissenyat en el Eagle CadSoft el citcuit bàsic del ATmel168 (que ens pot servir de plantilla), ara només cal fer la interfície de l'interruptor del sensor de nivell, i els dos relés. Per cercar esquemes de com es connecten al ATmel168, en el Google no he de cercar per Atmel168 sinó que buscaré per Arduino, i així obtinc molts més exemples.
El botó el connecto el puc connectar amb la configuració normalment a 1 (que utilitza la resistència interna de pull-up i no cal cap resistència externa):
o bé normalment a 0 (que utlitza una resistència externa):
En el cas de connectar un relé sí que hi ha una diferència important amb la RPi. El Atmel168 té en els pins de sortida 5V que poden drive directament el relé. Per tant, no cal l'amplificació dels transistor que feia amb els pins GPIO.
software
Un bon tutorial introductori:
En el projecte de la interface USB d'un gamepad ja vaig programar codi escrit amb C per als microcontroladors ATMEL, i tinc el programador. Amb la qual cosa ja tinc les eines i els coneixements per compilar, només cal repassar amb aquest tutorial els coneptes específics del microcontrolador (rellotge, etc).
Finalment ja sé fer pampallugues d'un LED controlat amb un switch:
- Atmel_ISP_MCU_programmer_ATmega._Programador_AVR_USBasp#Blink_LED
i ja puc migrar la v6 (software) basada en RPi A+, a la v7 (software) amb codi nadiu per a microcontrolador ATMega168.
rec_baga_v7.c:
/*
ATMEGA168
fuses:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U lfuse:w:0xE2:m -U hfuse:w:0xDF:m
avr-gcc -g -mmcu=atmega168 -c rec_baga_v7.c -Wa,-alh,-L -Os -o rec_baga_v7.o > rec_baga_v7.asm
avr-gcc -g -mmcu=atmega168 -Wl,-Map,rec_baga_v7.map -o rec_baga_v7.elf rec_baga_v7.o
avr-objdump -h -S rec_baga_v7.elf > rec_baga_v7.lst
avr-objcopy -j .text -j .data -O ihex rec_baga_v7.elf rec_baga_v7.hex
avr-size rec_baga_v7.elf
cat rec_baga_v7.hex
ja podem programar el xip:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U flash:w:rec_baga_v7.hex
// funcionament de l'electrovàlvula: http://www.infojardin.com/foro/showthread.php?t=17559
*/
#ifndef F_CPU
#define F_CPU 8000000UL //el rellotge intern va a 8MHz
#endif
#include <avr/io.h>
#include <util/delay.h>
int estat_ev;
int regant;
int esperant;
int esperant2;
int comptador;
int comptador2;
int main(void)
{
//setup del microcontrolador
DDRD &= ~(1<<PD2); //Interruptor sensor de nivell. Tercer pin del PORTD as Input
PORTD |= (1 << PD2); // Enable pull-up for switch on PORTD bit
DDRD |= (1<<PD3); //RELE1. Quart pin del PORTD as Output
DDRD |= (1<<PD4); //RELE2. Cinquè pin del PORTD as Output
//valors inicials
regant = 0;
esperant = 0;
comptador = 0;
comptador2 = 0;
delay(1000);
while(1) //infinite loop
{
//arriba aigua al sensor de nivell, s'obre el sensor (R=inf), i el pin valdrà 1. S'ha d'obrir la vàlvula
if ( ((PIND & (1<<PIND2)) == 1) && (regant==0) ) {
PORTD |= (1<<PD3); //RELE1 ON
delay(1000); //5000
PORTD &= ~(1<<PD3); //RELE1 OFF
delay(1000); //5000
//i ara està passant aigua... Però això s'ha de comprovar tres vegades cada 5 min.
regant = 1;
esperant = 1;
esperant2 = 1; //estic regant
comptador = 0;
}
if ((regant == 1) && (esperant == 1)) {
if (comptador == 3) { //25 min: 60. test: 3
//hem arribat als 25min
comptador = 0;
esperant = 0;
} else {
comptador++;
PORTD |= (1<<PD3); //RELE1 ON
delay(5000); //20000
PORTD &= ~(1<<PD3); //RELE1 OFF
delay(1000); //5000
}
}
if (esperant2 == 1) {
if (comptador2 <= 15) { //4 hores: 1440. test: 15
delay (1000); //5000
comptador2++;
} else { //s'ha omplert els dipòsits durant una hora i mitja, i ja podem tancar
//ja han passat 4 hores
PORTD |= (1<<PD4); //RELE2 ON
delay(1000); //5000
PORTD &= ~(1<<PD4); //RELE2 OFF
delay(1000); //5000
//ja no rego. Torno a començar el procés.
regant = 0;
esperant = 0;
esperant2 = 0;
comptador = 0;
comptador2 = 0;
}
}
delay (100);
}
}
creat per Joan Quintana Compte, febrer 2015, juny 2015