//DCC_Probe.ino --application or client code
/* * The class DCC_Probe will monitor DCC on a model railway track and * use interrupts to build up the DCC messages. * The results will be placed in a variable the_bytes[9] * along with setting the flag one_message_done * * Each byte of the DCC message may be read with the public method get_result(i) * The flag may be read with the public method message_done( ) and cleared with the * public method clear_message_done. * * This file (test code) creates on object DCC of the class DCC_Probe * Initialises the object DCC * Each time there is a new message prints the DCC bytes to the serial monitor */ //This application will dump to the serial port every byte that is on the//DCC bus except the Idle command/message.//With all loco speeds set to zero will get nothing.//For accessory 80 will capture 148 255 107 which is regarded as NORM on NCE CAB// OR 148 254 106 where the last bit of the second byte defines ON/OFF#include "DCC_Probe.h"DCC_Probe DCC; #include <LCD_1602.h> LCD_1602 LCD; char LCD_mess[16]; int result[6];int save_result[6]; void setup() { Serial.begin(115200); Serial.println("\nProgram DCC_Probe. Captures DCC data."); //optional Serial.println("On JK Layout BLACK probe to inner track. GREEN probe to outer track."); DCC.begin(); LCD.begin( ); LCD.message(0,"--DCC Sniffer--"); DCC.begin();}void loop() { DCC.DCC_Dump( ); //use routine from library} /*#if LTA digitalWrite(6,HIGH); #endif if (DCC.message_done()) //synchronise to complete message { if (DCC.get_result(1) != 255) { //ignore idle command int limit = DCC.get_result(0); for (int i = 1; i <= limit; i++){ Serial.print(DCC.get_result(i)); Serial.print(' '); } Serial.println(); DCC.clear_message_done(); //message used } } if (DCC.message_done()) //synchronise to complete message { for (int i = 0; i <6; i++) result[i] = DCC.get_result(i); if (result[1] != 255) { if (!((save_result[1]==result[1])&&(save_result[2]==result[2])&&(save_result[3]==result[3]))) { sprintf(LCD_mess,":%3u %3u %3u %3u",result[1],result[2],result[3],result[4]); LCD.clear_row(1); LCD.message(LCD_mess); for (int i= 0; i<6; i++) { save_result[i] = result[i]; Serial.print(result[i]); } } } } DCC.clear_message_done(); //message used }// } if (DCC.message_done()) //synchronise to complete message { if (DCC.get_result(1) != 255) { //ignore idle command int limit = DCC.get_result(0); LCD.clear_row(1); for (int i = 1; i <= limit; i++){ LCD.message(DCC.get_result(i)); LCD.message(" "); } Serial.println(); DCC.clear_message_done(); //message used } } */ // #if LTA // digitalWrite(6,LOW); // #endif //}--------------------------------------------------------------------------------//DCC_Probe.cpp Implementation file
#include "DCC_Probe.h"#define DEBUG 0DCC_Probe::DCC_Probe( ){ };enum dcc_state { look4preamble, look4preambleEnd, look4zero, buildpkt1, getzero2,alldone} state; volatile int preamble_count;volatile int byte_count;volatile int _count = 0; volatile int byte_pk[8];volatile int byte_val;volatile int byte_ptr; volatile int the_bytes[9]; volatile bool one_mess_done; void DCC_Probe::begin() { attachInterrupt(digitalPinToInterrupt(IRQ_PIN), _DCC_ISR, MODE); state = look4preamble; preamble_count = 0; #if LTA pinMode(4,OUTPUT); pinMode(5,OUTPUT); pinMode(6,OUTPUT); #endif }; void DCC_Probe::DCC_Dump( ){ #if LTA digitalWrite(6,HIGH); #endif if (one_mess_done) //synchronise to complete message { //Serial.print('*'); if (the_bytes[1] != 255) { //ignore idle command for (int i = 1; i <= the_bytes[0]; i++){ Serial.print(the_bytes[i]); Serial.print(' '); } Serial.println(); one_mess_done = false; //message used } } #if LTA digitalWrite(6,LOW); #endif }bool DCC_Probe::message_done( ){ return one_mess_done;}void DCC_Probe::clear_message_done( ){ one_mess_done = false;}int DCC_Probe::get_result(int i){ return the_bytes[i];};void save_bytes( ){ the_bytes[0] = byte_ptr; #if LTA digitalWrite(5,HIGH); //to time transfer #endif for (int i = 0 ; i < byte_ptr ; i++) //for (int i = 0 ; i < 8 ; i++) //worst case timing { //Serial.print(the_bytes[i]); the_bytes[i+1] = byte_pk[i]; }//Serial.print(the_bytes[byte_ptr]); #if LTA digitalWrite(5,LOW); #endif}void handle_bit(int the_bit){ // if (_count<SAMPLE_SIZE) { #if DEBUG Serial.print(the_bit); _count++; #endif; switch(state){ case look4preamble : #if DEBUG Serial.print('F'); //looking for preamble. ie series of '1s' #endif if (the_bit == 1 ) { preamble_count = 1; state = look4preambleEnd; //found first "1" so need many more } else preamble_count = 0;//should be 0 anyway? break; case look4preambleEnd : #if DEBUG Serial.print('E'); #endif if (the_bit == 1 ) { preamble_count++; if (preamble_count > PREAMBLE_SIZE) {state = look4zero; //look for a "0' to end preamble byte_ptr = 0; } } else if (the_bit ==0){ //found a "0" before PREAMBLE_SIZE so restart. state = look4preamble; preamble_count = 0; } break; case look4zero : #if DEBUG Serial.print('Z'); #endif if (the_bit == 0) { //packet starts with a "0" after the preamble state = buildpkt1; byte_count = 0; byte_val = 0; // Serial.print(','); } //stay in this state if found a "1" break; case buildpkt1 : #if DEBUG Serial.print('P'); #endif byte_count++; byte_val = 2*byte_val + the_bit; if (byte_count == 8){ byte_pk[byte_ptr++] = byte_val; state = getzero2; //after 8 bits look for terminating "0" //Serial.print(','); #if DEBUG Serial.print(byte_val); Serial.print(','); #endif } break; case getzero2 : #if DEBUG Serial.print('G'); #endif if (the_bit) { //if bit == '1' message complete state=alldone; // Serial.println(); } if (!the_bit) { //if bit == '0' get another byte //Serial.print(','); byte_val = 0; byte_count = 0; state = buildpkt1; } break; case alldone : #if LTA digitalWrite(5,HIGH); //Time will be critical #endif #if DEBUG Serial.println('#'); #endif if (the_bit) { //message should terminate with a 1. save_bytes( ); state = look4preamble; one_mess_done = true; } else Serial.print(" Error"); //should never happen #if LTA digitalWrite(5,LOW); #endif break; }} }volatile long int _start_time = 0;static void DCC_Probe::_DCC_ISR( ){ int bit_found; #if LTA digitalWrite(4,HIGH); //time interrupt service routine #endif // Serial.print('*'); volatile unsigned long _end_time = micros( ); volatile unsigned long _isr_time; _isr_time = _end_time - _start_time; if ((_isr_time)> DCC_TIME ) bit_found = 0; //else if (_isr_time < (DCC_TIME/4)) return; //fiddling trying to eliminate spikes - no luck else bit_found = 1; handle_bit(bit_found); _start_time = _end_time; #if LTA digitalWrite(4,LOW); #endif };--------------------------------------------------------------------//DCC_Probe.h header file
#ifndef DCC_Probe_H#define DCC_Probe_H#include "Arduino.h"#define IRQ_PIN 2#define MODE RISING#define DCC_TIME 160#define SAMPLE_SIZE 180#define PREAMBLE_SIZE 10#define LTA 1class DCC_Probe { public : DCC_Probe( ); void begin( ); void DCC_Dump( ); bool message_done( ); void clear_message_done( ); int get_result(int i); private : static void _DCC_ISR( ); }; //don't forget this semi colon#endif------------------------------------------------------------------