├── Makefile ├── README.md └── bletool.c /Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | LIB_DIRECTORY := -L/usr/lib/arm-linux-gnueabihf 3 | INC_DIRECTORY := -I/usr/include/bluetooth 4 | LIB := /usr/lib/arm-linux-gnueabihf/libbluetooth.a 5 | CFLAGS := -Wall -fPIC 6 | 7 | .c.o: 8 | $(CC) $(INC_DIRECTORY) $(CFLAGS) -c $< 9 | 10 | all: 11 | make tool 12 | make lib 13 | 14 | tool: bletool.o 15 | gcc -o bletool $(INC_DIRECTORY) $(LIB_DIRECTORY) $< -lbluetooth 16 | 17 | lib: bletool.o 18 | gcc $(CFLAGS) -shared -o libbletool.so $< $(LIB) 19 | install -m 644 libbletool.so ../ 20 | 21 | clean: 22 | rm -f bletool bletool.o libbletool.o libbletool.so 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raw BLE advertisement tool 2 | ==== 3 | This tool is for receiving/sending raw BLE advertisement messages. 4 | The project is based on the Linux Bluetooth protocol stack, [BlueZ](http://www.bluez.org/). 5 | 6 | The is configured for Raspberry Pi 3 and LIB_DIRECTORY needs to be set properly for other environments. 7 | 8 | ## Preparation 9 | Install dependencies. 10 | 11 | ``` 12 | sudo apt update && sudo apt-get -y install libdbus-1-dev libdbus-glib-1-dev libglib2.0-dev libical-dev libreadline-dev libudev-dev libusb-dev make bluetooth bluez-utils libbluetooth-dev 13 | ``` 14 | 15 | ## Compilation 16 | ``` 17 | make 18 | ``` 19 | 20 | ## Usage 21 | The following shows the usage. 22 | ``` 23 | ./bletool -h 24 | ``` 25 | 26 | If you want to receive advertisements, use -r option to set the tool *receive mode*. 27 | ``` 28 | ./bletool -r 29 | ``` 30 | 31 | If you want to send advetisement, use -s option to set the tool *send mode*. 32 | ``` 33 | ./bletool -s 00112233445566778899 34 | ``` 35 | The parameter following `-s` is the hex string of the message to advertise. 36 | 37 | -------------------------------------------------------------------------------- /bletool.c: -------------------------------------------------------------------------------- 1 | /** 2 | * BLE advertise and scan tool 3 | * t-kubo @ Zettant Inc. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define DEVICE_NAME "hci0" 20 | #define DEFAULT_ADV_HEADER "1F0201060303AAFE1716AAFE80" 21 | #define MAX_PKT_SIZE 32 22 | 23 | static struct hci_filter ofilter; 24 | static volatile int signal_received = 0; 25 | static int ble_min_interval = 32; 26 | static int ble_max_interval = 64; 27 | 28 | static void sigint_handler(int sig) { 29 | signal_received = sig; 30 | } 31 | 32 | static void hex_dump(char *pref, unsigned char *buf, int len) 33 | { 34 | printf("%s", pref); 35 | for (int i = 0; i < len; i++) 36 | printf("%2.2X", buf[i]); 37 | printf(" "); 38 | 39 | for (int i = 0; i < len; i++) 40 | printf("%c", (buf[i] < 0x20 || buf[i] > 0x7e) ? '.' : buf[i]); 41 | printf("\n"); 42 | } 43 | 44 | static int open_device(char *dev_name) 45 | { 46 | int dev_id = hci_devid(dev_name); 47 | if (dev_id < 0) 48 | dev_id = hci_get_route(NULL); 49 | 50 | int dd = hci_open_dev(dev_id); 51 | if (dd < 0) { 52 | perror("Could not open device"); 53 | exit(1); 54 | } 55 | return dd; 56 | } 57 | 58 | void ctrl_command(uint8_t ogf, uint16_t ocf, char *data) { 59 | unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf, tmp[2]; 60 | struct hci_filter flt; 61 | int i, len, dd; 62 | 63 | dd = open_device(DEVICE_NAME); 64 | len = (int)(strlen(data)/2); 65 | 66 | for (i=0; idata + 1); 134 | memcpy(data, info->data, datalen); 135 | return len; 136 | } 137 | 138 | int print_advertising_devices(int dd) { 139 | struct sigaction sa; 140 | unsigned char dat[MAX_PKT_SIZE]; 141 | 142 | memset(&sa, 0, sizeof(sa)); 143 | sa.sa_flags = SA_NOCLDSTOP; 144 | sa.sa_handler = sigint_handler; 145 | sigaction(SIGINT, &sa, NULL); 146 | 147 | while (1) { 148 | if (read_advertise(dd, dat, MAX_PKT_SIZE) == 0) break; 149 | hex_dump("", dat, MAX_PKT_SIZE); 150 | } 151 | return 0; 152 | } 153 | 154 | 155 | void lescan_close(int dd) 156 | { 157 | uint8_t filter_dup = 0; 158 | if (dd == -1) { 159 | dd = open_device(DEVICE_NAME); 160 | } else { 161 | setsockopt(dd, SOL_HCI, HCI_FILTER, &ofilter, sizeof(ofilter)); 162 | } 163 | int err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 1000); 164 | if (err < 0) { 165 | perror("Disable scan failed"); 166 | exit(1); 167 | } 168 | hci_close_dev(dd); 169 | } 170 | 171 | int lescan_setup() { 172 | int err, dd; 173 | uint8_t own_type = 0x00; 174 | uint8_t scan_type = 0x00; // passive 175 | uint8_t filter_policy = 0x00; 176 | uint16_t interval = htobs(0x0010); 177 | uint16_t window = htobs(0x0010); 178 | uint8_t filter_dup = 0; 179 | 180 | dd = open_device(DEVICE_NAME); 181 | 182 | err = hci_le_set_scan_parameters(dd, scan_type, interval, window, 183 | own_type, filter_policy, 1000); 184 | if (err < 0) { 185 | lescan_close(-1); 186 | perror("Set scan parameters failed"); 187 | exit(1); 188 | } 189 | 190 | err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000); 191 | if (err < 0) { 192 | hci_close_dev(dd); 193 | perror("Enable scan failed"); 194 | exit(1); 195 | } 196 | 197 | struct hci_filter nf; 198 | socklen_t olen; 199 | 200 | olen = sizeof(ofilter); 201 | if (getsockopt(dd, SOL_HCI, HCI_FILTER, &ofilter, &olen) < 0) { 202 | hci_close_dev(dd); 203 | printf("Could not get socket options\n"); 204 | return -1; 205 | } 206 | 207 | hci_filter_clear(&nf); 208 | hci_filter_set_ptype(HCI_EVENT_PKT, &nf); 209 | hci_filter_set_event(EVT_LE_META_EVENT, &nf); 210 | 211 | if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) { 212 | printf("Could not set socket options\n"); 213 | return -1; 214 | } 215 | 216 | return dd; 217 | } 218 | 219 | 220 | static void usage(void) 221 | { 222 | printf("Usage: bletool <-r | -s> [options...]\n"); 223 | printf("Options:\n" 224 | "\t-r, --read Receive mode\n" 225 | "\t-s, --send=HEX_STRING Send advertisements\n" 226 | "\t-h, --help Display help\n" 227 | "\n" 228 | "Send (-s) advertisement options:\n" 229 | "\t-m, --min_interval=MS Minimum interval between adverts in ms (default: 32)\n" 230 | "\t-M, --max_interval=MS Maximum interval between adverts in ms (default: 64)\n" 231 | ); 232 | } 233 | 234 | static struct option main_options[] = { 235 | { "help", no_argument, 0, 'h' }, 236 | { "read", no_argument, 0, 'r' }, 237 | { "send", required_argument, 0, 's' }, 238 | { "min_interval", required_argument, 0, 'm' }, 239 | { "max_interval", required_argument, 0, 'M' }, 240 | { 0, no_argument, 0, 0 } 241 | }; 242 | 243 | int main(int argc, char **argv) { 244 | int option_index = 0, mode = 0, opt; 245 | char *send_data; 246 | 247 | while ((opt = getopt_long(argc, argv, "r+s:m:M:h", main_options, &option_index)) != -1) { 248 | switch (opt) { 249 | case 'r': 250 | mode = 1; // receive mode 251 | break; 252 | 253 | case 's': 254 | mode = 2; 255 | send_data = optarg; 256 | break; 257 | 258 | case 'm': 259 | ble_min_interval = atoi(optarg); 260 | break; 261 | 262 | case 'M': 263 | ble_max_interval = atoi(optarg); 264 | break; 265 | 266 | case 'h': 267 | default: 268 | mode = 0; 269 | } 270 | } 271 | printf("ble_min_interval: %d\n", ble_min_interval); 272 | printf("ble_max_interval: %d\n", ble_max_interval); 273 | 274 | if (mode == 0) { 275 | usage(); 276 | exit(0); 277 | } else if (mode == 1) { 278 | int dd = lescan_setup(); 279 | print_advertising_devices(dd); 280 | lescan_close(dd); 281 | } else if (mode == 2) { 282 | configure(ble_min_interval, ble_max_interval); 283 | set_advertisement_data(send_data); 284 | advertise_on(true); 285 | sleep(1); 286 | advertise_on(false); 287 | } else { 288 | printf("ERROR: we shouldn't be here\n"); 289 | exit(1); 290 | } 291 | } 292 | --------------------------------------------------------------------------------