Un AMSTRAD CPC en tu bolsillo

Hace tiempo que no escribo nada de retroinformática y resulta que estos días si que estoy haciendo cositas, así que vamos a dedicar un rato a tachaaaan…. Construirnos un Amstrad CPC con un microcontrolador.

En primer lugar, por si queréis hacerlo vosotros, aunque el microcontrolador que vamos a utilizar es un ESP-32, que ya conocemos de antes (algunas entradas hay en este mismo blog) por facilidad vamos a utilizar un modelo muy concreto que tiene salida de VGA y entrada para teclado, además de un slot para MicroSD que nos será muy, muy util. Es el Lilygo FavGL VGA32 que se puede conseguir por unos 10 Euros en la web del fabricante (un poco más si lo compras desde aliexpress).

El proceso no podría ser más sencillo y el resultado final es como si tuviesemos un CPC 64 / 128 totalmente operativo, pero usando un monitor VGA y un teclado PS/2 (y unos cascos o altavoces para el sonido).

El resultado final debería quedar algo así:

Y ahora paso a paso…

Lo primero de todo es tener un cable microusb y conectar nuestra placa al ordenador en el que estemos, luego (después de haber comprobado que el dispositivo es reconocido como puerto serie) accedemos a esta web:

https://zxespectrum.speccy.org/flash

Seleccionamos CPCESP y pinchamos connect y nos pedirá que elijamos el puerto serie en el que se nos ha reconocido la placa (lo mío es linux, así que seguro que es más sencillo de reconocer que los que usan windows), luego indicamos que queremos instalar CPCESP y lo confirmamos para, finalmente esperar a que se instale.

Por desgracia el código de CPCESP no está publicado todavía, pero han prometido que pronto lo tendremos disponible, para más info podéis consultar este repositorio:

https://github.com/EremusOne/CPCESP_alpha

Ahora toca rebuscar entre tus cosas viejas y conseguir un teclado PS/2 y un monitor con entrada VGA (tengo unos cuantos por aquí, te lo dejo barato si lo necesitas). Inicialmente arranca en el modo 1 del cpc 64, pero puedes cambiarlo de manera muy sencilla. Las teclas para sacar todo el provecho al «invento» son estas:

  • F1 Main menu
  • F2 Load SNA
  • F5 Load DSK
  • F8 CPU stats
  • F9 Volume down
  • F10 Volume up
  • F11 Hard reset
  • F12 Reset ESP32
  • CTRL + F1 Hardware info
  • CTRL + F2 Turbo mode
  • CTRL + F5..F8 Screen centering in CRT 15K/50hz mode
  • Pause Pause

Para poder meter software del CPC solo tenemos que formatear una microsd en FAT16 o FAT32 y dejar ahí todos los .dsk o .sna que tengamos a mano. Luego desde el menú principal ya podréis seleccionarlo y cargarlo… Eso si, siempre que hayáis enchufado la placa a un alimentador, que necesita «chicha» para funcionar.

Y ya para rizar el rizo, lo mejor es meter el invento en una caja… Yo me he impreso una en 3D, pero seguro que se me ocurre alguna mejora:

Bueno, me voy a rememorar mis viejos tiempos… Que tengo más emuladores por ahí pendientes.

Usando una fuente de texto en IoT

Una de las características de los dispositivos que se usan para IoT es la limitación de recursos que tiene. No tienen un sistema operativo disponible y todo lo que queramos que haga hay que programarlo prácticamente de cero. Eso incluye cosas tan peregrinas como definir las fuentes de texto que se utilizarán para escribir en pantalla. Esto es algo que, generalmente, damos por hecho en cualquier otro sistema que programamos, pero que ofrece la oportunidad de ver un poco mejor cómo funciona la informática por dentro.

En principio las pantallas en las que querramos utilizar estas fuentes (en las que queramos escribir algo) son bastante pequeñas, la que vemos en la siguiente imagen, por ejemplo, es de 128×64 pixels (SSD1306):

