iOS藍(lán)牙Bluetooth

(一) iOS藍(lán)牙開(kāi)發(fā)藍(lán)牙相關(guān)基礎(chǔ)知識(shí)

藍(lán)牙常見(jiàn)名稱和縮寫(xiě)

MFI ======= make for ipad ,iphone, itouch 專們?yōu)樘O(píng)果設(shè)備制作的設(shè)備
BLE ==== buletouch low energy阱持,藍(lán)牙4.0設(shè)備因?yàn)榈秃碾姡砸步凶鯞LE
peripheral,central == 外設(shè)和中心,發(fā)起連接的時(shí)central喳张,被連接的設(shè)備為perilheral
service and characteristic === 服務(wù)和特征 每個(gè)設(shè)備會(huì)提供服務(wù)和特征晰洒,類似于服務(wù)端的api午绳,但是機(jī)構(gòu)不同癌佩。每個(gè)外設(shè)會(huì)有很多服務(wù),每個(gè)服務(wù)中包含很多字段喜最,這些字段的權(quán)限一般分為 讀read澜倦,寫(xiě)write芥永,通知notiy幾種篡殷,就是我們連接設(shè)備后具體需要操作的內(nèi)容。
Description 每個(gè)characteristic可以對(duì)應(yīng)一個(gè)或多個(gè)Description用戶描述characteristic的信息或?qū)傩?br> MFI === 開(kāi)發(fā)使用ExternalAccessory 框架
4.0 BLE === 開(kāi)發(fā)使用CoreBluetooth 框架

藍(lán)牙基礎(chǔ)知識(shí)

CoreBluetooth框架的核心其實(shí)是兩個(gè)東西埋涧,peripheral和central, 可以理解成外設(shè)和中心板辽。對(duì)應(yīng)他們分別有一組相關(guān)的API和類

CoreBluetoothFramework.jpeg

這兩組api分別對(duì)應(yīng)不同的業(yè)務(wù)場(chǎng)景,左側(cè)叫做中心模式飞袋,就是以你的app作為中心戳气,連接其他的外設(shè)的場(chǎng)景,而右側(cè)稱為外設(shè)模式巧鸭,使用手機(jī)作為外設(shè)別其他中心設(shè)備操作的場(chǎng)景瓶您。
服務(wù)和特征,特征的屬性(service and characteristic):
每個(gè)設(shè)備都會(huì)有一些服務(wù)纲仍,每個(gè)服務(wù)里面都會(huì)有一些特征呀袱,特征就是具體鍵值對(duì),提供數(shù)據(jù)的地方郑叠。每個(gè)特征屬性分為這么幾種:讀夜赵,寫(xiě),通知這么幾種方式乡革。

  //objcetive c特征的定義枚舉
  typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
      CBCharacteristicPropertyBroadcast                                             = 0x01,
      CBCharacteristicPropertyRead                                                  = 0x02,
      CBCharacteristicPropertyWriteWithoutResponse                                  = 0x04,
      CBCharacteristicPropertyWrite                                                 = 0x08,
      CBCharacteristicPropertyNotify                                                    = 0x10,
      CBCharacteristicPropertyIndicate                                              = 0x20,
      CBCharacteristicPropertyAuthenticatedSignedWrites                             = 0x40,
      CBCharacteristicPropertyExtendedProperties                                        = 0x80,
      CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)       = 0x100,
      CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
  };

外設(shè)寇僧、服務(wù)、特征間的關(guān)系

TreeOfServicesAndCharacteristics_Remote_2x.png

藍(lán)牙中心模式流程

  1. 建立中心角色
  1. 掃描外設(shè)(discover)
  2. 連接外設(shè)(connect)
  3. 掃描外設(shè)中的服務(wù)和特征(discover)
    • 4.1 獲取外設(shè)的services
    • 4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值沸版,獲取Characteristics的Descriptor和Descriptor的值
  4. 與外設(shè)做數(shù)據(jù)交互(explore and interact)
  5. 訂閱Characteristic的通知
  6. 斷開(kāi)連接(disconnect)

藍(lán)牙外設(shè)模式流程

  1. 啟動(dòng)一個(gè)Peripheral管理對(duì)象
  1. 本地Peripheral設(shè)置服務(wù),特性,描述嘁傀,權(quán)限等等
  2. Peripheral發(fā)送廣告
  3. 設(shè)置處理訂閱、取消訂閱视粮、讀characteristic细办、寫(xiě)characteristic的委托方法

藍(lán)牙設(shè)備狀態(tài)

  1. 待機(jī)狀態(tài)(standby):設(shè)備沒(méi)有傳輸和發(fā)送數(shù)據(jù),并且沒(méi)有連接到任何設(shè)
  1. 廣播狀態(tài)(Advertiser):周期性廣播狀態(tài)
  2. 掃描狀態(tài)(Scanner):主動(dòng)尋找正在廣播的設(shè)備
  3. 發(fā)起鏈接狀態(tài)(Initiator):主動(dòng)向掃描設(shè)備發(fā)起連接蕾殴。
  4. 主設(shè)備(Master):作為主設(shè)備連接到其他設(shè)備笑撞。
  5. 從設(shè)備(Slave):作為從設(shè)備連接到其他設(shè)備。

