Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
f9bf50cf
Commit
f9bf50cf
authored
Sep 29, 2016
by
Johny Mattsson
Browse files
Simple BlueTooth module for BT LE advertisements.
parent
8e441b59
Changes
4
Show whitespace changes
Inline
Side-by-side
components/modules/Kconfig
View file @
f9bf50cf
...
...
@@ -19,4 +19,10 @@ config LUA_MODULE_ENCODER
Includes the encoder module. This provides hex and base64 encoding and
decoding functionality.
config LUA_MODULE_BTHCI
bool "BlueTooth HCI interface module"
default "n"
help
Includes the simple BlueTooth HCI module.
endmenu
components/modules/bthci.c
0 → 100644
View file @
f9bf50cf
/*
* Copyright 2016 DiUS Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#include "module.h"
#include "lauxlib.h"
#include "task/task.h"
#include "platform.h"
#include "bt.h"
#include <stdlib.h>
#include <string.h>
#define BD_ADDR_LEN 6
typedef
uint8_t
bd_addr_t
[
BD_ADDR_LEN
];
#define st(x) do { x } while (0)
#define STREAM_U8(p, v) st( *(p)++ = (uint8_t)(v); )
#define STREAM_U16(p, v) \
st( \
*(p)++ = (uint8_t)(v); \
*(p)++ = (uint8_t)((v) >> 8); \
)
#define STREAM_BD_ADDR(p, addr) \
st( \
for (int i = 0; i < BD_ADDR_LEN; ++i) \
*(p)++ = (uint8_t)addr[BD_ADDR_LEN - 1 - i];\
)
#define STREAM_ARRAY(p, arr, len) \
st( \
for (int i = 0; i < len; ++i) \
*(p)++ = (uint8_t)arr[i]; \
)
enum
{
H4_TYPE_COMMAND
=
1
,
H4_TYPE_ACL
=
2
,
H4_TYPE_SCO
=
3
,
H4_TYPE_EVENT
=
4
};
#define EVENT_COMMAND_COMPLETE 0x0e
#define EVENT_LE_META 0x3e
// Subevent codes for LE-Meta
#define EVENT_LE_META_ADV_REPORT 0x02
#define ERROR_UNSPECIFIED 0x1f
#define HCI_OGF(x) (x << 10)
#define HCI_GRP_HOST_CONT_BASEBAND_CMDS HCI_OGF(0x03)
#define HCI_GRP_BLE_CMDS HCI_OGF(0x08)
#define HCI_RESET (HCI_GRP_HOST_CONT_BASEBAND_CMDS | 0x0003)
#define HCI_SET_EVENT_MASK (HCI_GRP_HOST_CONT_BASEBAND_CMDS | 0x0001)
#define HCI_BLE_ADV_WRI_PARAMS (HCI_GRP_BLE_CMDS | 0x0006)
#define HCI_BLE_ADV_WRI_DATA (HCI_GRP_BLE_CMDS | 0x0008)
#define HCI_BLE_ADV_WRI_ENABLE (HCI_GRP_BLE_CMDS | 0x000a)
#define HCI_BLE_SET_SCAN_PARAMS (HCI_GRP_BLE_CMDS | 0x000b)
#define HCI_BLE_SET_SCAN_ENABLE (HCI_GRP_BLE_CMDS | 0x000c)
#define SZ_HDR 4
#define SZ_HCI_RESET 4
#define SZ_HCI_BLE_ADV_WRI_PARAMS 19
#define SZ_HCI_BLE_ADV_WRI_DATA 36
#define SZ_HCI_BLE_ADV_WRI_ENABLE 5
#define SZ_HCI_BLE_SET_SCAN_ENABLE 6
#define SZ_HCI_BLE_SET_SCAN_PARAMS 11
#define ADV_MIN_INTERVAL 0x00a0
#define ADV_MAX_DATA 31
enum
{
ADV_IND
=
0x00
,
ADV_DIRECT_IND_HI_DUTY
=
0x01
,
ADV_SCAN_IND
=
0x02
,
ADV_NONCONN_IND
=
0x03
,
ADV_DIRECT_IND_LO_DUTY
=
0x04
};
enum
{
ADV_OWN_ADDR_PUB
=
0x00
,
ADV_OWN_ADDR_RAND
=
0x01
,
ADV_OWN_ADDR_PRIV_OR_PUB
=
0x02
,
ADV_OWN_ADDR_PRIV_OR_RAND
=
0x03
};
enum
{
ADV_PEER_ADDR_PUB
=
0x00
,
ADV_PEER_ADDR_RAND
=
0x01
};
#define ADV_CHAN_37 0x01
#define ADV_CHAN_38 0x02
#define ADV_CHAN_39 0x03
#define ADV_CHAN_ALL (ADV_CHAN_37 | ADV_CHAN_38 | ADV_CHAN_39)
#define ADV_FILTER_NONE 0x00
#define ADV_FILTER_SCAN_WHITELIST 0x01
#define ADV_FILTER_CONN_WHITELIST 0x02
#define ADV_FILTER_SCAN_CONN_WHITELIST \
(ADV_FILTER_SCAN_WHITELIST | ADV_FILTER_CONN_WHITELIST)
#define get_opt_field_int(idx, var, name) \
st( \
lua_getfield (L, idx, name); \
if (!lua_isnil (L, -1)) \
var = lua_tointeger (L, -1); \
)
#define MAX_CMD_Q 5
// --- Local state --------------------------------------------
static
struct
cmd_list
{
uint16_t
cmd
;
int
cb_ref
;
}
__attribute__
((
packed
))
cmd_q
[
MAX_CMD_Q
];
static
task_handle_t
hci_event_task_handle
;
static
int
adv_rep_cb_ref
=
LUA_NOREF
;
// --- VHCI callbacks ------------------------------------------
static
void
on_bthci_can_send
(
void
)
{
// Unused, we don't support queuing up commands on this level
}
static
int
on_bthci_receive
(
uint8_t
*
data
,
uint16_t
len
)
{
#if 0
printf ("BT:");
for (int i = 0; i < len; ++i)
printf (" %02x", data[i]);
printf ("\n");
#endif
if
(
data
[
0
]
==
H4_TYPE_EVENT
)
{
unsigned
len
=
data
[
2
];
char
*
copy
=
malloc
(
SZ_HDR
+
len
);
if
(
copy
)
memcpy
(
copy
,
data
,
SZ_HDR
+
len
);
if
(
!
copy
||
!
task_post_high
(
hci_event_task_handle
,
(
task_param_t
)
copy
))
{
NODE_ERR
(
"Dropped BT event due to no mem!"
);
free
(
copy
);
return
0
;
}
}
return
0
;
}
static
const
vhci_host_callback_t
bthci_callbacks
=
{
on_bthci_can_send
,
on_bthci_receive
};
// --- Helper functions ---------------------------------
// Expects callback function at top of stack
static
int
send_hci_command
(
lua_State
*
L
,
uint8_t
*
data
,
unsigned
len
)
{
if
(
API_vhci_host_check_send_available
())
{
uint16_t
cmd
=
(((
uint16_t
)
data
[
2
])
<<
8
)
|
data
[
1
];
for
(
int
i
=
0
;
i
<
MAX_CMD_Q
;
++
i
)
{
if
(
cmd_q
[
i
].
cb_ref
==
LUA_NOREF
)
{
if
(
lua_gettop
(
L
)
>
0
&&
!
lua_isnil
(
L
,
-
1
))
{
cmd_q
[
i
].
cmd
=
cmd
;
luaL_checkanyfunction
(
L
,
-
1
);
lua_pushvalue
(
L
,
-
1
);
cmd_q
[
i
].
cb_ref
=
luaL_ref
(
L
,
LUA_REGISTRYINDEX
);
}
API_vhci_host_send_packet
(
data
,
len
);
return
0
;
}
}
}
// Nope, couldn't send this command!
lua_pushinteger
(
L
,
ERROR_UNSPECIFIED
);
lua_pushlstring
(
L
,
NULL
,
0
);
lua_call
(
L
,
2
,
0
);
return
0
;
}
static
void
enable_le_meta_events
(
void
)
{
uint8_t
buf
[
SZ_HDR
+
8
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_SET_EVENT_MASK
);
STREAM_U8
(
p
,
sizeof
(
buf
)
-
SZ_HDR
);
STREAM_U8
(
p
,
0xff
);
STREAM_U8
(
p
,
0xff
);
STREAM_U8
(
p
,
0xff
);
STREAM_U8
(
p
,
0xff
);
STREAM_U8
(
p
,
0xff
);
STREAM_U8
(
p
,
0x1f
);
STREAM_U8
(
p
,
0x00
);
STREAM_U8
(
p
,
0x20
);
// LE Meta-Event
API_vhci_host_send_packet
(
buf
,
sizeof
(
buf
));
}
// --- Lua context handlers ------------------------------------
static
void
invoke_cmd_q_callback
(
lua_State
*
L
,
unsigned
idx
,
uint8_t
code
,
unsigned
len
,
const
uint8_t
*
data
)
{
if
(
cmd_q
[
idx
].
cb_ref
!=
LUA_NOREF
)
{
lua_rawgeti
(
L
,
LUA_REGISTRYINDEX
,
cmd_q
[
idx
].
cb_ref
);
luaL_unref
(
L
,
LUA_REGISTRYINDEX
,
cmd_q
[
idx
].
cb_ref
);
cmd_q
[
idx
].
cb_ref
=
LUA_NOREF
;
if
(
code
)
// non-zero response code?
lua_pushinteger
(
L
,
code
);
else
lua_pushnil
(
L
);
// no error
lua_pushlstring
(
L
,
(
const
char
*
)
data
,
len
);
// extra bytes, if any
lua_call
(
L
,
2
,
0
);
}
}
static
void
handle_hci_event
(
task_param_t
arg
,
task_prio_t
prio
)
{
(
void
)
prio
;
lua_State
*
L
=
lua_getstate
();
uint8_t
*
hci_event
=
(
uint8_t
*
)
arg
;
unsigned
type
=
hci_event
[
1
];
unsigned
len
=
hci_event
[
2
];
if
(
type
==
EVENT_COMMAND_COMPLETE
)
{
uint16_t
cmd
=
(((
uint16_t
)
hci_event
[
5
])
<<
8
)
|
hci_event
[
4
];
for
(
int
i
=
0
;
i
<
MAX_CMD_Q
;
++
i
)
{
if
(
cmd_q
[
i
].
cb_ref
!=
LUA_NOREF
&&
cmd_q
[
i
].
cmd
==
cmd
)
{
invoke_cmd_q_callback
(
L
,
i
,
hci_event
[
6
],
len
-
4
,
&
hci_event
[
7
]);
break
;
}
}
if
(
cmd
==
HCI_RESET
)
// clear cmd_q to prevent leaking slots
{
for
(
int
i
=
0
;
i
<
MAX_CMD_Q
;
++
i
)
invoke_cmd_q_callback
(
L
,
i
,
ERROR_UNSPECIFIED
,
0
,
NULL
);
enable_le_meta_events
();
// renenable le events
}
}
else
if
(
type
==
EVENT_LE_META
)
{
unsigned
subtype
=
hci_event
[
3
];
if
(
subtype
==
EVENT_LE_META_ADV_REPORT
)
{
unsigned
num_reps
=
hci_event
[
4
];
// The encoding of multiple reports is not clear in spec, and I've never
// seen a multiple-report event, so for now we only handle single reports
if
(
adv_rep_cb_ref
!=
LUA_NOREF
&&
num_reps
==
1
)
{
uint8_t
*
report
=
&
hci_event
[
5
];
lua_rawgeti
(
L
,
LUA_REGISTRYINDEX
,
adv_rep_cb_ref
);
lua_pushlstring
(
L
,
(
const
char
*
)
report
,
len
-
2
);
lua_call
(
L
,
1
,
0
);
}
}
}
free
(
hci_event
);
}
// --- Lua functions ------------------------------------
static
int
lbthci_init
(
lua_State
*
L
)
{
hci_event_task_handle
=
task_get_id
(
handle_hci_event
);
for
(
int
i
=
0
;
i
<
MAX_CMD_Q
;
++
i
)
cmd_q
[
i
].
cb_ref
=
LUA_NOREF
;
bt_controller_init
();
API_vhci_host_register_callback
(
&
bthci_callbacks
);
enable_le_meta_events
();
return
0
;
}
static
int
lbthci_reset
(
lua_State
*
L
)
{
uint8_t
buf
[
SZ_HCI_RESET
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_RESET
);
STREAM_U8
(
p
,
0
);
lua_settop
(
L
,
1
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_adv_enable
(
lua_State
*
L
)
{
bool
onoff
=
lua_tointeger
(
L
,
1
)
>
0
;
uint8_t
buf
[
SZ_HCI_BLE_ADV_WRI_ENABLE
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_BLE_ADV_WRI_ENABLE
);
STREAM_U8
(
p
,
1
);
STREAM_U8
(
p
,
onoff
);
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_adv_setdata
(
lua_State
*
L
)
{
size_t
len
;
const
char
*
data
=
luaL_checklstring
(
L
,
1
,
&
len
);
if
(
len
>
ADV_MAX_DATA
)
len
=
ADV_MAX_DATA
;
uint8_t
buf
[
SZ_HCI_BLE_ADV_WRI_DATA
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_BLE_ADV_WRI_DATA
);
STREAM_U8
(
p
,
sizeof
(
buf
)
-
SZ_HDR
);
STREAM_U8
(
p
,
len
);
STREAM_ARRAY
(
p
,
data
,
len
);
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_adv_setparams
(
lua_State
*
L
)
{
uint16_t
adv_interval_min
=
0x0400
;
// 0.64s
uint16_t
adv_interval_max
=
0x0800
;
// 1.28s
uint8_t
adv_type
=
ADV_IND
;
uint8_t
own_addr_type
=
ADV_OWN_ADDR_PUB
;
uint8_t
peer_addr_type
=
ADV_PEER_ADDR_PUB
;
bd_addr_t
peer_addr
;
uint8_t
adv_chan_map
=
ADV_CHAN_ALL
;
uint8_t
adv_filter_pol
=
ADV_FILTER_NONE
;
luaL_checkanytable
(
L
,
1
);
lua_settop
(
L
,
2
);
// Pad a nil into the function slot if necessary
get_opt_field_int
(
1
,
adv_interval_min
,
"interval_min"
);
get_opt_field_int
(
1
,
adv_interval_max
,
"interval_max"
);
get_opt_field_int
(
1
,
adv_type
,
"type"
);
get_opt_field_int
(
1
,
own_addr_type
,
"own_addr_type"
);
get_opt_field_int
(
1
,
peer_addr_type
,
"peer_addr_type"
);
// TODO: peer addr
get_opt_field_int
(
1
,
adv_chan_map
,
"channel_map"
);
get_opt_field_int
(
1
,
adv_filter_pol
,
"filter_policy"
);
if
(
adv_type
==
ADV_SCAN_IND
||
adv_type
==
ADV_NONCONN_IND
)
{
if
(
adv_interval_min
<
ADV_MIN_INTERVAL
)
adv_interval_min
=
ADV_MIN_INTERVAL
;
if
(
adv_interval_max
<
ADV_MIN_INTERVAL
)
adv_interval_max
=
ADV_MIN_INTERVAL
;
}
else
if
(
adv_type
==
ADV_DIRECT_IND_HI_DUTY
||
adv_type
==
ADV_DIRECT_IND_LO_DUTY
)
{
// TODO: enforce peer addr validity
}
if
(
adv_chan_map
==
0
||
adv_chan_map
>
ADV_CHAN_ALL
)
adv_chan_map
=
ADV_CHAN_ALL
;
if
(
adv_filter_pol
>
ADV_FILTER_SCAN_CONN_WHITELIST
)
adv_filter_pol
=
ADV_FILTER_SCAN_CONN_WHITELIST
;
uint8_t
buf
[
SZ_HCI_BLE_ADV_WRI_PARAMS
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_BLE_ADV_WRI_PARAMS
);
STREAM_U8
(
p
,
sizeof
(
buf
)
-
SZ_HDR
);
STREAM_U16
(
p
,
adv_interval_min
);
STREAM_U16
(
p
,
adv_interval_max
);
STREAM_U8
(
p
,
adv_type
);
STREAM_U8
(
p
,
own_addr_type
);
STREAM_U8
(
p
,
peer_addr_type
);
STREAM_BD_ADDR
(
p
,
peer_addr
);
STREAM_U8
(
p
,
adv_chan_map
);
STREAM_U8
(
p
,
adv_filter_pol
);
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_scan
(
lua_State
*
L
)
{
bool
onoff
=
lua_tointeger
(
L
,
1
)
>
0
;
uint8_t
buf
[
SZ_HCI_BLE_SET_SCAN_ENABLE
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_BLE_SET_SCAN_ENABLE
);
STREAM_U8
(
p
,
sizeof
(
buf
)
-
SZ_HDR
);
STREAM_U8
(
p
,
onoff
?
0x01
:
0x00
);
STREAM_U8
(
p
,
0x00
);
// no filter duplicates
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_scan_setparams
(
lua_State
*
L
)
{
uint8_t
scan_mode
=
0
;
uint16_t
scan_interval
=
0x0010
;
uint16_t
scan_window
=
0x0010
;
uint8_t
own_addr_type
=
0
;
uint8_t
filter_policy
=
0
;
luaL_checkanytable
(
L
,
1
);
lua_settop
(
L
,
2
);
// Pad a nil into the function slot if necessary
get_opt_field_int
(
1
,
scan_mode
,
"mode"
);
get_opt_field_int
(
1
,
scan_interval
,
"interval"
);
get_opt_field_int
(
1
,
scan_window
,
"window"
);
get_opt_field_int
(
1
,
own_addr_type
,
"own_addr_type"
);
get_opt_field_int
(
1
,
filter_policy
,
"filter_policy"
);
// TODO clamp ranges
uint8_t
buf
[
SZ_HCI_BLE_SET_SCAN_PARAMS
];
uint8_t
*
p
=
buf
;
STREAM_U8
(
p
,
H4_TYPE_COMMAND
);
STREAM_U16
(
p
,
HCI_BLE_SET_SCAN_PARAMS
);
STREAM_U8
(
p
,
sizeof
(
buf
)
-
SZ_HDR
);
STREAM_U8
(
p
,
scan_mode
);
STREAM_U16
(
p
,
scan_interval
);
STREAM_U16
(
p
,
scan_window
);
STREAM_U8
(
p
,
own_addr_type
);
STREAM_U8
(
p
,
filter_policy
);
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
buf
,
sizeof
(
buf
));
}
static
int
lbthci_scan_on
(
lua_State
*
L
)
{
const
char
*
on_what
=
luaL_checkstring
(
L
,
1
);
if
(
strcmp
(
on_what
,
"adv_report"
)
==
0
)
{
lua_settop
(
L
,
2
);
luaL_unref
(
L
,
LUA_REGISTRYINDEX
,
adv_rep_cb_ref
);
adv_rep_cb_ref
=
LUA_NOREF
;
if
(
!
lua_isnil
(
L
,
2
))
adv_rep_cb_ref
=
luaL_ref
(
L
,
LUA_REGISTRYINDEX
);
return
0
;
}
else
return
luaL_error
(
L
,
"unknown event '%s'"
,
on_what
);
}
static
int
lbthci_rawhci
(
lua_State
*
L
)
{
size_t
len
;
uint8_t
*
data
=
(
uint8_t
*
)
luaL_checklstring
(
L
,
1
,
&
len
);
if
(
len
<
SZ_HDR
||
data
[
0
]
!=
H4_TYPE_COMMAND
)
return
luaL_error
(
L
,
"definitely not a valid HCI command"
);
lua_settop
(
L
,
2
);
return
send_hci_command
(
L
,
data
,
len
);
}
static
const
LUA_REG_TYPE
bthci_adv_map
[]
=
{
{
LSTRKEY
(
"enable"
),
LFUNCVAL
(
lbthci_adv_enable
)
},
{
LSTRKEY
(
"setdata"
),
LFUNCVAL
(
lbthci_adv_setdata
)
},
{
LSTRKEY
(
"setparams"
),
LFUNCVAL
(
lbthci_adv_setparams
)
},
// Advertising types
{
LSTRKEY
(
"CONN_UNDIR"
),
LNUMVAL
(
ADV_IND
)
},
{
LSTRKEY
(
"CONN_DIR_HI"
),
LNUMVAL
(
ADV_DIRECT_IND_HI_DUTY
)
},
{
LSTRKEY
(
"SCAN_UNDIR"
),
LNUMVAL
(
ADV_SCAN_IND
)
},
{
LSTRKEY
(
"NONCONN_UNDIR"
),
LNUMVAL
(
ADV_NONCONN_IND
)
},
{
LSTRKEY
(
"CONN_DIR_LO"
),
LNUMVAL
(
ADV_DIRECT_IND_LO_DUTY
)
},
{
LSTRKEY
(
"CHAN_37"
),
LNUMVAL
(
ADV_CHAN_37
)
},
{
LSTRKEY
(
"CHAN_38"
),
LNUMVAL
(
ADV_CHAN_38
)
},
{
LSTRKEY
(
"CHAN_39"
),
LNUMVAL
(
ADV_CHAN_39
)
},
{
LSTRKEY
(
"CHAN_ALL"
),
LNUMVAL
(
ADV_CHAN_ALL
)
},
{
LNILKEY
,
LNILVAL
}
};
static
const
LUA_REG_TYPE
bthci_scan_map
[]
=
{
{
LSTRKEY
(
"enable"
),
LFUNCVAL
(
lbthci_scan
)
},
{
LSTRKEY
(
"setparams"
),
LFUNCVAL
(
lbthci_scan_setparams
)
},
{
LSTRKEY
(
"on"
),
LFUNCVAL
(
lbthci_scan_on
)
},
{
LNILKEY
,
LNILVAL
}
};
static
const
LUA_REG_TYPE
bthci_map
[]
=
{
{
LSTRKEY
(
"rawhci"
),
LFUNCVAL
(
lbthci_rawhci
)
},
{
LSTRKEY
(
"reset"
),
LFUNCVAL
(
lbthci_reset
)
},
{
LSTRKEY
(
"adv"
),
LROVAL
(
bthci_adv_map
)
},
{
LSTRKEY
(
"scan"
),
LROVAL
(
bthci_scan_map
)
},
{
LNILKEY
,
LNILVAL
}
};
NODEMCU_MODULE
(
BTHCI
,
"bthci"
,
bthci_map
,
lbthci_init
);
components/modules/component.mk
View file @
f9bf50cf
...
...
@@ -8,6 +8,6 @@ include $(PROJECT_PATH)/components/modules/uppercase.mk
MODULE_NAMES
:=
$(
call
uppercase,
$(
subst
.c,,
$(
wildcard
*
.c
)))
FORCE_LINK
:=
$(
foreach
mod,
$(MODULE_NAMES)
,
$(
if
$
(
CONFIG_LUA_MODULE_
$(mod)
)
,
-u
$(mod)
_module_selected1
))
COMPONENT_ADD_LDFLAGS
=
$(FORCE_LINK)
-lmodules
COMPONENT_ADD_LDFLAGS
=
$(FORCE_LINK)
-lmodules
$(
if
$(CONFIG_LUA_MODULE_BTHCI)
,-lbtdm_app
)
include
$(IDF_PATH)/make/component_common.mk
docs/en/modules/bthci.md
0 → 100644
View file @
f9bf50cf
# BT HCI Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2016-09-29 |
[
DiUS
](
https://github.com/DiUS
)
,
[
Johny Mattsson
](
https://github.com/jmattsson
)
|
[
Johny Mattsson
](
https://github.com/jmattsson
)
|
[
bthci.c
](
../../../components/modules/bthci.c
)
|
The BT HCI module provides a minimal HCI-level interface to BlueTooth
adverisements. Via this module you can set up BT LE advertisements and also
receive advertisements from other devices.
Advertisements are an easy way of publishing sensor data to e.g. a
smartphone app.
# bthci.rawhci(hcibytes, callback)
Sends a raw HCI command to the BlueTooth controller.
Only intended for development use.
#### Syntax
`bthci.rawhci(hcibytes [, callback])`
#### Parameters
-
`hcibytes`
raw HCI command bytes to send to the BlueTooth controller.
-
`callback`
optional function to be invoked when the reset completes. Its
first argument is the HCI error code, or
`nil`
on success. The second
argument contains any subsequent raw result bytes, or an empty string
if the result only contained the status code.
##### Returns
`nil`
#### Example
```
lua
-- Send a HCI reset command (it would be easier to use bthci.reset() though)
bthci
.
rawhci
(
encoder
.
fromHex
(
"01030c00"
),
function
(
err
)
print
(
err
or
"Ok!"
))
```
#### See also
[
`encoder.fromHex()`
](
encoder.md#fromhex
)
[
`encoder.toHex()`
](
encoder.md#tohex
)
[
`struct.pack()`
](
struct.md#pack
)
## bthci.reset(callback)
Resets the BlueTooth controller.
#### Syntax
`bthci.reset([callback])`
#### Parameters
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
#### Returns
`nil`
#### Example
```
lua
bthci
.
reset
(
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.adv.enable(onoff, callback)
Enables or disables BlueTooth LE advertisements.
Before enabling advertisements, both parameters and data should be set.
#### Syntax
`bthci.adv.enable(onoff [, callback])`
#### Parameters
-
`onoff`
1 or 0 to enable or disable advertisements, respectively.
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
##### Returns
`nil`
#### Example
```
lua
bthci
.
adv
.
enable
(
1
,
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.adv.setdata(advbytes, callback)
Configures the data to advertise.
#### Syntax
`bthci.adv.setdata(advbytes [, callback])`
#### Parameters
-
`advbytes`
the raw bytes to advertise (up to 31 bytes), in the correct
format (consult the BlueTooth specification for details).
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
##### Returns
`nil`
#### Example
```
lua
-- Configure advertisements of a short name "abcdefg"
bthci
.
adv
.
setdata
(
encoder
.
fromHex
(
"080861626364656667"
),
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.adv.setparams(paramtable, callback)
Configures advertisement parameters.
#### Syntax
`bthci.adv.setparams(paramtable [, callback])`
#### Parameters
-
`paramtable`
a table with zero or more of the following fields:
-
`interval_min`
value in units of 0.625ms. Default 0x0400 (0.64s).
-
`interval_max`
value in units of 0.625ms. Default 0x0800 (1.28s).
-
`type`
advertising type, one of following constants:
-
`bthci.adv.CONN_UNDIR`
, the default (ADV_IND in BT spec)
-
`bthci.adv.CONN_DIR_HI`
(ADV_DIRECT_IND, high duty cycle in the BT spec)
-
`bthci.adv.SCAN_UNDIR`
(ADV_SCAN_IND in the BT spec)
-
`bthci.adv.NONCONN_UNDIR`
(ADV_NONCONN_IND in the BT spec)
-
`bthci.adv.CONN_DIR_LO`
(ADV_DIRECT_IND, low duty cycle in the BT spec)
-
`own_addr_type`
own address type. Default 0 (public address).
-
`peer_addr_type`
peer address type. Default 0 (public address).
-
`peer_addr`
TODO, not yet implemented
-
`channel_map`
which channels to advertise on. The constants
`bthci.adv.CHAN_37`
,
`bthci.adv.CHAN_38`
,
`bthci.adv.CHAN_39`
or
`bthci.adv.CHAN_ALL`
may be used. Default is all channels.
-
`filter_policy`
filter policy, default 0 (no filtering).
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
#### Returns
`nil`
#### Example
```
lua
bthci
.
adv
.
setparams
({
type
=
bthci
.
adv
.
NONCONN_UNDIR
},
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.scan.enable(onoff, callback)
Enables or disable scanning for advertisements from other BlueTooth devices.
#### Syntax
`bthci.scan.enable(onoff [, callback])`
#### Parameters
-
`onoff`
1 or 0 to enable or disable advertisements, respectively.
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
##### Returns
`nil`
#### Example
```
lua
bthci
.
scan
.
enable
(
1
,
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.scan.setparams(paramstable, callback)
Configures scan parameters.
Note that if configuring the scan window to be the same as the scan interval
this will fully occupy the radio and no other activity takes place.
#### Syntax
`bthci.scan.setparams(paramstable [, callback])`
#### Parameters
-
`paramstable`
a table with zero or more of the following fields:
-
`mode`
scanning mode, 0 for passive, 1 for active. Default 0.
-
`interval`
scanning interval in units of 0.625ms. Default 0x0010.
-
`window`
length of scanning window in units of 0.625ms. Default 0x0010.
-
`own_addr_type`
own address type. Default 0 (public).
-
`filter_policy`
filtering policy. Default 0 (no filtering).
-
`callback`
optional function to be invoked when the reset completes. Its
only argument is the HCI error code, or
`nil`
on success.
#### Returns
`nil`
#### Example
```
lua
bthci
.
scan
.
setparams
({
mode
=
1
,
interval
=
40
,
window
=
20
},
function
(
err
)
print
(
err
or
"Ok!"
)
end
)
```
# bthci.scan.on(event, callback)
Registers the callback to be passed any received advertisements.
#### Syntax
`bthci.scan.on(event [, callback])`
#### Parameters
-
`event`
the string describing the event. Currently only "adv_report" is
supported, to register for advertising reports.
-
`callback`
the callback function to receive the advertising reports, or
`nil`
to deregister the callback. This callback receives the raw bytes
of the advertisement payload.
#### Returns
`nil`
#### Example
```
lua
bthci
.
scan
.
on
(
"adv_report"
,
function
(
rep
)
print
(
"ADV: "
..
encoder
.
toHex
(
rep
))
end
)
```
#### See also
[
`encoder.toHex()`
](
encoder.md#tohex
)
[
`struct.unpack()`
](
struct.md#unpack
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment