Proyecto: digitalización locomotora LGB años 40.

Hola chicos.

Tengo un amigo que tiene una maqueta de tren escala H0 con dos locomotoras, con sus vias y cambios. Digamos que el juguete es de los años 60 y claro es totalmente analógico: velocidad de la locomotora controlada por reostato, cambios por interruptores y manejados por transformadores de corriente alterna, uso de bombillas.., os podeis imaginar. El caso es que funciona pero con sus pegas, la mas gorda, que solo puede controlar una locomotora ya que la otra hará los mismo, al controlar la tensión de la via.

El caso que hablando sobre como digitalizarla estuve mirando un poco como son las maquetas modernas y como se hace el control: protocolo DCC, decoders y boosters, con lo que pensandolo bien, se hacia caro y nada divertido.

Así que lo siguiente que queda es, como no, Arduino.

He pensado en saltarme el protocolo DCC, aunque he visto por ahí cosejas y sé como funciona, pero como es algo nada comercial, nos lo "inventamos" y será más fácil de hacer.

Para la locomotora simplemente tengo que controlar la velocidad y la dirección que pienso hacerlo con un puente H basado en el L298H. En teoría el motor de la locomotora que más consume es de 1.8A, así que muy justo, pero creo que se puede poner en "puente" los dos drivers y ya tenemos 4A. Las luces de momento siempre estaran encendidas, al igual que la chimenea de humo (si, echa humo).

Ahora viene mi duda existencial. Para controlar remotamente la locomotora he pensado en hacerlo de forma inalámbrica, con lo que hay me pilla justo en conocimientos ya que soy mas partidario de los cables, pero en este caso es necesario.

Al final, la idea se queda en:

Un "mando a distancia" o "estación de control". Tengo un Mega y una pantalla LCD táctil muertas de risa y había pensado en usarlos como estación de control.

Dos locomotoras, con controles de velocidad y dirección, opcionalmente podemos controlar las luces, quizás el humo y también algún sonido.

Una caja tonta, para el control de los cambios de via, semáforos y otras cosas. Esté tendrá un poco mas de circuiteria.

Un total de 4 nodos, con un maestro (mando) y tres esclavos.

Y ahora es cuando os pido recomendación. No he controlado nunca nada de manera inalámbrica y no sé por cual de las tecnologias "comunes" decantarme:

WiFi con ESP2866
Bluetooth con HC05
Radio con NRF24L01

La distancia no es problema porque la maqueta está en su patio y no tiene mas de 4x4 metros... y los tuneles que hay no llegan al metro de longitud. El problema lo veo en tener varios nodos y como comunicarlos entre si.

¿Cúal creéis que será más fácil de implantar y cúal recomendáis?

Te aclaro que las 3 son buenas opciones

  1. WiFi con ESP2866 -> de las mejores opciones, podrias controlarlo con el movil
  2. Radio con NRF24L01-> tmb es buena opción si pensaras en mas locomotoras.
  3. Bluetooth con HC05-> puedes controlarlo con el movil. Solo un Serial para enviar datos

Ahora veamos tema consumos:

  1. nRF24L01 es el que menos consume
    2 y 3 los pongo juntos porque realmente no se. Supongo que HC05 consume menos que el ESP.
    (verificarlo)

Con el ESP01 por ejemplo podrias enlazarlo usando algo como Blynk.cc y tendrías una maravilla de interfaz con poco esfuerzo.

No se si pretendes desarrollar algo o ir a lo seguro y que salga funcionando. En teoría se podría sumar una componete alterna a la tensión DC y luego mediante filtros de banda, optoaclopadores, y amplificadores operacionales, separar la componente Ac de comunicación en la locomotora. El golazo seria utilizar un protocolo ya probado en arduino como el modbus y solo concentrarse en el bus de comunicación.

Que torpeza la mía... por la via tienes DC y de alta corriente asi que la energía no es problema.

No se si pretendes desarrollar algo o ir a lo seguro y que salga funcionando.

Pues casi que voy a lo seguro :). Lo que me propones de la corriente alterna acoplada a la componente DC es muy similar al DCC. La diferencia es que el DCC lo que hace es cambiar la polaridad de las vias a través del booster. En el decoder de la locomotora se pone un rectificador para convertir la corriente "alterna" (onda cuadrada de pulsos) para obtener la alimentación y con un opto puedes obtener la señal. El "pero" es el hecho de necesitar un booster de alta corriente ya que hay que alimentar las locomotoras y los accesorios; y también, que solo puedes enviar datos y no recibirlos desde la estación. Hice una simulación en proteus y la verdad me sorprendió gratamente ver que funcionaba incluso en el simulador.