¿Cómo hacemos para escribir un caracter en una pantalla de estas características? Quitando los comandos propios de inicialización, limpieza y demás, lo único que hacemos es indicar qué pixel queremos que se ilumine y cual no (si hay interés ya desarrollaremos un poco más en profundidad los comandos que se le mandan) y, para ahorrar ciclos y ancho de banda, hay que enviarle los datos en bytes completos (8 bits) y cada bit representa un pixel que se ilumina o no.

Hay software ya desarrollado para convertir fuentes ttf en código c que se puede usar con ciertas librerías, pero vamos a escoger el camino difícil, supongamos que queremos utilizar los primeros 95 caracteres ASCII empezando por el espacio. Serían estos:

Quitando el 127 que no nos interesa porque no es imprimible, una vez decididos los caracteres que queremos imprimir tenemos que decidir el tamaño que queremos utilizar. En este caso queremos que tengan una altura de 8 pixels y, como van a ser de ancho fijo, tendrán una anchura de 8 pixels igualmente. Un carácter se representaría entonces de esta manera:

Y su representación en bits de la primera fila sería 00011000 o lo que es lo mismo 0x18 en hexadecimal. Esta letra, traducida a bytes quedaría: 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66,0x66,0x00 y con esta información ya sabríamos qué bytes mandar a la pantalla cuando tuviésemos que escribir la letra.

Hacer este proceso para cada letra sería muy pesado, más teniendo en cuenta que son 95 caracteres, pero como nosotros somos programadores os voy a contar un método para extraer estos datos de una mejor manera. Vamos a generar una imagen y vamos a pintar allí las letras que queremos. Para ello abriremos el programa gráfico favorito que tengamos (el mío es gimp) y crearemos una imagen justo del tamaño para que quepan todas nuestras letras (ni más ni menos). En este caso son 95*8 = 760 y 8 de altura, con el fondo negro.

Una vez creada esa imagen, creamos un texto en la fuente que queramos y escribimos los caracteres que queremos:

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Ajustamos para que quepan todos (quitamos alisado y hints varios) y quedará algo así:

Luego cambiamos el tipo de imagen a indexada con 2 colores (solo blanco y negro) y lo exportamos a formato raw:

Esto lo que hace es generarnos un archivo .data que contiene 1 byte por pixel que, como hemos indexado solo contendrá ceros o unos… Y ahora toca programar.

El objetivo es obtener una ristra de bytes juntando los ceros y unos agrupándolos de 8 en ocho. Luego veremos cómo usarlos. Lo más sencillo es hacer un scrip en python, así que escribimos algo así:

def convert_to_hex(byte_data):
    # Agrupamos los datos de 8 en 8
    byte_data = [byte_data[i:i+8] for i in range(0, len(byte_data), 8)]
    # Convertimos cada byte a su representación en hexadecimal
    resu = []
    for i in range(len(byte_data)):
        byte = sum([byte_data[i][j] << j for j in range(8)])
        # Añadimos el valor en hexadecimal a la lista resu
        resu.append(f'0x{byte:02X}')

    return resu

Y ahora solo nos queda leer de un archivo y escribirlo en otro como código c:

def read_binary_file(file_path):
    with open(file_path, 'rb') as file:
        return file.read()

def write_hex_file(hex_data, output_path):
    with open(output_path, 'w') as file:
        # Separamos los valores por comas y ponemos un salto de linea cada 16 valores
        file.write ('static const char font_8x8[] = {\n\t')
        slice_size = 16
        for i in range(0, len(hex_data), slice_size):
            file.write(', '.join(hex_data[i:i+slice_size]))
            file.write(',\n\t')
        file.write('};')

Lo juntamos todo en una función principal pasándole como parámetro el archivo de entrada y el de salida:

def main(input_file, output_file):
    binary_data = read_binary_file(input_file)
    hex_data = convert_to_hex(binary_data)
    write_hex_file(hex_data, output_file)

Y ya podemos ejecutarlo, el resultado sería algo así:

