Category Archives: ¿Cómo se hace?

RFID Usando RDM6300 y Arduino 101, TinyTILE o ARDUINO UNO

7 Ago , 2017,
Jose Nunez
, , , ,
No Comments

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

El RDM6300 es un módulo bastante sencillo de utilizar en el entorno de ARDUINO gracias a la librería RDM6300 que se puede descargar de acá.

El módulo tiene tres conectores:

P1 (6 contactos): que contiene los pines para realizar comunicaciones. En este experimento utilizaremos solamente el pin 1 ubicado en la esquina que lo conectaremos al pin 6 del Arduino 101. Básicamente este es el único pin que se conecta al microcontrolador.

P2 (2 contactos) que es donde se conecta la antena (bobina)

P3 (3 contactos) que es donde se alimenta el sistema y donde se pude agregar un LED (todavía no tengo claro para qué exactamente) el punto es que de este solo utilizaremos el pin 2 y 3 que son +5V  y GND respectivamente.

El programa que utilizamos simula la apertura de un sistema mediante un pulso a un LED verde conectado al pin 3 de Arduino, y un pulso para cerrar simulado por un LED amarillo conectado al pin 2 del Arduino.

Algo interesante es que el Arduino 101 opera a 3.3V pero es tolerante a señales de 5V lo cual lo hace particularmente versatil (esto también aplica para TinyTILE. Con Arduino UNO también funciona.

Conexiones:

ARDUINO
PIN  2 ==> amarillo ====> LED Amarillo ===> Resistencia 150 Ohm ==> GND
PIN  3 ==> verde =======> LED Verde ======> Resistencia 150 Ohm ==> GND
PIN  6 <== café <======== PIN 1 del conector P1 del RDM6300 (TX)
PIN 13 ==> naranja =====> LED Verde ======> Resistencia 150 Ohm ==> GND
5V ======> rojo ========> Puerto + del protoboard
GND =====> negro =======> Puerto - del protoboard

RDM6300
PIN 2 del conector P3 (Vin) <===== Puerto + del protoboard (operando a 5V)
PIN 3 del conector P3 (GND) <===== Puerto - del protoboard

PIN 1 del conector P1 (TX) ======> PIN 6 del ARDUINO

Conector P2 =====< ANTENA (bobina)

 

Programa de ejemplo:

La versión oficial de este sketch se puede encontrar acá: https://github.com/janunezc/robotics/tree/master/rfid/rfid_rdm630_open_then_close

(!) También, será necesario que los archivos de la librería RDM6300 estén disponibles para compilar el sketch. Se puede copiar ambos archivos (rdm630.h y rdm630.cpp) en la misma carpeta del sketch, o en la carpeta de librerías del ARDUINO IDE (c:\user\myusername\documents\Arduino\libraries\rdm630\***). Es importante asegurarse que la librería RDM630 solo está copiada una vez en las diferentes rutas.

/*
 * Programa escrito por Jose Nunez como ejemplo didáctico del uso del RDM6300 con un ARDUINO 101.
 * Use bajo su propio riesgo.
 * Ejemplo de dominio público.
 *
*/
#include "rdm630.h"

rdm630 rfid(6, 0);  //TX-pin of RDM630 connected to Arduino pin 6
int led_yellow_close = 2;
int led_green_open = 3;
int led_signal = 13;
long OPEN_PULSE_WIDTH = 800;
long CLOSE_PULSE_WIDTH = 1000;
long OPEN_STATE_DURATION = 5000; 
long CODE_READ_DELAY = 10000;
long codeReadDelayMaxMillis = millis();

void setup()
{
    Serial.begin(115200);  // start serial to PC
    pinMode(led_yellow_close, OUTPUT);
    pinMode(led_green_open, OUTPUT);
    pinMode(led_signal, OUTPUT);
    rfid.begin();
    ledSignal(5,500);
    setMessage("READY!");
}

void loop()
{
  unsigned long rfidTagCode = 0;  
  
  if(rfid.available()){
    setMessage("RFID Data is available! reading it...");
    rfidTagCode = readRFIDCode();
    setMessage("Data: " + String(rfidTagCode));
    if(millis() > codeReadDelayMaxMillis) {
      if(rfidTagCode == 7598635) {
        setMessage("OPEN");
        openPulse();//This involves a closePulse() call inside the openPulse() function.
        codeReadDelayMaxMillis = millis() + CODE_READ_DELAY;
      } else {
        setMessage("I DONT KNOW YOU!");
        closePulse();
      }
    } else {
      setMessage("SkippingNewReads");
      delay(500);
    }   
  }
}

void openPulse(){
  setMessage("SENDING OPEN PULSE");
  digitalWrite(led_green_open, HIGH);
  delay(OPEN_PULSE_WIDTH);
  digitalWrite(led_green_open,LOW);
  setMessage("OPEN PULSE DONE! Waiting for OPEN STATE DURATION...");
  delay(OPEN_STATE_DURATION);
  setMessage("OPEN STATE COMPLETE");
  closePulse();
}

void closePulse(){
  setMessage("SENDING CLOSE PULSE");
  digitalWrite(led_yellow_close, HIGH);
  delay(CLOSE_PULSE_WIDTH);
  digitalWrite(led_yellow_close, LOW);
  setMessage("CLOSE PULSE DONE!");
}

unsigned long readRFIDCode(){
  byte data[6];
  byte length;

  rfid.getData(data,length);
  Serial.println("Data valid");
  for(int i=0;i<length;i++){
      Serial.print(data[i],HEX);
      Serial.print(" ");
  }
  Serial.println();
  //concatenate the bytes in the data array to one long which can be 
  //rendered as a decimal number
  unsigned long result = 
    ((unsigned long int)data[1]<<24) + 
    ((unsigned long int)data[2]<<16) + 
    ((unsigned long int)data[3]<<8) + 
    data[4];              
  Serial.print("decimal CardID: ");
  Serial.println(result);
  return result;  
}

void ledSignal(int times, int milliseconds){
  for(int i=0; i<times; i++){
    digitalWrite(led_signal, HIGH);
    delay(milliseconds);
    digitalWrite(led_signal, LOW);
    delay(milliseconds);
  }
}

void addToMessage(String message){
  setMessage(message, false);
}

void setMessage(String message){
  setMessage(message, true);
}

void setMessage(String message, bool newLine){
  String timeStamp = String(millis());
  String finalMessage = timeStamp + " - " + message;
  if(newLine){
    Serial.println(finalMessage);
  } else {
    Serial.print(message);
  }
}

Acá un ejemplo de la ejecución:

Espero que les sea de utilidad.

HTML – Resumen de 1 minuto

4 Ago , 2017,
Jose Nunez
, , , , , ,
No Comments

HTML son las siglas de Hyper-text Markup Language (Lenguaje de hiper-texto marcado). Es un lenguaje informático para describir un documento; y es una de las columnas vertebrales de la Internet moderna. Prácticamente todo el contenido que consumimos de la web se nos presenta usando HTML.

El formato HTML se basa en la definición de etiquetas (que describen partes de un documento) y atributos (que describen a las etiquetas)

Así por ejemplo la etiqueta <p> describe un párrafo en un documento y la etiqueta <div> describe una división de un documento.

Algunas etiquetas tienen incio y fin; por ejemplo la etiqueta de párrafo abre con<p> y cierra con </p>; de esta forma el contenido dentro de las etiquetas de apertura y cierre corresponde al contenido del párrafo.

<p>Este es un párrafo</p>

Algunas otras etiquetas son auto-cerradas. Por ejemplo la etiqueta <img> que define una imagen no tiene etiqueta “</img>” de cerrado sino que se declara así:

<img src="fotografia.jpg" />

La parte final de la etiqueta “/>” describe el auto-cerrado.

A su vez, en el ejemplo de la etiqueta “<img>” vemos también un ejemplo de lo que se conoce como atributo. La construcción src="fotografía.jpg" es un atributo que describe esa etiqueta <img>. Dice que se debe cargar un archivo denominado fotografía.jpg.

Existen algunos atributos que no tienen valor. Por ejemplo el atributo disabled.

<input type="text" value="algo de texto" disabled/>

En el ejemplo anterior se produce una caja de texto con el contenido “algo de texto” que aparece deshabilitada (en gris y no se puede cambiar el contenido).

Read More…

Documento Activo: ANGULAR 4 y TypeScript

1 Ago , 2017,
Jose Nunez
No Comments

1. Descripción

En este documento comparto mis anotaciones sobre cómo funciona Angular 4 y otros conceptos relacionados.


2. ¿Qué es una Aplicación Angular?

Una aplicación Angular es un programa o sistema construido mediante el framework Angular.

  1. Se compone de uno o más Módulos
  2. Cada módulo consite de uno o más Componentes que contienen:
    1. Metadata: Información sobre un componente
    2. Plantilla: La vista del componente (HTML y CSS)
    3. Clase: El controlador del componente (TypeScript)

3. CLI de Angular

Utilitario para generar proyectos Angular así como componentes y elementos en dichos proyectos. Implementa las mejores prácticas.

Instalación del utilitario CLI de Angular
npm install @angular/cli -g

https://cli.angular.io

https://github.com/angular/angular-cli/wiki


4. Creando una app Angular con Angular CLI

Una de las funciones del CLI de Angular es la creación de nuevos proyectos.

Creación de un proyecto con Angular CLI
ng new myProject

cd myProject

//Para abrir el proyecto usando Visual Studio Code:
code .   

//Para abrir el proyecto usando ATOM:
atom .

5. Anatomía de un proyecto Angular

Acá se describen las diferentes carpetas en que está estructurado un proyecto Angular
  1. /e2e/                 Carpeta para pruebas de tipo end-to-end
  2. /node_modules/        Paquetes de dependencias instalados con npm
  3. /src/app/             Código fuente del app. Incluye módulos y componentes
  4. /src/assets/          Activos reutilizables
  5. /src/environments/    Definiciones de ambientes
  6. /src/index.html       HTML principal del app
  7. /src/main.ts          TypeScript principal del app
  8. /src/polyfills.ts     Definiciones para la compatibilidad entre navegadores
  9. /src/styles.css       Estilos Generales
  10. /src/test.ts          Pruebas generales
  11. /src/tsconfig.app.json Configuración de TypeScript
  12. /src/tsconfig.spec.json Configuración de Pruebas con TypeScript
  13. /src/typings.d.ts     Tipos de dato personalizados en TypeScript

6. Anatomía de un módulo

Seguidamente se presenta el código de un módulo con comentarios sobre qué significa cada elemento
/*IMPORTS: Acá se incluyen las librerísa necesarias para que el componente pueda operar*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

/*Decorador @NgModule define la metadata correspondiente al módulo*/
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent] //Se incluye el componente AppComponent como parte del módulo.
})

/*Clase Principaldel Módulo. En este caso está vacía.*/
export class AppModule { }

7. Anatomía de un Componente

Aquí se presenta el código de un componente con comentarios sobre qué significa cada elemento
/*IMPORTS: Acá se incluyen las librerísa necesarias para que el componente pueda operar*/
import { Component } from '@angular/core';

/*Decorador @Component define metadata como la etiqueta que se usará para acceder al componente (selector) o los URL's a la plantilla o a los estilos.*/
@Component({ 
  selector: 'app-root', //Define la forma en que se puede hacer uso del componente. En este caso, mediante la etiqueta <app-root></app-root>
  templateUrl: './app.component.html', //Define cual plantilla HTML usará el componente.
  styleUrls: ['./app.component.css'] //Define cual archivo de estilos definirá la apariencia del componente
})

/*Clase principal del componente. Se exporta para hacerla accesible por parte de Angular y las vistas relacionadas.*/
export class AppComponent {
  title = 'app works!';
  sayHello() {
    alert("Hello");
  }
}

8. Ejecutando una App

El siguiente comando de consola ejecuta el app y abre (-o) un navegador para verla.

ng serve -o


9. Creando un Componente

Expande para ver cómo se crea un componente

El siguiente ejemplo genera un componente nuevo llamado Hello:

ng generate component Hello

El siguiente ejemplo solamente indica lo que se va a generar, pero no genera nada:

ng generate component Hello --dry-run

 


10. Haciendo Pruebas

Angular integra en sus proyectos la forma de desarrollar pruebas unitarias de código usando una metodología que se llama “Behavior Driven Development” o desarrollo basado en el comportamiento. Los archivos de tipo “*.spec.ts” en el proyecto definen este tipo de pruebas.

Expande para ver el comando para ejecutar las pruebas

ng test


11. Compilando la Aplicación

Mediante el proceso de compilación el código TypeScript es consolidado en archivos minificados de tipo JavaScript. A su vez el código HTML y CSS es también minificado y consolidado para ocupar la menor cantidad de espacio posible. El código es compilado en la carpeta ./dist dentro del proyecto.

Expande para ver el comando compilar la aplicación

ng build --prod

12. Notas sobre Data Binding

El proceso de “data binding” permite enviar datos desde el programa de un componente ( .ts ) hacia una vista o plantilla ( .html )

Para esto se supone que basta con agregar la directiva [(ngModel)]="datos" al código HTML de la plantilla donde “datos” es la variable definida en el archivo .ts del componente respectivo.

Ejemplo

Archivo hello.component.html

<p>
 {{greeting.toUpperCase()}}
</p>
<input type="text" [(ngModel)]="greeting" (keyup)="log($event)"/>

Archivo hello.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello',
templateUrl: './hello.component.html',
styleUrls: ['./hello.component.css']
})
export class HelloComponent implements OnInit {
greeting: string;
constructor() {
this.greeting = "Hello Joselito!";
}

log(event){
console.log(event);
}

ngOnInit() {
}
}

 

Nota Correctiva (Expande para leer más)

(!) Es importante destacar que para que funcione la directiva ngModel se debe asegurar que el archivo app.module.ts contenga la referencia a FormsModule y que esta sea inyectada dentro del descriptor de metadatos @NgModule tal y como se ejemplifica seguidamente.

 


 

Algunas buenas prácticas para programar

26 Jul , 2017,
Jose Nunez
, , , , , , ,
No Comments

Lineamientos Generales para todos los lenguajes

  1. Los métodos y variables privadas comienzan con minúscula y se escriben en formato “camelCase”
  2. Los métodos, variables y propeidades públicas comienzan con mayúscula y se escriben igual en formato “Camel Case”
  3. Las constantes se escriben usando todas las letras en mayúscula y separándo palabras con “guión bajo”. Ej.: MI_CONSTANTE
  4. Haga declaración explícita de las variables locales
  5. Evite el uso de variables globales
  6. Las declaraciones de variables se agrupan al principio de clases y métodos. En ocasiones es adecuado declarar una variable pública justo antes de la declaración de un método que use esa variable de manera exclusiva o prioritaria.
  7. Está bien declarar variables que se usan exclusivamente en bucles iterativos (for-loops) en la declaración de dichos bucles.
  8. Inicialice las variables que declara, y escriba su lógica de manera que pueda procesar valores “undefined/falsy/null”
  9. Evite el uso de raya baja o guiones en los nombres de métodos o variables. En casos excepcionales se puede usar raya baja para describir alguna situacion consistente a lo largo del proyecto. Por ejemplo es mejor escribir  var blueColor que  var blue_Color. Pero podría ser adecuado escribir  function testControllerInit_SuccessCase()
  10. Como convención de nombramiento, use verbos para los métodos y sustativos para las propiedades.
  11. Evite el uso de valores literales “hard0-coded” en diversos lugares. Hágalos reusables en constantes.
  12. Los operadores lucen mejor con espacios al rededor. Por ejemplo es mejor escribir  var x = 10 + 10;  que  var x=10+10;
  13. La indentación es primordial. Muchos editores de código traen herramientas para indentar adecuadamente el código.
    Good tools for code auto-format are Netbeans, Atom and Visual Studio Code.
  14. Utilice las herramientas de análisis estático de códigopara encontrar variables no utilizadas, métodos demasiado largos y errores de sintaxis. NetBeans provee herramientas muy útiles para lenguajes como JS, CSS, PHP, TypeScript, C++ y HTML.
  15. Mantenga las líneas de código de una longitud menor a 80 caracteres.
  16. Mantenga los métodos de una longitud menor a 30 lines
  17. Mantenga las anidaciones a menos de 3 niveles
  18. No deje código comentado. Escriba comentarios para encontrar código legado en su lugar.
  19. Evite comentarios innecesarios y reiterativos como /*This function creates a user*/ para una función llamada “CreateUser(args)”
  20. Aproveche las facilidades de los ambientes de desarrollo para generar documentación a partir de comentarios estructurados. Especialmente para los Web API’s y las API’s en general.

