· 

BLEを下から見ていく(4)

 

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第4回=nRF52840モジュールの電波を解析


nRF52840のアドバタイズをAdalm-plutoで記録してみる。Arduino IDEでボードについてSeeed XIAO nRF52840を選択すると、めちゃくちゃたくさんのexampleが出てきて何使えばいいのかわからんので、beaconにしてみる。
コードはこんなやつ。出力パワーをいじった。
  1. /*********************************************************************
  2.  This is an example for our nRF52 based Bluefruit LE modules
  3.  Pick one up today in the adafruit shop!
  4.  Adafruit invests time and resources providing this open source code,
  5.  please support Adafruit and open-source hardware by purchasing
  6.  products from Adafruit!
  7.  MIT license, check LICENSE for more information
  8.  All text above, and the splash screen below must be included in
  9.  any redistribution
  10. *********************************************************************/
  11. #include <bluefruit.h>
  12. // Beacon uses the Manufacturer Specific Data field in the advertising
  13. // packet, which means you must provide a valid Manufacturer ID. Update
  14. // the field below to an appropriate value. For a list of valid IDs see:
  15. // https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
  16. // 0x004C is Apple
  17. // 0x0822 is Adafruit
  18. // 0x0059 is Nordic
  19. #define MANUFACTURER_ID 0x0059
  20. // "nRF Connect" app can be used to detect beacon
  21. uint8_t beaconUuid[16] =
  22. {
  23.   0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
  24.   0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0
  25. };
  26. // A valid Beacon packet consists of the following information:
  27. // UUID, Major, Minor, RSSI @ 1M
  28. BLEBeacon beacon(beaconUuid, 0x0102, 0x0304, -54);
  29. void setup()
  30. {
  31.   Serial.begin(115200);
  32.   // Uncomment to blocking wait for Serial connection
  33.   while ( !Serial ) delay(10);
  34.   Serial.println("Bluefruit52 Beacon Example");
  35.   Serial.println("--------------------------\n");
  36.   Bluefruit.begin();
  37.   // off Blue LED for lowest power consumption
  38.   Bluefruit.autoConnLed(true);
  39.   Bluefruit.setTxPower(8); // Check bluefruit.h for supported values
  40.   // Manufacturer ID is required for Manufacturer Specific Data
  41.   beacon.setManufacturer(MANUFACTURER_ID);
  42.   // Setup the advertising packet
  43.   startAdv();
  44.   Serial.println("Broadcasting beacon, open your beacon app to test");
  45.   // Suspend Loop() to save power, since we didn't have any code there
  46.   suspendLoop();
  47. }
  48. void startAdv(void)
  49. {
  50.   // Advertising packet
  51.   // Set the beacon payload using the BLEBeacon class populated
  52.   // earlier in this example
  53.   Bluefruit.Advertising.setBeacon(beacon);
  54.   // Secondary Scan Response packet (optional)
  55.   // Since there is no room for 'Name' in Advertising packet
  56.   Bluefruit.ScanResponse.addName();
  57.   
  58.   /* Start Advertising
  59.    * - Enable auto advertising if disconnected
  60.    * - Timeout for fast mode is 30 seconds
  61.    * - Start(timeout) with timeout = 0 will advertise forever (until connected)
  62.    *
  63.    * Apple Beacon specs
  64.    * - Type: Non connectable, undirected
  65.    * - Fixed interval: 100 ms -> fast = slow = 100 ms
  66.    */
  67.   //Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_ADV_NONCONN_IND);
  68.   Bluefruit.Advertising.restartOnDisconnect(true);
  69.   Bluefruit.Advertising.setInterval(160, 160); // in unit of 0.625 ms
  70.   //Bluefruit.Advertising.setInterval(2, 2); // in unit of 0.625 ms
  71.   Bluefruit.Advertising.setFastTimeout(3600); // number of seconds in fast mode
  72.   Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
  73. }
  74. void loop()
  75. {
  76.   // loop is already suspended, CPU will not run loop() at all
  77. }
