Electrónica y Programación en Personal
«Si no se soluciona con un script en Python entonces no es viable»

por Yeison Cardona el 05 de marzo de 2012 a las 15:01 UTC
Actualizaciones:
08/Marzo/2012: Se agregan mas funciones soportadas.
11/Marzo/2012: El código vuelve a funcionar después de una actualización en PinguinoX.2
14/Marzo/2012: Se actualiza ambos códigos (8-bit y 32-bit)



Si bien, una línea de comandos para Pinguino podemos obtenerla usando Pynguino, esta está muy limitada al usar Python (de pronto no a todo el mundo le gusta tanto Python como a mi), así que podríamos generar un código en el que la interpretación del comando se haga directamente en el microcontrolador (a diferencia de Pynguino).

De qué se trata?:
Se trata de poder cambiar estados y leer pines de Pinguino desde simples ordenes enviadas como cadenas de texto por CDC, desde cualquier lenguaje de programación.
Lo que básicamente hace es recibir una cadena de texto por CDC ejemplo "pinMode(13,OUTPUT)" e interpretarla como si de una orden se tratara. Es decir, a diferencia de la dependencia de Pynguino con Python, este código permite interfazarce con cualquier lenguaje de programación (he ahí el potencial).

Cómo funciona:
Es bastante simple (no se cómo no se me ocurrió antes), recibe una cadena de texto, por ejemplo:
"pinMode(13,OUTPUT)" el programa toma esta cadena he identifica el bloque pinMode, luego aísla los parámetros para esta función (todos como cadenas), entonces arma y ejecuta la función construida.

veamos un ejemplo con una sola función (digitalWrite):
Supongamos que ha ingresado la cadena digitalWrite(13,HIGH)
else if (strncmp(*lectura, "digitalWrite", 12)==0){
 int cont=0;
 for (bloque = strtok(*lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
  switch (cont){
  case 0:
   break;
  case 1:
   pin=atoi(bloque);
   break;
  case 2:
   if (strncmp(bloque, "HIGH", 4)==0) digitalWrite(pin,HIGH);
   else if (strncmp(bloque, "LOW", 3)==0) digitalWrite(pin,LOW);
   CDC.printf("Done\n");
   break;
  } 
  cont+=1;
 }
}


Entonces: En la línea 1 comparamos si la cadena de entrada (digitalWrite(13,HIGH)) inicia con la cadena digitalWrite, al ser cierto se ejecuta el bloque. La línea 2 en un bucle para separar los parámetros, en la primera vuelta (línea 5) se captura un "digitalWrite(" (que es innecesario), en la segunda (línea 7) se captura un "13", el cual en convertido a entero, en la tercera (línea 10) se captura un "HIGH)", en esta última línea tenemos todo lo necesario para construir el comando, comparamos para saber si la última captura inicia con la cadena "HIGH" (línea 11) entonces ejecuta la orden respectiva (lo mismo en la línea 12) y terminamos enviando una cadena para informar que se ha terminado el bloque.
Código completo: Puede copiar el código directamente, este código ha sido probado correctamente en Pinguino 18F2550 y la revisión 305 de Pinguino X.2
/*-----------------------------------------------------
Author:  Yeison Cardona --
First release: 05/03/2012
Last release: 11/05/2012
Description: Control de la tarjeta pinguino mediante
comandos transparentes enviados por CDC.
-----------------------------------------------------*/

#include 
#include 
#define TotalPines 17
 
char lectura[21], *bloque;
int par1, valor, cont, i;
 
void setup() {
 }

void leer_cadena() {
    unsigned char valor_leido[21];
    unsigned char receivedbyte;
    int cont=1;
    while (cont){    
        receivedbyte=CDC.read(valor_leido);
        valor_leido[receivedbyte]=0;
        if (receivedbyte>0) cont=0;}
    strcpy(lectura,valor_leido);}
  
void loop() {
 
leer_cadena();
cont=0;
 
if (strncmp(lectura, "pinMode", 7)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){ 
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  if (strncmp(bloque, "INPUT", 5)==0) pinMode(par1,INPUT);
  else if (strncmp(bloque, "OUTPUT", 6)==0) pinMode(par1,OUTPUT);
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "digitalWrite", 12)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  if (strncmp(bloque, "HIGH", 4)==0) digitalWrite(par1,HIGH);
  else if (strncmp(bloque, "LOW", 3)==0) digitalWrite(par1,LOW);
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "analogWrite", 11)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  analogWrite(par1,atoi(bloque));
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "digitalRead", 11)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  CDC.printf("%d\n",digitalRead(atoi(bloque)));
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "analogRead", 10)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  CDC.printf("%d\n",analogRead(atoi(bloque)));
  break;
 } 
 cont+=1;
 }
}

