@@ -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)"; |
@@ -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 */ |
@@ -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 , |
@@ -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 | |||
@@ -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 |
@@ -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, | |||
}; |
@@ -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 |
@@ -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 */ |
@@ -119,7 +119,7 @@ typedef NS_ENUM(NSUInteger, ELSupportDeviceType) { | |||
ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤 | |||
ELSupportDeviceType_dualFrequencyEightelectrodeMCUAlgorithm = 0x0068, //双频八电极MCU算法秤 | |||
}; | |||
#endif /* ELExSupportDeviceType_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 , | |||
}; | |||
@@ -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 | |||
@@ -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 | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 */ |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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> |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 */ |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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> |
@@ -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 |
@@ -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 |
@@ -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); |
@@ -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"/> |
@@ -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)]], | |||
]; | |||
} |
@@ -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; |
@@ -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 | 修改 增加 带屏食物探针盒 多探针盒子 | | |||