Cacharreando desde cero… (3)

…pero desde cero, cero.

El NodeMCU

Hace unos días empecé a escribir un larguísimo artículo sobre el NodeMCU, sus variantes y sus componentes, y lo acabo de borrar hace unos segundos, porque he pensado: «¿Y qué le importa todo eso a la gente que viene aquí a hacer algo de cacharreo desde cero?»

La respuesta correcta a esa pregunta es «mucho», pero me parece que vamos a centrarnos en hacer «cosas» y luego ya veremos por qué suceden esas «cosas».

Así que vamos a pasar al meollo de la cuestión. Para el lector de estas entregas, el NodeMCU, en principio, no tiene «partes», sino que es un chisme que se enchufa y hace algo. Vamos a suponer entonces que ya disponéis de vuestro NodeMCU, que me habéis hecho caso y os habéis comprado uno, y ahora lo tenéis sobre la mesa preguntándoos: «Bueno, ¿y ahora qué?»

La principal deducción que haréis al tenerlo delante es que se conecta a través de un puerto USB. ¡Correcto! Lo que tenéis delante, como buen sistema SoC (System on a Chip), es un ordenador completo y funcional que, de hecho, tiene más velocidad de proceso y cuatro veces más memoria que mi primer PC de sobremesa del año 1988 (aunque eso tampoco es que sea tan difícil). Por dar otro dato tonto, su memoria es unas 62 veces la del mítico Commodore 64, funcionando con una frecuencia de reloj veinte veces superior. En definitiva, una mandanga muy seria para los que crecimos en la edad dorada de los ocho bits.

Sin embargo, nuestro amigo aquí presente no dispone de pantalla, de teclado, ni impresora ni ningún otro medio que nos permita interactuar con él, así que nuestra forma de poder programarlo será a través de un ordenador «de verdad», de nuestro sobremesa o portátil. (Se puede hacer incluso a través de un teléfono móvil con Android, pero vamos a dejar las florituras para otro día, ¿vale?)

Una de las ventajas de los modelos de microcontroladores basados en el ESP8266 es que se pueden programar en C/C++ con el clásico IDE de Arduino y…

-¡Oiga, espere usted! ¿Cómo dice?

-Uy, perdone, ya no me acordaba de lo de «desde cero, cero».

Los ordenadores, los microcontroladores y cualquier cosa que contenga un microprocesador deben ser programados de alguna manera para que hagan lo que nosotros queremos, porque cuando vienen de la fábrica contienen o bien una memoria vacía, o bien un programa pregrabado para verificar que el aparato funciona, y ninguna de esas cosas nos es de utilidad. Nosotros vamos a meter nuestro propio programa en esa pastilla que tenemos sobre la mesa, y lo vamos a hacer paso a paso.

El fabricante de estos controladores, Espressif, se dijo en su día: «¿Por qué no hacemos que nuestros controladores se puedan programar con el entorno de trabajo que han popularizado los chicos de Arduino, y así los venderemos mejor?» Y, efectivamente, eso hicieron. Cuando los chicos de Arduino diseñaron su entorno de desarrollo (que luego veremos), lo hicieron de una forma modular que permitiera trabajar con diferentes controladores, y eso facilitó mucho la tarea. Hoy en día se puede configurar el entorno de desarrollo de Arduino para una multitud de dispositivos, y el aficionado o el profesional puede tener un completo banco de trabajo para múltiples dispositivos en un solo entorno.

El IDE de Arduino

Bueno, vamos a meternos en harina y vamos a instalar el famoso IDE de Arduino. «IDE» significa, en sus siglas en inglés, «Integrated Development Environment», uséase, «Entorno de Desarrollo Integrado», así que supuestamente estamos hablando de un entorno de trabajo que nos permite desarrollar programas sin tener que depender de nada externo. Nosotros utilizaremos la versión actual del IDE de Arduino (la 1.8.15), que se puede obtener en la misma página de Arduino. Hay versiones para Linux, Windows y Mac OS-X. Aunque yo voy a trabajar sobre la versión de Windows, las otras son muy semejantes, con alguna pequeña diferencia.

IDE de Arduino

Al instalar el IDE de Arduino, éste no viene preparado para trabajar con nuestro NodeMCU. Afortunadamente, el proceso para configurarlo es muy sencillo:

Buscamos en el menú superior «Archivo-Preferencias» y pulsamos para que muestre la pantalla de preferencias:

En «Gestor de URLs Adicionales de Tarjetas» debemos introducir la siguiente línea:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Y luego pulsar Ok para validar el cambio.

Después de esto debemos cerrar el IDE de Arduino y volver a iniciarlo para que los cambios tengan efecto. Ahora ya disponemos en el IDE de todas las placas basadas en el ESP8266, entre ellas nuestro amigo el NodeMCU:

Debemos seleccionar el modelo «NodeMCU 1.0 (ESP-12E Module)», que se corresponde con el modelo NodeMCU v3 que hemos comprado.

Después de esto, y sin más preámbulos, nuestro ordenador estará preparado para escribir, compilar y subir al NodeMCU nuestro primer programa «maker», cosa que veremos a continuación.

Hasta la próxima entrega, amigos.

Cacharreando desde cero… (2)

…pero desde cero, cero.

¡Vámonos de compras!

Hola de nuevo, parvulitos del cacharreo. Para empezar a cacharrear lo primero que necesitamos, lógicamente, es un cacharro, y para eso pueden ocurrir varias cosas: que ya lo tengas, que te lo presten, que lo robes o, en el peor de los casos, que tengas que comprarlo.

Si por desgracia te encuentras en el último caso, relájate y respira: mi intención es que para seguir estos tutoriales no haya que gastarse más de 10€ cada quince días, euro arriba, euro abajo.

En fin, empecemos: para mi próximo experimento necesito un NodeMCU y un cable micro USB como esos que llevan los móviles. Y nada más. Sólo con eso ya podemos empezar a conocer nuestra principal herramienta de cacharreo y trabajar con ella. Voy a dar por hecho que tenemos un cable micro USB de algún móvil, aunque ¡ojo!, algunos de estos cables sólo traen el cableado para cargar, y nosotros necesitamos un cable que nos sirva para transmitir datos. Los que vienen con los móviles son todos de datos, pero hay otros, como los que vienen con auriculares, lamparitas recargables, etc, que podrían no servirnos. Cuando llegue el momento lo averiguaremos rápidamente, porque con el cable chungo no podremos programar el NodeMCU.

