瀏覽代碼

增加: 带屏食物探针盒 多探针盒子

tags/1.10.0^0
larryzhang 7 月之前
父節點
當前提交
828ef1f59d
共有 50 個檔案被更改,包括 3155 行新增29 行删除
  1. 108
    4
      AILinkBleSDKDemo.xcodeproj/project.pbxproj
  2. 二進制
      AILinkBleSDKDemo/AILinkBleSDK.framework/AILinkBleSDK
  3. 1
    1
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/AILinkBleSDK_Version.h
  4. 2
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyFatScaleBleHeader.h
  5. 4
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyFatScaleBleManager.h
  6. 31
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyIndexAlgorithmModel.h
  7. 2
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBroadcastScaleBleHeader.h
  8. 9
    1
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELEightScaleBleManager.h
  9. 9
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELEightScaleSDKHeader.h
  10. 1
    1
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELExSupportDeviceType.h
  11. 9
    0
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELHygrothermographBleHeader.h
  12. 12
    1
      AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELHygrothermographBleManager.h
  13. 二進制
      AILinkBleSDKDemo/AILinkBleSDK.framework/Info.plist
  14. 二進制
      AILinkBleSDKDemo/AILinkBleSDK.framework/PrivacyInfo.bundle/Info.plist
  15. 22
    0
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/Contents.json
  16. 二進制
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/aibbq_add_equipment_probe_100_ic@2x.png
  17. 二進制
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/aibbq_add_equipment_probe_100_ic@3x.png
  18. 22
    0
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/Contents.json
  19. 二進制
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/ailink_cid85_banding_cid_0062_100_ic@2x.png
  20. 二進制
      AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/ailink_cid85_banding_cid_0062_100_ic@3x.png
  21. 71
    0
      AILinkBleSDKDemo/DeviceDemo/26_MeatProbe/ELMeatProbeCustomDataModel.h
  22. 154
    0
      AILinkBleSDKDemo/DeviceDemo/26_MeatProbe/ELMeatProbeCustomDataModel.m
  23. 62
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleBBQParamModel.h
  24. 130
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleBBQParamModel.m
  25. 32
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleCmdTool.h
  26. 59
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleCmdTool.m
  27. 35
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleHeader.h
  28. 114
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleParser.h
  29. 297
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleParser.m
  30. 21
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.h
  31. 312
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.m
  32. 133
    0
      AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.xib
  33. 60
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleBBQParamModel.h
  34. 128
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleBBQParamModel.m
  35. 32
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleCmdTool.h
  36. 62
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleCmdTool.m
  37. 36
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleHeader.h
  38. 128
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleParser.h
  39. 325
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleParser.m
  40. 21
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.h
  41. 334
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.m
  42. 133
    0
      AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.xib
  43. 71
    0
      AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/Ble/ELMeatProbeBoxCustomDataModel.h
  44. 154
    0
      AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/Ble/ELMeatProbeBoxCustomDataModel.m
  45. 2
    10
      AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/MeatProbeBoxVC.m
  46. 10
    10
      AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/MeatProbeBoxVC.xib
  47. 5
    0
      AILinkBleSDKDemo/Others/ViewController.m
  48. 二進制
      AILinkBleSDKDemo/PrivacyInfo.bundle/Info.plist
  49. 1
    1
      AILinkBleSDKDemo/Tools/ELSelectView.m
  50. 1
    0
      README.md

+ 108
- 4
AILinkBleSDKDemo.xcodeproj/project.pbxproj 查看文件

@@ -7,6 +7,18 @@
objects = {

/* Begin PBXBuildFile section */
7802C5AD2C903A31008D7616 /* ELMeatProbeCustomDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */; };
7802C5B02C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */; };
7802C5C92C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */; };
7802C5CA2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */; };
7802C5CB2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */; };
7802C5CC2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */; };
7802C5CD2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */; };
7802C5CE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */; };
7802C5CF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */; };
7802C5D02C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */; };
7802C5D12C903A7F008D7616 /* MeatProbeScreenBoxVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */; };
7802C5D22C903A7F008D7616 /* MeatProbeScreenBoxVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */; };
78350BB329D2826900456695 /* libHTBodyfat_SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB129D2826900456695 /* libHTBodyfat_SDK.a */; };
78350BB729D282A300456695 /* libBhBodyComposition120.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB429D282A300456695 /* libBhBodyComposition120.a */; };
78350BBB29D2869000456695 /* BodyScaleVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78350BB929D2869000456695 /* BodyScaleVC.m */; };
@@ -120,6 +132,30 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
7802C5AB2C903A31008D7616 /* ELMeatProbeCustomDataModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeCustomDataModel.h; sourceTree = "<group>"; };
7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeCustomDataModel.m; sourceTree = "<group>"; };
7802C5AE2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxCustomDataModel.h; sourceTree = "<group>"; };
7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxCustomDataModel.m; sourceTree = "<group>"; };
7802C5B12C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleBBQParamModel.h; sourceTree = "<group>"; };
7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleBBQParamModel.m; sourceTree = "<group>"; };
7802C5B32C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleCmdTool.h; sourceTree = "<group>"; };
7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleCmdTool.m; sourceTree = "<group>"; };
7802C5B52C903A7F008D7616 /* ELMeatProbeBoxMultipleBleHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleHeader.h; sourceTree = "<group>"; };
7802C5B62C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleParser.h; sourceTree = "<group>"; };
7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleParser.m; sourceTree = "<group>"; };
7802C5B92C903A7F008D7616 /* MultipleMeatProbeBoxVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultipleMeatProbeBoxVC.h; sourceTree = "<group>"; };
7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultipleMeatProbeBoxVC.m; sourceTree = "<group>"; };
7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MultipleMeatProbeBoxVC.xib; sourceTree = "<group>"; };
7802C5BD2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleBBQParamModel.h; sourceTree = "<group>"; };
7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleBBQParamModel.m; sourceTree = "<group>"; };
7802C5BF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleCmdTool.h; sourceTree = "<group>"; };
7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleCmdTool.m; sourceTree = "<group>"; };
7802C5C12C903A7F008D7616 /* ELMeatProbeScreenBoxBleHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleHeader.h; sourceTree = "<group>"; };
7802C5C22C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleParser.h; sourceTree = "<group>"; };
7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleParser.m; sourceTree = "<group>"; };
7802C5C52C903A7F008D7616 /* MeatProbeScreenBoxVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeatProbeScreenBoxVC.h; sourceTree = "<group>"; };
7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeatProbeScreenBoxVC.m; sourceTree = "<group>"; };
7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MeatProbeScreenBoxVC.xib; sourceTree = "<group>"; };
78350BB129D2826900456695 /* libHTBodyfat_SDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libHTBodyfat_SDK.a; sourceTree = "<group>"; };
78350BB229D2826900456695 /* HTBodyfat_SDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTBodyfat_SDK.h; sourceTree = "<group>"; };
78350BB429D282A300456695 /* libBhBodyComposition120.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libBhBodyComposition120.a; sourceTree = "<group>"; };
@@ -337,6 +373,56 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
7802C5B82C903A7F008D7616 /* BLE */ = {
isa = PBXGroup;
children = (
7802C5B12C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.h */,
7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */,
7802C5B32C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.h */,
7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */,
7802C5B52C903A7F008D7616 /* ELMeatProbeBoxMultipleBleHeader.h */,
7802C5B62C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.h */,
7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */,
);
path = BLE;
sourceTree = "<group>";
};
7802C5BC2C903A7F008D7616 /* 27C_MultipleMeatProbeBox */ = {
isa = PBXGroup;
children = (
7802C5B82C903A7F008D7616 /* BLE */,
7802C5B92C903A7F008D7616 /* MultipleMeatProbeBoxVC.h */,
7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */,
7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */,
);
path = 27C_MultipleMeatProbeBox;
sourceTree = "<group>";
};
7802C5C42C903A7F008D7616 /* BLE */ = {
isa = PBXGroup;
children = (
7802C5BD2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.h */,
7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */,
7802C5BF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.h */,
7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */,
7802C5C12C903A7F008D7616 /* ELMeatProbeScreenBoxBleHeader.h */,
7802C5C22C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.h */,
7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */,
);
path = BLE;
sourceTree = "<group>";
};
7802C5C82C903A7F008D7616 /* 27B_MeatProbeScreenBox */ = {
isa = PBXGroup;
children = (
7802C5C42C903A7F008D7616 /* BLE */,
7802C5C52C903A7F008D7616 /* MeatProbeScreenBoxVC.h */,
7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */,
7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */,
);
path = 27B_MeatProbeScreenBox;
sourceTree = "<group>";
};
78350BB029D280D300456695 /* DeviceDemo */ = {
isa = PBXGroup;
children = (
@@ -367,6 +453,8 @@
78DC4DF72990FC10006C6911 /* 25_HeightBodyFatScale */,
78E07B6C2A49664D00E68200 /* 26_MeatProbe */,
78E07B702A49664E00E68200 /* 27_MeatProbeBox */,
7802C5C82C903A7F008D7616 /* 27B_MeatProbeScreenBox */,
7802C5BC2C903A7F008D7616 /* 27C_MultipleMeatProbeBox */,
78CF43522C2EC21A00D0A165 /* 28_Hygrothermograph */,
);
path = DeviceDemo;
@@ -471,6 +559,8 @@
78E07B6F2A49664D00E68200 /* MeatProbeVC.h */,
78E07B6D2A49664D00E68200 /* MeatProbeVC.m */,
78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */,
7802C5AB2C903A31008D7616 /* ELMeatProbeCustomDataModel.h */,
7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */,
);
path = 26_MeatProbe;
sourceTree = "<group>";
@@ -494,6 +584,8 @@
78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */,
78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */,
78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */,
7802C5AE2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.h */,
7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */,
);
path = Ble;
sourceTree = "<group>";
@@ -977,8 +1069,10 @@
78DC4DE32990FB2E006C6911 /* ELDemoScanVC.xib in Resources */,
E622E26E243F1CA6003C319D /* LaunchScreen.storyboard in Resources */,
78CF43542C2EC21B00D0A165 /* HygrothermographVC.xib in Resources */,
7802C5CD2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib in Resources */,
E622E26B243F1CA6003C319D /* Assets.xcassets in Resources */,
78AC3B2B2C0D994900A62C61 /* PrivacyInfo.bundle in Resources */,
7802C5D22C903A7F008D7616 /* MeatProbeScreenBoxVC.xib in Resources */,
78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */,
78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */,
78350BBD29D28C1200456695 /* README.md in Resources */,
@@ -1005,8 +1099,10 @@
95565B7A2786EA3B000265EA /* SkipScanViewController.m in Sources */,
78DC4DFB2990FC10006C6911 /* HeightBodyFatScaleVC.m in Sources */,
9557263B276C38CD003BFBAC /* FaceMaskScanViewController.m in Sources */,
7802C5D02C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m in Sources */,
9557263F276C38CD003BFBAC /* EightScaleConnectionViewController.m in Sources */,
9557262B276C38CD003BFBAC /* WheelMonitorScanViewController.m in Sources */,
7802C5AD2C903A31008D7616 /* ELMeatProbeCustomDataModel.m in Sources */,
95572629276C38CD003BFBAC /* BloodSugarConnectionViewController.m in Sources */,
78DC4DE52990FB2E006C6911 /* ELDemoScanVC.m in Sources */,
9557262A276C38CD003BFBAC /* WheelMonitorConnectViewController.m in Sources */,
@@ -1021,6 +1117,7 @@
78DC4DF22990FB40006C6911 /* ELDeviceTypeCell.m in Sources */,
78DC4DF52990FB40006C6911 /* ELDeviceScanCell.m in Sources */,
78DC4DF12990FB40006C6911 /* ELDemoView.m in Sources */,
7802C5B02C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m in Sources */,
95572626276C38CD003BFBAC /* AiFreshNutritionScaleConnectionViewController.m in Sources */,
9557262F276C38CD003BFBAC /* ELBfsWifiConnectTableViewCell.m in Sources */,
95572623276C38CD003BFBAC /* BloodConnectViewController.m in Sources */,
@@ -1032,6 +1129,7 @@
958B4EB5263C08E800476BAE /* MASViewConstraint.m in Sources */,
95572628276C38CD003BFBAC /* BloodSugarScanViewController.m in Sources */,
9557264C276C38CD003BFBAC /* RemoteControlScanViewController.m in Sources */,
7802C5D12C903A7F008D7616 /* MeatProbeScreenBoxVC.m in Sources */,
9557262C276C38CD003BFBAC /* BroadcastNutritionScaleViewController.m in Sources */,
95572643276C38CD003BFBAC /* BodyFatScaleScanViewController.m in Sources */,
95572627276C38CD003BFBAC /* BroadcastScaleViewController.m in Sources */,
@@ -1049,23 +1147,29 @@
E622E271243F1CA6003C319D /* main.m in Sources */,
78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */,
95572646276C38CD003BFBAC /* FoodThermometerConnectionViewController.m in Sources */,
7802C5CE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m in Sources */,
958B4EB3263C08E800476BAE /* View+MASAdditions.m in Sources */,
958B4EB1263C08E800476BAE /* MASLayoutConstraint.m in Sources */,
9557263A276C38CD003BFBAC /* FaceMaskConnectionViewController.m in Sources */,
95572636276C38CD003BFBAC /* OximeterScanViewController.m in Sources */,
78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */,
958B4EA4263C08E800476BAE /* ELInputAlertView.m in Sources */,
7802C5CC2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m in Sources */,
958B4EB6263C08E800476BAE /* MASViewAttribute.m in Sources */,
958B4EBB263C08E800476BAE /* InheritConnectViewController.m in Sources */,
958B4EB4263C08E800476BAE /* MASConstraint.m in Sources */,
7802C5CA2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m in Sources */,
958B4EAA263C08E800476BAE /* AilinkSuperConnectViewController.m in Sources */,
958B4EB0263C08E800476BAE /* MASConstraintMaker.m in Sources */,
7802C5C92C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m in Sources */,
78DC4DF32990FB40006C6911 /* ELDemoButton.m in Sources */,
7802C5CF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m in Sources */,
958B4EA5263C08E800476BAE /* UILabel+WZAdd.m in Sources */,
95572633276C38CD003BFBAC /* BabyScaleConnectViewController.m in Sources */,
78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */,
95572644276C38CD003BFBAC /* BodyFatScaleConnectViewController.m in Sources */,
958B4EBD263C08E800476BAE /* AppDelegate.m in Sources */,
7802C5CB2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m in Sources */,
95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */,
958B4EAE263C08E800476BAE /* ViewController+MASAdditions.m in Sources */,
958B4EA8263C08E800476BAE /* CABasicAnimation+ELAnimation.m in Sources */,
@@ -1233,7 +1337,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20240628;
CURRENT_PROJECT_VERSION = 20240910;
DEVELOPMENT_TEAM = YEU77XR292;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -1255,7 +1359,7 @@
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK",
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120",
);
MARKETING_VERSION = 1.9.2;
MARKETING_VERSION = 1.10.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1268,7 +1372,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20240628;
CURRENT_PROJECT_VERSION = 20240910;
DEVELOPMENT_TEAM = YEU77XR292;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -1290,7 +1394,7 @@
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK",
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120",
);
MARKETING_VERSION = 1.9.2;
MARKETING_VERSION = 1.10.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app;
PRODUCT_NAME = "$(TARGET_NAME)";

二進制
AILinkBleSDKDemo/AILinkBleSDK.framework/AILinkBleSDK 查看文件


+ 1
- 1
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/AILinkBleSDK_Version.h 查看文件

@@ -9,6 +9,6 @@
#define AILinkBleSDK_Version_h

//版本号 SDK Version
#define AiLinkBleSDKVersion @"1.9.0" // 20240603
#define AiLinkBleSDKVersion @"1.10.0" // 20240910