static const char font_8x8[] = {
	0x00, 0x0C, 0x36, 0x36, 0x0C, 0x00, 0x1C, 0x06, 0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
	0x3E, 0x0C, 0x1E, 0x1E, 0x38, 0x3F, 0x1C, 0x3F, 0x1E, 0x1E, 0x00, 0x00, 0x18, 0x00, 0x06, 0x1E,
	0x3E, 0x0C, 0x3F, 0x3C, 0x1F, 0x7F, 0x7F, 0x3C, 0x33, 0x1E, 0x78, 0x67, 0x0F, 0x63, 0x63, 0x1C,
	0x3F, 0x1E, 0x3F, 0x1E, 0x3F, 0x33, 0x33, 0x63, 0x63, 0x33, 0x7F, 0x1E, 0x03, 0x1E, 0x08, 0x00,
	0x0C, 0x00, 0x07, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x07, 0x0C, 0x30, 0x07, 0x0E, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x07, 0x6E, 0x00,
	0x1E, 0x36, 0x36, 0x3E, 0x63, 0x36, 0x06, 0x0C, 0x0C, 0x66, 0x0C, 0x00, 0x00, 0x00, 0x30, 0x63,
	0x0E, 0x33, 0x33, 0x3C, 0x03, 0x06, 0x33, 0x33, 0x33, 0x0C, 0x0C, 0x0C, 0x00, 0x0C, 0x33, 0x63,
	0x1E, 0x66, 0x66, 0x36, 0x46, 0x46, 0x66, 0x33, 0x0C, 0x30, 0x66, 0x06, 0x77, 0x67, 0x36, 0x66,
	0x33, 0x66, 0x33, 0x2D, 0x33, 0x33, 0x63, 0x63, 0x33, 0x63, 0x06, 0x06, 0x18, 0x1C, 0x00, 0x0C,
	0x00, 0x06, 0x00, 0x30, 0x00, 0x36, 0x00, 0x06, 0x00, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x0C, 0x3B, 0x00, 0x1E,
	0x36, 0x7F, 0x03, 0x33, 0x1C, 0x03, 0x06, 0x18, 0x3C, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x73, 0x0C,
	0x30, 0x30, 0x36, 0x1F, 0x03, 0x30, 0x33, 0x33, 0x0C, 0x0C, 0x06, 0x3F, 0x18, 0x30, 0x7B, 0x33,
	0x66, 0x03, 0x66, 0x16, 0x16, 0x03, 0x33, 0x0C, 0x30, 0x36, 0x06, 0x7F, 0x6F, 0x63, 0x66, 0x33,
	0x66, 0x07, 0x0C, 0x33, 0x33, 0x63, 0x36, 0x33, 0x31, 0x06, 0x0C, 0x18, 0x36, 0x00, 0x18, 0x1E,
	0x06, 0x1E, 0x30, 0x1E, 0x06, 0x6E, 0x36, 0x0E, 0x30, 0x66, 0x0C, 0x33, 0x1F, 0x1E, 0x3B, 0x6E,
	0x3B, 0x3E, 0x3E, 0x33, 0x33, 0x63, 0x63, 0x33, 0x3F, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x00,
	0x36, 0x1E, 0x18, 0x6E, 0x00, 0x06, 0x18, 0xFF, 0x3F, 0x00, 0x3F, 0x00, 0x0C, 0x7B, 0x0C, 0x1C,
	0x1C, 0x33, 0x30, 0x1F, 0x18, 0x1E, 0x3E, 0x00, 0x00, 0x03, 0x00, 0x30, 0x18, 0x7B, 0x33, 0x3E,
	0x03, 0x66, 0x1E, 0x1E, 0x03, 0x3F, 0x0C, 0x30, 0x1E, 0x06, 0x7F, 0x7B, 0x63, 0x3E, 0x33, 0x3E,
	0x0E, 0x0C, 0x33, 0x33, 0x6B, 0x1C, 0x1E, 0x18, 0x06, 0x18, 0x18, 0x63, 0x00, 0x00, 0x30, 0x3E,
	0x33, 0x3E, 0x33, 0x0F, 0x33, 0x6E, 0x0C, 0x30, 0x36, 0x0C, 0x7F, 0x33, 0x33, 0x66, 0x33, 0x6E,
	0x03, 0x0C, 0x33, 0x33, 0x6B, 0x36, 0x33, 0x19, 0x07, 0x00, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x7F,
	0x30, 0x0C, 0x3B, 0x00, 0x06, 0x18, 0x3C, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x6F, 0x0C, 0x06, 0x30,
	0x7F, 0x30, 0x33, 0x0C, 0x33, 0x30, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x0C, 0x7B, 0x3F, 0x66, 0x03,
	0x66, 0x16, 0x16, 0x73, 0x33, 0x0C, 0x33, 0x36, 0x46, 0x6B, 0x73, 0x63, 0x06, 0x3B, 0x36, 0x38,
	0x0C, 0x33, 0x33, 0x7F, 0x1C, 0x0C, 0x4C, 0x06, 0x30, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x03,
	0x33, 0x3F, 0x06, 0x33, 0x66, 0x0C, 0x30, 0x1E, 0x0C, 0x7F, 0x33, 0x33, 0x66, 0x33, 0x66, 0x1E,
	0x0C, 0x33, 0x33, 0x7F, 0x1C, 0x33, 0x0C, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1F,
	0x66, 0x33, 0x00, 0x0C, 0x0C, 0x66, 0x0C, 0x0C, 0x00, 0x0C, 0x03, 0x67, 0x0C, 0x33, 0x33, 0x30,
	0x33, 0x33, 0x0C, 0x33, 0x18, 0x0C, 0x0C, 0x0C, 0x3F, 0x0C, 0x00, 0x03, 0x33, 0x66, 0x66, 0x36,
	0x46, 0x06, 0x66, 0x33, 0x0C, 0x33, 0x66, 0x66, 0x63, 0x63, 0x36, 0x06, 0x1E, 0x66, 0x33, 0x0C,
	0x33, 0x1E, 0x77, 0x36, 0x0C, 0x66, 0x06, 0x60, 0x18, 0x00, 0x00, 0x00, 0x33, 0x66, 0x33, 0x33,
	0x03, 0x06, 0x3E, 0x66, 0x0C, 0x33, 0x36, 0x0C, 0x6B, 0x33, 0x33, 0x3E, 0x3E, 0x06, 0x30, 0x2C,
	0x33, 0x1E, 0x7F, 0x36, 0x3E, 0x26, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x36, 0x0C, 0x63,
	0x6E, 0x00, 0x18, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x01, 0x3E, 0x3F, 0x3F, 0x1E, 0x78, 0x1E,
	0x1E, 0x0C, 0x1E, 0x0E, 0x0C, 0x06, 0x18, 0x00, 0x06, 0x0C, 0x1E, 0x33, 0x3F, 0x3C, 0x1F, 0x7F,
	0x0F, 0x7C, 0x33, 0x1E, 0x1E, 0x67, 0x7F, 0x63, 0x63, 0x1C, 0x0F, 0x38, 0x67, 0x1E, 0x1E, 0x3F,
	0x0C, 0x63, 0x63, 0x1E, 0x7F, 0x1E, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x6E, 0x3B, 0x1E, 0x6E, 0x1E,
	0x0F, 0x30, 0x67, 0x1E, 0x33, 0x67, 0x1E, 0x63, 0x33, 0x1E, 0x06, 0x30, 0x0F, 0x1F, 0x18, 0x6E,
	0x0C, 0x36, 0x63, 0x30, 0x3F, 0x38, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x1F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
	};

