Creo un proyecto con LibreCAD, creo la capa ”stroke”, y aquí metemos las polilíneas, tal como se ha explicado en el anterior artículo [1], y tal como se muestra en la imagen. Con lenguaje C++, lo primero que hacemos es leer el fichero dxf y detectar la capa stroke, con todos sus puntos. En el LibreCAD todas las líneas son iguales, tienen un origen y un final. Pero a mi me interesa detectar las polilíneas, que son las líneas enlazadas de manera que el punt final de una línea es el mismo que el punto de origen de la línea siguiente. Recordemos la estructura de datos que se utiliza:

struct linia {
int x0;
int y0;
int x1;
int y1;
double length;
};

struct polilinia {
std::vector vlinia;
double length;
};

std::vector vpolilinia;

Recorremos todas las líneas, y vamos añadiendo elementos al vector polilínea, y dentro de cada polilínea vamos añadiendo elementos al vector línea. Para cada línea y para cada polilínea, tenemos calculada su longitud. Una vez tenemos rellenado el vector de polilíneas con sus líneas, ya podemos recorrer todas las polilíneas y generar la salida G-Code, de manera que el inicio de una polilínea está a profundidad -2mm, y el final de la polilínea está a profundidad 0mm, tal como se aprecia en este trozo del G-Code resultante, y en la imagen:

