├── DCCHardware.h ├── keywords.txt ├── README.md ├── examples ├── CmdrArduino_accessory │ └── CmdrArduino_accessory.ino ├── CmdrArduino_opsModeProgram │ └── CmdrArduino_opsModeProgram.ino └── CmdrArduino_minimum │ └── CmdrArduino_minimum.ino ├── DCCPacketQueue.h ├── DCCPacket.h ├── DCCPacket.cpp ├── DCCPacketScheduler.h ├── DCCPacketQueue.cpp ├── DCCHardware.c └── DCCPacketScheduler.cpp /DCCHardware.h: -------------------------------------------------------------------------------- 1 | #ifndef __DCCHARDWARE_H__ 2 | #define __DCCHARDWARE_H__ 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif 9 | 10 | void setup_DCC_waveform_generator(void); 11 | void DCC_waveform_generation_hasshin(void); 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | #endif //__DCCHARDWARE_H__ -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | DCCPacketScheduler KEYWORD1 2 | DCCPacket KEYWORD1 3 | DCCPacketQueue KEYWORD1 4 | setDefaultSpeedSteps KEYWORD2 5 | setup KEYWORD2 6 | setSpeed KEYWORD2 7 | setSpeed14 KEYWORD2 8 | setSpeed28 KEYWORD2 9 | setSpeed128 KEYWORD2 10 | setFunctions KEYWORD2 11 | setFunctions0to4 KEYWORD2 12 | setFunctions5to8 KEYWORD2 13 | setFunctions9to12 KEYWORD2 14 | setBasicAccessory KEYWORD2 15 | unsetBasicAccessory KEYWORD2 16 | opsProgramCV KEYWORD2 17 | eStop KEYWORD2 18 | update KEYWORD2 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CmdrArduino 2 | =========== 3 | 4 | To install, see the general instructions for Arduino library installation here: 5 | http://arduino.cc/en/Guide/Environment#libraries 6 | 7 | Discussion 8 | ---------- 9 | 10 | You can get support for the CmdrArduino software at the Railstars [Support Forum](http://support.railstars.com/index.php?p=/categories/cmdrarduino) 11 | 12 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Railstars/cmdrarduino/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 13 | -------------------------------------------------------------------------------- /examples/CmdrArduino_accessory/CmdrArduino_accessory.ino: -------------------------------------------------------------------------------- 1 | 2 | /******************** 3 | * Creates a minimum DCC command station that flips a turnout open and closed. 4 | * The DCC waveform is output on Pin 9, and is suitable for connection to an LMD18200-based booster directly, 5 | * or to a single-ended-to-differential driver, to connect with most other kinds of boosters. 6 | ********************/ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | DCCPacketScheduler dps; 14 | byte prev_state = 1; 15 | unsigned long timer = 0; 16 | unsigned int address = 4; //this address is not, strictly speaking, the accessory decoder address, but the address as it appears to the user 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | dps.setup(); 21 | 22 | //set up button on pin 4 23 | pinMode(4, OUTPUT); 24 | digitalWrite(4, HIGH); //activate built-in pull-up resistor 25 | } 26 | 27 | void loop() { 28 | if(millis() - timer > 1000) //only do this one per seconds 29 | { 30 | if(prev_state) 31 | { 32 | dps.setBasicAccessory((address >> 2) + 1, ((address - 1) & 0x03) << 1); 33 | } 34 | else 35 | { 36 | dps.unsetBasicAccessory((address >> 2) + 1, ((address - 1) & 0x03) << 1); 37 | } 38 | prev_state = prev_state?0:1; 39 | timer = millis(); 40 | } 41 | 42 | dps.update(); 43 | } 44 | -------------------------------------------------------------------------------- /examples/CmdrArduino_opsModeProgram/CmdrArduino_opsModeProgram.ino: -------------------------------------------------------------------------------- 1 | /******************** 2 | * Creates a minimum DCC command station from a potentiometer connected to analog pin 0, 3 | * and a button connected to ground on one end and digital pin 4 on the other end. See this link 4 | * http://www.arduino.cc/en/Tutorial/AnalogInput 5 | * The DCC waveform is output on Pin 9, and is suitable for connection to an LMD18200-based booster directly, 6 | * or to a single-ended-to-differential driver, to connect with most other kinds of boosters. 7 | ********************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | DCCPacketScheduler dps; 15 | unsigned int analog_value; 16 | char speed_byte, old_speed = 0; 17 | byte count = 0; 18 | byte prev_state = 1; 19 | boolean toggle = false; 20 | 21 | void setup() { 22 | dps.setup(); 23 | 24 | //set up button on pin 4 25 | pinMode(4, OUTPUT); 26 | digitalWrite(4, HIGH); //activate built-in pull-up resistor 27 | 28 | //Serial.begin(9600); 29 | } 30 | 31 | void loop() { 32 | //handle reading button, controls F0 33 | byte button_state = digitalRead(4); //high == not pushed; low == pushed 34 | if(button_state && (button_state != prev_state)) 35 | { 36 | //toggle! 37 | toggle = !toggle; 38 | if(toggle) 39 | dps.opsProgramCV(3,DCC_SHORT_ADDRESS, 3,10); //set decoder address 03's CV3 (acceleration rate) to 10 40 | else 41 | dps.opsProgramCV(3,DCC_SHORT_ADDRESS, 3,0); //set decoder address 03's CV3 (acceleration rate) to 0 42 | } 43 | prev_state = button_state; 44 | 45 | //handle reading throttle 46 | analog_value = analogRead(0); 47 | speed_byte = (analog_value >> 2)-127; //divide by two to take a 0-1023 range number and make it 0-127 range. 48 | if(speed_byte != old_speed) 49 | { 50 | dps.setSpeed128(3,DCC_SHORT_ADDRESS, speed_byte); 51 | old_speed = speed_byte; 52 | } 53 | dps.update(); 54 | 55 | ++count; 56 | } 57 | -------------------------------------------------------------------------------- /examples/CmdrArduino_minimum/CmdrArduino_minimum.ino: -------------------------------------------------------------------------------- 1 | /******************** 2 | * Creates a minimum DCC command station from a potentiometer connected to analog pin 0, 3 | * and a button connected to ground on one end and digital pin 4 on the other end. See this link 4 | * http://www.arduino.cc/en/Tutorial/AnalogInput 5 | * The DCC waveform is output on Pin 9, and is suitable for connection to an LMD18200-based booster directly, 6 | * or to a single-ended-to-differential driver, to connect with most other kinds of boosters. 7 | * The Differential DCC waveform is output on Pins 9 and 10. 8 | ********************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | DCCPacketScheduler dps; 16 | unsigned int analog_value; 17 | char speed_byte, old_speed = 0; 18 | byte count = 0; 19 | byte prev_state = 1; 20 | byte F0 = 0; 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | dps.setup(); 25 | 26 | //set up button on pin 4 27 | pinMode(4, INPUT); 28 | digitalWrite(4, HIGH); //activate built-in pull-up resistor 29 | } 30 | 31 | void loop() { 32 | //handle reading button, controls F0 33 | byte button_state = digitalRead(4); //high == not pushed; low == pushed 34 | if(button_state && (button_state != prev_state)) 35 | { 36 | //toggle! 37 | F0 ^= 1; 38 | Serial.println(F0,BIN); 39 | dps.setFunctions0to4(3,DCC_SHORT_ADDRESS,F0); 40 | } 41 | prev_state = button_state; 42 | 43 | //handle reading throttle 44 | analog_value = analogRead(0); 45 | speed_byte = (analog_value >> 2)-127 ; //divide by four to take a 0-1023 range number and make it 1-126 range. 46 | if(speed_byte != old_speed) 47 | { 48 | if(speed_byte == 0) //this would be treated as an e-stop! 49 | { 50 | if(old_speed > 0) speed_byte = 1; 51 | else speed_byte = -1; 52 | } 53 | Serial.print("analog = "); 54 | Serial.println(analog_value, DEC); 55 | Serial.print("digital = "); 56 | Serial.println(speed_byte, DEC); 57 | dps.setSpeed128(3,DCC_SHORT_ADDRESS,speed_byte); 58 | old_speed = speed_byte; 59 | } 60 | dps.update(); 61 | 62 | ++count; 63 | } 64 | -------------------------------------------------------------------------------- /DCCPacketQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef __DCCPACKETQUEUE_H__ 2 | #define __DCCPACKETQUEUE_H__ 3 | 4 | #include "Arduino.h" 5 | 6 | /** 7 | * A FIFO queue for holding DCC packets, implemented as a circular buffer. 8 | * Copyright 2010 D.E. Goodman-Wilson 9 | * TODO 10 | **/ 11 | 12 | #include "DCCPacket.h" 13 | 14 | class DCCPacketQueue 15 | { 16 | public: //protected: 17 | DCCPacket *queue; 18 | byte read_pos; 19 | byte write_pos; 20 | byte size; 21 | byte written; //how many cells have valid data? used for determining full status. 22 | public: 23 | DCCPacketQueue(void); 24 | 25 | virtual void setup(byte); 26 | 27 | ~DCCPacketQueue(void) 28 | { 29 | free(queue); 30 | } 31 | 32 | virtual inline bool isFull(void) 33 | { 34 | return (written == size); 35 | } 36 | virtual inline bool isEmpty(void) 37 | { 38 | return (written == 0); 39 | } 40 | virtual inline bool notEmpty(void) 41 | { 42 | return (written > 0); 43 | } 44 | 45 | virtual inline bool notRepeat(unsigned int address) 46 | { 47 | return (address != queue[read_pos].getAddress()); 48 | } 49 | 50 | //void printQueue(void); 51 | 52 | virtual bool insertPacket(DCCPacket *packet); //makes a local copy, does not take over memory management! 53 | virtual bool readPacket(DCCPacket *packet); //does not hand off memory management of packet. used immediately. 54 | 55 | bool forget(uint16_t address, uint8_t address_kind); 56 | void clear(void); 57 | }; 58 | 59 | //A queue that, when a packet is read, puts that packet back in the queue if it requires repeating. 60 | class DCCRepeatQueue: public DCCPacketQueue 61 | { 62 | public: 63 | DCCRepeatQueue(void); 64 | //void setup(byte length); 65 | bool insertPacket(DCCPacket *packet); 66 | bool readPacket(DCCPacket *packet); 67 | }; 68 | 69 | //A queue that repeats the topmost packet as many times as is indicated by the packet before moving on 70 | class DCCEmergencyQueue: public DCCPacketQueue 71 | { 72 | public: 73 | DCCEmergencyQueue(void); 74 | bool readPacket(DCCPacket *packet); 75 | }; 76 | 77 | #endif //__DCCPACKETQUEUE_H__ 78 | -------------------------------------------------------------------------------- /DCCPacket.h: -------------------------------------------------------------------------------- 1 | #ifndef __DCCPACKET_H__ 2 | #define __DCCPACKET_H__ 3 | 4 | #include "Arduino.h" 5 | 6 | typedef unsigned char uint8_t; 7 | 8 | //Packet kinds 9 | // enum packet_kind_t { 10 | // idle_packet_kind, 11 | // e_stop_packet_kind, 12 | // speed_packet_kind, 13 | // function_packet_kind, 14 | // accessory_packet_kind, 15 | // reset_packet_kind, 16 | // ops_mode_programming_kind, 17 | // other_packet_kind 18 | // }; 19 | 20 | #define MULTIFUNCTION_PACKET_KIND_MASK 0x10 21 | #define idle_packet_kind 0x10 22 | #define e_stop_packet_kind 0x11 23 | #define speed_packet_kind 0x12 24 | #define function_packet_1_kind 0x13 25 | #define function_packet_2_kind 0x14 26 | #define function_packet_3_kind 0x15 27 | #define accessory_packet_kind 0x16 28 | #define reset_packet_kind 0x17 29 | #define ops_mode_programming_kind 0x18 30 | 31 | 32 | #define ACCESSORY_PACKET_KIND_MASK 0x40 33 | #define basic_accessory_packet_kind 0x40 34 | #define extended_accessory_packet_kind 0x41 35 | 36 | #define other_packet_kind 0x00 37 | 38 | #define DCC_SHORT_ADDRESS 0x00 39 | #define DCC_LONG_ADDRESS 0x01 40 | 41 | class DCCPacket 42 | { 43 | private: 44 | //A DCC packet is at most 6 uint8_ts: 2 of address, three of data, one of XOR 45 | uint16_t address; 46 | uint8_t address_kind; 47 | uint8_t data[3]; 48 | uint8_t size_repeat; //a bit field! 0b11000000 = 0xC0 = size; 0x00111111 = 0x3F = repeat 49 | uint8_t kind; 50 | 51 | public: 52 | DCCPacket(uint16_t decoder_address=0xFF, uint8_t decoder_address_kind=0x00); 53 | 54 | uint8_t getBitstream(uint8_t rawuint8_ts[]); //returns size of array. 55 | uint8_t getSize(void); 56 | inline uint16_t getAddress(void) { return address; } 57 | inline uint8_t getAddressKind(void) { return address_kind; } 58 | inline void setAddress(uint16_t new_address) { address = new_address; } 59 | inline void setAddress(uint16_t new_address, uint8_t new_address_kind) { address = new_address; address_kind = new_address_kind; } 60 | void addData(uint8_t new_data[], uint8_t new_size); //insert freeform data. 61 | inline void setKind(uint8_t new_kind) { kind = new_kind; } 62 | inline uint8_t getKind(void) { return kind; } 63 | inline void setRepeat(uint8_t new_repeat) { size_repeat = ((size_repeat&0xC0) | (new_repeat&0x3F)) ;} 64 | inline uint8_t getRepeat(void) { return size_repeat & 0x3F; }//return repeat; } 65 | }; 66 | 67 | #endif //__DCCPACKET_H__ 68 | -------------------------------------------------------------------------------- /DCCPacket.cpp: -------------------------------------------------------------------------------- 1 | #include "DCCPacket.h" 2 | 3 | DCCPacket::DCCPacket(uint16_t new_address, uint8_t new_address_kind) : address(new_address), address_kind(new_address_kind), kind(idle_packet_kind), size_repeat(0x40) //size(1), repeat(0) 4 | { 5 | address = new_address; 6 | address_kind = new_address_kind; 7 | data[0] = 0x00; //default to idle packet 8 | data[1] = 0x00; 9 | data[2] = 0x00; 10 | } 11 | 12 | uint8_t DCCPacket::getBitstream(uint8_t rawbytes[]) //returns size of array. 13 | { 14 | int total_size = 1; //minimum size 15 | 16 | if (kind & MULTIFUNCTION_PACKET_KIND_MASK) { 17 | if (kind == idle_packet_kind) //idle packets work a bit differently: 18 | // since the "address" field is 0xFF, the logic below will produce C0 FF 00 3F instead of FF 00 FF 19 | { 20 | rawbytes[0] = 0xFF; 21 | } else if (address_kind == DCC_LONG_ADDRESS) //This is a 14-bit address 22 | { 23 | rawbytes[0] = (uint8_t)((address >> 8) | 0xC0); 24 | rawbytes[1] = (uint8_t)(address & 0xFF); 25 | ++total_size; 26 | } else //we have an 7-bit address 27 | { 28 | rawbytes[0] = (uint8_t)(address & 0x7F); 29 | } 30 | 31 | uint8_t i; 32 | for (i = 0; i < getSize(); ++i, ++total_size) { 33 | rawbytes[total_size] = data[i]; 34 | } 35 | 36 | uint8_t XOR = 0; 37 | for (i = 0; i < total_size; ++i) { 38 | XOR ^= rawbytes[i]; 39 | } 40 | rawbytes[total_size] = XOR; 41 | 42 | return total_size + 1; 43 | } else if (kind & ACCESSORY_PACKET_KIND_MASK) { 44 | if (kind == basic_accessory_packet_kind) { 45 | // Basic Accessory Packet looks like this: 46 | // {preamble} 0 10AAAAAA 0 1AAACDDD 0 EEEEEEEE 1 47 | // or this: 48 | // {preamble} 0 10AAAAAA 0 1AAACDDD 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1 (if programming) 49 | 50 | rawbytes[0] = 0x80; //set up address byte 0 51 | rawbytes[1] = 0x88; //set up address byte 1 52 | 53 | rawbytes[0] |= address & 0x03F; 54 | rawbytes[1] |= (~(address >> 2) & 0x70) 55 | | (data[0] & 0x07); 56 | 57 | //now, add any programming bytes (skipping first data byte, of course) 58 | uint8_t i; 59 | uint8_t total_size = 2; 60 | for (i = 1; i < getSize(); ++i, ++total_size) { 61 | rawbytes[total_size] = data[i]; 62 | } 63 | 64 | //and, finally, the XOR 65 | uint8_t XOR = 0; 66 | for (i = 0; i < total_size; ++i) { 67 | XOR ^= rawbytes[i]; 68 | } 69 | rawbytes[total_size] = XOR; 70 | 71 | return total_size + 1; 72 | } 73 | } 74 | return 0; //ERROR! SHOULD NEVER REACH HERE! do something useful, like transform it into an idle packet or something! TODO 75 | } 76 | 77 | uint8_t DCCPacket::getSize(void) 78 | { 79 | return (size_repeat>>6); 80 | } 81 | 82 | void DCCPacket::addData(uint8_t new_data[], uint8_t new_size) //insert freeform data. 83 | { 84 | for(int i = 0; i < new_size; ++i) 85 | data[i] = new_data[i]; 86 | size_repeat = (size_repeat & 0x3F) | (new_size<<6); 87 | } -------------------------------------------------------------------------------- /DCCPacketScheduler.h: -------------------------------------------------------------------------------- 1 | #ifndef __DCCCOMMANDSTATION_H__ 2 | #define __DCCCOMMANDSTATION_H__ 3 | #include "DCCPacket.h" 4 | #include "DCCPacketQueue.h" 5 | 6 | 7 | #define E_STOP_QUEUE_SIZE 2 8 | #define HIGH_PRIORITY_QUEUE_SIZE 10 9 | #define LOW_PRIORITY_QUEUE_SIZE 10 10 | #define REPEAT_QUEUE_SIZE 10 11 | //#define PERIODIC_REFRESH_QUEUE_SIZE 10 12 | 13 | #define LOW_PRIORITY_INTERVAL 5 14 | #define REPEAT_INTERVAL 11 15 | #define PERIODIC_REFRESH_INTERVAL 23 16 | 17 | #define SPEED_REPEAT 3 18 | #define FUNCTION_REPEAT 3 19 | #define E_STOP_REPEAT 5 20 | #define OPS_MODE_PROGRAMMING_REPEAT 3 21 | #define OTHER_REPEAT 2 22 | 23 | class DCCPacketScheduler 24 | { 25 | public: 26 | 27 | DCCPacketScheduler(void); 28 | 29 | //for configuration 30 | void setDefaultSpeedSteps(uint8_t new_speed_steps); 31 | void setup(void); //for any post-constructor initialization 32 | 33 | //for enqueueing packets 34 | bool setSpeed(uint16_t address, uint8_t address_kind, int8_t new_speed, uint8_t steps = 0); //new_speed: [-127,127] 35 | bool setSpeed14(uint16_t address, uint8_t address_kind, int8_t new_speed, bool F0=true); //new_speed: [-13,13], and optionally F0 settings. 36 | bool setSpeed28(uint16_t address, uint8_t address_kind, int8_t new_speed); //new_speed: [-28,28] 37 | bool setSpeed128(uint16_t address, uint8_t address_kind, int8_t new_speed); //new_speed: [-127,127] 38 | 39 | //the function methods are NOT stateful; you must specify all functions each time you call one 40 | //keeping track of function state is the responsibility of the calling program. 41 | bool setFunctions(uint16_t address, uint8_t address_kind, uint8_t F0to4, uint8_t F5to9=0x00, uint8_t F9to12=0x00); 42 | bool setFunctions(uint16_t address, uint8_t address_kind, uint16_t functions); 43 | bool setFunctions0to4(uint16_t address, uint8_t address_kind, uint8_t functions); 44 | bool setFunctions5to8(uint16_t address, uint8_t address_kind, uint8_t functions); 45 | bool setFunctions9to12(uint16_t address, uint8_t address_kind, uint8_t functions); 46 | //other cool functions to follow. Just get these working first, I think. 47 | 48 | bool setBasicAccessory(uint16_t address, uint8_t function); 49 | bool unsetBasicAccessory(uint16_t address, uint8_t function); 50 | 51 | bool opsProgramCV(uint16_t address, uint8_t address_kind, uint16_t CV, uint8_t CV_data); 52 | 53 | //more specific functions 54 | bool eStop(void); //all locos 55 | bool eStop(uint16_t address, uint8_t address_kind); //just one specific loco 56 | 57 | //to be called periodically within loop() 58 | void update(void); //checks queues, puts whatever's pending on the rails via global current_packet. easy-peasy 59 | 60 | //private: 61 | 62 | // void stashAddress(DCCPacket *p); //remember the address to compare with the next packet 63 | void repeatPacket(DCCPacket *p); //insert into the appropriate repeat queue 64 | uint8_t default_speed_steps; 65 | uint16_t last_packet_address; 66 | 67 | uint8_t packet_counter; 68 | 69 | DCCEmergencyQueue e_stop_queue; 70 | DCCPacketQueue high_priority_queue; 71 | DCCPacketQueue low_priority_queue; 72 | DCCRepeatQueue repeat_queue; 73 | //DCCTemporalQueue periodic_refresh_queue; 74 | 75 | //TODO to be completed later. 76 | //DCC_Packet ops_programming_queue[10]; 77 | 78 | //some handy thingers 79 | //DCCPacket idle_packet; 80 | }; 81 | 82 | //DCCPacketScheduler packet_scheduler; 83 | 84 | #endif //__DCC_COMMANDSTATION_H__ 85 | -------------------------------------------------------------------------------- /DCCPacketQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "DCCPacketQueue.h" 2 | 3 | DCCPacketQueue::DCCPacketQueue(void) : read_pos(0), write_pos(0), size(10), written(0) 4 | { 5 | return; 6 | } 7 | 8 | void DCCPacketQueue::setup(byte length) 9 | { 10 | size = length; 11 | queue = (DCCPacket *)malloc(sizeof(DCCPacket) *size); 12 | for(int i = 0; igetKind(), DEC); 22 | //First: Overwrite any packet with the same address and kind; if no such packet THEN hitup the packet at write_pos 23 | byte i = read_pos; 24 | while(i != (read_pos+written)%(size) )//(size+1) ) //size+1 so we can check the last slot, too… 25 | { 26 | if( (queue[i].getAddress() == packet->getAddress()) && (queue[i].getKind() == packet->getKind())) 27 | { 28 | // Serial.print("Overwriting existing packet at index "); 29 | // Serial.println(i, DEC); 30 | memcpy(&queue[i],packet,sizeof(DCCPacket)); 31 | //do not increment written or modify write_pos 32 | return true; 33 | } 34 | i = (i+1)%size; 35 | } 36 | 37 | //else, tack it on to the end 38 | if(!isFull()) 39 | { 40 | //else, just write it at the end of the queue. 41 | memcpy(&queue[write_pos],packet,sizeof(DCCPacket)); 42 | // Serial.print("Write packet to index "); 43 | // Serial.println(write_pos, DEC); 44 | write_pos = (write_pos + 1) % size; 45 | ++written; 46 | return true; 47 | } 48 | // Serial.println("Queue is full!"); 49 | return false; 50 | } 51 | 52 | // void DCCPacketQueue::printQueue(void) 53 | // { 54 | // byte i, j; 55 | // for(i = 0; i < size; ++i) 56 | // { 57 | // for(j = 0; j < (queue[i].size_repeat>>4); ++j) 58 | // { 59 | // Serial.print(queue[i].data[j],BIN); 60 | // Serial.print(" "); 61 | // } 62 | // if(i == read_pos) Serial.println(" r"); 63 | // else if(i == write_pos) Serial.println(" w"); 64 | // else Serial.println(""); 65 | // } 66 | // } 67 | 68 | bool DCCPacketQueue::readPacket(DCCPacket *packet) 69 | { 70 | if(!isEmpty()) 71 | { 72 | // Serial.print("Reading a packet from index: "); 73 | // Serial.println(read_pos, DEC); 74 | memcpy(packet,&queue[read_pos],sizeof(DCCPacket)); 75 | read_pos = (read_pos + 1) % size; 76 | --written; 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | bool DCCPacketQueue::forget(uint16_t address, uint8_t address_kind) 83 | { 84 | bool found = false; 85 | for(int i = 0; i < size; ++i) 86 | { 87 | if( (queue[i].getAddress() == address) && (queue[i].getAddressKind() == address_kind) ) 88 | { 89 | found = true; 90 | queue[i] = DCCPacket(); //revert to default value 91 | } 92 | } 93 | --written; 94 | return found; 95 | } 96 | 97 | void DCCPacketQueue::clear(void) 98 | { 99 | read_pos = 0; 100 | write_pos = 0; 101 | written = 0; 102 | for(int i = 0; igetRepeat()) 118 | { 119 | return(DCCPacketQueue::insertPacket(packet)); 120 | } 121 | return false; 122 | } 123 | 124 | bool DCCRepeatQueue::readPacket(DCCPacket *packet) 125 | { 126 | if(!isEmpty()) 127 | { 128 | memcpy(packet,&queue[read_pos],sizeof(DCCPacket)); 129 | read_pos = (read_pos + 1) % size; 130 | --written; 131 | 132 | if(packet->getRepeat()) //the packet needs to be sent out at least one more time 133 | { 134 | packet->setRepeat(packet->getRepeat()-1); 135 | insertPacket(packet); 136 | } 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | 143 | /**************/ 144 | 145 | DCCEmergencyQueue::DCCEmergencyQueue(void) : DCCPacketQueue() 146 | { 147 | } 148 | 149 | /* Goes through each packet in the queue, repeats it getRepeat() times, and discards it */ 150 | bool DCCEmergencyQueue::readPacket(DCCPacket *packet) 151 | { 152 | if(!isEmpty()) //anything in the queue? 153 | { 154 | queue[read_pos].setRepeat(queue[read_pos].getRepeat()-1); //decrement the current packet's repeat count 155 | if(queue[read_pos].getRepeat()) //if the topmost packet needs repeating 156 | { 157 | memcpy(packet,&queue[read_pos],sizeof(DCCPacket)); 158 | return true; 159 | } 160 | else //the topmost packet is ready to be discarded; use the DCCPacketQueue mechanism 161 | { 162 | return(DCCPacketQueue::readPacket(packet)); 163 | } 164 | } 165 | return false; 166 | } 167 | -------------------------------------------------------------------------------- /DCCHardware.c: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include 3 | #include 4 | #include "DCCHardware.h" 5 | 6 | /// An enumerated type for keeping track of the state machine used in the timer1 ISR 7 | /** Given the structure of a DCC packet, the ISR can be in one of 5 states. 8 | *dos_idle: there is nothing to put on the rails. In this case, the only legal thing 9 | to do is to put a '1' on the rails. The ISR should almost never be in this state. 10 | *dos_send_premable: A packet has been made available, and so we should broadcast the preamble: 14 '1's in a row 11 | *dos_send_bstart: Each data uint8_t is preceded by a '0' 12 | *dos_send_uint8_t: Sending the current data uint8_t 13 | *dos_end_bit: After the final uint8_t is sent, send a '0'. 14 | */ 15 | typedef enum { 16 | dos_idle, 17 | dos_send_preamble, 18 | dos_send_bstart, 19 | dos_send_uint8_t, 20 | dos_end_bit 21 | } DCC_output_state_t; 22 | 23 | DCC_output_state_t DCC_state = dos_idle; //just to start out 24 | 25 | /// The currently queued packet to be put on the rails. Default is a reset packet. 26 | uint8_t current_packet[6] = {0,0,0,0,0,0}; 27 | /// How many data uint8_ts in the queued packet? 28 | volatile uint8_t current_packet_size = 0; 29 | /// How many uint8_ts remain to be put on the rails? 30 | volatile uint8_t current_uint8_t_counter = 0; 31 | /// How many bits remain in the current data uint8_t/preamble before changing states? 32 | volatile uint8_t current_bit_counter = 14; //init to 14 1's for the preamble 33 | /// A fixed-content packet to send when idle 34 | //uint8_t DCC_Idle_Packet[3] = {255,0,255}; 35 | /// A fixed-content packet to send to reset all decoders on layout 36 | //uint8_t DCC_Reset_Packet[3] = {0,0,0}; 37 | 38 | 39 | /// Timer1 TOP values for one and zero 40 | /** S 9.1 A specifies that '1's are represented by a square wave with a half-period of 58us (valid range: 55-61us) 41 | and '0's with a half-period of >100us (valid range: 95-9900us) 42 | Because '0's are stretched to provide DC power to non-DCC locos, we need two zero counters, 43 | one for the top half, and one for the bottom half. 44 | 45 | Here is how to calculate the timer1 counter values (from ATMega168 datasheet, 15.9.2): 46 | f_{OC1A} = \frac{f_{clk_I/O}}{2*N*(1+OCR1A)}) 47 | where N = prescalar, and OCR1A is the TOP we need to calculate. 48 | We know the desired half period for each case, 58us and >100us. 49 | So: 50 | for ones: 51 | 58us = (8*(1+OCR1A)) / (16MHz) 52 | 58us * 16MHz = 8*(1+OCR1A) 53 | 58us * 2MHz = 1+OCR1A 54 | OCR1A = 115 55 | 56 | for zeros: 57 | 100us * 2MHz = 1+OCR1A 58 | OCR1A = 199 59 | 60 | Thus, we also know that the valid range for stretched-zero operation is something like this: 61 | 9900us = (8*(1+OCR1A)) / (16MHz) 62 | 9900us * 2MHz = 1+OCR1A 63 | OCR1A = 19799 64 | 65 | */ 66 | 67 | uint16_t one_count=115; //58us 68 | uint16_t zero_high_count=199; //100us 69 | uint16_t zero_low_count=199; //100us 70 | 71 | /// Setup phase: configure and enable timer1 CTC interrupt, set OC1A and OC1B to toggle on CTC 72 | void setup_DCC_waveform_generator() { 73 | 74 | //Set the OC1A and OC1B pins (Timer1 output pins A and B) to output mode 75 | //On Arduino UNO, etc, OC1A is Port B/Pin 1 and OC1B Port B/Pin 2 76 | //On Arduino MEGA, etc, OC1A is or Port B/Pin 5 and OC1B Port B/Pin 6 77 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90CAN128__) || defined(__AVR_AT90CAN64__) || defined(__AVR_AT90CAN32__) 78 | DDRB |= (1<>(current_bit_counter-1)) & 1) //is current bit a '1'? 169 | { 170 | OCR1A = OCR1B = one_count; 171 | // Serial.print("1"); 172 | } 173 | else //or is it a '0' 174 | { 175 | OCR1A = OCR1B = zero_high_count; 176 | // Serial.print("0"); 177 | } 178 | if(!--current_bit_counter) //out of bits! time to either send a new uint8_t, or end the packet 179 | { 180 | if(!--current_uint8_t_counter) //if not more uint8_ts, move to dos_end_bit 181 | { 182 | DCC_state = dos_end_bit; 183 | } 184 | else //there are more uint8_ts…so, go back to dos_send_bstart 185 | { 186 | DCC_state = dos_send_bstart; 187 | } 188 | } 189 | break; 190 | /// Done with the packet. Send out a final '1', then head back to dos_idle to check for a new packet. 191 | case dos_end_bit: 192 | OCR1A = OCR1B = one_count; 193 | DCC_state = dos_idle; 194 | current_bit_counter = 14; //in preparation for a premable... 195 | // Serial.println(" 1"); 196 | break; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /DCCPacketScheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "DCCPacketScheduler.h" 2 | #include "DCCHardware.h" 3 | 4 | /* 5 | * DCC Waveform Generator 6 | * 7 | * 8 | * Hardware requirements: 9 | * *A DCC booster with Rail A and Rail B wired to pin 9 and 10 respectively. 10 | * *A locomotive with a decoder installed, reset to factory defaults. 11 | * 12 | * Author: Don Goodman-Wilson dgoodman@artificial-science.org 13 | * 14 | * based on software by Wolfgang Kufer, http://opendcc.de 15 | * 16 | * Copyright 2010 Don Goodman-Wilson 17 | * This program is free software: you can redistribute it and/or modify 18 | * it under the terms of the GNU General Public License as published by 19 | * the Free Software Foundation, either version 3 of the License, or 20 | * at your option) any later version. 21 | * 22 | * This program is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | * GNU General Public License for more details. 26 | * 27 | * You should have received a copy of the GNU General Public License 28 | * along with this program. If not, see . 29 | * 30 | */ 31 | 32 | /// The currently queued packet to be put on the rails. Default is a reset packet. 33 | extern uint8_t current_packet[6]; 34 | /// How many data uint8_ts in the queued packet? 35 | extern volatile uint8_t current_packet_size; 36 | /// How many uint8_ts remain to be put on the rails? 37 | extern volatile uint8_t current_uint8_t_counter; 38 | /// How many bits remain in the current data uint8_t/preamble before changing states? 39 | extern volatile uint8_t current_bit_counter; //init to 14 1's for the preamble 40 | /// A fixed-content packet to send when idle 41 | //uint8_t DCC_Idle_Packet[3] = {255,0,255}; 42 | /// A fixed-content packet to send to reset all decoders on layout 43 | //uint8_t DCC_Reset_Packet[3] = {0,0,0}; 44 | 45 | /////////////////////////////////////////////// 46 | /////////////////////////////////////////////// 47 | /////////////////////////////////////////////// 48 | 49 | DCCPacketScheduler::DCCPacketScheduler(void) : default_speed_steps(128), last_packet_address(255), packet_counter(1) 50 | { 51 | e_stop_queue.setup(E_STOP_QUEUE_SIZE); 52 | high_priority_queue.setup(HIGH_PRIORITY_QUEUE_SIZE); 53 | low_priority_queue.setup(LOW_PRIORITY_QUEUE_SIZE); 54 | repeat_queue.setup(REPEAT_QUEUE_SIZE); 55 | //periodic_refresh_queue.setup(PERIODIC_REFRESH_QUEUE_SIZE); 56 | } 57 | 58 | //for configuration 59 | void DCCPacketScheduler::setDefaultSpeedSteps(uint8_t new_speed_steps) 60 | { 61 | default_speed_steps = new_speed_steps; 62 | } 63 | 64 | void DCCPacketScheduler::setup(void) //for any post-constructor initialization 65 | { 66 | setup_DCC_waveform_generator(); 67 | 68 | //Following RP 9.2.4, begin by putting 20 reset packets and 10 idle packets on the rails. 69 | //use the e_stop_queue to do this, to ensure these packets go out first! 70 | 71 | 72 | DCCPacket p; 73 | uint8_t data[] = {0x00}; 74 | 75 | //reset packet: address 0x00, data 0x00, XOR 0x00; S 9.2 line 75 76 | p.addData(data,1); 77 | p.setAddress(0x00, DCC_SHORT_ADDRESS); 78 | p.setRepeat(20); 79 | p.setKind(reset_packet_kind); 80 | e_stop_queue.insertPacket(&p); 81 | 82 | //WHy in the world is it that what gets put on the rails is 4 reset packets, followed by 83 | //10 god know's what, followed by something else? 84 | // C0 FF 00 FF 85 | // 00 FF FF what are these? 86 | 87 | //idle packet: address 0xFF, data 0x00, XOR 0xFF; S 9.2 line 90 88 | p.setAddress(0xFF, DCC_SHORT_ADDRESS); 89 | p.setRepeat(10); 90 | p.setKind(idle_packet_kind); 91 | e_stop_queue.insertPacket(&p); //e_stop_queue will be empty, so no need to check if insertion was OK. 92 | 93 | } 94 | 95 | //helper functions 96 | void DCCPacketScheduler::repeatPacket(DCCPacket *p) 97 | { 98 | switch(p->getKind()) 99 | { 100 | case idle_packet_kind: 101 | case e_stop_packet_kind: //e_stop packets automatically repeat without having to be put in a special queue 102 | break; 103 | case speed_packet_kind: //speed packets go to the periodic_refresh queue 104 | // periodic_refresh_queue.insertPacket(p); 105 | // break; 106 | case function_packet_1_kind: //all other packets go to the repeat_queue 107 | case function_packet_2_kind: //all other packets go to the repeat_queue 108 | case function_packet_3_kind: //all other packets go to the repeat_queue 109 | case accessory_packet_kind: 110 | case reset_packet_kind: 111 | case ops_mode_programming_kind: 112 | case other_packet_kind: 113 | default: 114 | repeat_queue.insertPacket(p); 115 | } 116 | } 117 | 118 | //for enqueueing packets 119 | 120 | //setSpeed* functions: 121 | //new_speed contains the speed and direction. 122 | // a value of 0 = estop 123 | // a value of 1/-1 = stop 124 | // a value >1 (or <-1) means go. 125 | // valid non-estop speeds are in the range [1,127] / [-127,-1] with 1 = stop 126 | bool DCCPacketScheduler::setSpeed(uint16_t address, uint8_t address_kind, int8_t new_speed, uint8_t steps) 127 | { 128 | uint8_t num_steps = steps; 129 | //steps = 0 means use the default; otherwise use the number of steps specified 130 | if(!steps) 131 | num_steps = default_speed_steps; 132 | 133 | switch(num_steps) 134 | { 135 | case 14: 136 | return(setSpeed14(address, address_kind, new_speed)); 137 | case 28: 138 | return(setSpeed28(address, address_kind, new_speed)); 139 | case 128: 140 | return(setSpeed128(address, address_kind, new_speed)); 141 | } 142 | return false; //invalid number of steps specified. 143 | } 144 | 145 | bool DCCPacketScheduler::setSpeed14(uint16_t address, uint8_t address_kind, int8_t new_speed, bool F0) 146 | { 147 | DCCPacket p(address, address_kind); 148 | uint8_t dir = 1; 149 | uint8_t speed_data_uint8_ts[] = {0x40}; 150 | uint16_t abs_speed = new_speed; 151 | if(new_speed<0) 152 | { 153 | dir = 0; 154 | abs_speed = new_speed * -1; 155 | } 156 | if(!new_speed) //estop! 157 | return eStop(address, address_kind);//speed_data_uint8_ts[0] |= 0x01; //estop 158 | 159 | else if (abs_speed == 1) //regular stop! 160 | speed_data_uint8_ts[0] |= 0x00; //stop 161 | else //movement 162 | speed_data_uint8_ts[0] |= map(abs_speed, 2, 127, 2, 15); //convert from [2-127] to [1-14] 163 | speed_data_uint8_ts[0] |= (0x20*dir); //flip bit 3 to indicate direction; 164 | //Serial.println(speed_data_uint8_ts[0],BIN); 165 | p.addData(speed_data_uint8_ts,1); 166 | 167 | p.setRepeat(SPEED_REPEAT); 168 | 169 | p.setKind(speed_packet_kind); 170 | 171 | //speed packets get refreshed indefinitely, and so the repeat doesn't need to be set. 172 | //speed packets go to the high proirity queue 173 | return(high_priority_queue.insertPacket(&p)); 174 | } 175 | 176 | bool DCCPacketScheduler::setSpeed28(uint16_t address, uint8_t address_kind, int8_t new_speed) 177 | { 178 | DCCPacket p(address, address_kind); 179 | uint8_t dir = 1; 180 | uint8_t speed_data_uint8_ts[] = {0x40}; 181 | uint16_t abs_speed = new_speed; 182 | if(new_speed<0) 183 | { 184 | dir = 0; 185 | abs_speed = new_speed * -1; 186 | } 187 | // Serial.println(speed); 188 | // Serial.println(dir); 189 | if(new_speed == 0) //estop! 190 | return eStop(address, address_kind);//speed_data_uint8_ts[0] |= 0x01; //estop 191 | else if (abs_speed == 1) //regular stop! 192 | speed_data_uint8_ts[0] |= 0x00; //stop 193 | else //movement 194 | { 195 | speed_data_uint8_ts[0] |= map(abs_speed, 2, 127, 2, 0X1F); //convert from [2-127] to [2-31] 196 | //most least significant bit has to be shufled around 197 | speed_data_uint8_ts[0] = (speed_data_uint8_ts[0]&0xE0) | ((speed_data_uint8_ts[0]&0x1F) >> 1) | ((speed_data_uint8_ts[0]&0x01) << 4); 198 | } 199 | speed_data_uint8_ts[0] |= (0x20*dir); //flip bit 3 to indicate direction; 200 | // Serial.println(speed_data_uint8_ts[0],BIN); 201 | // Serial.println("======="); 202 | p.addData(speed_data_uint8_ts,1); 203 | 204 | p.setRepeat(SPEED_REPEAT); 205 | 206 | p.setKind(speed_packet_kind); 207 | 208 | //speed packets get refreshed indefinitely, and so the repeat doesn't need to be set. 209 | //speed packets go to the high proirity queue 210 | //return(high_priority_queue.insertPacket(&p)); 211 | return(high_priority_queue.insertPacket(&p)); 212 | } 213 | 214 | bool DCCPacketScheduler::setSpeed128(uint16_t address, uint8_t address_kind, int8_t new_speed) 215 | { 216 | //why do we get things like this? 217 | // 03 3F 16 15 3F (speed packet addressed to loco 03) 218 | // 03 3F 11 82 AF (speed packet addressed to loco 03, speed hex 0x11); 219 | DCCPacket p(address, address_kind); 220 | uint8_t dir = 1; 221 | uint16_t abs_speed = new_speed; 222 | uint8_t speed_data_uint8_ts[] = {0x3F,0x00}; 223 | if(new_speed<0) 224 | { 225 | dir = 0; 226 | abs_speed = new_speed * -1; 227 | } 228 | if(!new_speed) //estop! 229 | return eStop(address, address_kind);//speed_data_uint8_ts[0] |= 0x01; //estop 230 | else if (abs_speed == 1) //regular stop! 231 | speed_data_uint8_ts[1] = 0x00; //stop 232 | else //movement 233 | speed_data_uint8_ts[1] = abs_speed; //no conversion necessary. 234 | 235 | speed_data_uint8_ts[1] |= (0x80*dir); //flip bit 7 to indicate direction; 236 | p.addData(speed_data_uint8_ts,2); 237 | //Serial.print(speed_data_uint8_ts[0],BIN); 238 | //Serial.print(" "); 239 | //Serial.println(speed_data_uint8_ts[1],BIN); 240 | 241 | p.setRepeat(SPEED_REPEAT); 242 | 243 | p.setKind(speed_packet_kind); 244 | 245 | //speed packets get refreshed indefinitely, and so the repeat doesn't need to be set. 246 | //speed packets go to the high proirity queue 247 | return(high_priority_queue.insertPacket(&p)); 248 | } 249 | 250 | bool DCCPacketScheduler::setFunctions(uint16_t address, uint8_t address_kind, uint16_t functions) 251 | { 252 | // Serial.println(functions,HEX); 253 | if(setFunctions0to4(address, address_kind, functions&0x1F)) 254 | if(setFunctions5to8(address, address_kind, (functions>>5)&0x0F)) 255 | if(setFunctions9to12(address, address_kind, (functions>>9)&0x0F)) 256 | return true; 257 | return false; 258 | } 259 | 260 | bool DCCPacketScheduler::setFunctions(uint16_t address, uint8_t address_kind, uint8_t F0to4, uint8_t F5to8, uint8_t F9to12) 261 | { 262 | if(setFunctions0to4(address, address_kind, F0to4)) 263 | if(setFunctions5to8(address, address_kind, F5to8)) 264 | if(setFunctions9to12(address, address_kind, F9to12)) 265 | return true; 266 | return false; 267 | } 268 | 269 | bool DCCPacketScheduler::setFunctions0to4(uint16_t address, uint8_t address_kind, uint8_t functions) 270 | { 271 | // Serial.println("setFunctions0to4"); 272 | // Serial.println(functions,HEX); 273 | DCCPacket p(address, address_kind); 274 | uint8_t data[] = {0x80}; 275 | 276 | //Obnoxiously, the headlights (F0, AKA FL) are not controlled 277 | //by bit 0, but by bit 4. Really? 278 | 279 | //get functions 1,2,3,4 280 | data[0] |= (functions>>1) & 0x0F; 281 | //get functions 0 282 | data[0] |= (functions&0x01) << 4; 283 | 284 | p.addData(data,1); 285 | p.setKind(function_packet_1_kind); 286 | p.setRepeat(FUNCTION_REPEAT); 287 | return low_priority_queue.insertPacket(&p); 288 | } 289 | 290 | 291 | bool DCCPacketScheduler::setFunctions5to8(uint16_t address, uint8_t address_kind, uint8_t functions) 292 | { 293 | // Serial.println("setFunctions5to8"); 294 | // Serial.println(functions,HEX); 295 | DCCPacket p(address, address_kind); 296 | uint8_t data[] = {0xB0}; 297 | 298 | data[0] |= functions & 0x0F; 299 | 300 | p.addData(data,1); 301 | p.setKind(function_packet_2_kind); 302 | p.setRepeat(FUNCTION_REPEAT); 303 | return low_priority_queue.insertPacket(&p); 304 | } 305 | 306 | bool DCCPacketScheduler::setFunctions9to12(uint16_t address, uint8_t address_kind, uint8_t functions) 307 | { 308 | // Serial.println("setFunctions9to12"); 309 | // Serial.println(functions,HEX); 310 | DCCPacket p(address, address_kind); 311 | uint8_t data[] = {0xA0}; 312 | 313 | //least significant four functions (F5--F8) 314 | data[0] |= functions & 0x0F; 315 | 316 | p.addData(data,1); 317 | p.setKind(function_packet_3_kind); 318 | p.setRepeat(FUNCTION_REPEAT); 319 | return low_priority_queue.insertPacket(&p); 320 | } 321 | 322 | 323 | //other cool functions to follow. Just get these working first, I think. 324 | 325 | //bool DCCPacketScheduler::setTurnout(uint16_t address) 326 | //bool DCCPacketScheduler::unsetTurnout(uint16_t address) 327 | 328 | bool DCCPacketScheduler::opsProgramCV(uint16_t address, uint8_t address_kind, uint16_t CV, uint8_t CV_data) 329 | { 330 | //format of packet: 331 | // {preamble} 0 [ AAAAAAAA ] 0 111011VV 0 VVVVVVVV 0 DDDDDDDD 0 EEEEEEEE 1 (write) 332 | // {preamble} 0 [ AAAAAAAA ] 0 111001VV 0 VVVVVVVV 0 DDDDDDDD 0 EEEEEEEE 1 (verify) 333 | // {preamble} 0 [ AAAAAAAA ] 0 111010VV 0 VVVVVVVV 0 DDDDDDDD 0 EEEEEEEE 1 (bit manipulation) 334 | // only concerned with "write" form here. 335 | 336 | DCCPacket p(address, address_kind); 337 | uint8_t data[] = {0xEC, 0x00, 0x00}; 338 | 339 | // split the CV address up among data uint8_ts 0 and 1 340 | data[0] |= ((CV-1) & 0x3FF) >> 8; 341 | data[1] = (CV-1) & 0xFF; 342 | data[2] = CV_data; 343 | 344 | p.addData(data,3); 345 | p.setKind(ops_mode_programming_kind); 346 | p.setRepeat(OPS_MODE_PROGRAMMING_REPEAT); 347 | 348 | return low_priority_queue.insertPacket(&p); 349 | } 350 | 351 | //more specific functions 352 | 353 | //broadcast e-stop command 354 | bool DCCPacketScheduler::eStop(void) 355 | { 356 | // 111111111111 0 00000000 0 01DC0001 0 EEEEEEEE 1 357 | DCCPacket e_stop_packet(0); //address 0 358 | uint8_t data[] = {0x71}; //01110001 359 | e_stop_packet.addData(data,1); 360 | e_stop_packet.setKind(e_stop_packet_kind); 361 | e_stop_packet.setRepeat(10); 362 | e_stop_queue.insertPacket(&e_stop_packet); 363 | //now, clear all other queues 364 | high_priority_queue.clear(); 365 | low_priority_queue.clear(); 366 | repeat_queue.clear(); 367 | return true; 368 | } 369 | 370 | bool DCCPacketScheduler::eStop(uint16_t address, uint8_t address_kind) 371 | { 372 | // 111111111111 0 0AAAAAAA 0 01001001 0 EEEEEEEE 1 373 | // or 374 | // 111111111111 0 0AAAAAAA 0 01000001 0 EEEEEEEE 1 375 | DCCPacket e_stop_packet(address, address_kind); 376 | uint8_t data[] = {0x41}; //01000001 377 | e_stop_packet.addData(data,1); 378 | e_stop_packet.setKind(e_stop_packet_kind); 379 | e_stop_packet.setRepeat(10); 380 | e_stop_queue.insertPacket(&e_stop_packet); 381 | //now, clear this packet's address from all other queues 382 | high_priority_queue.forget(address, address_kind); 383 | low_priority_queue.forget(address, address_kind); 384 | repeat_queue.forget(address, address_kind); 385 | } 386 | 387 | bool DCCPacketScheduler::setBasicAccessory(uint16_t address, uint8_t function) 388 | { 389 | DCCPacket p(address); 390 | 391 | uint8_t data[] = { 0x01 | ((function & 0x03) << 1) }; 392 | p.addData(data, 1); 393 | p.setKind(basic_accessory_packet_kind); 394 | p.setRepeat(OTHER_REPEAT); 395 | 396 | return low_priority_queue.insertPacket(&p); 397 | } 398 | 399 | bool DCCPacketScheduler::unsetBasicAccessory(uint16_t address, uint8_t function) 400 | { 401 | DCCPacket p(address); 402 | 403 | uint8_t data[] = { ((function & 0x03) << 1) }; 404 | p.addData(data, 1); 405 | p.setKind(basic_accessory_packet_kind); 406 | p.setRepeat(OTHER_REPEAT); 407 | 408 | return low_priority_queue.insertPacket(&p); 409 | } 410 | 411 | //to be called periodically within loop() 412 | void DCCPacketScheduler::update(void) //checks queues, puts whatever's pending on the rails via global current_packet. easy-peasy 413 | { 414 | DCC_waveform_generation_hasshin(); 415 | 416 | //TODO ADD POM QUEUE? 417 | if(!current_uint8_t_counter) //if the ISR needs a packet: 418 | { 419 | DCCPacket p; 420 | //Take from e_stop queue first, then high priority queue. 421 | //every fifth packet will come from low priority queue. 422 | //every 20th packet will come from periodic refresh queue. (Why 20? because. TODO reasoning) 423 | //if there's a packet ready, and the counter is not divisible by 5 424 | //first, we need to know which queues have packets ready, and the state of the this->packet_counter. 425 | if( !e_stop_queue.isEmpty() ) //if there's an e_stop packet, send it now! 426 | { 427 | //e_stop 428 | e_stop_queue.readPacket(&p); //nothing more to do. e_stop_queue is a repeat_queue, so automatically repeats where necessary. 429 | } 430 | else 431 | { 432 | bool doHigh = high_priority_queue.notEmpty() && high_priority_queue.notRepeat(last_packet_address); 433 | bool doLow = low_priority_queue.notEmpty() && low_priority_queue.notRepeat(last_packet_address) && 434 | !((packet_counter % LOW_PRIORITY_INTERVAL) && doHigh); 435 | bool doRepeat = repeat_queue.notEmpty() && repeat_queue.notRepeat(last_packet_address) && 436 | !((packet_counter % REPEAT_INTERVAL) && (doHigh || doLow)); 437 | //bool doRefresh = periodic_refresh_queue.notEmpty() && periodic_refresh_queue.notRepeat(last_packet_address) && 438 | // !((packet_counter % PERIODIC_REFRESH_INTERVAL) && (doHigh || doLow || doRepeat)); 439 | //examine queues in order from lowest priority to highest. 440 | //if(doRefresh) 441 | //{ 442 | // periodic_refresh_queue.readPacket(&p); 443 | // ++packet_counter; 444 | //} 445 | //else if(doRepeat) 446 | if(doRepeat) 447 | { 448 | //Serial.println("repeat"); 449 | repeat_queue.readPacket(&p); 450 | ++packet_counter; 451 | } 452 | else if(doLow) 453 | { 454 | //Serial.println("low"); 455 | low_priority_queue.readPacket(&p); 456 | ++packet_counter; 457 | } 458 | else if(doHigh) 459 | { 460 | //Serial.println("high"); 461 | high_priority_queue.readPacket(&p); 462 | ++packet_counter; 463 | } 464 | //if none of these conditions hold, DCCPackets initialize to the idle packet, so that's what'll get sent. 465 | //++packet_counter; //it's a uint8_t; let it overflow, that's OK. 466 | //enqueue the packet for repitition, if necessary: 467 | //Serial.println("idle"); 468 | repeatPacket(&p); 469 | } 470 | last_packet_address = p.getAddress(); //remember the address to compare with the next packet 471 | current_packet_size = p.getBitstream(current_packet); //feed to the starving ISR. 472 | //output the packet, for checking: 473 | //if(current_packet[0] != 0xFF) //if not idle 474 | //{ 475 | // for(uint8_t i = 0; i < current_packet_size; ++i) 476 | // { 477 | // Serial.print(current_packet[i],BIN); 478 | // Serial.print(" "); 479 | // } 480 | // Serial.println(""); 481 | //} 482 | current_uint8_t_counter = current_packet_size; 483 | } 484 | } 485 | --------------------------------------------------------------------------------