# S4SensorKit说明文档

本套SDK仅支持Minew公司出品的蓝牙Sensor设备,目前是工业温湿度传感器。通过SDK可以帮助开发者处理手机和Sensor之间的一切工作,包括:扫描设备,连接设备,向设备写入数据,从设备接收数据等。

# 前期工作

整体框架:MTICentralManager为设备管理类,在APP运行时始终是单例。MTIPeripheral是设备实例类,此套件会为每一个设备生成一个MTIPeripheral实例以便于对监听设备和操作设备。

MTICentralManager:设备管理类,可以扫描周围的Sensor设备,并且可以连接它们,校验它们等。

MTIPeripheral:设备实例类,当MTICentralManager发现一个物理设备时,MTICentralManager会生成一个MTIPeripheral实例,这个实例就对应一个物理设备。

MTIFrameHandler:设备广播类,可以获取设备广播时的数据。

MTConnectionHandler:设备连接类,进行设备的接发数据。

MTISensorHandler:传感器操作类,进行设备的读写数据。

# 开始上手

# 开发环境:

  • Xcode10+,当前SDK使用Xcode15编译,请使用Xcode10及以上版本进行开发;
  • iOS12.0,限制最低系统版本为iOS12;

# 导入到工程:

  1. 手动导入

    • 将开发套件的文件:MTIndustrialSensorKit.framework文件拷贝到项目工程目录下,然后添加到项目中。

PS:

  1. !!!在iOS10及以上版本,苹果对蓝牙APi添加了权限限制,你需要在工程的info.plist文件里添加一项字符串:Privacy - Bluetooth Peripheral Usage Description - "你的使用描述"。
  2. !!!在iOS13及以上版本,苹果对蓝牙APi添加了权限限制,你需要在工程的info.plist文件里添加一项字符串:Privacy - Bluetooth Always Usage Description - "你的使用描述"。

# 开始开发

# 扫描设备

首先需要获取到MTICentralManager的单例,然后检查手机当前的蓝牙状态,接着就可以进行设备扫描了。

// 获取Manager单例
MTICentralManager *manager = [MTICentralManager sharedInstance];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 手机端当前的蓝牙开关状态
    if(self->manager.status == PowerStatePoweredOn) {
        // 开始进行设备扫描
        [manager startScan:^(NSArray<MTIPeripheral *> *peripherals) {
          //可根据broadcast属性的type筛选所需要的传感器种类
        	self->deviceAry = peripherals;
    	}];
    }
});
//扫描到的设备也可以使用manager.scannedPeris获得

