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