Skip to content
monkeyserna edited this page May 25, 2014 · 41 revisions
<style> @media print { .header, #gollum-editor-title-field, .comment-form-head, .pagehead , .sunken-menu-contents { display: none; } } </style>

La aplicación Arduino se ha realizado empleando el framework PhoneGap. Esta plataforma permite la creación de aplicaciones móviles haciendo uso de las tecnologías web HTML, CSS, y JavasCript. Aunque el rendimiento general de la aplicaciones realizadas con PhoneGap es algo menor que con una aplicación nativa, la aplicación creada para controlar el robot es relativamente ligera por lo que esto no supone un gran inconveniente. En el dispositivo Android empleado, Nexus 4, no aparece ningún tipo de retardo.

Otra gran ventaja de emplear este framework es que si en un futuro se decide portar la aplicación a otros sistemas operativos, como iOS, Windows Phone, Blackberrry OS, WebOS, Symbian o Bada, se podrá reutilizar una gran parte del código empleado, siendo únicamente necesario realizar los ajustes de adaptación al sistema específico. Esta ventaja reduce sustancialmente los tiempos de desarrollo con respecto a una aplicación nativa.

Para emplear funciones específicas de los móviles (como por ejemplo obtener los valores del acelerómetro) se emplea la API de PhoneGap en código JavasScript. En el apartado drive.js se detalla el uso de esta API para obtener la posición física del móvil mediante el acelerómetro. Estos datos se emplean para enviar comandos de movimientos de motores al robot.

Configuración PhoneGap

Toda aplicación realizada en PhoneGap debe contener en la carpeta raíz del proyecto el archivo config.xml que define las características del proyecto.

config.xml

Algunas características que se han definido son:

  • id="es.sernaleon.charlie"
    • Identificador único de la aplicación. El estándar sugiere que sea la dirección web de la empresa en orden inverso (eliminando www si lo hubiera), seguido del nombre del proyecto. Mi página personal es http://sernaleon.es y el nombre del proyecto es Charlie, por lo que el identificador único es es.sernaleon.charlie.
  • <preference name="orientation" value="landscape"/>
    • Esta propiedad establece la orientación apaisada pro defecto, aun así, es necesario bloquear mediante el plugin orientationchanger esta orientación.
  • <preference name="fullscreen" value="true"/>
    • Establece la aplicación a pantalla completa.
  • <preference name="exit-on-suspend" value="false"/>
    • Esta opción permite mantener la aplicación en segundo plano. Esto es útil para no perder un script realizado en el modo de programación gráfica que no ha sido guardado.
  • <icon src="img/icon.png"/>
    • Establece el icono de la aplicación.
  • <access origin="*"/>
    • Permite cargar elementos externos a la aplicación. Si no se indica esta opción, no es posible visualizar la señal de vídeo.
  • <gap:plugin name="com.boyvanderlaak.cordova.plugin.orientationchanger" version="0.1.1" />
    • Plugin que bloquea la posición apaisada. Esto es importante, ya que en caso de no bloquear el modo apaisado, en el modo de conducción libre la pantalla comienza a girar cuando se inclina el dispositivo con el objetivo de hacer girar el robot.
  • <gap:plugin name="nl.x-services.plugins.insomnia" version="3.0" />
    • Plugin que impide que el dispositivo Android entre en modo de suspensión. En otras pantallas, fuerza a la pantalla a permanecer siempre activa.
  • <gap:plugin name="nl.x-services.plugins.toast" version="1.0" />
    • Plugin que permite mostrar mensajes emergentes de tipo toast. En la referencia de Android hay disponible más información acerca de los toast.
  • <gap:plugin name="org.apache.cordova.device-motion" version="0.2.6" />
    • Plugin que captura los valores del acelerómetro, empleados en la transmisión de comandos de movimiento.
Código fuente
<?xml version='1.0' encoding='utf-8'?>
<widget id="es.sernaleon.charlie" version="1.0.0" xmlns="http://www.w3.org/ns/widgets"
        xmlns:gap="http://phonegap.com/ns/1.0">
    <name>Charlie</name>
    <description>
        Charlie the robot.
    </description>
    <author email="arturo@sernaleon.es" href="http://sernaleon.es">
        Arturo Serna Leon
    </author>
    <feature name="http://api.phonegap.com/1.0/device"/>
    <preference name="permissions" value="none"/>
    <preference name="orientation" value="landscape"/>
    <preference name="target-device" value="universal"/>
    <preference name="fullscreen" value="true"/>
    <preference name="webviewbounce" value="true"/>
    <preference name="prerendered-icon" value="true"/>
    <preference name="stay-in-webview" value="false"/>
    <preference name="detect-data-types" value="true"/>
    <preference name="exit-on-suspend" value="false"/>
    <preference name="show-splash-screen-spinner" value="true"/>
    <preference name="auto-hide-splash-screen" value="true"/>
    <preference name="disable-cursor" value="false"/>
    <preference name="android-minSdkVersion" value="7"/>
    <preference name="android-installLocation" value="auto"/>
    <icon src="img/icon.png"/>
    <access origin="*"/>
    <gap:plugin name="com.boyvanderlaak.cordova.plugin.orientationchanger" version="0.1.1" />
    <gap:plugin name="nl.x-services.plugins.insomnia" version="3.0" />
    <gap:plugin name="nl.x-services.plugins.toast" version="1.0" />
    <gap:plugin name="org.apache.cordova.device-motion" version="0.2.6" />
</widget>

Vistas

La interfaz gráfica está desarrollada exclusivamente con las tecnologías HTML y CSS. No se ha empleado JavaScript para la maquetación de las interfaces.

La aplicación contiene 3 interfaces:

Index

Esta es la vista inicial de la aplicación. Su misión es la de servir de enlace a los dos modos de control de Charlie, mediante dos botones situadios en la parte inferior de la pantalla.

Como se puede observar, los enlaces a los modos se encuentran situados en la parte inferior de la vista. Son los siguentes:

  • DRIVE: Al pulsar este botón se accede al modo de conducción libre.
  • CODE: Al pulsar este botón se accede al modo de programación gráfica.

index.html

El código <html> se divide en dos partes:

  • <header>
    • Establece los metadatos para optimizar la visualización en dispositivos móviles.
    • Importa el fichero de estilo.
  • <body>
    • Un contenedor divide la pantalla en dos columnas
      • La columna izquierda contiene el logotipo.
      • La columna dereche contiene el nombre del proyecto.
    • El siguiente bloque alberga los botones mediante listas no ordenadas, maquetadas en CSS.
    • Con el fin de mejorar los tiempos de carga, los archivos JavaScript se importan al final de la etiqueta <body>.
Código fuente
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta name="format-detection" content="telephone=no"/>
    <meta name="viewport"
          content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width"/>
    <meta name="mobile-web-app-capable" content="yes">
    <title>Charlie</title>
    <link rel="stylesheet" href="style/index.css"/>
</head>
<body>
<div class="container">
    <div class="robotC">
        <div class="leftCol">
            <span class="helper"></span>
            <img src="img/logo.png" id="logo">
        </div>
        <div class="rightCol">
            <div class="title">Charlie</div>
            <div class="subtitle">the robot</div>
        </div>
    </div>
    <nav class="tab-fixed">
        <ul class="tab-inner">
            <li><a id="btDrive" href="drive.html">Drive</a></li>
            <li><a id="btCode" href="code.html">Code</a></li>
        </ul>
    </nav>
</div>

<script type="text/javascript" src="phonegap.js"></script>
<script type="text/javascript" src="js/values.js"></script>
<script type="text/javascript" src="js/globalFunctions.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

index.css

Este fichero es la hoja de estilos asociada a la página principal. Realiza la maquetación de index.html.

Para el título y subtítulo del proyecto se emplea una fuente externa llamada [Poiret One].(http://www.google.com/fonts/specimen/Poiret+One).

Los botones están realizados de forma que recuerden al objeto TAB de Android. El código CSS es una adaptación del código de los TAB de la librería FRIES V2.03.

Su estructura es secuencial, dividiendo los elementos en los siguientes apartados.

Fuente externa
@font-face {
    font-family: 'title';
    src: url('fonts/PoiretOne-Regular.ttf');
}

En este documento se importa la fuente externa empleada en el título y subtítulo. El nombre que recibe el title, por lo que para utilizar esta tipografía en una fuente se emplea la regla

font-family: title;
Reglas generales

Aplica formato general a las etiquetas html, body y a. Además define unas reglas aplicables a todos los elementos dentro de la definición de *.

Estilizado de TABS

Basado en el código de Fries 2.0.3, se realiza una adaptación de este objeto a las necesidades particulares del proyecto. Algunas variaciones con respecto al código original son que se han eliminando funcionalidades no empleadas, se agranda ligeramente el tamaño de letra de los botones, y se sitúa en la parte inferior de la vista, ya que por defecto los TABS se colocan en la parte alta de la pantalla.

Reglas de contenedores

Por último, se crean las reglas de los contenedores que posicionan tanto la imagen como el título y el subtítulo. Para ello, se crea una estructura de 2 columnas, donde se sitúa el logotipo a la izquierda, y a la derecha el título y subtítulo. Estos elementos se alinean verticalmente para que se ocupen una posición central.

Código fuente
@font-face {
    font-family: 'title';
    src: url('fonts/PoiretOne-Regular.ttf');
}

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    padding: 0;
    margin: 0;
    border: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    -ms-text-size-adjust: 100%;
    background-color: #111111;
    padding-bottom: 1px;
}

body {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 16px;
    line-height: 1.67em;
    color: white;
    background-color: #111111;
}

a {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    position: relative;
    display: block;
    height: 48px;
    color: white;
    overflow: hidden;
    text-transform: uppercase;
    text-overflow: ellipsis;
    font-size: 13px;
    font-weight: bold;
    line-height: 52px;

    text-decoration: none;

    text-decoration: none;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}


/* TABS:
    Based in fries js source code: http://getfri.es
 */


.tab-fixed {
    right: 0;
    left: 0;
    -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25) ;
    -moz-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25) ;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25) ;
    display: block;
    z-index: 1;
    height: 48px;
    background: #333333;
    -webkit-backface-visibility: hidden;

    position: fixed;
    bottom: 0px;
}

