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