viernes, 27 de abril de 2012

Need for Speed

Bueno, despues de un tiempo vuelvo a la carga, por un lado con un modulo TM1638 que me quema en las manos, y por otro con la pregunta que me hicieron de si era posible aumentar la velocidad de comunicacion entre el arduino y el script en python.
Empezemos por lo segundo, si, se puede, solo hay que en arduino modificar la primera linea de la funcion setupserial() y cambiar el 9600 por 115200, por ejemplo, y despues en el script en python cambiar la linea:
se = serial.Serial(11)
por
se = serial.Serial(11,115200)
(11 es por que a mi el arduino me aparece como COM12 y es una menos)


El TM1638 es un modulo que venden entre otros sitios en dealextreme, lo hay con los numeros en verde y en rojo, ademas de eso tiene 8 led bicolores (rojo+verde y en teoria tambien naranja, aunque a mi el naranja no se me ve muy naranja) control de brillo y 8 pulsadores, y se pueden conectar en cadena hasta 6, funciona a 5 voltios, utiliza 2 lineas de I/O + una por modulo, y existe una libreria para controlarlo con arduino.

Y por otro lado esta rfactor, un simulador de carreras para windows que... bueno.... es perfecto, o casi.

En opencockpits hay un plugins, que permite que rfactor sea un cliente IOCP, asi que basta con ejecutar SIOC, y ya tenemos el enlace rfactor->arduino.

Y algunas lineas de codigo mas tarde tenemos algunas vueltas al circuito de almeria, por si un dia de estos me doy unas vueltas en un porsche. jeje

La parte tecnica:

Con esto he llegado al limite de comunicaciones con el arduino, he tenido que reducir muchisimo el trasvase de datos dividiendo por  10000 las rpm.
No se si esto es culpa del arduino o es culpa del plugin de rfactor, pero es algo a tener en cuenta, si necesitamos datos a mucha velocidad.
En el codigo hay algunas cosas que sobra de pruebas, pero al menos funciona:

El script de python es el de siempre (salvo por la velocidad del puerto), no lo voy a poner.

Estas son las lineas que descomente en el archivo rf_IOPC.ini de configuracion del plugin:
var=0101,RPM ; Rev x Min x100
var=0102,SPEED ; Speed  x100
var=0105,GEAR ; Gear -1=reverse, 0=neutral, 1+=forward gears
var=0110,LAP_START; time this lap was started x1000

Este es el programa que ejecuto en SIOC:
Var 0001, name rpm, Value 0
Var 0002, name speed, Value 0
Var 0003, name gearn, Value 0
Var 0004, name lapt, Value 0

Var 0101, name rf_rpm
{
  &rpm = &rf_rpm / 10000
}

Var 0102, name rf_speed
{
  &speed = &rf_speed / 100
}
Var 0110, name rf_lap_time
{
  &lapt = &rf_lap_time / 100
}

Var 0105, name rf_gear
{
  IF &rf_gear = -1      
  {
    &gearn = 1
  }
  ELSE
  {
    &gearn = 0
  }
}

Y por ultimo este es el programa que ejecuto en el arduino:


#include <TM1638.h>
// Variables IOCP a utilizar:
#define RF_rpm 1
#define RF_speed 2
#define RF_gearn 3
#define RF_lap_start 4
#define RF_gear 105
// variables globales necesarias para la decodificacion.
int p=0; // el paso en la decodificacion.
int dat,valor;

TM1638 module(8, 9, 10);
int rpm,spd,gea,gean,lapt;
int mode=0;

#define v0 1

#define v1 13
#define v2 26
#define v3 39
#define v4 52

#define v5 60
#define v6 68
#define v7 76

#define v8 84

#define rpm_step 10.5
void updaterpm(){
  if (rpm >= v8) {
    module.setupDisplay(true,7);
  } else {
    module.setupDisplay(true,2);
  }
  // Los verdes
  if (rpm < v0) {
    module.setLEDs(0x0000); 
  }
  else if ( rpm < v1 ) {
    module.setLEDs(0x0100); 
  }
  else if ( rpm < v2 ) {
    module.setLEDs(0x0300); 
  }
  else if ( rpm < v3 ) {
    module.setLEDs(0x0700); 
  }
  else if ( rpm < v4 ) {
    module.setLEDs(0x0F00); 
  }
  else if ( rpm < v5 ) {
    module.setLEDs(0x1F00); 
    // Los naranjas
  }
  else if ( rpm < v6 ) {
    module.setLEDs(0x3F20); 
  }
  else if ( rpm < v7 ) {
    module.setLEDs(0x7F60); 
  }
  else if ( rpm < v8 ) {
    module.setLEDs(0xFFE0); 
    // Todos rojos
  }
  else {
    module.setLEDs(0x00FF); 
  }
}
void updatespeed(){
  module.setDisplayToDecNumber(spd,0);
}
void updategear(){
  if (gean == 1 ) {
    module.setDisplayToString("r",0,0);
  }
  else if (gea == 0) {
    module.setDisplayToString("n",0,0);
  }
  else{
    module.setDisplayDigit(gea,0,0);
  }
}
void updatetime(){
  module.setDisplayToDecNumber(lapt,0);
}


