Módulo Arduino S88-N compatible con Central Station 3+

Compartelo con otros!

¡Hay que ver lo caro que sale cada uno de los elementos de Märklin para la maqueta! Que suerte que Arduino hace posibles algunas cosas tan simples como, por ejemplo, leer una serie de entradas digitales y mandar esas lecturas mediante una salida digital, proyecto DYI completamente asequible.

modulo deteccion okkie
Modulo detection OKKIE

Vamos a ver como usar un Arduino, un par de OKKIEs y un IRF520N para reemplazar los módulos comerciales disponibles.

¿En qué consiste?

El bus S88 se usa principalmente para la ocupación de tramos o secciones del recorrido de nuestra maqueta. Otro uso que se le puede dar es, por ejemplo, para crear un tablero de mandos, que mediante la controladora, podemos asignar a elementos digitales como cambios de vía o las farolas de una estación.

Se basa en un registro de desplazamiento digital, que al ritmo de la señal de reloj del bus, cada una de las entradas digitales se «empujan» por la línea de datos. El protocolo incluye algún detalle más, como señales de inicio o de reset, cosa que necesitamos que nuestro Arduino tenga en cuenta.

¿Por dónde empezamos?

El código que se tiene que hacer cargo de tal faena, no podía ser muy complicado: en cuanto la señal PS manda un pulso, se lee el array de pines y se preparan para que, en cuanto llegue el primer pulso de reloj, uno a uno se van escribiendo los valores de los pines en DATA. Además, como el protocolo es expandible, se incluye un pin para DATA IN del siguiente módulo en cascada.

Antiguamente estaba además basado en niveles lógicos de 5V (perfecto para Arduino), pero en la Central Station 3+ funcionan a 12V, así que esto añadía un poco más de adaptación.

Pues nada, no fui capaz de que la Central Station 3+ entendiera lo que mi Arduino trataba de decirle. Y por suerte, alguien con más mano en código, lo había ya conseguido: gracias Rudy!

¿El hardware?

Una vez que tengo un código que habla correctamente el idioma del S88, solo faltaba encontrar una manera sencilla de adaptar la señal: una puerta lógica NOT usando un IRF-520N:

conversor 5V-12V
conversor 5V-12V

Como la línea de DATA de la Central Station 3+ hace pull-up a 12V, solo hay que generar el nivel logico 0, que se consigue usando los pines de esta manera:

Gate (1) = DATA del arduino
Drain (2) = Linea GND del bus
Source (3) = DATA hacia la CS3+

con esto conseguimos que con cada nivel alto saliendo del ultimo arduino de la cadena, la CS3+ reciba un nivel bajo; y con cada nivel bajo del arduino, la CS3+ tiene su propio pull-up a 12V. Solo hará falta uno de estos, para intermediar entre el ultimo arduino de la cadena y la CS3+:

conversor 5V-12V - vista interna
conversor 5V-12V – vista interna

por otra parte, asi es como queda la PCB una vez colocado el Arduino Nano y los conectores necesarios:

modulo S88 con detectores de consumo de tramo
modulo S88 con detectores de consumo de tramo

Al ser la primera PCB que monté para la maqueta, aún no sabía que ya se pueden comprar ese tipo de PCB para proyectos en fibra, en lugar de baquelita (más resistentes). ademas, para identificar si una seccion de via estaba ocupada, compre un par de modulos OKKIE (parte del modulo ArLoco), de ArCoMoRa.

¿Y el software?

El código después un par de arreglillos, un poco de inspiración de otro entusiasta del tema (gracias Norberto) e invertir la señal de salida para compensar el efecto del Mosfet NOT, queda asi:

/*
S88 occupancy sensor interface to Command Station (Arduino Nano/CS3 compatible)

Software by JM, 2018.
Freely distributable for private, non commercial, use.

Connections for S88N bus:
	s88 pin 1 Vin					Arduino pin Vin = Power Input (6V-12V)
	s88 pin 2 data				Arduino pin 13 = dataOut - to NMOS Drain
	s88 pin 3 GND					Arduino GND
	s88 pin 4 clock				Arduino pin 3, interrupt 1
	s88 pin 5 GND					Arduino GND
	s88 pin 6 PS/Load			Arduino pin 2, interrupt 0
	s88 pin 7 Reset				(not used)
	s88 pin 8 Rail data		(not used)
	Arduino pin 12 = dataIn from next Arduino in the S88 chain

Connections for sensors: see table in void Setup().
REMARK:   inputs have the internal pullup resistor active, the sensors must pull the input to GND.
REMARK:   NMOS Source to GND, Drain to DATA (S88), Gate to DATA (Arduino).
*/

int							clockCounter	= 0;
int							pausa					= 5;
int							pins[16]			= {4, 5, 6, 7, 8, 9, 10,11,
                                 14,15,16,17,18,19,1, 0};
unsigned int		sensors				= 0x0000;
unsigned int		data					= 0x0000;

const byte      load          = 2;								//Load signal from CS3
const byte      clock         = 3;								//Clock signal from CS3
const byte			dataIn 			  = 12;								//data input from next Arduino in S88 chain (PB4)
const byte			dataOut 		  = 13;								//data output pin 13 = PB5

void setup() {  
	pinMode(load,  INPUT_PULLUP);
	pinMode(clock, INPUT_PULLUP);

	attachInterrupt(0, Load_int,  RISING);           //pin 3 = Load interrupt
	attachInterrupt(1, clock_int, RISING);           //pin 2 = Clock interrupt
  
	pinMode(dataIn,  INPUT_PULLUP);						       //pin 12 = data in from next Arduino S88 in chain
	pinMode(dataOut, OUTPUT);							           //pin 13 = data out to NMOS Gate or to previous Arduino in S88 chain

for(int i = 0; i < sizeof(pins); ++i ){
	pinMode(pins[i], INPUT_PULLUP);
}
	//Serial.begin(9600);															//Used for test purposes only
}

void loop() {

for(int i = 0; i < sizeof(pins); ++i ){
   boolean reading = digitalRead(pins[i]);
   delay(pausa);
   if (digitalRead(pins[i]) == reading) {
      delay(pausa); }
      else loop;
   if (digitalRead(pins[i]) == reading) {
      bitWrite(sensors, i, reading);}
      else loop;
}
  //Serial.println(sensors, BIN);										//Used for test purposes only
}


void Load_int(){
	PORTB					=			PORTB | 0b00100000;
	clockCounter	=			0;
	data					=			sensors;
}


void clock_int(){
	if (bitRead(data,clockCounter))		PORTB = PORTB | 0b00100000;			// PB5 = Pin 13 = HIGH
	else															PORTB = PORTB & 0b11011111;			// PB5 = Pin 13 = LOW
  
	delayMicroseconds(8);																							//Delay makes reading output signal from next Arduino in chain more reliable.
	bitWrite(data,clockCounter,(PINB & (1<<PB4)));										// PB4 = Pin 12
	clockCounter = (clockCounter +1) % 16;
}

¡Y poco más! Simplemente hay que entretenerse a preparar tantos módulos como necesitemos, ya que es completamente compatible con conexión en cascada.

Hasta otra

Deja un comentario