Skip to content

Commit ee9928f

Browse files
committed
First version containing RFM12B communication
1 parent 532857c commit ee9928f

File tree

1 file changed

+179
-2
lines changed

1 file changed

+179
-2
lines changed

rotary_encoder/rotary_encoder.ino

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,20 @@
9191
* License: GNU GPL v2 or later
9292
*/
9393

94+
#include <RF12.h>
95+
9496
// Utility macros
9597
#define ARRAY_LENGTH(a) ((sizeof (a)) / (sizeof (a)[0]))
9698
#define MIN(x, y) ((x) < (y) ? (x) : (y))
9799
#define MAX(x, y) ((x) > (y) ? (x) : (y))
98100
#define LIMIT(min, val, max) (min > val ? min : (max < val) ? max : val)
99101

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;
101108

102109
volatile byte ring_buffer[256] = { 0 };
103110
volatile byte rb_write; // current write position in ring buffer
@@ -111,6 +118,70 @@ byte cur_channel = 0; // Index into above array
111118

112119
const byte pwm_pins[3] = {5, 6, 9};
113120

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+
114185
void setup(void)
115186
{
116187
Serial.begin(115200);
@@ -147,8 +218,23 @@ void setup(void)
147218

148219
sei(); // Re-enable interrupts
149220

221+
// Set up RFM12B communication
222+
rf12_initialize(rfm12b_local_id, rfm12b_band, rfm12b_group);
223+
224+
status_request(next_packet(), cur_channel);
225+
150226
Serial.print(F("Rotary Encoder version "));
151227
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"));
152238
Serial.println(F("Ready"));
153239
}
154240

@@ -238,6 +324,8 @@ void next_channel()
238324
++cur_channel %= ARRAY_LENGTH(rot_values);
239325
// Display level of the new channel
240326
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);
241329
}
242330

243331
void update_value(int increment)
@@ -247,6 +335,8 @@ void update_value(int increment)
247335
rot_values[cur_channel] = level;
248336
// Display adjusted level
249337
analogWrite(pwm_pins[cur_channel], 0xff - level);
338+
// Ask for remote end to update its status
339+
adjust_request(next_packet(), cur_channel, increment);
250340
}
251341

252342
void print_state(char event)
@@ -258,8 +348,96 @@ void print_state(char event)
258348
Serial.println(rot_values[cur_channel]);
259349
}
260350

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+
261436
void loop(void)
262437
{
438+
service_out_buf();
439+
recv_update();
440+
263441
int events = process_inputs();
264442

265443
if (events & BTN_DOWN)
@@ -278,7 +456,6 @@ void loop(void)
278456
print_state('<');
279457
}
280458

281-
// TODO: RFM12B communication
282459
// TODO: Low power mode
283460
// TODO: Run on JeeNode Micro v2?
284461
}

0 commit comments

Comments
 (0)