Y ya estaría… ¿Cómo usaríamos esto en nuestra aplicación IoT? Pues básicamente incluiríamos la cabecera font_8x8.h y cuando necesitásemos localizar un carácter ASCII simplemente tendremos que restarle 32 al caracter, multiplicarlo por el número de caracteres de nuestra fuente (95) y ese sería el primer byte del caracter dentro del array. Como vamos a necesitar todas las líneas del caracter podemos hacer una función que las recupere de esta manera:

char getcharslicefrom8x8font(char c, int rowInChar)
{
    return font_8x8[(c - 32) + (rowInChar)*95];
}

Para escribir una cadena completa en una línea habría que localizar la posición inicial de la línea, recuperar todos los bytes de cada caracter que queramos escribir y mandarlos a esa línea de la pantalla (así explicado simplificadamente).

Tenéis todo el código de la conversión de bitmap a c en este repositorio: https://github.com/yoprogramo/font_to_c

Happy coding!

UPDATE: Ya se puede encontrar la fuente así creada en los repositorios de un par de proyectos muy interesantes: https://github.com/fhoedemakers/pico-infonesPlus/ y https://github.com/fhoedemakers/pico-smsplus

Cambiar el agua del aire acondicionado mediante IoT

Si tienes aire acondicionado, uno de los defectos que más se encuentran en estos aparatos es cómo deshacerse del agua de condensación del mismo, especialmente en verano. Si eres como yo o como la mayoría de la gente, lo que harás será poner una botella en algún sitio accesible que se va llenando poco a poco y que, periódicamente, tendrás que cambiar para que no se desborde. Pero, en tiempos calurosos y ajetreados es muy común que la botella (sea del tamaño que sea) termine llenándose y mojemos a quien sea que tengamos debajo del aire acondicionado.