JavaScript

  1. Nunca declare objetos de tipo Number, String or Boolean
  2. No use “new Object()“. Use var x = {} en su lugar.
  3. Recuerde que JS tiene un mecanismo automático de conversión de tipos
  4. Use === en vez de  == siempre que se pueda.
    1. En ocasiones es apropiado usar  == cuando el tipo de los objetos no se ocnoce de manera predecible/estable. Sin embargo la situación de manejar objetos de tipo desconocido o impredecible debe ser evitada también.
  5. Ponga atención a los parámetros que adoptan una “definición” undefined cuando no se proveen en una llamada a una función y actúe consecuentemente.
  6. Termine los bloques “switch” con “defaults
  7. Nunca use eval()
  8. Use nombres de variable significativos comocuentaor valorPrevio en lugar de  c or vp
  9. Es apropiado utilizar funciones anónimas cuando estas serán llamadas solamente desde un mismo lugar. En caso contrario es necesario extraer las funciones con un nombre apropiado en el entorno (scope) correcto.
  10. No deje sentencias de tipo “debugging” tales como console.log en el código del ambiente productivo.
  11. En JavaScript, iniciar una declaración de bloque con una llave “{” en la misma línea de la declaración es correcto:var constants = {
       COLOR_RED: "#FF0000",
       COLOR_GREEN: "#00FF00",
       COLOR_BLUE: "#0000FF"
    };
  12. Referencias:
    1. https://www.w3schools.com/js/js_best_practices.asp
    2. https://www.w3schools.com/js/js_mistakes.asp
    3. https://www.w3schools.com/js/js_performance.asp

HTML

  1. Utilice etiquetas en minúscula de manera consistente. Use <div> en lugar de  <DIV>
  2. Asegúrese de cerrar todas las etiquetas HTML de manera apropiada. Por ejemplo,  <div> debe cerrar con una etiqueta  de cierre </div> mientras que <hr> luce mejor si se cierra de manera individual <hr />
  3. Utilice minúsculas para los nombres de atributo. Also, use camelCase format for ID’s of DOM elements. I.e.: <div id="myDiv">...</div>
  4. Utilice guiones para separar palabras en los nombres de las clases CSS: <div id="myDiv" class="jumbotron-alternative">...</div>
  5. Use comillas dobles para declarar los valores de atributos HTML: I.e.:<div id="myDiv" class="jumbotron-alternative">...</div>
  6. Use las etiquetas <table> apropiadamente para elementos tabulares no para generar estilos o layout.
  7. No agregue espacios entre los nombres de atributos, símbolo de igualdad y los valores de atributos.
    This is good<link rel="stylesheet" href="styles.css">
    This is bad: <link rel = "stylesheet" href = "styles.css">
  8. Utilice indentación apropiadamente, especialmente con elementos jerárquicos como listas, tablas, entradas de selección.
  9. Evite usar espacios en blanco, líneas en blanco o indentación innecesaria.
  10. Use comentarios de manera apropiada:
    Good for short comments:     <!-- This is a comment -->
    Good for longer comments:
    <!-- 
      This is a long comment example. This is a long comment example.
      This is a long comment example. This is a long comment example.
    -->
  11. Las definiciones de estilo van en un archivo separado, no en el código HTML. Nunca use estilos “en-línea”
  12. Coloque las referencias a archivos JavaScript al final del código HTML
  13. Valide su código HTML por medio de análisis estático. Netbeans tiene muy buenas herramientas de análisis estático para HTML.
  14. Utilice nombres con significado para variables y reglas de estilo/clases.
  15. Referencias:
    1. https://www.w3schools.com/html/html5_syntax.asp

CSS / SCSS

  1. Los estilos van en archivos separados del código HTML.
  2. Es apropiado escribir reglas cortas CSS en una sola linea.
  3. Las reglas más largas (que involucran multiples configuraciones) deben hacerse en modo multi-línea.
  4. Nótese que CSS suporta comentarios
  5. Referencias:
    1. https://code.tutsplus.com/tutorials/30-css-best-practices-for-beginners–net-6741

AngularJS Specific

  1. Organice las carpetas y archivos en una estructura basada en componentes.
  2. Remueva las dependencias no usadas en los controladores. Netbeans tiene buenas herramientas de análisis de código para detectar código no utilizado.
  3. Es apropiado usar funciones anónimas en controladores si estas serán llamadas desde un solo lugar. De lo contrario, es mejor agregar una función no-anónima y hacer las llamadas respectivas por medio de dicha función.
  4. Se recomienda usar notación de arreglo al instanciar controladores.
  5. Mantenga la lógica de presentación fuera de los controladores. Los controladores manipulan e modelo de datos y las vistas son quienes reaccionan a los cambios realizados en esos modelos. Los controladores no deben realizar llamadas de tipo document.getElementByID para encontrar elementos del DOM en una vista.
  6. Referencias:
    1. https://dzone.com/articles/angularjs-coding-best
    2. https://www.upwork.com/hiring/development/angularjs-best-practices/

C# and .Net

  1. Haga uso extensivo y prudente del registro de eventos de manera que se pueda habilitar solución expedita de problemas en ambientes productivos.
  2. WIP

SQL Server

  1. Tenga cuidado del acople y la cohesión al desarrollar Procedimientos Almacenados.
  2. Use Procedimientos Almacenados y/o vistas cuando esto mejore de manera significativa los procesos de acceso a datos. Especialmente consultas de múltiples tablas.
  3. WIP

TypeScript

  1. Una buena referencia a TypeScript: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines

Usando la memoria FLASH del Intel Curie

24 Jul , 2017,
Jose Nunez
, ,
No Comments

Hola a todos.

Este es un tópico un poco más detallado.

El siguiente Sketch que pueden descargar de mi Github permite registrar datos en la memoria FLASH (persistente) del Intel Curie, buscarlos y eliminarlos.

Espero que el código sea bastante legible para todos.

Esta es una versión corregida del código original. Resulta que la librería SerialFlash no permite reutilizar porciones de memoria, así que después de varias operaciones creando y removiendo el archivo la memoria se acaba y se hace necesario borrar toda la flash (proceso que toma 1 segundo). Puede observarse en la línea 373 cómo se realiza este borrado cuando la creación “rápida” del archivo falla.

Acá implemento varias técnicas de “debugging” tales como parpadeos informativos, mensajes de consola estructurados y “pruebas unitarias” así como funciones de alcance y tamaño limitado.

Acá se resuelven  varios problemas, incluyendo aquello de ¿Cómo convierto un String a un const char*?  Bueno, no necesariamente una conversión, sino una implementación que es soportada por la función que estaba tratando de llamar. (ver linea 319)

En resumen, acá implemento funciones para crear un archivo en la memoria flash, con un formato predefinido. Leer el contenido de ese archivo y borrar el archivo.

Luego tambien implemento funciones para buscar contenido en el archivo y para eliminar contenido del archivo.

A su vez hay funciones para verificar el formato del archivo y realizar algunas pruebas unitarias.

El caso de uso es el almacenamiento de datos pequeños (códigos de acceso por ejemplo) que se puedan agregar, buscar o eliminar.

Capas:

  1. Acceso a Datos: Manejo de la memoria flash con archivos (crear archivo, guardar contenido, obtener contenido, eliminar archivo)
  2. Negocio: Agregar llave, eliminar llave, buscar llave.
  3. Desarrollo: diferentes pruebas y verificaciones.

Son una veintena de funciones pequeñas. Cada una hace lo suyo de manera muy explícita. Espero que les sea de utilidad.


// Escrito por José Núñez como un ejemplo didáctico para el dominio público.
// Utiliza la librería SerialFlash de Paul Stoffregen.
// Dicha librería se puede descargar de https://github.com/PaulStoffregen/SerialFlash

//Abstracción de la librería SerialFlash para el controlador Intel Curie
#include 
#include 

#define FSIZE 256 //Tamaño predefinido del archivo
#define ledPin 13 //PIN para un LED 
#define buttonPin 10 //PIN para leer un botón. Debe estar "pull-up" y el botón realiza una conexión a tierra.

const char *filename = "rfidble.txt"; //Nombre del archivo
#define CONTENT_SIZE 153 //Tamaño predefinido del contenido del archivo para este ejemplo.

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  
  parpadear (5, 1000);

  debugMessage("Setup() BEGIN!");

  parpadear (3, 500);
  
  // Inicializar operación de la librería SerialFlash
  if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) {
    debugMessage("Setup() Unable to access SPI Flash chip");
  }

  debugMessage("Setup() DONE!");
}
/*------*/
/* LOOP */
/*------*/
bool executedOnce = false;
bool addKeyMode = false;
void loop() {
  if (!executedOnce) {
    debugMessage("loop() executing... reading file...");
    String fileContent = readFile();

    debugMessage("loop() file read: |" + fileContent + "| (" + String(fileContent.length()));

    debugMessage("loop() doing file verifications...");
    if (!verifyBegin(fileContent) || !verifyEnd(fileContent) || !verifySearch() || ! verifySize(fileContent)) {
      debugMessage("loop() Verifications Failed. Reformatting...");
      initFile();
      executedOnce = false;
    } else {
      debugMessage("loop() verifications ran OK");

      debugMessage("loop() testing key addition");
      test_addKey();

      debugMessage("loop() testing key removal");
      test_removeKey();
      executedOnce = true;
    }
  }
  
  processButtonCase();

}

void processButtonCase() {
  heartBeat();

  checkButton();

  if (addKeyMode) {
    addKey("1234");
    addKey("5678");
    addKey("91011");
    addKey("121314");
    addKey("151617");
    addKeyMode = false;
  }
}