#endif /* AILinkBleSDK_Version_h */

+ 2
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyFatScaleBleHeader.h 查看文件

@@ -70,6 +70,8 @@ typedef NS_ENUM(NSInteger , ELBodyFatScaleBleDataType) {
ELBodyFatScaleBleDataType_MCUBackHeartRateMeasureFailed = 0x0D ,
/** MCU->APP:MCU请求补全体脂数据 */
ELBodyFatScaleBleDataType_MCUBackNeedsFillBodyData = 0x0E ,
/** MCU->APP:设备上报模式 */
ELBodyFatScaleBleDataType_MCUReportingMode = 0x0F ,

/** App发送指令切换单位 */
ELBodyFatScaleBleDataType_AppSendUnit = 0x81 ,

+ 4
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyFatScaleBleManager.h 查看文件

@@ -114,6 +114,10 @@ MCU requests user information( MCU 请求用户信息)
*/
- (void)bodyFatScaleManagerCallBackSetIPAddressResult:(NSInteger)result;

/** MCU reporting mode (MCU上报模式 )
* @param mode : 0:体脂模式(默认),1:体重模式(没有阻抗和体脂数据)
*/
- (void)bodyFatScaleManagerCallBackReportingMode:(NSInteger)mode;
@end



+ 31
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBodyIndexAlgorithmModel.h 查看文件

@@ -14,6 +14,14 @@ typedef NS_ENUM(NSInteger, PHSex) {
PHSex_MALE = 1,//男
};

//矿物质重量
typedef NS_ENUM(NSUInteger, AILinkMineralWeightType) {
AILinkMineralWeightTypeUnknown = 0,//未知
AILinkMineralWeightTypeInsufficient = 1,//不足
AILinkMineralWeightTypeStandard = 2,//标准
AILinkMineralWeightTypeExcellent = 3,//偏高
};


//四肢肌肉指数
typedef NS_ENUM(NSUInteger, AILinkLimbMuscleIndexType) {
@@ -158,6 +166,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ELBodyIndexAlgorithmModel : NSObject
#pragma mark ============ 标准判断临界值数组 ==============


/// 四肢肌肉指数 标准判断
/// @param sex 性别
/// @param cmHeight 身高(cm)
@@ -233,6 +242,15 @@ NS_ASSUME_NONNULL_BEGIN
/// @param age 年龄
+ (NSArray<NSString *> *)getSkeletalMuscleMassScopeSex:(PHSex)sex age:(float)age;

/// 水分重量 标准判断2个临界值
/// @param sex 性别
/// @param weight 体重
+ (NSArray<NSString *> *)getWaterWeightScopeSex:(PHSex)sex weight:(float)weight;

/// 皮下脂肪量 标准判断2个临界值
/// @param sex 性别
/// @param weight 体重
+ (NSArray<NSString *> *)getSubcutaneousFatMassScopeSex:(PHSex)sex weight:(float)weight;
#pragma mark ============ 判断标准数组 ==============
//四肢肌肉指数标准词条
+(NSArray<NSNumber *> *)getLimbMuscleIndexStandardArray;
@@ -422,6 +440,19 @@ NS_ASSUME_NONNULL_BEGIN
/// @param weight 体重(KG)
/// @param standardWeight 标准体重(KG)
+ (AILinkFatLevelType)getfatLevelWithweight:(double)weight andStandardWeight:(double)standardWeight;


#pragma mark - 新增指标
/**
* 矿物质重量 标准判断
*
* @param mineralWeightValue 体重(KG)
* @param weight 体重
*
* @return 不足、标准、偏高、Null
*/
+ (AILinkMineralWeightType)getMineralWeightType:(float)mineralWeightValue;

@end

NS_ASSUME_NONNULL_END

+ 2
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELBroadcastScaleBleHeader.h 查看文件

@@ -21,6 +21,8 @@ typedef NS_ENUM(NSUInteger, BroadcastScaleTestStatus) {
BroadcastScaleTestStatusADCTestSuccess,
//Impedance measurement failed (the impedance value is 0xFFFF at this time)(阻抗测量失败(此时阻抗数值为 0xFFFF))
BroadcastScaleTestStatusADCTestFailed,
//Weight mode (skip impedance measurement, the impedance value is 0, and the APP will not prompt impedance failure)( 体重模式(跳过阻抗测量,此时阻抗数值为 0, APP不会提示阻抗失败的提示))
BroadcastScaleTestStatusWeightMode,
//End of test(测试结束)
BroadcastScaleTestStatusTestEnd = 0xFF,
};

+ 9
- 1
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELEightScaleBleManager.h 查看文件

@@ -70,7 +70,8 @@ NS_ASSUME_NONNULL_BEGIN

///MCU返回设备测量信息(测量完成的时候才会回调该方法)
- (void)eightScaleBleReportTestData:(ELEightScaleBleDataModel *)dataModle bfrData:(ELEightScaleBleBfrDataModel *)bfrDataModle;

// 双频八电极mcu测量完成返回数据(测量完成的时候才会回调该方法)
- (void)eightScaleBle0x0068ReportTestData:(ELEightScaleBleDataModel *)dataModel bodyFatData:(ELEightScaleBleBfrDataModel *)bfrDataModel;
/** MCU返回操作结果 */
- (void)eightScaleBleReportOperationWithType:(enum EightScaleOperationType)type unitResult:(enum EightScaleSwitchUnitResult)result;

@@ -82,7 +83,14 @@ NS_ASSUME_NONNULL_BEGIN

///MCU请求补全体脂数据
- (void)eightScaleBleCompletionBodyFatData;
- (void)eightScaleBleCompletionBodyFatData:(ELEightScaleBleDataModel *)dataModel;

/**
Back to impedance measurement(返回阻抗测量)

@param status 阻抗测量状态
*/
-(void)eightScaleScaleManagerCallBackAdcMeasureStatus:(DualBandEightScaleAdcMeasureStatus)status;
@end

@interface ELEightScaleBleManager : ELBluetoothManager

+ 9
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELEightScaleSDKHeader.h 查看文件

@@ -164,4 +164,13 @@ typedef NS_ENUM(NSUInteger, EightScaleErrorCodeType) {
EightScaleErrorCodeType_Overweight = 0x01 ,
} ;


//Impedance measurement process(阻抗测量过程) 双频八电极
typedef NS_ENUM(NSUInteger, DualBandEightScaleAdcMeasureStatus) {
DualBandEightScaleAdcMeasureStatus_Testing = 0x04,//测阻抗中
DualBandEightScaleAdcMeasureStatus_Fail = 0x06,//测阻抗失败
DualBandEightScaleAdcMeasureStatus_SuccessCustom = 0x07,//The impedance measurement is successful, and the APP algorithm is used. The APP will identify the corresponding algorithm according to VID and PID(测阻抗成功,并使用 APP 算法,APP 会根据 VID,PID 来识 别对应算法)
};

#endif /* ELEightScaleSDKHeader_h */

+ 1
- 1
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELExSupportDeviceType.h 查看文件

@@ -119,7 +119,7 @@ typedef NS_ENUM(NSUInteger, ELSupportDeviceType) {
ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤

ELSupportDeviceType_dualFrequencyEightelectrodeMCUAlgorithm = 0x0068, //双频八电极MCU算法秤
};

#endif /* ELExSupportDeviceType_h */

+ 9
- 0
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELHygrothermographBleHeader.h 查看文件

@@ -195,6 +195,15 @@ typedef NS_ENUM(NSInteger, TH_Dev_SupportFeatures) {
TH_Dev_SupportFeatures_GroundLight = 5 ,
/// 寻物功能(APP点击寻物时,设备蜂鸣器会响)
TH_Dev_SupportFeatures_Lost = 6 ,
/// 接收 APP 下发指令
TH_Dev_SupportFeatures_ReceiveCommand = 7 ,
/// 温度单位切换
TH_Dev_SupportFeatures_TemperatureUnitSwitch = 8 ,
/// 温度报警
TH_Dev_SupportFeatures_TemperatureAlarm = 9 ,
/// 湿度报警
TH_Dev_SupportFeatures_HumidityAlarm = 10 ,

};



+ 12
- 1
AILinkBleSDKDemo/AILinkBleSDK.framework/Headers/ELHygrothermographBleManager.h 查看文件

@@ -39,7 +39,9 @@ NS_ASSUME_NONNULL_BEGIN
/// @param unit 温度单位
/// @param hum 湿度,单位%
/// @param state 当前设备状态,详细内容见枚举注释
- (void)newTHManagerReturnDevStatePower:(int)power bootTime:(long long)time tem:(float)tem unit:(enum ELDeviceTemperatureUnit)unit hum:(float)hum devState:(enum TH_DevStateType)state;
/// @param isCharging 设备充电状态 默认是关闭状态
- (void)newTHManagerReturnDevStatePower:(int)power bootTime:(long long)time tem:(float)tem unit:(enum ELDeviceTemperatureUnit)unit hum:(float)hum devState:(enum TH_DevStateType)state isCharging:(BOOL)isCharging;



/** 设备返回阀值
@@ -269,6 +271,15 @@ NS_ASSUME_NONNULL_BEGIN
// App绑定设备 完成
- (void)bindDeviceDone;

/// 解析A7指令
- (void)processA7Data:(NSData *)rawData withMac:(NSData *)mac withCid:(UInt16)cid;
/// 解析A6指令
- (void)processA6Data:(NSData *)rawData;

/// 生成A7数据包
- (NSData * _Nullable)makePacketA7Payload:(NSData *)payload withMac:(NSData *)mac withCid:(UInt16)cid;
/// 生成A6数据包
- (NSData * _Nullable)makePacketA6Payload:(NSData *)payload;

@end


二進制
AILinkBleSDKDemo/AILinkBleSDK.framework/Info.plist 查看文件


二進制
AILinkBleSDKDemo/AILinkBleSDK.framework/PrivacyInfo.bundle/Info.plist 查看文件


+ 22
- 0
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/Contents.json 查看文件

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "aibbq_add_equipment_probe_100_ic@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "aibbq_add_equipment_probe_100_ic@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

二進制
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/aibbq_add_equipment_probe_100_ic@2x.png 查看文件


二進制
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/aibbq_add_equipment_probe_100_ic.imageset/aibbq_add_equipment_probe_100_ic@3x.png 查看文件


+ 22
- 0
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/Contents.json 查看文件

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ailink_cid85_banding_cid_0062_100_ic@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ailink_cid85_banding_cid_0062_100_ic@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

二進制
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/ailink_cid85_banding_cid_0062_100_ic@2x.png 查看文件


二進制
AILinkBleSDKDemo/Assets.xcassets/DeviceIcon/ailink_cid85_banding_cid_0062_100_ic.imageset/ailink_cid85_banding_cid_0062_100_ic@3x.png 查看文件


+ 71
- 0
AILinkBleSDKDemo/DeviceDemo/26_MeatProbe/ELMeatProbeCustomDataModel.h 查看文件

@@ -0,0 +1,71 @@
//
// ELMeatProbeCustomDataModel.h
// AILinkBleSDK
//

#import <Foundation/Foundation.h>
#import <AILinkBleSDK/ELMeatProbeBleTool.h>

NS_ASSUME_NONNULL_BEGIN

#pragma pack(1)
typedef struct ELMeatProbeCustomDataStruct {

UInt8 ver; //数据版本 (当前版本 0x02) //数据总长度: modelSize()

UInt32 cookingId; //烧烤id (选择食物的时间戳)

UInt8 foodType; //食物类型
UInt8 foodRawness; //食物熟度
SInt16 targetTemperature_C; //食物目标温度:℃
SInt16 targetTemperature_F; //食物目标温度:℉

SInt16 ambientMinTemperature_C; //炉温目标下限:℃
SInt16 ambientMinTemperature_F; //炉温目标下限:℉

SInt16 ambientMaxTemperature_C; //炉温目标上限:℃
SInt16 ambientMaxTemperature_F; //炉温目标上限:℉
double alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0

UInt32 timerStart;//计时开始时间戳
UInt32 timerEnd;//计时结束时间戳

UInt8 currentUnit; //当前温度单位
SInt16 alarmTemperature_C; //食物提醒温度:℃
SInt16 alarmTemperature_F; //食物提醒温度:℉
UInt8 remarkBytes[32]; //UTF-8 编码
} ELMeatProbeCustomDataStruct;
#pragma pack()

///
@interface ELMeatProbeCustomDataModel : NSObject


@property (nonatomic, assign) ELMeatProbeCustomDataStruct customStruct;

@property (nonatomic, assign, readonly) NSInteger ver;
@property (nonatomic, assign, readonly) NSInteger cookingId;
@property (nonatomic, assign, readonly) NSInteger foodType;
@property (nonatomic, assign, readonly) NSInteger foodRawness;

@property (nonatomic, assign) double alarmTemperaturePercent;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit currentUnit;


+ (instancetype _Nullable)modelWithData:(NSData *)data;

- (NSData *)dataValue;

- (NSString *)remark;
- (NSString *)setRemark:(NSString *)remark;

+ (Byte)version;
+ (Byte)modelSize;

@end

NS_ASSUME_NONNULL_END

+ 154
- 0
AILinkBleSDKDemo/DeviceDemo/26_MeatProbe/ELMeatProbeCustomDataModel.m 查看文件

@@ -0,0 +1,154 @@
//
// ELMeatProbeCustomDataModel.m
// AILinkBleSDK
//

#import "ELMeatProbeCustomDataModel.h"
#import <AILinkBleSDK/NSData+AILinkBle.h>
#import <objc/runtime.h>


@implementation ELMeatProbeCustomDataModel

+ (Byte)version {
return 0x02;
}

+ (Byte)modelSize {
return 128;//sizeof(ELMeatProbeCustomDataStruct);
}

+ (instancetype)modelWithData:(NSData *)data {
if (data.length != self.modelSize) {
NSLog(@"数据长度错误 data:%@", data);
return nil;
}
ELMeatProbeCustomDataModel *model = [[ELMeatProbeCustomDataModel alloc] initWithData:data];
if (model.customStruct.ver != self.version) {
NSLog(@"数据版本错误 data:%@", data);
return nil;
}
return model;
}

- (instancetype)init {
if (self = [super init]) {
memset(&_customStruct, 0, sizeof(_customStruct));
_customStruct.ver = self.class.version;
}
return self;
}

- (instancetype)initWithData:(NSData *)data {
if (self = [self init]) {
memcpy(&_customStruct, data.bytes, sizeof(_customStruct));
}
return self;
}

- (NSData *)dataValue {
Byte bytes[128] = {0x00};
memcpy(bytes, &_customStruct, sizeof(_customStruct));
return [NSData dataWithBytes:bytes length:sizeof(bytes)];
}

- (NSString *)remark {
char *remarkBytes[64] = {0};
memcpy(remarkBytes, _customStruct.remarkBytes, sizeof(_customStruct.remarkBytes));
NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes];
return str;
}

- (NSString *)setRemark:(NSString *)remark {
NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding];
if (data.length > 32) {
while (data.length > 29) {
remark = [remark substringToIndex:(remark.length - 1)];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
remark = [remark stringByAppendingString:@"..."];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
Byte *bytes = (Byte *)data.bytes;
NSUInteger total = data.length > 32 ? 32 : data.length;
memset(_customStruct.remarkBytes, 0, 32);
memcpy(_customStruct.remarkBytes, bytes, total);
return [self remark];
}

#pragma mark setter getter

- (NSInteger)ver {
return self.customStruct.ver;
}

- (NSInteger)cookingId {
return self.customStruct.cookingId;
}

- (NSInteger)foodType {
return self.customStruct.foodType;
}

- (NSInteger)foodRawness {
return self.customStruct.foodRawness;
}

- (double)alarmTemperaturePercent {
return self.customStruct.alarmTemperaturePercent;
}

- (NSInteger)alarmTemperature_C {
return self.customStruct.alarmTemperature_C;
}

- (NSInteger)alarmTemperature_F {
return self.customStruct.alarmTemperature_F;
}

- (void)setAlarmTemperaturePercent:(double)alarmTemperaturePercent {
_customStruct.alarmTemperaturePercent = alarmTemperaturePercent;
_customStruct.alarmTemperature_C = _customStruct.targetTemperature_C * alarmTemperaturePercent;
_customStruct.alarmTemperature_F = _customStruct.targetTemperature_F * alarmTemperaturePercent;
}

- (ELDeviceTemperatureUnit)currentUnit {
return self.customStruct.currentUnit;
}


#pragma mark ---- description

- (NSString *)description {
NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy;
[desc appendString:[NSString stringWithFormat:@"ver: %@,\n", @(self.customStruct.ver)]];
[desc appendString:[NSString stringWithFormat:@"cookingId: %@,\n", @(self.customStruct.cookingId)]];
[desc appendString:[NSString stringWithFormat:@"foodType: %@,\n", @(self.customStruct.foodType)]];
[desc appendString:[NSString stringWithFormat:@"foodRawness: %@,\n", @(self.customStruct.foodRawness)]];
[desc appendString:[NSString stringWithFormat:@"targetTemperature_C: %@,\n", @(self.customStruct.targetTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"targetTemperature_F: %@,\n", @(self.customStruct.targetTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_C: %@,\n", @(self.customStruct.ambientMinTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_F: %@,\n", @(self.customStruct.ambientMinTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_C: %@,\n", @(self.customStruct.ambientMaxTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_F: %@,\n", @(self.customStruct.ambientMaxTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperaturePercent: %@,\n", @(self.alarmTemperaturePercent)]];
[desc appendString:[NSString stringWithFormat:@"timerStart: %@,\n", @(self.customStruct.timerStart)]];
[desc appendString:[NSString stringWithFormat:@"timerEnd: %@,\n", @(self.customStruct.timerEnd)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperature_C: %@,\n", @(self.customStruct.alarmTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperature_F: %@,\n", @(self.customStruct.alarmTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"currentUnit: %@,\n", @(self.customStruct.currentUnit)]];
[desc appendString:[NSString stringWithFormat:@"remark: %@,\n", self.remark]];
[desc appendString:@"}"];
return desc;
}

- (NSString *)debugDescription {
return [self description];
}


@end

+ 62
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleBBQParamModel.h 查看文件

@@ -0,0 +1,62 @@
//
// ELMeatProbeScreenBoxBleBBQParamModel.h
// AILinkComponentMeatProbe
//
// Created by Larry Zhang on 2024/2/26.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ELMeatProbeScreenBoxBleBBQParamModel : NSObject

//生成时间
@property (nonatomic, assign) NSTimeInterval timestamp;

@property (nonatomic, strong) NSData *macData;
@property (nonatomic, copy, readonly) NSString *macString;

@property (nonatomic, assign) UInt8 state; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始

@property (nonatomic, assign) UInt8 foodType; //食物类型

@property (nonatomic, assign) UInt16 targetTemperatureRaw_C; //食物目标温度:℃
@property (nonatomic, assign, readonly) NSInteger targetTemperature_C;
@property (nonatomic, assign) UInt16 targetTemperatureRaw_F; //食物目标温度:℉
@property (nonatomic, assign, readonly) NSInteger targetTemperature_F;

@property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_C; //炉温目标下限:℃
@property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_C;
@property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_F; //炉温目标下限:℉
@property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_F;

@property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_C; //炉温目标上限:℃
@property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_C;
@property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_F; //炉温目标上限:℉
@property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_F;

@property (nonatomic, assign) float alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0

@property (nonatomic, assign) UInt32 cookingId; //CookingID

@property (nonatomic, assign) UInt32 workTime; //任务已运行时间,单位s

@property (nonatomic, assign) UInt32 timerCount; //计时剩余时间(秒)

@property (nonatomic, assign) UInt8 foodRawness; //食物熟度 (maturityID)

@property (nonatomic, strong) NSData *remarkData;

- (void)clearAll;

- (void)cookDone;

- (NSData *)dataValue;

- (NSString *)remark;
- (NSString *)setRemark:(NSString *)remark;

@end

NS_ASSUME_NONNULL_END

+ 130
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleBBQParamModel.m 查看文件

@@ -0,0 +1,130 @@
//
// ELMeatProbeScreenBoxBleBBQParamModel.m
// AILinkComponentMeatProbe
//
// Created by Larry Zhang on 2024/2/26.
//

#import "ELMeatProbeScreenBoxBleBBQParamModel.h"

@implementation ELMeatProbeScreenBoxBleBBQParamModel

- (NSString *)macString {
if (self.macData == nil || self.macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)self.macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (NSInteger)targetTemperature_C {
NSInteger value = self.targetTemperatureRaw_C & 0x3FFF;
if (self.targetTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)targetTemperature_F {
NSInteger value = self.targetTemperatureRaw_F & 0x3FFF;
if (self.targetTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMinTemperature_C {
NSInteger value = self.ambientMinTemperatureRaw_C & 0x3FFF;
if (self.ambientMinTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMinTemperature_F {
NSInteger value = self.ambientMinTemperatureRaw_F & 0x3FFF;
if (self.ambientMinTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMaxTemperature_C {
NSInteger value = self.ambientMaxTemperatureRaw_C & 0x3FFF;
if (self.ambientMaxTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMaxTemperature_F {
NSInteger value = self.ambientMaxTemperatureRaw_F & 0x3FFF;
if (self.ambientMaxTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (void)clearAll {
self.state = 0; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始
self.foodType = 0xFF; //食物类型
self.targetTemperatureRaw_C = 0;//0xFFFF; //食物目标温度:℃//
self.targetTemperatureRaw_F = 0;//0xFFFF; //食物目标温度:℉//
self.ambientMaxTemperatureRaw_C = 0;//0xFFFF; //炉温目标上限:℃
self.ambientMaxTemperatureRaw_F = 0;//0xFFFF; //炉温目标上限:℉
self.ambientMinTemperatureRaw_C = 0;//0xFFFF; //炉温目标下限:℃
self.ambientMinTemperatureRaw_F = 0;//0xFFFF; //炉温目标下限:℉
self.cookingId = 0; //CookingID
self.workTime = 0; //任务已运行时间,单位s
self.timerCount = 0; //计时剩余时间(秒)
self.alarmTemperaturePercent = 0.95; //提醒温度对目标温度百分比 0~1.0
self.foodRawness = 0xFF; //食物熟度
Byte bytes[32] = {0};
self.remarkData = [NSData dataWithBytes:bytes length:sizeof(bytes)];
}

- (void)cookDone {
[self clearAll];
}

- (NSData *)dataValue {
NSMutableData *data = [NSMutableData data];
[data appendData:self.macData];
UInt32 alarmTemperaturePercent = 0;
memcpy(&alarmTemperaturePercent, &(_alarmTemperaturePercent), sizeof(float));
Byte bytes[] = { self.state, self.foodType, self.targetTemperatureRaw_C, self.targetTemperatureRaw_C >> 8, self.targetTemperatureRaw_F, self.targetTemperatureRaw_F >> 8, self.ambientMaxTemperatureRaw_C, self.ambientMaxTemperatureRaw_C >> 8, self.ambientMaxTemperatureRaw_F, self.ambientMaxTemperatureRaw_F >> 8, self.ambientMinTemperatureRaw_C, self.ambientMinTemperatureRaw_C >> 8, self.ambientMinTemperatureRaw_F, self.ambientMinTemperatureRaw_F >> 8, self.cookingId, self.cookingId >> 8, self.cookingId >> 16, self.cookingId >> 24, self.workTime, self.workTime >> 8, self.workTime >> 16, self.workTime >> 24, self.timerCount, self.timerCount >> 8, self.timerCount >> 16, self.timerCount >> 24, alarmTemperaturePercent, alarmTemperaturePercent >> 8, alarmTemperaturePercent >> 16, alarmTemperaturePercent >> 24, self.foodRawness };
[data appendBytes:bytes length:sizeof(bytes)];
[data appendData:self.remarkData];
return data.copy;
}

- (NSString *)setRemark:(NSString *)remark {
NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding];
if (data.length > 32) {
while (data.length > 29) {
remark = [remark substringToIndex:(remark.length - 1)];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
remark = [remark stringByAppendingString:@"..."];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
Byte *bytes = (Byte *)data.bytes;
NSUInteger total = data.length > 32 ? 32 : data.length;
Byte remarkBytes[32] = {0};
memset(remarkBytes, 0, 32);
memcpy(remarkBytes, bytes, total);
self.remarkData = [NSData dataWithBytes:remarkBytes length:sizeof(remarkBytes)];
return [self remark];
}

- (NSString *)remark {
Byte remarkBytes[64] = {0};
[self.remarkData getBytes:remarkBytes length:self.remarkData.length];
NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes];
return str;
}


@end

+ 32
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleCmdTool.h 查看文件

@@ -0,0 +1,32 @@
//
// ELMeatProbeScreenBoxBleCmdTool.h
// Pods
//
// Created by Larry Zhang on 2024/2/26.
//

#import <Foundation/Foundation.h>
#import "ELMeatProbeScreenBoxBleHeader.h"

NS_ASSUME_NONNULL_BEGIN

@class ELAILinkBleManager;
@class ELMeatProbeScreenBoxStatusModel, ELMeatProbeScreenBoxProbeStatusModel, ELMeatProbeScreenBoxBleBBQParamModel;

@interface ELMeatProbeScreenBoxBleCmdTool : NSObject

//APP获取设备状态数据(Type:0x01)
+ (NSData *)requestStatus:(ELAILinkBleManager *)manager;

//切换单位
+ (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager;

//获取设置烧烤任务参数(Type:0x09)
+ (NSData *)requestParams:(ELAILinkBleManager *)manager;
+ (NSData *)setupParams:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager;

+ (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager;

@end

NS_ASSUME_NONNULL_END

+ 59
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleCmdTool.m 查看文件

@@ -0,0 +1,59 @@
//
// ELMeatProbeScreenBoxBleCmdTool.m
// Pods
//
// Created by Larry Zhang on 2024/2/26.
//

#import "ELMeatProbeScreenBoxBleCmdTool.h"
#import <AILinkBleSDK/ELAILinkBleManager.h>
#import "ELMeatProbeScreenBoxBleBBQParamModel.h"

@implementation ELMeatProbeScreenBoxBleCmdTool

//APP获取设备基础信息
+ (NSData *)requestStatus:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeScreenBoxCMDAppGetStatus_Type01;
Byte payloadBytes[] = { cmd, 0x01 };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

//切换单位
+ (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager {
if (unit > 1) { return nil; }
const Byte cmd = ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03;
Byte payloadBytes[] = { cmd, unit };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (NSData *)requestParams:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeScreenBoxCMDSetInfo_Type09;
Byte payloadBytes[] = { cmd, 0x01 };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (NSData *)setupParams:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeScreenBoxCMDSetInfo_Type09;
Byte payloadBytes[] = { cmd, 0x00, supportProbe };
NSMutableData *payloadData = [NSMutableData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
for (ELMeatProbeScreenBoxBleBBQParamModel *param in params) {
[payloadData appendData:param.dataValue];
}
[manager sendA7Payload:payloadData.copy];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager {
const Byte ELInetGetCmdTypeSpecialFunc = 0x49, op = 0x01;
Byte payloadBytes[16] = { ELInetGetCmdTypeSpecialFunc, op };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA6Payload:payloadData.copy];
}

@end

+ 35
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleHeader.h 查看文件

@@ -0,0 +1,35 @@
//
// ELMeatProbeScreenBoxBleHeader.h
// Pods
//
// Created by Larry Zhang on 2024/2/26.
//

#ifndef ELMeatProbeScreenBoxBleHeader_h
#define ELMeatProbeScreenBoxBleHeader_h

typedef NS_ENUM(UInt8, ELMeatProbeScreenBoxCMD) {
ELMeatProbeScreenBoxCMDAppGetStatus_Type01 = 0x01, //0x01:Type:APP 获取设备状态
ELMeatProbeScreenBoxCMDMcuUploadStatus_Type02 = 0x02, //0x02:Type:设备上报设备数据

ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03 = 0x03, //0x03:Type:切换单位
ELMeatProbeScreenBoxCCMDAppSwitchUnitResponse_Type04 = 0x04, //0x04:Type:切换单位 response

ELMeatProbeScreenBoxCMDStartAlarmState_Type05 = 0x05, //0x05:Type:发起报警
ELMeatProbeScreenBoxCMDStartAlarmStateResponse_Type06 = 0x06, //0x06:Type:发起报警 Response

ELMeatProbeScreenBoxCMDStoplAlarm_Type07 = 0x07, //0x07:Type:停止报警
ELMeatProbeScreenBoxCMDStoplAlarmResponse_Type08 = 0x08, //0x08:Type:停止报警 Response
ELMeatProbeScreenBoxCMDSetInfo_Type09 = 0x09, //Type:0x09 设置烧烤任务参数
ELMeatProbeScreenBoxCMDSetInfoResponse_Type0A = 0x0A, //Type:0x0A 设置烧烤任务参数 Response
};

#endif /* ELMeatProbeScreenBoxBleHeader_h */

+ 114
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleParser.h 查看文件

@@ -0,0 +1,114 @@
//
// ELMeatProbeScreenBoxBleParser.h
// Pods
//
// Created by Larry Zhang on 2024/2/26.
//

#import <Foundation/Foundation.h>
#import <AILinkBleSDK/ELSDKHeader.h>
#import "ELMeatProbeScreenBoxBleHeader.h"

NS_ASSUME_NONNULL_BEGIN

@protocol ELMeatProbeScreenBoxBleParserDelegate;
@class ELMeatProbeScreenBoxStatusModel, ELMeatProbeScreenBoxProbeStatusModel, ELMeatProbeScreenBoxBleBBQParamModel;

@interface ELMeatProbeScreenBoxBleParser : NSObject

@property (nonatomic, copy) NSString *mac;

@property (nonatomic, assign) NSInteger deviceId;

@property (nonatomic, weak) id<ELMeatProbeScreenBoxBleParserDelegate> delegate;

- (void)parseA7Payload:(NSData *)payload;

@end

@protocol ELMeatProbeScreenBoxBleParserDelegate <NSObject>

@optional

//MCU 上报设备状态数据(Type:02)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser boxStatus:(ELMeatProbeScreenBoxStatusModel *)boxStatus;

//切换单位(Type:04)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser unit:(ELDeviceTemperatureUnit)unit;

//报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac;

//取消报警(Type:08)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac;

//烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params;

@end

@interface ELMeatProbeScreenBoxStatusModel : NSObject

//设备状态数据协议版本:0x01
@property (nonatomic, assign) UInt8 version;

//支持的探针数量 M
@property (nonatomic, assign) UInt8 probeMaxCount;

//当前连接的探针数量 N
@property (nonatomic, assign) UInt8 probeCount;

//充电盒电池状态
@property (nonatomic, assign) UInt8 batteryStatus;
@property (nonatomic, assign) UInt8 batteryPercent;

//温度单位
@property (nonatomic, assign) ELDeviceTemperatureUnit unit;

//生成时间
@property (nonatomic, assign) NSTimeInterval timestamp;


@property (nonatomic, strong) NSArray <ELMeatProbeScreenBoxProbeStatusModel *>*probeStatusArray;

@end

@interface ELMeatProbeScreenBoxProbeStatusModel : NSObject

//探针编号(1-M) 无法确定可连 接探针的编号,上报 0xFF
@property (nonatomic, assign) UInt8 index;

//探针 mac 地址
@property (nonatomic, strong) NSData *macData;
@property (nonatomic, copy, readonly) NSString *macString;
//
//食物温度
@property (nonatomic, assign, readonly) NSInteger internalTemperature_C;
@property (nonatomic, assign, readonly) NSInteger internalTemperature_F;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit internalTemperatureUnit;
@property (nonatomic, assign) UInt16 internalTemperatureRaw;
//
//环境温度
@property (nonatomic, assign, readonly) NSInteger ambientTemperature_C;
@property (nonatomic, assign, readonly) NSInteger ambientTemperature_F;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit ambientTemperatureUnit;
@property (nonatomic, assign) UInt16 ambientTemperatureRaw;
//
//探针电池状态
@property (nonatomic, assign) UInt8 batteryStatus;
@property (nonatomic, assign) UInt8 batteryPercent;
@property (nonatomic, assign) UInt8 batteryRaw;

//探针插入食物状态
@property (nonatomic, assign) UInt8 insertStatusRaw;

//探针连接状态
@property (nonatomic, assign) UInt8 connectState;

//生成时间
@property (nonatomic, assign) double timestamp;

@end


NS_ASSUME_NONNULL_END

+ 297
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/BLE/ELMeatProbeScreenBoxBleParser.m 查看文件

@@ -0,0 +1,297 @@
//
// ELMeatProbeScreenBoxBleParser.m
// Pods
//
// Created by Larry Zhang on 2024/2/26.
//

#import "ELMeatProbeScreenBoxBleParser.h"
#import "ELMeatProbeScreenBoxBleBBQParamModel.h"


@interface ELMeatProbeScreenBoxBleParser()

@end

@implementation ELMeatProbeScreenBoxBleParser

- (void)parseA7Payload:(nonnull NSData *)payload {
NSLog(@"#### parseA7Payload payload:%@", payload.debugDescription);
Byte *bytes = (Byte *)payload.bytes;
Byte cmd = bytes[0];
switch (cmd) {
case ELMeatProbeScreenBoxCMDMcuUploadStatus_Type02:
[self parseStatusData:payload];
break;
case ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03:
{
UInt8 unit = bytes[1];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:unit:)]) {
[self.delegate meatProbeScreenBoxBleParser:self unit:unit];
}
}
break;
// case ELMeatProbeScreenBoxCCMDAppSwitchUnitResponse_Type04:
// break;
case ELMeatProbeScreenBoxCMDStartAlarmState_Type05: {
Byte i = 1;
Byte macBytes[6] = { bytes[i+5], bytes[i+4], bytes[i+3], bytes[i+2], bytes[i+1], bytes[i] };
NSData *macData = [NSData dataWithBytes:macBytes length:6];
i += 6;
Byte state = bytes[i];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:startAlarmWithState:macData:mac:)]) {
[self.delegate meatProbeScreenBoxBleParser:self startAlarmWithState:state macData:macData mac:[self macDataToString:macData]];
}
}
break;
// case ELMeatProbeScreenBoxCMDStartAlarmStateResponse_Type06:
// break;
case ELMeatProbeScreenBoxCMDStoplAlarm_Type07: {
Byte i = 1;
Byte macBytes[6] = { bytes[i+5], bytes[i+4], bytes[i+3], bytes[i+2], bytes[i+1], bytes[i] };
NSData *macData = [NSData dataWithBytes:macBytes length:6];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:stopAlarmWithMacData:mac:)]) {
[self.delegate meatProbeScreenBoxBleParser:self stopAlarmWithMacData:macData mac:[self macDataToString:macData]];
}
}
break;
// case ELMeatProbeScreenBoxCMDStoplAlarmResponse_Type08:
// break;
case ELMeatProbeScreenBoxCMDSetInfo_Type09:
case ELMeatProbeScreenBoxCMDSetInfoResponse_Type0A: {
[self parseSetInfoParamData:payload];
}
break;
default:
break;
}
}