(* POLILINE #2 *)
G0 X 30 Y 30
G0 Z 3.000
F150
G1 Z -2.0
F400
G1 X 50 Y 40 Z -1.11
G1 X 60 Y 30 Z -0.55
G1 X 70 Y 40 Z 0
F150
G0 Z 5.000

Como se aprecia en el G-Code, posicionamos la broca en el punto (30,30), agujereamos hasta -2.0mm, y vamos pasando por tres puntos sucesivos mientras vamos levantando de forma proporcional la broca hasta llegar al punto (70,40), punto en que la broca deja de atacar la superficie. Así conseguimos un trazo variable, tal como se aprecia en la imagen. Con esta técnica podremos perfilar detalles en los trabajos CNC, como pueden ser los cabellos de un retrato, o el trazo en una caligrafía.

Como se aprecia en la imagen (extraída del proyecto de fresar el retrato de Albert Einstein en blanco y negro, en otra entrada mostraremos el resultado final), se ve el resultado de aplicar el script cncstroke para las polilíneas agrupadas en la capa stroke del proyecto LibreCAD. Vemos dos trazos que acaban en punta (en la part izquierda de la imagen, sobre madera), y otro trazo, donde no se ha aplicado el script, que no acaba en punta. En la parte derecha de la imagen vemos un trozo del proyecto LibreCAD donde se han definido estas líneas (dos que pertenecen a la capa cncstroke, y otra que se fresa normal). Así pues, con un uso correcto del proyecto LibreCAD, podemos aumentar la resolución del fresado CNC, consiguiendo un efecto más real y detallado.

Puedes descargar y estudiar el código del script cncstroke desde el siguiente enlace [2] .
Referències:

Nota aclarativa: En el proyecto cncstroke utilizamos el término polilínea como sucesión de líneas enlazadas. Ahora bien, para generar estas líneas enlazadas, en LibreCAD, utilizamos la herramienta línea (y no la herramienta polilínea) (En una futura versión se estudiará la posibilidad de utilizar la herramienta polilínea, o ambas).

Uno de los trabajos típicos que se hacen con la CNC es el retrato de personajes en formato blanco y negro. Por ejemplo, son conocidos los retratos del Che Guevara o Albert Einstein. Estos días estoy trabajando con la imagen de Ovidi Montllor [1].

La idea es sencilla y conocida. Importo la imagen en LibreCAD [2] (el software CAD que utilizo, que es open source), defino la silueta, y defino unas áreas de hatching que seran fresades (rebajadas). Utilizo el programa dxf2gcode [3] para exportar a G-Code, y freso todo el trabajo a una profundidad de 1.5mm. La limitación que tengo es que la fresa que utilizo es de 2mm de diámetro, y esto limita la resolución final que pudo alcanzar.

Estoy programando un pequeño script (cncstroke [4]) para mejorar la resolución de mis trabajos. Imaginemos por ejemplo los cabellos de Albert Einstein. Lo ideal es que las mechas de cabello acaben en punta, haciendo un trazo que pase de grueso a fino. Esto se consigue haciendo que la profundidad de ataque de la broca sea variable, desde 1.5mm de profundidad en el inicio del trazo hasta 0mm al final del trazo. Pero para que el invento funcione, no he de utilizar una broca normal, sino una broca V-Shape (con punta de 40 grados). De esta manera conseguiré el efecto de hacer un trazo que pase de 2mm de ancho en el origen a 0 mm en el extremo. Esta técnica también será muy útil para fresar letras que simulen trazos variables.

La idea es que en els mis proyectos de LibreCAD tendré una capa con la silueta básica (que fresaré con una broca de 2mm), y una capa (que siempre nombraré stroke) que constará de polilíneas que representarán el trazo variable desde 2mm hasta a 0 mm de ancho. Con esta segunda capa podré hacer los detalles como cabellos o caligrafía de letras.

En este primer artículo sólo expongo la estructura de datos que me permitirá gestionar la generación del G-Code a partir del proyecto de LibreCAD. Cuando recorra (programáticamente) el contenido de un proyecto LibreCAD (formato dxf), tendré que buscar la definición de la capa stroke y de los puntos que contiene. Esto es relativamente fácil de hacer. Esta sucesión de puntos se pueden agrupar en polilíneas, entendiendo que una polilínea es un conjunto de líneas que van empalmadas.

Como el número de poliíneas es variable, y el número de líneas que contiene una polilínea también es variable, la mejor manera de programar esta estructura con lenguaje C++ es utilizar vectores.

struct linia {
int x0;
int y0;
int x1;
int y1;
double dist;
};

struct polilinia {
std::vector vlinia;
double dist;
};

std::vector vpolilinia;

Una línea consta de un punto origen y de un punto final (fácilmente se podrá calcular la longitud de esta línea). Una polilínea consta de un conjunto de líneas, y la longitud de la polilínea será la suma de les longitudes individuales.

A continuación mostramos el ejemplo mínimo de cómo se pueden gestionar diferentes polilíneas, cada una con un número variable de líneas.

// g++ -o polilinia polilinia.cpp
#include
#include
#include
#include
#include

struct linia {
int x0;
int y0;
int x1;
int y1;
double length;
};

struct polilinia {
std::vector vlinia;
double length;
};

std::vector vpolilinia;

using namespace std;

int main() {

double lengthlinia,lengthpolilinia;
polilinia polilinia1;
linia linia1;

lengthpolilinia=0;

linia1.x0=34;
linia1.y0=30;
linia1.x1=120;
linia1.y1=122;
lengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
linia1.length=lengthlinia;
lengthpolilinia += lengthlinia;

polilinia1.vlinia.push_back(linia1);

linia1.x0=320;
linia1.y0=322;
linia1.x1=420;
linia1.y1=422;
lengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
linia1.length=lengthlinia;
lengthpolilinia += lengthlinia;

polilinia1.vlinia.push_back(linia1);

polilinia1.length = lengthpolilinia;

vpolilinia.push_back(polilinia1);

//cout << vpolilinia.at(0).vlinia.size() << endl;

//anem per una altra polilinia
polilinia1.vlinia.clear();
lengthpolilinia=0;

linia1.x0=134;
linia1.y0=130;
linia1.x1=220;
linia1.y1=222;
lengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
linia1.length=lengthlinia;
lengthpolilinia += lengthlinia;

polilinia1.vlinia.push_back(linia1);

linia1.x0=220;
linia1.y0=222;
linia1.x1=320;
linia1.y1=322;
lengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
linia1.length=lengthlinia;
lengthpolilinia += lengthlinia;

polilinia1.vlinia.push_back(linia1);

linia1.x0=220;
linia1.y0=222;
linia1.x1=320;
linia1.y1=322;
lengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
linia1.length=lengthlinia;
lengthpolilinia += lengthlinia;

polilinia1.vlinia.push_back(linia1);

polilinia1.length = lengthpolilinia;

vpolilinia.push_back(polilinia1);

//cout << vpolilinia.at(1).vlinia.size() << endl;
//cout << vpolilinia.size() << endl;

//recorrem totes les polilínies
for (int i=0; i cout << "polilinia #" << i << ". length: " << vpolilinia.at(i).length << endl;
//dins de cada polilinia, recorrem les línies:
for (int j=0; j cout << "linia #" << j << ". length: " << vpolilinia.at(i).vlinia.at(j).length << endl;
}
}
}

I la sortida per pantalla:

$ ./polilinia
polilinia #0. length: 267.358
linia #0. length: 125.936
linia #1. length: 141.421
polilinia #1. length: 408.779
linia #0. length: 125.936
linia #1. length: 141.421
linia #2. length: 141.421

En el siguiente artículo [5] se explica la implementación del proyecto cncstroke para generar G-Code con z-depth variable.

Referencias:

Nota aclarativa: En el proyecto cncstroke utilizamos el término polilínea como sucesión de líneas enlazadas. Ahora bien, para generar estas líneas enlazadas, en LibreCAD, utilizamos la herramienta línea (y no la herramienta polilínea) (En una futura versión se estudiará la posibilidad de utilizar la herramienta polilínea, o ambas).

Estos días he estado perfeccionando cómo hacer un prototipo de PCB con la fresadora CNC.

Antes que nada he tenido que implementar el auto-probe para compensar la nivelación superficial de la placa donde quiero fresar. Este proyecto requiere la máxima precisión. El tema del auto-probe fue especialmente rápido y fácil de conseguir, cosa rara pues las cosas fáciles normalmente se complican de forma inesperada… Está documentado en [1].

Una vez el auto-probe está funcionando, ya puedo mirar de hacer las placas de doble cara. El principal reto está en girar la placa y que los agujeros en una capa y en la otra coincidan exactamente. Como se ve en la foto, esto se ha conseguido bastante bien, y la buena noticia es que hay margen para centrar bien los agujeros en la capa top (pues en la foto se ve que los agujeros no están exactamente centrados). Todo está bien documentado en el enlace [2]

Ahora que ya tengo el proceso bastante claro, ya puedo afrontar un pequeo proyecto que requiere una placa de doble cara. Consiste en montar un mini-ordenador, con finalidad puramente académica, con un microprocesador Z80 [3]. Pero antes de hacerlo, primero vendrá el programador de EEPROM [4], pues al ordenador se le ha de meter un programa en una EEPROM. No sé si todo estará listo antes de vacaciones.

En el pasado había hecho placas con el método UV y con el método de la plancha. Hay que decir que siempre he tenido dificultades varias en consegur placas con cualidad óptima. Ahora, con la CNC, estoy buscando una manera rápida de hacer prototipos. En gran medida creo que lo he conseguido. Ahora bien, el proceso, aunque satisfactorio, es más lento de lo que me pensaba.

Ya hemos acabado la extraescolar de Robótica al Balmes. Este año hemos programado el juego del Simon con una Raspberry Pi. Toda la documentación (y la programación didáctica) está en la wiki [1].

Durante estas 13 sesiones hemos ido trabajando sobre diferentes temas, que nos llevaban al objetivo final de hacer un prototipo de Simon:

  • Hemos programado el script con Python, viajando por las diferentes versiones a medida que necesitábamos incorporar nuevas funcionalidades.
  • Hemos conectado botones y LEDs a los pines GPIO de la RPi.
  • Hemos visto cómo, utilizando transistores, podemos disparar desde los pines GPIO señales de 12V para encender tiras de LEDs blancos.
  • Hemos discutido sobre la alimentación de todo el sistema, y alternativas.
  • Hemos discutido sobre cómo producir el sonido y su amplificación.
  • Hemos diseñado el mueble final, utilizando herramientas de fabricación digital como la impresora 3D y la fresadora CNC.

Al final ha sido un curso muy multidisciplinar, hemos tocado muchas cosas, aunque por falta de tiempo, conocimientos e infraestructura, muchas cosas las ha implementado el professor y las ha mostrado en clase. Pero al final la experiencia ha sido enriquecedora para todos.

El resultado final se puede ver en la foto y en este video:

Referencias:



Estos últimos días he revivido el ordenador Amstrad PCW8256 que teníamos en casa hacia el 1986. Lo utilizaba mi padre para escribir, pero también lo utilizábamos para los trabajos del instituto. Venía con una pantalla monocroma verde, el teclado y la impresora de agujas. Toda la electrónica estaba en el monitor, incluso la placa de la impresora. Y en el monitor también había la disketera de 3”, que tenía dos caras.

He instalado el Joyce en la Raspberry Pi, y me ha gustado revivir este sistema. Evidentmente, se ha de escoger la opción de letras verdes, simulando la pantalla de fósforo verde.

El ordenador venía con dos disketes, que eran discos de arranque. El primer era el CP/M, el sistema operativo, desde el que se podía acceder al intérprete de Basic (Mallard Basic). El segudo diskette, tambié bootable, era el Locoscript, que es el processador de textos con que venía. He hecho un documento con LocoScript sobre los ríos de Catalunya, simulando como si fuese un trabajo del insti.

También he jugado un rato al Tetris. El emulador emula muy bien la máquina original. Si quieres jugar al Tetris, has de hacer eject del disket inserido (el CP/M)(el SO ya está cargado en la RAM), y meter el disket del Tetris (Insert). Entonces, ya desde la línea de comandos, ya se puede llamar al ejecutable y jugar al Tetris, com se ve en la imagen.

Qué tiempos aquéllos! El link en la wiki:

Ya tengo prácticamente acabado el proyecto IoT con una REST Api [3][4] para leer la temperatura de un sensor DS18B20.

El problema (y grave) es que las temperaturas que estoy cogiendo del sensor son erróneas. Para saber cuál es la temperatura real en mi barrio (Gràcia-Barcelona) cojo como referencia la web de meteo.cat. La estación meteorológica automática más cercana es la de Zona Universitaria, aunque en realidad la temperatura no será la misma exactamente. La calle Joan Blanques, donde tengo el sensor, es estrecha y enmedio de la ciudad y del barrio; la Zona Universitaria es un espacio más ancho y abierto, posiblemente las temperaturas mínimas tiendan a ser más bajas…

Calibrar el sensor DS18B20 puede ser una tarea difícil. Aquí tenemos un artículo [1] muy interesante sobre cómo proceder para la calibración. El artículo es realmente interesante, pero no tengo el tiempo (ni las ganas) de implementarlo. Lo que sí que tendría que haber hecho, y esto era fácil, era comprar 10 sensores DS18B20 (son baratos), descartar aquéllos que se desvíen bastante de la temperatura real, y promediar la temperatura con el resto de sensores escogidos, por ejemplo 5. Esto no sería ningún problema, en la RPi tengo muchos pines GPIO disponibles.

Otra posibilidad es utilizar otro sensor de temperatura. Peo ejemplo, utilizar el BMP280, que además de la temperatura también tengo la presión y la humedad relativa (y es barato). Es muy interesante este artículo [2] donde se utiliza el BMP280 para construir una estación meteorológica alimentada per un panel solar.

La conclusión es que confiar en que el sensor que tengo dé la temperatura correcta es un grave error. El objetivo del proyecto es hacer un ejemplo didáctico de IoT, RestFul API, y gráficas como frontend. Esto ya está implementado, ahora sólo falta que la temperatura que leo sea real. Por tanto, lo que haré es encontrar una recta de regresión para aproximarme a la temperatura real a partir de los datos que me da mi sensor, suponiendo que la mejor aproximación sea una recta.

En la imagen se pueden ver dos gráficas. En la de arriba se ve la diferencia entre la temperatura real y la de misensor. En la de abajo se puede deducir la recta de regresión para convertir los datos del sensor en datos reales. De todas maneras, estos datos se corresponden a dos días y medio. Esperaré una semana más (y que suban las temperaturea del mes de mayo, y así tener más rango) para poder hacer una recta de regresió.

Enlaces:

Estoy montando un proyecto académico de Internet of Things. Se pretende medir la temperatura exterior en mi barrio con un sensor DS18B20 conectado a una Raspberry Pi Zero. Tal como se ve en la foto, he tenido que conectar un lápiz Wifi USB. Idealmente sería mejor hacer el proyecto con la nueva Raspberry Pi Zero W que acaba de salir hace un mes, y que lleva wifi incorporada.

La temperatura se va almacenando cada 10 minutos en una base de datos MongoDB. La parte más interesante del proyecto es programar una Restful API, y para hacerlo lo haré con Python y el microframework Flask, a partir de algún ejemplo que he encontrado (ver los enlaces). En la versión 1 sólo había un servicio, que era el bolcado de todas las temperaturas. La versión 2 ya hace más cosas: bolcar todas las temperaturas; recuperar la temperatura actual; filtrar por un día; filtrar por varios días al mismo tiempo.

Otra parte importante es la capa de presentación, y para ello utilizo la librería Highcharts.com, que da unos resultados muy espectaculares. Para cada función de la API se dará un ejemplo html, para ejemplificar cómo se pueden digerir los datos.

En las futuras versiones de la API se calcularán las medias de temperatura diarias, mensuales y anuales, de manera que se puedan hacer comparativas entre días, meses,… De momento todavía no está disponible.

La API todavía no es pública, estoy en fase de desarrollo. En el momento que sea estable y pública ya lo anunciaré. De momento la tengo dentro de casa, bajo los efectos del termostato de la calefacción, con lo que los resultados no son interesantes. Pero la idea es después de Semana Santa trasladarlo todo al local, el sensor en el exterior, y la Raspberry Pi dentro del local. Se podrá acceder a la API en el domini ohttp://joanqc.no-ip.biz/, pero todavía no está disponible.

La idea de desarrollar esta API es, a parte de que programar es divertido, poder utilitzar open data en formato JSON en la asignatura de Javascript (M06) en el ciclo de Desarrollo de Aplicaciones Web (DAW), en el instituto. Evidentmente, hay muchos ejemplos disponibles de open data y format JSON. Pero un ejemplo donde los alumnos vean de dónde salen realmente los datos, siempre será interesante.

En el primer enlace tenemos la documentación y discusión de toda la puesta a punto. Es el punto de partida principal si se quiere reproducir el proyecto. En el segundo enlace se documenta la API, en sus diferentes versiones.

Tenía pendiente desde octubre conseguir esta funcionalidad. Se trata de una máquina en que la operación está controlada por el tiempo. Una moneda son 15 minutos de juego. Los emuladores MAME y gngeo (NeoGeo) tienen el botón de COIN que permite dar créditos para jugar. Ahora bien, el monedero controla el contador de tiempo, y es necesario meter monedas para dar tiempo a las partidas. La comunicación entre el front-end y los dos emuladores se hace sencillamente con un fichero de texto donde se lee y se graba el valor del contador. La funcionalidad implementada es:

  • Cuando estoy en el front-end el tiempo no corre. Si se mete una moneda, suma el tiempo establecido (15 minutos por defecto).
  • Cuando entro en el emulador (mame4all o gngeo), se lee el valor del tiempo restante, y empieza a descontar el tiempo, que está siempre visible.
  • Desde el emulador puedo poner una moneda, suma el tiempo, y se actualiza el contador correctamente.
  • Cuando salimos del emulador y volvemos al frontend, el tiempo queda bien guardado.
  • Si mientras juego se acaba el tiempo, se vuelve al front-end, esperando la inserción de una nueva moneda.

Cambios en el código y documentación:

  • http://wiki.joanillo.org/index.php/M%C3%A1quina_Arcade_amb_monedes_i_limitaci%C3%B3_de_temps._MAME_i_neogeo#Introducci.C3.B3_2

Y el video:

Big Arcade

Big Arcade

Ya tengo acabada la nueva máquina de pie , que llamaré Big Arcade. El diseño viene directamente de un cliente que me hizo la petición (supongo que encontró las medidas por Internet), y realmente me gusta. En esta máquina se ha cuidado unos cuantos detalles. Concretamente, se ha puesto un monedero de Industrias Lorenzo, se ha cuidado el sonido, una TV de 21 pulgadas, vidrio protector, y se ha mejorado la colocación y los acabados de la marquesina.

En cuanto al monitor, se consiguió una TV de 21”, aunque sólo tiene una resolución de 640x480px. Si bien la resolución no es ninguna maravilla, al final me gusta estos pixels un poco gordos, y es un consuelo saber que puedo montar máquinas con esta resolución. En esta ocasión el cable HDMI to VGA no me ha dado problemas, y esto es un gran avance pues tengo una pista de por qué en anteriorse ocasiones había tenido tantos problemas con este tipo de cables.

Otra mejora que se ha hecho en esta máuina es poder cambiar el volumen apretando dos botones, una solución totalmente software, tanto en el front-end como en los emuladores. Esta es una solución buena pues ya no es preciso acceder al potenciómetro del amplificador de sonido.

En definitiva, una nueva máquina en que he implementado unas cuantas mejoras en la construcción y en los acabados. Una máquina para disfrutar construyéndola y jugándola.

Y el video:

El Buscaminas (Pescamines, Minesweeper) es uno de los 1001 videojuegos a que tienes que jugar antes de morir. Si tienes una cierta edad, seguro que te has hartado, porque el Buscaminas ha venido en las diferentes versiones de Windows hasta el Windows 7.

Existen diferentes versiones para jugar online. Concretamente, con los alumnos hemos cogido la versión en Javascript de Andrew D. Birrell, que ya tiene unos años, y la hemos actualizado en diferentes aspectos. Cuanto a Javascript, se ha generalizado a NxM celdas. Esto implica crear la tabla de forma dinmica (createElement), y también los eventos (addEventListener). Recordar que en el Buscaminas hemos de distinguir entre el botón izquierdo y el derecho del ratón.

Además, hemos añadido una capa de programación con PHP y MySQL. Ahora que estamos estudiando llamadas asíncronas con AJAX, hemos creado una tabla MySQL, y podemos registrar el usuario y hacer estadísticas de las mejores puntuaciones.

Puedes probar el Buscaminas en:

Page 1 of 1212345»...Last »