unsigned long nextHeartBeat = 0;
void heartBeat() {
  if (millis() > nextHeartBeat) {
    debugMessage("Heartbeat!");
    parpadear (2, 200);
    nextHeartBeat = millis() + 2000;
  }
}

/******************************
   INTERACTION LAYER
 ******************************/

bool didResetMode = false;
void checkButton() {
  if (digitalRead(buttonPin) == LOW) {
    debugMessage("checkButton() Button is low!");
    long lowDetectionMillis = millis();

    while (digitalRead(buttonPin) == LOW) {
      long timeInLow = millis() - lowDetectionMillis;
      if (timeInLow > 3000) {
        debugMessage("checkButton() button was low for more than 3 seconds! Initializing File...");
        initFile();
        didResetMode = true;
        break;
      }
    }
    if (!didResetMode) {
      debugMessage("checkButton() Switching to addKey mode...");
      addKeyMode = true;
    }

    didResetMode = false;
  }
}

/*****************************
   DEBUG LAYER
 *****************************/
void test_addKey() {
  debugMessage("test_addKey() BEGIN!");
  String newKey = "2097876";
  debugMessage("test_addKey() Key to add: " + newKey);

  bool newKeyAlreadyFound = findKey(newKey);
  if (newKeyAlreadyFound) {
    debugMessage("test_addKey() WRONG: Key was not expected, but found. " + newKey);
  } else {
    debugMessage("test_addKey() CORRECT: New Key was not found, as expected. " + newKey);
  }

  debugMessage("test_addKey() Calling addKey()!");
  addKey(newKey);

  debugMessage("test_addKey() verifying addKey() results...");
  bool newKeyFound = findKey(newKey);
  if (newKeyFound) {
    debugMessage("test_addKey() CORRECT: New Key was found as expected. " + newKey);
  } else {
    debugMessage("test_addKey() WRONG: New Key was not found. " + newKey);
  }
}

void test_removeKey() {
  debugMessage("test_removeKey() BEGIN!");
  String keyToRemove = "2097876";
  debugMessage("test_removeKey() Key to remove: " + keyToRemove);

  bool keyToRemovePresent = findKey(keyToRemove);
  if (keyToRemovePresent) {
    debugMessage("test_removeKey() CORRECT: Key to remove is present as expected. " + keyToRemove);
  } else {
    debugMessage("test_removeKey() WRONG: Key to remove is not found!!!. " + keyToRemove);
  }

  debugMessage("test_removeKey() calling remove key...");
  removeKey(keyToRemove);

  if (findKey(keyToRemove)) {
    debugMessage("test_removeKey() WRONG: Key to remove is present. NOT EXPECTED!!!! " + keyToRemove);
  } else {
    debugMessage("test_removeKey() CORRECT: Key to remove not present as expected.");
  }

}
void parpadear(int times, int milliseconds) {
  for (int i = 0; i < times; i++) {
    digitalWrite(ledPin, HIGH);
    delay(milliseconds);
    digitalWrite(ledPin, LOW);
    delay(milliseconds);
  }
}

void debugMessageNoLF(String message) {
  Serial.print (message);
}

void debugMessage(String message) {
  long curMillis = millis();
  Serial.print (curMillis);
  Serial.print (": ");
  Serial.println (message);
}

bool verifyBegin(String fileContent) {
  debugMessage("verifyBegin() BEGIN!");
  if (fileContent.startsWith("FILE_BEGIN_OK")) {
    debugMessage("verifyBegin() CORRECT: File begin is OK");
    return true;
  } else {
    debugMessage("verifyBegin() WRONG: File begin is NOT AS EXPECTED");
    return false;
  }
}
bool verifyEnd(String fileContent) {
  debugMessage("verifyEnd() BEGIN!");
  if (fileContent.endsWith("FILE_END_OK")) {
    debugMessage("verifyEnd() Correct: End of file is as expected");
    return true;
  } else {
    debugMessage("verifyEnd() WRONG: End of file is NOT AS EXPECTED");
    return false;
  }
}

bool verifySize(String fileContent) {
  debugMessage("verifySize() BEGIN!");
  if (fileContent.length() == CONTENT_SIZE) {
    debugMessage("verifySize() CORRECT!");
    return true;
  } else {
    debugMessage("verifySize() WRONG!");
    return false;
  }
}
bool verifySearch() {
  debugMessage("verifySearch() BEGIN!");
  bool k1found = findKey("7598635");
  bool k2found = findKey("notfoundhere");

  if (k1found && ! k2found) {
    debugMessage("verifySearch() SEARCH IS OK!");
    return true;
  } else {
    debugMessage("verifySearch() ERROR!!!! SEARCH IS WRONG!!");
    return false;
  }
}


/******************************************
   BUSINESS LAYER
 ******************************************/

bool findKey(String key) {
  debugMessage("findKey(): " + key);
  String formattedKey = formatKey(key);
  String fileContent = readFile();
  int pos = fileContent.indexOf(formattedKey);
  if (pos > 0) {
    debugMessage("findKey() FOUND: " + key + " at " + String(pos));
    return true;
  } else {
    debugMessage("findKey() Not found: " + key);
    return false;
  }
}

void addKey (String key) {
  debugMessage("AddKey() BEGIN!");
  String fileContent = readFile();
  if (findKey(key) == true) {
    debugMessage("AddKey() Key already stored!");
    return; //Already there
  } else {
    int pos = fileContent.indexOf(",----------");
    String beginning = fileContent.substring(0, pos);
    String thisKey = formatKey(key);
    String restOfFile = fileContent.substring(pos + 11);
    String newFileContent = beginning + thisKey + restOfFile;
    debugMessage("OLD FILE:" + fileContent);
    debugMessage("NEW FILE:" + newFileContent);
    saveFile(newFileContent);
  }
}

String formatKey(String key) {

  int dashesRequired = 10 - key.length();
  String result = "";
  for (int i = 0; i < dashesRequired; i++) {
    result = result + "-";
  }
  return "," + key + result;
}
void removeKey(String key) {
  debugMessage("removeKey() BEGIN!");
  String fileContent = readFile();
  if (findKey(key) == false) {
    debugMessage("removeKey() key not present!");
    return; //Already not there
  } else {
    String formattedKey = formatKey(key);
    debugMessage("removeKey() Formatted Key: |" + formattedKey + "|");
    int pos = fileContent.indexOf(formattedKey);
    debugMessage("removeKey() Index of Formatted key: " + String(pos));
    if (pos > 0) {
      String beginning = fileContent.substring(0, pos);
      String thisKey = formatKey("");
      String restOfFile = fileContent.substring(pos + 11);
      String newFileContent = beginning + thisKey + restOfFile;
      debugMessage("removeKey() OLD FILE:" + fileContent);
      debugMessage("removeKey() NEW FILE:" + newFileContent);
      saveFile(newFileContent);
    } else {
      debugMessage("removeKey() Formatted Key not found! " + formattedKey);
    }
  }

}

/**
   This function initializes file with raw content.
*/
void initFile() {
  debugMessage("initFile() BEGIN!");

  String fileContent = "FILE_BEGIN_OK:123455,7598635---,----------,----------,----------,----------,----------,----------,----------,----------,----------,----------,FILE_END_OK";
  saveFile(fileContent);

  debugMessage("initFile() DONE!");
}


/********************
   DATA ACCESS LAYER
 ********************/

/**
   This function reads file content into an string. Implements full debugging mechanism
*/
String readFile() {
  debugMessage("readFile(): Begin!");

  debugMessage("readFile() Opening file: " + String(filename));
  SerialFlashFile file = SerialFlash.open(filename);

  // Get the size and position
  uint32_t fsize = file.size();
  debugMessage("readFile() Size: " + String(fsize));

  uint32_t filePos = file.position();
  debugMessage("readFile() Position: " + String(filePos));

  debugMessage("readFile() Seeking...");
  file.seek(filePos);

  debugMessage("readFile() Reading buffer...");
  char mybuff[fsize];
  file.read(mybuff, fsize);
  String result = String(mybuff);
  debugMessage("readFile() Closing file...");
  file.close();

  debugMessage("readFile() Content:");
  debugMessageNoLF("|||");
  debugMessageNoLF(result);
  debugMessageNoLF("|||");
  debugMessage("Size:" + String(result.length()));

  debugMessage("readFile() DONE!");
  return result;
}

/**
   This function drops existing file and saves a new one with proposed content.
*/
void saveFile(String newFileContent) {
  debugMessage("saveFile() BEGIN!");

  removeFile();

  uint8_t flashBuffer[CONTENT_SIZE + 1];
  newFileContent.getBytes(flashBuffer, CONTENT_SIZE + 1);
  SerialFlashFile file;


  debugMessage("saveFile() Creating file if not exist...");
  // Create the file if it doesn't exist
  while (!create_if_not_exists(filename)) {
    debugMessage("Memory filled up! Clearing Flash...");
    SerialFlash.eraseAll();

    while (SerialFlash.ready() == false) {
      // wait, 30 seconds to 2 minutes for most chips
    }
  }

  debugMessage("saveFile() Opening file for write!");
  // Open the file and write test data
  file = SerialFlash.open(filename);

  debugMessage("saveFile() Writing into file...!");
  file.write(flashBuffer, CONTENT_SIZE + 1 );
  debugMessage("saveFile() String \"" + String(newFileContent) + "\" written to file " + String(filename));

  debugMessage("saveFile() Closing file...");

  file.close();

  debugMessage("saveFile() DONE!");

}