Y creo que me leiste el pensamiento. Mi idea, la implemente como la implemente, iba encaminada a utilizar el protocolo modbus o algo muy similar.

Que torpeza la mía... por la via tienes DC y de alta corriente asi que la energía no es problema.

Si y no. El mayor problema es que casi no tengo hueco dentro de la locomotora para poner placas. Así que lo tengo que minimizar y cuanto menos circuito mejor.

El L298 ya se lleva un hueco importante. Y el transceptor obviamente, tendrá que ir fuera, ya que la locomotora es metálica y me hará de jaula de Faraday. Aun así, casi todos los modulos funcionan a 3.3v lo que implica tener una fuente de 3.3v y la tensión de la via es superior a los 18v con lo que:

  • Si uso un regulador step-down no debería tener problemas de disipación de calor pero tendré que usar un hueco importante.

  • Si uso un regulador lineal, tendré un TO220, con sus condensadores asociados, ocupa menos sitio, pero si tengo consumo se me dispara la potencia de disipación y tendré que usar un disipador.

Dicho esto, estoy mirando que el NRF consume menos, tiene buen alcance aunque no sea un problema, y es mas barato.

A su vez estaba pensando en alimentar todo con 3.3V, y en vez de usar una atmega328p (UNO) usar un attiny85 de los que tengo por casa también aburridos. Si lo alimento todo con 3.3v me quito también el problema de usar convertidores de nivel, aunque el NRF sea tolerante a 5v.

Así que el "decoder" de la locomotora se compondrá de:

  • Fuente 3.3V
  • Attiny85
  • Puente H
  • NRF24L01+

victorjam:

  • Si uso un regulador step-down no debería tener problemas de disipación de calor pero tendré que usar un hueco importante.

No sé qué palabras clave específicas tendrías que usar para encontrarlo, pero puedo jurar que existen de esos que de largo no llegan ni a 5 cm, de ancho ni a 3, y de alto ni a 2.
Son casi tan anchos como un dedo índice, y como 3/4 del largo del pulgar

En lo personal veo mas ventajas en un ESP8266 tal vez 01 que cumpliría la tarea, todo en 1, es chico. Tiene Memoria de sobra. 2 GPIOs y si miras fino, 2 mas soldandolo.
Es para considerarlo. Ahora no se que pasará dentro de la locomotora.

Quiero decir, tienes en un solo chip todo, comunicación y control. Mismo tamaño del nRF24

Hola chicos.

Pues la verdad es que tengo ya alguna cosa hecha. En un principio tengo la controladora de las vias casi "terminada", por no decir terminada del todo. El proyecto lo hemos modificado un poco. Sigo teniendo dos locomotoras y el control de las vias, usando NRF24 para la comunicación. Pero mi amigo optó por usar un
PC en vez de un "mando a distancia".

Luego postearé el control control de vias.

Ahora quiero empezar a hacer el decoder de locomotora, con lo cual me asalta una duda. Para controlar el motor voy a usar un L298 y dicho motor no llega a consumir los 2A pero se queda muy cerca. Estaba pensando en "puentear" el puente H. Y no sé si será muy recomendable:

Me refiero a que tengo dos salidas de motor y unirlas de forma que solo trabajen con un motor. Para ello uno las entradas correspondientes de cada uno de ellos y las salidas. En la simulación de proteus si funciona, pero es solo eso una simulación.

¿Creeis que es factible?

Echando un vistazo al datasheet del L298N he visto que si se puede hacer y la forma en la que hay que hacerlo. Así que duda resuelta y os la comentaré mas tarde.

Hoy os voy a contar como va el proyecto. Os recuerdo: dos locomotoras y cambios de vias a controlar, todo ello con una red NRF24.

Mientras mi amigo se dedicaba a desarrollar el circuito de la vias, yo me preocupe mas de como controlar el cambio.

Al ser una locomotora de cuarenta años no sabia lo que iba a llevar el cambio. Lo que conlleva a que hay que desmontar uno y ver como funciona.

cambio01.jpg

cambio02.jpg

