Tag Archives: INTEL Curie

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 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.

Un Vistazo al ARDUINO 101 / GENUINO 101

23 Feb , 2016,
José Núñez
, , , ,
No Comments

Desde hace más de un año Intel Corp se ha estado esfor101zando para traer productos compatibles con ARDUINO, basasdos en su arquitectura x86 al mundo Maker. Fue así como vimos la llegada de Intel Galileo, Intel Galileo GEN2 e Intel Edison.

Este año nos trae un nuevo miembro de la familia Intel: El Intel Curie que es la base para la nueva tarjeta de prototipado Arduino/Genuino llamada el ARDUINO 101.


En Resumen:

Pros: Mayor poder computacional, sensores adicionales de movimiento y comunicación BLE a un bajo costo y bajo consumo eléctrico. Perfecto para… ¿drones? Flexibilidad en el GPIO para 3.3V y 5V.

Cons: Solución de prototipado no aprovecha las capacidades para computación vestible del Intel Curie.


En Detalle:

Curie es la nueva solución SoC x86 (System on Chip) de Intel para computación vestible. Está orientado a aplicaciones que necesiten una capacidad computacional superior y que sean de bajo consumo eléctrico, para prototipado de soluciones embebidas y vestibles. Trae integrado comunicación Bluetooth BLE así como giroscopio de 6 ejes y acelerómetro.

El Arduino 101 es la primera tarjeta de prototipado lanzada por ARDUINO con Intel Curie dentro.

El precio actual varía. Lo hemos visto disponible en Amazon a $35 así como en Mouser.com a $30. Al parecer su venta aun está restringida a los Estados Unidos.

Dentro de las principales características podemos resaltar:

  1. Voltage de operacion de I/O de 3.3V (tolera 5V)
  2. Dos núcleos de procesamiento: un x86 Quark Curie y un ARC de 32Bits
  3. Voltaje Recomendado de Alimentación: 7V – 12 V
  4. Voltaje Máximo de Alimentación: 7V-20V
  5. Pines Digitales I/O: 14 de los cuales 4 son PWM
  6. Pines Analógicos de Entrada: 6
  7. Corriente DC por Pin: 20mA
  8. Memoria Flash: 196Kb for sketches out of 384Kb total.
  9. Memoria SRAM: 24Kb for sketches out of 80Kb total.
  10. Velocidad de Reloj: 32MHz
  11. Funciones Integradas:
    1. Bluetooth BLE
    2. Acelerómetro/Giroscopio de 6 ejes
    3. Dimensiones: 68.6mm x 53.4mm

Pronto estarems evaluando este dispositivo en la práctica.

Fuentes:

  1. Intel Curie Fact Sheet
  2. Arduino 101 Spec @ Arduino.CC