Add RN-42 Bluetooth module support

master
tmk 2014-07-01 04:29:45 +09:00
parent be19b2bf32
commit 31a298f9d9
6 changed files with 696 additions and 6 deletions

View File

@ -0,0 +1,294 @@
Roving RN-42
============
TODO
----
- USB connection check
- BT on/off check: whether RX line is pulled up?
- CTS/RTS lines are needed? just connect in loop back if no flow control is needed.
- CTS is needed for waking up from deep sleep?
- USB/BT switching BT is on -> BT, BT is off -> USB
- Low voltage lock out LVLO of DC converter for protection of Lipo
- Lipo charger configuration: fast charge time
- Low voltage alarm: LED indcates voltage of Lipo
- Power saving: HHKB scan, BT radio control
- Serial API callback when data are available/received(and when send buffer is available)
- Serial API instance several serial ports can be used
Configuration
-------------
Ver 6.15 04/26/2013
(c) Roving Networks
***Settings***
BTA=0006664B3AE3
BTName=tmkBT-3AE3
Baudrt(SW4)=115K
Mode =Pair
Authen=2
Bonded=1
Rem=001BDC06415B
***ADVANCED Settings***
SrvName= keyboard/mouse
SrvClass=0000
DevClass=05C0
InqWindw=0100
PagWindw=0100
CfgTimer=255
StatuStr=NULL
HidFlags=3f
DTRtimer=8
KeySwapr=0
***OTHER Settings***
Profile= HID
CfgChar= $
SniffEna=0
LowPower=0
TX Power=4
IOPorts= 0
IOValues=0
Sleeptmr=0
DebugMod=0
RoleSwch=0
Ver 6.15 04/26/2013
(c) Roving Networks
***Settings***
BTA=0006664B3AE3
BTName=tmkBT-3AE3
Baudrt(SW4)=115K
Mode =DTR
Authen=2
Bonded=0
Rem=BCF5AC9BCB7E
***ADVANCED Settings***
SrvName= keyboard/mouse
SrvClass=0000
DevClass=0000
InqWindw=0100
PagWindw=0100
CfgTimer=255
StatuStr=NULL
HidFlags=3f
DTRtimer=8
KeySwapr=0
***OTHER Settings***
Profile= HID
CfgChar= $
SniffEna=0
LowPower=0
TX Power=ffe8
IOPorts= 0
IOValues=0
Sleeptmr=0
DebugMod=0
RoleSwch=0
command mode
------------
To enter command mode disconnect the module from host and type '$$$'.(you will see 'CMD')
To exit type '---'.(you will see 'END')
Serial line: 115200bps, 8bit, 1-stopbit, non-parity, no flow control
SSP: 115200bps, 8bit, 1-stopbit, non-parity, no flow control(via Bluetooth)
S-,tmkBT // Device name
SH,0038 // HID register
SC,0000 // COD: 000005C0 (see HID spec/Bluegiga doc)
SD,05C0 // bit 12-8 7 6 5-0
// 00101 1 1 0
// peripheral pointing keybaord joystick, gamepad, ...
S~,6 // HID profile
SS,keyboard/mouse // service name
SM,6 // Pairing mode: auto connect
SM,4 // Master mode: Connection can be controled with GPIO6
SY,FEE8 // lower power -20dbM
HID profile
-----------
S~,6 HID profile
S~,0 SPP profile
R,1 reboot
Apple iOS
---------
Cannot supported without apple iAP authentication chip.
HID flag register
-----------------
SH,0200
GH
10 0000 0000(0200) default
00 0011 1000(0038) Combo
|| | | |\_\____ number of paired devices to which the module can reconnect
|| | | \_______ send out reports over UART (0xFF <len> <data>)
|| \__\_________ descriptor type
|\______________ toggle virtual keyboard on iOS when first connected
\_______________ Force HID mode if GPIO11 is high on power-up
Descriptor type:
0000: keybaord
0001: Game Pad
0010: Mouse
0011: Combo
0100: Joystick
1xxx: reserved
Out report - Indicator
----------------------
0xFE 0x02 0x01 <LED_state>
LED Status
----------
Configuring 10 times per sec
Startup/configuration timer 2 times per sec
Discoverable/Inquiring/Idle once per sec
Connected solid on
Pairing
-------
First, host initiates pairing process and once it is done, auto connect will work thereafter.
SM,3 Master mode
SM,4 Auto Connect DTR Mode uses GPIO6 to make and break connection(Mode =DTR)
confirm: auto connect works and control connection with GPIO6
SM,5 Auto Connect ANY Mode (Mode =ANY)
each time GPIO is set, make inquiry and connect to the first found device
SM,6 automatically reconnect(Mode =Pair)
confirm: auto connect works well but difficult to enter command mode.
Fast data mode
--------------
The module enters fast data mode after 'remote configuration timer' window is passed from power on.
In this mode the module does not accept '$$$' to enter command mode.
Power Management
----------------
Inquiry and Page window Idle or Active (3.1.1)
Downside: delay in discovery or connection time
SI, // set inquiry scan window(discovery) on/off duty?
SJ, // set page scan window(connection)
This reduces averaege power >20mA to 5mA(3mA in Sniff mode)
Sniff mode Transmit
Sniff mode is disabled by default and radio is active continuously when connected.(25-30mA)
In Sniff mode the radio wakes up intermittently and sleeps in very low power mode.(2mA)
SW,<val> // set interval timer(*0.625ms) 0000-7FFF
Deep sleep Idle (3.1.2)
In this mode the module shuts down completly and only draws about 300uA. To enable this set the most signifant bit(0x8000) of Sniff interaval timer.
SW,8320 // deep sleep enable(interval=0x320*0.625ms)
In normal sleep the firmware is still running in idle mode, and wakes up about 20 times per second to check ports, update LEDs, etc. During deep sleep, the firmware actually stops runnig some tasks and the LEDs only update about once per second.
To wake from deep sleep there are three ways: (in worst case wake up takes 5ms)
*send a charactor to the UART(first charactor will be lost)
*toggle CTS low to high and wait 5ms
*wake automatically every slot time(<val>*0.625ms)
Once the radio is awake it stay active for exactly 1 second of inactivity and then sleeps again.
Downside: latency and data loss
Disable Output driver Idle or Active (3.1.3)
S%,1000 // set all GPIO pins(0-11) to inputs.
Lower Transmit Power Idle or Active (3.1.4)
SY,<hex> // transmit power setting(takes effect after a power cycle and reboot)
Downside: reducing effective range
Optimizig for Latency
---------------------
By default the firmware is optimized for throughput.
SQ,16 // set latency bit
SQ,0 // unset latency bit
Configuration timer settings
----------------------------
Remote configuration is used for the module to be configured with various commands over Bluetooth(SPP profile only?).
The module has remote configuration timer to allow remote configuration over Bluetooth after power up in Slave mode. In Master modes the remote configuration timer is set to 0(no remote configuration). (In Trigger Master mode the timer is used as an idle timer to break the connection after time expires with no charactors receive.)
ST,0 // no remote, no local when connected
ST,<1-252> // local and remote with timeout in seconds from power up
ST,253 // local only without timeout
ST,254 // remote only without timeout
ST,255 // local and remote without timeout
Android
-------
3.7.1.5 Note: To connect with Android phone the modules must wake up 11ms every 2.5seconds.
Commands
--------
SC,
SM,<val>
SD,
SP,<string> Pin code(alpahnumeric)
SQ,<mask> Special configuration(GPIO, discovery mode, low latency, reboot, UART)
SR,<hex> Store remote address
SR,Z Erase all address
SS,<string> Set service name(1-20)**
ST,<val> Remote configuration timer(Master:0, Slave:0-255, Triger:as idle timer)
SU,<val> UART baud rate
SW,<val> low-power sniff mode** deep sleep and wake up every 625us * <val>
SX,<0|1> bonding enable only acceps device that matches the stored address
SY,<hex> power setting**
SZ,<val> non-standard raw baud rate <val>=baud*0.004096
S~,<val> Profile 0:SPP, 5:APL, 6:HID
S-,<string> Device name -15 alphanumeric charactors
S?,<0|1> role switch enable
S$,<char> command mode char
$|,<hex> low-power connect mode deep sleep/active(discoverable and connectable) cycle
D display basic setting
E display extended setting
GB display the device's Bluetooth address
GF display Bluetooth address of connected device
GK show connection status
GR show remote address for reconnecting
G& show GPIO pin
G<char> show stored setting
+ toggle local echo on/off
& show GPIO 3,4,6,7(DIP switch)
C connect to stored remote address
C,<address> connect last address
CFI connect and go into fast data mode
CFR connect and go into fast data mode
CT,<address>,<val> connect to the address and disconnect after val?
F,1 fast data mod:
H display help
I,<time>,<cod> inquiry scan with <cod>
IN
IQ scan
IS inquiry scan with 001F00
J hide pin code
K, kill disconnects current connection
L link quality
M show modem signlal status
O display other settings
P,<car> pass through?
Q quiet mode make the module not discoverable
Q,0 discoverable and connectable
Q,1 not discoverable and not connectable
Q,2 not discoverable and connectable
Q,? display current quiet mode
R,1 reboot
T,<0|1> pass received data while in command mode
U,<baud>,<parity> change UART setting tentatively
V display firmware version
W wake from quiet mode enable discovery and connection
Z deep sleep mode(<2mA)