cambio03.jpg

Como veis no es el último grito en tecnologia. Pensaba que iría como un solenoide con un electroimán y muelle y no.

El circuito se compone de una bobina que está unida a unas chapas que cuando introduces corriente funcionan como un electroimán. En su interior hay un cilindro de plastico, con un engranaje y un tope que evita que se valla más allá de la posición. Dentro de este cilindro se halla un imán permanente.

Ahí esta la magia. Si introduces una tensión continua con una determinada polaridad en la bobina se genera un campo magnetico que si está en oposición al del iman, le obliga a girar para alinearse y si ya estaba alineado no pasa nada. Para volver a producir el cambio de posición hay que cambiar la polaridad de la bobina para hacer que el iman se vuelva a mover.

¿Cómo lo controlamos? Pues claro está con un puente H. Salvo que en vez de alimentar permanentemente la bobina, tan solo hay que dar un pulso.

Una vez que ya sabía como controlar el cambio me faltaba una cosa. Saber su estado. Despues de discutirlo con mi colega, llegamos a la conclusion que necesitabamos una carrera. Así que le instalamos una carrera al cambio de via:

IMG-20170920-WA0011.jpg

Esta carrera además de indicar al controlador de vias en que posición está, controla la luz de los semáforos que ha creado con diodos led.

Ya tengo el estado y sé como cambiar la via. Tenía que preparar un circuito:

controlVias.jpg

La placa se compone de:

  • Atmega328p
  • Zócalo para NRF24
  • Entradas optoacopladas
  • Dos 595
  • 4 puentes L293D

No explicaré para que el atmega ni el zócalo del NRF porque es obvio.

Las entradas optoacopladas se debe a que el circuito funciona a 5V y la alimentación la coge directamente de las vias.

El circuito de las vias tiene una tensión fija de 19-20V para alimentar a este y a las locomotoras (o cualquier cosa que se desee poner). Con tensión fija, no solo me refiero a que sea constante, sino que está conectada directamente a la via, así que se puede coger en cualquier punto de esta y que cada riel tiene su polaridad.

No he dicho el número de cambios que tiene el circuito, pero os lo diré ahora: 8. Teniendo en cuenta que tengo que tener 8 entradas y para controlar el puente H necesito dos pines por cada uno (total 16) el arduino se quedaba sin pines. Así que para controlar la lógica de los puente H, utilizo dos registro de desplazamiento 74595 en cascada y obtengo las 16 señales necesarias.

Haciendo eso, me sobraba las lineas del bus SPI, para controlar el NRF, espacio para programarlo a traves del puerto serie y para un diodo de señalización.

El circuito funcionando:

controlVias2.jpg

Quitando un problemilla con el footprint de proteus del botón de reset que al final lo tuve que quitar es totalmente funcional. El mayor problema que tiene, y no es del circuito, si no del cambio, es que al ser tan viejos, por dentro están oxidados y hay que limpiarlos bien para que funcione correctamente.

En cuanto al software nada especial. Utilizo la libreria para el RF24 de TMHR, así que como la RF24Network para crear una red de nodos. Y transmito mensajes de tipo: funcion, valor. Por ejemplo el paquete { 01, 00 } me devuelve un paquete tipo { 01, "estado de las entradas" }. Otro ejemplo { 02, 01 }, es la orden de cambiar la via 1.

Que placer me da leer a alguien que entiende lo que es documentar un proyecto!!

Esto es un proyecto... gracias victorjam por como lo vas llevando.

Ahora he empezado a trabajar con la locomotora, entre otras cosas hay que saber como funciona.

Locomotora:
loco.jpg

Una vez abierta:

Dentro de ella podemos ver los poco elementos que la componen y aun así un poco lioso:

En la parte de atrás hay una placa que contiene una PCB. En dicha PCB estan las luces de atrás y un circuito regulador a base de transistores. Las luces son bombillas de 7.5V. En la otra locomotora que hay las luces van unidas a la tensión de la via, al ser el control analógico, cuando bajas la tensión con el reostato, las luces se vienen abajo. En este no pasa eso, ya que utiliza un par de reguladores de tensión y las bombillas son de poco voltage y aunque baje la tensión de la via siguen luciendo igual. Problema: La mitad de las bombillas estan fundidas. Solución: Diodos LED blancos. Deberé prescindir de esta placa.