.tab-fixed ul.tab-inner {
    display: -webkit-box;
    display: -moz-box;
    display: box;
    -webkit-box-orient: horizontal;
    -moz-box-orient: horizontal;
    box-orient: horizontal;
    width: 100%;
    list-style: none;
}

.tab-fixed ul.tab-inner li {
    -webkit-box-flex: 1;
    -moz-box-flex: 1;
    box-flex: 1;
    whitespace: nowrap;
    overflow: hidden;
    text-align: center;
}

.tab-fixed ul.tab-inner li a:after {
    position: absolute;
    content: '';
    width: 1px;
    height: 24px;
    top: 13px;
    right: 0;
    background-color: #4d4d4d;
}

.tab-fixed ul.tab-inner li:active {
    background-color: rgba(51, 181, 229, 0.6);
}

/*Wrappers*/

.robotC {
    position: fixed;
    height: 100%;
    width: 100%;
    background-color: #eee;


    display: table-cell;
    vertical-align: middle;
}


.leftCol {
    position: absolute;
    height: 100%;
    width: 50%;
    text-align: center;
}

.rightCol {
    position: absolute;
    margin-left: 50%;
    height: 100%;
    width: 50%;
}

/*Left column content */

.helper {
    display: inline-block;
    height: 100%;
    vertical-align: middle;
}

#logo {
    vertical-align: middle;
}

/* Right column content */

.robotC .title {
    margin-top: 40%;
    padding-top: 20px;
    font-size: 80px;
    height: 90px;
    color: #000;
    font-variant: small-caps;
    font-family: title;
    text-rendering:optimizeLegibility;

}

.robotC .subtitle {
    font-size: 35px;
    padding: 0 0 10px 80px;
    color: #000;
    font-family: title;
    text-rendering:optimizeLegibility;
}

Drive

El modo de conducción libre permite realizar las siguientes acciones:

  • Ver en tiempo real la señal de la cámara del robot.
  • Desplazar el robot mediante la posición física del dispositivo Android, haciendo uso del acelerómetro.
  • Desplazar verticalmente la cámara mediante una barra deslizante.

La disposición de los elementos es la siguiente:

A la izquierda de la pantalla hay una barra deslizante que permite controlar el ángulo vertical de la cámara. El rango que abarca esta barra deslizante es [-35,100]. Algunos valores son los siguientes:

  • -35º Es el valor mínimo. El robot apunta hacia abajo. El desplazable de la barra se en encuentra el extremo inferior.
  • El robot apunta hacia el frente. El desplazable se encuentra ligeramente debajo del centro de su rango.
  • 90º El robot apunta hacia arriba. El desplazable se encuentra ligeramente por debajo de su extremo superior.
  • 100º El robot apunta hacia arriba y ligeramente hacia atrás (una inclinación de 100º sobre la horizontal). El desplazable se encuentra en su extremo superior.

El resto de la pantalla lo ocupa la señal de vídeo del robot.

Para comenzar a controlar el movimiento del robot, basta con tocar la pantalla, encima de la señal de vídeo. En ese momento, aparece un triángulo verde en la esquina superior derecha que indica que se están transmitiendo comandos de movimiento. Para detener el robot, basta con volver a tocar la pantalla.

El movimiento del robot se realiza mediante la inclinación del dispositivo móvil, con 2 variables.

  • La velocidad se controla con la inclinación hacia adelante y hacia atrás.
  • El giro se controla con la inclinación lateral del dispositivo.

drive.html

El documento index.html es un fichero html. En las etiquetas <html> se definen 2 partes:

  • <header>
    • Importa el fichero de estilo
  • <body>
    • Define el contenido de la vista.
    • Importa los ficheros javascript al final de esta etiqueta para mejorar los tiempos de carga.

Dentro de la etiqueta body, encontramos los siguientes elementos:

Barra deslizante
<div id="sliderContainer">
    <input type="range" id="slider" min="100" max="250" value="215" step="1"  onchange="moveServo()" />
</div>

En primer lugar se define la barra desplazable y su rango de valores. El rango definido es de [100,250] (este rango es adaptado posteriormente a las especificaciones de entrada del servo). Donde:

  • 250 indica mirar hacia abajo (aproximadamente -35º).
  • 100 establece un ángulo aproximado de 100º sobre la horizontal (apunta hacia arriba y ligeramente hacia atrás).

Este rango puede ser aumentado hasta [50,250] aunque por motivos de usabilidad no se encuentra disponible por defecto. Un valor 50 se correspondería con una incliación de unos 150º. La sensación que produce es la de estar mirando hacia atrás con el cuerpo hacia adelante (inclinando la cabeza hacia atrás). Por tanto, este grado hace que la señal de la cámara se vea invertida.

Señal de transmisión
<div id="play"></div>

Esta etiqueta define el indicador de la señal de transmisión. Por defecto no se encuentra visible. Al comenzar la transmisión de comandos, se establece esta etiqueta a visible, mostrando un triángulo verde en la posición superior izquierda de la señal de vídeo, indicando al usuario que los comandos de movimiento están siendo enviados.

Señal de vídeo
<div id="container">
    <iframe id="camFrame"></iframe>
    <div id="overlay"></div>
</div>

Por último se define el espacio que ocupa la señal de vídeo. La señal no es más que un iframe apuntando a la dirección del servidor de MJPG, bajo protocolo HTTP sobre TCP/IP. Por defecto, esta dirección es http://192.168.0.123:8080, aunque se establece por JavaScript para que sea posible modificar la dirección del servidor de motion-jpeg.