/**
   This function drops the file.
*/
void removeFile() {
  debugMessage("removeFile() BEGIN!");

  if (SerialFlash.exists(filename)) {
    debugMessage("removeFile() Removing file...");
    SerialFlash.remove(filename);
  } else {
    debugMessage("removeFile() No file to remove..." + String(filename));
  }
  parpadear (3, 50);

  debugMessage("removeFile() DONE!");
}

bool create_if_not_exists (const char *filename) {
  if (!SerialFlash.exists(filename)) {
    debugMessage("Creating file " + String(filename));
    return SerialFlash.create(filename, FSIZE);
  }

  debugMessage("File " + String(filename) + " already exists");
  return true;
}

Comandos por BLE – (Parte 2 de 2) – El Transmisor (TX)

22 Jul , 2017,
Jose Nunez
, , , , , ,
No Comments

Como lo prometido es deuda, acá está la 2da entrega referente a controlar cosas con telefonos celulares mediante Bluetooth Low Energy (BLE) usando IONIC 3.

En esta segunda parte, implementaremos una App (Android y IOS) usando IONIC 3 que le envía comandos al micro-controlador mediante el sistema BLE del teléfono.

Para ver la Parte 1 haz clic acá

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

Programando el App

En resumen:

  1. IONIC 3 es un sistema para crear aplicaciones móviles usando tecnologías híbridas (cordova, angularjs 4, typescript, html, css, etc.)
  2. Necesitaremos configurar IONIC3 en nuestro computador; esto incluye NodeJS, NVM, NPM y IONIC.
  3. También necesitaremos un muy buen editor de código para TypeScript. Puedo recomendarles Visual Studio Code o Atom.
  4. Descargamos el código del app de ejemplo de mi repositorio de Github y lo abrimos con el IDE de ARDUINO.git clone https://github.com/janunezc/robotics.gitEl código se ubica en ~/robotics/intel_curie/ble_command_app/
  5. En el celular podemos utilizar el sistema Ionic View (descargado del App Store o Google Play) y previsualizar el app 03B50F88Las siguientes imágenes muestran la operación del app

 

Seguidamente mostramos las partes más importantes del código original del programa con los comentarios detallados sobre su funcionamiento. Es realmente sencillo; podemos resumirlo en:

  1. El tab “SCAN” implementa un proceso básico de búsqueda de dispositivos Bluetooth. No vamos a detallar mucho en este tutorial sobre ese tab.
  2. El tab “LED” implementa la búsqueda de un dispositivo específico y una vez que lo encuentra permite interactuar con el LED para encenderlo y apagarlo.
    1. Los componentes (tabs) de la aplicación IONIC3 se dividen en 3 elementos principales: una vista o view (led.html), un controlador (led.ts) y un modelo que corresponde a los datos que va a manejar la aplicación y que se ubican en el objeto this y sus propiedades públicas.
    2. El archivo led.html es realmente simple. Aparte de los párrafos de texto expicativo y títulos, posee un botón que tiene algunas características importantes de AngularJS como lo son el atributo [disabled] ligado a una variable del modelo denominada “ejecutandoComando”. Cuando la variable tiene un valor verdadero (true) el atributo disabled se activa y el botón se deshabilita. A su vez tiene una directiva (click) que ejecuta la función “Command()” del controlador.
    3. Una parte importante del archivo led.html es el último párrafo en la línea 11. Este utiliza una directiva *ngFor para iterar sobre la variable “messages” y mostrar cada mensaje como un párrafo ( <p> ) individual. Esto nos permite mostrar mensajes de aclaración conforme se ejecutan los procesos en el controlador.
  3. Exploremos ahora el archivo led.ts. Se trata de un script escrito en TypeScript. Un pseudo-lenguaje basado en JavaScript que permite una orientación a objetos más elaborada.
    1. Lo primero que se realiza en led.ts es importar las librerías necesarias. Dos de las más notorias serán BLE y ApplicationRef. La primera permite comunicación BLE, y la segunda permite manipular el framework de Angular para refrescar la pantalla cuando sea necesario.
    2. La siguiente parte super importante es el constructor, el cual implementa una serie de inyecciones de dependencia que vale la pena mencionar.
    3. Nótese la forma en que se inyectan en el constructor tanto la librería BLE como la librería de Angular ApplicationRef. En el constructor también se inicializan variables importantes tales como el ID de servicio y el ID de Característica que se van a manipular.
    4. Las funciones más interesantes en este archivo son:
      1. findDevice() que busca un dispositivo particular y una vez que lo encuentra cambia el modo de comandos para encender o apagar un LED. Para esto usa la funcion de BLE nativo denominada ble.scan()
      2. txData() que utiliza ble.connect y ble.write para conectarse al dispositivo y hacerle llegar un mensaje.
      3. SetMessage() tambien merece ser mecionado como la forma que tenemos de agregar mensajes de control que nos permitan entender cómo se está ejectuando el programa.

Espero que este artículo les sea de utilidad junto con el anterior para aprender a enviar comandos a un dispositivo BLE basado en Intel Curie, mediante el teléfono celular.

Los dejo con estos enlaces de utilidad:

  1. Aprender TypeScript: https://www.typescriptlang.org/docs/home.html
  2. Aprender Ionic 3: https://ionicframework.com/
  3. Aprender Angular 4: https://www.youtube.com/watch?v=kFTmoLm9Jwg

Que estén bien!


led.html

<ion-header>
   <ion-navbar>
   <ion-title>LED</ion-title>
   <p>Acá podemos operar el LED que está implementado en TinyTILE</p>
   <button ion-button [disabled]="ejecutandoComando" (click)="Command()">{{ComandoTXT}}</button>
</ion-navbar>
</ion-header>
<ion-content padding>
   <p>Histórico de Ejecución: ({{myCount}} | {{test}})</p>
   <hr />
   <p *ngFor="let message of messages">{{message}}</p>
</ion-content>

led.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { BLE } from '@ionic-native/ble';
import { ApplicationRef } from '@angular/core';

@Component({
  selector: 'page-led',
  templateUrl: 'led.html'
})

export class LEDPage {

  public messages = []; //Histórico de ejecución
  public ejecutandoComando = false;
  public BLE;
  public ComandoTXT = "";
  public Value = "";
  public TargetDevice;
  private appRef;

  /**
   * CONSTANTES que serán usadas a lo largo del controlador
   */
  public constants  = {
      DEVICE_NAME: "COMANDO LED",
      CMD_FIND_DEVICE:"FIND DEVICE",
      CMD_STOP_SCAN:"Escaneando... (Clic para parar)",
      CMD_TOGGLE_LED:"CAMBIAR LED",
      ON:"ON",
      OFF:"OFF"
    };

  /**
   * Constructor para el controlador. Todo comienza acá...
   */
  constructor(public navCtrl: NavController, private ble: BLE /*Librería BLE nativa*/, private applicationRef : ApplicationRef /*Librería ApplicationRef de Angular*/ ) {
    this.appRef = applicationRef; //Referencia a la aplicación ANGULAR
    this.SetMessage("Constructor: Iniciado!");
    this.BLE = ble; //Acceso a la librería BLE Nativa
    this.ComandoTXT = this.constants.CMD_FIND_DEVICE;
    this.Value = "ON"; //VALOR PARA ENVIAR
    this.TargetDevice = {}; //Dispositivo al cual conectarse. Inicialmente vacío... se llenará cuando se encuentre el dispositivo en el escaneo.
    this['service_id'] = "db938b80-f010-44b6-8aa9-1835adf9419a"; //Identificador del servicio BLE a conectarse
    this['characteristic_id'] = "9906064e-9bbe-4eba-b415-bbd223f7d3d9"; //Identificador de la característica BLE a conectarse
    this.SetMessage("Constructor: Finalizado!");
  }