Esta dinámica, que a mi se me antoja muy molesta, la llevo repitiendo en casa y en la oficina (si, en mi oficina tengo un aire acondicionado con botellita) durante varios años y siempre me planteo, ¿y si hago algo para que me avise justo cuando está apunto de llenarse y así puedo cambiarla a tiempo? Hasta el momento no había tenido tiempo, pero ahora he encontrado un ratillo y me he puesto manos a la obra.

Los requisitos eran sencillos, necesito saber si el nivel de agua de una botella ha llegado a cierta altura en la botella, no necesito saber el % exacto, pero si saber si se esta llenando antes de que se llene. Hay algunos sensores de ultrasonidos para depósitos, pero me parecía matar moscas a cañonazos, además, el tamaño de la botella podía ser variable, por lo que el sensor no tendría que ser muy aparatoso. Buscando encontré este:

Puede funcionar a 3.3v o 5v y básicamente devuelve un valor analógico distinto según por donde le llegue el agua en la parte inferior. Este es el enlace de compra en amazon: https://www.amazon.es/dp/B07DJ5FZ31

Por otro lado quería que la solución fuese portable, es decir, que no tuviese que andar echando cables por ahí, por lo que una solución que admitiese baterías también era recomendable. Como plataforma yo estoy muy acostumbrado a usar ESP32 y me hacía mucho más sencillo la parte de conexión a la wifi, así que buscando encontré esto:

Un ESP32-C3 que tiene un puerto para batería que se puede recargar mediante usb, me fue casi más complicado hacerme con las pilas LS16340

Con todos los componentes solo me quedaba conectar el ESP32-C3 al sensor, teniendo cuidado de no usar el pin que se usa para medir el voltaje de la pila (spoiler, es el A0 y lo descubrí después de haberlo soldado). El esquema sería tal que así:

Todo soldado para probar quedaría así:

Y ahora lo interesante, que sería programarla para que podamos controlar el nivel de agua. Como las especificaciones no son siempre exactas lo que hice fue unas pruebas leyendo el nivel del GPIO04 (que tiene conversor A/D) y meter y sacar el sensor del agua imprimiendo por el puerto serie los valores obtenidos:

#define A1 4
void setup() {
  // (Optional)Press reset button
  // on the dev board to see these print statements
  Serial.begin(115200);
  while (!Serial) { }
  // Configuramos el pin A1 como de entrada analógica
  pinMode(A1, INPUT);
  delay (1000);
  Serial.println("Starting...");
}