La etiqueta con id overlay es la encargada de capturar el toque de pantalla (para comenzar o finalizar la transmisión de comandos de movimiento).

Código fuente
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Charlie drive</title>
    <link href="style/drive.css" rel="stylesheet" />
</head>
<body>

<div id="sliderContainer">
    <input type="range" id="slider" min="100" max="250" value="215" step="1"  onchange="moveServo()" />
</div>

<div id="play"></div>

<div id="container">
    <iframe id="camFrame"></iframe>
    <div id="overlay"></div>
</div>

<script type="text/javascript" src="phonegap.js"></script>
<script type="text/javascript" src="js/values.js"></script>
<script type="text/javascript" src="js/globalFunctions.js"></script>
<script type="text/javascript" src="js/drive.js"></script>
</body>
</html>

drive.css

Este documento es la hoja de estilo asociada al fichero drive.html.

Su estructura es secuencial, dividiendo los elementos en los siguientes apartados.

Reglas generales
html, body {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
}

Aplica formato general a las etiquetas html y body. Establece las dimensiones al 100% y elimina bordes, y márgenes. También elimina posibles barras de desplazamiento con overflow:hiiden.

Reglas de contenedores

Se definen 2 contenedores: ``#containery#sliderContainer`:

#container {
    width:  calc(100% - 60px);
    height: 100%;
    position: absolute;
    top: 0;
    right: 0;
    overflow: hidden;
    border: 0;
}

#sliderContainer {
    position: absolute;
    width: 40px;
    height: calc(100% - 20px);
    top: 10px;
    left: 10px;
    z-index: 999;
}

#container es el contenedor que engloba el iframe con la señal de video, y el div overlay que se encarga de capturar los eventos de toque de pantalla (para comenzar y finalizar las transmisiones de comandos de movimiento). Ocupa toda la pantalla excepto 60pixeles a la izquierda, reservados a la barra deslizante que controla el servomotor (para establecer el ángulo vertical de la cámara).

#sliderContainer Contiene la barra deslizante. Se situa de forma alargada a la izquierda de la vista.

Barra deslizante
input[type=range]#slider {
    height: 100%;
    width: 100%;
    -webkit-appearance: slider-vertical;
    -webkit-transform:rotate(180deg);
}

La barra deslizante controla la posición del servo, para controlar el ángulo vertical en el que se encuentra la cámara. Para ello, se establece que ocupe el 100% (es decir, el máximo del tamaño del contenedor donde se encuentra). También se indica que es de tipo vertical. También se gira 180º el slider para coincidir que cuanto más abajo esté el deslizante, más hacia abajo apunte la cámara.

Señal de vídeo

La señal de vídeo es recibida a través de un iframe, sus reglas css se definen así:

#camFrame {
    height: 100%;
    width: 100%;
    border: 0;
}

Esta regla establece la señal de la cámara al máximo posible (el máximo de su contenedor, que es toda la pantalla excepto 60 píxeles a la izquierda reservados para el slider). También se elimina el borde que incorporan los iframe por defecto.

Overlay

El overlay es el encargado de capturar cuando el usuario toca la pantalla con la finalidad de comenzar o finalizar la transimsión de video. Para ello, se superpone un div. Mediante JavaScript, se le asocia el evento correspondiente para realizar estas acciones.

#overlay {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    position: absolute;
}

El overlay se sitúa en el 100% del espacio reservado para la señal de vídeo.

Play

Cuando el dispositivo se encuentra enviando comandos, se muestra un indicador de forma triangular para que el usuario tenga constancia de que se están enviando comandos. Si el envío de comandos está detenido, esta marca desaparece.

#play {
    display: none;
    z-index: 999;
    width: 0;
    height: 0;
    border-top: 20px solid transparent;
    border-bottom: 20px solid transparent;
    border-left: 20px solid green;
    position: absolute;
    top: 10px;
    left: 70px;
    opacity: 0.5;
}

Estas reglas definen un triángulo verde con una opacidad del 50% situado en la posición superior izquierda de la pantalla. Para superponer este elemento al resto (y que no sea tapado) se define la regla z-index: 999.

Código fuente
html, body {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
}

#container {
    width:  calc(100% - 60px);
    height: 100%;
    position: absolute;
    top: 0;
    right: 0;
    overflow: hidden;
    border: 0;
}

#sliderContainer {
    position: absolute;
    width: 40px;
    height: calc(100% - 20px);
    top: 10px;
    left: 10px;
    z-index: 999;

}

input[type=range]#slider {
    height: 100%;
    width: 100%;
    -webkit-appearance: slider-vertical;
    -webkit-transform:rotate(180deg);
}


#camFrame {
    height: 100%;
    width: 100%;
    border: 0;
}

#overlay {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    position: absolute;
}

#play {
    display: none;
    z-index: 999;
    width: 0;
    height: 0;
    border-top: 20px solid transparent;
    border-bottom: 20px solid transparent;
    border-left: 20px solid green;
    position: absolute;
    top: 10px;
    left: 70px;
    opacity: 0.5;
}

Code

A continuación se detalla la implementación del modo de programación gráfica. Este modo de control del robot está basado en el lenguaje Blockly.

Para programar automatismos en el robot, se emplean tanto bloques ya existentes en Blockly como bloques propios. Los bloques propios implementados a su vez se dividen de la siguiente manera:

  • Movimiento de avance
    • Move straight [slow|medium|fast]
    • Move left [slow|medium|fast]
    • Move right [slow|medium|fast]
    • Stop motors
    • Set [left|right|both] motor speed at [0-255]
    • Set speed [0-255] and balance [0-255]
  • Movimiento de retroceso
    • Set both motors at back speed [0-255]
    • Set right motor at back speed [0-255]
    • Set right motor at back speed [0-255]
    • Set backwards speed [0-255] and balance [0-255]
    • Stop motors
  • Funciones de lecutra de infrarrojos
    • Read front sensors
    • Read middle sensors
    • Read front and middle
    • From [list] get first black from left
    • Analog read front sensors
  • Otras funciones de control de Charlie
    • Wait [time] miliseconds
    • Read distance (cm)
    • Set led [0-13] [ON|OFF]
    • Set servo position [-35,150]
    • Set horn [ON|OFF]

code.html

En este fichero se definen los elementos que contiene la vista del modo de programación. Se pueden resumir en 3 bloques.

  • La barra superior, con los comandos:
    • Show code Muestra la traducción a Python del script Arduino que será enviada al robot.
    • Execute Envía el script al robot para ser ejecutado.
    • Clear Elimina todos los bloques del espacio de trabajo.
    • Load Incrusta en el espacio de trabajo bloques guardados previamente.
    • Save Guarda el espacio de trabajo.
  • La barra de bloques (también llamada toolbox de Blockly) contiene tanto bloques nativos como propios, dedicados a funciones específicas del robot. La explicación detallada de estos bloques está disponible en el capítulo Funcionamiento del modo de programación gráfica.
  • El espacio de trabajo, donde se realizan los scripts.

Dentro de la etiqueda body se encuentra el contenido disponible.

La barra superior se define en el div con id btContainer:

<div id="btContainer">
    <div id="test" class="button">Show code</div>
    <div id="btSend" class="button">Execute</div>
    <div id="btClear" class="button">Clear</div>

    <div id="btSave" class="button right">Save</div>
    <div id="btLoad" class="button right">Load</div>
</div>

A continuación, se reserva el espacio de trabajo de Blockly:

<div id="blocklyContainer"></div>

La siguiente etiqueta define la barra de bloques, donde se especifican los bloques que deben ser ejecutados:

<xml id="toolbox" style="display: none">
<category name="Logic">
    <block type="controls_if"></block>
    (...)
</category>
(...)
</xml>

Algunos de estos bloques contienen valores por defecto:

El código xml del bloque es el siguiente:

    <block type="wait">

        <value name="timeD">
            <block type="math_number">
                <field name="NUM">1000</field>
            </block>
        </value>
    </block>

El parámetro que recibe el bloque wait es timeD (ver apartado JavaScript). Este valor debe ser numérico, y se especifica un valor por defecto de 1000.

Por último, se especifica la posición del toolBox mediante Javascript:

<script>

    Blockly.inject(document.getElementById('blocklyContainer'),
            {path: 'Blockly/', toolbox: document.getElementById('toolbox')});
</script>
Código fuente
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Charlie script generator</title>
    <link rel="stylesheet" href="style/code.css"/>

    <script type="text/javascript" src="js/globalFunctions.js"></script>
    <script type="text/javascript" src="js/values.js"></script>
    <script src="js/code.js"></script>
    <script type="text/javascript" src="Blockly/blockly_compressed.js"></script>
    <script type="text/javascript" src="Blockly/blocks_compressed.js"></script>
    <script type="text/javascript" src="Blockly/msg/js/en.js"></script>
    <script type="text/javascript" src="Blockly/python_compressed.js"></script>
    <script type="text/javascript" src="js/customBlocks.js"></script>
</head>
<body onload="onLoad()">

<div id="btContainer">
    <div id="test" class="button">Show code</div>
    <div id="btSend" class="button">Execute</div>
    <div id="btClear" class="button">Clear</div>

    <div id="btSave" class="button right">Save</div>
    <div id="btLoad" class="button right">Load</div>
</div>
<div id="blocklyContainer"></div>

<xml id="toolbox" style="display: none">
<category name="Logic">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_negate"></block>
    <block type="logic_boolean"></block>
    <block type="logic_null"></block>
    <block type="logic_ternary"></block>
</category>
<category name="Loops">
    <block type="controls_repeat_ext">
        <value name="TIMES">
            <block type="math_number">
                <field name="NUM">10</field>
            </block>
        </value>
    </block>
    <block type="controls_whileUntil"></block>
    <block type="controls_for">
        <value name="FROM">
            <block type="math_number">
                <field name="NUM">1</field>
            </block>
        </value>
        <value name="TO">
            <block type="math_number">
                <field name="NUM">10</field>
            </block>
        </value>
        <value name="BY">
            <block type="math_number">
                <field name="NUM">1</field>
            </block>
        </value>
    </block>
    <block type="controls_forEach"></block>
    <block type="controls_flow_statements"></block>
</category>
<category name="Math">
    <block type="math_number"></block>
    <block type="math_arithmetic"></block>
    <block type="math_single"></block>
    <block type="math_trig"></block>
    <block type="math_constant"></block>
    <block type="math_number_property"></block>
    <block type="math_change">
        <value name="DELTA">
            <block type="math_number">
                <field name="NUM">1</field>
            </block>
        </value>
    </block>
    <block type="math_round"></block>
    <block type="math_on_list"></block>
    <block type="math_modulo"></block>
    <block type="math_constrain">
        <value name="LOW">
            <block type="math_number">
                <field name="NUM">1</field>
            </block>
        </value>
        <value name="HIGH">
            <block type="math_number">
                <field name="NUM">100</field>
            </block>
        </value>
    </block>
    <block type="math_random_int">
        <value name="FROM">
            <block type="math_number">
                <field name="NUM">1</field>
            </block>
        </value>
        <value name="TO">
            <block type="math_number">
                <field name="NUM">100</field>
            </block>
        </value>
    </block>
    <block type="math_random_float"></block>

    <block type="toInt"></block>
    <block type="map">

        <value name="x">
            <block type="variables_get">
            </block>
        </value>

        <value name="in_min">
            <block type="math_number">
            </block>
        </value>

        <value name="in_max">
            <block type="math_number">
            </block>
        </value>

        <value name="out_min">
            <block type="math_number">
            </block>
        </value>

        <value name="out_max">
            <block type="math_number">
            </block>
        </value>
    </block>
</category>

<category name="Text">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
  <value name="TEXT">
    <block type="text"></block>
  </value>
</block>
<block type="text_length"></block>
<block type="text_isEmpty"></block>
<block type="text_indexOf">
  <value name="VALUE">
    <block type="variables_get">
      <field name="VAR">text</field>
    </block>
  </value>
</block>
<block type="text_charAt">
  <value name="VALUE">
    <block type="variables_get">
      <field name="VAR">text</field>
    </block>
  </value>
</block>
<block type="text_getSubstring">
  <value name="STRING">
    <block type="variables_get">
      <field name="VAR">text</field>
    </block>
  </value>
</block>
<block type="text_changeCase"></block>
<block type="text_trim"></block>
<block type="text_print"></block>
<block type="text_prompt"></block>
</category>


<category name="Lists">
    <block type="lists_create_empty"></block>
    <block type="lists_create_with"></block>
    <block type="lists_repeat">
        <value name="NUM">
            <block type="math_number">
                <field name="NUM">5</field>
            </block>
        </value>
    </block>
    <block type="lists_length"></block>
    <block type="lists_isEmpty"></block>
    <block type="lists_indexOf">
        <value name="VALUE">
            <block type="variables_get">
                <field name="VAR">list</field>
            </block>
        </value>
    </block>
    <block type="lists_getIndex">
        <value name="VALUE">
            <block type="variables_get">
                <field name="VAR">list</field>
            </block>
        </value>
    </block>
    <block type="lists_setIndex">
        <value name="LIST">
            <block type="variables_get">
                <field name="VAR">list</field>
            </block>
        </value>
    </block>
    <block type="lists_getSublist">
        <value name="LIST">
            <block type="variables_get">
                <field name="VAR">list</field>
            </block>
        </value>
    </block>
</category>

<category name="Variables" custom="VARIABLE"></category>
<category name="Procedures" custom="PROCEDURE"></category>



<category></category>



<category name="Motor forwards">
    <block type="move_fwd"></block>
    <block type="move_l"></block>
    <block type="move_r"></block>
    <block type="stop_motors"></block>
    <block type="move_motor">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>
    </block>

    <block type="move_balanced">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>

        <value name="balance">
            <block type="math_number">
                <field name="NUM">123</field>
            </block>
        </value>
    </block>
</category>

<category name="Motor backwards">

    <block type="move_back">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>
    </block>

    <block type="move_b_left">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>
    </block>

    <block type="move_b_right">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>
    </block>


    <block type="move_back_balanced">

        <value name="speed">
            <block type="math_number">
                <field name="NUM">50</field>
            </block>
        </value>

        <value name="balance">
            <block type="math_number">
                <field name="NUM">123</field>
            </block>
        </value>
    </block>

    <block type="stop_motors"></block>

</category>


<category name="Ground sensors">

    <block type="read_front"></block>
    <block type="read_middle"></block>
    <block type="read_ground"></block>
    <block type="first_black_left"></block>
    <block type="read_front_analog"></block>

</category>


<category name="Other functions">


    <block type="wait">

        <value name="timeD">
            <block type="math_number">
                <field name="NUM">1000</field>
            </block>
        </value>
    </block>

    <block type="read_sonar"></block>

    <block type="led">

        <value name="ledN">
            <block type="math_number">
                <field name="NUM">0</field>
            </block>
        </value>
    </block>
    <block type="servo">

        <value name="pos">
            <block type="math_number">
                <field name="NUM">0</field>
            </block>
        </value>
    </block>
    <block type="horn"></block>
</category>

</xml>

<script>

    Blockly.inject(document.getElementById('blocklyContainer'),
            {path: 'Blockly/', toolbox: document.getElementById('toolbox')});


</script>

<script type="text/javascript" src="phonegap.js"></script>
</body>
</html>

code.css