  /**
   * This is triggered when the button is clicked.
   */
  public Command(){
    this.ejecutandoComando = true;
    this.SetMessage("Command() - Comando recibido!");
    this.SetMessage(this["ComandoTXT"]);
    this.SetMessage("Interpretando comando...");

    if(this['ComandoTXT']===this.constants.CMD_FIND_DEVICE) {
      this.SetMessage("Ejecutando Comando: FIND DEVICE");
      this['ComandoTXT'] = this.constants.CMD_STOP_SCAN;
      this.findDevice();

    } else if (this['ComandoTXT']===this.constants.CMD_STOP_SCAN) {
      this.SetMessage("Ejecutando Comando: STOP SCAN");
      clearInterval(this['intervalHandle']);
      this['ComandoTXT'] = this.constants.CMD_FIND_DEVICE;
      this.ejecutandoComando = false;

    } else if(this['ComandoTXT']=== this.constants.CMD_TOGGLE_LED){
      this.SetMessage("Ejecutando Comando: TOGGLE LED: " + this.Value);
      this.SetMessage("Acá es donde enviamos un valor al dispositivo BLE");
      this.txData();
    }
  }

  /**
   * This searches for DEVICE in the AIR using SCAN technique.
   */
  private findDevice(){
      this.SetMessage("FindDevice() - INICIO!");
      let  ble = this.BLE;

      this.SetMessage("FindDevice() - ENABLE!");
      ble.enable();

      this.SetMessage("FindDevice() - SET INTERVAL!");
      this['intervalHandle'] = setInterval(() => { // PROGRAMAR UN TIMER DE 2.1 SEGUNDOS
        this.SetMessage("INTERVAL: INICIO! LLAMANDO A BLE SCAN...");
        ble.scan([], 2 /*seconds (0) */).subscribe( data => { //REALIZAR SCAN BLE POR 2 SEGUNDOS
          this.SetMessage("BLE SCAN CALLBACK: " + data['id'] + ' | ' + data['name'] + ' | ' + data['rssi']);
          if(data['name']==this.constants.DEVICE_NAME){
            this.SetMessage(this.constants.DEVICE_NAME + " HA SIDO ENCONTRADO!!");
            clearInterval(this["intervalHandle"]);
            this.TargetDevice = data;
            this["ComandoTXT"] = this.constants.CMD_TOGGLE_LED;
            this.ejecutandoComando = false;
          }
          this.appRef.tick();
        });
      },2100);//FIN DE LA DEFINICIÓN DEL TIMER
      this.ejecutandoComando = false;
    }

  /**
   * Transmitir datos al dispositivo BLE
   */
  private txData(){
    this.SetMessage("txData(): INICIO! Llamando a BLE CONNECT...");

    let id = this.TargetDevice.id;
    this.SetMessage("ID DE DISPOSITIVO: " + id);

    this.ble.connect(id).subscribe(datos=>{
      this.SetMessage("BLE CONNECT CALLBACK: INICIO!. Llamando a BLE WRITE..." + this.Value);

      this.ble.write(this.TargetDevice.id, this['service_id'],this['characteristic_id'], this.StringToBytes(this.Value) ).then(()=>{
        this.SetMessage("BLE WRITE CALLBACK: INICIO! Cambiando valor... " + this.Value);
        if(this.Value==this.constants.ON){
          this.Value = this.constants.OFF;
        } else {
          this.Value = this.constants.ON;
        }
        this.SetMessage("Nuevo valor... " + this.Value);

        this.SetMessage("Llamando a BLE DISCONNECT...");
        this.ble.disconnect(id);
        this.ejecutandoComando = false;
        this.appRef.tick();
      },(error)=>{
        this.SetMessage("BLE Write ERROR!");
        this.SetMessage(error);
        this.SetMessage("Llamando a BLE DISCONNECT...");
        this.ble.disconnect(id);
        this.ejecutandoComando = false;
        this.appRef.tick();
      });
    },error=>{
      this.SetMessage("BLE Connect ERROR!");
      this.SetMessage(error.message);
      this.ejecutandoComando = false;
      this.appRef.tick();
    });
  }

  /**
   * Agrega mensajes de ejecución al histórico
   */
  public SetMessage(message){
    var count = this['messages'].length;
    message = count + ':' + message;
    this['messages'].unshift(message);

    /*
      ESTA LINEA ES IMPORTANTE PARA REFRESCAR LA PANTALLA CUANDO SE GENERAN EVENTOS
      FUERA DEL CONTROL DE ANGULAR. POR EJEMPLO CUANDO SE LLAMA A UN CALLBACK EN
      UN PROCESO DE BLE SCAN.
    */
    this.appRef.tick();
  }

  // ASCII only. Convierte el valor a escribir a un arreglo de BYTES
  public StringToBytes(string) {
     let array = new Uint8Array(string.length);
     for (let i = 0, l = string.length; i < l; i++) {
         array[i] = string.charCodeAt(i);
      }
      return array.buffer;
  }

  // ASCII only
  public BytesToString(buffer) {
      return String.fromCharCode.apply(null, new Uint8Array(buffer));
  }
}

Repetier Software para Printrbot Simple Metal

Jul , 2017,
Luis Diego Jimenez Sanchez
, , ,
No Comments

Que es Repetier?

Repetier es un software de impresion 3D gratuito que incluye algunos motores de slicing como Cura, Slic3r y Skeinforge.  Hasta el momento, llevo un mes usando Repetier para impresiones y me ha gustado bastante mas que Cura por los siguientes motivos:

Primer motivo: tengo control de la temperatura del extrusor en tiempo real durante la impresion.  En una impresión que estaba realizando de prueba, era una estructura tipo domo, a medida que la estructura iba avanzando hacia arriba, la impresión estaba saliendo “lagrimosa” o derretida y esto causaba que las paredes no se solidificaban bien.  Con Repetier, hicimos una disminución en tiempo real de la temperatura del extrusor de 210C a 204C y esto termino de solucionar el problema.

Segundo motivo: también se pueden controlar otros parametros como el abanico y temperatura de cama caliente.

Tercer motivo: cuando se esta imprimiendo, la interfaz de Repetier demuestra el tiempo que falta para terminar la impresión.

Finalmente, se permite programar diferentes scripts de codigo G, ya sea al inicio de la impresón o al final, una cuestión que permite personalizar cada impresion.  Hay un par de aspectos que aun me faltan explorar, como vigilar impresiones mediante el celular o establecer un host wifi con un raspberry pi por ejemplo.

Instalar Repetier y configuracion para Printrbot Simple Metal.

Repetier Host se puede descargar en el siguiente enlace.

Download now

El ajuste de los parametros que se establece en este artículo es para la versión de Repetier Host V2.0.1.

Una vez que se tenga, se puede configurar para muchas impresoras 3D, en este caso se muestra a continuación los parametros que hay que configurar para usar la Printrbot Simple Metal.

  1. Pagina de Inicio de Repetier.

La primera vez que se abra Repetier Host, se va ensenar la pantalla anterior.  El primer ajuste que se hará es donde dice “Printer Settings” en el lado derecho superior.  Se abre lo siguiente y se debe ajustar los parametros de acuerdo a las imagenes.  Conforme se vaya avanzando en el uso del software, se puede ir ajustando los parametros segun el criterio de cada uno.

2. Connection Settings (Ajustes de Conexion)

Aqui los primeros parametros que se ajustarán son los de la conexión de la impresora a la computadora.  El puerto puede variar segun la computadora.

3. Printer Settings. (Ajustes de Impresora)

Como se ha mencionado anteriormente, muchos de estos ajustes son recomendaciones generales para la Printrbot Simple Metal, pero segun la impresión y el criterio de la persona, se pueden cambiar.

4. Extruder Settings. (Ajustes de extrusor)

5. Printer Shape Settings. (Ajustes de tamano de impresora)

Después de los ajustes de la impresora, se sigue con los ajustes de configuración del Slicer, Cura en este caso.

6. Slicer Configuration.

7. Slicer Configuration. Parte 2: Perfiles de Impresión.

En la parte baja de la imagen en el paso 6 se llega al ajuste del paso 7, en esta instancia se pueden poner valores de las impresiones para la calidad de la impresión.

8.  Slicer Configuration. Parte 3: Perfiles de Estructura

9. Slicer Configuration. Parte 3: Perfiles de Estructura contd. 

10. Slicer Configuration. Parte 3: Perfiles de Estructura contd.

Una vez que se hayan hecho los ajustes anteriores, le daremos al software los scripts de inicio y fin de la impresion.  Destaco que los siguientes scripts fueron hechos por un amigo y fanatico de la impresion 3D, Luis Diego Maroto.  A continuación los scripts y como insertarlos en el software.

En la imagen del paso 6, hay un tab bajo Slicer Configuration que se llama G Codes.  Esta sección es donde se pueden personalizar lineas de código G para que a la hora de iniciar o terminar la impresión se haga algo según el usuario.  Para el caso de Start G Codes, se inserta el siguiente código de la manera como se observa en la imagen.

; Codigo de inicio de impresión
;
M107 ; Apagar el abanico del hotend.
M109 T0 S180; Precalentar el hotend hasta 180C.
G28 X0 Y0; Mover el hotend a XY Home.
G28 Z0; Mover el hotend a Z Home.

; G1 Z15 F{Z_TRAVEL_SPEED}
G90 ; Absolute positioning
M82 ; Extruder in absolute mode
G92 E0 ; Reset extruder position

