2 * Copyright © 2017 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
16 #include <ao_rn4678.h>
20 static uint8_t ao_rn_connected;
25 static void ao_rn_dbg(char *format, ...) {
27 uint32_t irq = ao_arch_irqsave();
28 ao_arch_release_interrupts();
33 ao_arch_irqrestore(irq);
36 static char ao_rn_dir;
39 ao_rn_log_char(char c, char dir)
41 if (dir != ao_rn_dir) {
42 putchar(dir); putchar('\n');
47 putchar('\\'); putchar('r');
50 putchar('\\'); putchar('n');
59 ao_rn_log_out_char(char c)
61 ao_rn_log_char(c, '}');
65 ao_rn_log_in_char(char c)
67 ao_rn_log_char(c, '{');
73 ao_rn_log_out_char(c);
74 ao_serial_rn_putchar(c);
80 int c = _ao_serial_rn_pollchar();
82 if (c != AO_READ_AGAIN) {
83 ao_arch_release_interrupts();
84 ao_rn_log_in_char((char) c);
85 ao_arch_block_interrupts();
90 #define ao_rn_dbg(fmt, ...)
91 #define ao_rn_putchar(c) ao_serial_rn_putchar(c)
92 #define _ao_rn_pollchar() _ao_serial_rn_pollchar()
95 /* For stdio, this skips all status messages *sigh* */
97 #define STATUS_CHAR '%'
99 static const char *status_strings[] = {
108 #define NUM_STATUS_STRING (sizeof status_strings/sizeof status_strings[0])
110 static char ao_rn_buffer[64];
111 static size_t ao_rn_buf_cnt, ao_rn_buf_ptr;
112 static int ao_rn_draining;
113 static AO_TICK_TYPE ao_rn_buf_time;
115 /* Well, this is annoying. The status strings from the RN4678 can't be
116 * disabled due to a firmware bug. So, this code finds those in the
117 * input and strips them out.
120 _ao_wrap_rn_pollchar(void)
122 int c = AO_READ_AGAIN;
126 while (!done && !ao_rn_draining) {
127 c = _ao_serial_rn_pollchar();
129 if (c == AO_READ_AGAIN) {
130 if (ao_rn_buf_cnt && (ao_time() - ao_rn_buf_time) > AO_MS_TO_TICKS(1000)) {
134 return AO_READ_AGAIN;
138 /* buffering chars */
140 if (c == STATUS_CHAR) {
141 /* End of status string, drop it and carry on */
142 ao_rn_buffer[ao_rn_buf_cnt] = '\0';
143 // ao_rn_dbg("discard %s\n", ao_rn_buffer);
145 } else if (ao_rn_buf_cnt == sizeof(ao_rn_buffer)) {
146 /* If we filled the buffer, just give up */
149 ao_rn_buffer[ao_rn_buf_cnt++] = (char) c;
150 for (i = 0; i < NUM_STATUS_STRING; i++) {
151 size_t cmp = strlen(status_strings[i]);
152 if (cmp >= ao_rn_buf_cnt)
153 cmp = ao_rn_buf_cnt-1;
154 if (memcmp(ao_rn_buffer+1, status_strings[i], cmp) == 0)
157 if (i == NUM_STATUS_STRING)
160 } else if (c == STATUS_CHAR) {
161 ao_rn_buffer[0] = (char) c;
164 ao_rn_buf_time = ao_time();
168 if (ao_rn_draining) {
169 c = ao_rn_buffer[ao_rn_buf_ptr++] & 0xff;
170 if (ao_rn_buf_ptr == ao_rn_buf_cnt) {
171 ao_rn_buf_ptr = ao_rn_buf_cnt = 0;
192 // ao_rn_dbg("drain...\n");
193 ao_serial_rn_drain();
195 ao_arch_block_interrupts();
196 while (_ao_rn_pollchar() == AO_READ_AGAIN) {
197 if (_ao_serial_rn_sleep_for(AO_MS_TO_TICKS(10))) {
202 ao_arch_release_interrupts();
204 // ao_rn_dbg("drain done\n");
208 ao_rn_send_cmd(char *cmd, char *param)
210 // ao_rn_dbg("send_cmd %s%s\n", cmd, param ? param : "");
219 ao_rn_wait_char(AO_TICK_TYPE giveup_time)
223 ao_arch_block_interrupts();
224 while ((c = _ao_rn_pollchar()) == AO_READ_AGAIN) {
225 AO_TICK_SIGNED delay = (AO_TICK_SIGNED) (giveup_time - ao_time());
227 ao_arch_release_interrupts();
228 return AO_READ_AGAIN;
230 _ao_serial_rn_sleep_for((AO_TICK_TYPE) delay);
232 ao_arch_release_interrupts();
237 ao_rn_wait_for(AO_TICK_TYPE timeout, char *match)
239 char reply[AO_RN_MAX_REPLY_LEN + 1];
240 size_t match_len = strlen(match);
241 AO_TICK_TYPE giveup_time = ao_time() + timeout;
244 // ao_rn_dbg("wait for %d, \"%s\"\n", timeout, match);
245 memset(reply, ' ', sizeof(reply));
246 while (memcmp(reply, match, match_len) != 0) {
247 c = ao_rn_wait_char(giveup_time);
248 if (c == AO_READ_AGAIN) {
249 // ao_rn_dbg("\twait for timeout\n");
250 return AO_RN_TIMEOUT;
252 reply[match_len] = (char) c;
253 memmove(reply, reply+1, match_len);
254 reply[match_len] = '\0';
255 // ao_rn_dbg("\tmatch now \"%s\"\n", reply);
257 // ao_rn_dbg("\twait for ok\n");
262 ao_rn_wait_line(AO_TICK_TYPE giveup_time, char *line, int len)
266 // ao_rn_dbg("wait line\n");
268 int c = ao_rn_wait_char(giveup_time);
271 if (c == AO_READ_AGAIN) {
272 // ao_rn_dbg("\twait line timeout\n");
273 return AO_RN_TIMEOUT;
279 // ao_rn_dbg("\twait line \"%s\"\n", line);
296 ao_rn_wait_status(void)
298 char message[AO_RN_MAX_REPLY_LEN];
299 AO_TICK_TYPE giveup_time = ao_time() + AO_RN_CMD_TIMEOUT;
302 // ao_rn_dbg("wait status\n");
303 status = ao_rn_wait_line(giveup_time, message, sizeof (message));
304 if (status == AO_RN_OK)
305 if (strncmp(message, "AOK", 3) != 0)
306 status = AO_RN_ERROR;
317 // ao_rn_dbg("set name...\n");
319 n = ao_serial_number;
321 *--s = (uint8_t) ('0' + n % 10);
323 ao_rn_send_cmd(AO_RN_SET_NAME_CMD "TeleBT-", s);
324 return ao_rn_wait_status();
328 ao_rn_get_name(char *name, int len)
330 // ao_rn_dbg("get name...\n");
331 ao_rn_send_cmd(AO_RN_GET_NAME_CMD, NULL);
332 return ao_rn_wait_line(ao_time() + AO_RN_CMD_TIMEOUT, name, len);
336 ao_rn_check_link(void)
338 ao_rn_connected = 1 - ao_gpio_get(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN);
345 ao_wakeup(&ao_rn_connected);
349 ao_bt_panic(int where)
353 for (i = 0; i < 25; i++) {
354 ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(10));
355 ao_delay(AO_MS_TO_TICKS(10));
357 ao_delay(AO_MS_TO_TICKS(500));
358 for (i = 0; i < where; i++) {
359 ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(200));
360 ao_delay(AO_MS_TO_TICKS(200));
365 static uint8_t ao_rn_stdio;
368 * Set the stdio echo for the bluetooth link
371 ao_rn_echo(uint8_t echo)
373 ao_stdios[ao_rn_stdio].echo = echo;
379 int status = AO_RN_ERROR;
383 ao_rn_dbg("ao_rn top\n");
385 /* Select CMD mode after the device gets out of reset */
386 ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, AO_RN_CMD_CMD);
388 for (i = 0; i < 3; i++) {
389 ao_rn_dbg("reset device\n");
391 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, 0);
392 ao_delay(AO_MS_TO_TICKS(100));
394 /* Reboot the RN4678 and wait for it to start talking */
396 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, 1);
397 status = ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, AO_RN_REBOOT_MSG);
398 if (status != AO_RN_OK) {
399 ao_rn_dbg("reboot failed\n");
403 /* After it reboots, it can take a moment before it responds
406 status = ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, "CMD> ");
408 if (status == AO_RN_TIMEOUT) {
410 (void) ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, "CMD> ");
413 ao_rn_send_cmd(AO_RN_VERSION_CMD, NULL);
414 (void) ao_rn_wait_status();
416 /* Check to see if the name is already set and assume
417 * that the device is ready to go
419 status = ao_rn_get_name(name, sizeof (name));
420 if (status != AO_RN_OK) {
421 ao_rn_dbg("get name failed\n");
422 status = ao_rn_get_name(name, sizeof (name));
423 if (status != AO_RN_OK)
427 if (strncmp(name, "TeleBT-", 7) == 0) {
428 ao_rn_dbg("name is set\n");
433 /* Make the command pin control command/data mode */
434 ao_rn_send_cmd(AO_RN_SET_COMMAND_PIN, NULL);
435 if (ao_rn_wait_status() != AO_RN_OK) {
436 ao_rn_dbg("set command pin failed\n");
440 ao_rn_send_cmd(AO_RN_SET_STATUS_STRING, AO_RN_STATUS_STRING_ENABLE);
441 if (ao_rn_wait_status() != AO_RN_OK) {
442 ao_rn_dbg("set status string\n");
446 /* Select 'fast' mode to ignore command sequence (more or less) */
447 ao_rn_send_cmd(AO_RN_SET_FAST_MODE, NULL);
448 if (ao_rn_wait_status() != AO_RN_OK) {
449 ao_rn_dbg("set fast mode failed\n");
453 /* Finally, set the name. Doing this last makes it possible to check
454 * if the whole sequence has been done
456 if (ao_rn_set_name() != AO_RN_OK) {
457 ao_rn_dbg("set name failed\n");
461 /* After we've configured the device, go back around and reboot it
462 * as that's how we get the new configuration to take effect
465 ao_rn_dbg("ao_rn status %d\n", status);
467 if (status != AO_RN_OK)
470 ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, AO_RN_CMD_DATA);
472 /* Wait for the hardware to finish sending messages, then clear the queue */
473 ao_delay(AO_MS_TO_TICKS(200));
476 ao_exti_enable(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN);
481 * Separate debug code when things aren't working. Just dump
482 * inbound bluetooth characters to stdout
487 ao_arch_block_interrupts();
488 while ((c = _ao_rn_pollchar()) == AO_READ_AGAIN)
489 ao_sleep(&ao_serial_rn_rx_fifo);
490 ao_arch_release_interrupts();
493 ao_rn_stdio = ao_add_stdio(_ao_wrap_rn_pollchar,
494 ao_serial_rn_putchar,
500 * Now just hang around and flash the blue LED when we've got
504 ao_arch_block_interrupts();
505 while (!ao_rn_connected)
506 ao_sleep(&ao_rn_connected);
507 ao_arch_release_interrupts();
508 while (ao_rn_connected) {
509 ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20));
510 if (ao_rn_buf_cnt != 0)
511 ao_wakeup(&ao_stdin_ready);
512 ao_delay(AO_SEC_TO_TICKS(3));
518 static struct ao_task ao_rn_task;
527 * Factory reset. Flip pin P3_1 5 times within the first five
528 * seconds of power-on
531 /* Select our target output pin */
532 ao_enable_output(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, v);
534 /* Turn off the BT device using the SW_BTN pin */
535 printf("Power down BT\n"); flush();
536 ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, 0);
537 ao_delay(AO_MS_TO_TICKS(1000));
539 /* And turn it back on */
540 printf("Power up BT\n"); flush();
541 ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, 1);
543 /* Right after power on, poke P3_1 five times to force a
546 for (i = 0; i < 20; i++) {
548 ao_delay(AO_MS_TO_TICKS(50));
549 ao_gpio_set(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, v);
551 ao_led_on(AO_BT_LED);
553 ao_led_off(AO_BT_LED);
556 /* And let P3_1 float again */
557 ao_enable_input(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, AO_EXTI_MODE_PULL_NONE);
559 printf("Reboot BT\n"); flush();
560 ao_delay(AO_MS_TO_TICKS(100));
561 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, 0);
562 ao_delay(AO_MS_TO_TICKS(100));
563 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, 1);
572 while ((c = getchar()) != '~')
577 static const struct ao_cmds rn_cmds[] = {
578 { ao_rn_factory, "F\0Factory reset rn4678" },
580 { ao_rn_send, "B\0Send data to rn4678. End with ~" },
588 (void) ao_rn_set_name;
590 ao_serial_rn_set_speed(AO_SERIAL_SPEED_115200);
593 ao_enable_output(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, 0);
596 ao_enable_output(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, 1);
598 /* P3_7 command/data selector */
599 ao_enable_output(AO_RN_CMD_PORT, AO_RN_CMD_PIN, AO_RN_CMD_CMD);
601 ao_enable_input(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, AO_EXTI_MODE_PULL_NONE);
602 ao_exti_setup(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN,
603 AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_LOW,
606 ao_cmd_register(rn_cmds);
607 ao_add_task(&ao_rn_task, ao_rn, "bluetooth");