El documento de maquetación asociado al modo de programación gráfica contiene 5 bloques de reglas, y realiza lo siguiente:

  • Establece las etiquetas html y body al 100%, elimina márgenes y posibles barras de desplazamiento.
  • #btContainer define la barra superior, reservando 40px de altura, 100% del ancho, y el mismo color de fondo que la barra de bloques , #ddd.
  • .button y .right son reglas asociadas a los comandos situados dentro de la barra superior. Los elementos Load y Savese alinean a la derecha.
  • #blocklyContainer especifica el espacio reservado para Blockly. El 100% de la pantalla excepto los 40px de la barra superior.
Código fuente
html,body {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
}

#btContainer{
    height: 40px;
    width: 100%;
    background-color: #ddd;
}

.button{
    float: left;
    padding: 10px 30px;
    font-family: 'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
    font-size: 16px;
}

.right {
    float: right;
}

#blocklyContainer {
    height: calc(100% - 40px);
    width: 100%;
}

JavaScript

Mediante HTML se definen los elementos contenidos en la interfaz. CSS se encarga de la maquetación de estos elementos, el apartado estético. JavaScript es el lenguaje que implementa toda la funcionalidad asociada a la aplicación.

La estructura de ficheros contempla 6 archivos:

  • values.js Valores empleados en la web, como la IP, o el equivalente numérico de cierto comando.
  • globalFunctions.js Define funciones auxiliares, empleadas por los ficheros javascript asociados a los dos modos de control del robot.
  • index.js Define la lógica de la pantalla de entrada (inicializa PhoneGap).
  • drive.js Define la lógica asociada a este modo de control.
  • code.js Define la lógica asociada a este modo de control. Concretamente la inicialización de componentes, las comunicaciones WebSocket, y comandos relacionados con el tratamiento de los bloques creados (crear, borrar, guardar y mostrar código)
  • customBlocks.js Define los bloques propios de Blockly, que proporcionan funcionalidad asociada a este robot, así como la definición de la traducción a Python que será enviada a la Raspberry Pi.

Ficheros globales

values.js

Este fichero contiene los valores predefinidos que se emplean en las funciones de JavaScript. Concretamente los valores que se especifican son:

  • Configuración de red
    • SERVER_IP: IP del servidor
    • SERVER_CAM_PORT: Puerto del servidor de la cámara
    • SERVER_CMD_PORT:
    • WEBHOST: Dirección del WebSocket
  • Configuración general
    • ACCELEROMETER_FREQUENCY: Frecuencia de lectura del acelerómetro
    • SLOW_SPEED: Define numéricamente la velocidad baja (empleado en algunos bloques de movimiento de motores)
    • MEDIUM_SPEED: Define numéricamente la velocidad media (empleado en algunos bloques de movimiento de motores)
    • FAST_SPEED: Define numéricamente la velocidad alta (empleado en algunos bloques de movimiento de motores)
  • Definición numérica de los comandos Arduino
    • CMD_ON: Parámetro de estado HIGH (empleado en leds y claxon) su valor es 1.
    • CMD_OFF: Parámetro de estado LOW (empleado en leds y claxon) su valor es 0.
    • CMD_NOPARAM: Se emplea para funciones que no necesitan parámetro.
    • CMD_STOP: Comando 0. Detener motores
    • CMD_MOVE_FORWARD: Comando 1. Movimiento de avance con velocidad y balance
    • CMD_SET_LEFT: Comando 2. Establece la velocidad del motor izquierdo.
    • CMD_SET_RIGHT: Comando 3. Establece la velocidad del motor derecho.
    • CMD_BOTH_MOTORS: Comando 4. Establece la velocidad de ambos motores.
    • CMD_LEFT_MOTOR: Comando 5. Establece la velocidad del motor izquierdo y detiene el derecho.
    • CMD_RIGHT_MOTOR: Comando 6. Establece la velocidad del motor derecho y detiene el izquierdo.
    • CMD_MOVE_BACKWARD: Comando 7. Movimiento de retroceso con velocidad y balance.
    • CMD_BACK_BOTH: Comando 8. Establece la velocidad de retroceso de ambos motores.
    • CMD_BACK_LEFT: Comando 9. Establece la velocidad de retroceso del motor izquierdo.
    • CMD_BACK_RIGHT: Comando 10. Establece la velocidad de retroceso del motor derecho.
    • CMD_BEEP: Comando 11. Controla el estado del zumbador.
    • CMD_LED: Comando 12. Controla el estado de uno de los 14 leds (Numerados del 0 al 13).
    • CMD_SENSORS: Comando 13. Lee todos los sensores infrarrojos.
    • CMD_FRONT: Comando 14. Lee los sensores frontales.
    • CMD_MIDDLE: Comando 15.Lee los sensores centrales.
    • CMD_FRONT_ANALOG: Comando 16. Lee los sensores frontales en analógico.
    • CMD_SERVO: Comando 17. Establece la posición del servo.
  • Funciones del servidor de comandos empleadas en la traducción de Blockly a Python.
    • PYT_SEND: Envía un comando a Arduino.
    • PYT_RECEIVE: Envia un comando a Arduino y leer una respuesta (lectura de sensores).
    • PYT_CAM: Realiza una fotografía. No implementada.
    • PYT_SONAR: Realiza una lectura del sonar.
    • PYT_SERVO: Función para establecer la posición del servo.
    • PYT_MAP: Realiza un mapeado de valores.
Código fuente:
SERVER_IP= "192.168.0.123"
SERVER_CAM_PORT = 8080;
SERVER_CMD_PORT  = 8000;
WEBHOST = "ws://" + SERVER_IP + ":" + SERVER_CMD_PORT;

ACCELEROMETER_FREQUENCY = 40;

SLOW_SPEED = 130;
MEDIUM_SPEED = 190;
FAST_SPEED = 255;

//COMMANDS

CMD_ON = 1;
CMD_OFF = 0;
CMD_NOPARAM = 0;

CMD_STOP = 0;
CMD_MOVE_FORWARD = 1;
CMD_SET_LEFT = 2;
CMD_SET_RIGHT = 3;
CMD_BOTH_MOTORS = 4;
CMD_LEFT_MOTOR = 5;
CMD_RIGHT_MOTOR = 6;
CMD_MOVE_BACKWARD = 7;
CMD_BACK_BOTH= 8;
CMD_BACK_LEFT= 9;
CMD_BACK_RIGHT= 10;
CMD_BEEP = 11;
CMD_LED = 12;
CMD_SENSORS = 13;
CMD_FRONT = 14;
CMD_MIDDLE = 15;
CMD_FRONT_ANALOG= 16;
CMD_SERVO = 17;

PYT_SEND = "sendToArduino";
PYT_RECEIVE = "receiveFromArduino";
PYT_CAM= "takePic";
PYT_SONAR = "readSonar";
PYT_SERVO = "moveServo";
PYT_MAP = "map";

globalFunctions.js

Este fichero contiene funciones generales que están disponibles en para las tres vistas de la aplicación. En la práctica, estas funciones son empleadas tanto en el modo Drive (el fichero drive.js) como el en Code (fichero code.js)

Las funciones disponibles son 2.

map

Esta función es empleada para realizar el mapeado de un valor desde su rango original hasta otro de salida. Mapea el valor x desde el rango [in_min, in_max] hasta [out_min, out_max]. Esta función está basada en la función map de Arduino.

Por ejemplo:

map(13,2,4,5,6) -> 10.5

Los límites "inferiores" de algún rango pueden ser mayores o menores que el límite "superior" por lo que map puede utilizarse para revertir una serie de números, por ejemplo:

map(x, 1, 50, 50, 1)
```
La función maneja correctamente también los números negativos, por ejemplo:
```
map(x, 1, 50, 50, -100)
```
también es válido y funciona correctamente.

Si el parámetro `checkRanges` vale `true`, se aplica la función de restricción, de forma que el valor devuelto siempre estará contenido dentro del rango de salida.

##### Parámetros

Recibe los parámetros:

* `x` Valor a escalar
* `in_min` Cota inferior del valor a escalar.
* `in_max` Cota superior del valor a escalar.
* `out_min` Cota inferior del valor resultante.
* `out_max` Cota superior del valor resultante.
* `checkRanges` Si este parámetro vale `true`, se aplica la función de restricción.

##### Valor devuelto

Devuelve un valor numérico, pudiendo éste ser decimal.

##### Código fuente

```javascript
//Map function
// Receives an x in a range [inMin,inMax], and escalates this value to [outMin,outMax]
// if the option checkRanges is set to true and the x value is out of range, sets its value to the outMin or outMax, depending of the value.
// Example:
// map(13,2,4,5,6)      -> 10.5
// map(13,2,4,5,6,true) -> 6
function map(x, in_min, in_max, out_min, out_max,checkRanges) {
    if ((typeof checkRanges != 'undefined') && checkRanges) {
        if (x < in_min) x = in_min;
        else if (x > in_max) x = in_max;
    }
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
```

## Ficheros de vistas

Los siguientes ficheros contienen toda la lógica algorítmica relacionada con la aplicación. PhongeGap es un framework de desarrollo de aplicaciones móviles basado en tecnologías web, por lo que la maquetación se realiza empleando HTML y CSS, y la logica de la aplicación se implementa en JavasScript, haciendo uso tanto de las funciones disponibles en  este lenguaje como con las de la API de PhoneGap, que permiten hacer uso de características específicas del dispositivo, como el acelerómetro.

### index.js

En este fichero se implementa la lógica de la pantalla de entrada. Contiene una única función que realiza las tareas asociadas a la inicialización de PhoneGap.

En primer lugar, se crea un evento para que cuando PhoneGap arranque llame a la función de inicialización

```javascript
document.addEventListener("deviceready", onDeviceReady, false);
```

#### onDeviceReady

Cuando PhoneGap se inicia, invoca a esta función. No recibe parámetros ni devuelve valores. 

Realiza las siguientes acciones:

* Fija la orientación de la aplicación a modo apaisado.
* Bloquea la suspensión de pantalla (la pantalla siempre permanece activa).
* Crea enlace del botón Drive a la vista asociada.  
* Crea enlace del botón Code a la vista asociada.  

##### Código fuente

```javascript
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
    window.plugins.orientationchanger.lockOrientation('landscape');
    window.plugins.insomnia.keepAwake();

    document.getElementById('btDrive').addEventListener('click', function () {
        location.href = "drive.html"
    });
    document.getElementById('btCode').addEventListener('click', function () {
        location.href = "code.html"
    });
}
```

### drive.js

Este fichero contiene las funciones que proporcionan la funcionalidad del modo de control Drive. Además de inicializar distintos componentes, se encarga de capturar los valores del acelerómetro y los cambios en la barra desplegable, para los comandos asociados a estos eventos en forma de comando al robot mediante un WebSocket. Por otra parte, también establece la dirección desde la que se puede visualizar la cámara, así como mostrar distintas notificaciones `toast`.


#### Variables globales

Se definen las siguientes variables globales:

* `watchID` Evento asociado a la lectura del accelerómetro.
* `websocket` Objeto de conexión con el servidor.
* `isSending` Variable booleana. Si está activa (`true`) lee los valores del acelerómetro y envía los comandos al servidos.

También se añade un evento, que cuando PhoneGap se inicie llame a la función de inicialización.

```javascript
var watchID = null;
var websocket = null;
var isSending = false;