藍(lán)牙設(shè)備的五種工作狀態(tài)

準(zhǔn)備(standby)
廣播(advertising)
監(jiān)聽(tīng)掃描(Scanning
發(fā)起連接(Initiating)
已連接(Connected)

藍(lán)牙和版本的使用限制

藍(lán)牙2.0 === 越獄設(shè)備
藍(lán)牙4.0 === IOS6 以上
MFI認(rèn)證設(shè)備(Make For ipod/ipad/iphone) === 無(wú)限制

名詞解釋

GAAT :
Generic Attribute Profile , GATT配置文件是一個(gè)通用規(guī)范钓觉,用于在BLE鏈路上發(fā)送和接收被稱為“屬性”的數(shù)據(jù)塊茴肥。目前所有的BLE應(yīng)用都基于GATT。 藍(lán)牙SIG規(guī)定了許多低功耗設(shè)備的配置文件荡灾。配置文件是設(shè)備如何在特定的應(yīng)用程序中工作的規(guī)格說(shuō)明炉爆。注意一個(gè)設(shè)備可以實(shí)現(xiàn)多個(gè)配置文件堕虹。例如,一個(gè)設(shè)備可能包括心率監(jiān)測(cè)儀和電量檢測(cè)芬首。
Characteristic:
一個(gè)characteristic包括一個(gè)單一變量和0-n個(gè)用來(lái)描述characteristic變量的descriptor,characteristic可以被認(rèn)為是一個(gè)類型逼裆,類 似于類郁稍。
Descriptor:
Descriptor用來(lái)描述characteristic變量的屬性。例如胜宇,一個(gè)descriptor可以規(guī)定一個(gè)可讀的描述耀怜,或者一個(gè)characteristic變量可接受的范圍,或者一個(gè)characteristic變量特定的測(cè)量單位桐愉。 Service service是characteristic的集合财破。例如,你可能有一個(gè)叫“Heart Rate Monitor(心率監(jiān)測(cè)儀)”的service从诲,它包括了很多characteristics左痢,如“heart rate measurement(心率測(cè)量)”等。你可以在bluetooth.org 找到一個(gè)目前支持的基于GATT的配置文件和服務(wù)列表系洛。

(二)ios藍(lán)牙開(kāi)發(fā)ios連接外設(shè)的代碼實(shí)現(xiàn)

這里我們具體說(shuō)明一下中心模式的應(yīng)用場(chǎng)景俊性。主設(shè)備(手機(jī)去掃描連接外設(shè),發(fā)現(xiàn)外設(shè)服務(wù)和屬性描扯,操作服務(wù)和屬性的應(yīng)用定页。一般來(lái)說(shuō),外設(shè)(藍(lán)牙設(shè)備绽诚,比如智能手環(huán)之類的東西)典徊, 會(huì)由硬件工程師開(kāi)發(fā)好,并定義好設(shè)備提供的服務(wù)恩够,每個(gè)服務(wù)對(duì)于的特征卒落,每個(gè)特征的屬性(只讀,只寫(xiě)玫鸟,通知等等)导绷,本文例子的業(yè)務(wù)場(chǎng)景,就是用一手機(jī)app去讀寫(xiě)藍(lán)牙設(shè)備屎飘。

ios連接外設(shè)的代碼實(shí)現(xiàn)流程

  1. 建立中心角色
  1. 掃描外設(shè)(discover)
  2. 連接外設(shè)(connect)
  3. 掃描外設(shè)中的服務(wù)和特征(discover)
    • 4.1 獲取外設(shè)的services
    • 4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值妥曲,獲取Characteristics的Descriptor和Descriptor的值
  4. 與外設(shè)做數(shù)據(jù)交互(explore and interact)
  5. 訂閱Characteristic的通知
  6. 斷開(kāi)連接(disconnect)

準(zhǔn)備環(huán)境

1 xcode
2 開(kāi)發(fā)證書(shū)和手機(jī)(藍(lán)牙程序需要使用使用真機(jī)調(diào)試,使用模擬器也可以調(diào)試钦购,但是方法很蛋疼檐盟,我會(huì)放在最后說(shuō))
3 藍(lán)牙外設(shè)

實(shí)現(xiàn)步驟

1 導(dǎo)入CoreBluetooth頭文件,建立主設(shè)備管理類押桃,設(shè)置主設(shè)備委托

#import <CoreBluetooth/CoreBluetooth.h>
    @interface ViewController : UIViewController<CBCentralManagerDelegate>


    @interface ViewController (){
        //系統(tǒng)藍(lán)牙設(shè)備管理對(duì)象葵萎,可以把他理解為主設(shè)備,通過(guò)他,可以去掃描和鏈接外設(shè)
        CBCentralManager *manager;
        //用于保存被發(fā)現(xiàn)設(shè)備
        NSMutableArray *peripherals;
    }

    - (void)viewDidLoad {
        [super viewDidLoad];
        /*
         設(shè)置主設(shè)備的委托,CBCentralManagerDelegate
            必須實(shí)現(xiàn)的:
            - (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主設(shè)備狀態(tài)改變的委托羡忘,在初始化CBCentralManager的適合會(huì)打開(kāi)設(shè)備谎痢,只有當(dāng)設(shè)備正確打開(kāi)后才能使用
            其他選擇實(shí)現(xiàn)的委托中比較重要的:
            - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外設(shè)的委托
            - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托
            - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托
            - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開(kāi)外設(shè)的委托
        */
         //初始化并設(shè)置委托和線程隊(duì)列,最好一個(gè)線程的參數(shù)可以為nil卷雕,默認(rèn)會(huì)就main線程
         manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

2 掃描外設(shè)(discover)节猿,掃描外設(shè)的方法我們放在centralManager成功打開(kāi)的委托中,因?yàn)橹挥性O(shè)備成功打開(kāi)漫雕,才能開(kāi)始掃描滨嘱,否則會(huì)報(bào)錯(cuò)。

 -(void)centralManagerDidUpdateState:(CBCentralManager *)central{

            switch (central.state) {
                case CBCentralManagerStateUnknown:
                    NSLog(@">>>CBCentralManagerStateUnknown");
                    break;
                case CBCentralManagerStateResetting:
                    NSLog(@">>>CBCentralManagerStateResetting");
                    break;
                case CBCentralManagerStateUnsupported:
                    NSLog(@">>>CBCentralManagerStateUnsupported");
                    break;
                case CBCentralManagerStateUnauthorized:
                    NSLog(@">>>CBCentralManagerStateUnauthorized");
                    break;
                case CBCentralManagerStatePoweredOff:
                    NSLog(@">>>CBCentralManagerStatePoweredOff");
                    break;
                case CBCentralManagerStatePoweredOn:
                    NSLog(@">>>CBCentralManagerStatePoweredOn");
                    //開(kāi)始掃描周圍的外設(shè)
                    /*
                     第一個(gè)參數(shù)nil就是掃描周圍所有的外設(shè)浸间,掃描到外設(shè)后會(huì)進(jìn)入
                          - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
                     */
                    [manager scanForPeripheralsWithServices:nil options:nil];

                    break;
                default:
                    break;
            }

        }

        //掃描到設(shè)備會(huì)進(jìn)入方法
        -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{

            NSLog(@"當(dāng)掃描到設(shè)備:%@",peripheral.name);
            //接下來(lái)可以連接設(shè)備

        }

3 連接外設(shè)(connect)

        //掃描到設(shè)備會(huì)進(jìn)入方法
        -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{

            //接下連接我們的測(cè)試設(shè)備太雨,如果你沒(méi)有設(shè)備,可以下載一個(gè)app叫l(wèi)ightbule的app去模擬一個(gè)設(shè)備
            //這里自己去設(shè)置下連接規(guī)則魁蒜,我設(shè)置的是P開(kāi)頭的設(shè)備
                   if ([peripheral.name hasPrefix:@"P"]){
                   /*
                       一個(gè)主設(shè)備最多能連7個(gè)外設(shè)囊扳,每個(gè)外設(shè)最多只能給一個(gè)主設(shè)備連接,連接成功,失敗梅惯,斷開(kāi)會(huì)進(jìn)入各自的委托
                    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托
                    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托
                    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開(kāi)外設(shè)的委托
                    */

                    //找到的設(shè)備必須持有它宪拥,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用O臣酢她君!
                    [peripherals addObject:peripheral];
                    //連接設(shè)備
                   [manager connectPeripheral:peripheral options:nil];
               }

        }


        //連接到Peripherals-成功
        - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
        {
            NSLog(@">>>連接到名稱為(%@)的設(shè)備-成功",peripheral.name);
        }

        //連接到Peripherals-失敗
        -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
        {
            NSLog(@">>>連接到名稱為(%@)的設(shè)備-失敗,原因:%@",[peripheral name],[error localizedDescription]);
        }

        //Peripherals斷開(kāi)連接
        - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
            NSLog(@">>>外設(shè)連接斷開(kāi)連接 %@: %@\n", [peripheral name], [error localizedDescription]);

        }

有一點(diǎn)非常容易出錯(cuò),大家請(qǐng)注意葫哗。在 didDiscoverPeripheral這個(gè)委托中有這一行

//找到的設(shè)備必須持有它缔刹,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用A诱搿校镐!
    [peripherals addObject:peripheral];

請(qǐng)?zhí)貏e注意,如果不保存捺典,會(huì)影響到后面的方法執(zhí)行鸟廓,這個(gè)地方很多人出錯(cuò),在我的藍(lán)牙交流群中每天幾乎都會(huì)因?yàn)檫@個(gè)問(wèn)題導(dǎo)致無(wú)法連接和對(duì)外設(shè)后續(xù)的操作襟己。