- (NSString *)macDataToString:(NSData *)macData {
if (macData == nil || macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (void)parseSetInfoParamData:(NSData *)payload {
NSLog(@"##### parseSetInfoParamData payload:%@", payload.debugDescription);
if (payload.length != 72) {
return;
}
Byte *bytes = (Byte *)payload.bytes;
Byte i = 1;
Byte option = bytes[i];
i = 2;
Byte supportProbe = bytes[i];
i = 3;
NSMutableArray *arr = [NSMutableArray array];
while (i < payload.length) {
//探针 1
ELMeatProbeScreenBoxBleBBQParamModel *param = [[ELMeatProbeScreenBoxBleBBQParamModel alloc] init];
param.timestamp = [NSDate date].timeIntervalSince1970;
param.macData = [payload subdataWithRange:NSMakeRange(i, 6)];
i += 6;
param.state = bytes[i];
i += 1;
param.foodType = bytes[i];//if (param.foodType == 0xFF) param.foodType = 0xFE;
i += 1;
param.targetTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.targetTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMaxTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMaxTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMinTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMinTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.cookingId = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
param.workTime = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
param.timerCount = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
UInt32 alarmTemperaturePercentU32 = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
float alarmTemperaturePercent = 0;
memcpy(&alarmTemperaturePercent, &alarmTemperaturePercentU32, sizeof(UInt32));
param.alarmTemperaturePercent = alarmTemperaturePercent;
i += 4;
param.foodRawness = bytes[i];
i += 1;
param.remarkData = [payload subdataWithRange:NSMakeRange(i, 32)];
i += 32;
if (param.state == 0) {
[param clearAll];
}
[arr addObject:param];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:option:supportProbe:params:)]) {
[self.delegate meatProbeScreenBoxBleParser:self option:option supportProbe:supportProbe params:arr.copy];
}
}