else if (strncmp(lectura, "eepromRead", 10)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  CDC.printf("%d\n",EEPROM.read(atoi(bloque)));
  break;
 } 
 cont+=1;
 }
}

else if (strncmp(lectura, "eepromWrite", 11)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  bloque = strtok(bloque,")");
  EEPROM.write(par1, atoi(bloque));
  break;
 } 
 cont+=1;
 }
}

else if (strncmp(lectura, "delay", 5)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  delay(atoi(bloque));
  break;
 } 
 cont+=1;
 }
}
  
else if (strncmp(lectura, "delayMicroseconds", 17)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  delayMicroseconds(atoi(bloque));
  CDC.printf("Done\n");
  break;
 } 
 cont+=1;
 }
}
  
else if (strcmp(lectura, "allOutput")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,LOW);
 }
}

else if (strcmp(lectura, "allInput")==0){
 for (i=0;i<=TotalPines;i++)
  pinMode(i,INPUT);
}

else if (strcmp(lectura, "allHigh")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,HIGH);
 }
}

else if (strcmp(lectura, "allLow")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,LOW);
 }
}
  
else if (strcmp(lectura, "reset")==0)
 reset();
  
}
y también con unas modificaciones se tiene una versión para 32-bit. En PIC 32 Pinguino OTG hay que tener en cuenta que los pines especiales se llaman con el número de pin asociado es decir: A0-A7 --> 14-21 LED1 o GREENLED --> 13 LED2 o YELLOWLED --> 30 USERBUTTON --> 2
/*-----------------------------------------------------
Author:  Yeison Cardona --
First release: 05/03/2012
Last release: 11/05/2012
Description: Control de la tarjeta pinguino mediante
comandos transparentes enviados por CDC.
-----------------------------------------------------*/
 
char lectura[21], *bloque;
int par1, valor, cont, i;
 
#include 
#include 
#define TotalPines 21
 
void setup() {
 }

void leer_cadena() {
    unsigned char valor_leido[21];
    unsigned char receivedbyte;
    int cont=1;
    while (cont){    
        receivedbyte=CDC.read(valor_leido);
        valor_leido[receivedbyte]=0;
        if (receivedbyte>0) cont=0;}
    strcpy(lectura,valor_leido);}
  
void loop() {
 
leer_cadena();
cont=0;
 
if (strncmp(lectura, "pinMode", 7)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){ 
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  if (strncmp(bloque, "INPUT", 5)==0) pinMode(par1,INPUT);
  else if (strncmp(bloque, "OUTPUT", 6)==0) pinMode(par1,OUTPUT);
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "digitalWrite", 12)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  if (strncmp(bloque, "HIGH", 4)==0) digitalWrite(par1,HIGH);
  else if (strncmp(bloque, "LOW", 3)==0) digitalWrite(par1,LOW);
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "analogWrite", 11)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ",") ){
 switch (cont){
 case 0: break;
 case 1:
  par1=atoi(bloque);
  break;
 case 2:
  analogWrite(par1,atoi(bloque));
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "digitalRead", 11)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  CDC.printf("%d\n",digitalRead(atoi(bloque)));
  break;
 } 
 cont+=1;
 }
}
 
else if (strncmp(lectura, "analogRead", 10)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  CDC.printf("%d\n",analogRead(atoi(bloque)));
  break;
 } 
 cont+=1;
 }
}

else if (strncmp(lectura, "delay", 5)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  delay(atoi(bloque));
  break;
 } 
 cont+=1;
 }
}
  
else if (strncmp(lectura, "delayMicroseconds", 17)==0){
 for (bloque = strtok(lectura,"("); bloque != NULL; bloque = strtok(NULL, ")") ){
 switch (cont){
 case 0: break;
 case 1:
  delayMicroseconds(atoi(bloque));
  CDC.printf("Done\n");
  break;
 } 
 cont+=1;
 }
}
  
else if (strcmp(lectura, "allOutput")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,LOW);
 }
}

else if (strcmp(lectura, "allInput")==0){
 for (i=0;i<=TotalPines;i++)
  pinMode(i,INPUT);
}

else if (strcmp(lectura, "allHigh")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,HIGH);
 }
}