大家也可以看一下這個(gè)委托在xcode中的說(shuō)明,重點(diǎn)看@discussion中的內(nèi)容引谜,里面特別指出了需要retained對(duì)象。

/*!
 *  @method centralManager:didDiscoverPeripheral:advertisementData:RSSI:
 *
 *  @param central              The central manager providing this update.
 *  @param peripheral           A <code>CBPeripheral</code> object.
 *  @param advertisementData    A dictionary containing any advertisement and scan response data.
 *  @param RSSI                 The current RSSI of <i>peripheral</i>, in dBm. A value of <code>127</code> is reserved and indicates the RSSI
 *                              was not available.
 *
 *  @discussion                 This method is invoked while scanning, upon the discovery of <i>peripheral</i> by <i>central</i>. A discovered peripheral must
 *                              be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For
 *                              a list of <i>advertisementData</i> keys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.
 *
 *  @seealso                    CBAdvertisementData.h
 *
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;

4掃描外設(shè)中的服務(wù)和特征(discover)

設(shè)備連接成功后擎浴,就可以掃描設(shè)備的服務(wù)了员咽,同樣是通過(guò)委托形式,掃描到結(jié)果后會(huì)進(jìn)入委托方法贮预。但是這個(gè)委托已經(jīng)不再是主設(shè)備的委托(CBCentralManagerDelegate)贝室,而是外設(shè)的委托(CBPeripheralDelegate),這個(gè)委托包含了主設(shè)備與外設(shè)交互的許多 回叫方法契讲,包括獲取services,獲取characteristics滑频,獲取characteristics的值捡偏,獲取characteristics的Descriptor,和Descriptor的值峡迷,寫(xiě)數(shù)據(jù)霹琼,讀rssi,用通知的方式訂閱數(shù)據(jù)等等凉当。

4.1獲取外設(shè)的services

 //連接到Peripherals-成功
        - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
        {
            NSLog(@">>>連接到名稱為(%@)的設(shè)備-成功",peripheral.name);
            //設(shè)置的peripheral委托CBPeripheralDelegate
            //@interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>
            [peripheral setDelegate:self];
            //掃描外設(shè)Services,成功后會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
            [peripheral discoverServices:nil];

        }

        //掃描到Services
        -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
            //  NSLog(@">>>掃描到服務(wù):%@",peripheral.services);
            if (error)
            {
                NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
                return;
            }

            for (CBService *service in peripheral.services) {
                             NSLog(@"%@",service.UUID);
                             //掃描每個(gè)service的Characteristics售葡,掃描到后會(huì)進(jìn)入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
                             [peripheral discoverCharacteristics:nil forService:service];
                         }

        }

4.2獲取外設(shè)的Characteristics,獲取Characteristics的值看杭,獲取Characteristics的Descriptor和Descriptor的值

//掃描到Characteristics
     -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
         if (error)
         {
             NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
             return;
         }

         for (CBCharacteristic *characteristic in service.characteristics)
         {
             NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);
         }

         //獲取Characteristic的值,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
         for (CBCharacteristic *characteristic in service.characteristics){
             {
                 [peripheral readValueForCharacteristic:characteristic];
             }
         }

         //搜索Characteristic的Descriptors挟伙,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
         for (CBCharacteristic *characteristic in service.characteristics){
             [peripheral discoverDescriptorsForCharacteristic:characteristic];
         }


     }

    //獲取的charateristic的值
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
        //打印出characteristic的UUID和值
        //!注意楼雹,value的類型是NSData,具體開(kāi)發(fā)時(shí)尖阔,會(huì)根據(jù)外設(shè)協(xié)議制定的方式去解析數(shù)據(jù)
        NSLog(@"characteristic uuid:%@  value:%@",characteristic.UUID,characteristic.value);

    }

    //搜索到Characteristic的Descriptors
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

        //打印出Characteristic和他的Descriptors
         NSLog(@"characteristic uuid:%@",characteristic.UUID);
        for (CBDescriptor *d in characteristic.descriptors) {
            NSLog(@"Descriptor uuid:%@",d.UUID);
        }

    }
    //獲取到Descriptors的值
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
        //打印出DescriptorsUUID 和value
        //這個(gè)descriptor都是對(duì)于characteristic的描述贮缅,一般都是字符串,所以這里我們轉(zhuǎn)換成字符串去解析
        NSLog(@"characteristic uuid:%@  value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
    }

5 把數(shù)據(jù)寫(xiě)到Characteristic中

 //寫(xiě)數(shù)據(jù)
    -(void)writeCharacteristic:(CBPeripheral *)peripheral
                characteristic:(CBCharacteristic *)characteristic
                         value:(NSData *)value{

        //打印出 characteristic 的權(quán)限介却,可以看到有很多種谴供,這是一個(gè)NS_OPTIONS,就是可以同時(shí)用于好幾個(gè)值齿坷,常見(jiàn)的有read桂肌,write,notify永淌,indicate崎场,知知道這幾個(gè)基本就夠用了,前連個(gè)是讀寫(xiě)權(quán)限遂蛀,后兩個(gè)都是通知谭跨,兩種不同的通知方式。
        /*
         typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
         CBCharacteristicPropertyBroadcast                                              = 0x01,
         CBCharacteristicPropertyRead                                                   = 0x02,
         CBCharacteristicPropertyWriteWithoutResponse                                   = 0x04,
         CBCharacteristicPropertyWrite                                                  = 0x08,
         CBCharacteristicPropertyNotify                                                 = 0x10,
         CBCharacteristicPropertyIndicate                                               = 0x20,
         CBCharacteristicPropertyAuthenticatedSignedWrites                              = 0x40,
         CBCharacteristicPropertyExtendedProperties                                     = 0x80,
         CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)        = 0x100,
         CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)  = 0x200
         };

         */
        NSLog(@"%lu", (unsigned long)characteristic.properties);


        //只有 characteristic.properties 有write的權(quán)限才可以寫(xiě)
        if(characteristic.properties & CBCharacteristicPropertyWrite){
            /*
                最好一個(gè)type參數(shù)可以為CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,區(qū)別是是否會(huì)有反饋
            */
            [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
        }else{
            NSLog(@"該字段不可寫(xiě)李滴!");
        }


    }