En el centro hay otra PCB que canaliza las conexiones y las lleva hacia atrás, al motor de vuelta, al control de humo de la chimenea y a las luces delanteras. Esta placa tendré que ver como sustituirla, ya que el control del humo quiero poner un interruptor para poder apagarlo sin que tenga que controlarlo el arduino.

La pesa, me sorprendió. La locomotora es todo plástico y pesa un quintal. Así que cuando la abrimos y vimos el cacho de plomo, descubrimos porque pesaba tanto. Este peso hace que la locomotora se agarre bien a la via.

En la parte roja vereis un conector para la alimentación que se extrae de las vias y para el motor. Hay tres conectores y no sé como va internamente ya que no puedo abrir la reductora sin cargarmela.

Detalle reductora:

Así que la solución que he optado es sacar en dos terminales la alimentación de la via y soldar al motor unos cables en las escobillas anulando asi el posible funcionamiento de los conectores.

Detalle del motor "trucado":
loco_detallemotor.jpg

Tengo preparado un circuito decoder de locomotora de pruebas.

decoder03.jpg

Cuento con poco espacio así que he tenido que aprevechar al máximo.

El circuito consta de:

  • Rectificador
  • Fuente de 12, 5, 3'3 V
  • Drivers de luces
  • Puente H
  • CPU
  • NRF24

En la entrada del circuito he puesto un rectificador con diodos 1N5400, para poder obtener la señal de las vias. Uso el puente porque así me olvido de saber donde tengo que poner la polaridad en las vias.

Uso 3 fuentes de alimentacion, una de 12V que tiene doble uso, por un lado me rebaja la tensión de las vias (recordad que son 19V) asi el regulador de 5 y el de 3'3 no tienen tanta caida de tensión; además de poder enchufarle las luces.

El puente H es un L298N que al final no ha hecho falta puentearlo, ya que el motor solo llega a consumir 1A haciendo fuerza.

La CPU es un Attiny85. Por problemas de espacio el Atmega era una opción poco viable y además no usaba todos los pines.

El driver de luces sirve para que el attiny pueda controlar las luces. Si va hacia adelante tendra que encenderse las de delente, si va hacia atras, las de atras. Además de la cabina. También me sirve para indicar un error haciendo que las lueces parpadeen. El driver es un simple transistor BC547 que me da de sobra para alimentar a tres leds.

El mayor problema ha sido el NFR24 conectado al attiny. En otra entrada os explicaré el problema y la solución.

Gracias Surbyte!. Pero te contaré mi secreto: no tengo memoria (al contrario que tu) y se me va la cabeza por todos los lados, así que hago fotos, anotó, re-escribo, y todo lo que haga falta para que cuando vuelva a echar mano, lo tenga todo claro.

Hola de nuevo chicos.

Como dije en un post anterior, voy a explicaros el problema que he tenido con el Attiny84 y el uso del modufo rf NRF24L01 y cual ha sido la solución que he encontrado.

Antes de empezar os digo que librerias he usado:

Libreria NRF24L01 de TMRH20
ATTinyCore de SpenceKonde

La libreria de TMRh20 trae soporte para micros de la serie attiny y dado que estos carecen de bus SPI nativo, implementa dicho protocolo usando el bus serie universal del que dispone (USI). En teoría no es necesario disponer de ninguna otra libreria para hacerlo funcionar.

Ahora bien si cogemos el código de ejemplo que viene en la libreria, vemos que tenemos que hacer lo siguiente:

/*
    ATtiny24/44/84 Pin map with CE_PIN 8 and CSN_PIN 7
    Schematic provided and successfully tested by Carmine Pastore (https://github.com/Carminepz)
                                  +-\/-+
    nRF24L01  VCC, pin2 --- VCC  1|o   |14 GND --- nRF24L01  GND, pin1
                            PB0  2|    |13 AREF
                            PB1  3|    |12 PA1
                            PB3  4|    |11 PA2 --- nRF24L01   CE, pin3
                            PB2  5|    |10 PA3 --- nRF24L01  CSN, pin4
                            PA7  6|    |9  PA4 --- nRF24L01  SCK, pin5
    nRF24L01 MOSI, pin7 --- PA6  7|    |8  PA5 --- nRF24L01 MISO, pin6
                                  +----+
*/
// CE and CSN are configurable, specified values for ATtiny85 as connected above
#define CE_PIN 3
#define CSN_PIN 4
//#define CSN_PIN 3 // uncomment for ATtiny85 3 pins solution
#include "RF24.h"
RF24 radio(CE_PIN, CSN_PIN);

