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