Go Down

Topic: Adaptador para controles del volante de Corolla a estéreo Pioneer (1/2) (Read 345 times) previous topic - next topic

gatul

Nov 07, 2020, 06:04 am Last Edit: Nov 13, 2020, 12:54 am by gatul Reason: Corregidos algunos errores de tipeo
Finalmente me he decidido a presentar mi primer proyecto y primer contacto con Arduino.

Antes que nada aclarar que he programado en assembler para Z80, 8085, 6502, 8086, en ese órden.
Y lenguajes como Fort, Fortran, Cobol, Pascal y Turbo Pascal,  Basic, Visual Basic y Python
Finalmente Pic assembler gracias a Monty (¿Alguno se acuerda del robot Monty?).



Quiero decir, experiencia en programación tengo pero nada de C, menos de Arduino.

Bueno, volvamos a lo que me llevó a hacer el proyecto.

Hace unos meses adquirí un Toyota Corolla 2010 que trae un estéreo bastante básico, solo radio y lector de CDs. No trae entrada auxiliar ni puerto USB, menos aún conexión bluetooth. Lo más moderno que trae es control remoto en el volante.

Por eso decidí cambiarlo por un estéreo Pioneer MVH-285BT que tenía instalado en otro auto y que, para mis necesidades, es suficiente y tiene justamente lo que al del Corolla le falta, además tiene entrada para un control remoto cableado.

A pesar de que venden interfaces adaptadoras, no le veía sentido pagar por una de ellas más que el valor del estereo que estaba instalando, así que me puse a investigar con la idea de hacer algo con algún PIC ya que, como conté antes, hice con ellos algunas cosas aunque hace ya muchos años pero, bueno, eramos viejos conocidos.

El primer paso fue conseguir los esquemas eléctricos del auto para ver qué tenía en la botonera del volante y entender su funcionamiento. ¿Usaría algún protocolo como CANBUS? ¿Cómo sería el cableado?
Finalmente dí con el esquema necesario



Ya se develaron los primeros interrogantes:
- El botón [ MODE ] cierra a masa, es independiente, tiene su propio pin y podría fácilmente leerse con un puerto digital.
- Los otros, conmutan resistencias a masa, tienen un pin común de salida, podrían leerse con un ADC.
- Ningún protocolo con que liarse.
Nótese que hay resistencias pull-down en las salidas de la botonera (más adelante las denominaré "Rf").

Luego vino encontrar información sobre el funcionamiento del control remoto de Pioneer con similares interrogantes.
Dí con algunos links interesantes como este en donde encontre la información necesaria.



Esto me permitió ir evaluando cual PIC podría necesitar.
El 16F84A que tengo en una placa de un viejo proyecto no me iba a servir porque no tiene ADC, tendría que ir por un 16F628 o algo parecido (tampoco quería un PIC monstruoso para usar unas pocas entradas y salidas)

Hasta que la búsqueda me llevó a este link y me abrió la cabeza ya que Arduino ni se había cruzado por la mente porque nunca habia tocado uno.

Viendo las opciones, en principio, pensé usar directamente un ATtiny85, que alcanzaría justo, pero leyendo un poco más se me ocurrieron algunas cosas que podría agregar a futuro, así que en tren de experimentar y aprender me decidí por el Nano, y me hice de un par (uno para este proyecto y el otro para seguir "jugando").

Luego de hacer las correspondientes mediciones en el auto, así quedo la información unificada.



Después de realizar algunos cálculos, me pareció apropiado, además de la resistencia de referencia para el ADC que generará un divisor de tensión, poner otra en serie con la botonera "analógica" para separar un poco los niveles que tenía que medir.
Aquí los valores que esperaba medir luego del agregado.



Y éste es el primer esquema a mano alzada.



Este esquema tenía todavía un gran interrogante: ¿Cómo conmutar las resistencias?
Primero pensé en usar reed relés, pero se encarecería bastante el proyecto. ¿Podría hacerlo solo con transistores?.
En resumen, todavía tenía que instruirme un poco más, pero el esquema brinda una idea general de lo que pensaba hacer.