Lo lógico y normal, es que hagas lo que te dice el ejemplo, pero a la hora de ponerlo en marcha, compila, pero no funciona.

Analizando el ejemplo, está mal. Los pines CE y CSN que define aquí son los que utiliza el NRF, pero NO los del attiny. Corrigiendo eso y poniendo los valores correctos de pines del core, tampoco funciona.

Aquí entré en pánico y eche mano de google, que curiosamente me mandaba a un "issue" de la libreria que indicaba esto mismo. Depués de leermelo, no enterarme, leerlo otra vez, medio aclarame, probar lo que proponian, incluso cambié el core, la misma libreria por otra. Nada. El attiny y el NRF no funcionan juntos.

A la! vamos a leer... Cogí la libreria la lei detenidamente hasta que di con el siguiente trozo de código:

#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
// these depend on the core used (check pins_arduino.h)
// this is for jeelabs' one (based on google-code core)
# define DI   4   // PA6
# define DO   5   // PA5
# define USCK 6   // PA4
# define SS   3 // PA7

Esto hace que forzosamente tengamos que utilizar dichos pines. Dichos pines son exactamente el bus SPI, siendo DI->MISO, DO->MOSI, USCK->SCK y SS->SS. Y digo forzosamente porque de otra forma no funciona. Eso hace que cuando declaremos un objeto Radio(CE,CSN), el pin CE lo podemos elegir, pero no el CSN (SS). Todo lo contrario a lo que dice la libreria.

Otra cosa importante es que hay introducir el código:

#define __AVR_ATtiny84__

Simplemente para que la libreria use el bus SPI antes indicado.

Como anecdota me resulta curioso que haya una función llamada isChipConnected que te indica si el NRF está conectado y casi nadie la usa... simplemente te dicen es que no comunica... leche, primero vamos a ver si está conectado ¿no?

Y como apertivo os dejo una de mis chuletas que suelo hacer para acordarme de las cosas:

Hola chicos.

Ya lleva un tiempo sin decir nada y tengo algo mas listo.

Como comenté, el tren lleva dos placas. La del centro que lleva las conexiones de luces y chimenea y la de atrás que posee un regulador.

Aquí os muestro un detalle de la central:

Como ya no me vale he tenido que hacer una para realizar las nuevas conexiones de delante y detras.

He aquí el resultado:


Hacia ella van:

  • Las tres luces delanteras.
  • La corriente de las vias. La utilizo para alimentar la chimenea. Como no queriamos controlarla con el attiny, lo hacemos mediante un interruptor que va en la parte posterior y accede a la cabina de la locomotora.
  • El conector de las luces traseras, así como la salida a la placa trasera

He buscado las fotos de la placa trasera que hice, pero no las encuentro, se ve que las borre accidentalmente. Pero no tiene misterio, simplemente van los zócalos de las bombillas que ahora son leds (totalmente compatibles por suerte) y el conector que da corriente a los vagones traseros.

Ya tengo la locomotora finalizada.

No sé si se verá el video, no sé si no se ha subido o que pero aqui dejo el enlace.

Para que lo veais.

Como aclaración, hemos cambiado la opción de control. La idea original era fabricarnos un mando a distancia con un Mega y una pantalla táctil, pero mi amigo pensó que le gustaba mas la idea de controlarlo desde el ordenador, ya que tiene una remesa de portatiles del año la pera sin uso y queria utilizar alguno.

Así que la opción de control es PC+Arduino+NRF24. Dado que el portatil es viejo: pentium M con 256 de RAM y XP de sistema operativo; estoy utilizando Borland Builder C++ 6.0 para hacer la interfaz del programa.

De momento con poco exito. Aunque el programa funciona y controlo las vias/locomotora, tengo una tasa de errores muy alta y un comportamiento anomalo del programa. Y llevo atascado con él mucho tiempo.

El mayor problema es la comunicación Arduino-PC. La red NRF funciona al 100% pero la comunicación serie da problemas y la verdad no lo entiendo.

Tengo dos versiones de programa. Una sin interfaz, consola o MSDOS, como querais llamarla. La tasa de errores en esta es baja, por no decir nula, pero cutre a mas no poder. En cambio la versión visual, es bonita, pero no sé porque falla.

