본문 바로가기

지식/Embeded

큐비트럭 블루투스 프로그래밍 - BLE 연결

bluez 에서는 bluetooth 4.0의 core(?)를 지원한다고 나와있으나 bluez에서 제공하는 util을 사용해보면

4.0을 스캔하기 위해서는 별도의 스캔 옵션을 주어야 한다. 

이말은 4,0 이전 버전과 4.0 버전의 인터페이스가 다르다는 뜻이다.


bluetooth 4.0 디바이스를 검색하기 위해서는 


hcitool lescan


을 사용해야한다. 

사용해보면 알겠지만 이 방법은 사용자가 종료하기 전까지 무한대기한다. 

내부 함수를 살펴보면 read 함수를 호출하는데 블루투스 소켓이기 때문에 서비스가 내려가기 전까지 대기할 수 밖에 없는 구조이다. 이 부분은 차후에 수정할 예정이며 아래는 일단 스캔에 설공한 소스예제이다.


이 역시 hcitool 소스에서 필요한 부분만은 편집한 소스이다.



  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <ctype.h>
  4. #include <fcntl.h>
  5. #include <sys/param.h>
  6. #include <sys/ioctl.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. #include <signal.h>
  10. #include <bluetooth/bluetooth.h>
  11. #include <bluetooth/hci.h>
  12. #include <bluetooth/hci_lib.h>
  13. #include <errno.h>
  14.  
  15. #include "textfile.h"
  16. #include "oui.h"
  17.  
  18. #define FLAGS_AD_TYPE   0x01
  19. #define FLAGS_LIMITED_MODE_BIT  0x01
  20. #define FLAGS_GENERAL_MODE_BIT  0x02
  21.  
  22. #define EIR_FLAGS           0x01
  23. #define EIR_UUID16_SOME     0x02
  24. #define EIR_UUID16_ALL      0x03
  25. #define EIR_UUID32_SOME     0x04
  26. #define EIR_UUID32_ALL      0x05
  27. #define EIR_UUID128_SOME    0x06
  28. #define EIR_UUID128_ALL     0x07
  29. #define EIR_NAME_SHORT      0x08
  30. #define EIR_NAME_COMPLETE   0x09
  31. #define EIR_TX_POWER        0x0A
  32. #define EIR_DEVICE_ID       0x10
  33.  
  34. static volatile int signal_received = 0;
  35.  
  36. static int print_advertising_devices(int dd, uint8_t filter_type);
  37. static void sigint_handler(int sig);
  38. static int check_report_filter(uint8_t procedure, le_advertising_info *info);
  39. static int read_flags(uint8_t *flags, const uint8_t *data, size_t size);
  40. static void eir_parse_name(uint8_t *eir, size_t eir_len, char *buf, size_t buf_len);
  41.  
  42.  
  43. static void eir_parse_name(uint8_t *eir, size_t eir_len, char *buf, size_t buf_len)
  44. {
  45.     size_t offset;
  46.  
  47.     offset = 0;
  48.  
  49.     while(offset < eir_len)
  50.     {
  51.         uint8_t field_len = eir[0];
  52.         size_t name_len;
  53.  
  54.         if(field_len == 0)
  55.             break;
  56.  
  57.         if(offset + field_len > eir_len)
  58.             goto failed;
  59.  
  60.         switch(eir[1])
  61.         {
  62.             case EIR_NAME_SHORT:
  63.             case EIR_NAME_COMPLETE:
  64.                 name_len = field_len -1;
  65.                 if(name_len > buf_len)
  66.                     goto failed;
  67.  
  68.                 memcpy(buf, &eir[2], name_len);
  69.                 return ;
  70.         }
  71.  
  72.         offset += field_len + 1;
  73.         eir += field_len + 1;
  74.     }
  75.  
  76. failed:
  77.     snprintf(buf, buf_len,"(unknow)");
  78. }
  79.  
  80.  
  81. static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
  82. {
  83.     size_t offset;
  84.  
  85.     if(!flags || !data)
  86.         return -EINVAL;
  87.  
  88.     offset = 0;
  89.     while(offset < size)
  90.     {
  91.         uint8_t len = data[offset];
  92.         uint8_t type;
  93.  
  94.         if(len == 0)
  95.             break;
  96.  
  97.         if(len + offset > size)
  98.             break;
  99.  
  100.         type = data[offset +1];
  101.  
  102.         if(type == FLAGS_AD_TYPE)
  103.         {
  104.             *flags = data[offset +2];
  105.             return 0;
  106.         }
  107.  
  108.         offset += 1 + len;
  109.     }
  110.  
  111.     return -ENOENT;
  112.  
  113. }
  114.  
  115.  
  116.  
  117. static int check_report_filter(uint8_t procedure, le_advertising_info *info)
  118. {
  119.     printf("report filter..\n");
  120.     uint8_t flags;
  121.  
  122.     if(procedure == 0)
  123.         return 1;
  124.  
  125.     printf("read flags...\n");
  126.     if(read_flags(&flags, info->data, info->length))
  127.         return 0;
  128.  
  129.     switch(procedure)
  130.     {
  131.         case 1:
  132.             if(flags & FLAGS_LIMITED_MODE_BIT)
  133.                 return 1;
  134.             break;
  135.  
  136.         case 'g':
  137.             if(flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
  138.                 return 1;
  139.             break;
  140.  
  141.         default:
  142.             fprintf(stderr"Unknow discovery procedure\n");
  143.             break;
  144.     }
  145.  
  146.     return 0;
  147. }
  148.  
  149.  
  150. static void sigint_handler(int sig)
  151. {
  152.     signal_received = sig;
  153. }
  154.  
  155. static int print_advertising_devices(int dd, uint8_t filter_type)
  156. {
  157.     unsigned char buf[HCI_MAX_EVENT_SIZE]*ptr;
  158.     struct hci_filter nf, of;
  159.     struct sigaction sa;
  160.     socklen_t olen;
  161.     int len;
  162.  
  163.     olen = sizeof(of);
  164.     if(getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
  165.     {
  166.         printf("Could not get socket option\n");
  167.         return -1;
  168.     }
  169.  
  170.     hci_filter_clear(&nf);
  171.     hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
  172.     hci_filter_set_event(EVT_LE_META_EVENT, &nf);
  173.  
  174.     if(setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
  175.     {
  176.         printf("Could not set socket option\n");
  177.         return -1;
  178.     }
  179.    
  180.     memset(&sa, 0x00sizeof(sa));
  181.     sa.sa_flags = SA_NOCLDSTOP;
  182.     sa.sa_handler = sigint_handler;
  183.     sigaction(SIGINT&sa, NULL);
  184.  
  185.     while(1)
  186.     {
  187.         evt_le_meta_event *meta;
  188.         le_advertising_info *info;
  189.         char addr[18];
  190.        
  191.  
  192.         while( (len = read(dd, buf, sizeof(buf))) < 0 )
  193.         {
  194.             printf("while...\n");
  195.             if(errno == EINTR && signal_received == SIGINT )
  196.             {
  197.                 len = 0;
  198.                 goto done;
  199.             }
  200.  
  201.             if( errno == EAGAIN || errno == EINTR )
  202.                 continue;
  203.             goto done;
  204.  
  205.         }
  206.  
  207.         ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
  208.         len -= (1 + HCI_EVENT_HDR_SIZE);
  209.  
  210.         meta = (void*) ptr;
  211.  
  212.         if(meta->subevent != 0x02)
  213.             goto done;
  214.  
  215.         /* ignoring multiple reports */
  216.         info = (le_advertising_info*) (meta->data + 1);
  217.         if(check_report_filter(filter_type, info))
  218.         {
  219.             char name[30];
  220.  
  221.             memset(name, 0x00sizeof(name));
  222.  
  223.             ba2str(&info->bdaddr, addr);
  224.             eir_parse_name(info->data, info->length, name, sizeof(name)-1);
  225.  
  226.             printf("%s %s\n",addr, name);
  227.         }
  228.     }
  229.        
  230. done:
  231.     setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
  232.  
  233.     if(len < 0)
  234.         return -1;
  235.  
  236.  
  237.     return 0;
  238. }
  239.  
  240.  
  241.  
  242.  
  243.  
  244.  
  245. int main(int argc, char **argv)
  246. {
  247.     int dev_id,dd;
  248.  
  249.     int err;
  250.  
  251.     uint8_t own_type    = 0x00;
  252.     uint8_t scan_type   = 0x01; // 0x00 passive sacan 0x01 random scan
  253.     uint8_t filter_type = 0;
  254.     uint8_t filter_dup  = 1;
  255.     uint16_t interval   = htobs(0x0010); // 0x12 none filtering duplicates
  256.     uint16_t window     = htobs(0x0010);        // same upper
  257.  
  258.     dev_id = hci_get_route(NULL);
  259.  
  260.     if(dev_id < 0)
  261.     {
  262.         perror("Could not open device");
  263.         exit(1);
  264.     }
  265.  
  266.     dd = hci_open_dev(dev_id);
  267.     if(dd < 0)
  268.     {
  269.         perror("Could not open device");
  270.         exit(1);
  271.     }
  272.  
  273.     interval = htobs(0x0012);
  274.     window = htobs(0x0012);
  275.  
  276.     err = hci_le_set_scan_parameters(dd, scan_type, interval, window, own_type, 0x001000);
  277.  
  278.     if(err < 0)
  279.     {
  280.         perror("set scan parameters failed");
  281.         exit(1);
  282.     }
  283.  
  284.     err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000);
  285.  
  286.     if(err < 0)
  287.     {
  288.         perror("Enable scan failed");
  289.         exit(1);
  290.     }
  291.     printf("LE Scan ...\n");
  292.  
  293.     err = print_advertising_devices(dd, filter_type);
  294.  
  295.     if(err < 0)
  296.     {
  297.         perror("Could not reveive adverising events");
  298.         exit(1);
  299.     }
  300.        
  301.     err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 1000);
  302.     if(err < 0)
  303.     {
  304.         perror("Disable scan failed");
  305.         exit(1);
  306.     }
  307.  
  308.     hci_close_dev(dd);
  309.  
  310.     return 0;
  311. }
  312.  
  313.  
  314.