Ahora tocaba el turno del soft...
Lo que me resultaría más práctico sería poder trabajar con el celular (el mío tiene función OTG) ya que me evitaría tener que llevar la notebook al garage donde guardo el auto, sobre todo teniendo en cuenta que mi notebook HP530 es vieja, tiene la batería muerta, y en el garage no tengo ningún toma cercano.
Rápidamente di con ArduinoDroid para programar y con Sofia, que a pesar de sus limitaciones, me serviría perfectamente para emular y probar el soft sin siquiera usar el protoboard.
Y en la notebook instalé la IDE para Ubuntu y UnoArduSim que corre perfectamente con Wine.

Ya teniendo el soft, lo primero fue generar el sketch que me permitiera verificar la lectura de los botones (principalmente los "analógicos").

Code: [Select]
// CorollaSWButtonsReader.ino
// Lee los controles del volante de Toyota Corolla

const unsigned long DEBOUNCE_TIME = 20;

const int NO_BTN = 3000;
const int MODE_BTN = 2000;
const int ADC_TOP = 950;

const int PIN_MODEBTN = 2;
const int PIN_SWCTRL = A0;

const int PIN_LED = LED_BUILTIN;

int oldValue = NO_BTN;
int newValue = NO_BTN;

int ledState = LOW;

// funcion para chequear si hay algun boton pulsado
// devuelve el valor de ADC correspondiente al boton
// si no hay ninguno pulsado devuelve NO_BTN
// ** Corolla:
// cable 6= GND
// cable 8= [MODE] cierra a masa
// cable 7= [+][-][^][v] con resistencias

int get_btn_value() {

  if (digitalRead(PIN_MODEBTN) == LOW) return MODE_BTN;
  
  int r = analogRead(PIN_SWCTRL);
  if (r <= ADC_TOP) return r;
  return NO_BTN; // ninguno pulsado
}

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_MODEBTN, INPUT_PULLUP);
  pinMode(PIN_SWCTRL, INPUT);
  digitalWrite(PIN_LED, LOW);
  Serial.begin(9600);
  delay(3000); // para darme tiempo a abrir la terminal ;)
  Serial.println(F("Listo para leer botones"));
  Serial.println(F("-----------------------"));
  Serial.println("");
  delay(1000);
}

void loop() {
  newValue = get_btn_value();
  if (newValue != oldValue) {
    delay(DEBOUNCE_TIME);
    newValue = get_btn_value(); // confirma el boton pulsado
    if (newValue != oldValue) {
      oldValue = newValue;
      ledState = LOW;
      if (newValue != NO_BTN) {
        ledState = HIGH;
        if (newValue == MODE_BTN){
          Serial.println(F("Boton MODE"));
        }
        else {
          Serial.print(F("Valor ADC = "));
          Serial.println(newValue);
        }
      }
    }
    digitalWrite(PIN_LED, ledState);
  }
}


Los valores leídos fueron igual a los calculados +/- 1 en sucesivas lecturas, nada mal.

Finalmente quedaba el tema de la conmutación de resistencias que lo resolví mucho más rápido de lo suponía y con cero costo.

Me hice el siguiente planteo: Un botón no pulsado está dejando la resistencia "en el aire", lo que sería lo mismo que decir que está conectada a alta impedancia (en este caso infinita). La gran pregunta era si podía poner un pin de Arduino en alta impedancia, y sí, poniendo el pin como entrada y sin habilitar la resistencia pull-up queda en alta impedancia. De todos modos el pin respectivo nunca quedaría "en el aire" porque del estéreo sale una tensión de 3.3V con una resistencia interna de 10k.
Fin del asunto, manos a la obra.

El esquema en Eagle



El layout en Fritzing




gatul

Ahora sí, el código que, luego de varias mutaciones (por ej. originalmente hacía un doble parpadeo del LED testigo cada 30 seg., con un hermoso uso de millis() y toda la parafernalia, pero que finalmente no iba a ver porque queda oculto detrás del estéreo), es por demás sencillo y quedó así

Code: [Select]
// CorollaPioneerWR.ino