6 訂閱Characteristic的通知

 //設(shè)置通知
    -(void)notifyCharacteristic:(CBPeripheral *)peripheral
                characteristic:(CBCharacteristic *)characteristic{
        //設(shè)置通知螃宙,數(shù)據(jù)通知會(huì)進(jìn)入:didUpdateValueForCharacteristic方法
        [peripheral setNotifyValue:YES forCharacteristic:characteristic];

    }

    //取消通知
    -(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral
                 characteristic:(CBCharacteristic *)characteristic{

         [peripheral setNotifyValue:NO forCharacteristic:characteristic];
    }

7 斷開(kāi)連接(disconnect)

//停止掃描并斷開(kāi)連接
    -(void)disconnectPeripheral:(CBCentralManager *)centralManager
                     peripheral:(CBPeripheral *)peripheral{
        //停止掃描
        [centralManager stopScan];
        //斷開(kāi)連接
        [centralManager cancelPeripheralConnection:peripheral];
    }

8 模擬器藍(lán)牙調(diào)試,慎用悬嗓,最好還是用真機(jī)去調(diào)試污呼。

 由于在iPhone 4s之后的iOS才支持BLE,新一代的這些iOS設(shè)備又都不便宜包竹,在做測(cè)試的時(shí)候燕酷,用iOS模擬器進(jìn)行調(diào)試籍凝,可以節(jié)約一些開(kāi)發(fā)成本。怎么在iOS模擬器上調(diào)試BLE苗缩,
    蘋(píng)果最初給出的說(shuō)明是饵蒂,支持BLE的mac機(jī)子上可以用模擬器進(jìn)行調(diào)試,并給出了一份技術(shù)文檔(傳送門(mén))酱讶,惡心的是退盯,后來(lái)蘋(píng)果抽風(fēng),又把這份文檔移除泻肯,
    并且把iOS 7.0的模擬器上對(duì)BLE的支持也移除掉了(難道是想讓大家多買設(shè)備測(cè)試渊迁?Apple sucks.)后面,網(wǎng)上搜了一下灶挟,解決辦法如下:
    1. 買一個(gè)CSR藍(lán)牙4.0 USB適配器(某寶上大概30塊錢(qián))琉朽,在機(jī)子上插入該物(你懂的)
    2. 在Terminal下敲入sudo nvram bluetoothHostControllerSwitchBehavior="never" , 重啟Mac稚铣。
    3. 用XCode 4.6調(diào)試代碼箱叁,在iOS 6.1的模擬器上跑程序(用XCode 5.0跑iOS 7.0模擬器會(huì)拋異常,原因上面詳訴過(guò)了惕医,Apple sucks耕漱,你懂的)

    如何降低模擬器的IOS版本呢?
    XCode->Preferences->Downloads里面有很多simulators你可以下載
    選擇個(gè)6.1的下載好了

demo地址是:https://github.com/coolnameismy/demo
點(diǎn)擊跳轉(zhuǎn)代碼下載地址,本文代碼存放目錄是BleDemo

三:ios藍(lán)牙開(kāi)發(fā)app作為外設(shè)被連接的實(shí)現(xiàn)

再上一節(jié)說(shuō)了app作為central連接peripheral的情況抬伺,這一節(jié)介紹如何使用app發(fā)布一個(gè)peripheral螟够,給其他的central連接

peripheral模式的流程

  1. 打開(kāi)peripheralManager,設(shè)置peripheralManager的委托
  1. 創(chuàng)建characteristics沛简,characteristics的description 創(chuàng)建service齐鲤,把characteristics添加到service中,再把service添加到peripheralManager中
  2. 開(kāi)啟廣播advertising
  3. 對(duì)central的操作進(jìn)行響應(yīng)
    • 4.1 讀characteristics請(qǐng)求
    • 4.2 寫(xiě)characteristics請(qǐng)求
    • 4.4 訂閱和取消訂閱characteristics

準(zhǔn)備環(huán)境

1 xcode
2 開(kāi)發(fā)證書(shū)和手機(jī)(藍(lán)牙程序需要使用使用真機(jī)調(diào)試椒楣,使用模擬器也可以調(diào)試给郊,但是方法很蛋疼,我會(huì)放在最后說(shuō))捧灰,如果不行可以使用osx程序調(diào)試
3 藍(lán)牙外設(shè)

實(shí)現(xiàn)步驟

  1. 打開(kāi)peripheralManager淆九,設(shè)置peripheralManager的委托

設(shè)置當(dāng)前ViewController實(shí)現(xiàn)CBPeripheralManagerDelegate委托

 @interface BePeripheralViewController : UIViewController<CBPeripheralManagerDelegate>

初始化peripheralManager

 /*
     和CBCentralManager類似,藍(lán)牙設(shè)備打開(kāi)需要一定時(shí)間毛俏,打開(kāi)成功后會(huì)進(jìn)入委托方法
     - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
     模擬器永遠(yuǎn)也不會(huì)得CBPeripheralManagerStatePoweredOn狀態(tài)
     */
    peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];

