Simple BlueTooth module for BT LE advertisements.

This commit is contained in:
Johny Mattsson 2016-09-29 17:02:40 +10:00
parent 8e441b59c1
commit f9bf50cf03
4 changed files with 804 additions and 1 deletions

View File

@ -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

575
components/modules/bthci.c Normal file
View File

@ -0,0 +1,575 @@
/*
* 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);

View File

@ -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

222
docs/en/modules/bthci.md Normal file
View File

@ -0,0 +1,222 @@
# 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)