- (void)parseStatusData:(NSData *)payload {
if (payload.length != 25) {
return;
}
Byte *bytes = (Byte *)payload.bytes;
ELMeatProbeScreenBoxStatusModel *boxStatus = [[ELMeatProbeScreenBoxStatusModel alloc] init];
boxStatus.timestamp = [NSDate date].timeIntervalSince1970;
Byte i = 1;
boxStatus.version = bytes[i];
i = 2;
boxStatus.probeMaxCount = bytes[i];
i = 3;
boxStatus.probeCount = bytes[i];
i = 4;
boxStatus.batteryStatus = bytes[i] >> 7;
boxStatus.batteryPercent = bytes[i] & 0x7F;
i = 5;
boxStatus.unit = bytes[i];
i = 9;
NSMutableArray *arr = [NSMutableArray array];
{
ELMeatProbeScreenBoxProbeStatusModel *probeStatus = [[ELMeatProbeScreenBoxProbeStatusModel alloc] init];
probeStatus.timestamp = boxStatus.timestamp;
probeStatus.index = bytes[i];
i += 1;
NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)];
probeStatus.macData = macData;
i += 6;
probeStatus.internalTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
probeStatus.ambientTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8);
// 凸(艹皿艹 ) //0x639F (25503)
if (probeStatus.ambientTemperatureRaw == 25503) {
probeStatus.ambientTemperatureRaw = 0xFFFF;
}
i += 2;
probeStatus.batteryStatus = bytes[i] >> 7;
probeStatus.batteryPercent = bytes[i] & 0x7F;
probeStatus.batteryRaw = bytes[i];
i += 1;
probeStatus.insertStatusRaw = bytes[i];
i += 1;
probeStatus.connectState = bytes[i];
i += 3;
[arr addObject:probeStatus];
}
boxStatus.probeStatusArray = arr.copy;
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:boxStatus:)]) {
[self.delegate meatProbeScreenBoxBleParser:self boxStatus:boxStatus];
}
}

@end

@implementation ELMeatProbeScreenBoxStatusModel

@end

@implementation ELMeatProbeScreenBoxProbeStatusModel

- (NSString *)macString {
if (self.macData == nil || self.macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)self.macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (NSInteger)internalTemperature_C {
if (self.internalTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_C) {
return self.internalTemperature;
}
return [self convertF2C:self.internalTemperature];
}

- (NSInteger)internalTemperature_F {
if (self.internalTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_F) {
return self.internalTemperature;
}
return [self convertC2F:self.internalTemperature];
}

- (NSInteger)internalTemperature {
NSInteger value = self.internalTemperatureRaw & 0x3FFF;
if (self.internalTemperatureRaw & 0x4000) {
value = -value;
}
return value;
}

- (ELDeviceTemperatureUnit)internalTemperatureUnit {
return self.internalTemperatureRaw >> 15;
}

- (NSInteger)ambientTemperature_C {
if (self.ambientTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_C) {
return self.ambientTemperature;
}
return [self convertF2C:self.ambientTemperature];
}

- (NSInteger)ambientTemperature_F {
if (self.ambientTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_F) {
return self.ambientTemperature;
}
return [self convertC2F:self.ambientTemperature];
}

- (NSInteger)ambientTemperature {
NSInteger value = self.ambientTemperatureRaw & 0x3FFF;
if (self.ambientTemperatureRaw & 0x4000) {
value = -value;
}
return value;
}

- (ELDeviceTemperatureUnit)ambientTemperatureUnit {
return self.ambientTemperatureRaw >> 15;
}

//温度换算
- (float)convertF2C:(float)fahrenheit {
return round((fahrenheit - 32) / 1.8);
}

- (float)convertC2F:(float)celsius {
return round(celsius * 1.8 + 32);
}

@end

+ 21
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.h 查看文件

@@ -0,0 +1,21 @@
//
// MeatProbeScreenBoxVC.h
// AILinkBleSDK_Example
//
// Created by LarryZhang on 2024/9/7.
// Copyright © 2024 zhengzida. All rights reserved.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@class ELAILinkPeripheral;

@interface MeatProbeScreenBoxVC : UIViewController

@property (nonatomic, strong) ELAILinkPeripheral *per;

@end

NS_ASSUME_NONNULL_END

+ 312
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.m 查看文件

@@ -0,0 +1,312 @@
//
// MeatProbeScreenBoxVC.m
// AILinkBleSDK_Example
//
// Created by LarryZhang on 2024/9/7.
// Copyright © 2024 zhengzida. All rights reserved.
//

#import "MeatProbeScreenBoxVC.h"
#import <AILinkBleSDK/ELAILinkBleManager.h>
#import <AILinkBleSDK/NSData+AILinkBle.h>
#import "ELMeatProbeScreenBoxBleCmdTool.h"
#import "ELMeatProbeScreenBoxBleParser.h"
#import "ELMeatProbeScreenBoxBleBBQParamModel.h"

@interface MeatProbeScreenBoxVC () <ELAILinkBleManagerDelegate, ELMeatProbeScreenBoxBleParserDelegate>

@property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel;

@property (weak, nonatomic) IBOutlet UITextView *logTextView;


@property (nonatomic, strong) ELAILinkBleManager *bleManager;

@property (nonatomic, assign) NELBleManagerConnectState bleConnectState;

@property (nonatomic, strong) ELMeatProbeScreenBoxBleParser *bleParser;

@property (nonatomic, strong) ELMeatProbeScreenBoxStatusModel *boxStatus;

@property (nonatomic, strong) ELMeatProbeScreenBoxBleBBQParamModel *param;

@end

@implementation MeatProbeScreenBoxVC

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self initBle];
}

- (void)dealloc {
[self deinitBle];
}

#pragma mark - Action

- (IBAction)requestParamsAction:(id)sender {
NSData *payload = [ELMeatProbeScreenBoxBleCmdTool requestParams:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload];
[self addLog:log];
}

- (IBAction)setupParamsAction:(id)sender {
if (self.param == nil) {
return;
}
ELMeatProbeScreenBoxBleBBQParamModel *param = self.param;
param.foodType = 1;
param.targetTemperatureRaw_C = 70;
param.targetTemperatureRaw_F = 212;
param.foodRawness = 1;
param.alarmTemperaturePercent = 0.95;
param.cookingId = [NSDate date].timeIntervalSince1970;
NSData *payload = [ELMeatProbeScreenBoxBleCmdTool setupParams:@[param] supportProbe:1 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload];
[self addLog:log];
}

- (IBAction)switchUnitAction:(id)sender {
if (self.boxStatus.unit == 1) {
NSData *payload = [ELMeatProbeScreenBoxBleCmdTool switchUnit:0 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℃", payload];
[self addLog:log];
} else {
NSData *payload = [ELMeatProbeScreenBoxBleCmdTool switchUnit:1 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℉", payload];
[self addLog:log];
}
}

- (IBAction)clearAction:(id)sender {
self.logTextView.text = @"";
}

- (IBAction)shareLogAction:(id)sender {
}


#pragma mark - ELAILinkBleManagerDelegate

- (void)initBle {
self.bleManager = [[ELAILinkBleManager alloc] init];
self.bleManager.ailinkDelegate = self;
self.bleParser = [[ELMeatProbeScreenBoxBleParser alloc] init];
self.bleParser.delegate = self;
self.bleParser.mac = self.per.macAddressString;
}

- (void)scanBle {
[self.bleManager scanFilterWithCidArray:@[@(self.per.cid)]];
}

- (void)connect:(ELAILinkPeripheral *)peripheral {
[self.bleManager connectAILinkPeripheral:peripheral];
[self updateBleStatusView:NELBleManagerConnectStateConnecting];
}

- (void)deinitBle {
[self.bleManager stopScan];
self.bleManager.ailinkDelegate = nil;
[self.bleManager disconnectPeripheral];
}

- (void)updateBleStatusView:(NELBleManagerConnectState)state {
if (self.bleManager.central.state == CBManagerStatePoweredOff) {
self.bleStatusLabel.text = @"蓝牙关闭";
[self addLog:self.bleStatusLabel.text];
return;
}
switch (state) {
case NELBleManagerConnectStateDisconnected:
self.bleStatusLabel.text = @"断开连接";
break;
case NELBleManagerConnectStateFailed:
case NELBleManagerConnectStateFailedValidation:
self.bleStatusLabel.text = @"连接失败";
break;
case NELBleManagerConnectStateConnecting:
self.bleStatusLabel.text = @"正在连接...";
break;
case NELBleManagerConnectStatePassed:
self.bleStatusLabel.text = @"连接成功";

[self requestDeviceData];
// read RSSI
// [self readRSSI];
break;
case NELBleManagerConnectStateCentralScanning:
self.bleStatusLabel.text = @"正在扫描...";
break;

default:
break;
}
[self addLog:self.bleStatusLabel.text];
}

- (void)requestDeviceData {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{
});
}

// 设备状态变更
- (void)managerDidUpdateState:(CBCentralManager *)central {
NSLog(@"%s state:%@", __func__, @(central.state));
if (central.state == CBManagerStatePoweredOn) {
[self scanBle];
} else if (central.state == CBManagerStatePoweredOff) {
self.bleConnectState = NELBleManagerConnectStateCentralPowerOff;
[self updateBleStatusView:NELBleManagerConnectStateCentralPowerOff];
}
}

- (void)managerScanState:(BOOL)scanning {
NSLog(@"%s scanning:%@", __func__, @(scanning));
if (scanning) {
[self updateBleStatusView:NELBleManagerConnectStateCentralScanning];
}
}

// 扫描到设备
- (void)managerDidDiscoverPeripheral:(ELAILinkPeripheral *)peripheral {
NSLog(@"managerDidDiscoverPeripheral cid:%02x vid:%02x pid:%02x mac:%@", peripheral.cid, peripheral.vid, peripheral.pid, peripheral.macAddressString);
if ([peripheral.macAddressString isEqualToString:self.per.macAddressString]) {
[self.bleManager stopScan];
[self connect:peripheral];
}
}

- (void)managerDidUpdateConnect:(NELBleManagerConnectState)state {
NSLog(@"%s NELBleManagerConnectState:%@", __func__, @(state));
[self updateBleStatusView:state];
}

//A7数据
- (void)aiLinkBleReceiveA7Data:(NSData *)payload {
NSLog(@"%s #### payload:%@", __func__, payload.elHexString);
NSString *log = [NSString stringWithFormat:@"收: payload: %@", payload.elHexString];
[self addLog:log];
[self.bleParser parseA7Payload:payload];
}

//A6数据
- (void)aiLinkBleReceiveA6Data:(NSData *)packet {
NSLog(@"%s ##### packet:%@", __func__, packet.elHexString);
Byte *bytes = (Byte *)packet.bytes;
Byte cmd = bytes[2];
if (cmd == ELInetGetCmdTypeGetBMVersion) {
NSString *bmVersion = self.bleManager.bmVersion;
NSLog(@"##### bmVersion: %@", bmVersion);
}
if (cmd == ELInetGetCmdTypeGetBMVersionPro) {
NSString *bmVersionPro = self.bleManager.bmVersionPro;
NSLog(@"##### bmVersionPro: %@", bmVersionPro);
}
}

#pragma mark - ELMeatProbeScreenBoxBleParser

//MCU 上报设备状态数据(Type:02)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser boxStatus:(ELMeatProbeScreenBoxStatusModel *)boxStatus {
NSLog(@"##### boxStatus(): %@ ", boxStatus.debugDescription);
self.boxStatus = boxStatus;
NSString *log = [NSString stringWithFormat:@"boxStatus: { mac:%@, probeMaxCount:%hhu, probeCount:%hhu, batteryStatus:%hhu batteryPercent:%hhu, unit:%ld }", parser.mac, boxStatus.probeMaxCount, boxStatus.probeCount, boxStatus.batteryStatus, boxStatus.batteryPercent, boxStatus.unit];
[self addLog:log];
ELMeatProbeScreenBoxProbeStatusModel *probeStatus = boxStatus.probeStatusArray.firstObject;
NSString *log2 = [NSString stringWithFormat:@"probeStatus: { connectState:%@ macString:%hhu, internalTemperature_C:%ld ℃, ambientTemperature_C:%ld ℃ batteryStatus:%hhu batteryPercent:%hhu }", probeStatus.macString, probeStatus.connectState, probeStatus.internalTemperature_C, probeStatus.ambientTemperature_C, probeStatus.batteryStatus, probeStatus.batteryPercent];
[self addLog:log2];

}