2. 創(chuàng)建characteristics炭庙,characteristics的description ,創(chuàng)建service煌寇,把characteristics添加到service中焕蹄,再把service添加到peripheralManager中

在委托方法 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral中,當(dāng)peripheral成功打開(kāi)后阀溶,才可以配置service和characteristics腻脏。 這里創(chuàng)建的service和chara對(duì)象是CBMutableCharacteristic和CBMutableService鸦泳。他們的區(qū)別就像NSArray和NSMutableArray區(qū)別類似。 我們先創(chuàng)建characteristics和description永品,description是characteristics的描述做鹰,描述分很多種, 這里不細(xì)說(shuō)了鼎姐,常用的就是CBUUIDCharacteristicUserDescriptionString钾麸。

//peripheralManager狀態(tài)改變
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    switch (peripheral.state) {
            //在這里判斷藍(lán)牙設(shè)別的狀態(tài)  當(dāng)開(kāi)啟了則可調(diào)用  setUp方法(自定義)
        case CBPeripheralManagerStatePoweredOn:
            NSLog(@"powered on");
            [info setText:[NSString stringWithFormat:@"設(shè)備名%@已經(jīng)打開(kāi),可以使用center進(jìn)行連接",LocalNameKey]];
            [self setUp];
            break;
        case CBPeripheralManagerStatePoweredOff:
            NSLog(@"powered off");
            [info setText:@"powered off"];
            break;

        default:
            break;
    }
}