G29; Calibrar la cama

; Wait for all used extruders to reach temperature

{IF_EXT0}M109 T0 S{TEMP0}

De la misma manera se inserta el codigo para la End G-Code.


; Default end code
G1 X0 Y0 ; Get extruder out of way. Uncomment to use!
M107 ; Turn off fan
; Disable all extruder
G91 ; Relative positioning
{IF_EXT0}T0
{IF_EXT0}G1 E-1 ; Reduce filament pressure
M104 T0 S0
G90 ; Absolute positioning
G92 E0 ; Reset extruder position
;M140 S0 ; Disable heated bed
M84 ; Turn steppers off

Para entender o personalizar scripts de G Code para las impresiones, se puede referenciar al siguiente enlace donde se encuentran comandos y significados de este lenguaje.

http://reprap.org/wiki/G-code/es

Hasta aqui es sólo el inicio de muchas posibilidades de configuraciones en Repetier. Esperamos que les haya sido de utilidad.

 

 

 

Comandos por BLE – (Parte 1 de 2) – El Receptor

21 Jul , 2017,
Jose Nunez
, ,
No Comments

Para continuar la exploración de la tecnología BLE que comenzó nuestra compañera Rebeca Rodriguez Tencio acá, les presentamos este primer tutorial de dos relacionados. La idea es poder implementar una forma de enviar comandos a una tarjeta de prototipado via Bluetooth Low Energy (BLE) desde una App de un teléfono Móvil.

En esta primera parte implementaremos un servicio “Comando LED” en un microcontrolador compatible con Arduino 101 llamado TinyTILE (basado en el chip Curie de Intel)

En la segunda parte, implementaremos una App (Android y IOS) usando IONIC 3 que le envía comandos al micro-controlador mediante el sistema BLE del teléfono.

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

Primero: Programemos el Curie

En resumen:

  1. Necesitaremos un protoboard, un LED, una resistencia de 150 Ohm, un TinyTILE y un cable USB a micro-USB y una PC
  2. El circuito es muy sencillo:


  3. En la PC instalamos los controladores de placa “Arduino/Genuino 101” en el IDE de ARDUINO (V.1.8.2)
    • Menu: Tools >  Board > Board Manager > Search “Curie” > Instalar V 2.0.2 o superior.
  4. Descargamos el programa (sketch) de ejemplo de mi repositorio de Github y lo abrimos con el IDE de ARDUINO.
  5. Instalamos el boceto de ejemplo en la placa
    • Menu: Sketch > Upload
  6. Usamos una app para pruebas BLE denominada “BLE Scanner” para enviar comandos “ON” y “OFF”
  7. Nótese que hemos implementado una función “parpadear” que nos permite ver si el sketch arrancó adecuadamente.

Las siguientes imágenes ilustran el uso de BLE Scanner para enviar los comandos.

Seguidamente mostramos el código original del programa con los comentarios detallados sobre su funcionamiento. Es realmente sencillo; podemos resumirlo en:

  1. Definir un servicio BLE “ServicioBLE_Comando” (línea 5)
  2. Deinir una característica BLE “CaracteristiciaBLE_Comando” de tipo genérica, de lectura y escritura con mensajes de 20 bytes (línea 8)
  3. Darle un nombre al dispositivo “COMANDO LED” e inicializarlo, agregando el servicio y la característica (línea 19)
  4. Publicitamos el servicio (línea 23)
  5. Leemos eventos BLE en la característica (nuevos comandos) que luego son interpretados y ejecutados (líneas 31,33,57,61)
  6. Para esto convertimos el valor recibido a un String (línea 63)

 


#include <CurieBLE.h>
#define ledPin 13

//Definimos un servicio BLE
BLEService servicioBLE_Comandos("db938b80-f010-44b6-8aa9-1835adf9419a"); // create service

//Definimos una característica que pueda ser leida o escrita desde una central BLE, con capacidad de 20 bytes por mensaje.
BLECharacteristic caracteristicaBLE_Comandos("9906064e-9bbe-4eba-b415-bbd223f7d3d9", BLERead | BLEWrite, 20);

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT); 
  delay(500); //Esperamos un poco de tiempo (500ms) por si se puede habilitar la terminal serial.
  parpadear(5,200);//Señalizamos con el LED que estamos iniciando el sketch
  
  BLE.begin(); //Inicializamos el sistema Bluetooth Low Energy (BLE) del Curie
  parpadear(2 ,100);//Señal de aviso de que la operación anterior fue exitosa
  
  BLE.setLocalName("COMANDO LED"); //Definimos un nombre para publicitar nuestro dispositivo
  BLE.setAdvertisedService(servicioBLE_Comandos);//Definimos el servicio que se va a publicitar
  servicioBLE_Comandos.addCharacteristic(caracteristicaBLE_Comandos); //Agregamos la característica al servicio
  BLE.addService(servicioBLE_Comandos); //Agregamos el servicio al dispositivo  
  BLE.advertise(); // Publicitamos el dispositivo
  parpadear(3 ,100);//Señal de aviso de que la operación anterior fue exitosa

  Serial.println("En este punto el dispositivo queda listo, publicitado y esperando conexiones...");
  parpadear(10,50);//Señal de aviso de que SETUP se concluyó satisfactoriamente
}

void loop() {
  BLE.poll(); // Obtenemos cualquier evento BLE que haya sido enviado al dispositivo

  if (caracteristicaBLE_Comandos.written()) { //Si hay datos, ejecutamos el comando
    leerYEjecutarComado();
  } // ... caso contrario simplemente se termina el flujo de loop()
}

/**
 * Este método lee el comando registrado en la característica BLE y lo ejecuta.
 */
void leerYEjecutarComado(){
    String valorDelComando = obtenerValorDelComando();

    if (valorDelComando.startsWith("ON")) {
      Serial.println("LED on");
      digitalWrite(ledPin, HIGH);
    } else {
      Serial.println("LED off");
      digitalWrite(ledPin, LOW);
    }
}

/**
 * Extraer el valor del comando en formato String
 */
String obtenerValorDelComando(){
    int longitudDelComando = caracteristicaBLE_Comandos.valueLength();
    Serial.print("Longitud: ");
    Serial.println(longitudDelComando);
      
    const byte* val = caracteristicaBLE_Comandos.value();
    
    String strValor = String((const char *)val).substring(0, longitudDelComando);
    Serial.println("|" + strValor + "|");

    return strValor;
}

void parpadear(int times, int milliseconds){
  for(int i=0; i<times; i++){
    digitalWrite(ledPin, HIGH);
    delay(milliseconds);
    digitalWrite(ledPin, LOW);
    delay(milliseconds);
  }
}

 

 

Fundamentos y Experimentación con Bluetooth Low Energy

1 Jul , 2017,
Rebeca Rodriguez Tencio
, , , , ,
No Comments

Resumen: En esta publicación, Rebeca Rodiguez nos da una importante lección sobre la importancia de profundizar un poco en los fundamentos de las tecnologías que usamos, y a su vez nos propone un experimento muy interesante para aplicar estos conocimientos en una aplicación que permite controlar una luz (LED) de manera remota desde un teléfono celular.

(!) Antes de seguir estas instrucciones asegúrese de entender las Condiciones de Uso de nuestro sitio.

¿Conocen lo que es el Bluetooth de Baja Energía?

Saber de una tecnología va más allá de solo leer artículos informativos acerca de ella, antes de empezar a experimentar con BLE pensé que se me facilitaría desarrollar con está herramienta, no obstante descubrí que los conocimientos con los que contaba eran mínimos, y necesitaba aprender más para poder trabajar en uno de los proyectos actuales del Centro de Innovación, por lo que con el apoyo de Jose Núñez, uno de los ingenieros y colaboradores del centro, empecé la aventura de investigar y experimentar el mundo del Bluetooth de Baja Energía.

En muchos casos es muy fácil poder hacer aplicaciones funcionales e interesantes porque ya alguien más se tomó la molestia de hacer frameworks, librerías y tutoriales que nos facilitan la complejidad que conlleva realizar algo desde cero; probablemente si te gusta el mundo maker o estudias algo relacionado a la tecnología te has encontrado con proyectos que incluyan bluetooth como parte de las aplicaciones, sin embargo ¿te has preguntado la historia o cómo funciona toda la comunicación que hay detrás de esa tecnología o solamente has seguido los pasos de un tutorial e instalado las librerías respectivas copiando y pegando código y nos conformamos con que funcione a la primera?

PD: no importa si agarramos código de internet o nos basamos en algo ya existente para realizar un proyecto o aprender, existe mucha información y hay que sacarle el mayor provecho, pero es importante entender cómo funciona lo que estamos programando, la historia que hay detrás de  la comunicación entre los dispositivos y los términos empleados.

Resultado de imagen para BLUETOOTH low energy