View File

@ -52,12 +52,14 @@ TARGET_DIR = .
SRC += keymap_common.c \
matrix.c \
led.c \
serial_uart.c \
rn42.c \
main.c
ifdef KEYMAP
SRC := keymap_$(KEYMAP).c $(SRC)
else
SRC := keymap_hhkb.c $(SRC)
SRC := keymap_hasu.c $(SRC)
endif
CONFIG_H = config.h
@ -123,14 +125,15 @@ MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = yes # Console for debug
COMMAND_ENABLE = yes # Commands for debug and configuration
NKRO_ENABLE = yes # USB Nkey Rollover
KEYMAP_SECTION_ENABLE = yes # fixed address keymap for keymap editor
#NKRO_ENABLE = yes # USB Nkey Rollover
#KEYMAP_SECTION_ENABLE = yes # fixed address keymap for keymap editor
# Search Path
VPATH += $(TARGET_DIR)
VPATH += $(TOP_DIR)
include $(TOP_DIR)/protocol.mk
include $(TOP_DIR)/protocol/lufa.mk
include $(TOP_DIR)/common.mk
include $(TOP_DIR)/rules.mk

View File

@ -47,6 +47,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BOOTMAGIC_KEY_SALT KC_FN6
/*
* Hardware Serial(UART)
* Baud rate are calculated with round off(+0.5).
*/
#ifdef __AVR_ATmega32U4__
/* iom32u4.h has no definition of UCSR1D. copy from iom32u2.h */
#define UCSR1D _SFR_MEM8(0xCB)
#define RTSEN 0
#define CTSEN 1
#define SERIAL_UART_BAUD 115200
#define SERIAL_UART_DATA UDR1
#define SERIAL_UART_UBRR ((F_CPU/(16.0*SERIAL_UART_BAUD)-1+0.5))
#define SERIAL_UART_RXD_VECT USART1_RX_vect
#define SERIAL_UART_TXD_READY (UCSR1A&(1<<UDRE1))
#define SERIAL_UART_INIT() do { \
UBRR1L = (uint8_t) SERIAL_UART_UBRR; /* baud rate */ \
UBRR1H = ((uint16_t)SERIAL_UART_UBRR>>8); /* baud rate */ \
UCSR1B |= (1<<RXCIE1) | (1<<RXEN1); /* RX interrupt, RX: enable */ \
UCSR1B |= (0<<TXCIE1) | (1<<TXEN1); /* TX interrupt, TX: enable */ \
UCSR1C |= (0<<UPM11) | (0<<UPM10); /* parity: none(00), even(01), odd(11) */ \
UCSR1D |= (0<<RTSEN) | (0<<CTSEN); /* RTS, CTS */ \
sei(); \
} while(0)
#else
#error "USART configuration is needed."
#endif
/*
* Feature disable options
* These options are also useful to firmware size reduction.

View File

@ -4,8 +4,18 @@
#include "lufa.h"
#include "print.h"
#include "sendchar.h"
#include "rn42.h"
#include "serial.h"
#include "keyboard.h"
#include "command.h"
#include "keycode.h"
#include "action.h"
#include "action_util.h"
#include "wait.h"
bool config_mode = false;
static void SetupHardware(void)
{
/* Disable watchdog if enabled by bootloader/fuses */
@ -25,14 +35,17 @@ static void SetupHardware(void)
print_set_sendchar(sendchar);
}
static bool force_usb = false;
int main(void) __attribute__ ((weak));
int main(void)
{
SetupHardware();
sei();
/* wait for USB startup & debug output */
while (USB_DeviceState != DEVICE_STATE_Configured) {
/* wait for USB startup to get ready for debug output */
uint8_t timeout = 255; // timeout when USB is not available(Bluetooth)
while (timeout-- && USB_DeviceState != DEVICE_STATE_Configured) {
wait_ms(4);
#if defined(INTERRUPT_CONTROL_ENDPOINT)
;
#else
@ -41,27 +54,240 @@ int main(void)
}
print("USB configured.\n");
rn42_init();
print("RN-42 init\n");
/* init modules */
keyboard_init();
host_set_driver(&lufa_driver);
if (rn42_ready()) {
host_set_driver(&rn42_driver);
} else {
host_set_driver(&lufa_driver);
}
#ifdef SLEEP_LED_ENABLE
sleep_led_init();
#endif
print("Keyboard start.\n");
while (1) {
/*
while (USB_DeviceState == DEVICE_STATE_Suspended) {
suspend_power_down();
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
USB_Device_SendRemoteWakeup();
}
}
*/
keyboard_task();
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
USB_USBTask();
#endif
int16_t c;
if (config_mode) {
while ((c = serial_recv2()) != -1) xprintf("%c", c);
} else {
while ((c = serial_recv2()) != -1) {
// LED Out report: 0xFE, 0x02, 0x01, <leds>
// To get the report over UART set bit3 with SH, command.
static enum {LED_INIT, LED_FE, LED_02, LED_01} state = LED_INIT;
xprintf("%X\n", c);
switch (state) {
case LED_INIT:
if (c == 0xFE) state = LED_FE;
else state = LED_INIT;
break;
case LED_FE:
if (c == 0x02) state = LED_02;
else state = LED_INIT;
break;
case LED_02:
if (c == 0x01) state = LED_01;
else state = LED_INIT;
break;
case LED_01:
// TODO: move to rn42.c and make accessible with keyboard_leds()
xprintf("LED status: %X\n", c);
state = LED_INIT;
break;
default:
state = LED_INIT;
}
}
}
/* Bluetooth mode when ready */
if (!force_usb) {
if (rn42_ready() && host_get_driver() != &rn42_driver) {
clear_keyboard();
host_set_driver(&rn42_driver);
} else if (!rn42_ready() && host_get_driver() != &lufa_driver) {
clear_keyboard();
host_set_driver(&lufa_driver);
}
}
}
}
static bool local_echo = false;
bool command_extra(uint8_t code)
{
static host_driver_t *prev_driver = &rn42_driver;
switch (code) {
case KC_H:
case KC_SLASH: /* ? */
print("\n\n----- Bluetooth RN-42 Help -----\n");
print("w: toggle RN-42 config mode(enter/exit)\n");
print("l: toggle print module output(local echo)\n");
print("a: Bluetooth auto connect\n");
print("del: Bluetooth disconnect\n");
print("i: info\n");
if (config_mode) {
return true;
} else {
print("u: force USB mode\n");
return false; // to display default command help
}
case KC_W:
if (!config_mode) {
print("\nEnter RN-42 config mode\n");
print("type $$$ to enter RN-42 command mode\n");
print("type Delete to disconnect Bluetooth connection\n");
command_state = CONSOLE;
config_mode = true;
prev_driver = host_get_driver();
clear_keyboard();
host_set_driver(&rn42_config_driver);
} else {
print("\nExit RN-42 config mode\n");
command_state = ONESHOT;
config_mode = false;
clear_keyboard();
host_set_driver(prev_driver);
}
return true;
case KC_L:
if (local_echo) {
print("local echo off\n");
local_echo = false;
} else {
print("local echo on\n");
local_echo = true;
}
return true;
case KC_U:
if (config_mode) return false;
if (force_usb) {
print("Auto mode\n");
force_usb = false;
} else {
print("USB mode\n");
force_usb = true;
clear_keyboard();
host_set_driver(&lufa_driver);
}
return true;
case KC_A:
print("auto connect\n");
rn42_autoconnect();
return true;
case KC_DELETE:
print("disconnect\n");
rn42_disconnect();
//rn42_putc('\0'); // see 5.3.4.4 DISCONNECT KEY of User's Guide
return true;
case KC_I:
print("\nRN-42 info\n");
xprintf("protocol: %s\n", (host_get_driver() == &rn42_driver) ? "RN-42" : "LUFA");
xprintf("force_usb: %X\n", force_usb);
xprintf("rn42_ready(): %X\n", rn42_ready());
xprintf("config_mode: %X\n", config_mode);
return true;
default:
if (config_mode)
return true;
else
return false; // exec default command
}
return true;
}
static uint8_t code2asc(uint8_t code);
bool command_console_extra(uint8_t code)
{
switch (code) {
default:
rn42_putc(code2asc(code));
if (local_echo) xprintf("%c", code2asc(code));
return true;
}
return false;
}
// convert keycode into ascii charactor
static uint8_t code2asc(uint8_t code)
{
bool shifted = (get_mods() & (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT))) ? true : false;
switch (code) {
case KC_A: return (shifted ? 'A' : 'a');
case KC_B: return (shifted ? 'B' : 'b');
case KC_C: return (shifted ? 'C' : 'c');
case KC_D: return (shifted ? 'D' : 'd');
case KC_E: return (shifted ? 'E' : 'e');
case KC_F: return (shifted ? 'F' : 'f');
case KC_G: return (shifted ? 'G' : 'g');
case KC_H: return (shifted ? 'H' : 'h');
case KC_I: return (shifted ? 'I' : 'i');
case KC_J: return (shifted ? 'J' : 'j');
case KC_K: return (shifted ? 'K' : 'k');
case KC_L: return (shifted ? 'L' : 'l');
case KC_M: return (shifted ? 'M' : 'm');
case KC_N: return (shifted ? 'N' : 'n');
case KC_O: return (shifted ? 'O' : 'o');
case KC_P: return (shifted ? 'P' : 'p');
case KC_Q: return (shifted ? 'Q' : 'q');
case KC_R: return (shifted ? 'R' : 'r');
case KC_S: return (shifted ? 'S' : 's');
case KC_T: return (shifted ? 'T' : 't');
case KC_U: return (shifted ? 'U' : 'u');
case KC_V: return (shifted ? 'V' : 'v');
case KC_W: return (shifted ? 'W' : 'w');
case KC_X: return (shifted ? 'X' : 'x');
case KC_Y: return (shifted ? 'Y' : 'y');
case KC_Z: return (shifted ? 'Z' : 'z');
case KC_1: return (shifted ? '!' : '1');
case KC_2: return (shifted ? '@' : '2');
case KC_3: return (shifted ? '#' : '3');
case KC_4: return (shifted ? '$' : '4');
case KC_5: return (shifted ? '%' : '5');
case KC_6: return (shifted ? '^' : '6');
case KC_7: return (shifted ? '&' : '7');
case KC_8: return (shifted ? '*' : '8');
case KC_9: return (shifted ? '(' : '9');
case KC_0: return (shifted ? ')' : '0');
case KC_ENTER: return '\n';
case KC_ESCAPE: return 0x1B;
case KC_BSPACE: return '\b';
case KC_TAB: return '\t';
case KC_SPACE: return ' ';
case KC_MINUS: return (shifted ? '_' : '-');
case KC_EQUAL: return (shifted ? '+' : '=');
case KC_LBRACKET: return (shifted ? '{' : '[');
case KC_RBRACKET: return (shifted ? '}' : ']');
case KC_BSLASH: return (shifted ? '|' : '\\');
case KC_NONUS_HASH: return (shifted ? '|' : '\\');
case KC_SCOLON: return (shifted ? ':' : ';');
case KC_QUOTE: return (shifted ? '"' : '\'');
case KC_GRAVE: return (shifted ? '~' : '`');
case KC_COMMA: return (shifted ? '<' : ',');
case KC_DOT: return (shifted ? '>' : '.');
case KC_SLASH: return (shifted ? '?' : '/');
case KC_DELETE: return '\0'; // Delete to disconnect
default: return ' ';
}
}