//配置bluetooch的
 -(void)setUp{

        //characteristics字段描述
        CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];

        /*
         可以通知的Characteristic
         properties:CBCharacteristicPropertyNotify
         permissions CBAttributePermissionsReadable
         */
        CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];

        /*
         可讀寫(xiě)的characteristics
         properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
         permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
         */
        CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
        //設(shè)置description
        CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];
        [readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];


        /*
         只讀的Characteristic
         properties:CBCharacteristicPropertyRead
         permissions CBAttributePermissionsReadable
         */
        CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];


        //service1初始化并加入兩個(gè)characteristics
        CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
        [service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];

        //service2初始化并加入一個(gè)characteristics
        CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
        [service2 setCharacteristics:@[readCharacteristic]];

        //添加后就會(huì)調(diào)用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
        [peripheralManager addService:service1];
        [peripheralManager addService:service2];
 }

3. 開(kāi)啟廣播advertising

//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    if (error == nil) {
        serviceNum++;
    }

    //因?yàn)槲覀兲砑恿?個(gè)服務(wù)炕桨,所以想兩次都添加完成后才去發(fā)送廣播
    if (serviceNum==2) {
        //添加服務(wù)后可以在此向外界發(fā)出通告 調(diào)用完這個(gè)方法后會(huì)調(diào)用代理的
        //(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
        [peripheralManager startAdvertising:@{
                                              CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
                                              CBAdvertisementDataLocalNameKey : LocalNameKey
                                             }
         ];

    }

}

//peripheral開(kāi)始發(fā)送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
    NSLog(@"in peripheralManagerDidStartAdvertisiong");
}

4. 對(duì)central的操作進(jìn)行響應(yīng)

  • 4.1 讀characteristics請(qǐng)求
  • 4.2 寫(xiě)characteristics請(qǐng)求
  • 4.3 訂閱和取消訂閱characteristics
//訂閱characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
    NSLog(@"訂閱了 %@的數(shù)據(jù)",characteristic.UUID);
    //每秒執(zhí)行一次給主設(shè)備發(fā)送一個(gè)當(dāng)前時(shí)間的秒數(shù)
    timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic  repeats:YES];
}