En una ardua tarea de periodismo de himbestigación que me llevó por lo menos cinco minutos pude encontrar varios sitios donde comprar nuestro NodeMCU a precios bastante interesantes. En primer lugar voy a mostraros qué es lo que andamos buscando y cómo se llama, por si queréis buscarlo por vuestra cuenta, qué narices.

NodeMCU V3

Esto de arriba es el NodeMCU, que como conté en la entrada anterior integra varios elementos como el procesador ESP8266, la memoria de 4MB, la antena WiFi, el puerto micro USB y algunas otras cosas que más adelante veremos. ESTO ES IMPRESCINDIBLE. SIN ESTO NO PODEMOS EMPEZAR, ¿QUEDA CLARO?

***

Base de desarrollo para NodeMCU

Esto otro es la base de desarrollo, que aunque no sea imprescindible, cuenta con algunos elementos muy, pero que muy interesantes para poder trabajar con el NodeMCU. Como podéis deducir, el NodeMCU se coloca pinchado sobre el zócalo hembra de esta base de desarrollo, y su potencial se encuentra en el alimentador que se ve abajo a la izquierda, que admite voltajes entre 5 y 12V, así que se le puede enchufar casi cualquier alimentador que tengamos en casa (aunque yo miraría antes el voltaje de salida del alimentador, y me aseguraría que fuera de corriente continua, si no queréis tener una desagradable sorpresa).