document.addEventListener("deviceready", onDeviceReady, false);
document.getElementById("overlay").onclick = onTap;
```

#### onDeviceReady

Función de inicialización. Es invocado por PhoneGap una vez ésta ha cargado. No recibe ni devuelve valores.

Realiza las siguientes acciones:

* Fija la orientación de la aplicación a modo apaisado.
* Bloquea la suspensión de pantalla (la pantalla siempre permanece activa).
* Crea enlace del `overlay` a la función asociada.  El overlay es una capa situada encima de la señal de la cámara que captura los toques de pantalla que activan y desactivan el envío de comandos.
* Especifica la dirección del servidor de la cámara.
* Crea un enlace del evento `backbuton` (botón volver/atrás del dispositivo) a la función asociada.  
* Inicia la comunicación WebSocket.
* Establece la funcionalidad que ocurre al establecerse la conexión.
  * Muestra un mensaje `toast` indicando el mensaje `Connected. Tap to start`
* Establece la funcionalidad que ocurre al recibir un mensaje.
  * En principio no se reciben mensajes, por lo que únicamente se muestran en la consola de debug (para pruebas y depuración).
  * Establece la funcionalidad asociada al evento producido por un error en el WebSocket.
    * Cierra la comunicación y vuelve a invocar a la función de inicialización.

##### Código fuente

```javascript
function onDeviceReady() {
    window.plugins.orientationchanger.lockOrientation('landscape');
    window.plugins.insomnia.keepAwake();

    document.getElementById("overlay").onclick = onTap;
    document.getElementById("camFrame").src = "http://" + SERVER_IP + ":" + SERVER_CAM_PORT;
    document.addEventListener("backbutton", onBackKeyDown, false);

    websocket = new WebSocket(WEBHOST);

    websocket.onopen = function (evt) {
        toast("Connected. Tap to start")
    };

    websocket.onmessage = function (msg) {
        console.log(msg)
    };

    websocket.onerror = function (evt) {
        websocket.close();
        onDeviceReady();
    };
}
```

#### onBackKeyDown

Esta funcion se invoca cuando el usuario presiona el botón atrás (o volver) del dispositivo Android. No recibe ni devuelve ningún valor.

Las acciones que realia son:
* Cierra la conexión WebSocket
* Redirige a la pantalla de inicio.

##### Código fuente

``` javascript
function onBackKeyDown() {

    websocket.close();
    window.location.href = "index.html";
}
```

#### onTap

Esta función se invoca cuando el usuario toca la pantalla. Las acciones que esto se desencadena son:
* Si el robot no está emitiendo, llama a la función que activa el acelerómetro y envía los comandos asociados.
* Si el robot sí está emitiendo, invoca a la función que detiene la lectura del acelerómetro y el envío de comandos de desplazamiento.

Esta función no recibe parámetros, y no devuelve ningún valor.

##### Código fuente

``` javascript
function onTap() {
    if (isSending) {
        stopCharlie();
    }
    else {
        startCharlie();
    }
}
```

#### startCharlie

Inicia el acelerómetro y asocia la función que envía los comandos de desplazamiento por WebSocket al robot. 
Cada vez que el acelerómetro realiza una lectura, llama a la función `onAccelerometerChanged`, encargada de enviar el comando de movimiento asociado.

Concretamente, las acciones que realiza son las siguientes:

* Activa la lectura del acelerómetro a la frecuencia de lectura definida en la variable .`ACCELEROMETER_FREQUENCY` definida en el fichero `values.js`.
* Asocia el evento que provoca la lectura del acelerómetro a la función `onAccelerometerChanged`.
* Establece el estado `enviando`.
* Muestra un triángulo en la pantalla para que el usuario reconozca visualmente que la opción está activada.
* Muestra un mensaje `toast` con el siguiente contenido: `STARTED. Tap to stop.`

Esta función no recibe parámetros, y no devuelve ningún valor.

##### Código fuente

``` javascript
function startCharlie() {
    var options = { frequency: ACCELEROMETER_FREQUENCY };
    watchID = navigator.accelerometer.watchAcceleration(onAccelerometerChanged, onAccelerometerError, options);

    isSending = true;
    document.getElementById("play").style.display = 'block';
    toast("STARTED. Tap to stop.");
}
```

#### stopCharlie

Envía el comando de frenado y detiene la lectura del acelerómetro.

Las acciones que realiza son las siguientes:
* Envía el comando `CMD_STOP`
* Detiene la lectura de valores del acelerómetro.
* Establece el estado `No enviando`
* Oculta el triángulo de la pantalla, para indicar al usuario que se ha detenido el envío de comandos de desplazamiento.
* Muestra mediante un `toast` el mensaje `STOPPED. Tap to start.`.

Esta función no recibe parámetros, y no devuelve ningún valor.

##### Código fuente

``` javascript
function stopCharlie() {
    var msg = new Uint8Array(3)
    msg[0] = CMD_STOP;
    msg[1] = CMD_NOPARAM
    msg[2] = CMD_NOPARAM
    websocket.send(msg)

    if (watchID) {
        navigator.accelerometer.clearWatch(watchID);
        watchID = null;
    }

    isSending = false;

    document.getElementById("play").style.display = 'none';
    toast("STOPPED. Tap to start.");
}
```

#### onAccelerometerChanged

Esta función es invocada cuando el dispositivo Android ha realizado una lectura del acelerómetro.
Recibe el valor del acelerómetro y envía el comando de desplazamiento asociado a esta función.

* Crea un mensaje de 3 bytes cada byte contiene:
  * `CMD_MOVE_FORWARD` El comando de movimiento de avance mediante velocidad y balance.
  * Velocidad. Se mapea el valor X del acelerómetro desde [0,1000] hasta [0,255], es decir el rango de posibles valores contenido en un byte. El parámetro `true` de la llamada a la función `map` indica que se aplica la función de restricción del rango, de forma que el valor de salida siempre esté dentro del rango de los bytes.
  * Balance. Se mapea el valor Y del acelerómetro desde [-1000,1000] hasta [0,255], es decir el rango de posibles valores contenido en un byte. El parámetro `true` de la llamada a la función `map` indica que se aplica la función de restricción del rango, de forma que el valor de salida siempre esté dentro del rango de los bytes.
* Envía el comando mediante WebSocket.

#### Parámetros

Recibe el objeto `acceleration`, del que se pueden extraer 3 datos:
* `acceleration.x` Devuelve la lectura del eje X (empleado para la velocidad).
* `acceleration.x` Devuelve la lectura del eje Y (empleado para el balance).
* `acceleration.z` Devuelve la lectura del eje Z (no empleado).

Esta función no devuelve ningún valor.

##### Código fuente

``` javascript
function onAccelerometerChanged(acceleration) {
    var msg = new Uint8Array(3);
    msg[0] = CMD_MOVE_FORWARD;
    msg[1] = map(acceleration.x * 100, 0, 1000, 255, 0,true);
    msg[2] = map(acceleration.y * 100, -1000, 1000, 0, 255,true);

    var element = document.getElementById('accelerometer');
    websocket.send(msg)
}
```

#### onAccelerometerError

Muestra por consola el mensaje de error producido por una lectura fallida del acelerómetro.

No devuelve ningún valor.

##### Parmateros

* `a` Mensaje asociado al error.

##### Código fuente

``` javascript
function onAccelerometerError(a) {
    console.log('onAccelerometerError!' + a);
}
```

####

Esta función es invocada cuando el desplazable vertical cambia de posición. Envía el comando asociado al movimiento del servo.

Envía un comando de 2 bytes con los siguientes valores:
* `CMD_SERVO` Comando de control de posición del servo.
* Valor a establecer.

Esta función no recibe parámetros, y no devuelve ningún valor.

##### Código fuente

``` javascript
function moveServo(){
    var msg = new Uint8Array(2)
    msg[0] = CMD_SERVO;
    msg[1] = document.getElementById("slider").value;
    websocket.send(msg)
}
```


### code.js

En este fichero se define la lógica asociada a al modo de control `Code`, o modo de programación grafica. Concretamente se encarga de la inicialización de componentes, las comunicaciones WebSocket, y comandos relacionados con el tratamiento de los bloques creados (crear, borrar, guardar y mostrar código)

#### blocklyLoaded

Se invoca a esta función una vez Blockly está completamente cargado. Incrusta el objeto Blockly dentro de `window`, para que las funciones de Blockly puedan ser empleadas en todo el código.

#### Parámetros

* `blockly` : El objeto Blockly, que contiene todas las funciones del lenguaje.

Esta función no devuelve ningún valor.

##### Código fuente

``` javascript
// Called once Blockly is fully loaded.
function blocklyLoaded(blockly) {
    window.Blockly = blockly;
}
```

#### onLoad

Función de inicialización. Se invoca una vez se ha cargado la página.

Realiza las siguientes acciones.
* Asocia el evento resultante de pulsar sobre `Show code` a la función `showCode`.
* Asocia el evento resultante de pulsar sobre `Execute` a la función `sendToRaspi`.
* Asocia el evento resultante de pulsar sobre `Save` a la función `save`.
* Asocia el evento resultante de pulsar sobre `Load` a la función `load`.
* Asocia el evento resultante de pulsar sobre `Clear` a la función `clearWorkspace`.
* Crea un evento para que cuando PhoneGap se haya cargado llame a la función `onDeviceReady`.

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function onLoad() {
    document.getElementById('test').onclick = showCode;
    document.getElementById('btSend').onclick = sendToRaspi;
    document.getElementById('btSave').onclick = save;
    document.getElementById('btLoad').onclick = load;
    document.getElementById('btClear').onclick = clearWorkspace

    document.addEventListener("deviceready", onDeviceReady, false);
}
```

#### onDeviceReady

Esta función es invocada cuando PhoneGap ha terminado de cargarse.

Establece la función asociada al evento `backButton` (cuando el usuario pulsa sobre el botón volver/atrás de su dispositivo Android).

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
// PhoneGap is loaded and it is now safe to call PhoneGap methods
function onDeviceReady() {
    // Register the event listener
    document.addEventListener("backbutton", onBackKeyDown, false);
}
```

#### onBackKeyDown

Esta función se invoca cuando el usuario presiona el botón atrás (o volver) del dispositivo Android. No recibe ni devuelve ningún valor.

Redirige a la pantalla de inicio.

##### Código fuente

``` javascript
function onBackKeyDown() {
    window.location.href = "index.html";
}
```

#### showCode

Muestra mediante un mensaje emergente la traducción de los Bloques a Python.

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function showCode() {
    alert(Blockly.Python.workspaceToCode())
}
```

#### save

Almacena el espacio de trabajo en formato XML dentro de `localStorage`.

Las acciones que realiza son las siguientes:
* Muestra un mensaje emergente, sugiriendo un nombre.
* Almacena el espacio de trabajo
* Muestra un mensaje indicado que se ha guardado satisfactoriamente.

El nombre que sugiere por defecto es 's' seguido del número de scripts actualmente almacenados. Por ejemplo, si no hay ningún script guardado, sugiere el nombre `s0`.


Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function save() {
    var n = window.prompt("Save as ", 's' + localStorage.length);
    if (n) {
        var xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace);
        var xml_text = Blockly.Xml.domToText(xml);
        localStorage.setItem(n, xml_text)
        alert("Saved as " + n)
    }
}
```

#### load

Importa un espacio de trabajo almacenado al espacio actual. NO elimina los posibles elementos que ya se encuentren en el espacio de trabajo.

Las acciones que realiza son las siguientes:
* Muestra un mensaje que indica todos los espacios guardados y sugiere cargar el último elemento de la lista mostrada.
* Carga el espacio seleccionado.

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function load() {
    var text = "Stored files: \n";
    var n = ""
    for (var storedName in localStorage) {
        text += storedName + '\n';
        n = storedName;
    }
    text += "Load program name: ";

    var n = window.prompt(text, n);
    if (n) {
        var xml_text = localStorage.getItem(n)
        var xml = Blockly.Xml.textToDom(xml_text);
        Blockly.Xml.domToWorkspace(Blockly.mainWorkspace, xml);
    }
}
```

#### sendToRaspi