void setupserial(void){
  Serial.begin(115200);
  Serial.print("Arn.Inicio:");
  Serial.print(RF_rpm);
  Serial.print(":");
  Serial.print(RF_speed);
  Serial.print(":");
  Serial.print(RF_gear);
  Serial.print(":");
  Serial.print(RF_gearn);
  Serial.print(":");
  Serial.print(RF_lap_start);
  Serial.println(":");
}


int isnum(int n){
  if ((n>47) && (n<58)) return 1;
  else return 0;
}

void enviapares(int dat, int valor){
  Serial.print("Arn.Resp:");
  Serial.print(dat);
  Serial.print("=");
  Serial.print(valor);
  Serial.println(":");
}

// Proceso de las comunicaciones serie
void procesapares(int dat, int valor){
  if (dat == RF_rpm){
    rpm=valor;
    if (mode == 0){
      updaterpm();
    }
  }
  if (dat == RF_speed){
    spd=valor;
    if (mode == 0){
      updatespeed();
    }
  }
  if (dat == RF_gear){
    gea=valor;
    if (mode == 0){
      updategear();
    }
  }
  if (dat == RF_gearn){
    gean=valor;
    if (mode == 0){
      updategear();
    }
  }
  if (dat == RF_lap_start){
    lapt=valor;
    if (mode == 1){
      updatetime();
    }
  }
}

void serialstuff(void) {
  int c=0,pr;
  pr=0;
  if (Serial.available()==0) return;
  c=Serial.read();
  do {
    switch (p){
    case 0:
      if (c=='A'){
        p=1;
      }
      pr=1;
      break;
    case 1:
      if (c=='r') {
        p=2;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 2:
      if (c=='n') {
        p=3;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 3:
      if (c=='.') {
        p=4;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 4:  // VALOR ESPECIAL : primera Bifurcacion de respuesta
      switch (c) {
      case 'R':
        p=5;
        pr=1;
        break;
      default:
        p=0;
        break;
      }
      break;
    case 5:
      if (c=='e') {
        p=6;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 6:
      if (c=='s') {
        p=7;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 7:
      if (c=='p') {
        p=8;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 8: 
      if (c==':'){
        p=9;
        pr=1;
        dat=0;
      }
      else {
        p=0;
      }
      break;
    case 9: 
      switch (c){
      case 13: 
        p=20;
        pr=1;
        break; 
      case '=':
        if (dat==0) {
          p=0;
        }
        else {
          p=10;
          pr=1;
        }
        break;
      default:
        if (isnum(c)) {
          dat = 10*dat + (c-48);
          pr=1;
        }
        else {
          p=0;
        }
        break;
      }
      break;
    case 10: 
      if (isnum(c)){
        valor = (c-48);
        p=11;
        pr=1;
      }
      else {
        p=0;
      }
      break;
    case 11: 
      switch (c) {
      case ':':
        procesapares(dat,valor);
        p=8;
        break;
      default:
        if (isnum(c)){
          valor = 10*valor + (c-48);
          pr=1;
        }
        else {
          p=0;
        }
        break;
      }
      break;
    case 20: 
      if (c==10) pr=1;
      p=0;
      break;
    default:
      p=0;
      pr=1;
      break;
    }
  }
  while (pr==0);
}


void setup(){
  setupserial();
  module.setupDisplay(true,7);
  module.setDisplayToString("Init");
  delay(1000);
  module.setupDisplay(true,2);
  module.setDisplayToString("done");
  delay(1000);
  module.clearDisplay();
}


void loop(){  // aqui solo miramos si cambio una tecla y actualizamos el lcd.
  if ((mode != 0) && (module.getButtons() == 0b00000001)) {
    module.setDisplayToString("        ");
    updategear();
    updatespeed();
    mode = 0 ;
  }
  if ((mode !=1) && (module.getButtons() == 0b10000000)) {
    module.setDisplayToString("        ");
    updatetime();
    mode = 1 ; 
  }
  // La parte de comunicaciones
  serialstuff();
}