書き込んで、こんなかんじで。
で、BLEを下から見ていく(1)のpythonコードで受信して、
こうなる。
保存されたデータをBLEを下から見ていく(2)のpythonコードで解析すると、
こうなる。
最初のほうをちょっと拡大してみると、
こうなっている。
で、取り出したビットデータ列は
[1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0 1 0 0 0 1 0 1 1 1 0 0 0 1 1 0 1 1 0 1 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 0 1 0 0 1 0 1 1 0 0 1 1 1 0 1 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 1 0 1 0 0 0 0 1 0 1 1 1 1 0 0 1 1 1 0 1 1 1 0 0 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 0 0 1 0 1 0 0 1 1 0 1 1 1 1 1 1 0 1 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 1 1 1 0 0 1 1 0 1 1 1 1 0 1 0 0 0 1 0 0 0 1 1 1 1 0 0 1 1 1 0 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1 1 0 1 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0]
8E89BED6ってのがあるので、たぶんあっているんだろう。ここからよ。で、仕様書をもう一度よく読んでみるとwhitenって処理やってるぜって書いてある。青い歯をホワイトニングするんか、、、Channel Indexをキーにしてビットをぐやぐちゃにする定数を作るらしい。いや、こんなんじゃぐちゃぐちゃにはならんと思うので、なんともいまいちな感じもする。仕様書にはごちゃごちゃと書いてあるんだけど、Channel Indexさえわかれば定数の配列を作ることができて、それと入力または出力ビットで排他的論理和をとればいいらしい。
で、その定数の配列はこうやる。
  1. #include <stdio.h>
  2. #include <stdint.h>
  3.  
  4. int main(int argc,char** argv){
  5.     uint8_t chan=37;
  6.     uint8_t p_byte;
  7.     uint8_t x_byte;
  8.     uint8_t s_byte;
  9.     uint8_t p_6;
  10.     uint8_t i,j;
  11.     p_byte=(0x40|chan)&0x7F;
  12.     s_byte=p_byte&0x01;
  13.     for(j=0;j<64;j++){
  14.         for(i=0;i<8;i++){
  15.             p_6=p_byte&0x01;
  16.             s_byte=(s_byte<<1)|p_6;
  17.             x_byte=(p_6<<6)|(p_6<<2);
  18.             p_byte=(p_byte>>1)^x_byte;
  19.         }
  20.         printf("%02X\n",s_byte);
  21.     }
  22.     return 0;
  23. }
で、Beaconがどんなデータかってのは、親切な方がこちら(https://pe-bank.jp/guide/column/theme/iot_things/20171120)にまとめている。で、
received bin2hex whiten whitened swap bin2hex contents value
01010101 55



Preamble
01101011 6B

11010110 D6 Access Address
01111101 7D

10111110 BE Access Address
10010001 91

10001001 89 Access Address
01110001 71

10001110 8E Access Address
10110111 B7 B1 00000110 01100000 60 Header
01101111 6F 4B 00100100 00100100 24 Header 36
10010001 91 EA 01111011 11011110 DE AdvA
10001001 89 85 00001100 00110000 30 AdvA
00001111 0F BC 10110011 11001101 CD AdvA
11111111 FF E5 00011010 01011000 58 AdvA
10110011 B3 66 11010101 10101011 AB AdvA
11110110 F6 0D 11111011 11011111 DF AdvA
11101110 EE AE 01000000 00000010 02 iBeacon Prefix
00001100 0C 8C 10000000 00000001 01 iBeacon Prefix
11101000 E8 88 01100000 00000110 06 iBeacon Prefix
01001010 4A 12 01011000 00011010 1A iBeacon Prefix
10010110 96 69 11111111 11111111 FF iBeacon Prefix
01110100 74 EE 10011010 01011001 59 iBeacon Prefix
00011111 1F 1F 00000000 00000000 00 iBeacon Prefix
10000111 87 C7 01000000 00000010 02 iBeacon Prefix
11001010 CA 62 10101000 00010101 15 iBeacon Prefix
00010111 17 97 10000000 00000001 01 beacon uuid
10011101 9D D5 01001000 00010010 12 beacon uuid
11001111 CF 0B 11000100 00100011 23 beacon uuid
01010101 55 79 00101100 00110100 34 beacon uuid
01101000 68 CA 10100010 01000101 45 beacon uuid
10100110 A6 CC 01101010 01010110 56 beacon uuid
11111101 FD 1B 11100110 01100111 67 beacon uuid
01000011 43 5D 00011110 01111000 78 beacon uuid
10001000 88 19 10010001 10001001 89 beacon uuid
01001001 49 10 01011001 10011010 9A beacon uuid
11110001 F1 24 11010101 10101011 AB beacon uuid
11101110 EE D3 00111101 10111100 BC beacon uuid
01101111 6F DC 10110011 11001101 CD beacon uuid
01000100 44 3F 01111011 11011110 DE beacon uuid
01111001 79 8E 11110111 11101111 EF beacon uuid
11001010 CA C5 00001111 11110000 F0 beacon uuid
10101111 AF 2F 10000000 00000001 01 maj
11101010 EA AA 01000000 00000010 02 maj
11010110 D6 16 11000000 00000011 03 min
11010011 D3 F3 00100000 00000100 04 min
11000110 C6 95 01010011 11001010 CA TxPower -54
00011110 1E 98 10000110 01100001 61 CRC
01100111 67 36 01010001 10001010 8A CRC
11011100 DC BA 01100110 01100110 66 CRC
00000100 04 32 00110110 01101100 6C

こんなかんじで読めたわけだ。
ところで、仕様書によると、このwhiten配列は127bitで1周するようなことが書いてあるんだけど、計算してみると、127bit目のところで0xC7=0xC6|0x01てなっていて、その後0b01100010,0b10010111って続く。127bit周期はそうっぽいけど、byteでみると1bitずれになってくるわけで、それはそれで扱いが面倒。bit単位で処理するなら有用なのかも。
 
いろんなWebページでアドバタイズやBeaconについてこういうデータが入っているんだよって記述はあっちこっちにあるんだけど、whitenに触れているページは見つからなかったので苦労した。仕様書読めばわかることなんだけど、結局、実際の電波を見て書いている人はいないってことやなと思う。なんともさみしくも困った世の中だわ。