else if (strcmp(lectura, "allLow")==0){
 for (i=0;i<=TotalPines;i++){
  pinMode(i,OUTPUT);
  digitalWrite(i,LOW);
 }
}
}
Que hay que construir en el lenguaje de la computadora?: Para usar este código en Pinguino y poder controlarlo sólo basta con (después de tener el código cargando y ejecutándose en Pinguino) cualquier lenguaje:
  1. Establecer una comunicación con el puerto serie correspondiente a Pinguino.
  2. Enviar a este dispositivo la orden en forma de cadena ejemplo: "pinMode(0,INPUT)"
  3. Leer la línea enviada desde el mismo dispositivo (en caso de ser una lectura).
  4. Repetir el paso 2 y 3 para cada orden enviada.
Tal fácil como eso!!
Lista de comandos: Todos estos comando deben ser enviados como una cadena. Funciones (con parámetros) Son las mismas funciones del lenguaje de Pinguino.
  • pinMode: para usarlo simplemente basta con enviar por ejemplo "pinMode(13,OUTPUT)"
  • digitalWrite: enviar "digitalWrite(13,HIGH)"
  • analogWrite: enviar "analogWrite(12,450)"
  • digitalRead: enviar "digitalRead(0)" y entonces efectuar una lectura de línea.
  • analogRead: enviar "analogRead(13)" y efectuar una lectura de línea.
  • eepromRead: enviar "eepromRead(4)" y efectuar una lectura de línea.
  • eepromWrite: enviar "eepromWrite(5,12)"
  • delay: enviar "delay(250)"
  • delayMicroseconds: enviar "delayMicroseconds(500)"
Ordenes
No necesitan parámetros, se envían tal cual.
  • allOutput: define como salida todos los pines.
  • allInput: define como entrada todos los pines.
  • allHigh: pone todos los pines en alto.
  • allLow: pone todos los pines en bajo.
  • reset: efectúa un reset en el dispositivo .
Todos estos comandos ocupan un 79% de la memoria de un Pinguino 18F2550.
Alguna idea para mas comandos?
Ejemplo con Python: Como ya lo dije, este ejemplo puede ser ejecutado desde cualquier lenguaje de programación, o mediante cualquier método para enviar cadenas de texto a un dispositivo serie, yo usaré Python y PySerial porque es con lo que trabajo (posiblemente después suba algunos ejemplos usando otro lenguaje, o si me los envían con gusto los publico).
yeison@YeisonEng:~$ python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> pinguino=serial.Serial("/dev/ttyACM0")
>>> pinguino.write("pinMode(0,OUTPUT)"
17
>>> pinguino.write("digitalWrite(0,HIGH)")
20
>>> pinguino.write("digitalWrite(0,LOW)")
19
>>> pinguino.write("digitalWrite(0,HIGH)")
20
>>> pinguino.write("analogRead(13)")
14
>>> pinguino.readline()
'689\n'
>>> pinguino.write("analogRead(13)")
14
>>> pinguino.readline()
'215\n'
>>> pinguino.write("analogRead(13)")
14
>>> pinguino.readline()
'1021\n'
>>>


Conclusiones:
Es completamente viable desarrollar un código para interfazar Pinguino con cualquier lenguaje de programación (como se hizo con Pynguino pero sin que dependa de un lenguaje de programación específico) con ordenes "transparentes" enviadas por CDC (y USB en su momento).

Sería de mayor utilidad definir un código estable y desde ese momento comenzar a desarrollar diferentes bibliotecas, módulos o ejemplos en diferentes lenguajes.

También podría interesarte:

Comentarios:
JoE dijo:
5 de marzo de 2012 17:57
Excelente trabajo. Espero poder desarrollar algo en uno de los lenguajes que conozco (Java,Pascal,C,C++,C# ) para poder compartir con la comunidad.
Responder a JoE
Juan Pablo Toledo dijo:
7 de marzo de 2012 22:35
Te pasas, en serio este blog es una inspiración gigante. Acabas de hacer los primeros pasos de lo que hizo Hans Christoph Steiner con arduino (lo que él llamó firmata). Voy a intentar trabajar pingüino con puredata y te aviso los resultados. Salud y anarquía tropical. Si vienes a Venezuela te ganaste un almuerzo.
Responder a Juan Pablo Toledo
Alexis -Pax- Sanchez dijo:
13 de marzo de 2012 11:18
Revisando Yeison, voy a hacer unas pruebas con algunos lenguajes, matlab, principalmente... quizas agregar la posibilidad de controlar servos y paso a paso, seria un empuje increible, y un paso mas alla seria y creo que con este metodo se podria, agregar el control de una LCD... con esto se cubriria un GRAN y amplio espectro de posibilidades para este proyecto...
Responder a Alexis -Pax- Sanchez
Añadir un nuevo comentario:
Si desean una respuesta para su comentario sólo deben agregarme en G+ y hacer una mención a Yeison Cardona, así les podré responder lo antes posible.