//切换单位(Type:04)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser unit:(ELDeviceTemperatureUnit)unit {
NSLog(@"##### unit(): %@", @(unit));
NSString *log = [NSString stringWithFormat:@"unit(): %@", unit == 0 ? @"℃" : @"°F"];
[self addLog:log];
}

//报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac {
NSLog(@"##### box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state));
NSString *log = [NSString stringWithFormat:@"box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)];
[self addLog:log];
}

//取消报警(Type:08)
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac {
NSLog(@"##### box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac);
NSString *log = [NSString stringWithFormat:@"box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac];
[self addLog:log];
}

//烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务
- (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params {
NSLog(@"##### box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe));
ELMeatProbeScreenBoxBleBBQParamModel *param = params.firstObject;
NSLog(@"##### param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType));
self.param = param;
NSString *log = [NSString stringWithFormat:@"box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)];
NSString *log2 = [NSString stringWithFormat:@"param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)];
[self addLog:log];
[self addLog:log2];
}


- (void)peripheralDidReadRSSI:(nonnull NSNumber *)RSSI {
NSLog(@"%s peripheralDidReadRSSI: %@", __FUNCTION__, RSSI);
// main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self addLog:[NSString stringWithFormat:@"peripheralDidReadRSSI: %@", RSSI]];
});
[self readRSSI];
}

- (void)readRSSI {
// delay
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// Read RSSI
[self.bleManager readRSSI];
});
}

#pragma mark - addLog

- (void)addLog:(NSString *)log {
[self addLog:log newline:YES];
}

- (void)addWithoutNewlineLog:(NSString *)log {
[self addLog:log newline:NO];
}

- (void)addLog:(NSString *)log newline:(BOOL)newline {
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = @"HH:mm:ss.SSS";
NSString *time = [format stringFromDate:[NSDate date]];
if (newline) {
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log];
} else {
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log];
}
[self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)];
}



@end

+ 133
- 0
AILinkBleSDKDemo/DeviceDemo/27B_MeatProbeScreenBox/MeatProbeScreenBoxVC.xib 查看文件

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MeatProbeScreenBoxVC">
<connections>
<outlet property="bleStatusLabel" destination="lTu-0Z-Tge" id="aaN-iq-CUB"/>
<outlet property="logTextView" destination="QAW-Ww-9FQ" id="jSk-nf-wNh"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lTu-0Z-Tge">
<rect key="frame" x="0.0" y="59" width="393" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BL3-Qk-9Hg">
<rect key="frame" x="8" y="380" width="377" height="438"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QAW-Ww-9FQ">
<rect key="frame" x="0.0" y="0.0" width="377" height="438"/>
<color key="backgroundColor" systemColor="labelColor"/>
<color key="textColor" systemColor="opaqueSeparatorColor"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="10"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="QAW-Ww-9FQ" secondAttribute="trailing" id="18q-Eb-y4q"/>
<constraint firstItem="QAW-Ww-9FQ" firstAttribute="leading" secondItem="BL3-Qk-9Hg" secondAttribute="leading" id="APS-gg-hbg"/>
<constraint firstAttribute="bottom" secondItem="QAW-Ww-9FQ" secondAttribute="bottom" id="Dz9-Nx-gZg"/>
<constraint firstItem="QAW-Ww-9FQ" firstAttribute="top" secondItem="BL3-Qk-9Hg" secondAttribute="top" id="HfL-Mt-VYw"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qs9-zC-31a">
<rect key="frame" x="354" y="342" width="31" height="30"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="分享"/>
<connections>
<action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="O10-vI-2uq"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9po-bu-c16">
<rect key="frame" x="8" y="342" width="31" height="30"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="清空"/>
<connections>
<action selector="clearAction:" destination="-1" eventType="touchUpInside" id="MJ3-27-vjh"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Crb-qO-me7">
<rect key="frame" x="16" y="96" width="128" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="请求烧烤参数"/>
<connections>
<action selector="requestParamsAction:" destination="-1" eventType="touchUpInside" id="ihu-lG-9Zg"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bId-6O-Ndu">
<rect key="frame" x="176" y="96" width="128" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="设置烧烤参数"/>
<connections>
<action selector="setupParamsAction:" destination="-1" eventType="touchUpInside" id="o7Z-30-SJV"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cgo-YJ-V1h">
<rect key="frame" x="16" y="147" width="93.333333333333329" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="单位切换"/>
<connections>
<action selector="switchUnitAction:" destination="-1" eventType="touchUpInside" id="YRG-hs-YTh"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="bottom" secondItem="BL3-Qk-9Hg" secondAttribute="bottom" id="3vZ-eL-8ye"/>
<constraint firstItem="bId-6O-Ndu" firstAttribute="top" secondItem="Crb-qO-me7" secondAttribute="top" id="Adu-Nt-6GA"/>
<constraint firstItem="bId-6O-Ndu" firstAttribute="leading" secondItem="Crb-qO-me7" secondAttribute="trailing" constant="32" id="GgJ-IF-Lyh"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="BL3-Qk-9Hg" secondAttribute="trailing" constant="8" id="Hvr-Qw-Sfd"/>
<constraint firstItem="lTu-0Z-Tge" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" id="J1z-u5-gvv"/>
<constraint firstItem="lTu-0Z-Tge" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" id="OLV-Lf-DKV"/>
<constraint firstItem="9po-bu-c16" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="8" id="QjW-lL-FtU"/>
<constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="9po-bu-c16" secondAttribute="bottom" constant="8" symbolic="YES" id="RtA-G5-d6U"/>
<constraint firstItem="BL3-Qk-9Hg" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="8" id="SNc-kd-Xyj"/>
<constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="lTu-0Z-Tge" secondAttribute="bottom" constant="300" id="SSt-fZ-CNI"/>
<constraint firstItem="lTu-0Z-Tge" firstAttribute="trailing" secondItem="Q5M-cg-NOt" secondAttribute="trailing" id="YG3-5g-z3J"/>
<constraint firstItem="Crb-qO-me7" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="16" id="a7S-pR-2xD"/>
<constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="Qs9-zC-31a" secondAttribute="bottom" constant="8" symbolic="YES" id="cR7-NU-eXL"/>
<constraint firstItem="Cgo-YJ-V1h" firstAttribute="leading" secondItem="Crb-qO-me7" secondAttribute="leading" id="hXk-fs-S6Y"/>
<constraint firstItem="Crb-qO-me7" firstAttribute="top" secondItem="lTu-0Z-Tge" secondAttribute="bottom" constant="16" id="kC5-sR-XD2"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="Qs9-zC-31a" secondAttribute="trailing" constant="8" id="oJa-9R-5R5"/>
<constraint firstItem="Cgo-YJ-V1h" firstAttribute="top" secondItem="Crb-qO-me7" secondAttribute="bottom" constant="16" id="vTs-18-bkM"/>
</constraints>
<point key="canvasLocation" x="139" y="21"/>
</view>
</objects>
<resources>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

+ 60
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleBBQParamModel.h 查看文件

@@ -0,0 +1,60 @@
//
// ELMeatProbeBoxMultipleBleBBQParamModel.h
// AILinkComponentMeatProbe
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ELMeatProbeBoxMultipleBleBBQParamModel : NSObject

//生成时间
@property (nonatomic, assign) NSTimeInterval timestamp;

@property (nonatomic, strong) NSData *macData;
@property (nonatomic, copy, readonly) NSString *macString;

@property (nonatomic, assign) UInt8 state; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始

@property (nonatomic, assign) UInt8 foodType; //食物类型

@property (nonatomic, assign) UInt16 targetTemperatureRaw_C; //食物目标温度:℃
@property (nonatomic, assign, readonly) NSInteger targetTemperature_C;
@property (nonatomic, assign) UInt16 targetTemperatureRaw_F; //食物目标温度:℉
@property (nonatomic, assign, readonly) NSInteger targetTemperature_F;

@property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_C; //炉温目标下限:℃
@property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_C;
@property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_F; //炉温目标下限:℉
@property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_F;

@property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_C; //炉温目标上限:℃
@property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_C;
@property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_F; //炉温目标上限:℉
@property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_F;

@property (nonatomic, assign) float alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0

@property (nonatomic, assign) UInt32 cookingId; //CookingID

@property (nonatomic, assign) UInt32 workTime; //任务已运行时间,单位s

@property (nonatomic, assign) UInt32 timerCount; //计时剩余时间(秒)

@property (nonatomic, assign) UInt8 foodRawness; //食物熟度 (maturityID)

@property (nonatomic, strong) NSData *remarkData;

- (void)clearAll;

- (void)cookDone;

- (NSData *)dataValue;

- (NSString *)remark;
- (NSString *)setRemark:(NSString *)remark;

@end

NS_ASSUME_NONNULL_END

+ 128
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleBBQParamModel.m 查看文件

@@ -0,0 +1,128 @@
//
// ELMeatProbeBoxMultipleBleBBQParamModel.m
// AILinkComponentMeatProbe
//

#import "ELMeatProbeBoxMultipleBleBBQParamModel.h"

@implementation ELMeatProbeBoxMultipleBleBBQParamModel

- (NSString *)macString {
if (self.macData == nil || self.macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)self.macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (NSInteger)targetTemperature_C {
NSInteger value = self.targetTemperatureRaw_C & 0x3FFF;
if (self.targetTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)targetTemperature_F {
NSInteger value = self.targetTemperatureRaw_F & 0x3FFF;
if (self.targetTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMinTemperature_C {
NSInteger value = self.ambientMinTemperatureRaw_C & 0x3FFF;
if (self.ambientMinTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMinTemperature_F {
NSInteger value = self.ambientMinTemperatureRaw_F & 0x3FFF;
if (self.ambientMinTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMaxTemperature_C {
NSInteger value = self.ambientMaxTemperatureRaw_C & 0x3FFF;
if (self.ambientMaxTemperatureRaw_C & 0x4000) {
value = -value;
}
return value;
}

- (NSInteger)ambientMaxTemperature_F {
NSInteger value = self.ambientMaxTemperatureRaw_F & 0x3FFF;
if (self.ambientMaxTemperatureRaw_F & 0x4000) {
value = -value;
}
return value;
}

- (void)clearAll {
self.state = 0; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始
self.foodType = 0xFF; //食物类型
self.targetTemperatureRaw_C = 0;//0xFFFF; //食物目标温度:℃//
self.targetTemperatureRaw_F = 0;//0xFFFF; //食物目标温度:℉//
self.ambientMaxTemperatureRaw_C = 0;//0xFFFF; //炉温目标上限:℃
self.ambientMaxTemperatureRaw_F = 0;//0xFFFF; //炉温目标上限:℉
self.ambientMinTemperatureRaw_C = 0;//0xFFFF; //炉温目标下限:℃
self.ambientMinTemperatureRaw_F = 0;//0xFFFF; //炉温目标下限:℉
self.cookingId = 0; //CookingID
self.workTime = 0; //任务已运行时间,单位s
self.timerCount = 0; //计时剩余时间(秒)
self.alarmTemperaturePercent = 0.95; //提醒温度对目标温度百分比 0~1.0
self.foodRawness = 0xFF; //食物熟度
Byte bytes[32] = {0};
self.remarkData = [NSData dataWithBytes:bytes length:sizeof(bytes)];
}

- (void)cookDone {
[self clearAll];
}

- (NSData *)dataValue {
NSMutableData *data = [NSMutableData data];
[data appendData:self.macData];
UInt32 alarmTemperaturePercent = 0;
memcpy(&alarmTemperaturePercent, &(_alarmTemperaturePercent), sizeof(float));
Byte bytes[] = { self.state, self.foodType, self.targetTemperatureRaw_C, self.targetTemperatureRaw_C >> 8, self.targetTemperatureRaw_F, self.targetTemperatureRaw_F >> 8, self.ambientMaxTemperatureRaw_C, self.ambientMaxTemperatureRaw_C >> 8, self.ambientMaxTemperatureRaw_F, self.ambientMaxTemperatureRaw_F >> 8, self.ambientMinTemperatureRaw_C, self.ambientMinTemperatureRaw_C >> 8, self.ambientMinTemperatureRaw_F, self.ambientMinTemperatureRaw_F >> 8, self.cookingId, self.cookingId >> 8, self.cookingId >> 16, self.cookingId >> 24, self.workTime, self.workTime >> 8, self.workTime >> 16, self.workTime >> 24, self.timerCount, self.timerCount >> 8, self.timerCount >> 16, self.timerCount >> 24, alarmTemperaturePercent, alarmTemperaturePercent >> 8, alarmTemperaturePercent >> 16, alarmTemperaturePercent >> 24, self.foodRawness };
[data appendBytes:bytes length:sizeof(bytes)];
[data appendData:self.remarkData];
return data.copy;
}

- (NSString *)setRemark:(NSString *)remark {
NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding];
if (data.length > 32) {
while (data.length > 29) {
remark = [remark substringToIndex:(remark.length - 1)];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
remark = [remark stringByAppendingString:@"..."];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
Byte *bytes = (Byte *)data.bytes;
NSUInteger total = data.length > 32 ? 32 : data.length;
Byte remarkBytes[32] = {0};
memset(remarkBytes, 0, 32);
memcpy(remarkBytes, bytes, total);
self.remarkData = [NSData dataWithBytes:remarkBytes length:sizeof(remarkBytes)];
return [self remark];
}

- (NSString *)remark {
Byte remarkBytes[64] = {0};
[self.remarkData getBytes:remarkBytes length:self.remarkData.length];
NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes];
return str;
}


@end

+ 32
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleCmdTool.h 查看文件

@@ -0,0 +1,32 @@
//
// ELMeatProbeBoxMultipleBleCmdTool.h
// Pods
//

#import <Foundation/Foundation.h>
#import "ELMeatProbeBoxMultipleBleHeader.h"

NS_ASSUME_NONNULL_BEGIN

@class ELAILinkBleManager;
@class ELMeatProbeBoxMultipleStatusModel, ELMeatProbeBoxMultipleProbeStatusModel, ELMeatProbeBoxMultipleBleBBQParamModel;

@interface ELMeatProbeBoxMultipleBleCmdTool : NSObject

//APP获取设备状态数据(Type:0x01)
+ (NSData *)requestStatus:(ELAILinkBleManager *)manager;

//切换单位
+ (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager;

//获取设置烧烤任务参数(Type:0x09)
+ (NSData *)requestParams:(ELAILinkBleManager *)manager;
+ (NSData *)setupParam:(ELMeatProbeBoxMultipleBleBBQParamModel *)param supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager;

+ (void)requestIColornfoWithManager:(ELAILinkBleManager *)manager;

+ (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager;

@end

NS_ASSUME_NONNULL_END

+ 62
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleCmdTool.m 查看文件

@@ -0,0 +1,62 @@
//
// ELMeatProbeBoxMultipleBleCmdTool.m
// Pods
//

#import "ELMeatProbeBoxMultipleBleCmdTool.h"
#import <AILinkBleSDK/ELAILinkBleManager.h>
#import "ELMeatProbeBoxMultipleBleBBQParamModel.h"

@implementation ELMeatProbeBoxMultipleBleCmdTool

//APP获取设备基础信息
+ (NSData *)requestStatus:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeBoxMultipleCMDAppGetStatus_Type01;
Byte payloadBytes[] = { cmd, 0x01 };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

//切换单位
+ (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager {
if (unit > 1) { return nil; }
const Byte cmd = ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03;
Byte payloadBytes[] = { cmd, unit };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (NSData *)requestParams:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeBoxMultipleCMDSetInfo_Type09;
Byte payloadBytes[] = { cmd, 0x01 };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (NSData *)setupParam:(ELMeatProbeBoxMultipleBleBBQParamModel *)param supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager {
const Byte cmd = ELMeatProbeBoxMultipleCMDSetInfo_Type09;
Byte payloadBytes[] = { cmd, 0x00, supportProbe };
NSMutableData *payloadData = [NSMutableData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[payloadData appendData:param.dataValue];
[manager sendA7Payload:payloadData.copy];
return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
}

+ (void)requestIColornfoWithManager:(ELAILinkBleManager *)manager {
const Byte read = 0x00;
Byte payloadBytes[] = { ELMeatProbeBoxMultipleCMDColorInfoRequest_Type0B, read };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA7Payload:payloadData.copy];
}

+ (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager {
const Byte ELInetGetCmdTypeSpecialFunc = 0x49, op = 0x01;
Byte payloadBytes[16] = { ELInetGetCmdTypeSpecialFunc, op };
NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)];
[manager sendA6Payload:payloadData.copy];
}

@end

+ 36
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleHeader.h 查看文件

@@ -0,0 +1,36 @@
//
// ELMeatProbeBoxMultipleBleHeader.h
// Pods
//

#ifndef ELMeatProbeBoxMultipleBleHeader_h
#define ELMeatProbeBoxMultipleBleHeader_h

typedef NS_ENUM(UInt8, ELMeatProbeBoxMultipleCMD) {
ELMeatProbeBoxMultipleCMDAppGetStatus_Type01 = 0x01, //0x01:Type:APP 获取设备状态
ELMeatProbeBoxMultipleCMDMcuUploadStatus_Type02 = 0x02, //0x02:Type:设备上报设备数据

ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03 = 0x03, //0x03:Type:切换单位
ELMeatProbeBoxMultipleCCMDAppSwitchUnitResponse_Type04 = 0x04, //0x04:Type:切换单位 response

ELMeatProbeBoxMultipleCMDStartAlarmState_Type05 = 0x05, //0x05:Type:发起报警
ELMeatProbeBoxMultipleCMDStartAlarmStateResponse_Type06 = 0x06, //0x06:Type:发起报警 Response

ELMeatProbeBoxMultipleCMDStoplAlarm_Type07 = 0x07, //0x07:Type:停止报警
ELMeatProbeBoxMultipleCMDStoplAlarmResponse_Type08 = 0x08, //0x08:Type:停止报警 Response
ELMeatProbeBoxMultipleCMDSetInfo_Type09 = 0x09, //Type:0x09 设置烧烤任务参数
ELMeatProbeBoxMultipleCMDSetInfoResponse_Type0A = 0x0A, //Type:0x0A 设置烧烤任务参数 Response
ELMeatProbeBoxMultipleCMDColorInfoRequest_Type0B = 0x0B,
ELMeatProbeBoxMultipleCMDColorInfoResponse_Type0C = 0x0C,
};

#endif /* ELMeatProbeBoxMultipleBleHeader_h */

+ 128
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleParser.h 查看文件

@@ -0,0 +1,128 @@
//
// ELMeatProbeBoxMultipleBleParser.h
// Pods
//

#import <Foundation/Foundation.h>
#import <AILinkBleSDK/ELSDKHeader.h>
#import "ELMeatProbeBoxMultipleBleHeader.h"

NS_ASSUME_NONNULL_BEGIN

@protocol ELMeatProbeBoxMultipleBleParserDelegate;
@class ELMeatProbeBoxMultipleStatusModel, ELMeatProbeBoxMultipleProbeStatusModel, ELMeatProbeBoxMultipleBleBBQParamModel, ELMeatProbeBoxMultipleColorInfoModel;

@interface ELMeatProbeBoxMultipleBleParser : NSObject

@property (nonatomic, copy) NSString *mac;

@property (nonatomic, assign) NSInteger deviceId;

@property (nonatomic, weak) id<ELMeatProbeBoxMultipleBleParserDelegate> delegate;

- (void)parseA7Payload:(NSData *)payload;

@end

@protocol ELMeatProbeBoxMultipleBleParserDelegate <NSObject>

@optional

//MCU 上报设备状态数据(Type:02)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser boxStatus:(ELMeatProbeBoxMultipleStatusModel *)boxStatus;

//切换单位(Type:04)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser unit:(ELDeviceTemperatureUnit)unit;

//报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac;

//取消报警(Type:08)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac;

//烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务
//- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeBoxMultipleBleBBQParamModel *>*)params;
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe param:(ELMeatProbeBoxMultipleBleBBQParamModel *)param;

//color info
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser colorInfos:(NSArray<ELMeatProbeBoxMultipleColorInfoModel *> *)colorInfos;

@end

@interface ELMeatProbeBoxMultipleStatusModel : NSObject

//设备状态数据协议版本:0x01
@property (nonatomic, assign) UInt8 version;

//支持的探针数量 M
@property (nonatomic, assign) UInt8 probeMaxCount;

//当前连接的探针数量 N
@property (nonatomic, assign) UInt8 probeCount;

//充电盒电池状态
@property (nonatomic, assign) UInt8 batteryStatus;
@property (nonatomic, assign) UInt8 batteryPercent;

//温度单位
@property (nonatomic, assign) ELDeviceTemperatureUnit unit;

//生成时间
@property (nonatomic, assign) NSTimeInterval timestamp;


@property (nonatomic, strong) NSArray <ELMeatProbeBoxMultipleProbeStatusModel *>*probeStatusArray;

//盒子 mac 地址
@property (nonatomic, copy) NSString *boxMacString;

@end

@interface ELMeatProbeBoxMultipleProbeStatusModel : NSObject

//探针编号(1-M) 无法确定可连 接探针的编号,上报 0xFF
@property (nonatomic, assign) UInt8 index;

//探针 mac 地址
@property (nonatomic, strong) NSData *macData;
@property (nonatomic, copy, readonly) NSString *macString;
//
//食物温度
@property (nonatomic, assign, readonly) NSInteger internalTemperature_C;
@property (nonatomic, assign, readonly) NSInteger internalTemperature_F;
//@property (nonatomic, assign, readonly) NSInteger internalTemperature;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit internalTemperatureUnit;
@property (nonatomic, assign) UInt16 internalTemperatureRaw;
//
//环境温度
@property (nonatomic, assign, readonly) NSInteger ambientTemperature_C;
@property (nonatomic, assign, readonly) NSInteger ambientTemperature_F;
//@property (nonatomic, assign, readonly) NSInteger ambientTemperature;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit ambientTemperatureUnit;
@property (nonatomic, assign) UInt16 ambientTemperatureRaw;
//
//探针电池状态
@property (nonatomic, assign) UInt8 batteryStatus;
@property (nonatomic, assign) UInt8 batteryPercent;
@property (nonatomic, assign) UInt8 batteryRaw;

//探针插入食物状态
@property (nonatomic, assign) UInt8 insertStatusRaw;

//探针连接状态
@property (nonatomic, assign) UInt8 connectState;

//生成时间
@property (nonatomic, assign) double timestamp;

@end

@interface ELMeatProbeBoxMultipleColorInfoModel : NSObject

@property (nonatomic, assign) UInt8 probeIndex;

@property (nonatomic, assign) UInt8 colorIndex;

@end

NS_ASSUME_NONNULL_END

+ 325
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/BLE/ELMeatProbeBoxMultipleBleParser.m 查看文件

@@ -0,0 +1,325 @@
//
// ELMeatProbeBoxMultipleBleParser.m
// Pods
//

#import "ELMeatProbeBoxMultipleBleParser.h"
#import "ELMeatProbeBoxMultipleBleBBQParamModel.h"


@interface ELMeatProbeBoxMultipleBleParser()

//@property (nonatomic, strong) ELMeatProbeBoxMultipleStatusModel *boxStatus;

@end

@implementation ELMeatProbeBoxMultipleBleParser

- (void)parseA7Payload:(nonnull NSData *)payload {
NSLog(@"#### parseA7Payload payload:%@", payload.debugDescription);
Byte *bytes = (Byte *)payload.bytes;
Byte cmd = bytes[0];
switch (cmd) {
case ELMeatProbeBoxMultipleCMDMcuUploadStatus_Type02:
[self parseStatusData:payload];
break;
case ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03:
{
UInt8 unit = bytes[1];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:unit:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self unit:unit];
}
}
break;
// case ELMeatProbeBoxMultipleCCMDAppSwitchUnitResponse_Type04:
// break;
case ELMeatProbeBoxMultipleCMDStartAlarmState_Type05: {
Byte i = 1;
NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)];
i += 6;
Byte state = bytes[i];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:startAlarmWithState:macData:mac:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self startAlarmWithState:state macData:macData mac:[self macDataToString:macData]];
}
}
break;
// case ELMeatProbeBoxMultipleCMDStartAlarmStateResponse_Type06:
// break;
case ELMeatProbeBoxMultipleCMDStoplAlarm_Type07: {
Byte i = 1;
NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)];
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:stopAlarmWithMacData:mac:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self stopAlarmWithMacData:macData mac:[self macDataToString:macData]];
}
}
break;
// case ELMeatProbeBoxMultipleCMDStoplAlarmResponse_Type08:
// break;
case ELMeatProbeBoxMultipleCMDSetInfo_Type09:
case ELMeatProbeBoxMultipleCMDSetInfoResponse_Type0A: {
[self parseSetInfoParamData:payload];
}
break;
case ELMeatProbeBoxMultipleCMDColorInfoResponse_Type0C: {
NSMutableArray *arr = [NSMutableArray array];
if (payload.length < 4) break;
for (int i=2; i<payload.length; i+=2) {
UInt8 probeIndex = bytes[i];
UInt8 colorIndex = bytes[i+1];
ELMeatProbeBoxMultipleColorInfoModel *color = [[ELMeatProbeBoxMultipleColorInfoModel alloc] init];
color.probeIndex = probeIndex;
color.colorIndex = colorIndex;
[arr addObject:color];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:colorInfos:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self colorInfos:arr.copy];
}
}
default:
break;
}
}

- (NSString *)macDataToString:(NSData *)macData {
if (macData == nil || macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (void)parseSetInfoParamData:(NSData *)payload {
NSLog(@"##### parseSetInfoParamData payload:%@", payload.debugDescription);
if (payload.length != 72 && payload.length != 140) {
return;
}
Byte *bytes = (Byte *)payload.bytes;
Byte i = 1;
Byte option = bytes[i];
i = 2;
Byte supportProbe = bytes[i];
i = 3;
// NSMutableArray *arr = [NSMutableArray array];
//
// while (i < payload.length) {
//探针 1
ELMeatProbeBoxMultipleBleBBQParamModel *param = [[ELMeatProbeBoxMultipleBleBBQParamModel alloc] init];
param.timestamp = [NSDate date].timeIntervalSince1970;
param.macData = [payload subdataWithRange:NSMakeRange(i, 6)];
i += 6;
param.state = bytes[i];
i += 1;
param.foodType = bytes[i];//if (param.foodType == 0xFF) param.foodType = 0xFE;
i += 1;
param.targetTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.targetTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMaxTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMaxTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMinTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.ambientMinTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8);
i += 2;
param.cookingId = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
param.workTime = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
param.timerCount = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
i += 4;
UInt32 alarmTemperaturePercentU32 = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
float alarmTemperaturePercent = 0;
memcpy(&alarmTemperaturePercent, &alarmTemperaturePercentU32, sizeof(UInt32));
param.alarmTemperaturePercent = alarmTemperaturePercent;
i += 4;
param.foodRawness = bytes[i];
i += 1;
param.remarkData = [payload subdataWithRange:NSMakeRange(i, 32)];
i += 32;
if (param.state == 0) {
[param clearAll];
}
// [arr addObject:param];
// }
// if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:option:supportProbe:params:)]) {
// [self.delegate meatProbeBoxMultipleBleParser:self option:option supportProbe:supportProbe params:arr.copy];
// }
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:option:supportProbe:param:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self option:option supportProbe:supportProbe param:param];
}
}

