Tordek http://tordek.me/feed/index.html Tordek blogspam@tordek.me 2020-11-29T00:00:00Z PlotBot http://tordek.me/posts/2020-11-29-biplotter/index.html 2020-11-29T00:00:00Z 2020-11-29T00:00:00Z Varias veces vi1 este tipo de plotters verticales donde el cursor es controlado por un par de correas sostenidas por motores y siempre quise uno. Así que lo armé.

Parte 1: ¿Cómo y por qué?

Todo empezó con una suposición: es suficiente con determinar la longitud de los segmentos. Un grafico más tarde tenía esto:

Inicialmente lo pensé como la intersección de dos círculos; recién después de un par de implementaciones me di cuenta que se podía simplificar a «la distancia del punto a los centros» (el modelo con dos círculos es más fácil de visualizar porque no hace falta calcular la intersección).

El modelo es menos que perfecto; si uno se pone a leer sobre el tema (cosa que no hice inicialmente) empiezan a aparecer problemas con la tensión de las cuerdas que hacen que se pierda precisión, y la inercia que causa rayas no deseadas… pero para dibujar monas chinas en un pizarrón no son un tema de vida o muerte.

Cinco minutos de matemática más tarde, arranqué Processing2 para iniciar un prototipo que me permita visualizar la idea: dos círculos en esquinas opuestas, y calculamos el radio de cada uno como la distancia de su centro al punto del mouse, resultando en una intersección:

PVector c1p = new PVector(0, 0);
PVector c2p = new PVector(800, 0);

void setup() {
  size(800, 600);
}

void draw() {
  PVector mousePos = new PVector(mouseX, mouseY);
  float c1r = mousePos.dist(c1p);
  float c2r = mousePos.dist(c2p);

  background(224);
  noFill();
  stroke(128);
  circle(c1p.x, c1p.y, 2 * c1r);
  circle(c2p.x, c2p.y, 2 * c2r);
  stroke(0);
  line(c1p.x, c1p.y, mouseX, mouseY);
  line(c2p.x, c2p.y, mouseX, mouseY);
}

Resulta que este sistema de coordenadas se llama «Bicéntrico»; una vez que encontré esto me simplificó un montón de ecuaciones. Lo único que hago de especial es correr y hacia abajo para que (0, 0) no quede entre los dos polos: en ese punto las cuerdas tendrían máxima tensión y un movimiento equivocado podría romper todo. Con esto tengo un poquito más de seguridad.

Armado con confianza poco merecida, avancé.

Parte 2: El espantapájaros y su cerebro.

Para esta receta necesitás:

  • Un Arduino Uno
  • Un shield Arduino CNC (o al menos un par de drivers de motor)
  • Dos motores paso a paso para controlar la posición (yo usé unos parecidos a estos)
  • Una forma de montar los motores (yo usé un par de piezas impresas en 3D)
  • Una cuerda, correa o similar, con un a forma de sujetarla a los motores (yo usé la correa de una persiana rodante, y otra pieza impresa)
  • Un marcador, lapicera, lápiz, o similar.
  • Una forma de montar el marcador a la cuerda (yo hice… cosas raras con un par de piezas descartadas de un intento anterior)

Opcional: - Un Servo, para controlar el marcador.

El código del Arduino es una traducción fiel (excepto por la clase PVector) del de Processing. El prototipo en Processing estaba escrito pensando en las limitaciones del Arduino: el motor no conoce su posición absoluta, y la cantidad de pasos en su movimiento depende de los engranajes del motor como del diámetro del eje3. Traté de mantener ambas versiones lo más similares que se podía: si ambas se comportan igual, es más fácil debuggear la que me deja ver exactamente dónde están las cosas. Muchos de estos factores son configurables, así si querés hacerlo más grande, más chico, o con menos precisión, se puede configurar.

La diferencia principal entre la versión de Arduino y la de Processing es que en la de Arduino en cada loop() se parsea la línea, se calcula el segmento, y se mueve el motor; mientras que la de Processing, como es más visual, divide la ejecución en varios pasos, para poder animarla.

Parte 2a: Do you speak-a my language?

Todo muy lindo con que el cursor se mueva, pero ¿cómo le digo a dónde? Afortunadeamente, como muchos de mis problemas, ya lo resolvieron 30 años de que nazca: G-code es un lenguaje usado en CNC para comunicarse con máquinas herramienta.