Sé que el foro no es de programación en Windows, pero dado que lo comunico con un Arduino, si alguien me quiere aconsejar, será de mucho gusto por mi parte ofrecerle toda la información que precise.

PD. Ahora que lo veo y me he dado cuenta, os propongo un juego: mirar la foto de las vias. Teniendo en cuenta que la polaridad es fija en cada rail. ¿Notais algo raro?

¿Con qué has programado el interfaz windows?
¿Qué problema o diferencia de funcionalidad te está dando respecto del interfaz de consola?
Puedes pegar o adjuntar el código del programa a ver si alguien puede detectar la posible fuente del problema.

Utilizo un método de trabajo un poco raro:

Por norma general, lo primero es hacer un programa de consola que haga lo básico, en este caso, se trataba de mandar paquetes al arduino, analizar la
respuesta y mostrar. Cuando el código en consola funciona lo adapto a la interfaz gráfica.

En ambos casos he usado el compilador de Borland Builder C++ en su versión 6. No me pidais correr la version 10.2 en un pentium M por favor :D.

No utilizo ninguna libreria de comunciones de Delphi/BCB (TCommPort o similar), utilizo una propia utilizando la api de windows y creada de forma muy similar a la de Arduino: begin, read, write

Cuando envio información, lo que hago es crear un paquete con la estructura:

struct message {
 word fn;     // función a realizar.
 word value;  // parametro de la función.
 word option; // parametro opcional, no se usa, pero está ahi.
};

class query {
 word to;     // para quien es el mensaje.
 message msg; // mensaje, función y valor.
};

El PC necesita enviar una peticion o query, a un determinado dispositivo to con un determinado mensaje msg. Para ello lo transmite al Arduino, que recibe el mensaje y se encarga de transmitirlo por radio. El Arduino recibe la respuesta del dispositivo y se la manda al PC como respuesta
utilizando la misma estructura de mensaje.

Como curiosidad, y como demostración de que todos los compiladores no son iguales, primero intente enviar la estructura por el puerto serie directamente, pero daba errores hasta que me di cuenta que sizeof(query) en el IDE de arduino es 8 (lo lógico y normal), y en BCB es 9 (me sobra un byte...). De hay que tubiera que pasar la información a un array.

query es una clase que tiene un método send() que envia por puerto serie el mensaje, espera la respuesta y la procesa, comprobando si hay errores en la comunicación.

La locomotora tiene un mecanismo de seguridad, que si no recibe un mensaje en un minuto, este como este, se para. Este mecanismo lo pensé en un principio por si fallaba la radio evitar que la locomotora descarrilase. Por ello en la interfaz visual utilizo un hilo para la comunicación que siempre envia a las vias y a la locomotora un mensaje para ver su estado.

En consola, hay un bucle que infinito que si hay una tecla pulsada asociada a un determinado mensaje, lo envia y procesa. Incluso un modo automatico que cada x tiempo manda un mensaje para ver el estado de la via y de la locomotora.

En ambos caso compruebo los errores, llevando una cuenta de ellos y en consola muestro siempre el código de error devuelto. En la interfaz visual, aun no me he propuesto ver el error pero si sé cuantos hay y lo que tarda en ejecutarse el bucle del hilo. Generalmente el mayor error que hay en la interfaz visual es de timeout: no recibe respuesta aunque la trama se halla enviado bien.

Os adjunto el código que llevo hasta ahora:

  • Unit1 es el dialogo principal (pantalla de vias y locomotora).
  • Unit2 es el dialogo de configurar el puerto serie.
  • Unit3 no hace nada de momento, formulario vacio.
  • Unit4 donde está el código de las locomotoras.
  • Unit5 el hilo de trabajo. Es una clase TThread de BCB.
  • cserial es la libreria del puerto serie.
  • winmess, es una rutina que muestra un mensaje en caso de que la llamada a una api de windows falle, mas que nada para saber por qué.

No tengo a mano el código de consola, pero esta tarde lo subiré sin falta.

p8.zip (24.7 KB)

He aquí el código de consola (No cabe en todo el post, así que continuará en el siguiente):

// debug es una macro que ayuda a la verificación del programa. 
#define debug

#include "CSerial.h"
#include <stdio.h>
#include <conio.h>