La base de desarrollo tiene además una serie de pines a la izquierda que ofrecen distintos voltajes de salida (el voltaje del alimentador, el voltaje del puerto USB y 5V convertidos desde la salida del alimentador. En fin, no quiero que os mareéis con esto ahora, pero todos esos voltajes nos van a ser de mucha utilidad el día de mañana.

A un lado y otro de los zócalos hembra del NodeMCU tenemos los pines de salida del mismo para poder pinchar nuestros cables y conectar cosas a nuestro cacharro. Los pines de la derecha, que son los más usados para controlar aparatos, vienen además multiplados en cuatro salidas cada uno, lo que nos va a permitir aumentar muchísimo la capacidad de nuestro equipo. Y arriba a la derecha, además, tenemos una serie de pines de salida para voltajes de 3,3V.

Por todo lo anterior, recomiendo encarecidamente que incluyáis esta base de desarrollo a vuestra lista de la compra, aunque, como ya he dicho, no va a ser indispensable de momento. Y sin más preámbulos, paso a poneros los enlaces donde podéis adquirirlos:

Aliexpress – 4,98€

ebay – 4,78€

Amazon – 12,79€

Los chicos de Bezos tienen la ventaja de que te lo ponen en tu puerta al día siguiente, mientras que los otros parece que los trae Marco Polo andando por la ruta de la seda, pero como se puede ver, la diferencia de precios es más que obvia.

Lógicamente, sois muy libres de buscaros la vida por vuestra cuenta, porque hay muchos otros sitios donde se puede comprar, y estos enlaces están sólo para facilitar la vida de quieres no se quieran complicar buscando.

Nuestra próxima entrada, dentro de algunos días, estará dedicada íntegramente al NodeMCU, y veremos cómo conectarlo a un ordenador y programarlo para que haga «algo».

Hasta pronto.

Cacharreando desde cero…

…pero desde cero, cero.

Voy a empezar diciendo que el título de esta serie de artículos es bastante engañoso. Mi idea original es que cualquiera, pero cualquiera, sea capaz de utilizar un microcontrolador y algunos módulos de sensores o actuadores para construir un aparato «que haga algo».

Pero ahí está la trampa: para empezar «desde cero, cero», primero hay que saber con qué vamos a trabajar, aprender algunos conceptos y tener claro lo que queremos conseguir. Esto se puede conseguir, bien por la vía «reglada», de abajo hacia arriba, primero la teoría y después ya veremos, o bien llevando a cabo pequeñas hazañas maker siguiendo las recetas que se vayan proponiendo y aprendiendo mientras tanto el cómo y el por qué. Aquí pretendo seguir el segundo método.

Lógicamente, si lo que quieres es adquirir conocimientos sobre informática, electrónica o ingeniería, huye de aquí cuanto antes, porque no sólo no vas a encontrar esos conocimientos, sino que es posible que estos artículos te ensucien la mente y te hagan adquirir vicios que los que de verdad saben podrían afearte el día de mañana. Aquí venimos a hacer cacharreo, a montar cacharritos y, a lo mejor, a aprender POR NUESTRA CUENTA algunas cosas interesantes. Y ahora, un poquito de historia, porque todas las cosas vienen de algún sitio, y esto del cacharreo no va a ser menos:

Cacharrear con componentes electrónicos es algo que se ha hecho desde siempre, aunque hasta entrados los años setenta componentes básicos como los transistores eran demasiado caros y difíciles de conseguir como para estar al alcance de cualquiera. A finales de los setenta, jóvenes talentosos que cacharreaban con transistores y microchips crearon cosas tan famosas como el Apple I, revolucionando el mundo de la informática. Andando los años, Internet supuso otra revolución en lo que se refiere al intercambio de información entre aficionados, que ya no tenían por qué reunirse en un sitio concreto y podían estar en cualquier rincón del mundo.

Y en 2005, un grupo de estudiantes de ingeniería electrónica de la ciudad italiana de Ivrea se reunieron en un bar a poner negro sobre blanco lo que debería ser la base de desarrollo ideal para crear dispositivos electrónicos controlados por microprocesador, y de allí, de una mesa de un bar de Ivrea, salió el primer prototipo de Arduino. En los siguientes años, Arduino se iba a convertir no sólo en la plataforma favorita de los makers, sino en todo un estándar que muchos fabricantes seguirían.

Arduino UNO

De hecho, y aunque siga siendo una placa muy práctica y versátil para el cacharreo (ya explicaré por qué), Arduino UNO ha sido superada ampliamente por otros diseños con más velocidad de proceso, más memoria y mejor conectividad. Hay en especial una expresión que lo ha cambiado todo y ha relegado a la venerable UNO a simple juguete de iniciación, y esa expresión es: «IoT».

IoT, el Internet of Things, el Internet de las cosas, la actual necesidad de que todo, pero absolutamente todo, esté conectado a Internet, de que tengamos sensores, actuadores y cámaras hasta en el cu… en la sopa, exige que nuestros aparatitos dispongan de algún medio para enviar y recibir datos por internet. Aunque Arduino lo intentó con algunos dispositivos muy interesantes como la MKR-1000, no pudo competir con el procesador ESP8266 de la marca china Espressif. Voy a poneros una comparativa entre el ESP8266 y el procesador de la Arduino UNO, para que os hagáis una idea del salto tecnológico que representa el primero respecto del segundo:

__________________________Arduino UNO__________________ESP8266

Procesador_______________ATmega16U2__________________ESP8266

Bits____________________________________8________________________32

Frecuencia______________________16MHz____________________80MHz

Memoria Flash____________________32KB_______________hasta 16MB

Conectividad________________________No_________Wifi 802.11 b/g/n

Como se puede ver, esto es comparar un seiscientos con un ferrari, aunque también esta comparación es un poco tramposa, ya que la memoria flash del ESP8266 es externa, pero eso es algo que no nos va a importar lo más mínimo, porque tendremos nuestro ESP8266 integrado en una placa llamada «NodeMCU» (Node Micro-Controller Unit) que incluye procesador, memoria, puerto serie micro USB, antena WiFi, pines de entrada-salida… en definitiva, todo lo que un maker puede necesitar para trabajar con él.

Para los que a estas alturas no hayáis metido la cabeza en el horno, en el próximo artículo describiré los materiales necesarios para empezar a cacharrear desde cero, pero desde cero, cero, y daré enlaces para adquirirlos según plataformas y precios. Hasta pronto.

Bot de Mastodon para NodeMCU

Vale, tengo que reconocer que el título de este post es engañoso, ya que, técnicamente, no he construido ningún bot nativo para NodeMCU. Mi «invento» consiste en crear un pequeño servidor web en NodeMCU (o cualquier otro desarrollo con ESP8266) y que un bot programado en Python y alojado en un ordenador dentro de la misma red local (en este caso mi sufrida Raspberry Pi 3) haga de puente entre Mastodon y el NodeMCU.

Así que empecemos por el principio. Y el principio es tener algo con lo que trabajar, en este caso, un banco de pruebas compuesto por un NodeMCU, display, keypad, una batería de 4 relés y un motor servo. Es sorprendente la cantidad de cosas que podemos controlar con un simple NodeMCU, pero gracias al bus I2C el trabajo se simplifica mucho. El esquema general sería parecido a esto:

Desgraciadamente no he encontrado el módulo I2C del keypad en Fritzing, así que he utilizado un keypad que ya viene con las salidas I2C, pero adjuntaré fotografías del montaje real para que veáis cómo queda.

En este esquema también he simplificado las vías de alimentación, que en el montaje real van a los diferentes conectores de la base del NodeMCU, como podréis ver también en las fotografías.

Puesto que en anteriores posts expliqué cómo configurar el IDE de Arduino para trabajar con NodeMCU y cómo instalar librerías, no voy a extenderme en ese punto. El código para el controlador es el siguiente:

#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include "I2CKeyPad.h"
#include "Servo.h"

const uint8_t KEYPAD_ADDRESS = 0x20;
I2CKeyPad keyPad;
uint32_t start, stop;
uint32_t lastKeyPressed = 0;
char keys[] = "123A456B789C*0#DNF";  // N = Nokey, F = Fail

int rele[]={2,14,12,13};
String estadoRele[]={"OFF","OFF","OFF","OFF"};
int l;
String st;

int servo_pin=15;
Servo myservo;
int angle = 0;

// Conexión wifi y servidor web
const char* ssid     = "TU SSID";
const char* password = "TU PASSWORD";
IPAddress ip(192,168,1,205);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
ESP8266WebServer server(8081);

int reles=0;

// Display LCD
int lcdColumns = 16;
int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

void mostrarDatos() {
  lcd.setCursor(0,0);
  lcd.print("Con. reles:"+(String)reles+"   ");
  lcd.setCursor(0,1);
  lcd.print("Ang. Servo:"+(String)angle+"   ");
}

void getServo() {
  angle=server.arg(String("Angulo")).toInt();
  setServo(angle);
  server.send(200, "text/plain", "Ok");
}

void setServo(int angulo) {
  myservo.write(angulo);
  angle=angulo;
}

void ayuda() {
  server.send(200, "text/plain", "\n\nHispaBot_NodeMCU:\n\nEste bot se encuentra en @HispaBot_NodeMCU@botsin.space\n\nComandos:\n*rele [0-3] [on-off] - Enciende o apaga relés\n*servo [angulo] - Mueve el servo al angulo indicado\n*barrido - Hace un barrido completo del servo hasta su posición actual\n*ayuda - Muestra este mensaje\n*status - Muestra el estado actual");
}

void barrido() {
  int anguloinicial=angle;
  for (int i=angle; i<=180; i++) {
    setServo(i);
    delay(5);
  }
  for (int i=180; i>=0; i--) {
    setServo(i);
    delay(5);
  }
  for (int i=0; i<=anguloinicial; i++) {
    setServo(i);
    delay(5);
  }
  server.send(200, "text/plain", "Ok");
}

void devolverStatus() {
  String st="\n\nHispaBot_NodeMCU:\n\nEstado:\n\n";
  for (int i=0; i<4; i++)
    st=st+"*Relé "+(String)i+": "+estadoRele[i]+"\n";
  st=st+"\n\n*Ángulo del servo: "+(String)angle;
  server.send(200, "text/plain", st);
}

// Funcion al recibir petición GET
void setRele() 
{
  l=server.arg(String("Id")).toInt();
  st=server.arg(String("Status"));
  Serial.println("Led:"+(String)l+" - Status:"+st);
  if (st=="on") {
    digitalWrite(rele[l],LOW);
    estadoRele[l]="ON";
  }
  else {
    digitalWrite(rele[l],HIGH);
    estadoRele[l]="OFF";
  }
  reles++;
  server.send(200, "text/plain", "Ok");
}

char CaptarTecla() {
  char c=0;
  if (keyPad.isPressed()) {
    c=(char)"123A456B789C*0#DNF"[keyPad.getKey()];
    while (keyPad.isPressed());
  }
  return c;
}

void examinarTeclado() {
  String s="";
  String c=(String)CaptarTecla();
  if (c=="*") {
    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print("ORDEN:");
    s=c;
    while (c!="#" && s.length()<6) {
      c=CaptarTecla();
      if (c!=0) {
        s=s+(String)c;
        lcd.setCursor(7,0);
        lcd.print(s);
      }
      delay(100);
    }
    lcd.setCursor(1,1);
    if (s[s.length()-1]!='#' || (s[1]=='A' && s.length()<4) || (s[1]=='B' && s.length()<5)) {
      lcd.print("ERROR");
      delay(1000);
    }
    else {
      lcd.print("EJECUTANDO...");
      delay(1000);
      EjecutarOrden(s.substring(1,s.length()-1));
    }
  }
}

void EjecutarOrden(String s) {
  int a;
  int rel;
  char estado;
  switch (s[0]) {
    case 'A':
      a=s.substring(1).toInt();
      if (a>=0 && a<=180)
        setServo(a);
      break;
    case 'B':
      rel=s.substring(1,s.length()-1).toInt();
      estado=s[s.length()-1];
      if (rel>=0 && rel<=3) {
        if (estado=='A') {
          digitalWrite(rele[rel],LOW);
          estadoRele[rel]="ON";
        }
        else {
          digitalWrite(rele[rel],HIGH);
          estadoRele[rel]="OFF";
        }
      }
      break;
  }
}

void setup()
{
  // Monitor serie
  Serial.begin(9600);

  myservo.attach(servo_pin);
  setServo(angle);

  if (keyPad.begin(KEYPAD_ADDRESS) == false)
  {
    Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
    while(1);
  }

  // Leds
  for (int i=0; i<4; i++) {
    pinMode(rele[i],OUTPUT);
    digitalWrite(rele[i],HIGH);
  }
  // LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  
  // Wifi y servidor web
  WiFi.begin(ssid, password);
  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  while (WiFi.status() != WL_CONNECTED)
    delay(200);
  server.on("/ayuda",ayuda);
  server.on("/rele", HTTP_GET, setRele);
  server.on("/servo", HTTP_GET, getServo);
  server.on("/barrido",barrido);
  server.on("/status",devolverStatus);
  server.begin();
}

void loop()
{
  // Comprobar si estamos conectados
  if (WiFi.status() == WL_CONNECTED) {
    server.handleClient();
  }
  examinarTeclado();
  mostrarDatos();
}

Este código generará un servidor web que trabajará en la dirección IP local 192.168.1.205. Si ya tienes otro dispositivo trabajando en esa dirección, tendrás que cambiar la IP de este servidor por otra que esté libre. Además tendrás que poner el SSID de tu WiFi y la clave del mismo. Hecho esto, y salvo error u omisión, nuestro servidor web debería estar funcionando.

Dentro de la función «ayuda» puedes configurar el mensaje de ayuda de tu bot para personalizarlo.

Con este servidor web es con el que se comunicará el bot-puente escrito en Python que podemos ejecutar desde cualquier ordenador que se encuentre conectado a la misma red local. El código del bot-puente es éste:

from mastobot import *
from urllib.request import urlopen
import re
from unicodedata import normalize

bot = Bot(
    instance_url="LA URL DE LA INSTANCIA DE MASTODON DE TU BOT",
    access_token="EL TOKEN DE ACCESO DE TU BOT",
)

def enviarNodeMCU(texto):
	cliente = urlopen("http://192.168.1.205:8081/"+texto)
	respuesta=cliente.read().decode("utf-8")
	return respuesta

def evaluar(content: str):
	global c
	c=content
	return content
	
def normalizar(texto):
	texto=texto.lower()
	texto = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1",normalize( "NFD", texto), 0, re.I)
	texto = normalize( 'NFC', texto)
	return texto

@bot.on_mention(evaluar, validation=EVALUATE)
def respond(status):
	global c
	name = "@"+status.account.acct
	print(name)
	c=normalizar(c)
	comando=c.split()
	print(comando)
	b=False
	if comando[0]=="barrido" or comando[0]=="ayuda" or comando[0]=="status":
		r=enviarNodeMCU(comando[0])
		b=True
	elif comando[0]=="servo":
		r="Operación con servo "
		if len(comando)!=2:
			r=r+" --- Número de parámetros incorrecto [2]"
		else:
			try:
				angulo=int(comando[1])
				if angulo<0 or angulo>180:
					r=r+" --- Ángulo de servo incorrecto [0..180]"
				else:
					r=r+"--- Ángulo de "+comando[1]+"º"
					enviarNodeMCU(comando[0]+"?Angulo="+comando[1])
			except:
				r=r+" --- Parámetro de ángulo incorrecto"
	elif comando[0]=="rele":
		r="Operación con relé "
		if len(comando)!=3:
			r=r+" --- Número de parámetros incorrecto [3]"
		elif int(comando[1])<0 or int(comando[1])>3:
			r=r+" --- Número de led erróneo [0..3]"
		elif comando[2]!="on" and comando[2]!="off":
			r=r+" --- Operación con led errónea [on-off]"
		else:
			r=r+comando[1]+" -- Acción: "+comando[2]
			enviarNodeMCU(comando[0]+"?Id="+comando[1]+"&Status="+comando[2])
	else:
		r="Comando no reconocido: "+c
	return f"{name} {r}"

bot.run()

En este código tendrás que configurar correctamente la URL de la instancia donde se aloje tu bot, así como el token de acceso del mismo. Yo recomiendo alojarlo en https://botsin.space ya que es una instancia dedicada expresamente al alojamiento de cuentas bot. Es muy probable que para que este script de Python funcione tengas que instalar algunas de las librerías necesarias para su funcionamiento.

He configurado el keypad para poder mover el servo y encender/apagar los relés. Las órdenes admitidas son las siguientes:

*A[1..180]# – Mueve el servo al ángulo indicado. Ejemplo: *A125#

*B[relé][A..B] – Enciende [A] o apaga [B] el relé indicado. Ejemplos:

*B0A# – Enciende el relé 0

*B2B# – Apaga el relé 2

*B3A# – Enciende el relé 3

Y creo que eso es todo. Si tenéis alguna duda podéis preguntarme al respecto comentando en este post o enviando una mención a @hispa@mastodon.social

¡Buena suerte!

(Archivo) – El licenciado Márquez Torres

Entrada procedente de la desaparecida página El ojo del tuerto, rescatada desde Archive.org.

Tal día como hoy, 27 de febrero, en 1615, el licenciado Francisco Márquez Torres daba curso a la aprobación de la segunda parte de El quijote de Miguel de Cervantes tras su censura previa. Entre otros halagos al autor y a su obra, el licenciado Márquez Torres dejaba caer la anécdota que más abajo reproduzco, tal vez sabiendo que tanto el libro como su documento de aprobación pasarían a la historia de nuestro país y de la literatura mundial; una pequeña aunque dolorosa espina para cualquier español que tenga un mínimo de vergüenza.

Certifico con verdad que en veinte y cinco de febrero d’este año de seiscientos y quince, habiendo ido el ilustrísimo señor don Bernardo de Sandoval y Rojas, cardenal arzobispo de Toledo, mi señor, a pagar la visita que a Su Ilustrísima hizo el embajador de Francia, que vino a tratar cosas tocantes a los casamientos de sus príncipes y los de España, muchos caballeros franceses, de los que vinieron acompañando al embajador, tan corteses como entendidos y amigos de buenas letras, se llegaron a mí y a otros capellanes del cardenal mi señor, deseosos de saber qué libros de ingenio andaban más validos; y tocando acaso en este que yo estaba censurando, apenas oyeron el nombre de Miguel de Cervantes, cuando se comenzaron a hacer lenguas, encareciendo la estimación en que, así en Francia como en los reinos sus confinantes, se tenían sus obras: la Galatea, que alguno d’ellos tiene casi de memoria la primera parte d’esta, y las Novelas. Fueron tantos sus encarecimientos, que me ofrecí llevarles que viesen el autor d’ellas, que estimaron con mil demostraciones de vivos deseos. Preguntáronme muy por menor su edad, su profesión, calidad y cantidad. Halleme obligado a decir que era viejo, soldado, hidalgo y pobre, a que uno respondió estas formales palabras: “Pues, ¿a tal hombre no le tiene España muy rico y sustentado del erario público?” Acudió otro de aquellos caballeros con este pensamiento y con mucha agudeza, y dijo: “Si necesidad le ha de obligar a escribir, plega a Dios que nunca tenga abundancia, para que con sus obras, siendo él pobre, haga rico a todo el mundo”.

Ojalá pudiera decir que las cosas han cambiado desde entonces, pero no: España siempre ha sido una mala madre para sus más preclaros hijos, negándoles en vida el pan y la sal mientras se deshace en lisonjas para con sus genios muertos.

Y esta entrada quiero dedicársela a tantos y tantos escritores, profesores, científicos, doctores e investigadores a los que algún día su país dedicará premios, semanas culturales y edificios públicos, pero que hoy languidecen en el desempleo, en el desamparo o fuera de nuestras fronteras.

(Archivo) – Diez segundos

Relato procedente de la desaparecida página El ojo del tuerto, rescatada desde Archive.org.

Después de diez horas observando atentamente el desfilar de cifras en una pantalla y de aporrear con frenesí un teclado mientras bailaba con su mano derecha sobre un ratón, el suave ronroneo, la música y el movimiento le parecían un bálsamo para los sentidos. Mientras disfrutaba del momento, en un un giro del cuello para sacudirse la tensión acumulada, la vio a ella.

Ella tenía la mirada fija, perdida en un horizonte inexistente, tal vez disfrutando de las mismas sensaciones que él, en su burbuja de soledad donde nadie podía alcanzarla. En diez segundos, él inventó para ella toda una vida, imaginando cuál sería su trabajo, cuáles sus aficiones, sus anhelos, sus frustraciones. La miró tocarse el cabello sin dejar de mirar al frente, y entonces decidió que la amaba.

Justo en ese momento, mientras la contemplaba extasiado, se dio cuenta de que ella también le miraba, y se ruborizó como lo haría un niño cazado in fraganti. Esperaba indiferencia, pero de repente, ella le sonrió, derribando ese muro social invisible que separa a los desconocidos y los encierra en cárceles de soledad, y decidió que la recordaría toda su vida por aquello.

Entonces la luz del semáforo se puso en verde, y el coche de ella empezó a rodar. Él tomó la primera salida a la derecha, y allí terminó la mejor historia de amor de su vida, que había durado exactamente diez segundos.

(Archivo) – Tierra de nadie

Relato procedente de la desaparecida página El ojo del tuerto, rescatada desde Archive.org.

En la tarde del 24 de diciembre de 1914, tras haber decorado las lúgubres trincheras con serpentinas de papel y haber cantado a coro Stille Nacht con el resto de su compañía, el soldado de infantería Kurt Heinz y otro compañero cuyo nombre desconocía saltaron la trinchera desarmados en un peculiar misión.

El barro mil veces batido por las explosiones de los obuses convertía a aquella ancha franja de tierra en un paisaje lunar. A mitad de camino entre las trincheras inglesas y las alemanas, Kurt y su compañero se cruzaron con una pareja de soldados ingleses que cumplían una misión similar a la de ellos. Era curioso verles allí, desarmados y caminando sin prisas por donde antes habían muerto miles de soldados de uno y otro lado. Uno de los ingleses, el más alto, se quedó mirándole y le sonrió. Kurt le devolvió la sonrisa, y ninguno de los cuatro pudo reprimir el impulso de acercarse y conversar.

El diálogo era complicado, ya que ninguno de ellos hablaba el idioma del enemigo. Tan sólo algunas palabras soeces sueltas, que frecuentemente se intercambiaban de trinchera en trinchera a modo de insulto, y que no venían al caso. El inglés alto extendió un arrugado paquete de cigarrillos, ofreciéndoselo a los alemanes, y estos lo aceptaron sin vacilar. Kurt, para corresponder, sacó de su bolsillo un par de chocolatinas que le habían llegado con el último paquete enviado por su madre -las últimas- y tuvo que insistir para que los ingleses se las quedaran. Allí mismo se fumaron aquel delicioso cigarrillo inglés mientras hablaban a sus enemigos acompañándose de todo tipo de gestos sobre el tremendo frío que hacía, sin entender lo que estos les contestaban.

Poco a poco la desconfianza iba dando paso a la curiosidad, y otros soldados saltaron las tincheras de uno y otro lado para encontrarse en medio del barrizal. Kurt se despidió de sus amables enemigos y comenzó la penosa tarea de retirar a los muertos para enterrarlos detrás de las líneas, mientras veía al enemigo hacer lo propio. Era algo totalmente irracional. Por doquier podían verse grupos de soldados de ambos bandos departiendo. Se diría que la Primera Guerra Mundial había terminado, pero no; aún quedaban muchos años de crueles matanzas para poner fin a aquella locura. Kurt pensó en lo absurdo de la guerra al ver a todos aquellos muchachos estrechándose las manos, riendo a carcajadas en complicados diálogos, incluso jugando al fútbol en medio del barro.

Tras reunirse en un gran grupo para rezar una oración conjunta por los muertos, unos y otros se retiraron a dormir en sus húmedas madrigueras, en lo que sería la última noche de paz en la vida de muchos de ellos.

Aquel día  no hubo explosiones. Los cañones no dispararon su carga mortal, y el asfixiante gas mostaza se quedó guardado en los polvorines. Aquella navidad de 1914, en medio de la locura fratricida orquestada por gobernantes sin escrúpulos, la juventud de dos países antepuso sus valores humanos sobre la barbarie, dando lugar a uno de los acontecimientos más extraordinarios de la historia de los conflictos bélicos: la Tregua de Navidad.

Sistema de riego autónomo (V) – El código

En los cuatro posts anteriores hemos visto los elementos que necesitamos para nuestro sistema de riego, el montaje del mismo y la forma de instalar el IDE de Arduino para compilar y subir nuestro código al microcontrolador NodeMCU que hará funcionar todo el tinglado. Ahora veremos cómo es el código.

include "CTBot.h"
 class BotTelegramClass {
   private:
     // Atributos de conexión a internet y bot
     bool estadoConex=false;
     String SSID = "SSID de tu conexión wifi";
     String passwd = "Password de tu conexión wifi;
     String token = "Token del bot de Telegram";
     CTBot myBot;
     TBMessage msg;
     uint32_t ID=000000000; // ID de tu usario de Telegram
     // Atributos de entrada/salida
     int circuitoHumedad=16;
     int lecturaHumedad=A0;
     int ledHumedad=5;
     int circuitoNivel=12;
     int lecturaNivel=13;
     int ledNivel=14;
     int circuitoRiego=4;
     int ledRiego=15;
     // Atributos de temporización, umbrales y medidas
     int umbralHumedad=850;
     int delayCircuito=500;
     int delayMedida=300;
     int delayRiego=5000;
     int tiempoRiego=10;
     int tempPrincipal=0;
     int tempRiego=delayRiego-5;
     // Banderas booleanas de modo, notificación y alarmas
     bool modo=true;
     bool notificaciones=false;
     bool alarmaNivel=false;
     bool estadoConexion=false;
     // Métodos privados
     int GetHumedad();
     void EntregarHumedad();
     bool GetNivel();
     void EntregarNivel();
     void ActivarRiego();
     void EmitirMensaje(bool s, bool t, String m);
     void leerTelegram();
     void TemporizadorDeEventos();
     void Conectar();
   public:
     void Iniciar();
     void BucleInfinito();
 };

 int BotTelegramClass::GetHumedad() {
   digitalWrite(circuitoHumedad, HIGH);
   delay(delayCircuito);
   int h=analogRead(lecturaHumedad);
   digitalWrite(circuitoHumedad, LOW);
   return h;
 }

 void BotTelegramClass::EntregarHumedad() {
   int humedad=GetHumedad();
   EmitirMensaje(true, true, "Humedad del terreno: " + (String) humedad);
   if (humedad > umbralHumedad) {
     digitalWrite(ledHumedad,HIGH);
     EmitirMensaje(true,true,"¡ALARMA! - Humedad del terreno baja");
   }
   else {
     digitalWrite(ledHumedad,LOW);
   }  
 }

 bool BotTelegramClass::GetNivel() {
   bool ban=false;
   digitalWrite(circuitoNivel,LOW);
   if (digitalRead(lecturaNivel) == HIGH) {
     ban=true;
   }
   digitalWrite(circuitoNivel,HIGH);
   return ban;
 }

 void BotTelegramClass::EntregarNivel() {
   if (GetNivel()) {
     EmitirMensaje(true, true, "¡ALARMA! - Nivel de agua bajo");
     digitalWrite(ledNivel,HIGH);
   }
   else {
     EmitirMensaje(true, true, "Nivel de agua correcto");
     digitalWrite(ledNivel,LOW);
   }
 }

 void BotTelegramClass::ActivarRiego() {
   EntregarHumedad();
   digitalWrite(circuitoRiego,LOW);
   EmitirMensaje(true,true,"Motor de riego activado");
   digitalWrite(ledRiego,HIGH);
   if (modo) {
     EmitirMensaje(true,true,"Modo de funcionamiento automático");
   }
   else {
     EmitirMensaje(true,true,"Modo de funcionamiento manual");
   }
   delay(tiempoRiego*1000);
   digitalWrite(circuitoRiego,HIGH);
   EmitirMensaje(true,true,"Motor de riego desactivado");
   digitalWrite(ledRiego,LOW);
   EntregarHumedad();
   EntregarNivel();
 }

 void BotTelegramClass::EmitirMensaje(bool s, bool t, String m) {
   if (s) {
     Serial.println(m);
   }
   if (t) {
     myBot.sendMessage(ID,m);
   }
 }

 void BotTelegramClass::Conectar() {
   while(!estadoConexion) {
     EmitirMensaje(true,false,"Estado de la conexión: OFFLINE");
     EmitirMensaje(true,false,"Conectando…");
     myBot.wifiConnect(SSID, passwd);
     myBot.setTelegramToken(token);
     if (myBot.testConnection()) {
       EmitirMensaje(true,true,"Estado de la conexión: ONLINE");
       estadoConexion=true;
     }
     else {
       EmitirMensaje(true,false,"Intento de conexión fallido. Reintentando…");
     }
   }
 }

 void BotTelegramClass::Iniciar() {
   Conectar();
   pinMode(circuitoHumedad, OUTPUT);
   pinMode(ledHumedad, OUTPUT);
   pinMode(circuitoNivel,OUTPUT);
   pinMode(lecturaNivel,INPUT);
   pinMode(ledNivel,OUTPUT);
   pinMode(circuitoRiego,OUTPUT);
   pinMode(ledRiego,OUTPUT);
   digitalWrite(ledHumedad,LOW);
   digitalWrite(ledNivel,LOW);
   digitalWrite(ledRiego,LOW);
   digitalWrite(circuitoRiego,HIGH);
   EntregarHumedad();
   EntregarNivel();
 }

 void BotTelegramClass::leerTelegram() {
   String orden;
   int umbral;
   if (myBot.getNewMessage(msg)) {
     orden=msg.text;
     EmitirMensaje(true,false,"Comando recibido desde el bot: "+orden);
     if (msg.sender.id!=ID) {
       myBot.sendMessage(msg.sender.id,"Usuario no autorizado.");
       EmitirMensaje(true,true,"Comando recibido desde usuario no autorizado.");
       EmitirMensaje(true,true,"Usuario ID: "+(String) msg.sender.id);
       EmitirMensaje(true,true,"Comando   : "+orden);
     }
     else {
       switch(orden[0]) {
         case 'H':
           EntregarHumedad();
           break;
         case 'U':
           orden=orden.substring(1);
           umbral=orden.toInt();
           if (umbral<100 || umbral>1000) {
             EmitirMensaje(false,true,"Parámetro de umbral incorrecto");
           }
           else {
             umbralHumedad=umbral;
             EmitirMensaje(true,true,"Nuevo umbral de alarma establecido: "+(String) umbralHumedad);
             EntregarHumedad();
           }
           break;
         case 'N':
           EmitirMensaje(true,true,"Notificaciones activadas");
           notificaciones=true;
           EntregarHumedad();
           break;
         case 'n':
           EmitirMensaje(true,true,"Notificaciones desactivadas");
           notificaciones=false;
           EntregarHumedad();
           break;
         case 'R':
           ActivarRiego();
           break;
         case 'M':
           modo=false;
           EmitirMensaje(true,true,"Modo de funcionamiento manual");
           break;
         case 'A':
           modo=true;
           tempRiego=delayRiego-5;
           EmitirMensaje(true,true,"Modo de funcionamiento autónomo");
           break;
         case 'L':
           EntregarNivel();
           break;
       }
     }
   }
 }

 void BotTelegramClass::TemporizadorDeEventos() {
   if (tempPrincipal>delayMedida) {
     tempPrincipal=0;
     if (myBot.testConnection()) {
       if (notificaciones) {
         EmitirMensaje(true,true,"Estado de la conexión: ONLINE");
         EntregarHumedad();
       }     
     }
     else {
       EmitirMensaje(true,false,"Intento de conexión fallido. Reintentando…");
       Conectar();
     }
   }
   else {
     tempPrincipal+=1;
     delay(1000);
     EmitirMensaje(true,false,"Temporizador principal: "+(String) tempPrincipal);
   }
   if (modo) {
     EmitirMensaje(true,false,"Temporizador de riego: "+(String) tempRiego);
     if (tempRiego>delayRiego) {
       if (GetHumedad()>umbralHumedad) {
         ActivarRiego();
       }
       tempRiego=0;
     }
     else {
         tempRiego+=1;
     }
   }
 }

 void BotTelegramClass::BucleInfinito() {
   while (true) {
     leerTelegram();
     TemporizadorDeEventos();
   }
 }

 BotTelegramClass bot;
 void setup() {
   Serial.begin(9600);
   delay(1000);
   Serial.println("Iniciando el sistema…");
   bot.Iniciar();
 }

 void loop() { 
   bot.BucleInfinito();
 }

Ahora sólo hay que copiar este código, pegarlo en un sketch del IDE de Arduino y subirlo a nuestra NodeMCU. Antes de eso tendrás que incluir en su lugar correcto dentro del código los datos de tu conexión wifi (he puesto unos comentarios sobre dónde tienen que ir), así como el Token de tu bot de Telegram. Para saber cómo crear un bot de Telegram, te recomiendo este artículo. Por supuesto, la única parte que nos interesa de ese artículo es cómo dar de alta el bot con Botfather y recibir el token que nos permitirá utilizarlo con nuestro programa.

En el próximo (y espero que último) post, pondré algunas fotos de mi montaje final. Podéis comentar cualquier duda en los comentarios de estos posts. Cualquier duda o sugerencia será bien recibida.

Sistema de riego autónomo (IV) – Arduino IDE y librerías

Antes de pasar a describir el código fuente del sistema de riego necesitamos disponer del IDE de Arduino y las librerías necesarias instaladas. Hay algunas diferencias entre hacerlo en Windows y en Linux, pero trataré de explicarlas las dos lo mejor que pueda.

El IDE de Arduino es un entorno de desarrollo que nos permite escribir software para los microcontroladores soportados. En este caso, la NodeMCU, que está basada en el popular chip ESP8266, una de cuyas principales características es que incorpora WiFi, lo que la hace muy práctica para este tipo de aplicaciones conectadas. Podéis ver una descripción de esta plataforma y sus pines de salida en hwlibre.com.

Si nuestro sistema operativo es Windows, podemos acudir a la página de descarga del IDE de Arduino. En esta página podemos descargar el IDE para Windows, Linux y Mac OS, pero si nuestro sistema operativo es Ubuntu o alguno de sus derivados, la instalación es mucho más simple, ya que sólo tenemos que escribir en el terminal:

sudo apt install arduino

O incluso podemos acudir al centro de software de Ubuntu y buscar «arduino».

En cambio, si estás instalando Arduino IDE en una Raspberry Pi, no es recomendable instalar el IDE desde el comando apt, ya que la versión existente en los repositorios de raspbian está muy desactualizada. Aquí hay una buena receta para instalar Arduino IDE en vuestra Raspberry Pi (en inglés).

No me voy a extender más, porque hay muchos tutoriales para instalar el IDE de Arduino para todos los sistemas operativos, pero tras la instalación del IDE es imprescindible que también instalemos en éste el soporte para las placas ESP8266. Para ello nos iremos a Archivos – Preferencias:

Y en el cuadro de texto «Gestor de URLs Adicionales de Tarjetas» pondremos la siguiente dirección:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Tras esto, ya podemos seleccionar desde Herramientas – Placa – Gestor de Tarjetas el grupo de tarjetas basadas en ESP8266:

Una vez instalado este paquete, nuestro Arduino IDE será capaz de compilar programas para, entre otros, el NodeMCU que utilizaremos para poner en marcha nuestro sistema de riego. Para ello tendremos que seleccionar el tipo de placa de entre las soportadas por este paquete. En nuestro caso, la NodeMCU 1.0:

A continuación instalaremos la librería CTBot, que gestionará el acceso a internet mediante wifi y el bot de Telegram, mediante el menú Programa – Incluir librería – Administrar bibliotecas:

Asimismo, para resolver las dependencias de esta librería CTBot, necesitaremos instalar (si no la tenemos ya instalada) la librería ArduinoJson, con el mismo procedimiento que hemos usado para CTBot.

Después de esto, nuestro programa podrá compilarse sin problemas.

En el próximo post veremos cómo se crea un bot de Telegram, y los datos que necesitaremos para hacerlo funcionar en nuestra NodeMCU.

Sistema de riego autónomo (III) – El montaje (Segunda parte)

En la entrada anterior mostré los diagramas del montaje así como de la placa de prototipado. En este post iré mostrando uno a uno los diferentes circuitos que componen esta placa, y que permiten a nuestra NodeMCU interactuar con los distintos elementos del riego.

Como mostramos previamente, ésta es la placa del circuito:

Ahora iremos examinando cada una de sus partes.

1. Medidor de humedad (higrómetro)

Ésta es la parte más «complicada» del circuito.

En el conector 1 va la alimentación a 5V, siendo el de la izquierda el negativo y el de la derecha el positivo. Esta alimentación se obtiene desde los terminales de 5V de la base de desarrollo del NodeMCU.

El conector número 2 es la salida analógica del sensor, que entrega la medida de humedad al microcontrolador y se conecta a la entrada A0 del NodeMCU.

El conector 3 es un interruptor accionado mediante un transistor NPN, de manera que el módulo sensor de humedad sólo reciba alimentación cuando se vaya a efectuar una medida, porque en caso contrario, y debido al efecto de electrolisis, la sonda se deterioraría en pocos días. Este interruptor transistorizado es controlado desde el pin D2 del NodeMCU, y va a la base del transistor con una resistencia de 4,7KOhm

El módulo sensor de humedad YL-69 (a la derecha) va soldado a la placa en las posiciones indicadas por los cables amarillo, azul y rojo, mientras las salidas del mismo hacia la sonda van soldadas para que pueda conectarse la misma en el conector número 4.

2. Medidor de nivel de agua

Como expliqué en la primera entrada, este medidor de nivel es un interruptor de tipo boya flotante que nos informará de cuando el nivel de agua en el depósito llegue a un nivel bajo, para evitar que nos quedemos sin agua y que la bomba pueda averiarse. Su circuito es el siguiente:

Como podemos ver, comparte la alimentación (conector 1) con el medidor de humedad. Como en el caso anterior, y al tratarse de un elemento que podría estar en contacto con el agua, he puesto un interruptor transistorizado en el conector 2 que sólo cerrará el circuito cuando vayamos a tomar una medida. Como en el caso anterior, la resistencia a la base del transistor es de 4,7KOhm.

En el conector 3 tenemos la entrada digital que indicará al NodeMCU el nivel de agua.

En los conectores numerados como 4 tenemos las salidas hacia el interruptor/boya. El transistor cerrará el circuito (o no, según convenga) a través de una resistencia pull-down de 10KOhm.

3. Alimentación a 12V, relé y bomba de agua

Esta parte del circuito sólo comparte con el resto la masa común GND. Se encarga de recibir la alimentación de 12V a través del conector 2, entregar esa misma alimentación a la base de desarrollo de la NodeMCU a través del conector 3 y enviarla también a la bomba de agua (conector B), aunque pasando primero por el relé (conector R) para que tengamos un control del encendido de la misma. Es seguramente la parte más simple del circuito, pero también la más importante.

4. Diodos de alarma

He montado tres diodos como alarmas luminosas de humedad baja del terreno, de bajo nivel de agua en el depósito y de bomba de agua en funcionamiento. Son unos diodos opcionales, por si se quieren poner en un lugar visible, aunque yo los he dejado dentro de la caja. El circuito es el siguiente:

De arriba a abajo tenemos las salidas digitales del NodeMCU 1,2 y 3, que proporcionarán el voltaje necesario para encender cada uno de los diodos según convenga. Cada uno de estos tres circuitos lleva una resistencia de 220 Ohm para evitar el deterioro del led, y finalmente los leds pueden conectarse a las salidas LED 1, LED 2 y LED 3.

Con esto queda descrito el circuito (o mejor dicho, los distintos circuitos) que componen la placa de soldadura. En la siguiente entrada veremos cómo instalar el IDE de Arduino y las distintas librerías necesarias para poder compilar nuestro código y subirlo a la NodeMCU.