// 如果你需要对手机的蓝牙状态作出响应。请监听回调。
[manager didChangesBluetoothStatus:^(PowerState status){
    
    switch(status) {
        case PowerStatePoweredOn:
            NSLog(@"bluetooth status change to poweron");
            break;
        case PowerStatePoweredOff:
            NSLog(@"bluetooth status change to poweroff");
            break;
        case PowerStateUnknown:
            NSLog(@"bluetooth status change to unknown");
    }
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

PS: 只有手机蓝牙状态处于Poweron时,整个SDK才能够正常工作。

# 连接到设备

// 从上一步能够获取到扫描到的设备
MTIPeripheral *device = deviceAry[0];
//连接设备
[manager connectToPeriperal:device SecretKey:@"Device's secretKey"];
// 监听设备连接状态。
[device.connector didChangeConnection:^(Connection connection) {
    if (connection == Vaildated) {
      	//验证成功,成功连接设备
        NSLog(@"vaildated");
      	//需要写入密码,密码验证成功后进行其他的操作
    }
    if (connection == Disconnected) {
        NSLog(@"device has disconnected.");
    }
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 帧类型


    MTIFrame *frame = device.frameHandler.advFrames[0];
    if (frame.frameType == MTIFrameDeviceInfo) {
            MTIDeviceInfoFrame *deviceInfoFrame = (MTIDeviceInfoFrame *)frame;
        
        } else if (frame.frameType == MTSModelTypeDoorContactByte) {  // 门磁状态帧
            
            MTIHeaderByteFrame *coordinateFrame = (MTIHeaderByteFrame *)frame;
            
            NSString *text1  = [NSString stringWithFormat:@"%@", tempHumiFrame.doorSensorAlarmStatus? @"打开" : @"关闭";
            NSString *text2 = tempHumiFrame.tamperProofAlarmStatus ? @"·防拆状态:01触发" : @"·防拆状态 :00正常";
            NSLog(@":%@\t%@", text1, text2);
            
        }
    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 其他指令操作

接上一步,当设备成功验证写入的密钥正确后,方可进行其他指令操作。

以下操作不分先后顺序,可自行选择。

# 恢复蓝牙出厂设置
    /// 恢复蓝牙出厂设置
    [device.connector.sensorHandler resetDevice:^(BOOL success) {
        
        if (success){
            // 成功后断开蓝牙
            [[MTICentralManager sharedInstance] disconnectFromPeriperal:self->device];
        }else{
        
            NSLog(@"failed");
        }
    }];
    
1
2
3
4
5
6
7
8
9
10
11
12
# 获取设备信息帧的广播参数
    /// 获取设备信息帧的广播参数
    /// @param completionHandler 结果回调
    [device.connector.sensorHandler getDeviceInfoFrameAdvertisingParameters:^(BOOL success, MTIAdvertisingParametersData * _Nonnull data) {

        if (success) {
            NSLog(@"广播间隔:%ld, 广播功率:%d", data.advertisingInterval, data.txPower);
        }
    }];
1
2
3
4
5
6
7
8
# 设置设备信息帧的广播参数

请注意,广播功率的值只能设置 -40, -20, -16, -12, -8, -4, 0, 4。

    int interval = 100; // unit is ms.
    int txpower = 4;
    
    /// 设置设备信息帧的广播参数
    /// @param advertisingInterval 广播间隔,单位为毫秒
    /// @param txPower -40 -20 -16 -12 -8 -4 0 4dBm
    /// @param completionHandler 结果回调
    [device.connector.sensorHandler setDeviceInfoFrameAdvertisingParametersConfiguration:interval TxPower:txpower CompletionHandle:^(BOOL success) {
        if (success) {
            NSLog(@"设置设备信息帧配置参数:成功");
        } else {
            NSLog(@"设置设备信息帧配置参数:失败");
        }
    }];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 获取模式参数配置
/// Get doorControl sensor's configuration
/// @param completionHandler the receive block.

    [device.connector.sensorHandler getIndustryHTFrameAdvertisingParameters:^(BOOL success, MTIAdvertisingParametersData *data) {
        
        
        if (success) {
                NSLog(@"Advertising type:\t%@", data.isAlwaysAdvertising ? @"常规模式" : @"省电模式");
                
            NSLog(@"广播间隔:%ld, 广播功率:%d", data.advertisingInterval, data.txPower);
        }
    }];

1
2
3
4
5
6
7
8
9
10
11
12
13
/// Get Energy-saving Mode sensor's configuration
/// @param completionHandler the receive block.

    [device.connector.sensorHandler getDoorcontrolTriggerConfiguration:^(BOOL success, MTIAdvertisingParametersData *data) {
        
        
        if (success) {
            
            NSLog(@"广播间隔:%ld, 广播功率:%d,持续广播:%@", data.txPower, data.advertisingInterval, data.timeValues);
        }
    }];

1
2
3
4
5
6
7
8
9
10
11
12
# 设置常规模式参数配置
/// Set Regular Mode sensor configuration
/// @param completionHandler the receive block.
    [device.connector.sensorHandler  setDoorcontrolFrameAdvertisingParametersConfiguration:interval TxPower:txpower alwaysD:self.isAlwaysAdvertising CompletionHandle:^(BOOL success) {
        if (success) {
          // "设置成功"
        } else {
          //  "设置失败"
        }
    }];

1
2
3
4
5
6
7
8
9
10
# 设置省电模式参数配置
/// Set Energy-saving sensor configuration
/// @param completionHandler the receive block.

// note:要先切换到省电模式!
    self.isAlwaysAdvertising = false;
    
    [device.connector.sensorHandler  setDoorcontrolFrameAdvertisingParametersConfiguration:interval TxPower:txpower alwaysD: CompletionHandle:^(BOOL success) {
        if (success) {
          // "设置成功"
          
              [device.connector.sensorHandler setDoorContactHTFrameAdvertisingParametersConfiguration:interval TxPower:txpower TimeValue:timeValue CompletionHandle:^(BOOL success) {
            
                    if (success) {
                      // "设置成功"
                    } else {
                      //  "设置失败"
                    }
            
             }];
          
        } else {
          //  "设置失败"
        }
    }];



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 存储开关 && 开门灯效 获取

[device.connector.sensorHandler getDoorContactOptionSwitch:^(BOOL optionSwitch1, BOOL optionSwitch2) {

    NSLog(@"存储开关:\t%d\t开门灯效:\t%d",data.isAlwaysAdvertising ? optionSwitch1, optionSwitch2);
    
 }];

1
2
3
4
5
6
7
# 存储开关 设置

[device.connector.sensorHandler setDoorContactDataOptionSwitch:selectOrNo withReceiceResultHandler:^(BOOL success){

         if (success) {
          // "设置成功"
        } else {
          //  "设置失败"
        }
        
 }];
 
1
2
3
4
5
6
7
8
9
10
11
# 开门灯效 设置
 [device.connector.sensorHandler setDoorContactOptionSwitch:selectOrNo withReceiceResultHandler:^(BOOL success){

         if (success) {
          // "设置成功"
        } else {
          //  "设置失败"
        }
    
 }];

1
2
3
4
5
6
7
8
9
10
# 查询设备在一天中保持正常工作的时间段
    [device.connector.sensorHandler getWorkPeriodsConfiguration:^(BOOL success, NSInteger totalNum, NSArray<MTIDoorContactTimeListData *> * _Nonnull data) {
 
        if (success) {
        }
        
    }];

1
2
3
4
5
6
7
# 设置设备在一天中保持正常工作的时间段

NSMutableArray<MTIDoorContactTimeListData *> *modelArray = [NSMutableArray array];
 [device.connector.sensorHandler setWorkPeriodsConfiguration:[modelArray mutableCopy] withHandlerCompletion:^(BOOL success) {
        
        if (success) {
            

        }

    }];

1
2
3
4
5
6
7
8
9
10
11
# 按时间段获取历史数据
      
      [device.connector.sensorHandler readDoorContactHistoryWithDateFormatter:formatter Unit:MTICelsius StartTimeInterval:timeStamp EndTimeInterval:endtimeStamp SystemTimeInterval:[[NSDate new] timeIntervalSince1970] DeviceHTModelType:1 completionHandler:^(BOOL success, NSInteger totalNum, NSArray<MTIDoorContactTimeListData *> * _Nonnull data) {
            
        if (success) {
        
          NSLog(@"%ld\t\t%@",totalNum,data);
        } 
          
    }];

1
2
3
4
5
6
7
8
9
10
# 固件升级
# 固件升级

请注意,需要使用匹配的固件升级包,否则可能导致设备无法正常使用。

    // ⚠️注意:demo 附带的ota升级包未必与当前设备匹配,请根据自己手头设备的硬件及固件信息,跟业务沟通后,使用匹配的固件升级包进行 ota 升级。如果使用了错误的固件升级包,这可能会让你的设备无法正常使用。

   
    NSData *otaPacketData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"S3V2_NV900_20210312_s3_ota_v1_0_23" ofType:@".bin"]];
    

    /// 固件升级
    /// @param originOtaData 固件升级包数据
    /// @param stateHandler 监听升级状态的改变,“完成”表示升级成功。
    /// @param progressHandler 监听升级包发送进度
    /// @param errorHandler 监听升级失败原因
    [device.connector startOTAWithOTAData:otaPacketData stateHandler:^(OTAState state) {
        if (state == OTAStateStarting) {
            NSLog(@"ota start");
        } else if(state == OTAStateUploading) {
            NSLog(@"ota uploading");
        } else if(state == OTAStateAborted) {
            NSLog(@"ota aborted");
        } else if(state == OTAStateDisconnecting) {
            NSLog(@"ota disconnect");
        } else if (state == OTAStateCompleted) {
            NSLog(@"ota successfully");
        } else if(state == OTAStateFailed) {
            NSLog(@"ota failed");
        }
    } progressHandler:^(float progress) {
        NSLog(@"ota progress:%f", progress);
    } errorHandler:^(NSError * _Nonnull error) {
        NSLog(@"ota  failed,error:%@",error);
    }];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 注意事项

  • 同时写入多条指令可能会导致异常,建议每条指令完成后,再写入新的指令。

# 文档版本记录

  • 2024.09.23 v1.0 初版;
上次更新:: 2024/9/24 10:34:25