// Incluyo el fichero .cpp directamente para no tener que crear un proyecto.
#include "cserial.cpp"

CSerial Serial;

#define MAX_FRAMESIZE 256
#define HI(x) ((x)>>8)&0xFF
#define LO(x) ((x)&0xFF)

//@ Define los errores que se pueden producir al enviar un mensaje.
#define QRY_NO_ERROR        0
#define QRY_NO_SERIAL_OPEN  1
#define QRY_ERROR_TIMEOUT   2
#define QRY_ERROR_OVERFLOW  3
#define QRY_ERROR_UNDERFLOW 4
#define QRY_ERROR_ID        5

//@ Funciones al estilo Arduino.
#define delay(x) Sleep((x))
#define millis() GetTickCount()

//@ Estan tomados del código de la libreria de ModBus.
unsigned int T1_5=2;
unsigned int T3_5=37;

//@ Parametros que deseo de vias y de locomotora.
int velocidad=0;
int velocidad_deseada=0;
int direccion=0;
int error=0;
int vias;

//@ Estructura del mensaje. Es el payload del NRF, es decir, lo que cada radio
//  se transmite entre ellas.
struct message {
  unsigned short function; // Función a ejecutar.
  unsigned short value;    // Valor del parametro de la función.
  unsigned short option;   // Un valor opcional.
};

//@ Clase query. Es lo que el Arduino y el PC se retransmiten. Lleva implicito
//  un miembro to que es la dirección de destino del mensaje (locomotoras o 
//  vias ).
class query {
	public: 
    unsigned short to;
    message msg;
		query();
   	query (unsigned int _to, unsigned short _fn, unsigned short _val, unsigned short _opt);
		int send(CSerial &Serial);
    void show();
  private:
    //@ El id del destino y el mensaje lo introduciré en un array que será el 
    //  paquete que enviare. Tengo también el tamaño.
    unsigned char frame[MAX_FRAMESIZE]; 
    int framesize;
    //@ En la llamada a send(..) define el tiempo que esperará antes de dar un 
    //  error de timeout.
    int timeout_time;
    
};

//@ Solo sirve para mostrar la información del mensaje.
void query::show() {
  printf("query.to           =%4x\n", to);
  printf("query.msg.function =%4x\n", msg.function);
  printf("query.msg.value    =%4x\n", msg.value);
  printf("query.msg.option   =%4x\n", msg.option);
  printf("--------------------------\n");
}

//@ Crea un mensaje pasandole los parametro to (para quien) y el mensaje { fn,val,opt }
query::query(unsigned int _to, unsigned short _fn, unsigned short _val, unsigned short _opt) {
	to = _to;
	msg.function = _fn;
	msg.value = _val;
	msg.option = _opt;
  timeout_time = 5000;
}

//@ Un constructor vacio.
query::query() {
	to = 00;
	msg.function = 0x00;
	msg.value = 0x00;
	msg.option = 0x00;
  timeout_time = 5000;
}

