DCCpp
This is the library version of a program for Arduino to control railroading DCC devices.
Outputs.cpp
1 /**********************************************************************
2 
3 Outputs.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 #include "Outputs.h"
16 
17 #ifdef VISUALSTUDIO
18 #include "string.h"
19 #endif
20 #include "TextCommand.h"
21 #include "DCCpp_Uno.h"
22 #include "EEStore.h"
23 #ifdef USE_EEPROM
24 #include "EEPROM.h"
25 #endif
26 #include "Comm.h"
27 
29 
30 void Output::begin(int id, int pin, int iFlag) {
31 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
32 #if defined(USE_EEPROM) && defined(DCCPP_DEBUG_MODE)
33  if (strncmp(EEStore::data.id, EESTORE_ID, sizeof(EESTORE_ID)) != 0) { // check to see that eeStore contains valid DCC++ ID
34  INTERFACE.println(F("Output::begin() must be called BEFORE DCCpp.begin() !"));
35  }
36 #endif
37  if (firstOutput == NULL) {
38  firstOutput = this;
39  }
40  else if ((get(id)) == NULL) {
41  Output *tt = firstOutput;
42  while (tt->nextOutput != NULL)
43  tt = tt->nextOutput;
44  tt->nextOutput = this;
45  }
46 #endif
47 
48  this->set(id, pin, iFlag);
49 
50 #ifdef USE_TEXTCOMMAND
51  INTERFACE.print("<O>");
52 #if !defined(USE_ETHERNET)
53  INTERFACE.println("");
54 #endif
55 #endif
56 }
57 
59 
60 void Output::set(int id, int pin, int iFlag) {
61  this->data.id = id;
62  this->data.pin = pin;
63  this->data.iFlag = iFlag;
64  this->data.oStatus = 0;
65 
66  // sets status to 0 (INACTIVE) is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
67  this->data.oStatus = bitRead(this->data.iFlag, 1) ? bitRead(this->data.iFlag, 2) : 0;
68 #ifdef VISUALSTUDIO
69  ArduiEmulator::Arduino::dontCheckNextPinAccess = true;
70 #endif
71  digitalWrite(this->data.pin, this->data.oStatus ^ bitRead(this->data.iFlag, 0));
72 #ifdef VISUALSTUDIO
73  ArduiEmulator::Arduino::dontCheckNextPinAccess = true;
74 #endif
75  pinMode(this->data.pin, OUTPUT);
76 }
77 
79 
80 void Output::activate(int s){
81  data.oStatus=(s>0); // if s>0, set status to active, else inactive
82  digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); // set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW)
83 #ifdef USE_EEPROM
84  if(num>0)
85 #ifdef VISUALSTUDIO
86  EEPROM.put(num, (void *)&data.oStatus, 1);
87 #else
88  EEPROM.put(num, data.oStatus);
89 #endif
90 #endif
91 #ifdef USE_TEXTCOMMAND
92  INTERFACE.print("<Y");
93  INTERFACE.print(data.id);
94  if(data.oStatus==0)
95  INTERFACE.print(" 0>");
96  else
97  INTERFACE.print(" 1>");
98 #if !defined(USE_ETHERNET)
99  INTERFACE.println("");
100 #endif
101 #endif
102 }
103 
104 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
105 
107 Output* Output::get(int n){
108  Output *tt;
109  for(tt=firstOutput;tt!=NULL && tt->data.id!=n;tt=tt->nextOutput);
110  return(tt);
111 }
113 
114 void Output::remove(int n){
115  Output *tt,*pp;
116 
117  for(tt=firstOutput;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextOutput);
118 
119  if(tt==NULL){
120 #ifdef USE_TEXTCOMMAND
121  INTERFACE.print("<X>");
122 #if !defined(USE_ETHERNET)
123  INTERFACE.println("");
124 #endif
125 #endif
126  return;
127  }
128 
129  if(tt==firstOutput)
130  firstOutput=tt->nextOutput;
131  else
132  pp->nextOutput=tt->nextOutput;
133 
134  free(tt);
135 
136 #ifdef USE_TEXTCOMMAND
137  INTERFACE.print("<O>");
138 #if !defined(USE_ETHERNET)
139  INTERFACE.println("");
140 #endif
141 #endif
142 }
143 
145 
146 int Output::count() {
147  int count = 0;
148  Output *tt;
149  for (tt = firstOutput; tt != NULL; tt = tt->nextOutput)
150  count++;
151  return count;
152 }
153 
155 
156 #ifdef USE_EEPROM
157 void Output::load() {
158  struct OutputData data;
159  Output *tt;
160 
161  for (int i = 0; i<EEStore::data.nOutputs; i++) {
162 #ifdef VISUALSTUDIO
163  EEPROM.get(EEStore::pointer(), (void *)&data, sizeof(OutputData)); // ArduiEmulator version...
164 #else
165  EEPROM.get(EEStore::pointer(), data);
166 #endif
167 #if defined(USE_TEXTCOMMAND)
168  tt = create(data.id, data.pin, data.iFlag);
169 #else
170  tt = get(data.id);
171 #ifdef DCCPP_DEBUG_MODE
172  if (tt == NULL)
173  INTERFACE.println(F("Output::begin() must be called BEFORE Output::load() !"));
174  else
175 #endif
176  tt->set(data.id, data.pin, data.iFlag);
177 #endif
178 
179  tt->data.oStatus = bitRead(tt->data.iFlag, 1) ? bitRead(tt->data.iFlag, 2) : data.oStatus; // restore status to EEPROM value is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
180 #ifdef VISUALSTUDIO
181  ArduiEmulator::Arduino::dontCheckNextPinAccess = true;
182 #endif
183  digitalWrite(tt->data.pin, tt->data.oStatus ^ bitRead(tt->data.iFlag, 0));
184 #ifdef VISUALSTUDIO
185  ArduiEmulator::Arduino::dontCheckNextPinAccess = true;
186 #endif
187  pinMode(tt->data.pin, OUTPUT);
188  tt->num = EEStore::pointer();
189  EEStore::advance(sizeof(tt->data));
190  }
191 }
192 
194 
195 void Output::store() {
196  Output *tt;
197 
198  tt = firstOutput;
199  EEStore::data.nOutputs = 0;
200 
201  while (tt != NULL) {
202  tt->num = EEStore::pointer();
203 #ifdef VISUALSTUDIO
204  EEPROM.put(EEStore::pointer(), (void *)&(tt->data), sizeof(OutputData)); // ArduiEmulator version...
205 #else
206  EEPROM.put(EEStore::pointer(), tt->data);
207 #endif
208  EEStore::advance(sizeof(tt->data));
209  tt = tt->nextOutput;
210  EEStore::data.nOutputs++;
211  }
212 }
213 #endif
214 
215 #endif
216 
217 #if defined(USE_TEXTCOMMAND)
218 
220 void Output::parse(char *c){
221  int n,s,m;
222  Output *t;
223 
224  switch(sscanf(c,"%d %d %d",&n,&s,&m)){
225 
226  case 2: // argument is string with id number of output followed by zero (LOW) or one (HIGH)
227  t=get(n);
228  if(t!=NULL)
229  t->activate(s);
230  else
231  {
232  INTERFACE.print("<X>");
233 #if !defined(USE_ETHERNET)
234  INTERFACE.println("");
235 #endif
236  }
237  break;
238 
239  case 3: // argument is string with id number of output followed by a pin number and invert flag
240  create(n,s,m);
241  break;
242 
243  case 1: // argument is a string with id number only
244  remove(n);
245  break;
246 
247 #ifdef DCCPP_PRINT_DCCPP
248  case -1: // no arguments
249  show(); // verbose show
250 #endif
251  break;
252  }
253 }
254 
256 
257 Output *Output::create(int id, int pin, int iFlag){
258  Output *tt = new Output();
259 
260  if (tt == NULL) { // problem allocating memory
261  INTERFACE.print("<X>");
262 #if !defined(USE_ETHERNET)
263  INTERFACE.println("");
264 #endif
265  return(tt);
266  }
267 
268  tt->begin(id, pin, iFlag);
269 
270  return(tt);
271 }
272 
273 #endif
274 
275 #if defined(USE_EEPROM) || defined(USE_TEXTCOMMAND)
276 #ifdef DCCPP_PRINT_DCCPP
277 
279 
280 void Output::show() {
281  Output *tt;
282 
283  if (firstOutput == NULL) {
284  INTERFACE.print("<X>");
285 #if !defined(USE_ETHERNET)
286  INTERFACE.println("");
287 #endif
288  return;
289  }
290 
291  for (tt = firstOutput; tt != NULL; tt = tt->nextOutput) {
292  INTERFACE.print("<Y");
293  INTERFACE.print(tt->data.id);
294  INTERFACE.print(" ");
295  INTERFACE.print(tt->data.pin);
296  INTERFACE.print(" ");
297  INTERFACE.print(tt->data.iFlag);
298 
299  if (tt->data.oStatus == 0)
300  INTERFACE.print(" 0>");
301  else
302  INTERFACE.print(" 1>");
303 #if !defined(USE_ETHERNET)
304  INTERFACE.println("");
305 #endif
306  }
307 }
308 #endif
309 
311 
312 Output *Output::firstOutput=NULL;
313 
314 #endif
315 
316 #endif //USE_OUTPUT
317 #endif