/*
 Adapta controles del volante de Toyota Corolla G10
 a estereos Pioneer con control remoto cableado.
 Probado en unidad MVH-285BT (3.3V @ 10kohms)
  
 Autor: Gatul - noviembre 2020
 
 --------------------------------------------------
 Cableado del control del volante.
 Cable 6 = GND
 Cable 7 = [^][v][+][-]
 Cable 8 = [MODE]

  Corolla    R >>  Pioneer     R
 ---------------+-----------------
  [MODE]     0  | [Src/Off]  1k2
   [^]      12  |  [Trk>>]   8k0
   [v]     350  |  [<<Trk]   11k
   [+]     1k0  |  [Vol+]    16k
   [-]     3k1  |  [Vol-]    24k

 --------------------------------------------------
*/

const int NO_BTN = 0;
const int MODE_BTN = 3; // botones numerados segun
const int NEXT_BTN = 4; // el pin de salida sobre
const int PREV_BTN = 5; // el que generan su
const int VOLUP_BTN = 6; // respuesta
const int VOLDWN_BTN = 7;

const int PIN_MODEBTN = 2; // entrada [MODE]
const int PIN_SWCTRL = A0; // entrada [^][v][+][-]

const int PIN_LED = LED_BUILTIN;

const unsigned long DEBOUNCER_TIME = 50;

// Decodifica el boton pulsado [^][v][+][-]

int decode_analog_btn(int adcValue){

  if(adcValue <= 130) return NEXT_BTN;
  if(adcValue >= 145 && adcValue <= 225) return PREV_BTN;
  if(adcValue >= 285 && adcValue <= 365) return VOLUP_BTN;
  if(adcValue >= 525 && adcValue <= 605) return VOLDWN_BTN;  
  return NO_BTN;
}

int get_button() {
  
  static int lastButton = NO_BTN;
  static bool btnPressed = false;
  
  int analogButton = decode_analog_btn(analogRead(PIN_SWCTRL));
  int digitalButton = digitalRead(PIN_MODEBTN);
        
  if(digitalButton && !analogButton) { // no hay boton pulsado
    btnPressed = false;
    lastButton = NO_BTN;
  }
  else { // algun boton pulsado
    if(!btnPressed) {
      btnPressed = true;
      delay(DEBOUNCER_TIME);
      if(!digitalButton) { // [MODE] pulsado?
        if(digitalRead(PIN_MODEBTN) == digitalButton) {
        lastButton = MODE_BTN;
        }
      }
      else { // boton analogico pulsado
        if(decode_analog_btn(analogRead(PIN_SWCTRL)) == analogButton) {
          lastButton = analogButton;
        }
      }
    }
  }
  return lastButton;
}

void send_remote_command(int newBtn, int oldBtn) {

  byte ledStatus = HIGH;
  if(newBtn == NO_BTN) {
    pinMode(oldBtn, INPUT);
    ledStatus = LOW;
  }
  else pinMode(newBtn, OUTPUT);
  
  digitalWrite(PIN_LED, ledStatus);
}

void setup() {

  pinMode(PIN_LED, OUTPUT);
  DDRD &= B00000011; // D2 a D7 INPUT
  PORTD &= B00000111; // D3 a D7 LOW
// para reducir ruido ADC, o eso intento...
  DDRC |= B00111110; // A1 a A5 OUTPUT
  PORTC &= B11000001; // A1 a A5 LOW
}

void loop() {

  static int oldButton = NO_BTN;
  int newButton = get_button();

  if(newButton != oldButton) {
    send_remote_command(newButton, oldButton);
    oldButton = newButton;
  }
}

Voy a hacer una explicación de por qué pongo algunos pines como entrada e inmediatamente les escribo un LOW, no estoy loco, eso es lo que me permite conmutar fácilmente las resistencias que manejan el control remoto.