//@ La función enviar... es el corazón del programa:
int query::send(CSerial &Serial) {
   int i,j;
   unsigned short crc;
   // Comprobamos que el puerto serie está abierto, si no lo está salimos.
   if ( !Serial.isOpen() ) return QRY_NO_SERIAL_OPEN;

   // Preparamos el frame.
	 frame[0]=LO(to);
	 frame[1]=HI(to);
	 frame[2]=LO(msg.function);
	 frame[3]=HI(msg.function);
	 frame[4]=LO(msg.value);
	 frame[5]=HI(msg.value);
	 frame[6]=LO(msg.option);
	 frame[7]=HI(msg.option);
   framesize=8;
   #ifdef debug
     // Muestra el frame...
     gotoxy(1,9); printf(">>");
     for (j=0; j<framesize; j++) printf("[%X]", frame[j]);
   #endif
   

   // Enviamos el frame.
   for (i=0; i<framesize; i++) { 
     Serial.write(frame[i]);
     //Sleep(T1_5);
   }
   //@ Dado que tenia problemas con la velocidad, habia veces que esperando
   //  el tiempo indicado como T1_5, mejoraba.

   
   //@ Este tiempo es necesario. No sé porque, si no espero 150ms no recibo 
   //  absolutamente nada. La respuesta debería ser inmediata.
   Sleep(150);

   //@ Espero el tiempo de espera, si pasa demasiado tiempo damos un mensaje
   //  de error timeout.
   DWORD t;
   t = GetTickCount();
   while ( !Serial.available() ) {
     if ( GetTickCount() > t + timeout_time ) {
       return QRY_ERROR_TIMEOUT;
     }
   }

   //@ Ya hay datos en el puerto serie. Debemos leerlos todos (si hay mas bytes
   //  también.
   i=0; unsigned char b;
   while ( Serial.available() ) {
     b = Serial.read();
     if ( i < MAX_FRAMESIZE ) frame[i]=b;
     i++;
     Sleep(2);
   }
   //@ Aquí esperaba otros 150 ms para poder seguir, pero este no es necesario.
   //Sleep(150);

   //@ Hay mas caracteres de los deseados: error de overflow.
   if ( i>8 ) return QRY_ERROR_OVERFLOW;
   //@ Hay menos caracteres de los deseados: error de underflow.
   if ( i<8 ) 
   {
     if ( Serial.available() ) while ( Serial.available() ) Serial.read();
     return QRY_ERROR_UNDERFLOW;
   }

   //@ Comprobamos el id para ver que hemos recibido algo correcto.
   unsigned short _to = frame[1]<<8 | frame[0];
   if ( to != _to )
     return QRY_ERROR_ID;
   #ifdef debug
     //@ Muestro lo que he recibido.
     gotoxy(1,10); printf("<<");
     for (j=0; j<i; j++) printf("[%X]", frame[j]);
     delay(2000);
   #endif
   //@ Obtengo la información que me ha devuelto el arduino en el 
   //  query.
   to = _to;
   msg.function = frame[3]<<8 | frame[2];
   msg.value    = frame[5]<<8 | frame[4];
   msg.function = frame[7]<<8 | frame[6];
   //@ Retorno sin errores.
   return QRY_NO_ERROR; 
}

//@ Estas cadenas sirven para mostrar el error que ha sido.
char *errorString[]= { 
  "OK",
  "FALLO: Leer velocidad.",
  "FALLO: Al poner velocidad.",
  "FALLO: La máquina no esta parada, no se pueda cambiar la direccion",
  "FALLO: Al cambiar la direccion",
  "FALLO: Al parar la maquina",
  "FALLO: Al leer el estado de la via",
  "FALLO: Al cambiar la via"
};

//@ Leer las vias el mensaje es {01,01,00,00}.
void leerVias() {
  int r;
  query q = query(01,01,00,00);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR ) {
    error=0;
    vias=q.msg.value;
  }
  else
    error=6;
}

//@ Cambiar via. El mensaje tiene al forma {01,02,via,00}.
void cambiarVia(int v) {
  int r;
  query q = query(01,02,v,0);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR ) error=0;
  else
    error=7;
}

//@ Leer la velocidad. El mensaje tiene la forma {02,00,00,00}
//  La respuesta ha de ser un mensaje { 02, 00, velocidad, direccion }
void leerVelocidad(){
  int r;
  query q = query(02,00,00,00);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR )
  {
    velocidad = (int)q.msg.value;
    direccion = (int)q.msg.option;
    error = 0;
  }
  else
    error = 1;
}
//@ Poner la velocidad. El mensaje tiene la forma {02, 01, velocidad, 00 }
void ponerVelocidad(int x)
{
  int r;
  query q = query(02,01,x,00);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR )
    error = 0;
  else
    error = 2;
}

//@ Poner direccion. El mensaje tine la forma {02, 02, direccion, 00}
//  donde direccion puede ser 2 (hacia atrás) o 1 (hacia delante).
//  Como protección del motor la locomotora solo permite cambiar la
//  dirección cuando la máquina está parada. Si la velocidad no es 0
//  nos devuelve un mensaje {02, 02, FF, 00} indicando que no puede
//  cambiar la dirección.
void pondireccion(int d) {
  int r;
  query q = query(02,02,d,00);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR )
  {
    if ( q.msg.value==0xFF ) error=3;
    else error=0;
  }
  else
    error=4;
}

//@ PARADA DE EMERGENCIA. Para la locomotora de golpe, poniendo la 
//  direccion y la velocidad a 0 de golpe. El mensaje es de la 
//  forma {02, 03, 00, 00}
void parar() {
  int r;
  query q = query(02,03,00,00);
  r = q.send(Serial);
  if ( r==QRY_NO_ERROR ) error=0;
  else error = 5;
}