//取消訂閱characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
    NSLog(@"取消訂閱 %@的數(shù)據(jù)",characteristic.UUID);
    //取消回應(yīng)
    [timer invalidate];
}

//發(fā)送數(shù)據(jù)饭尝,發(fā)送當(dāng)前時(shí)間的秒數(shù)
-(BOOL)sendData:(NSTimer *)t {
    CBMutableCharacteristic *characteristic = t.userInfo;
    NSDateFormatter *dft = [[NSDateFormatter alloc]init];
    [dft setDateFormat:@"ss"];
    NSLog(@"%@",[dft stringFromDate:[NSDate date]]);

    //執(zhí)行回應(yīng)Central通知數(shù)據(jù)
    return  [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];

}


//讀characteristics請(qǐng)求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    NSLog(@"didReceiveReadRequest");
    //判斷是否有讀數(shù)據(jù)的權(quán)限
    if (request.characteristic.properties & CBCharacteristicPropertyRead) {
        NSData *data = request.characteristic.value;
        [request setValue:data];
        //對(duì)請(qǐng)求作出成功響應(yīng)
        [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}


//寫(xiě)characteristics請(qǐng)求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
    NSLog(@"didReceiveWriteRequests");
    CBATTRequest *request = requests[0];

    //判斷是否有寫(xiě)數(shù)據(jù)的權(quán)限
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
        //需要轉(zhuǎn)換成CBMutableCharacteristic對(duì)象才能進(jìn)行寫(xiě)值
        CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
        c.value = request.value;
        [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }


}

地址是:https://github.com/coolnameismy/demo
點(diǎn)擊跳轉(zhuǎn)代碼下載地址
本文代碼存放目錄是BleDemo

(四)ios藍(lán)牙開(kāi)發(fā)BabyBluetooth藍(lán)牙庫(kù)介紹

BabyBluetooth 是一個(gè)最簡(jiǎn)單易用的藍(lán)牙庫(kù)献宫,基于CoreBluetooth的封裝芋肠,并兼容ios和mac OS X。

特色:

基于原生CoreBluetooth框架封裝的輕量級(jí)的開(kāi)源庫(kù)遵蚜,可以幫你更簡(jiǎn)單地使用CoreBluetooth API。
CoreBluetooth所有方法都是通過(guò)委托完成奈惑,代碼冗余且順序凌亂吭净。BabyBluetooth使用block方法,可以重新按照功能和順序組織代碼肴甸,并提供許多方法減少藍(lán)牙開(kāi)發(fā)過(guò)程中的代碼量寂殉。
鏈?zhǔn)椒椒w,代碼更簡(jiǎn)潔原在、優(yōu)雅友扰。
通過(guò)channel切換區(qū)分委托調(diào)用,并方便切換

來(lái)源

最近幾個(gè)月都在做藍(lán)牙項(xiàng)目庶柿,用CoreBluetooch感覺(jué)語(yǔ)句寫(xiě)的到處都是村怪,不優(yōu)雅。一整條鏈下來(lái)要近10幾個(gè)委托方法浮庐,并且不斷的在委托方法中調(diào)用方法再進(jìn)入其他的委托甚负,導(dǎo)致 代碼很零散。因此我就想讓coreBluetooth使用更簡(jiǎn)單审残,語(yǔ)法更優(yōu)雅梭域,所以開(kāi)始寫(xiě)這個(gè)BabyBluetooch藍(lán)牙庫(kù)。

Quick Example

//導(dǎo)入.h文件和系統(tǒng)藍(lán)牙庫(kù)的頭文件
#import "BabyBluetooth.h"

-(void)viewDidLoad {
    [super viewDidLoad];

   //初始化BabyBluetooth 藍(lán)牙庫(kù)
    baby = [BabyBluetooth shareBabyBluetooth];
     //設(shè)置藍(lán)牙委托
     [self babyDelegate];
     //設(shè)置委托后直接可以使用搅轿,無(wú)需等待CBCentralManagerStatePoweredOn狀態(tài)
     baby.scanForPeripherals().begin()
}