View File

@ -1,6 +1,9 @@
#include "host.h"
#include "host_driver.h"
#include "serial.h"
#include "rn42.h"
#include "print.h"
#include "wait.h"
/* Host driver */
@ -18,20 +21,146 @@ host_driver_t rn42_driver = {
send_consumer
};
void rn42_init(void)
{
// PF1: check RTS(active low)
DDRF &= ~(1<<1);
PORTF &= ~(1<<1);
// PF7: BT connection control(HiZ: connect, low: disconnect)
// JTAG disable for PORT F. write JTD bit twice within four cycles.
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
rn42_autoconnect();
// PD5: CTS (low: allow to send, high:not allowed)
DDRD |= (1<<5);
PORTD &= ~(1<<5);
serial_init();
}
void rn42_putc(uint8_t c)
{
serial_send(c);
}
void rn42_autoconnect(void)
{
DDRF &= ~(1<<7);
PORTF &= ~(1<<7);
}
void rn42_disconnect(void)
{
DDRF |= (1<<7);
PORTF &= ~(1<<7);
}
bool rn42_ready(void)
{
// RTS low
return PINF&(1<<1) ? false : true;
}
static uint8_t keyboard_leds(void) { return 0; }
static void send_keyboard(report_keyboard_t *report)
{
// wake from deep sleep
PORTD |= (1<<5); // high
wait_ms(5);
PORTD &= ~(1<<5); // low
serial_send(0xFD); // Raw report mode
serial_send(9); // length
serial_send(1); // descriptor type
serial_send(report->mods);
serial_send(0x00);
serial_send(report->keys[0]);
serial_send(report->keys[1]);
serial_send(report->keys[2]);
serial_send(report->keys[3]);
serial_send(report->keys[4]);
serial_send(report->keys[5]);
}
static void send_mouse(report_mouse_t *report)
{
// wake from deep sleep
PORTD |= (1<<5); // high
wait_ms(5);
PORTD &= ~(1<<5); // low
serial_send(0xFD); // Raw report mode
serial_send(5); // length
serial_send(2); // descriptor type
serial_send(report->buttons);
serial_send(report->x);
serial_send(report->y);
serial_send(report->v);
}
static void send_system(uint16_t data)
{
// Table 5-6 of RN-BT-DATA-UB
// 81,82,83 scan codes can be used?
}
static uint16_t usage2bits(uint16_t usage)
{
switch (usage) {
case AC_HOME: return 0x01;
case AL_EMAIL: return 0x02;
case AC_SEARCH: return 0x04;
//case AL_KBD_LAYOUT: return 0x08; // Apple virtual keybaord toggle
case AUDIO_VOL_UP: return 0x10;
case AUDIO_VOL_DOWN: return 0x20;
case AUDIO_MUTE: return 0x40;
case TRANSPORT_PLAY_PAUSE: return 0x80;
case TRANSPORT_NEXT_TRACK: return 0x100;
case TRANSPORT_PREV_TRACK: return 0x200;
case TRANSPORT_STOP: return 0x400;
case TRANSPORT_STOP_EJECT: return 0x800;
//case return 0x1000; // Fast forward
//case return 0x2000; // Rewind
//case return 0x4000; // Stop/eject
//case return 0x8000; // Internet browser
};
return 0;
}
static void send_consumer(uint16_t data)
{
uint16_t bits = usage2bits(data);
serial_send(0xFD); // Raw report mode
serial_send(3); // length
serial_send(3); // descriptor type
serial_send((bits>>8)&0xFF);
serial_send(bits&0xFF);
}
/* Null driver for config_mode */
static uint8_t config_keyboard_leds(void);
static void config_send_keyboard(report_keyboard_t *report);
static void config_send_mouse(report_mouse_t *report);
static void config_send_system(uint16_t data);
static void config_send_consumer(uint16_t data);
host_driver_t rn42_config_driver = {
config_keyboard_leds,
config_send_keyboard,
config_send_mouse,
config_send_system,
config_send_consumer
};
static uint8_t config_keyboard_leds(void) { return 0; }
static void config_send_keyboard(report_keyboard_t *report) {}
static void config_send_mouse(report_mouse_t *report) {}
static void config_send_system(uint16_t data) {}
static void config_send_consumer(uint16_t data) {}

View File

@ -1,6 +1,15 @@
#ifndef RN42_H
#define RN42_H
#include <stdbool.h>
host_driver_t rn42_driver;
host_driver_t rn42_config_driver;
void rn42_init(void);
void rn42_putc(uint8_t c);
void rn42_autoconnect(void);
void rn42_disconnect(void);
bool rn42_ready(void);
#endif