void loop() {
  int sensorValue = analogRead(A1);
  Serial.println(sensorValue);
  delay(1000);
}

Nota: Esto lo he compilado usando platformio.io (os lo recomiendo mucho) y os dejaré el proyecto completo en un repositorio para que podáis compilarlo vosotros también.

Una vez compilado e instalado el programa veíamos por el puerto serie cada segundo el valor que leíamos del sensor, obteniendo valores entre cero y 3000 (supongo que el rango máximo será Vin que en nuestro caso es 3.3v), así que nuestro valor «umbral» será de 2000 (no se ha llegado todavía arriba del todo pero ya hay agua mojando el sensor).

El siguente paso es conectar a internet para que nos pueda mandar la alerta. Esto en ESP32 es relativamente sencillo:

#include <WiFi.h>

void printWifiStatus() {
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
boolean connectWifi (char *ssid, char *pass){
  WiFi.useStaticBuffers(true);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  int max = MAXATTEMPS;

  Serial.println("Connecting to WiFi");
  while ((WiFi.status() != WL_CONNECTED) && --max>0) {
    delay(500);
    Serial.print(".");
  }
  if (max == 0)
    return false;
  Serial.println("Connected to WiFi");
  return true;
}

Con esto solo hay que añadir una llamada a connectWifi parándole el SSID y la contraseña de nuestra red wifi para tener conexión. Una vez que tenemos conexión podemos hacer varias cosas para hacernos llegar una alerta, pero la más sencilla para mi era hacer que me enviase un mensaje por telegram. Para ello habría que crear un bot (hablando con botfather) y obtener el token necesario para poder usarlo. Luego he creado un canal y he puesto a este bot como administrador, con lo que para utilizar este bot solo tendría que hacer estos defines:

#define BOTTOKEN "7413559559:AAHmlkmiubcvtxZO0j0nqITZjByjrS1Ah3U"
#define CHAT_ID "-1002245988095"

y usar la librería Universal-Arduino-Telegram-bot, que podéis encontrar en https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot de esta manera:

#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>

WiFiClientSecure client;
UniversalTelegramBot bot(BOTTOKEN, client);
client.setCACert(TELEGRAM_CERTIFICATE_ROOT);

bot.sendMessage(CHAT_ID, "esto es un mensaje");

Así que la lógica del programa cambia ahora a que cuando el valor detectado sea mayor que 2000 enviemos un mensaje con el bot para alertarnos.

El programa lo he mejorado para leer el valor de voltaje de la batería y para que entre en suspensión y se despierte cada 10 segundos para ahorrar energía, pero eso podéis sacarlo de los ejemplos que hay en el repositorio https://github.com/Xinyuan-LilyGO/LilyGo-T-OI-PLUS. El caso es que ahora tenía que hacer el montaje final en mi botella de agua del aire acondicionado. Elegí un bote de estos gigantes de proteinas (si, alguien en casa va al gimnasio) y tras impermeabilizar adecuadamente los cables y las conexiones decidí meter el sensor por la parte superior del tapón y así antes de llegar arriba del todo haría el contacto adecuado y quedó tal que así:

Y ya con el tubo del aire metido y todo:

Si queréis ver el resultado final (probando con una taza con agua, que no voy a esperar que se llene el bidón este) os dejo aquí un video:

El repositorio con el código inicial podéis encontrarlo en: https://github.com/yoprogramo/waterlevel

Cómo usar raspberry pi pico con PlatformIO

Hace tiempo que trasteo con varias placas de desarrollo que intento integrar en distintos proyectos de IoT, la mayoría relacionados con nomorekeys, el caso es que generalmente he utilizado arduino (el pro micro es mi favorito) o el ESP32 cuando necesito wifi (antes el ESP8266). Hay muchos otros por ahi, como el seeeduino xiao y muchos de Nordic.

Raspberry pi pico pinout

El caso es que tengo mucho código escrito en C/C++ para el entorno Arduino y, resulta, que puedo reutilizarlo en distintos procesadores utilizando un plugin para visual studio code llamado PlatformIO, lo que he ido haciendo para los distintos arduino, ESP y Seeeduino, Hace relativamente poco tiempo me decidí a probar el nuevo MCU de Raspberry, el Raspberry Pi Pico, pero me centre en usarlo con CircuitPython, teniendo unos resultados excelentes y que os recomiendo probar.

Llegado el caso necesité más memoria para un proyecto que inicialmente estaba codificado para arduino pro micro ya que este solo posee 2,5k de memoria RAM y se quedaba muy corta. Revisando lo que tenía por casa me di cuenta que tenía un par de rpi pico por ahí de las pruebas con python y me di cuenta que tenían 264k de memoria (x100 lo que tiene un arduino), tampoco andan mal de precio y tienen todos los pines de entrada salida que necesitaba, así que, manos a la obra… Vamos a ver si podemos adaptar el código de arduino a la pico… Usando PlatformIO.

No voy a entrar ahora mismo ni en cómo instalar platformio ni en como crear un proyecto, eso os lo dejo para vosotros o si me lo pedís lo esccribo más adelante, por ahora partiremos de que eso ya lo has hecho.

Si ya tienes un proyecto hecho con platformio, enhorabuena, todos los cambios que tendrás que hacer es incluir esto en tu platformio.ini:

[env:pico]
platform = raspberrypi
board = pico
framework = arduino

Recuerda poner el #include <Arduino.h> si estás importando un sketch del ide de arduino y ya estaría…

La primera vez que quieras subir el código a la placa tendrás que copiar el archivo firmware.uf2 después de haber puesto en modo boot la placa (enchufala al ordenador pulsando el botón de la misma), yo lo hago con este comando (uso linux)

cp .pio/build/pico/firmware.uf2 /media/$USER/RPI-RP2/

Las siguientes veces ya no hará falta, puedes dar al botón upload directamente y el código compilado se subirá a la placa.

Happy coding!

Cómo dar de alta tu propia librería en el gestor de librerías de Arduino

De un tiempo a esta parte he estado haciendo cositas con microcontroladores y Ardunio (ultimamente con platformio), y el caso es que he querido dejar como librerías libres algunos de los códigos que he generado (o mejorado), como por ejemplo la librería para generar códigos QR en displays OLED o TFT (https://github.com/yoprogramo/QRcodeDisplay). La gestión de librerías en Arduino es una de las cosas que hacen el entorno interesante (en platformio mejor todavía) y es muy interesante tener disponible tu librería en el gestor de librerías de arduino.

Hasta hace poco el hacer que tu librería estuviese disponible para todo el mundo en el gestor de librerías de Arduino IDE era una tarea muy «manual», básicamente poner un mensaje en los foros de soporte de Arduino y esperar por la respuesta… Esto ha cambiado un poquito y podemos encontrar un nuevo método aquí:

https://github.com/arduino/library-registry#readme

Os lo resumo para los que seais impacientes. Los pasos una vez que tengáis el código de la librería en github son los siguientes:

  • Crear una release en github y darle un número
  • Crear un archivo llamado library.properties con el número de versión y los datos correspondientes de las plataformas y dependencias
  • Abrir este enlace para hacer un fork del repositorio: https://github.com/arduino/library-registry/edit/main/repositories.txt
  • Hacer fork del repositorio
  • Editar el archivo repositories.txt añadiendo la url de github del repositorio
  • Hacer click en «Proponer cambios»
  • Crear un pull request

El pull request genera una petición para que un bot revise los cambios, si todo está ok la librería aparecerá al día siguiente en el gestor de arduino, si hay algún problema se puede poner un comentario mencionando a @ArduinoBot o hacer un commit con las modificaciones en el archivo (en la rama de nuestro fork).

Nuestra librería en el gestor de librerías de Arduino