El BLE es la versión ligera y mejorada del Bluetooth 4.0 clásico, está diseñado para abordar las necesidades de eficiencia energética y la simplicidad en el diseño de los productos, convirtiendose en una solución inalámbrica ULP (Ultra Low Power, Ultra Bajo Consumo); esta tecnología opera en la banda mundial de 2.4GHz, cuenta con una banda de bits de capa física de 1Mbps en un rango de 15metros.

El Bluetooth de Baja Energía fue desarrollado por Nordic Semiconductor, fue uno de los primeros miembros de “Wibree Alliance” la organización que inicio el desarrollo estándar de la ULP (Ultra Low Power), sin embargo después fue adoptado por el SIG (Special Interest Group, Grupo con Especial Interés), que fue conformado por cinco compañías (Ericsson, Nokia, Toshiba, IBM e INTEL). Nordic ha jugado un papel importante en el desarrollo de BLE, y continua desempeñando un papel clave a través del trabajo realizado como miembro asociado de Bluetooth SIG.

Lo interesante al usar BLE es que ha sido implementado en la gran mayoría de plataformas móviles modernas, sea iOS, Android, móviles con Windows, entre otros.

Hay conceptos indispensables con los que se van a encontrar siempre que empiecen a investigar acerca del BLE, por lo que conocer el significado o familiarizarse con los términos es importante.

Comenzamos hablando de perfiles y protocolos: el primer concepto es la subcategoría GAP (Generic Access Profile, Perfil de Acceso Genérico). El GAP es el encargado de permitir que el dispositivo sea público al exterior y por lo tanto define la forma de interacción entre los dispositivos. El GAP tiene dos roles importantes de conexión:  (1) los dispositivos centrales – de ahí el término BLE Central – (que corresponde a los móviles, tablet o dispositivos con capacidad de procesamiento mayor que inician la conexión con los periféricos) y (2) los periféricos (dispositivos pequeños de baja potencia y consumo) a los que se pueden conectar los dispositivos centrales.

Una vez que se establece la conexión entre los dispositivos centrales y los dispositivos periféricos entra al juego el GATT (Generic Attribute Profile, Perfil Genérico de Atributos) permitiendo la comunicación en ambas direcciones, a través del protocolo ATT que se usa para almacenar los servicios, los datos y las características propias de cada dispositivo utilizado; es importante destacar que un periférico BLE solo puede ser conectado a un dispositivo central a la vez, ya que tan pronto como el periférico es conectado al dispositivo central dejara de ser visible para otros dispositivos, sin embargo un dispositivo central (celulares, tablets…) si puede ser conectado a varios periféricos.

Al igual que en el GAP, el GATT también tiene roles de interacción que es importante conocer, como por ejemplo: el servidor que contiene las características de la búsqueda ATT asi como las definiciones del servicio y las características, el otro concepto es el cliente GATT que es el encargado de enviar las solicitudes. Todas las transacciones de comunicación entre los dispositivos son iniciadas por el GATT cliente (central) que recibe la respuesta del dispositivo esclavo, el GATT servidor (periférico).

Existen muchas aplicaciones para poder escanear periféricos BLE desde un dispositivo móvil, sin embargo hay una de ellas que me gustó mucho al momento de utilizarla. Se trata de nRF Connect for Mobile (Que antes se llamaba nRF Master Control Panel) es un app muy útil para experimentar y entender los conceptos de comunicación BLE.

Hay un ejemplo que encontré y quiero compartir porque está interesante ya que permite controlar un LED desde el celular, es decir podemos controlar un TinyTILE de Intel o una placa de Arduino 101 a través de un dispositivo central.

NOTA: Antes de iniciar el ejercicio es importante repasar qué es Intel Curie, esto para tener más claro el funcionamiento de los dispositvos perifericos utilizados, pueden guiarse con el siguiente enlace: https://costaricamakers.com/?s=curie

EJERCICIO PROPUESTO

Materiales:

  • 1LED
  • 1 Protoboard
  • Cable para prototipar
  • 1 tiny tile o placa arduino 101
  • 1 resistencia de 220Ω
  • Instalar el app de nRF Master Control Panel

Pasos a seguir:


PASO 1: Prototipar según la imagen del diagrama, que se obtuvo de: https://cdn.sparkfun.com/assets/learn_tutorials/4/9/2/Exp_1_Blink_bb.png

 

 

 

 

 

 

 


PASO 2: Conectar el periférico utilizado (Arduino 101 o TinyTILE), abrir el Arduino IDE y cargarle el código de ejemplo:

#include 
BLEPeripheral blePeripheral;
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED servicio app

//BLE caracteristicas asigandas
BLEUnsignedCharCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);

const int ledPin = 13; // pin asignado al LED

void setup() {
    pinMode(ledPin, OUTPUT);

    blePeripheral.setLocalName("TINYTILE BLE"); //Nombre que se le asigna al periferico para ser encontrado
    blePeripheral.setAdvertisedServiceUuid(ledService.uuid());

    blePeripheral.addAttribute(ledService); //caracteristicas del servicio
    blePeripheral.addAttribute(switchCharacteristic);

    switchCharacteristic.setValue(0);

    blePeripheral.begin();
}

void loop() {
    // Escucha los perifericos BLE para conectarse:
    BLECentral central = blePeripheral.central();

    // Si el central es conectado al periferico:
    if (central) {
        // Mientras la central siga conectada al periferico:
        while (central.connected()) {
            // Si el dispositivo remoto escribió a la característica,
            // usar un valor para encender el LED
            if (switchCharacteristic.written()) {
                // Cualquier valor a diferencia de 0 encendera el LED
                if (switchCharacteristic.value()) {
                    digitalWrite(ledPin, HIGH);
                }
                else {
                    digitalWrite(ledPin, LOW);
                }
            }
        }
    }
}

PASO 3: Abrir el app y hacer escaner de los dispositivos periféricos, conectar en este caso con el “TINYTILE BLE” como se muestra en la siguiente imagen. Una vez que se selecciona la opción de “CONNECT”, hay que hacer click en “Unknown Service” van aparecer unas flechas, haga click en la  flecha que tiene direción hacia abajo.

 


PASO 4: Se abrirá una pantalla o cuadro de dialogo, donde debemos seleccionar la opción de “UNIT 8” e introducir un número distinto de 0 para encencer el LED y haga click en enviar o “SEND”.

¡El LED debe encenderse! para volver apagarlo, debe abrir el mismo cuadro de diálogo e ingresar el número 0 y hacer nuevamente click en enviar y el LED debe apagarse.

Gracias por leer este artículo. Nos será de mucho valor que nos digas en comentarios si te fue de utilidad, si trataste de hacer el experimento y si realizaste cambios al mismo.

Como instalar OpenCV en Ubuntu

19 Jun , 2017,
Luis Ramirez
No Comments

https://blog.desdelinux.net/wp-content/uploads/2016/04/ubuntu.png

OpenCV es una biblioteca para manipulación de imágenes bastante potente y gratis tanto para propósitos académicos como comerciales. Posee extensiones en C++, C, Python y Java y soporta gran variedad de Sistemas Operativos, como Windows, Linux, Mac OS, iOS y Android.

En esta publicación se explicará la manera de instalar OpenCV para Python en Ubuntu.

http://www.akirasan.net/content/images/2016/06/opencv-python.png

Existen múltiples maneras de lograr este propósito, a continuación explicaremos las 2 principales.

1- La primera opción nos instalará la versión más actualizada de OpenCV 2.

  1. sudo apt-get install libopencv-dev python-opencv

2- La segunda opción nos instalará la última versión que existe (version 3.2.0 actualmente) de OpenCV 3, la cual incluye mejoras y optimizaciones sobre la versión 2. Por lo lo primero debemos descargar el siguiente script https://github.com/milq/milq/blob/master/scripts/bash/install-opencv.sh

Una vez descargado el script, nos dirigimos hacia el directorio en el que se encuentra y ejecutamos el siguiente comando

bash install-opencv.sh

Nos pedirá la contraseña y comenzará la instalación.

 

Si deseamos verificar la versión que tenemos instalada de OpenCV, se puede crear un sencillo script de Python con las siguientes líneas

import cv2
print(cv2.__version__)

 

Así de simple tenemos OpenCV en nuestro sistema.

¿Que cosas se pueden hacer con OpenCV?

  • Reconocimiento de vehículos en tiempo real.

 

  • Reconocimiento de rostros

http://docs.opencv.org/3.2.0/faceDetection.png

  • Manipulación de videos

http://www.pyimagesearch.com/wp-content/uploads/2016/02/writing_to_video_quicktime.jpg

 

Y gran variedad de cosas mas.

 

La manera más sencilla de comenzar a desarrollar programas con OpenCV es visitando el siguiente enlace en el que existen pequeños tutoriales que brindan las bases suficientes para hacer lo que se nos ocurra http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_tutorials.html