-
Notifications
You must be signed in to change notification settings - Fork 1
/
XBee.cs
304 lines (252 loc) · 9.86 KB
/
XBee.cs
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
using System.IO.Ports;
using Microsoft.SPOT.Hardware;
using System.Threading;
namespace STLK
{
public delegate void PortStatusReceived(byte status);
public delegate void FrameReceivedEventHandler(byte[] data);
public delegate void AnalogStatusReceived(int[] readings);
public delegate void XBeeCallback(ApiFrame frame);
public class XBee
{
public event PortStatusReceived StatusReceived;
public event AnalogStatusReceived AnalogStatusReceived;
public event FrameReceivedEventHandler FrameReceived;
private const int ApiIdentifier = 0;
private const int FrameIdentifier = 1;
private object _syncObject = new object();
private byte _nextFrameId;
private readonly SerialPort _uart;
private readonly ApiFrameBuilder _frameBuilder;
private ApiFrame[] _frameBuffer;
private readonly byte[] _txDataBase = new byte[] {
0x0, // frame type
0x1, // frame id, set to zero for no reply
// ID of recipient, or use 0xFFFF for broadcast
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
// 16 bit of recipient or 0xFFFE if unknown
0xFF,
0xFE
};
public XBee()
{
_frameBuffer = new ApiFrame[10];
_nextFrameId = 1;
_frameBuilder = new ApiFrameBuilder();
_uart = new SerialPort("COM1", 9800, Parity.None, 8, StopBits.One);
_uart.DataReceived += DataReceived;
_uart.Open();
}
public void SetDigitalOutput(ulong destinationSerialNumber, XBeeCommand command, bool state)
{
byte stateByte;
if (state)
stateByte = 0x5;
else
stateByte = 0x4;
SendRemoteAtCommand(destinationSerialNumber, command, null, stateByte);
}
public void SendRemoteAtCommand(ulong destinationSerialNumber, XBeeCommand command, XBeeCallback callback)
{
SendRemoteAtCommand(destinationSerialNumber, command, callback, 0x0);
}
public byte SendRemoteAtCommand(ulong destinationSerialNumber, XBeeCommand command, XBeeCallback callback, byte value)
{
ApiFrame frame = new ApiFrame();
byte frameId = GetNextFrameId();
int txDataLenght;
if (value == 0x0)
txDataLenght = 3;
else
txDataLenght = 4;
byte[] txDataContent = new byte[txDataLenght];
txDataContent[0] = 0x02; // appply changes on remote
ushort commandUShort = (ushort)command;
txDataContent[1] = (byte)(commandUShort >> 8);
txDataContent[2] = (byte)commandUShort;
if (value != 0x0)
txDataContent[3] = value;
byte[] txData = Utility.CombineArrays(_txDataBase, txDataContent);
txData[0] = (byte)ApiFrameName.RemoteCommandRequest;
txData[1] = frameId;
for (int i = 0; i < 8; i++)
{
txData[9 - i] = (byte)(destinationSerialNumber >> (8 * i));
}
frame.FrameData = txData;
frame.Callback = callback;
return SendApiFrame(frame);
}
public ApiFrame SendRemoteAtCommandSync(ulong destinationSerialNumber, XBeeCommand command)
{
AutoResetEvent stopWaitHandle = new AutoResetEvent(false);
ApiFrame receivedFrame = null;
SendRemoteAtCommand(destinationSerialNumber, command, result =>
{
receivedFrame = result;
stopWaitHandle.Set();
});
stopWaitHandle.WaitOne(500, false);
return receivedFrame;
}
public byte SendAtCommand(XBeeCommand command)
{
return SendAtCommand(command, null, 0, 0);
}
public byte SendAtCommand(XBeeCommand command, XBeeCallback callback)
{
return SendAtCommand(command, callback, 0, 0);
}
public byte SendAtCommand(XBeeCommand command, XBeeCallback callback, uint value, int size)
{
ApiFrame frame = new ApiFrame();
byte frameId = GetNextFrameId();
byte[] buffer = new byte[4 + size];
buffer[ApiIdentifier] = (byte)ApiFrameName.ATCommand;
buffer[FrameIdentifier] = frameId;
ushort commandUShort = (ushort)command;
buffer[2] = (byte)(commandUShort >> 8);
buffer[3] = (byte)commandUShort;
if (size > 0)
{
Utility.InsertValueIntoArray(buffer, 4, size, value);
}
frame.FrameData = buffer;
frame.Callback = callback;
return SendApiFrame(frame);
}
public byte SendData(ulong destinationSerialNumber, ushort destinationAddress, byte[] data, XBeeCallback callback)
{
ApiFrame frame = new ApiFrame();
byte frameId = GetNextFrameId();
byte[] txDataContent = new byte[2];
txDataContent[0] = 0x0;
txDataContent[1] = 0x0;
txDataContent = Utility.CombineArrays(txDataContent, data);
byte[] txData = Utility.CombineArrays(_txDataBase, txDataContent);
txData[0] = (byte)ApiFrameName.ZigBeeTransmitRequest;
txData[1] = frameId;
for (int i = 0; i < 8; i++)
{
txData[9 - i] = (byte)(destinationSerialNumber >> (8 * i));
}
frame.FrameData = txData;
frame.Callback = callback;
return SendApiFrame(frame);
}
internal byte SendApiFrame(ApiFrame frame)
{
byte frameId = frame.FrameData[FrameIdentifier];
_frameBuffer[frameId] = frame;
var buffer = frame.Serialize();
_uart.Write(buffer, 0, frame.Length + 4);
return frameId;
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_uart.BytesToRead <= 0)
{
return;
}
byte[] buffer = new byte[1];
while (_uart.BytesToRead > 0)
{
_uart.Read(buffer, 0, 1);
_frameBuilder.Append(buffer[0]);
if (_frameBuilder.IsComplete)
{
// got a frame, do something
ReceivedApiFrame(_frameBuilder.GetApiFrame());
_frameBuilder.Reset();
}
}
}
private void ReceivedApiFrame(ApiFrame frame)
{
switch (frame.FrameData[ApiIdentifier])
{
case (byte)ApiFrameName.ZigBeeIODataSampleRxIndicator:
if (frame.Length >= 18)
{
int analogSampleIndex = 16;
int digitalChannelMask = (frame.FrameData[13] << 8) | frame.FrameData[14];
if (digitalChannelMask > 0)
{
if (StatusReceived != null)
{
StatusReceived(frame.FrameData[17]);
}
analogSampleIndex = 18;
}
if (AnalogStatusReceived != null)
{
int[] analogSamples = new int[4];
int analogChannelMask = frame.FrameData[15];
for (int i = 0; i < 4; i++)
{
if ((analogChannelMask >> i) == 1)
{
analogSamples[i] = (frame.FrameData[analogSampleIndex] << 8) | frame.FrameData[analogSampleIndex + 1];
analogSampleIndex += 2;
}
else
{
analogSamples[i] = -1;
}
}
AnalogStatusReceived(analogSamples);
}
}
break;
case (byte)ApiFrameName.ATCommandResponse:
ReceivedAtCommandResponse(frame);
break;
case (byte)ApiFrameName.RemoteCommandResponse:
ReceivedAtCommandResponse(frame);
break;
case (byte)ApiFrameName.ZigBeeReceivePacket:
ReceivedZigBeePacket(frame);
break;
}
}
internal void ReceivedAtCommandResponse(ApiFrame frame)
{
byte frameID = frame.FrameData[FrameIdentifier];
XBeeCommand command = (XBeeCommand)(frame.FrameData[2] << 8 | frame.FrameData[3]);
ApiFrame sentFrame = _frameBuffer[frameID];
if (sentFrame != null && sentFrame.Callback != null)
{
sentFrame.Callback(frame);
if (!(command == XBeeCommand.NodeDiscover && frame.Length > 0))
{
_frameBuffer[frameID] = null;
}
}
}
internal void ReceivedZigBeePacket(ApiFrame frame)
{
byte[] data = Utility.ExtractRangeFromArray(frame.FrameData, 12, frame.Length - 12);
if (FrameReceived != null)
FrameReceived(data);
}
private byte GetNextFrameId()
{
lock (_syncObject)
{
_nextFrameId++;
if (_nextFrameId > 9)
{
_nextFrameId = 0;
}
return _nextFrameId;
}
}
}
}