Mostrando entradas con la etiqueta coches. Mostrar todas las entradas
Mostrando entradas con la etiqueta coches. Mostrar todas las entradas

viernes, 15 de marzo de 2013

Seguimos con los Coches.

Aparentemente esta entrada es casi identica a la anterior, pero por dentro tiene mucha mas miga.

Existe un software para simuladores (aviones o coches) disponible en www.x-sim.de y mientras lo investigaba para un futuro simulador con movimiento, descubri que tambien permite realizar paneles de instrumentos, sobre todo orientado a los simuladores de coches, asi que decidi actualizar el display.

Este software tiene varias ventajas, por un lado puede utilizarse con varios simuladores, simplemente configurando el perfil adecuado, por otro, tiene un modulo de comunicacion serie, por lo que eliminamos varias capas intermedias, recordemos que antes teniamos:
simulador->servidor iocp->SIOC->script en python->arduino 
y ahora se reduce a:
simulador->software x-sim->arduino

Desde aqui podeis bajaros el programa para arduino:
https://www.dropbox.com/s/nwc9pek3ynyj2qw/xsim_rpm5.ino

Esta ampliamente inspirado en el que aparece en este post: http://www.x-sim.de/forum/viewtopic.php?f=40&t=155

Tiene dos mejoras, por un lado la gestion de las RPM maximas y minimas, y por otro lado tiene una conexion serie mas fiable, que soporta sin problemas interrupciones y desconexiones del puerto serie.

Funcionamiento:

En el display numerico aparecen:
Indicador de Bandera (no funciona, deberia aparecer cuando hay bandera amarilla, pero el xsim no lo exporta).
La marcha actual.
La velocidad en km/h.

Los led se encienden progresivamente y se ponen todos rojos con brillo al alcanzar las maximas rpm, el valor de minimas rpm (cuando se enciende el primer led) y maximas (cuando se ponen en rojo) es configurable con los botones, este es su funcionamiento:
  • Boton 1 - Establece el modo F1, rpm minimas = 5000, rpm maximas = 18500
  • Boton 2 - Establece el modo GT, rpm minimas = 0, rpm maximas = 9000
  • Boton 3 - Recupera de la eeprom los valores de rpm personalizados (modo CU).
  • Boton 4 - Guarda en la eeprom los valores de rpm personalizados, al arrancar el arduino lee esto valores y los utiliza, asi para que siempre arranque en modo F1 pulsamos el boton 1, luego el 4 y confirmamos que queremos guardarlos pulsando otra vez el 1.
  • Boton 5 - decrementa el valor de rpm minimas
  • Boton 6 - incrementa el valor de rpm minimas
  • Boton 7 - decrementa el valor de rpm maximas
  • Boton 8 - incrementa el valor de rpm maximas

 Ademas de la parte visible del programa, las salidas 5 y 6 estan configuradas como servos de modelismo, de cara a experimentar con la simulacion de movimiento de 2DOF.

Configuracion de x-sim.de

El archivo de configuracion se puede bajar de aqui:
https://www.dropbox.com/s/ldc191rbhn2glzn/display.rn2

Basicamente el truco esta en configurar adecuadamente la parte matematica, asi como para el valor de velocidad y rpm utilizamos 16bits y un valor siempre positivo, hay que poner el valor maximo de esa señal en 65535, y ajustar el offset a la izquierda del todo.
Para las marchas, utilizamos 8bits, y el valor puede ser negativo, asi que ajustamos a un valor maximo de 128, simetrico, y el offset lo dejamos a 0.

La cadena que envia, usando el modulo SIO, cada 20ms, (puede ser menos), seria:
R~01~~04~S~02~G~03~~05~~06~~07~
Siendo 01 las RPM, 02 la velocidad, 03 la marcha, 04 la bandera, 05 reservado para un futuro force feedback del pedal de freno (8bits), y 06 y 07 (8bits) las posiciones de los 2 servos para la simulacion de movimiento de la cabina.

Como veis, tiene alguna cosa añadida que no esta un implementada, pero para tener un display delante del volante esta perfecto

EDITADO: Nueva version del software, los led funcionan diferente segun sea F1 o GT. Aqui: https://www.dropbox.com/s/uwkqgulp2bohj7a/xsim_rpm6.ino



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();
}