- (void)parseStatusData:(NSData *)payload {
if (payload.length != 25 && payload.length != 41) {
return;
}
Byte *bytes = (Byte *)payload.bytes;
ELMeatProbeBoxMultipleStatusModel *boxStatus = [[ELMeatProbeBoxMultipleStatusModel alloc] init];
boxStatus.timestamp = [NSDate date].timeIntervalSince1970;
boxStatus.boxMacString = self.mac;
Byte i = 1;
boxStatus.version = bytes[i];
i = 2;
boxStatus.probeMaxCount = bytes[i];
i = 3;
boxStatus.probeCount = bytes[i];
i = 4;
boxStatus.batteryStatus = bytes[i] >> 7;
boxStatus.batteryPercent = bytes[i] & 0x7F;
i = 5;
boxStatus.unit = bytes[i];
i = 9;
NSMutableArray *arr = [NSMutableArray array];
while (i < payload.length) {
ELMeatProbeBoxMultipleProbeStatusModel *probeStatus = [[ELMeatProbeBoxMultipleProbeStatusModel alloc] init];
probeStatus.timestamp = boxStatus.timestamp;
probeStatus.index = bytes[i];
i += 1;
NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)];
probeStatus.macData = macData;
i += 6;
probeStatus.internalTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8);
// 凸(艹皿艹 ) //0x639F (25503)
if (probeStatus.internalTemperatureRaw == 25503) {
probeStatus.internalTemperatureRaw = 0xFFFF;
}
i += 2;
probeStatus.ambientTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8);
// 凸(艹皿艹 ) //0x639F (25503)
if (probeStatus.ambientTemperatureRaw == 25503) {
probeStatus.ambientTemperatureRaw = 0xFFFF;
}
i += 2;
probeStatus.batteryStatus = bytes[i] >> 7;
probeStatus.batteryPercent = bytes[i] & 0x7F;
probeStatus.batteryRaw = bytes[i];
i += 1;
probeStatus.insertStatusRaw = bytes[i];
i += 1;
probeStatus.connectState = bytes[i];
i += 3;
[arr addObject:probeStatus];
}
boxStatus.probeStatusArray = arr.copy;
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:boxStatus:)]) {
[self.delegate meatProbeBoxMultipleBleParser:self boxStatus:boxStatus];
}
}

@end

