DCCpp
This is the library version of a program for Arduino to control railroading DCC devices.
Turnout.cpp
1 /**********************************************************************
2 
3 Turnout.cpp, renamed from Accessories.cpp
4 COPYRIGHT (c) 2013-2016 Gregg E. Berman
5 
6 Part of DCC++ BASE STATION for the Arduino
7 
8 **********************************************************************/
9 #include "Arduino.h"
10 #ifdef ARDUINO_ARCH_AVR
11 
12 #include "DCCpp.h"
13 
14 #ifdef USE_TURNOUT
15 
16 #ifdef VISUALSTUDIO
17 #include "string.h"
18 #endif
19 
20 #include "Turnout.h"
21 #include "DCCpp_Uno.h"
22 //#include "Comm.h"
23 
24 #ifdef USE_TEXTCOMMAND
25 #include "TextCommand.h"
26 #endif
27 
28 #ifdef USE_EEPROM
29 #include "EEStore.h"
30 #include "EEPROM.h"
31 #endif
32 
34 
35 void Turnout::begin(int id, int add, int subAdd) {
36 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
37 #if defined(USE_EEPROM) && defined(DCCPP_DEBUG_MODE)
38  if (strncmp(EEStore::data.id, EESTORE_ID, sizeof(EESTORE_ID)) != 0) { // check to see that eeStore contains valid DCC++ ID
39  DCCPP_INTERFACE.println(F("Turnout::begin() must be called BEFORE DCCpp.begin() !"));
40  }
41 #endif
42  if (firstTurnout == NULL) {
43  firstTurnout = this;
44  }
45  else if (get(id) == NULL) {
46  Turnout *tt = firstTurnout;
47  while (tt->nextTurnout != NULL)
48  tt = tt->nextTurnout;
49  tt->nextTurnout = this;
50  }
51 #endif
52 
53  this->set(id, add, subAdd);
54 
55 #ifdef USE_TEXTCOMMAND
56  DCCPP_INTERFACE.print("<O>");
57 #if !defined(USE_ETHERNET)
58  DCCPP_INTERFACE.println("");
59 #endif
60 #endif
61 }
62 
64 
65 void Turnout::set(int id, int add, int subAdd) {
66  this->data.id = id;
67  this->data.address = add;
68  this->data.subAddress = subAdd;
69  this->data.tStatus = 0;
70 }
71 
73 
74 void Turnout::activate(int s) {
75  data.tStatus = (s>0); // if s>0 set turnout=ON, else if zero or negative set turnout=OFF
76  DCCpp::mainRegs.setAccessory(this->data.address, this->data.subAddress, this->data.tStatus);
77 #ifdef USE_EEPROM
78  if (this->eepromPos>0)
79 #ifdef VISUALSTUDIO
80  EEPROM.put(this->eepromPos, (void *) &(this->data.tStatus), sizeof(int)); // ArduiEmulator version...
81 #else
82  EEPROM.put(this->eepromPos, this->data.tStatus);
83 #endif
84 #endif
85 #ifdef USE_TEXTCOMMAND
86  DCCPP_INTERFACE.print("<H");
87  DCCPP_INTERFACE.print(data.id);
88  if (data.tStatus == 0)
89  DCCPP_INTERFACE.print(" 0>");
90  else
91  DCCPP_INTERFACE.print(" 1>");
92 #if !defined(USE_ETHERNET)
93  DCCPP_INTERFACE.println("");
94 #endif
95 #endif
96 }
97 
98 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
99 
101 Turnout* Turnout::get(int id) {
102  Turnout *tt;
103  for (tt = firstTurnout; tt != NULL && tt->data.id != id; tt = tt->nextTurnout)
104  ;
105  return(tt);
106 }
107 
109 void Turnout::remove(int id) {
110  Turnout *tt, *pp;
111 
112  for (tt = firstTurnout, pp = NULL; tt != NULL && tt->data.id != id; pp = tt, tt = tt->nextTurnout)
113  ;
114 
115  if (tt == NULL) {
116 #ifdef USE_TEXTCOMMAND
117  DCCPP_INTERFACE.print("<X>");
118 #if !defined(USE_ETHERNET)
119  DCCPP_INTERFACE.println("");
120 #endif
121 #endif
122  return;
123  }
124 
125  if (tt == firstTurnout)
126  firstTurnout = tt->nextTurnout;
127  else
128  pp->nextTurnout = tt->nextTurnout;
129 
130  free(tt);
131 
132 #ifdef USE_TEXTCOMMAND
133  DCCPP_INTERFACE.print("<O>");
134 #if !defined(USE_ETHERNET)
135  DCCPP_INTERFACE.println("");
136 #endif
137 #endif
138 }
139 
141 
142 int Turnout::count() {
143  int count = 0;
144  Turnout *tt;
145  for (tt = firstTurnout; tt != NULL; tt = tt->nextTurnout)
146  count++;
147  return count;
148 }
149 
151 
152 #ifdef USE_EEPROM
153 void Turnout::load() {
154  struct TurnoutData data;
155  Turnout *tt;
156 
157  for (int i = 0; i<EEStore::data.nTurnouts; i++) {
158 #ifdef VISUALSTUDIO
159  EEPROM.get(EEStore::pointer(), (void *)&data, sizeof(TurnoutData));
160 #else
161  EEPROM.get(EEStore::pointer(), data);
162 #endif
163 #if defined(USE_TEXTCOMMAND)
164  tt = create(data.id, data.address, data.subAddress);
165 #else
166  tt = get(data.id);
167 #ifdef DCCPP_DEBUG_MODE
168  if (tt == NULL)
169  DCCPP_INTERFACE.println(F("Turnout::begin() must be called BEFORE Turnout::load() !"));
170  else
171 #endif
172  tt->set(data.id, data.address, data.subAddress);
173 #endif
174  tt->data.tStatus = data.tStatus;
175  tt->eepromPos = EEStore::pointer();
176  EEStore::advance(sizeof(tt->data));
177  }
178 }
179 
181 
182 void Turnout::store() {
183  Turnout *tt;
184 
185  tt = firstTurnout;
186  EEStore::data.nTurnouts = 0;
187 
188  while (tt != NULL) {
189  tt->eepromPos = EEStore::pointer();
190 #ifdef VISUALSTUDIO
191  EEPROM.put(EEStore::pointer(), (void *) &(tt->data), sizeof(TurnoutData)); // ArduiEmulator version...
192 #else
193  EEPROM.put(EEStore::pointer(), tt->data);
194 #endif
195  EEStore::advance(sizeof(tt->data));
196  tt = tt->nextTurnout;
197  EEStore::data.nTurnouts++;
198  }
199 }
200 #endif
201 
202 #endif
203 
204 #if defined(USE_TEXTCOMMAND)
205 
207 void Turnout::parse(char *c){
208  int n,s,m;
209  Turnout *t;
210 
211  switch(sscanf(c,"%d %d %d",&n,&s,&m)){
212 
213  case 2: // argument is string with id number of turnout followed by zero (not thrown) or one (thrown)
214  t=get(n);
215  if(t!=NULL)
216  t->activate(s);
217 #ifdef USE_TEXTCOMMAND
218  else
219  {
220  DCCPP_INTERFACE.print("<X>");
221 #if !defined(USE_ETHERNET)
222  DCCPP_INTERFACE.println("");
223 #endif
224  }
225 #endif
226  break;
227 
228  case 3: // argument is string with id number of turnout followed by an address and subAddress
229  create(n,s,m);
230  break;
231 
232  case 1: // argument is a string with id number only
233  remove(n);
234  break;
235 
236 #ifdef DCCPP_PRINT_DCCPP
237  case -1: // no arguments
238  show();
239  break;
240 #endif
241  }
242 }
243 
244 Turnout *Turnout::create(int id, int add, int subAdd) {
245  Turnout *tt = new Turnout();
246 
247  if (tt == NULL) { // problem allocating memory
248 #ifdef USE_TEXTCOMMAND
249  DCCPP_INTERFACE.print("<X>");
250 #if !defined(USE_ETHERNET)
251  DCCPP_INTERFACE.println("");
252 #endif
253 #endif
254  return(tt);
255  }
256 
257  tt->begin(id, add, subAdd);
258 
259  return(tt);
260 }
261 
262 #endif //USE_TEXTCOMMAND
263 
264 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
265 #ifdef DCCPP_PRINT_DCCPP
266 
268 
269 void Turnout::show() {
270  Turnout *tt;
271 
272  if (firstTurnout == NULL) {
273  DCCPP_INTERFACE.print("<X>");
274 #if !defined(USE_ETHERNET)
275  DCCPP_INTERFACE.println("");
276 #endif
277  return;
278  }
279 
280  for (tt = firstTurnout; tt != NULL; tt = tt->nextTurnout) {
281  DCCPP_INTERFACE.print("<H");
282  DCCPP_INTERFACE.print(tt->data.id);
283  DCCPP_INTERFACE.print(" ");
284  DCCPP_INTERFACE.print(tt->data.address);
285  DCCPP_INTERFACE.print(" ");
286  DCCPP_INTERFACE.print(tt->data.subAddress);
287  if (tt->data.tStatus == 0)
288  DCCPP_INTERFACE.print(" 0>");
289  else
290  DCCPP_INTERFACE.print(" 1>");
291 #if !defined(USE_ETHERNET)
292  DCCPP_INTERFACE.println("");
293 #endif
294  }
295 }
296 #endif
297 
299 
300 Turnout *Turnout::firstTurnout = NULL;
301 #endif
302 
303 #endif //USE_TURNOUT
304 #endif