Traduce el espacio de trabajo a Python, y envía el código resultante al servidor mediante WebSocket.

Las acciones que realiza son las siguientes:
* Traduce el espacio de trabajo a Python
* Establece una conexión mediante WebSocket con el servidor.
* Define los eventos asociados al websocket.
  * Al iniciarse la conexión, envía la traducción a Python y muestra un `toast` con el mensaje `Sent`
  * Al cerrar la conexión o recibir un mensaje (esto no ocurre nunca), muestra un mensaje en la consola de debug.
  * Si ocurre un error en el WebSocket, cierra la conexión y muestra un `toast` con el mensaje `Error sending the code. Please try again.`

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function sendToRaspi() {
    var code = Blockly.Python.workspaceToCode()
    var websocket = new WebSocket(WEBHOST);

    websocket.onopen = function (evt) {
        websocket.send(code);
        console.log("sent:\n" + code + '\n');
        toast("Sent")
    };

    websocket.onclose = function (evt) {
        console.log("disconnected\n");
    };

    websocket.onmessage = function (evt) {
        console.log("Received: " + evt.data);
    };

    websocket.onerror = function (evt) {
        websocket.close();
        toast("Error sending the code. Please try again.")
    };
}
```

#### clearWorkspace

Muestra un mensaje de confirmación, y si se acepta elimina todos los bloques contenidos en el espacio de trabajo.

Esta función no recibe parámetros. No devuelve ningún valor.

##### Código fuente

``` javascript
function clearWorkspace() {
    if (confirm('Are you sure you want to delete the workspace?')) {
        Blockly.mainWorkspace.clear()
    }
}
```









### customBlocks.js 

Este fichero define los bloques propios y sus traducciones a Python.

Para cada bloque, se definen dos objetos. La descripción del bloque y la función de traducción a Python. En el apartado [Blockly](Blockly) está disponible una explicación detallada de cómo se realiza esta traducción.

#### Palabras Reservadas

Con el objetivo de prevenir colisiones entre la ejecución de los scripts realizados en en el modo de programación gráfico y el código Python del servidor, Blockly permite declarar una serie palabras reservadas que impiden que una función de traducción declare una variable o función con un nombre que ya está siendo empleado por el código del servidor, o bien es una función nativa del lenguaje Python.

Por ejemplo, algunos bloques emplean la función auxiliar `sendToArduino` para enviar comandos a Arduino. Si definimos mediante bloques gráficos una función con el mismo nombre (es decir, `sendToArduino`), cuando se realice la traducción a Python, Blockly renombrará esta función añadiéndole un sufijo numérico que prevendrá la sobreescritura de la función original.

![](https://dl.dropboxusercontent.com/u/36785744/Charlie/reservedWords.PNG)

Si no hubiésemos incluido la palabra reservada `sendToArduino`, la función original sería sobreescrita.

Por tanto, mediante esta instrucción se definen todas las palabras reservadas:

```javascript
//Add reserved word to prevent collisions with python server
Blockly.Python.addReservedWords("logging,SERVER_IP,WS_PORT,LEDPIN,SERVOPIN,ECHOPIN,TRIGPIN,SONARTIMEOUT,DUE_PORT,DUE_BAUDS,initSonar,readSonar,destructSonar,moveServo,sendToArduino,receiveFromArduino,executeScript,map,WebServer,WebSocket,self,parser,OptionParser,options,args,SimpleSSLWebSocketServer,SimpleWebSocketServer,close_sig_handler,server,sys,servo,serialArduino,signal");
```

#### Movimiento de avance

##### Definición del bloque

La definición del bloque contiene principalmente los siguientes elementos:
* Un campo con el texto `Move straight`
* Una lista desplegable, cuyo valor se almacena en la variable `speed`, con los siguientes elementos:
  * `slow` Esta opción devuelve el valor  de la variable `SLOW_SPEED`, definida en `values.js`.
  * `medium` Devuelve el valor  de la variable `MEDIUM_SPEED`, definida en `values.js`.
  * `fast` Devuelve el valor  de la variable `FAST_SPEED`, definida en `values.js`.
* Declara que puede tener instrucciones precedentes y sucesoras, y así permitir encadenar varios bloques.

```javascript
Blockly.Blocks['move_fwd'] = {
    init: function() {
        this.setHelpUrl('https://github.com/monkeyserna/charlie/wiki/');
        this.appendDummyInput()
            .appendField("Move straight")
            .appendField(new Blockly.FieldDropdown([
                ["slow", SLOW_SPEED.toString()],
                ["medium", MEDIUM_SPEED.toString()],
                ["fast", FAST_SPEED.toString()]
            ]), "speed");
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip('');
    }
};
```

##### Función de traducción a Python

Recupera el valor `speed` (la lista desplegable) y define la traducción a Python como una llamada a la función auxiliar que envía comandos a Arduino (no devuelve ningún valor).

Los argumentos de esta función son cada uno de los 3 bytes que ocupa el mensaje que será enviado a Arduino, donde el primero es el comando y los dos restantes parámetros. 

Para obtener más información acerca de los distintos comandos que puede recibir Arduino, consulta el apartado [Comandos](https://github.com/monkeyserna/charlie/wiki/Servidor-de-comandos#comandos-arduino).

El valor las definiciones generales (como `PYT_SEND` o las definiciones de los comandos) se especifica el apartado [values.js](#valuesjs).

```javascript
Blockly.Python['move_fwd'] = function(block) {
    var dropdown_speed = block.getFieldValue('speed');
    return PYT_SEND + '(' + CMD_BOTH_MOTORS + ',' + dropdown_speed + ',' + CMD_NOPARAM + ')\n';
};
```

#### Giro a la izquierda

##### Definición del bloque

La definición del bloque contiene principalmente los siguientes elementos:
* Un campo con el texto `Move left`
* Una lista desplegable, cuyo valor se almacena en la variable `speed`, con los siguientes elementos:
  * `slow` Esta opción devuelve el valor  de la variable `SLOW_SPEED`, definida en `values.js`.
  * `medium` Devuelve el valor  de la variable `MEDIUM_SPEED`, definida en `values.js`.
  * `fast` Devuelve el valor  de la variable `FAST_SPEED`, definida en `values.js`.
* Declara que puede tener instrucciones precedentes y sucesoras, y así permitir encadenar varios bloques.

```javascript
Blockly.Blocks['move_l'] = {
    init: function() {
        this.setHelpUrl('https://github.com/monkeyserna/charlie/wiki/');
        this.appendDummyInput()
            .appendField("Move left")
            .appendField(new Blockly.FieldDropdown([
                ["slow", SLOW_SPEED.toString()],
                ["medium", MEDIUM_SPEED.toString()],
                ["fast", FAST_SPEED.toString()]
            ]), "speed");
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip('');
    }
};
```

##### Función de traducción a Python

Recupera el valor `speed` (la lista desplegable) y define la traducción a Python como una llamada a la función auxiliar que envía comandos a Arduino (no devuelve ningún valor). 

Los argumentos de esta función son cada uno de los 3 bytes que ocupa el mensaje que será enviado a Arduino, donde el primero es el comando y los dos restantes parámetros. 

Para obtener más información acerca de los distintos comandos que puede recibir Arduino, consulta el apartado [Comandos](https://github.com/monkeyserna/charlie/wiki/Servidor-de-comandos#comandos-arduino).

El valor las definiciones generales (como `PYT_SEND` o las definiciones de los comandos) se especifica el apartado [values.js](#valuesjs).

```javascript
Blockly.Python['move_l'] = function(block) {
    var dropdown_speed = block.getFieldValue('speed');
    return PYT_SEND + '(' + CMD_LEFT_MOTOR + ',' + dropdown_speed + ',' + CMD_NOPARAM + ')\n';
};
```









#### 

##### Definición del bloque

```javascript

```

##### Función de traducción a Python

```javascript

```