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