91
91
* License: GNU GPL v2 or later
92
92
*/
93
93
94
+ #include < RF12.h>
95
+
94
96
// Utility macros
95
97
#define ARRAY_LENGTH (a ) ((sizeof (a)) / (sizeof (a)[0 ]))
96
98
#define MIN (x, y ) ((x) < (y) ? (x) : (y))
97
99
#define MAX (x, y ) ((x) > (y) ? (x) : (y))
98
100
#define LIMIT (min, val, max ) (min > val ? min : (max < val) ? max : val)
99
101
100
- const byte VERSION = 1 ;
102
+ const byte VERSION = 2 ;
103
+
104
+ const byte rfm12b_band = RF12_868MHZ;
105
+ const byte rfm12b_group = 123 ;
106
+ const byte rfm12b_local_id = 13 ;
107
+ const byte rfm12b_remote_id = 0 ;
101
108
102
109
volatile byte ring_buffer[256 ] = { 0 };
103
110
volatile byte rb_write; // current write position in ring buffer
@@ -111,6 +118,70 @@ byte cur_channel = 0; // Index into above array
111
118
112
119
const byte pwm_pins[3 ] = {5 , 6 , 9 };
113
120
121
+ // Format of rfm12b data packets used to send commands and status updates:
122
+ struct rc_data {
123
+ uint8_t modify : 1 ; // Modify (=1) or Read (=0) command.
124
+ uint8_t relative : 1 ; // Next byte is relative (=1) or absolute (=0).
125
+ uint8_t channel : 6 ; // Channel number to be written/read
126
+ union {
127
+ uint8_t abs_value;
128
+ int8_t rel_value;
129
+ };
130
+ };
131
+
132
+ struct rc_packet {
133
+ uint8_t hdr; // RF12 packet header
134
+ struct rc_data d; // RF12 packet payload
135
+ };
136
+
137
+ /*
138
+ * RFM12B network protocol between this rotary remote control (RRC) and
139
+ * the remote nodes that "channels" and "levels":
140
+ *
141
+ * The objective of this network protocol is to allow an RRC to read and
142
+ * modify the current level of a channel on a remote node. The RRC knows
143
+ * the remote node ID and channel number beforehand, but must be able to
144
+ * the request the current channel level, and also request a modification
145
+ * of the channel level across the network. Furthermore, the channel level
146
+ * may be manipulated by other means (either from other RRCs, or via some
147
+ * other mechanism), so the RRC needs to receive updates of the current
148
+ * channel level. The channel and its associated level is "owned" by the
149
+ * remote node, but the RRC needs to know it in order to display useful
150
+ * feedback to its user.
151
+ *
152
+ * More specifically, the RFM12B messages needed are:
153
+ *
154
+ * - Status request: From RRC to remote node to request current level of
155
+ * a given channel. Packet data: {0, 0, $channel, 0}
156
+ * - Status update: Broadcast from remote node, to inform of current level
157
+ * of a given channel. Packet data: {0, 0, $channel, $abs_value}
158
+ * - Change request: From RRC to remote node to request a change in the
159
+ * level of a given channel. Packet data: {1, 1, $channel, $rel_value}
160
+ * OR {1, 0, $channel, $abs_value}
161
+ *
162
+ * The network traffic proceeds as follows:
163
+ *
164
+ * - When switching to a (different) channel, the RRC typically sends a
165
+ * status request to the remote node, to establish the current level for
166
+ * that channel.
167
+ *
168
+ * - Upon receiving a status request, the remote node _broadcasts_ a
169
+ * status update containing the current level of that channel.
170
+ *
171
+ * - When rotation occurs, the RRC sends a change request to the remote
172
+ * node, which should adjust its channel accordingly.
173
+ *
174
+ * - Whenever the remote node adjusts a channel, the new level of that
175
+ * channel should be _broadcast_ as a status update.
176
+ *
177
+ * Fundamentally, the RRC is free to send status requests at any time, and
178
+ * the remote node is free to broadcast status updates at any time.
179
+ */
180
+
181
+ struct rc_packet out_buf[16 ]; // RFM12B packet output buffer
182
+ byte ob_write; // current write position in out_buf ringbuffer
183
+ byte ob_read; // current read position in out_buf ringbuffer
184
+
114
185
void setup (void )
115
186
{
116
187
Serial.begin (115200 );
@@ -147,8 +218,23 @@ void setup(void)
147
218
148
219
sei (); // Re-enable interrupts
149
220
221
+ // Set up RFM12B communication
222
+ rf12_initialize (rfm12b_local_id, rfm12b_band, rfm12b_group);
223
+
224
+ status_request (next_packet (), cur_channel);
225
+
150
226
Serial.print (F (" Rotary Encoder version " ));
151
227
Serial.println (VERSION);
228
+ Serial.print (F (" Using RFM12B group " ));
229
+ Serial.print (rfm12b_group);
230
+ Serial.print (F (" from node " ));
231
+ Serial.print (rfm12b_local_id);
232
+ Serial.print (F (" to node " ));
233
+ Serial.print (rfm12b_remote_id);
234
+ Serial.print (F (" @ " ));
235
+ Serial.print (rfm12b_band == RF12_868MHZ ? 868
236
+ : (rfm12b_band == RF12_433MHZ ? 433 : 915 ));
237
+ Serial.println (F (" MHz" ));
152
238
Serial.println (F (" Ready" ));
153
239
}
154
240
@@ -238,6 +324,8 @@ void next_channel()
238
324
++cur_channel %= ARRAY_LENGTH (rot_values);
239
325
// Display level of the new channel
240
326
analogWrite (pwm_pins[cur_channel], 0xff - rot_values[cur_channel]);
327
+ // Ask for a status update on the current channel
328
+ status_request (next_packet (), cur_channel);
241
329
}
242
330
243
331
void update_value (int increment)
@@ -247,6 +335,8 @@ void update_value(int increment)
247
335
rot_values[cur_channel] = level;
248
336
// Display adjusted level
249
337
analogWrite (pwm_pins[cur_channel], 0xff - level);
338
+ // Ask for remote end to update its status
339
+ adjust_request (next_packet (), cur_channel, increment);
250
340
}
251
341
252
342
void print_state (char event)
@@ -258,8 +348,96 @@ void print_state(char event)
258
348
Serial.println (rot_values[cur_channel]);
259
349
}
260
350
351
+ void print_byte_buf (volatile byte * buf, size_t len)
352
+ {
353
+ for (size_t i = 0 ; i < len; i++) {
354
+ Serial.print (F (" " ));
355
+ if (buf[i] <= 0xf )
356
+ Serial.print (F (" 0" ));
357
+ Serial.print (buf[i], HEX);
358
+ }
359
+ Serial.println ();
360
+ }
361
+
362
+ bool recv_update (void )
363
+ {
364
+ if (rf12_recvDone ()) {
365
+ Serial.println (F (" Received RFM12B communication:" ));
366
+ if (rf12_crc)
367
+ Serial.println (F (" CRC mismatch. Discarding!" ));
368
+ else {
369
+ Serial.print (F (" rf12_hdr: 0x" ));
370
+ Serial.println (rf12_hdr, HEX);
371
+ Serial.print (F (" rf12_len: " ));
372
+ Serial.println (rf12_len);
373
+ Serial.print (F (" rf12_data(hex):" ));
374
+ print_byte_buf (rf12_data, rf12_len);
375
+ return true ;
376
+ }
377
+ }
378
+ return false ;
379
+ }
380
+
381
+ struct rc_packet * next_packet (void )
382
+ {
383
+ byte i = ob_write;
384
+ ++ob_write %= ARRAY_LENGTH (out_buf);
385
+ return out_buf + i;
386
+ }
387
+
388
+ void status_request (struct rc_packet * packet, byte channel)
389
+ {
390
+ if (channel >= 64 ) {
391
+ Serial.print (F (" Illegal channel number: " ));
392
+ Serial.println (channel);
393
+ return ;
394
+ }
395
+
396
+ // Regular packet destined for remote node id
397
+ packet->hdr = RF12_HDR_DST | (RF12_HDR_MASK & rfm12b_remote_id);
398
+ packet->d .modify = 0 ;
399
+ packet->d .relative = 0 ;
400
+ packet->d .channel = channel;
401
+ packet->d .abs_value = 0 ;
402
+ }
403
+
404
+ void adjust_request (struct rc_packet * packet, byte channel, int8_t adjust)
405
+ {
406
+ if (channel >= 64 ) {
407
+ Serial.print (F (" Illegal channel number: " ));
408
+ Serial.println (channel);
409
+ return ;
410
+ }
411
+
412
+ // Regular packet destined for remote node id
413
+ packet->hdr = RF12_HDR_DST | (RF12_HDR_MASK & rfm12b_remote_id);
414
+ packet->d .modify = 1 ;
415
+ packet->d .relative = 1 ;
416
+ packet->d .channel = channel;
417
+ packet->d .rel_value = adjust;
418
+ }
419
+
420
+ bool service_out_buf (void )
421
+ {
422
+ if (ob_write != ob_read && rf12_canSend ()) {
423
+ // Send next scheduled packet
424
+ byte i = ob_read;
425
+ ++ob_read %= ARRAY_LENGTH (out_buf);
426
+ struct rc_packet * p = out_buf + i;
427
+ rf12_sendStart (p->hdr , &(p->d ), sizeof p->d );
428
+ Serial.print (F (" Sending packet: " ));
429
+ Serial.print (p->hdr , HEX);
430
+ print_byte_buf ((byte *) &(p->d ), sizeof p->d );
431
+ return true ;
432
+ }
433
+ return false ;
434
+ }
435
+
261
436
void loop (void )
262
437
{
438
+ service_out_buf ();
439
+ recv_update ();
440
+
263
441
int events = process_inputs ();
264
442
265
443
if (events & BTN_DOWN)
@@ -278,7 +456,6 @@ void loop(void)
278
456
print_state (' <' );
279
457
}
280
458
281
- // TODO: RFM12B communication
282
459
// TODO: Low power mode
283
460
// TODO: Run on JeeNode Micro v2?
284
461
}
0 commit comments