-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAccessories.cpp
239 lines (182 loc) · 7.36 KB
/
Accessories.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/**********************************************************************
Accessories.cpp
COPYRIGHT (c) 2013-2015 Gregg E. Berman
Part of DCC++ BASE STATION for the Arduino
**********************************************************************/
/**********************************************************************
DCC++ BASE STATION can keep track of the direction of any turnout that is controlled
by a DCC stationary accessory decoder. All turnouts, as well as any other DCC accessories
connected in this fashion, can always be operated using the DCC BASE STATION Accessory command:
<a ADDRESS SUBADDRESS ACTIVATE>
However, this general command simply sends the appropriate DCC instruction packet to the main tracks
to operate connected accessories. It does not store or retain any information regarding the current
status of that accessory.
To have this sketch store and retain the direction of DCC-connected turnouts, as well as automatically
invoke the required <a> command as needed, first define/edit/delete such turnouts using the following
variations of the "T" command:
<T ID ADDRESS SUBADDRESS>: creates a new turnout ID, with specified ADDRESS and SUBADDRESS
if turnout ID already exists, it is updated with specificed ADDRESS and SUBADDRESS
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
<T ID>: deletes definition of turnout ID
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
<T>: lists all defined turnouts
returns: <H ID ADDRESS SUBADDRESS THROW> for each defined turnout or <X> if no turnouts defined
where
ID: the numeric ID (0-32767) of the turnout to control
ADDRESS: the primary address of the decoder controlling this turnout (0-511)
SUBADDRESS: the subaddress of the decoder controlling this turnout (0-3)
Once all turnouts have been properly defined, use the <E> command to store their definitions to EEPROM.
If you later make edits/additions/deletions to the turnout definitions, you must invoke the <E> command if you want those
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
To "throw" turnouts that have been defined use:
<T ID THROW>: sets turnout ID to either the "thrown" or "unthrown" position
returns: <H ID THROW>, or <X> if turnout ID does not exist
where
ID: the numeric ID (0-32767) of the turnout to control
THROW: 0 (unthrown) or 1 (thrown)
When controlled as such, the Arduino updates and stores the direction of each Turnout in EEPROM so
that it is retained even without power. A list of the current directions of each Turnout in the form <H ID THROW> is generated
by this sketch whenever the <s> status command is invoked. This provides an efficient way of initializing
the directions of any Turnouts being monitored or controlled by a separate interface or GUI program.
**********************************************************************/
#include "Accessories.h"
#include "DCCpp_Uno.h"
#include "Comm.h"
#include "Config.h"
#include "EEStore.h"
#include "SerialCommand.h"
#include <EEPROM.h>
///////////////////////////////////////////////////////////////////////////////
void Turnout::activate(int s){
char c[20];
data.tStatus=(s>0); // if s>0 set turnout=ON, else if zero or negative set turnout=OFF
sprintf(c,"a %d %d %d",data.address,data.subAddress,data.tStatus);
SerialCommand::parse(c);
if(num>0)
EEPROM.put(num,data.tStatus);
INTERFACE.print("<H");
INTERFACE.print(data.id);
if(data.tStatus==0)
INTERFACE.print(" 0>");
else
INTERFACE.print(" 1>");
}
///////////////////////////////////////////////////////////////////////////////
Turnout* Turnout::get(int n){
Turnout *tt;
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;tt=tt->nextTurnout);
return(tt);
}
///////////////////////////////////////////////////////////////////////////////
void Turnout::remove(int n){
Turnout *tt,*pp;
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextTurnout);
if(tt==NULL){
INTERFACE.print("<X>");
return;
}
if(tt==firstTurnout)
firstTurnout=tt->nextTurnout;
else
pp->nextTurnout=tt->nextTurnout;
free(tt);
INTERFACE.print("<O>");
}
///////////////////////////////////////////////////////////////////////////////
void Turnout::show(int n){
Turnout *tt;
if(firstTurnout==NULL){
INTERFACE.print("<X>");
return;
}
for(tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
INTERFACE.print("<H");
INTERFACE.print(tt->data.id);
if(n==1){
INTERFACE.print(" ");
INTERFACE.print(tt->data.address);
INTERFACE.print(" ");
INTERFACE.print(tt->data.subAddress);
}
if(tt->data.tStatus==0)
INTERFACE.print(" 0>");
else
INTERFACE.print(" 1>");
}
}
///////////////////////////////////////////////////////////////////////////////
void Turnout::parse(char *c){
int n,s,m;
Turnout *t;
switch(sscanf(c,"%d %d %d",&n,&s,&m)){
case 2: // argument is string with id number of turnout followed by zero (not thrown) or one (thrown)
t=get(n);
if(t!=NULL)
t->activate(s);
else
INTERFACE.print("<X>");
break;
case 3: // argument is string with id number of turnout followed by an address and subAddress
create(n,s,m,1);
break;
case 1: // argument is a string with id number only
remove(n);
break;
case -1: // no arguments
show(1); // verbose show
break;
}
}
///////////////////////////////////////////////////////////////////////////////
void Turnout::load(){
struct TurnoutData data;
Turnout *tt;
for(int i=0;i<EEStore::eeStore->data.nTurnouts;i++){
EEPROM.get(EEStore::pointer(),data);
tt=create(data.id,data.address,data.subAddress);
tt->data.tStatus=data.tStatus;
tt->num=EEStore::pointer();
EEStore::advance(sizeof(tt->data));
}
}
///////////////////////////////////////////////////////////////////////////////
void Turnout::store(){
Turnout *tt;
tt=firstTurnout;
EEStore::eeStore->data.nTurnouts=0;
while(tt!=NULL){
tt->num=EEStore::pointer();
EEPROM.put(EEStore::pointer(),tt->data);
EEStore::advance(sizeof(tt->data));
tt=tt->nextTurnout;
EEStore::eeStore->data.nTurnouts++;
}
}
///////////////////////////////////////////////////////////////////////////////
Turnout *Turnout::create(int id, int add, int subAdd, int v){
Turnout *tt;
if(firstTurnout==NULL){
firstTurnout=(Turnout *)calloc(1,sizeof(Turnout));
tt=firstTurnout;
} else if((tt=get(id))==NULL){
tt=firstTurnout;
while(tt->nextTurnout!=NULL)
tt=tt->nextTurnout;
tt->nextTurnout=(Turnout *)calloc(1,sizeof(Turnout));
tt=tt->nextTurnout;
}
if(tt==NULL){ // problem allocating memory
if(v==1)
INTERFACE.print("<X>");
return(tt);
}
tt->data.id=id;
tt->data.address=add;
tt->data.subAddress=subAdd;
tt->data.tStatus=0;
if(v==1)
INTERFACE.print("<O>");
return(tt);
}
///////////////////////////////////////////////////////////////////////////////
Turnout *Turnout::firstTurnout=NULL;