Cada línea tiene una serie de instrucciones que le dicen al puntero hacia dónde y cómo tiene que moverse. Hay un montón de instrucciones, pero la mayoría no me interesan. Acá sólo uso:

  • G00: Movimiento rápido; no importa el camino. En mi implementación resulta en una curva.
  • G01: Interpolación lineal. Mueve el cursor en pasos de longitud fija.
  • G02 y G03: Movimiento circular. Mueve el cursor circularmente en pasos de longitud fija.
  • X e Y: Posición (absoluta) del destino.
  • I y J: Posición (relativa) del centro del círculo.
  • R: Radio del círculo.
  • Z: No lo implementé aún, pero controla la profundidad del corte o, en mi caso, si el marcador escribe o no.

El modo de fallo es bastante generoso: mientras sea algo parseable, lo aceptamos; si es una instrucción que no conocemos, la ignoramos.

El paso siguiente fue implementar el parser de G-Code. Como sabía que al Arduino le iba a hablar mediante puerto serie, lo implementé para que consuma un caracter a la vez (aunque Arduino también me da un método readFloat() que me simplifica bocha):

  • Si es un %, lo ignoramos; indica el inicio del programa, pero es opcional.
  • Si es un (, descartamos hasta encontrar el ); son comentarios.
  • Si es un espacio, lo salteamos.
  • Si es la letra ‘G’, sólo me interesa si es 0, 1, 2, o 3; los demás modos no me importan (pero, si me importaran, por ejemplo, para cambiar a pulgadas, los implementaría acá).
  • Finalmente, cualquier otro caracter (que damos por asumido es una letra), simplemente almacenamos el valor.

Algunos valores son especiales y permanecen seteados (como el tipo de movimiento que se ejecuta); otros sólo nos importan para el comando actual (como el radio del círculo). Para hacerla fácil, el parser siempre mantiene los valores anteriores.

Parte 2b: One step at a time

Procesamos un poquito los valores obtenidos, y podemos hacer interpolación en pasos de longitud fija, cosa que se repite hasta que estemos suficientemente cerca del punto final.

Finalmente, para cada segmento de la interpolación hay que mover los motores, que lo hacemos con un algoritmo de dibujo de líneas bastante simple: Calculamos cuantos pasos tiene que dar cada motor con respecto al otro, y vamos moviendo un paso, revisando si estamos muy lejos de la inclinación requerida, y moviendo el otro cuando haga falta.

Aprovechamos también para calcular la distancia (en realidad, el cuadrado de la distancia, para ahorrar una raíz cuadrada en un bucle tan cerrado), y hacer una leve pausa (ajustada por la distancia) para fijar la velocidad del movimiento.

Acá sería un excelente lugar para poner un failsafe y parar la máquina si sale fuera del rango de impresión definido.

Enough talk

Funciona sorprendentemente bien para lo simple que es:

El path fue generado directamente por Inkscape.


  1. Remember, remember…↩︎

  2. Está muy lindo para prototipar cosas visuales sin mucho laburo de antemano. Un abrazo a Daniel Shiffman y The Coding Train.↩︎

  3. En una versión anterior había usado un piolín envuelto por el eje. Funcionó, pero el cambio del diámetro en el eje al envolverse el piolín era suficiente para agregar una distorsión enorme.↩︎

]]>
Sin Comentarios http://tordek.me/posts/2014-12-03-sin-comentarios/index.html 2014-12-03T00:00:00Z 2014-12-03T00:00:00Z Uno de los cambios que vinieron junto al nuevo sistema de blog (además de la misteriosa (?) desaparición de los posts viejos y vergonzosos) es que saqué los comentarios. No porque Hakyll no los soporte ni mucho menos; es más que fácil poner un Disqus al fondo y listo. Sino porque no agregan suficiente valor como para valer la pena.

La gente que se tomó el tiempo de comentar siempre me agregó algo útil; pero pelear con los spambots era un desperdicio.

Además, de esta manera, el que me quiere corregir, siempre puede escribir una respuesta en algún lado público, y me da más publicidad.

Yay.

]]>
REST http://tordek.me/posts/2014-12-01-rest/index.html 2014-12-01T00:00:00Z 2014-12-01T00:00:00Z Están muy de moda las API que dicen ser REST, pero en lo único que tienen son URLs lindas.

Pongamos algo en claro: tener URLs lindas no hace una API REST. Que sea HTTP no la hace REST. Que use los métodos que corresponden no la hace REST.