@implementation ELMeatProbeBoxMultipleStatusModel

@end

@implementation ELMeatProbeBoxMultipleProbeStatusModel

- (NSString *)macString {
if (self.macData == nil || self.macData.length != 6) {
return nil;
}
Byte *bytes = (Byte *)self.macData.bytes;
NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]];
return str;
}

- (NSInteger)internalTemperature_C {
if (self.internalTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_C) {
return self.internalTemperature;
}
return [self convertF2C:self.internalTemperature];
}

- (NSInteger)internalTemperature_F {
if (self.internalTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_F) {
return self.internalTemperature;
}
return [self convertC2F:self.internalTemperature];
}

- (NSInteger)internalTemperature {
NSInteger value = self.internalTemperatureRaw & 0x3FFF;
if (self.internalTemperatureRaw & 0x4000) {
value = -value;
}
return value;
}

- (ELDeviceTemperatureUnit)internalTemperatureUnit {
return self.internalTemperatureRaw >> 15;
}

- (NSInteger)ambientTemperature_C {
if (self.ambientTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_C) {
return self.ambientTemperature;
}
return [self convertF2C:self.ambientTemperature];
}

- (NSInteger)ambientTemperature_F {
if (self.ambientTemperatureRaw == 0xFFFF) {
return 0xFFFF;
}
if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_F) {
return self.ambientTemperature;
}
return [self convertC2F:self.ambientTemperature];
}

- (NSInteger)ambientTemperature {
NSInteger value = self.ambientTemperatureRaw & 0x3FFF;
if (self.ambientTemperatureRaw & 0x4000) {
value = -value;
}
return value;
}

- (ELDeviceTemperatureUnit)ambientTemperatureUnit {
return self.ambientTemperatureRaw >> 15;
}

//温度换算
- (float)convertF2C:(float)fahrenheit {
return round((fahrenheit - 32) / 1.8);
}

- (float)convertC2F:(float)celsius {
return round(celsius * 1.8 + 32);
}

@end

@implementation ELMeatProbeBoxMultipleColorInfoModel

@end

+ 21
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.h 查看文件

@@ -0,0 +1,21 @@
//
// MultipleMeatProbeBoxVC.h
// AILinkBleSDK_Example
//
// Created by LarryZhang on 2024/9/7.
// Copyright © 2024 zhengzida. All rights reserved.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@class ELAILinkPeripheral;

@interface MultipleMeatProbeBoxVC : UIViewController

@property (nonatomic, strong) ELAILinkPeripheral *per;

@end

NS_ASSUME_NONNULL_END

+ 334
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.m 查看文件

@@ -0,0 +1,334 @@
//
// MultipleMeatProbeBoxVC.m
// AILinkBleSDK_Example
//
// Created by LarryZhang on 2024/9/7.
// Copyright © 2024 zhengzida. All rights reserved.
//

#import "MultipleMeatProbeBoxVC.h"
#import <AILinkBleSDK/ELAILinkBleManager.h>
#import <AILinkBleSDK/NSData+AILinkBle.h>
#import "ELMeatProbeBoxMultipleBleParser.h"
#import "ELMeatProbeBoxMultipleBleCmdTool.h"
#import "ELMeatProbeBoxMultipleBleBBQParamModel.h"

@interface MultipleMeatProbeBoxVC () <ELAILinkBleManagerDelegate, ELMeatProbeBoxMultipleBleParserDelegate>

@property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel;

@property (weak, nonatomic) IBOutlet UITextView *logTextView;


@property (nonatomic, strong) ELAILinkBleManager *bleManager;

@property (nonatomic, assign) NELBleManagerConnectState bleConnectState;

@property (nonatomic, strong) ELMeatProbeBoxMultipleBleParser *bleParser;

@property (nonatomic, strong) ELMeatProbeBoxMultipleStatusModel *boxStatus;

@property (nonatomic, strong) NSMutableDictionary<NSString *, ELMeatProbeBoxMultipleBleBBQParamModel *> *paramDic;

@end

@implementation MultipleMeatProbeBoxVC

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.

self.paramDic = [NSMutableDictionary dictionary];
[self initBle];
}

- (void)dealloc {
[self deinitBle];
}

#pragma mark - Action

- (IBAction)requestParamsAction:(id)sender {
NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool requestParams:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload];
[self addLog:log];
}

- (IBAction)setupParamsAction:(id)sender {
if (self.boxStatus == nil) {
return;
}
if (self.boxStatus.probeStatusArray == nil || self.boxStatus.probeStatusArray.count != 2) {
return;
}
ELMeatProbeBoxMultipleProbeStatusModel *status = self.boxStatus.probeStatusArray.firstObject;
if (status == nil) {
return;
}
ELMeatProbeBoxMultipleBleBBQParamModel *param = self.paramDic[status.macString];
if (param == nil) {
return;
}
param.foodType = 1;
param.targetTemperatureRaw_C = 70;
param.targetTemperatureRaw_F = 212;
param.foodRawness = 1;
param.alarmTemperaturePercent = 0.95;
param.cookingId = [NSDate date].timeIntervalSince1970;
NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool setupParam:param supportProbe:1 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload];
[self addLog:log];
}

- (IBAction)switchUnitAction:(id)sender {
if (self.boxStatus.unit == 1) {
NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool switchUnit:0 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℃", payload];
[self addLog:log];
} else {
NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool switchUnit:1 manager:self.bleManager];
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℉", payload];
[self addLog:log];
}
}

- (IBAction)clearAction:(id)sender {
self.logTextView.text = @"";
}

- (IBAction)shareLogAction:(id)sender {
}


#pragma mark - ELAILinkBleManagerDelegate

- (void)initBle {
self.bleManager = [[ELAILinkBleManager alloc] init];
self.bleManager.ailinkDelegate = self;
self.bleParser = [[ELMeatProbeBoxMultipleBleParser alloc] init];
self.bleParser.delegate = self;
self.bleParser.mac = self.per.macAddressString;
}

- (void)scanBle {
[self.bleManager scanFilterWithCidArray:@[@(self.per.cid)]];
}

- (void)connect:(ELAILinkPeripheral *)peripheral {
[self.bleManager connectAILinkPeripheral:peripheral];
[self updateBleStatusView:NELBleManagerConnectStateConnecting];
}

- (void)deinitBle {
[self.bleManager stopScan];
self.bleManager.ailinkDelegate = nil;
[self.bleManager disconnectPeripheral];
}

- (void)updateBleStatusView:(NELBleManagerConnectState)state {
if (self.bleManager.central.state == CBManagerStatePoweredOff) {
self.bleStatusLabel.text = @"蓝牙关闭";
[self addLog:self.bleStatusLabel.text];
return;
}
switch (state) {
case NELBleManagerConnectStateDisconnected:
self.bleStatusLabel.text = @"断开连接";
break;
case NELBleManagerConnectStateFailed:
case NELBleManagerConnectStateFailedValidation:
self.bleStatusLabel.text = @"连接失败";
break;
case NELBleManagerConnectStateConnecting:
self.bleStatusLabel.text = @"正在连接...";
break;
case NELBleManagerConnectStatePassed:
self.bleStatusLabel.text = @"连接成功";

[self requestDeviceData];
// read RSSI
// [self readRSSI];
break;
case NELBleManagerConnectStateCentralScanning:
self.bleStatusLabel.text = @"正在扫描...";
break;

default:
break;
}
[self addLog:self.bleStatusLabel.text];
}

- (void)requestDeviceData {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{
});
}

// 设备状态变更
- (void)managerDidUpdateState:(CBCentralManager *)central {
NSLog(@"%s state:%@", __func__, @(central.state));
if (central.state == CBManagerStatePoweredOn) {
[self scanBle];
} else if (central.state == CBManagerStatePoweredOff) {
self.bleConnectState = NELBleManagerConnectStateCentralPowerOff;
[self updateBleStatusView:NELBleManagerConnectStateCentralPowerOff];
}
}

- (void)managerScanState:(BOOL)scanning {
NSLog(@"%s scanning:%@", __func__, @(scanning));
if (scanning) {
[self updateBleStatusView:NELBleManagerConnectStateCentralScanning];
}
}

// 扫描到设备
- (void)managerDidDiscoverPeripheral:(ELAILinkPeripheral *)peripheral {
NSLog(@"managerDidDiscoverPeripheral cid:%02x vid:%02x pid:%02x mac:%@", peripheral.cid, peripheral.vid, peripheral.pid, peripheral.macAddressString);
if ([peripheral.macAddressString isEqualToString:self.per.macAddressString]) {
[self.bleManager stopScan];
[self connect:peripheral];
}
}

- (void)managerDidUpdateConnect:(NELBleManagerConnectState)state {
NSLog(@"%s NELBleManagerConnectState:%@", __func__, @(state));
[self updateBleStatusView:state];
}

//A7数据
- (void)aiLinkBleReceiveA7Data:(NSData *)payload {
NSLog(@"%s #### payload:%@", __func__, payload.elHexString);
NSString *log = [NSString stringWithFormat:@"收: payload: %@", payload.elHexString];
[self addLog:log];
[self.bleParser parseA7Payload:payload];
}

//A6数据
- (void)aiLinkBleReceiveA6Data:(NSData *)packet {
NSLog(@"%s ##### packet:%@", __func__, packet.elHexString);
Byte *bytes = (Byte *)packet.bytes;
Byte cmd = bytes[2];
if (cmd == ELInetGetCmdTypeGetBMVersion) {
NSString *bmVersion = self.bleManager.bmVersion;
NSLog(@"##### bmVersion: %@", bmVersion);
}
if (cmd == ELInetGetCmdTypeGetBMVersionPro) {
NSString *bmVersionPro = self.bleManager.bmVersionPro;
NSLog(@"##### bmVersionPro: %@", bmVersionPro);
}
}

#pragma mark - ELMeatProbeBoxMultipleBleParserDelegate

//MCU 上报设备状态数据(Type:02)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser boxStatus:(ELMeatProbeBoxMultipleStatusModel *)boxStatus { NSLog(@"##### boxStatus(): %@ ", boxStatus.debugDescription);
self.boxStatus = boxStatus;
NSString *log = [NSString stringWithFormat:@"boxStatus: { mac:%@, probeMaxCount:%hhu, probeCount:%hhu, batteryStatus:%hhu batteryPercent:%hhu, unit:%ld }", parser.mac, boxStatus.probeMaxCount, boxStatus.probeCount, boxStatus.batteryStatus, boxStatus.batteryPercent, boxStatus.unit];
[self addLog:log];
ELMeatProbeBoxMultipleProbeStatusModel *probeStatus = boxStatus.probeStatusArray.firstObject;
NSString *log2 = [NSString stringWithFormat:@"probeStatus: { connectState:%@ macString:%hhu, internalTemperature_C:%ld ℃, ambientTemperature_C:%ld ℃ batteryStatus:%hhu batteryPercent:%hhu }", probeStatus.macString, probeStatus.connectState, probeStatus.internalTemperature_C, probeStatus.ambientTemperature_C, probeStatus.batteryStatus, probeStatus.batteryPercent];
[self addLog:log2];
}

//切换单位(Type:04)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser unit:(ELDeviceTemperatureUnit)unit {
NSLog(@"##### unit(): %@", @(unit));
NSString *log = [NSString stringWithFormat:@"unit(): %@", unit == 0 ? @"℃" : @"°F"];
[self addLog:log];
}

//报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac {
NSLog(@"##### box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state));
NSString *log = [NSString stringWithFormat:@"box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)];
[self addLog:log];
}

//取消报警(Type:08)
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac {
NSLog(@"##### box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac);
NSString *log = [NSString stringWithFormat:@"box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac];
[self addLog:log];
}

//烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe param:(ELMeatProbeBoxMultipleBleBBQParamModel *)param {
NSLog(@"##### box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe));
NSLog(@"##### param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType));
self.paramDic[param.macString] = param;
NSString *log = [NSString stringWithFormat:@"box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)];
NSString *log2 = [NSString stringWithFormat:@"param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)];
[self addLog:log];
[self addLog:log2];
}

//color info
- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser colorInfos:(NSArray<ELMeatProbeBoxMultipleColorInfoModel *> *)colorInfos {
NSLog(@"##### colorInfos(): %@", colorInfos);
if (colorInfos == nil || colorInfos.count == 0) {
return;
}
ELMeatProbeBoxMultipleColorInfoModel *colorInfo = colorInfos.firstObject;
NSString *log = [NSString stringWithFormat:@"colorInfos(): count:%@ probeIndex:%@ colorIndex:%@", @(colorInfos.count), @(colorInfo.probeIndex), @(colorInfo.colorIndex)];
[self addLog:log];
}

- (void)peripheralDidReadRSSI:(nonnull NSNumber *)RSSI {
NSLog(@"%s peripheralDidReadRSSI: %@", __FUNCTION__, RSSI);
// main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self addLog:[NSString stringWithFormat:@"peripheralDidReadRSSI: %@", RSSI]];
});
[self readRSSI];
}

- (void)readRSSI {
// delay
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// Read RSSI
[self.bleManager readRSSI];
});
}

#pragma mark - addLog

- (void)addLog:(NSString *)log {
[self addLog:log newline:YES];
}

- (void)addWithoutNewlineLog:(NSString *)log {
[self addLog:log newline:NO];
}

- (void)addLog:(NSString *)log newline:(BOOL)newline {
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = @"HH:mm:ss.SSS";
NSString *time = [format stringFromDate:[NSDate date]];
if (newline) {
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log];
} else {
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log];
}
[self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)];
}



@end