//藍(lán)牙網(wǎng)關(guān)初始化和委托方法設(shè)置
-(void)babyDelegate{

    //設(shè)置掃描到設(shè)備的委托
    [baby setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
        NSLog(@"搜索到了設(shè)備:%@",peripheral.name);
    }];
    //設(shè)置設(shè)備連接成功的委托
    [baby setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {
        NSLog(@"設(shè)備:%@--連接成功",peripheral.name);
    }];
    //設(shè)置發(fā)現(xiàn)設(shè)備的Services的委托
    [baby setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {
        for (CBService *service in peripheral.services) {
            NSLog(@"搜索到服務(wù):%@",service.UUID.UUIDString);
        }
    }];
    //設(shè)置發(fā)現(xiàn)設(shè)service的Characteristics的委托
    [baby setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        NSLog(@"===service name:%@",service.UUID);
        for (CBCharacteristic *c in service.characteristics) {
            NSLog(@"charateristic name is :%@",c.UUID);
        }
    }];
    //設(shè)置讀取characteristics的委托
    [baby setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
        NSLog(@"characteristic name:%@ value is:%@",characteristics.UUID,characteristics.value);
    }];
    //設(shè)置發(fā)現(xiàn)characteristics的descriptors的委托
    [baby setBlockOnDiscoverDescriptorsForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        NSLog(@"===characteristic name:%@",characteristic.service.UUID);
        for (CBDescriptor *d in characteristic.descriptors) {
            NSLog(@"CBDescriptor name is :%@",d.UUID);
        }
    }];
    //設(shè)置讀取Descriptor的委托
    [baby setBlockOnReadValueForDescriptors:^(CBPeripheral *peripheral, CBDescriptor *descriptor, NSError *error) {
        NSLog(@"Descriptor name:%@ value is:%@",descriptor.characteristic.UUID, descriptor.value);
    }];

    //過(guò)濾器
    //設(shè)置查找設(shè)備的過(guò)濾器
    [baby setDiscoverPeripheralsFilter:^BOOL(NSString *peripheralsFilter) {
        //設(shè)置查找規(guī)則是名稱大于1 病涨, the search rule is peripheral.name length > 1
        if (peripheralsFilter.length >1) {
            return YES;
        }
        return NO;
    }];

    //設(shè)置連接的設(shè)備的過(guò)濾器
     __block BOOL isFirst = YES;
    [baby setFilterOnConnetToPeripherals:^BOOL(NSString *peripheralName) {
        //這里的規(guī)則是:連接第一個(gè)AAA打頭的設(shè)備
        if(isFirst && [peripheralName hasPrefix:@"AAA"]){
            isFirst = NO;
            return YES;
        }
        return NO;
    }];


}

CoreBluetooch中實(shí)現(xiàn)上掃描,連接璧坟,發(fā)現(xiàn)服務(wù)和characteristic以及它的值相關(guān)方法調(diào)用是很麻煩啰嗦凌亂的既穆。如下: centralManager啟動(dòng)->狀態(tài)委托->調(diào)用掃描方法->進(jìn)入掃描到設(shè)備的委托->調(diào)用連接設(shè)備方法->進(jìn)入連接到設(shè)備的委托->發(fā)現(xiàn)服務(wù)方法->發(fā)現(xiàn)服務(wù)委托-> 發(fā)現(xiàn)characteristic方法->發(fā)現(xiàn)characteristic委托->讀characteristic的value->讀characteristic的value的委托->讀description赎懦,讀description的value-> ….的委托

而B(niǎo)abyBluetooth只需要一句話就執(zhí)行了上面的內(nèi)容。

//掃描設(shè)備 然后讀取服務(wù),然后讀取characteristics名稱和值和屬性循衰,獲取characteristics對(duì)應(yīng)的description的名稱和值
  baby.scanForPeripherals().connectToPeripheral().discoverServices()
  .discoverCharacteristics().readValueForCharacteristic().discoverDescriptorsForCharacteristic()
  .readValueForDescriptors().begin();

另一方面铲敛,BabyBluetooth所有的委托方法都緊湊的聚在了一起。此外会钝,快速示例中沒(méi)有包括channel的使用伐蒋,如果包括了channel,那么ios幾個(gè)頁(yè)面或者組件的藍(lán)牙 調(diào)用模塊都可以寫(xiě)在一起迁酸,看起來(lái)就覺(jué)得很方便先鱼。

轉(zhuǎn)發(fā)自:http://liuyanwei.jumppo.com/2015/08/14/ios-BLE-2.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奸鬓,隨后出現(xiàn)的幾起案子焙畔,更是在濱河造成了極大的恐慌,老刑警劉巖串远,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宏多,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡澡罚,警方通過(guò)查閱死者的電腦和手機(jī)伸但,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)留搔,“玉大人更胖,你說(shuō)我怎么就攤上這事「粝裕” “怎么了却妨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)括眠。 經(jīng)常有香客問(wèn)我彪标,道長(zhǎng),這世上最難降的妖魔是什么掷豺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任捐下,我火速辦了婚禮,結(jié)果婚禮上萌业,老公的妹妹穿的比我還像新娘坷襟。我一直安慰自己,他們只是感情好生年,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布婴程。 她就那樣靜靜地躺著,像睡著了一般抱婉。 火紅的嫁衣襯著肌膚如雪档叔。 梳的紋絲不亂的頭發(fā)上桌粉,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音衙四,去河邊找鬼铃肯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛传蹈,可吹牛的內(nèi)容都是我干的押逼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惦界,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼挑格!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起沾歪,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤漂彤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后灾搏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挫望,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年狂窑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了士骤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕾域,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出到旦,到底是詐尸還是另有隱情旨巷,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布添忘,位于F島的核電站采呐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏搁骑。R本人自食惡果不足惜斧吐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仲器。 院中可真熱鬧煤率,春花似錦、人聲如沸乏冀。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辆沦。三九已至昼捍,卻和暖如春识虚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妒茬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工担锤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乍钻。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓肛循,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親团赁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子育拨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容