Sabemos que un pin en modo OUTPUT mantiene el valor que le escribamos hasta tanto le escribamos otro.
También que un pin puesto como entrada tiene alta impedancia (HI-Z) salvo que habilitemos su resistencia pull-up escribiendo un HIGH en el mismo. Luego, escribiendo LOW se deshabilita esa resistencia interna y queda en HI-Z.
Lo ventajoso es que ese valor (HIGH o LOW) queda memorizado así que con solo poner el pin como OUTPUT inmediatamente se obtiene ese estado, que le fue escrito a la entrada, en la salida.
Por lo tanto, una vez escrito LOW, podemos repetir el ciclo "INPUT-OUTPUT" manteniendo la alternancia "Alta impedancia-LOW" sin necesidad de reescribirle ningún valor (no se que ocurrirá si en medio se efectúa una lectura, pero no aplica en este caso, así que acá culmino la explicación).

O sea, pongo el pin como entrada, le escribo LOW, una sola vez, para asegurar la alta impedancia (botón no pulsado) y con solo ponerlo como salida tengo inmediatamente la resistencia asociada conectada a masa (botón pulsado). Y puedo hacer lo mismo indefinidamente. ¡Genial!

Un último detalle, dado que la tensión del auto es superior a los 12V (sobre todo cuando carga la batería) y excede lo que soporta el Nano, pensaba alimentar la placa con algún cargador USB por el puerto idem pero finalmente decidí reciclar un viejo cargador de celular que tenía en un cajón.
Está basado en el integrado MC34063 y no es más que una implementación del circuito step-down que propone el fabricante en la hoja de datos para una salida de 4.5v.
Solo cambié un par de resistencias para obtener 7.5V a su salida para alimentar al Nano y dejar el puerto USB para futuras ampliaciones del soft.

Y hablando de las ampliaciones, el auto donante es un Corsa y su estereo original tenía la posibilidad de funcionar durante una hora aunque la llave no estuviese en contacto.
Lo cierto es que esa funcionalidad me gustaba, sobre todo a la hora de lavar el auto, y como el Corolla no habilita el estéreo si no está puesta la llave en posición "Accesorios" o directamente en contacto, pienso pronto emularla con el Nano. Para eso voy a necesitar "mandarlo a dormir" para disminuir el gasto de batería porque va a estar siempre alimentado (ahora no me preocupa porque se desenergiza al sacar la llave del auto), así que ya estoy planeando la ampliación del sketch y la modificación de las conexiones para utilizar interrupciones para reactivarlo. Además necesito ampliar el hardware mínimamente.
Se materializará en un futuro cercano (espero...).

En cuanto al montaje, utilicé una placa Nano sin terminales, soldando las resistencias directamente en la placa y aislando cada una con funda termocontraíble para evitar cortocircuitos indeseables. Soldé los cables de alimentación a Vin y GND, respectivamente. Los de salida, uno  entre el puente de resistencias y la punta (Tip) del plug de 3.5 mm para el estéreo, el otro entre GND y la masa (Slave) del mismo.
Para la entrada desde el conector del volante utilicé tres cables para protoboard que corté un extremo y soldé a la placa en D2, a la resistencia de 270R y a GND, respectivamente, los otros extremos con sus respectivos pines los inserté en el conector hembra del auto en las posiciones indicadas.

La lista de materiales:
- 1 placa Nano (UNO, Mini)
- 1 fuente step-down
- 8 resistencias (270R, 1K2, 2K7, 8K, 10K, 11K, 16K, 24K) 1/4W
- 1 plug estéreo de 3.5 mm
- Cables varios
- 1 cajita acorde

Y como cierre, el aparatejo con su fuente montado en una vieja cajita de púas para giradiscos instalado en su destino y funcionando a la perfección.



Queda a disposición de quien le pueda ser de utilidad.

Saludos

PD: La conversión para usar un ATtiny85 es casi trivial, la señal del botón [MODE] se conecta a la entrada ADC junto con la de los otros botones (otra ventaja de haber agregado la resistencia de 270R), por supuesto, se modificará el sketch porque solo hay que discriminar botones "analógicos". De este modo el ATtiny85 alcanza justito, una entrada ADC y cinco salidas.

Mikms

Felicitarte por tu buen trabajo e interesante y útil proyecto.

Swift

Felicidades por el proyecto, me va a ser de mucha utilidad.

Go Up