+ 133
- 0
AILinkBleSDKDemo/DeviceDemo/27C_MultipleMeatProbeBox/MultipleMeatProbeBoxVC.xib 查看文件

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MultipleMeatProbeBoxVC">
<connections>
<outlet property="bleStatusLabel" destination="avL-mp-uk3" id="FYb-Yh-SLT"/>
<outlet property="logTextView" destination="E0x-Fa-KTj" id="N8a-h2-SB1"/>
<outlet property="view" destination="x2W-jW-2kg" id="rLz-wJ-z8g"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="x2W-jW-2kg">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="avL-mp-uk3">
<rect key="frame" x="0.0" y="59" width="393" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vOk-FU-BQ9">
<rect key="frame" x="8" y="380" width="377" height="438"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E0x-Fa-KTj">
<rect key="frame" x="0.0" y="0.0" width="377" height="438"/>
<color key="backgroundColor" systemColor="labelColor"/>
<color key="textColor" systemColor="opaqueSeparatorColor"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="10"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="E0x-Fa-KTj" secondAttribute="bottom" id="BHm-cI-1oG"/>
<constraint firstItem="E0x-Fa-KTj" firstAttribute="leading" secondItem="vOk-FU-BQ9" secondAttribute="leading" id="KII-WE-P1m"/>
<constraint firstAttribute="trailing" secondItem="E0x-Fa-KTj" secondAttribute="trailing" id="nXv-F2-TLg"/>
<constraint firstItem="E0x-Fa-KTj" firstAttribute="top" secondItem="vOk-FU-BQ9" secondAttribute="top" id="uJZ-ST-1Ce"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="e5p-j3-6VG">
<rect key="frame" x="354" y="342" width="31" height="30"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="分享"/>
<connections>
<action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="8YU-6o-y8q"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FoH-SE-VcD">
<rect key="frame" x="8" y="342" width="31" height="30"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="清空"/>
<connections>
<action selector="clearAction:" destination="-1" eventType="touchUpInside" id="sBW-gD-CZ4"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d8W-4g-bv9">
<rect key="frame" x="16" y="96" width="128" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="请求烧烤参数"/>
<connections>
<action selector="requestParamsAction:" destination="-1" eventType="touchUpInside" id="zbL-Pf-OOz"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RUX-kg-vMY">
<rect key="frame" x="176" y="96" width="128" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="设置烧烤参数"/>
<connections>
<action selector="setupParamsAction:" destination="-1" eventType="touchUpInside" id="acG-k5-idT"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dXn-E2-KcZ">
<rect key="frame" x="16" y="147" width="93.333333333333329" height="35"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" title="单位切换"/>
<connections>
<action selector="switchUnitAction:" destination="-1" eventType="touchUpInside" id="rCn-DM-9bK"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="NHq-9v-F7t"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="d8W-4g-bv9" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="16" id="0mG-RC-xTr"/>
<constraint firstItem="RUX-kg-vMY" firstAttribute="leading" secondItem="d8W-4g-bv9" secondAttribute="trailing" constant="32" id="4o1-mG-nyl"/>
<constraint firstItem="avL-mp-uk3" firstAttribute="top" secondItem="NHq-9v-F7t" secondAttribute="top" id="65b-CM-qWc"/>
<constraint firstItem="dXn-E2-KcZ" firstAttribute="leading" secondItem="d8W-4g-bv9" secondAttribute="leading" id="MnL-Ml-ELh"/>
<constraint firstItem="NHq-9v-F7t" firstAttribute="bottom" secondItem="vOk-FU-BQ9" secondAttribute="bottom" id="P3H-ha-my3"/>
<constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="avL-mp-uk3" secondAttribute="bottom" constant="300" id="XgC-e8-meq"/>
<constraint firstItem="dXn-E2-KcZ" firstAttribute="top" secondItem="d8W-4g-bv9" secondAttribute="bottom" constant="16" id="Y47-BO-ap7"/>
<constraint firstItem="vOk-FU-BQ9" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="8" id="bI5-zC-F3I"/>
<constraint firstItem="RUX-kg-vMY" firstAttribute="top" secondItem="d8W-4g-bv9" secondAttribute="top" id="hGc-8d-bah"/>
<constraint firstItem="avL-mp-uk3" firstAttribute="trailing" secondItem="NHq-9v-F7t" secondAttribute="trailing" id="jgI-X6-rF6"/>
<constraint firstItem="FoH-SE-VcD" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="8" id="ldB-hE-eps"/>
<constraint firstItem="d8W-4g-bv9" firstAttribute="top" secondItem="avL-mp-uk3" secondAttribute="bottom" constant="16" id="mb7-bS-812"/>
<constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="e5p-j3-6VG" secondAttribute="bottom" constant="8" symbolic="YES" id="mlJ-Mg-6V6"/>
<constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="FoH-SE-VcD" secondAttribute="bottom" constant="8" symbolic="YES" id="szh-L5-bcY"/>
<constraint firstItem="avL-mp-uk3" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" id="uaL-lJ-HX1"/>
<constraint firstItem="NHq-9v-F7t" firstAttribute="trailing" secondItem="vOk-FU-BQ9" secondAttribute="trailing" constant="8" id="wtV-so-dPb"/>
<constraint firstItem="NHq-9v-F7t" firstAttribute="trailing" secondItem="e5p-j3-6VG" secondAttribute="trailing" constant="8" id="z2L-Bc-1Rc"/>
</constraints>
<point key="canvasLocation" x="139" y="21"/>
</view>
</objects>
<resources>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

+ 71
- 0
AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/Ble/ELMeatProbeBoxCustomDataModel.h 查看文件

@@ -0,0 +1,71 @@
//
// ELMeatProbeBoxCustomDataModel.h
// AILinkBleSDK
//

#import <Foundation/Foundation.h>
#import <AILinkBleSDK/ELMeatProbeBleTool.h>

NS_ASSUME_NONNULL_BEGIN

#pragma pack(1)
typedef struct ELMeatProbeBoxCustomDataStruct {

UInt8 ver; //数据版本 (当前版本 0x02) //数据总长度: modelSize()

UInt32 cookingId; //烧烤id (选择食物的时间戳)

UInt8 foodType; //食物类型
UInt8 foodRawness; //食物熟度
SInt16 targetTemperature_C; //食物目标温度:℃
SInt16 targetTemperature_F; //食物目标温度:℉

SInt16 ambientMinTemperature_C; //炉温目标下限:℃
SInt16 ambientMinTemperature_F; //炉温目标下限:℉

SInt16 ambientMaxTemperature_C; //炉温目标上限:℃
SInt16 ambientMaxTemperature_F; //炉温目标上限:℉
double alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0

UInt32 timerStart;//计时开始时间戳
UInt32 timerEnd;//计时结束时间戳

UInt8 currentUnit; //当前温度单位
SInt16 alarmTemperature_C; //食物提醒温度:℃
SInt16 alarmTemperature_F; //食物提醒温度:℉
UInt8 remarkBytes[32]; //UTF-8 编码
} ELMeatProbeBoxCustomDataStruct;
#pragma pack()

///
@interface ELMeatProbeBoxCustomDataModel : NSObject


@property (nonatomic, assign) ELMeatProbeBoxCustomDataStruct customStruct;

@property (nonatomic, assign, readonly) NSInteger ver;
@property (nonatomic, assign, readonly) NSInteger cookingId;
@property (nonatomic, assign, readonly) NSInteger foodType;
@property (nonatomic, assign, readonly) NSInteger foodRawness;

@property (nonatomic, assign) double alarmTemperaturePercent;
@property (nonatomic, assign, readonly) ELDeviceTemperatureUnit currentUnit;


+ (instancetype _Nullable)modelWithData:(NSData *)data;

- (NSData *)dataValue;

- (NSString *)remark;
- (NSString *)setRemark:(NSString *)remark;

+ (Byte)version;
+ (Byte)modelSize;

@end

NS_ASSUME_NONNULL_END

+ 154
- 0
AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/Ble/ELMeatProbeBoxCustomDataModel.m 查看文件

@@ -0,0 +1,154 @@
//
// ELMeatProbeCustomDataModel.m
// AILinkBleSDK
//

#import "ELMeatProbeBoxCustomDataModel.h"
#import <AILinkBleSDK/NSData+AILinkBle.h>
#import <objc/runtime.h>


@implementation ELMeatProbeBoxCustomDataModel

+ (Byte)version {
return 0x02;
}

+ (Byte)modelSize {
return 128;//sizeof(ELMeatProbeCustomDataStruct);
}

+ (instancetype)modelWithData:(NSData *)data {
if (data.length != self.modelSize) {
NSLog(@"数据长度错误 data:%@", data);
return nil;
}
ELMeatProbeBoxCustomDataModel *model = [[ELMeatProbeBoxCustomDataModel alloc] initWithData:data];
if (model.customStruct.ver != self.version) {
NSLog(@"数据版本错误 data:%@", data);
return nil;
}
return model;
}

- (instancetype)init {
if (self = [super init]) {
memset(&_customStruct, 0, sizeof(_customStruct));
_customStruct.ver = self.class.version;
}
return self;
}

- (instancetype)initWithData:(NSData *)data {
if (self = [self init]) {
memcpy(&_customStruct, data.bytes, sizeof(_customStruct));
}
return self;
}

- (NSData *)dataValue {
Byte bytes[128] = {0x00};
memcpy(bytes, &_customStruct, sizeof(_customStruct));
return [NSData dataWithBytes:bytes length:sizeof(bytes)];
}

- (NSString *)remark {
char *remarkBytes[64] = {0};
memcpy(remarkBytes, _customStruct.remarkBytes, sizeof(_customStruct.remarkBytes));
NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes];
return str;
}

- (NSString *)setRemark:(NSString *)remark {
NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding];
if (data.length > 32) {
while (data.length > 29) {
remark = [remark substringToIndex:(remark.length - 1)];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
remark = [remark stringByAppendingString:@"..."];
data = [remark dataUsingEncoding:NSUTF8StringEncoding];
}
Byte *bytes = (Byte *)data.bytes;
NSUInteger total = data.length > 32 ? 32 : data.length;
memset(_customStruct.remarkBytes, 0, 32);
memcpy(_customStruct.remarkBytes, bytes, total);
return [self remark];
}

#pragma mark setter getter

- (NSInteger)ver {
return self.customStruct.ver;
}

- (NSInteger)cookingId {
return self.customStruct.cookingId;
}

- (NSInteger)foodType {
return self.customStruct.foodType;
}

- (NSInteger)foodRawness {
return self.customStruct.foodRawness;
}

- (double)alarmTemperaturePercent {
return self.customStruct.alarmTemperaturePercent;
}

- (NSInteger)alarmTemperature_C {
return self.customStruct.alarmTemperature_C;
}

- (NSInteger)alarmTemperature_F {
return self.customStruct.alarmTemperature_F;
}

- (void)setAlarmTemperaturePercent:(double)alarmTemperaturePercent {
_customStruct.alarmTemperaturePercent = alarmTemperaturePercent;
_customStruct.alarmTemperature_C = _customStruct.targetTemperature_C * alarmTemperaturePercent;
_customStruct.alarmTemperature_F = _customStruct.targetTemperature_F * alarmTemperaturePercent;
}

- (ELDeviceTemperatureUnit)currentUnit {
return self.customStruct.currentUnit;
}


#pragma mark ---- description

- (NSString *)description {
NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy;
[desc appendString:[NSString stringWithFormat:@"ver: %@,\n", @(self.customStruct.ver)]];
[desc appendString:[NSString stringWithFormat:@"cookingId: %@,\n", @(self.customStruct.cookingId)]];
[desc appendString:[NSString stringWithFormat:@"foodType: %@,\n", @(self.customStruct.foodType)]];
[desc appendString:[NSString stringWithFormat:@"foodRawness: %@,\n", @(self.customStruct.foodRawness)]];
[desc appendString:[NSString stringWithFormat:@"targetTemperature_C: %@,\n", @(self.customStruct.targetTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"targetTemperature_F: %@,\n", @(self.customStruct.targetTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_C: %@,\n", @(self.customStruct.ambientMinTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_F: %@,\n", @(self.customStruct.ambientMinTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_C: %@,\n", @(self.customStruct.ambientMaxTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_F: %@,\n", @(self.customStruct.ambientMaxTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperaturePercent: %@,\n", @(self.alarmTemperaturePercent)]];
[desc appendString:[NSString stringWithFormat:@"timerStart: %@,\n", @(self.customStruct.timerStart)]];
[desc appendString:[NSString stringWithFormat:@"timerEnd: %@,\n", @(self.customStruct.timerEnd)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperature_C: %@,\n", @(self.customStruct.alarmTemperature_C)]];
[desc appendString:[NSString stringWithFormat:@"alarmTemperature_F: %@,\n", @(self.customStruct.alarmTemperature_F)]];
[desc appendString:[NSString stringWithFormat:@"currentUnit: %@,\n", @(self.customStruct.currentUnit)]];
[desc appendString:[NSString stringWithFormat:@"remark: %@,\n", self.remark]];
[desc appendString:@"}"];
return desc;
}

- (NSString *)debugDescription {
return [self description];
}


@end

+ 2
- 10
AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/MeatProbeBoxVC.m 查看文件

@@ -111,8 +111,9 @@
- (void)initBle {
self.bleManager = [[ELAILinkBleManager alloc] init];
self.bleManager.ailinkDelegate = self;
self.bleParser = [ELMeatProbeBoxBleParser new];
self.bleParser = [[ELMeatProbeBoxBleParser alloc] init];
self.bleParser.delegate = self;
self.bleParser.mac = self.per.macAddressString;
}

- (void)scanBle {
@@ -169,10 +170,6 @@
- (void)requestDeviceData {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{
// //APP获取设备功能列表
// [self.bleManager airDetectorRequestDeviceFunctions];
// //APP获取设备状态 //0x03 CMD:获取设备状态
// [self.bleManager airDetectorRequestDeviceStatus];
});
}

@@ -224,11 +221,6 @@
Byte *bytes = (Byte *)packet.bytes;
Byte cmd = bytes[2];
if (cmd == ELInetGetCmdTypeGetBatteryState) {
int power = self.bleManager.battery.power;
ELBatteryChargingState state = self.bleManager.battery.state;
NSLog(@"##### state: %lu power: %d", (unsigned long)state, power);
}
if (cmd == ELInetGetCmdTypeGetBMVersion) {
NSString *bmVersion = self.bleManager.bmVersion;
NSLog(@"##### bmVersion: %@", bmVersion);

+ 10
- 10
AILinkBleSDKDemo/DeviceDemo/27_MeatProbeBox/MeatProbeBoxVC.xib 查看文件

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -21,6 +21,12 @@
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rgz-Ge-utp">
<rect key="frame" x="0.0" y="63" width="393" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K5I-eH-HGA">
<rect key="frame" x="16" y="346" width="31" height="30"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
@@ -37,12 +43,6 @@
<action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="aLe-00-nz6"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rgz-Ge-utp">
<rect key="frame" x="0.0" y="63" width="393" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I1h-EM-UuI">
<rect key="frame" x="8" y="384" width="377" height="434"/>
<subviews>
@@ -133,10 +133,10 @@
</objects>
<resources>
<systemColor name="labelColor">
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>

+ 5
- 0
AILinkBleSDKDemo/Others/ViewController.m 查看文件

@@ -47,6 +47,8 @@
#import "MeatProbeVC.h"
#import "MeatProbeBoxVC.h"
#import "HygrothermographVC.h"
#import "MeatProbeScreenBoxVC.h"
#import "MultipleMeatProbeBoxVC.h"


@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@@ -106,6 +108,9 @@
[ELDemoDeviceModel modelWithIndex:@"26" imageName:@"meat_probe_type_ic" title:@"MeatProbe" subtitle:@"食物探针" entryVCName:NSStringFromClass(MeatProbeVC.class) cids:@[@(ELSupportDeviceTypeMeatProbe)]],
[ELDemoDeviceModel modelWithIndex:@"27" imageName:@"ailink_cid85_type_ic" title:@"MeatProbeBox" subtitle:@"食物探针盒子" entryVCName:NSStringFromClass(MeatProbeBoxVC.class) cids:@[@(ELSupportDeviceTypeMeatProbeBox)]],
[ELDemoDeviceModel modelWithIndex:@"27B" imageName:@"ailink_cid85_banding_cid_0062_100_ic" title:@"MeatProbeScreenBox" subtitle:@"食物探针带屏盒子" entryVCName:NSStringFromClass(MeatProbeScreenBoxVC.class) cids:@[@(0x0062)]],
[ELDemoDeviceModel modelWithIndex:@"27C" imageName:@"aibbq_add_equipment_probe_100_ic" title:@"MultipleMeatProbeBox" subtitle:@"多食物探针盒子" entryVCName:NSStringFromClass(MultipleMeatProbeBoxVC.class) cids:@[@(0x006B)]],
[ELDemoDeviceModel modelWithIndex:@"28" imageName:@"Hygrothermograph_add_icon2_100" title:@"Hygrothermograph" subtitle:@"温湿度计" entryVCName:NSStringFromClass(HygrothermographVC.class) cids:@[@(ELSupportDeviceTypeHygrothermograph)]],
];
}

二進制
AILinkBleSDKDemo/PrivacyInfo.bundle/Info.plist 查看文件


+ 1
- 1
AILinkBleSDKDemo/Tools/ELSelectView.m 查看文件

@@ -57,7 +57,7 @@ static NSString * ELSelectViewCellid = @"ELSelectViewCellid";
_tableView = [[UITableView alloc]init];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.tableFooterView = [UIView new];
_tableView.tableFooterView = [[UIView alloc] init];
_tableView.separatorColor = [UIColor clearColor];
}
return _tableView;

+ 1
- 0
README.md 查看文件

@@ -15,4 +15,5 @@
| 1.8.0 | 20240313 | larry | 修改 增加 readRSSI 接口 |
| 1.9.0 | 20240603 | larry | 修改 增加 Privacy Manifest |
| 1.9.0(Demo v1.9.2) | 20240628 | larry | 修改 增加 温湿度计 Demo |
| 1.10.0 | 20240910 | larry | 修改 增加 带屏食物探针盒 多探针盒子 |


Loading…
取消
儲存