Si tenemos que reducir la verificación de si algo es REST al elemento más simple, podemos empezar por una pregunta: ¿Un recurso tiene links a los otros recursos?

Si la respuesta es “No”, entonces NO ES REST.

La API de Twitter no es REST. Supongamos que queremos ver un tweet particular. Para eso tenemos que hacer una request a

GET statuses/show/:id

Linda URL. Clara, directa… Debe ser REST, ¿no?

No.

¿De dónde salió ese ID? ¿Por qué me dio un ID en vez de una URL?

Antes de hacer la request del tweet hubo una request anterior para la autenticación del usuario. El último paso de la autenticación nos da varios valores; pero en muchos casos se usa como si sólo se pudieran mandar los relacionados a la autenticación. ¿Por qué no incluir datos del usuario? El más obvio es la URL del perfil (también podríamos incluir la URL de su página principal… y de ahí más links, por ejemplo, hacia cada uno de los tweets en su perfil). El ID igualmente lo teníamos que haber leído de alguno de esos lugares: de la misma lista de Tweets del perfil. En vez de solo poner un ID, ponemos un link al tweet.

Las URLs son los nuevos IDs

HATEOAS (Hypermedia As The Engine Of Application State) es uno de los pilares de REST. Nos dice que el Estado de la aplicación se altera mediante los vínculos provistos. Casi como si el recurso fuera un objeto en el cual llamamos a un método. Es el mismo objeto el que nos dice: “Para borrarme, mandá un DELETE a esta URL”, en vez de tener que encontrar un método de borrado al cual le pasamos un ID.

Pero, ¿por qué es tan malo tener que armar las URL?

  • Estamos multiplicando el trabajo:

    Si NO devolvemos un link…

    • El creador de la API parsea el link para generar el contenido.
    • El creador lo documenta “fuera de banda”
    • Cada cliente tiene que armar su propio link.

    Mientras que si el creador de la API nos pasa un link…

    • El creador de la API parsea el link
    • El creador de la API genera el link (típicamente usando el mismo router para las dos cosas)
  • Estamos exhibiendo valores innecesariamente. Las URLs bien armadas sin duda son lindas, pero un servicio REST podría funcionar perfectamente si todas las URLs apuntaran a UUIDs.

    Si en un sitio sabemos que la última respuesta que nos dieron está en .../respuestas/12345, es extremadamente fácil probar .../respuestas/12344 a ver si encontramos algo. En cambio, si estuviera en .../respuestas/021979ef-e183-4bb5-9dcf-28d4e0508c3f, ¿qué probamos?

    Parece una simple capa de seguridad por oscuridad (especialmente si usamos un caso levemente más realista donde el ID es aleatorio, pero corto), pero también ayuda a prevenir que se filtre cierta información, como “¿Cuantas transacciones hizo este usuario?” o “¿Cuantos usuarios tiene el sitio?”

  • La API sin links es más difícil de explorar. En la API con links, cada request nos apunta a las demás acciones que podemos tomar: “Tomá el Tweet. Si querés borrarlo, andá acá; si querés retweetearlo andá acá; si querés más detalles del usuario, andá acá”.

Por supuesto, los links no son el único factor; solo que son el que más veo abusado por ahí. Demasiadas APIs que dicen ser REST pero no respetan uno de los elementos centrales, que es el hypermedia.

Una API REST debe:

  • Publicar recursos (no llamadas a procedimientos).
  • Vinculados entre sí (links; no IDs).
  • Que se modifican utilizando los métodos apropiados (GET, POST, PUT, DELETE, etc.).

Si falla esas características no necesariamente es una “mala” API; solo que no es REST.

]]>
Rebuild. Rebrand. http://tordek.me/posts/2014-11-29-rebuild-rebrand/index.html 2014-11-29T00:00:00Z 2014-11-29T00:00:00Z TL;DR: Tengo blog nuevo. Probablemente se actualice igual de frecuentemente que el otro.

Está hecho con Hakyll, because Haskell.

También tengo URL nueva, porque es más corta que un .com.ar, y aún así es más barata.

Las entradas del blog anterior están… «en pausa». Quizá repase y vuelva a subir alguna; los logs de 404 me dirán si alguna rinde. Por ahora, se van.

Oh, I’m out there, Jerry, and I’m loving every minute of it

P.D.: No sé hacer diseños; escucho opiniones/quejas/insultos. Bootstrap hace el 90%, yo solo pinto de rojo algunas cosas.

]]>