\r
## 介绍\r
\r
-当前版本:v1.0.31\r
+当前版本:v1.0.32\r
\r
-更新内容20240816:\r
+更新内容20240821:\r
\r
-1. æ\94¯æ\8c\81é\98¿é\87\8cé\89´æ\9d\83\r
+1. æ\96°å¢\9eæ\94¯æ\8c\81æ³°å\87\8cå¾®æ\96¹æ¡\88\r
\r
## 环境要求\r
\r
\r
## 引入依赖库\r
\r
-将 libAiMouseSdk2 添加进项目中。然后,在 Build Phases 选项卡中,添加以下框架和库:\r
+将 sdk 添加进项目中。然后,在 Build Phases 选项卡中,添加以下框架和库:\r
\r
libz.tbd、libbz2.tbd、libiconv.tbd、liblzma5.tbd、CoreAudio.framework、VideoToolbox.framework\r
\r
\r
\r
\r
-## SDK相关接口\r
+## 富瑞坤方案\r
+\r
+\r
+\r
+### SDK相关接口\r
\r
```objective-c\r
/// 单利\r
- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;\r
```\r
\r
-## SDK相关代理\r
+### SDK相关代理\r
\r
\r
\r
\r
```\r
\r
-## 其他类型数据\r
+### 其他类型数据\r
\r
```objective-c\r
\r
\r
\r
\r
-## 蓝牙示例\r
+### 蓝牙示例\r
\r
```objective-c\r
//\r
\r
\r
\r
-## USB示例\r
+### USB示例\r
\r
\r
\r
\r
```\r
\r
+## 泰凌微方案\r
+\r
+### sdk相关接口\r
+\r
+```objective-c\r
+/// 单利\r
++(TlwMouse *)shared;\r
+\r
+/// 设置日志等级\r
+/// - Parameter level: 等级\r
+- (void)setLogLevel:(TlwMouseLogLevel)level;\r
+\r
+/// 断开连接\r
+- (void)didDisconnect;\r
+\r
+/// 切换连接类型\r
+/// - Parameter type: 连接类型\r
+- (void)switchConnect:(TlwConnectType)type;\r
+\r
+/// 传输业务数据给sdk\r
+/// - Parameter data:ai特征传过来的数据\r
+- (void)sendAiDataToSdk:(NSData *)data;\r
+\r
+/// 传输设备信息数据给sdk\r
+/// - Parameter data:设备信息特征传过来的数据\r
+- (void)sendDeviceDataToSdk:(NSData *)data;\r
+\r
+/// 获取设备信息\r
+/// - Parameter completation: 如果获取成功,则deviceInfo有值,否则为nil\r
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))complete;\r
+\r
+/// 设置DPI\r
+/// - Parameters:\r
+/// - value: 数值1 ~ 4 (分别对应 800 1200 1800 2400)\r
+/// - finished: 设置结果回调\r
+- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;\r
+\r
+/// 打开录音\r
+/// - finished: 执行结果回调\r
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;\r
+\r
+/// 关闭录音\r
+/// - finished: 执行结果回调\r
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;\r
+```\r
+\r
+### sdk相关代理\r
+\r
+```objective-c\r
+@protocol TlwMouseDelegate <NSObject>\r
+\r
+/// 设备连接状态\r
+/// - Parameter connected: 是否连接\r
+- (void)aiMouseConnectStateDidChange:(BOOL)connected;\r
+\r
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备\r
+/// - Parameter data: 数据\r
+- (void)tlwMouseSendDeviceInfoData:(NSData *)data;\r
+\r
+/// 鼠标按键指令回调\r
+/// - Parameter data: 数据。\r
+/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音)\r
+/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音)\r
+/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音)\r
+/// 其他未定义\r
+- (void)tlwMouseSendCmdData:(NSData *)data;\r
+\r
+/// 录音回调\r
+/// - Parameter data: pcm格式语音数据\r
+- (void)tlwMouseSendRecordData:(NSData *)data;\r
+\r
+@optional\r
+/// 输出sdk日志\r
+/// - Parameter log: 日志\r
+- (void)aiMouseAddLog:(NSString *)log;\r
+\r
+@end\r
+```\r
+\r
+### 其他数据类型\r
+\r
+```objective-c\r
+@interface TlwDeviceInfo : NSObject\r
+@property (nonatomic, assign) int DPI;/// DPI值 数值1 ~ 4 (分别对应 800 1200 1800 2400)\r
+@property (nonatomic, assign) int version;///软件版本\r
+@property (nonatomic, assign) int batteryState;/// 电池状态\r
+@property (nonatomic, assign) int batteryValue;/// 电池百分百电量,0-100\r
+@end\r
+\r
+/// 日志等级\r
+typedef NS_ENUM(NSInteger,TlwMouseLogLevel) {\r
+ TlwMouseLogLevel_none = 0,\r
+ TlwMouseLogLevel_error,\r
+ TlwMouseLogLevel_info,\r
+};\r
+\r
+typedef NS_ENUM(NSInteger,TlwConnectType) {\r
+ TlwConnectType_bluetooth = 0,///蓝牙\r
+ TlwConnectType_hid,///hid\r
+};\r
+```\r
+\r
+### 蓝牙示例\r
+\r
+```objective-c\r
+//\r
+// BleManager.m\r
+// AliIotSdkDemo\r
+//\r
+// Created by incar on 2024/1/30.\r
+//\r
+\r
+#import "BleManager.h"\r
+#import "AiMouseSdk.h"\r
+#import "Config.h"\r
+\r
+// 服务id\r
+#define kServiceUUID @"FFF1"\r
+//ai特征\r
+#define kAiUUID @"0783B03E-8535-B5A0-7140-A304D2194CB8"\r
+//设备信息特征\r
+#define kDeviceInfoUUID @"0783B63E-8535-B5A0-7140-A304D2495CB6"\r
+\r
+@interface BleManager ()<CBCentralManagerDelegate,CBPeripheralDelegate,AiMouseDelegate>\r
+@property(nonatomic, strong) CBCentralManager *centralManager;///蓝牙管理\r
+@property(nonatomic, strong) CBPeripheral *connectPeripheral;//连接的设备\r
+@property(nonatomic, strong) CBCharacteristic *aiCha;//ai特征\r
+@property(nonatomic, strong) CBCharacteristic *deviceInfoCha;//设备信息特征\r
+@property (nonatomic,strong) NSMutableData *recordData;//录音的数据\r
+@end\r
+\r
+@implementation BleManager\r
+- (instancetype)init{\r
+ self = [super init];\r
+ if (self) {\r
+ self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];\r
+ }\r
+ return self;\r
+}\r
+\r
+#pragma mark - CBCentralManagerDelegate\r
+- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {\r
+ [self showLog:[NSString stringWithFormat:@"蓝牙状态 = %@",@(central.state)]];\r
+ switch (central.state) {\r
+ case CBManagerStateUnknown:\r
+ \r
+ break;\r
+ case CBManagerStateResetting:\r
+ \r
+ break;\r
+ case CBManagerStateUnsupported:\r
+ \r
+ break;\r
+ case CBManagerStateUnauthorized:\r
+ \r
+ break;\r
+ case CBManagerStatePoweredOff:\r
+ \r
+ break;\r
+ case CBManagerStatePoweredOn:\r
+ [self autoConnectDevice];\r
+ break;\r
+ }\r
+}\r
+\r
+- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{\r
+// [self showLog:[NSString stringWithFormat:@"扫描到设备 = %@, data = %@",peripheral,advertisementData]];\r
+ if(self.discoverDevice){\r
+ self.discoverDevice(peripheral);\r
+ }\r
+}\r
+\r
+- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{\r
+ [self showLog:[NSString stringWithFormat:@"连接成功 name = %@",peripheral.name]];\r
+ if(self.didConnectDevice){\r
+ self.didConnectDevice(peripheral);\r
+ }\r
+ peripheral.delegate = self;\r
+ [peripheral discoverServices:nil];\r
+ \r
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil];\r
+}\r
+-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{\r
+ [self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]];\r
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil];\r
+ if(self.didDisConnectDevice){\r
+ self.didDisConnectDevice(peripheral);\r
+ }\r
+// [self aiMouseConnectStateDidChange:false];\r
+ [[AiMouse shared] didDisconnect];\r
+}\r
+- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{\r
+ [self showLog:[NSString stringWithFormat:@"连接失败。。。%@",error.description]];\r
+}\r
+#pragma mark - CBPeripheralDelegate\r
+//发现服务\r
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {\r
+ for (CBService *service in peripheral.services){\r
+ [peripheral discoverCharacteristics:nil forService:service];\r
+ }\r
+}\r
+// 发现特征\r
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {\r
+ for (CBCharacteristic *characteristic in service.characteristics){\r
+ if ([characteristic.UUID.UUIDString isEqualToString:kAiUUID]) {\r
+ //AI特征\r
+ self.aiCha = characteristic;\r
+ [peripheral setNotifyValue:YES forCharacteristic:characteristic];\r
+ }\r
+ else if ([characteristic.UUID.UUIDString isEqualToString:kDeviceInfoUUID]) {\r
+ //设备信息特征\r
+ self.deviceInfoCha = characteristic;\r
+ [peripheral setNotifyValue:YES forCharacteristic:characteristic];\r
+ \r
+ //切换代理\r
+ [AiMouse shared].delegate = self;\r
+ //切换当前连接类型\r
+ [[AiMouse shared] switchConnect:AiMouseConnectType_bluetooth];\r
+\r
+ }\r
+ }\r
+}\r
+//收到蓝牙数据\r
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {\r
+ NSData *data = characteristic.value;\r
+ if([characteristic.UUID.UUIDString isEqualToString:kAiUUID]){\r
+ //AI数据\r
+ [[AiMouse shared] sendAiDataToSdk:data];\r
+ }\r
+ else if([characteristic.UUID.UUIDString isEqualToString:kDeviceInfoUUID]){\r
+ //设备信息数据\r
+ [[AiMouse shared] sendDeviceDataToSdk:data];\r
+ }\r
+}\r
+\r
+#pragma mark - AiDelegate\r
+/// 设备鉴权结果返回\r
+/// - Parameter authState: 鉴权状态\r
+-(void)aiMouseAuthenticationResult:(AiMouseAuthState)authState{\r
+ [self showLog:[NSString stringWithFormat:@"鉴权结果 = %@",@(authState)]];\r
+ if(self.authCallBack){\r
+ self.authCallBack(authState == AiMouseAuthState_Success);\r
+ }\r
+}\r
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备\r
+/// - Parameter data: 数据\r
+-(void)aiMouseSendDeviceInfoData:(NSData *)data{\r
+// [self showLog:[NSString stringWithFormat:@"[send] %@",data]];\r
+ [self.connectPeripheral writeValue:data forCharacteristic:self.deviceInfoCha type:CBCharacteristicWriteWithoutResponse];\r
+}\r
+\r
+/// 鼠标按键指令回调\r
+/// - Parameter data: 数据。\r
+/// 前 第一次单击31(开始录音), 第二次单击32(结束录音) , 按下21(开始录音)抬起22(结束录音)\r
+/// 中 单击42, 按下25(开始录音)抬起26(结束录音)\r
+/// 后 单击43,按下27(开始录音)抬起28(结束录音)\r
+/// 其他未定义\r
+- (void)aiMouseSendCmdData:(NSData *)data{\r
+ uint8 cmd;\r
+ [data getBytes:&cmd length:1];\r
+ [self showLog:[NSString stringWithFormat:@"收到指令 = %X",cmd]];\r
+ \r
+ if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){\r
+ [self showLog:@"开始录音"];\r
+ self.recordData = [[NSMutableData alloc] init];\r
+ }\r
+ else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){\r
+ [self showLog:@"结束录音"];\r
+ [self finishRecored:self.recordData];\r
+ }\r
+}\r
+\r
+/// 录音回调\r
+/// - Parameter data: pcm格式语音数据\r
+- (void)aiMouseSendRecordData:(NSData *)data{\r
+ [self.recordData appendData:data];\r
+}\r
+/// 输出日志\r
+- (void)aiMouseAddLog:(NSString *)log { \r
+// [self showLog:log];\r
+}\r
+\r
+// 连接状态改变\r
+- (void)aiMouseConnectStateDidChange:(BOOL)connected{\r
+ if (connected) {\r
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil];\r
+ }\r
+ else{\r
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil];\r
+ }\r
+}\r
+#pragma mark - public\r
+/// 连接设备\r
+- (void)startScanDevice{\r
+ [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO]}];\r
+}\r
+\r
+/// 扫描设备\r
+- (void)stopScanDevice{\r
+ [self.centralManager stopScan];\r
+}\r
+\r
+/// 连接设备\r
+- (void)connectPeripheral:(CBPeripheral *)peripheral{\r
+ self.connectPeripheral = peripheral;\r
+ [self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : [NSNumber numberWithBool:YES]}];\r
+}\r
+\r
+/// 获取设备信息\r
+- (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo * _Nonnull, NSError * _Nonnull))completation{\r
+ [[AiMouse shared] getDeviceInfo:completation];\r
+}\r
+\r
+#pragma mark - other\r
+//自动连接,获取已配对的蓝牙,取第一个连接\r
+- (void)autoConnectDevice{\r
+ //如果是已连接或者正在连接,则不再重连\r
+ if (self.connectPeripheral && (self.connectPeripheral.state == CBPeripheralStateConnected || self.connectPeripheral.state == CBPeripheralStateConnecting)) {\r
+ return;\r
+ }\r
+ NSArray<CBPeripheral *> *devices = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]];\r
+ if (devices.count > 0) {\r
+ CBPeripheral *peripheral = devices[0];\r
+ [self showLog:[NSString stringWithFormat:@"匹配过的设备%@",peripheral.name]];\r
+ [self connectPeripheral:peripheral];\r
+ }\r
+ // 10s自动重连\r
+ [self performSelector:@selector(autoConnectDevice) withObject:nil afterDelay:10];\r
+}\r
+- (void)showLog:(NSString *)log{\r
+ NSLog(@"%@",log);\r
+}\r
+\r
+//将录音数据保存\r
+- (void)finishRecored:(NSData *)data{\r
+ NSString *doc = NSTemporaryDirectory();\r
+ NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];\r
+ NSError *err;\r
+ [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];\r
+ if (err) {\r
+ [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];\r
+ }\r
+ else{\r
+ [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];\r
+ }\r
+}\r
+\r
+- (NSString *)dataToHexString:(NSData *)data {\r
+ NSUInteger length = data.length;\r
+ NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];\r
+ const unsigned char *byte = data.bytes;\r
+ for (int i = 0; i < length; i++, byte++) {\r
+ [result appendFormat:@"%02X", *byte];\r
+ }\r
+ return result;\r
+}\r
+@end\r
+\r
+```\r
+\r
+### usb示例\r
+\r
+```objective-c\r
+//\r
+// HidManager.m\r
+// MouseSdk\r
+//\r
+// Created by incar on 2024/3/26.\r
+//\r
+\r
+#import "HidManager.h"\r
+#include <IOKit/hid/IOHIDLib.h>\r
+#include <IOKit/hid/IOHIDKeys.h>\r
+#import "Config.h"\r
+#import "DeviceManger.h"\r
+\r
+//供应商id\r
+const long vendorID = 0x6675;\r
+//产品id\r
+const long productID = 0x8601;\r
+\r
+@interface HidManager ()<AiMouseDelegate>\r
+\r
+@property(nonatomic, assign) IOHIDManagerRef hidManager;///hid管理\r
+@property(nonatomic, assign) IOHIDDeviceRef aiCha;//ai通道\r
+@property(nonatomic, assign) IOHIDDeviceRef deviceInfoCha;//设备信息通道\r
+@property (nonatomic,strong) NSMutableData *recordData;//录音的数据\r
+@end\r
+\r
+@implementation HidManager\r
+\r
+- (instancetype)init{\r
+ self = [super init];\r
+ if (self) {\r
+ [self initHid];\r
+ [self open];\r
+ }\r
+ return self;\r
+}\r
+\r
+- (void)initHid{\r
+\r
+}\r
+\r
+- (void)open{\r
+ self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);\r
+ IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);\r
+ IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L);\r
+ if (ret != kIOReturnSuccess) {\r
+ NSLog(@"打开设备失败!");\r
+ }else{\r
+ NSLog(@"打开设备成功!");\r
+ }\r
+ //设置匹配设备\r
+ NSMutableDictionary* dict= [NSMutableDictionary dictionary];\r
+ [dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];\r
+ [dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];\r
+ IOHIDManagerSetDeviceMatching(self.hidManager, (__bridge CFMutableDictionaryRef)dict);\r
+ //设置匹配到设备回调\r
+ IOHIDManagerRegisterDeviceMatchingCallback(self.hidManager, &hidAppearDevice, NULL);\r
+ //设置移除设备回调\r
+ IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, &hidDisAppearDevice, NULL);\r
+}\r
+\r
+- (void)close{\r
+ IOReturn ret = IOHIDManagerClose(self.hidManager, 0);\r
+ if (ret != kIOReturnSuccess) {\r
+ NSLog(@"关闭设备失败!");\r
+ }else{\r
+ NSLog(@"关闭设备成功!");\r
+ }\r
+}\r
+\r
+#pragma mark - Hid CallBack\r
+/// 匹配到设备回调\r
+static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {\r
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]];\r
+ uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey));\r
+ uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey));\r
+ if(PrimaryUsagePage == 1 && PrimaryUsage == 0){\r
+ //设备信息\r
+ [DeviceManger shared].hid.deviceInfoCha = inIOHIDDeviceRef;\r
+ [[DeviceManger shared].hid connectDevice];\r
+ //注册消息回调\r
+ unsigned char InputBuffer[128];\r
+ IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL);\r
+ IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef, 0);\r
+ if (ret != kIOReturnSuccess) {\r
+ NSLog(@"打开设备信息失败!");\r
+ }else{\r
+ NSLog(@"打开设备信息成功!");\r
+ }\r
+ CFRetain(inIOHIDDeviceRef);\r
+ IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);\r
+ }\r
+ else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){\r
+ //ai\r
+ [DeviceManger shared].hid.aiCha = inIOHIDDeviceRef;\r
+ //注册消息回调\r
+ unsigned char InputBuffer[128];\r
+ IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL);\r
+ IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef, 0);\r
+ if (ret != kIOReturnSuccess) {\r
+ NSLog(@"打开AI失败!");\r
+ }else{\r
+ NSLog(@"打开AI成功!");\r
+ }\r
+ CFRetain(inIOHIDDeviceRef);\r
+ IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);\r
+ }\r
+}\r
+\r
+/// 设备移除回调\r
+static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {\r
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]];\r
+ uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey));\r
+ uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey));\r
+\r
+ if(PrimaryUsagePage == 1 && PrimaryUsage == 0){\r
+ [DeviceManger shared].hid.deviceInfoCha = nil;\r
+ [DeviceManger shared].hid.aiCha = nil;\r
+ [[AiMouse shared] didDisconnect];\r
+ }\r
+}\r
+\r
+static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){\r
+// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]];\r
+ \r
+ NSInteger len = get_data_len(report, reportLength);\r
+ NSData *data = [[NSData alloc] initWithBytes:report length:len];\r
+// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]];\r
+ \r
+ uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsagePageKey));\r
+ uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsageKey));\r
+\r
+ if(PrimaryUsagePage == 1 && PrimaryUsage == 0){\r
+ [[AiMouse shared] sendDeviceDataToSdk:data];\r
+ \r
+ }\r
+ else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){\r
+ [[AiMouse shared] sendAiDataToSdk:data];\r
+ }\r
+}\r
+#pragma mark - AiDelegate\r
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备\r
+/// - Parameter data: 数据\r
+-(void)aiMouseSendDeviceInfoData:(NSData *)data{\r
+ [self sendData:data toDevice:self.deviceInfoCha];\r
+}\r
+\r
+/// 设备鉴权结果返回\r
+/// - Parameter authState: 鉴权状态\r
+-(void)aiMouseAuthenticationResult:(AiMouseAuthState)authState{\r
+ [self showLog:[NSString stringWithFormat:@"鉴权结果 = %@",@(authState)]];\r
+ if(self.authCallBack){\r
+ self.authCallBack(authState == AiMouseAuthState_Success);\r
+ }\r
+}\r
+/// 鼠标按键指令回调\r
+/// - Parameter data: 数据。\r
+/// 前 第一次单击31(开始录音), 第二次单击32(结束录音) , 按下21(开始录音)抬起22(结束录音)\r
+/// 中 单击42, 按下25(开始录音)抬起26(结束录音)\r
+/// 后 单击43,按下27(开始录音)抬起28(结束录音)\r
+/// 其他未定义\r
+- (void)aiMouseSendCmdData:(NSData *)data{\r
+ uint8 cmd;\r
+ [data getBytes:&cmd length:1];\r
+ [self showLog:[NSString stringWithFormat:@"收到指令 = %X",cmd]];\r
+ \r
+ if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){\r
+ [self showLog:@"开始录音"];\r
+ self.recordData = [[NSMutableData alloc] init];\r
+ }\r
+ else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){\r
+ [self showLog:@"结束录音"];\r
+ [self finishRecored:self.recordData];\r
+ }\r
+}\r
+/// 录音回调\r
+/// - Parameter data: pcm格式语音数据\r
+- (void)aiMouseSendRecordData:(NSData *)data{\r
+ [self.recordData appendData:data];\r
+}\r
+/// 输出日志\r
+- (void)aiMouseAddLog:(NSString *)log {\r
+// [self showLog:log];\r
+}\r
+\r
+-(void)hidDataError{\r
+ [self close];\r
+ [self open];\r
+}\r
+\r
+// 连接状态改变\r
+- (void)aiMouseConnectStateDidChange:(BOOL)connected{\r
+ [DeviceManger shared].hid.connectStateCallBack(connected);\r
+}\r
+#pragma mark - public\r
+-(void)connectDevice{\r
+ //设置代理\r
+ [AiMouse shared].delegate = self;\r
+ //切换当前连接类型\r
+ [[AiMouse shared] switchConnect:AiMouseConnectType_hid];\r
+}\r
+/// 获取设备信息\r
+- (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo * _Nonnull, NSError * _Nonnull))completation{\r
+ [[AiMouse shared] getDeviceInfo:completation];\r
+}\r
+\r
+/// 发送数据给设备\r
+-(void)sendData:(NSData *)data toDevice:(IOHIDDeviceRef)device{\r
+ if(device == nil){\r
+ [self showLog:@"请先连上设备"];\r
+ return;\r
+ }\r
+ \r
+ IOReturn ret = IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, 0, data.bytes, data.length);\r
+ if (ret != kIOReturnSuccess) {\r
+ [self showLog:[NSString stringWithFormat:@"数据发送失败! code = %d",ret]];\r
+ }else{\r
+ [self showLog:@"数据发送成功!"];\r
+ }\r
+}\r
+//将录音数据保存\r
+- (void)finishRecored:(NSData *)data{\r
+ NSString *doc = NSTemporaryDirectory();\r
+ NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];\r
+ NSError *err;\r
+ [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];\r
+ if (err) {\r
+ [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];\r
+ }\r
+ else{\r
+ [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];\r
+ }\r
+}\r
+- (void)showLog:(NSString *)log{\r
+ NSLog(@"%@",log);\r
+}\r
+\r
+/// 实际到数据长度\r
+static inline int get_data_len(uint8_t *buf, u_long len) {\r
+ if (len <= 0 ) return 0;\r
+ int url_len = 0;\r
+ while (buf[url_len] != 0x00) url_len++;\r
+ return url_len;\r
+}\r
+\r
+/// 获取对应属性值\r
+static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey) {\r
+ uint32_t outValue = 0;\r
+ if (inIOHIDDeviceRef != NULL) {\r
+ CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey);\r
+ if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) {\r
+ Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue);\r
+ if (!result) {\r
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]];\r
+ }\r
+ }\r
+ }\r
+ return outValue;\r
+}\r
+@end\r
+\r
+```\r
+\r
\r
\r
## 注意事项\r
\r
-- 设备信息数据使用的是设备信息通道,走设备信息接口;AI数据使用的是AI通道,走AI接口。\r
-- 只有鉴权成功了以后,AI相关功能才可用。\r
+- 不同的方案,使用不同的通讯方式\r
\r
archiveVersion = 1;
classes = {
};
- objectVersion = 56;
+ objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
18AD83262BFAE8B9009BDE3B /* AiDeviceInfoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AD83242BFAE8B9009BDE3B /* AiDeviceInfoManager.m */; };
18AD83292BFB22DE009BDE3B /* AiSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 18AD83272BFB22DE009BDE3B /* AiSender.h */; };
18AD832A2BFB22DE009BDE3B /* AiSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AD83282BFB22DE009BDE3B /* AiSender.m */; };
+ 18EF7AF72C7603E800AC5223 /* TlwRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */; };
+ 18EF7AF82C7603E800AC5223 /* ADPCMConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */; };
+ 18EF7AF92C7603E800AC5223 /* adpcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEA2C7603E800AC5223 /* adpcm.h */; };
+ 18EF7AFA2C7603E800AC5223 /* TlwMouseSdk.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */; };
+ 18EF7AFB2C7603E800AC5223 /* TlwTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AEC2C7603E800AC5223 /* TlwTools.m */; };
+ 18EF7AFC2C7603E800AC5223 /* TlwConnectManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */; };
+ 18EF7AFD2C7603E800AC5223 /* TlwAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */; };
+ 18EF7AFE2C7603E800AC5223 /* ADPCMConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */; };
+ 18EF7B002C7603E800AC5223 /* TlwRecordManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */; };
+ 18EF7B012C7603E800AC5223 /* adpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AF22C7603E800AC5223 /* adpcm.c */; };
+ 18EF7B022C7603E800AC5223 /* TlwTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF32C7603E800AC5223 /* TlwTools.h */; };
+ 18EF7B032C7603E800AC5223 /* TlwMouseSdk.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 18EF7B042C7603E800AC5223 /* TlwAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */; };
+ 18EF7B052C7603E800AC5223 /* TlwConnectManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
18AD83242BFAE8B9009BDE3B /* AiDeviceInfoManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AiDeviceInfoManager.m; sourceTree = "<group>"; };
18AD83272BFB22DE009BDE3B /* AiSender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AiSender.h; sourceTree = "<group>"; };
18AD83282BFB22DE009BDE3B /* AiSender.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AiSender.m; sourceTree = "<group>"; };
+ 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwRecordManager.m; sourceTree = "<group>"; };
+ 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADPCMConverter.m; sourceTree = "<group>"; };
+ 18EF7AEA2C7603E800AC5223 /* adpcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adpcm.h; sourceTree = "<group>"; };
+ 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwMouseSdk.m; sourceTree = "<group>"; };
+ 18EF7AEC2C7603E800AC5223 /* TlwTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwTools.m; sourceTree = "<group>"; };
+ 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwConnectManager.m; sourceTree = "<group>"; };
+ 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwAudioManager.h; sourceTree = "<group>"; };
+ 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADPCMConverter.h; sourceTree = "<group>"; };
+ 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwRecordManager.h; sourceTree = "<group>"; };
+ 18EF7AF22C7603E800AC5223 /* adpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adpcm.c; sourceTree = "<group>"; };
+ 18EF7AF32C7603E800AC5223 /* TlwTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwTools.h; sourceTree = "<group>"; };
+ 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwMouseSdk.h; sourceTree = "<group>"; };
+ 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwAudioManager.m; sourceTree = "<group>"; };
+ 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwConnectManager.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
180E7A9D2BD7BB12008558E8 = {
isa = PBXGroup;
children = (
+ 18EF7AE72C7603E800AC5223 /* TlwMouseSdk */,
180E7AA82BD7BB12008558E8 /* AiMouseSdk */,
180E7AA72BD7BB12008558E8 /* Products */,
180E7D092BD8FEB2008558E8 /* Frameworks */,
path = AFNetworking;
sourceTree = "<group>";
};
+ 18EF7AE72C7603E800AC5223 /* TlwMouseSdk */ = {
+ isa = PBXGroup;
+ children = (
+ 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */,
+ 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */,
+ 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */,
+ 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */,
+ 18EF7AF32C7603E800AC5223 /* TlwTools.h */,
+ 18EF7AEC2C7603E800AC5223 /* TlwTools.m */,
+ 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */,
+ 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */,
+ 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */,
+ 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */,
+ 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */,
+ 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */,
+ 18EF7AEA2C7603E800AC5223 /* adpcm.h */,
+ 18EF7AF22C7603E800AC5223 /* adpcm.c */,
+ );
+ path = TlwMouseSdk;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
180E7C882BD8FC4E008558E8 /* hwcontext.h in Headers */,
180E7CB12BD8FC4E008558E8 /* dovi_meta.h in Headers */,
184A710F2BDA4C050025A332 /* AFHTTPSessionManager.h in Headers */,
+ 18EF7B052C7603E800AC5223 /* TlwConnectManager.h in Headers */,
180E7CF92BD8FC4F008558E8 /* dirac.h in Headers */,
184A71032BDA4C050025A332 /* AFNetworkReachabilityManager.h in Headers */,
184A71242BDE2DA60025A332 /* AiSecretModel.h in Headers */,
180E7CBA2BD8FC4E008558E8 /* common.h in Headers */,
180E7C9D2BD8FC4E008558E8 /* avutil.h in Headers */,
180E7C982BD8FC4E008558E8 /* rational.h in Headers */,
+ 18EF7B022C7603E800AC5223 /* TlwTools.h in Headers */,
180E7CF82BD8FC4F008558E8 /* codec_id.h in Headers */,
180E7C9B2BD8FC4E008558E8 /* avconfig.h in Headers */,
180E7CDA2BD8FC4E008558E8 /* avassert.h in Headers */,
180E7CD92BD8FC4E008558E8 /* downmix_info.h in Headers */,
180E7CA72BD8FC4E008558E8 /* spherical.h in Headers */,
180E7CA92BD8FC4E008558E8 /* hwcontext_opencl.h in Headers */,
+ 18EF7B032C7603E800AC5223 /* TlwMouseSdk.h in Headers */,
180E7B542BD7BD83008558E8 /* AiFrameManager.h in Headers */,
180E7CD12BD8FC4E008558E8 /* parseutils.h in Headers */,
180E7CEC2BD8FC4E008558E8 /* codec_par.h in Headers */,
180E7CB92BD8FC4E008558E8 /* hwcontext_drm.h in Headers */,
180E7CE22BD8FC4E008558E8 /* version.h in Headers */,
189C15862C05726600676A80 /* AiConnectManager.h in Headers */,
+ 18EF7AFD2C7603E800AC5223 /* TlwAudioManager.h in Headers */,
180E7C892BD8FC4E008558E8 /* time.h in Headers */,
180E7CD32BD8FC4E008558E8 /* bprint.h in Headers */,
180E7CB02BD8FC4E008558E8 /* timecode.h in Headers */,
180E7CBB2BD8FC4E008558E8 /* hmac.h in Headers */,
184A710B2BDA4C050025A332 /* AFNetworking.h in Headers */,
180E7CAC2BD8FC4E008558E8 /* threadmessage.h in Headers */,
+ 18EF7AF92C7603E800AC5223 /* adpcm.h in Headers */,
180E7B582BD7BD83008558E8 /* AiRecordManager.h in Headers */,
180E7CE12BD8FC4E008558E8 /* avdevice.h in Headers */,
180E7CE92BD8FC4E008558E8 /* defs.h in Headers */,
+ 18EF7AFE2C7603E800AC5223 /* ADPCMConverter.h in Headers */,
180E7C942BD8FC4E008558E8 /* stereo3d.h in Headers */,
180E7CDE2BD8FC4E008558E8 /* film_grain_params.h in Headers */,
180E7C8C2BD8FC4E008558E8 /* error.h in Headers */,
180E7CD52BD8FC4E008558E8 /* hwcontext_qsv.h in Headers */,
180E7CD72BD8FC4E008558E8 /* aes_ctr.h in Headers */,
180E7CFC2BD8FC4F008558E8 /* vorbis_parser.h in Headers */,
+ 18EF7B002C7603E800AC5223 /* TlwRecordManager.h in Headers */,
180E7CE82BD8FC4E008558E8 /* avcodec.h in Headers */,
180E7C962BD8FC4E008558E8 /* pixdesc.h in Headers */,
);
};
};
buildConfigurationList = 180E7AA12BD7BB12008558E8 /* Build configuration list for PBXProject "AiMouseSdk" */;
- compatibilityVersion = "Xcode 14.0";
+ compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
184A71062BDA4C050025A332 /* AFURLResponseSerialization.m in Sources */,
184A710C2BDA4C050025A332 /* AFNetworkReachabilityManager.m in Sources */,
180E7BE42BD7BD84008558E8 /* AiRecordManager.m in Sources */,
+ 18EF7B042C7603E800AC5223 /* TlwAudioManager.m in Sources */,
+ 18EF7AF72C7603E800AC5223 /* TlwRecordManager.m in Sources */,
180E7BE32BD7BD84008558E8 /* AiFrameManager.m in Sources */,
180E7BE72BD7BD84008558E8 /* AiAudioManager.m in Sources */,
189C15872C05726600676A80 /* AiConnectManager.m in Sources */,
+ 18EF7AF82C7603E800AC5223 /* ADPCMConverter.m in Sources */,
+ 18EF7AFB2C7603E800AC5223 /* TlwTools.m in Sources */,
+ 18EF7AFA2C7603E800AC5223 /* TlwMouseSdk.m in Sources */,
180E7BDF2BD7BD84008558E8 /* AiMouseConfig.m in Sources */,
+ 18EF7AFC2C7603E800AC5223 /* TlwConnectManager.m in Sources */,
181CE5C22C6EED9900EF8D07 /* AiAliAuthManager.m in Sources */,
184A710D2BDA4C050025A332 /* AFSecurityPolicy.m in Sources */,
180E7B552BD7BD83008558E8 /* AiAudioDecoder.m in Sources */,
18AD83262BFAE8B9009BDE3B /* AiDeviceInfoManager.m in Sources */,
+ 18EF7B012C7603E800AC5223 /* adpcm.c in Sources */,
184A71092BDA4C050025A332 /* AFURLSessionManager.m in Sources */,
180E7BE82BD7BD84008558E8 /* AiAuthManager.m in Sources */,
);
--- /dev/null
+//
+// AiAdpcm2pcm.h
+// AiMouseSdk
+//
+// Created by incar on 2024/8/20.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ADPCMConverter : NSObject
+
+- (NSData *)convertADPCMToPCM:(NSData *)adpcmData;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+
+#import "ADPCMConverter.h"
+#import <Foundation/Foundation.h>
+
+
+
+@implementation ADPCMConverter
+
+static const signed char idxtbl[] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
+static const unsigned short steptbl[] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+};
+
+#define NUM_OF_ORIG_SAMPLE 2
+
+- (NSData *)convertADPCMToPCM:(NSData *)adpcmData {
+ NSMutableData *pcmData = [NSMutableData data];
+ const signed short *source = (const signed short *)[adpcmData bytes];
+ signed short *destination = (signed short *)[pcmData mutableBytes];
+ int len = (int)[adpcmData length] / sizeof(signed short);
+
+ int i;
+ // 假设 source 中的前两个元素分别是 predict 和 predict_idx
+ int predict = source[0];
+ int predict_idx = source[1] & 0xff;
+ const unsigned char *pcode = (const unsigned char *)(source + NUM_OF_ORIG_SAMPLE);
+ unsigned char code;
+ code = *pcode++;
+
+ for (i = 0; i < len; i++) {
+ if (1) {
+ int step = steptbl[predict_idx];
+
+ int diffq = step >> 3;
+
+ if (code & 4) {
+ diffq = diffq + step;
+ }
+ step = step >> 1;
+ if (code & 2) {
+ diffq = diffq + step;
+ }
+ step = step >> 1;
+ if (code & 1) {
+ diffq = diffq + step;
+ }
+
+ if (code & 8) {
+ predict = predict - diffq;
+ } else {
+ predict = predict + diffq;
+ }
+
+ if (predict > 32767) {
+ predict = 32767;
+ } else if (predict < -32768) {
+ predict = -32768;
+ }
+
+ predict_idx = predict_idx + idxtbl[code & 15];
+
+ if (predict_idx < 0) {
+ predict_idx = 0;
+ } else if (predict_idx > 88) {
+ predict_idx = 88;
+ }
+
+ if (i & 1) {
+ code = *pcode++;
+ } else {
+ code = code >> 4;
+ }
+ }
+
+ if (0 && i < NUM_OF_ORIG_SAMPLE) {
+ destination[i] = source[i];
+ } else {
+ destination[i] = predict;
+ }
+ }
+ return pcmData;
+}
+
+@end
--- /dev/null
+//
+// TlwAudioManager.h
+// AiMouse
+//
+// Created by incar on 2024/2/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwAudioManager : NSObject
+@property (nonatomic,strong)void (^outputData)(NSData *data);
+@property (nonatomic,strong)void (^decodeCallBack)(NSData *data);
+@property (nonatomic,strong)void (^outputLog)(NSString *log);
+
+- (void)receiveData:(NSData *)data;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwAudioManager.m
+// AiMouse
+//
+// Created by incar on 2024/2/28.
+//
+
+#import "TlwAudioManager.h"
+#import <AVFoundation/AVFoundation.h>
+#import "ADPCMConverter.h"
+#import "adpcm.h"
+
+@interface TlwAudioManager ()
+@end
+
+@implementation TlwAudioManager
+
+-(instancetype)init{
+ if (self = [super init]) {
+
+ }
+ return self;
+}
+
+- (void)receiveData:(NSData *)data{
+// [self.audioDecoder decodeSBCData:data];
+
+ NSData *pcmData = [self convertADPCMDataToPCMData:data];
+ if(self.decodeCallBack)self.decodeCallBack(pcmData);
+}
+- (NSData *)convertADPCMDataToPCMData:(NSData *)adpcmData {
+ const unsigned char *adpcmBytes = (const unsigned char *)[adpcmData bytes];
+ NSUInteger adpcmLength = [adpcmData length];
+
+ signed short *ps = (signed short *)malloc(adpcmLength);
+ memcpy(ps, adpcmBytes, adpcmLength);
+
+ signed short *pd = (signed short *)malloc(adpcmLength * 4);
+
+ adpcm_to_pcm(ps, pd, adpcmLength / sizeof(signed short)*2);
+
+ NSData *pcmData = [NSData dataWithBytes:pd length:adpcmLength * 4];
+
+ free(ps);
+ free(pd);
+
+ return pcmData;
+}
+
+@end
--- /dev/null
+//
+// TlwConnectManager.h
+// AiMouseSdk
+//
+// Created by incar on 2024/5/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class TlwConnectManager;
+@protocol TlwConnectManagerDelegate <NSObject>
+
+- (void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected;
+
+- (void)TlwConnectManagerReSendData:(TlwConnectManager *)manager;
+
+- (void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log;
+
+@end
+@interface TlwConnectManager : NSObject
+@property (nonatomic, weak) id<TlwConnectManagerDelegate> delegate;
+
+- (void)start;
+
+//重置
+- (void)resetTimeOut;
+
+/// 增加超时
+- (void)addTimeOut;
+
+- (void)disConnected;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwConnectManager.m
+// AiMouseSdk
+//
+// Created by incar on 2024/5/28.
+//
+
+#import "TlwConnectManager.h"
+
+@interface TlwConnectManager()
+@property (nonatomic, assign) NSInteger currentValue;//当前计数
+@property (nonatomic, assign) NSInteger timeOut;//超时次数
+@property (nonatomic, strong) NSTimer *timer;
+@property (nonatomic, assign) int connecteState;//连接状态0未知,1连上,2断开连接
+@end
+
+@implementation TlwConnectManager
+-(instancetype)init{
+ if (self = [super init]) {
+
+ }
+ return self;
+}
+
+- (void)start{
+ self.timeOut = 0;
+ self.connecteState = 0;
+
+ [self addSendTimer];
+}
+
+- (void)resetTimeOut{
+ self.timeOut = 0;
+ self.currentValue = 0;
+
+ if (self.connecteState != 1) {
+ [self showLog:@"连上设备。。。"];
+ [self.delegate TlwConnectManager:self connectStateDidChange:true];
+ }
+ self.connecteState = 1;
+}
+
+- (void)addTimeOut{
+ self.timeOut++;
+
+ if (self.timeOut >= 3) {
+ if (self.connecteState != 2) {
+ [self showLog:@"设备断开连接!!!"];
+ [self.delegate TlwConnectManager:self connectStateDidChange:false];
+ }
+ self.connecteState = 2;
+ }
+}
+
+- (void)disConnected{
+ [self showLog:@"设备断开连接!!!"];
+ [self.delegate TlwConnectManager:self connectStateDidChange:false];
+ self.connecteState = 2;
+}
+#pragma mark - timer
+- (void)addSendTimer{
+ dispatch_async(dispatch_queue_create("timeoutQueue", DISPATCH_QUEUE_SERIAL), ^{
+ self.timer = [NSTimer timerWithTimeInterval:1000 target:self selector:@selector(timerAction) userInfo:nil repeats:true];
+ [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
+ [[NSRunLoop currentRunLoop] run];
+ });
+// self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:true];
+// [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
+}
+
+//- (void)removeSendTimer{
+// if (self.timer && [self.timer isValid]) {
+// [self.timer invalidate];
+// }
+//}
+
+- (void)timerAction{
+ self.currentValue++;
+// [self showLog:[NSString stringWithFormat:@"time = %ld",self.currentValue]];
+ if (self.currentValue >= 3) {
+ self.currentValue = 0;
+ if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManagerReSendData:)]) {
+// [self showLog:@"检查是否断开连接"];
+ [self.delegate TlwConnectManagerReSendData:self];
+ }
+ }
+}
+
+- (void)showLog:(NSString *)log{
+ if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManager:outputLog:)]) {
+ [self.delegate TlwConnectManager:self outputLog:log];
+ }
+}
+@end
--- /dev/null
+//
+// AiMouseSdk.h
+// AiMouseSdk
+//
+// Created by incar on 2024/4/23.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface TlwDeviceInfo : NSObject
+@property (nonatomic, assign) int DPI;/// DPI值 数值1 ~ 4 (分别对应 800 1200 1800 2400)
+@property (nonatomic, assign) int version;///软件版本
+@property (nonatomic, assign) int batteryState;/// 电池状态
+@property (nonatomic, assign) int batteryValue;/// 电池百分百电量,0-100
+@end
+
+/// 日志等级
+typedef NS_ENUM(NSInteger,TlwMouseLogLevel) {
+ TlwMouseLogLevel_none = 0,
+ TlwMouseLogLevel_error,
+ TlwMouseLogLevel_info,
+};
+
+typedef NS_ENUM(NSInteger,TlwConnectType) {
+ TlwConnectType_bluetooth = 0,///蓝牙
+ TlwConnectType_hid,///hid
+};
+
+@protocol TlwMouseDelegate <NSObject>
+
+/// 设备连接状态
+/// - Parameter connected: 是否连接
+- (void)aiMouseConnectStateDidChange:(BOOL)connected;
+
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备
+/// - Parameter data: 数据
+- (void)tlwMouseSendDeviceInfoData:(NSData *)data;
+
+/// 鼠标按键指令回调
+/// - Parameter data: 数据。
+/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音)
+/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音)
+/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音)
+/// 其他未定义
+- (void)tlwMouseSendCmdData:(NSData *)data;
+
+/// 录音回调
+/// - Parameter data: pcm格式语音数据
+- (void)tlwMouseSendRecordData:(NSData *)data;
+
+@optional
+/// 输出sdk日志
+/// - Parameter log: 日志
+- (void)aiMouseAddLog:(NSString *)log;
+
+@end
+
+@interface TlwMouse : NSObject
+@property (nonatomic, weak) id<TlwMouseDelegate> delegate;/// 代理
+@property (nonatomic, readonly) TlwConnectType connectType;//连接类型
+@property (nonatomic, readonly) BOOL connected;//是否已连接
+
+/// 单利
++(TlwMouse *)shared;
+
+/// 设置日志等级
+/// - Parameter level: 等级
+- (void)setLogLevel:(TlwMouseLogLevel)level;
+
+/// 断开连接
+- (void)didDisconnect;
+
+/// 切换连接类型
+/// - Parameter type: 连接类型
+- (void)switchConnect:(TlwConnectType)type;
+
+/// 传输业务数据给sdk
+/// - Parameter data:ai特征传过来的数据
+- (void)sendAiDataToSdk:(NSData *)data;
+
+/// 传输设备信息数据给sdk
+/// - Parameter data:设备信息特征传过来的数据
+- (void)sendDeviceDataToSdk:(NSData *)data;
+
+/// 获取设备信息
+/// - Parameter completation: 如果获取成功,则deviceInfo有值,否则为nil
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))complete;
+
+/// 设置DPI
+/// - Parameters:
+/// - value: 数值1 ~ 4 (分别对应 800 1200 1800 2400)
+/// - finished: 设置结果回调
+- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 打开录音
+/// - finished: 执行结果回调
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 关闭录音
+/// - finished: 执行结果回调
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+@end
--- /dev/null
+//
+// AiMouseSdk.m
+// AiMouseSdk
+//
+// Created by incar on 2024/4/23.
+//
+
+#import "TlwMouseSdk.h"
+#import "TlwAudioManager.h"
+#import "TlwTools.h"
+#import "TlwRecordManager.h"
+#import "TlwConnectManager.h"
+
+@interface TlwDeviceInfo()
+@end
+@implementation TlwDeviceInfo
+-(NSString *)description{
+ NSString *info = [NSString stringWithFormat:@"\n version = %d\n DPI = %d\n batteryState = %d\n batteryValue = %d\n",self.version,self.DPI,self.batteryState,self.batteryValue];
+ return info;
+}
+@end
+
+@interface TlwMouse()<TlwConnectManagerDelegate>
+
+@property (nonatomic, assign) TlwMouseLogLevel logLevel;
+@property (nonatomic, strong) TlwAudioManager *audioManager;
+@property (nonatomic, strong) TlwRecordManager *recordManager;
+@property (nonatomic, strong) TlwConnectManager *connectManager;
+
+@property (nonatomic,strong)void (^openRecord)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^closeRecord)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^setDpi)(BOOL isSuccess,NSError *error);
+@property (nonatomic,strong)void (^getMouseInfo)(TlwDeviceInfo *deviceInfo,NSError *error);
+@end
+
+@implementation TlwMouse
++(TlwMouse *)shared{
+ static TlwMouse *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[TlwMouse alloc]init];
+ });
+ return manager;
+}
+
+-(instancetype)init{
+ if (self = [super init]) {
+ _logLevel = TlwMouseLogLevel_none;
+ _connectType = TlwConnectType_bluetooth;
+ [self.connectManager start];
+ NSLog(@"[TlwMouseSdk]%@",[NSString stringWithFormat:@"AiMouseSdk_v%@",self.version]);
+ }
+ return self;
+}
+
+-(NSString *)version{
+ return @"1.0.0";
+}
+
+-(TlwAudioManager *)audioManager{
+ if (!_audioManager) {
+ _audioManager = [[TlwAudioManager alloc] init];
+ __weak typeof(self) ws = self;
+ _audioManager.decodeCallBack = ^(NSData * _Nonnull data) {
+ [ws sendRecordData:data];
+ };
+ _audioManager.outputLog = ^(NSString * _Nonnull log) {
+ [ws showLog:log];
+ };
+ }
+ return _audioManager;
+}
+
+- (TlwRecordManager *)recordManager{
+ if (!_recordManager) {
+ _recordManager = [[TlwRecordManager alloc] init];
+ __weak typeof(self) ws = self;
+ _recordManager.didRecordData = ^(NSData * _Nonnull data) {
+ [ws showLog:[NSString stringWithFormat:@"%@",data]];
+ [ws sendRecordData:data];
+ };
+ }
+ return _recordManager;
+}
+
+-(TlwConnectManager *)connectManager{
+ if (!_connectManager) {
+ _connectManager = [[TlwConnectManager alloc] init];
+ _connectManager.delegate = self;
+ }
+ return _connectManager;
+}
+
+#pragma mark - pub Api
+- (void)setLogLevel:(TlwMouseLogLevel)level{
+ _logLevel = level;
+}
+- (void)didDisconnect{
+ [self showLog:@"系统断开连接"];
+ [self stopRecord];
+ [self.connectManager disConnected];
+}
+
+- (void)switchConnect:(TlwConnectType)type{
+ if(type != _connectType && self.connected){
+ [self.connectManager disConnected];
+ }
+ _connectType = type;
+ [self showLog:type == TlwConnectType_bluetooth?@"sdk切换到蓝牙模式":@"sdk切换到usb模式"];
+
+
+ if (type == TlwConnectType_hid) {
+ [self.recordManager checkPermissions];
+ }
+}
+
+- (void)sendAiDataToSdk:(NSData *)data{
+ [self.connectManager resetTimeOut];
+
+ [self showLog:[NSString stringWithFormat:@"[receive][Ai] %@",data]];
+ if(self.connectType == TlwConnectType_hid){
+//// 0AFFFF12BC32
+// if(data.length == 6){
+// NSData *subData = [data subdataWithRange:NSMakeRange(5, 1)];
+// [self sendCmdData:subData];
+// uint8 cmd;
+// [subData getBytes:&cmd length:1];
+//// [self showLog:[NSString stringWithFormat:@"cmd = %X",cmdInt]];
+// if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){
+// self.recordManager.startCmd = subData;
+// [self startRecord];
+// }
+// else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){
+// [self stopRecord:false];
+// }
+// }
+// else{
+// [self showLog:@"未实现指令"];
+// }
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ [self bleReceiveAiData:data];
+ }
+}
+
+-(void)sendDeviceDataToSdk:(NSData *)data{
+ [self showLog:[NSString stringWithFormat:@"[receive][info] %@",data]];
+ if(self.connectType == TlwConnectType_hid){
+ [self hidReceiveDeviceData:data];
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ [self bleReceiveDeviceData:data];
+ }
+}
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if(self.connectType == TlwConnectType_hid){
+ self.openRecord = finished;
+ uint8_t cmd = 0x03;
+ uint8_t len = 0x00;
+ uint8_t tail = 0xEF;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendData:[self getHidHeader]];
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&len length:1];
+ [mData appendBytes:&tail length:1];
+ [self sendToDevice:mData];
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ self.openRecord = finished;
+ uint8_t cmd = 0x03;
+ uint8_t dir = 0x01;
+ uint8_t len = 0x00;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&dir length:1];
+ [mData appendBytes:&len length:1];
+ [self sendToDevice:mData];
+ }
+}
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if(self.connectType == TlwConnectType_hid){
+ self.closeRecord = finished;
+ uint8_t cmd = 0x04;
+ uint8_t len = 0x00;
+ uint8_t tail = 0xEF;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendData:[self getHidHeader]];
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&len length:1];
+ [mData appendBytes:&tail length:1];
+ [self sendToDevice:mData];
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ self.closeRecord = finished;
+ uint8_t cmd = 0x04;
+ uint8_t dir = 0x01;
+ uint8_t len = 0x00;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&dir length:1];
+ [mData appendBytes:&len length:1];
+ [self sendToDevice:mData];
+ }
+
+}
+
+- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if(self.connectType == TlwConnectType_hid){
+ self.setDpi = finished;
+ uint8_t type = 0x06;
+ uint8_t len = 0x01;
+ uint8_t tail = 0xEF;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendData:[self getHidHeader]];
+ [mData appendBytes:&type length:1];
+ [mData appendBytes:&len length:1];
+ [mData appendBytes:&value length:1];
+ [mData appendBytes:&tail length:1];
+ [self sendToDevice:mData];
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ self.setDpi = finished;
+ uint8_t cmd = 0x06;
+ uint8_t dir = 0x01;
+ uint8_t len = 0x01;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&dir length:1];
+ [mData appendBytes:&len length:1];
+ [mData appendBytes:&value length:1];
+ [self sendToDevice:mData];
+ }
+
+
+}
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished{
+ if(self.connectType == TlwConnectType_hid){
+ self.getMouseInfo = finished;
+// 0x05fff1fec09f0114ef
+ uint8_t cmd = 0x0c;
+ uint8_t len = 0x00;
+ uint8_t tail = 0xEF;
+
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendData:[self getHidHeader]];
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&len length:1];
+ [mData appendBytes:&tail length:1];
+ [self sendToDevice:mData];
+ }
+ else if (self.connectType == TlwConnectType_bluetooth){
+ self.getMouseInfo = finished;
+ uint8_t cmd = 0x0c;
+ uint8_t dir = 0x01;
+ uint8_t len = 0x00;
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendBytes:&cmd length:1];
+ [mData appendBytes:&dir length:1];
+ [mData appendBytes:&len length:1];
+ [self sendToDevice:mData];
+ }
+}
+
+#pragma mark - ble
+- (void)bleReceiveAiData:(NSData *)data{
+ [self.connectManager resetTimeOut];
+ [self.audioManager receiveData:data];
+}
+
+-(void)bleReceiveDeviceData:(NSData *)data{
+ NSData *typeData = [data subdataWithRange:NSMakeRange(0, 1)];
+ uint8 type;
+ [typeData getBytes:&type length:1];
+
+ NSData *stateData = [data subdataWithRange:NSMakeRange(1, 1)];
+ uint8 state;
+ [stateData getBytes:&state length:1];
+ BOOL success = state == 0x02;
+
+ NSData *lenData = [data subdataWithRange:NSMakeRange(2, 1)];
+ uint8 len;
+ [lenData getBytes:&len length:1];
+
+ switch (type) {
+ case 0x6A:
+ //语音开启回复
+ [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]];
+ self.openRecord(success, nil);
+ break;
+ case 0x6B:
+ //语音停止回复
+ [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]];
+ self.closeRecord(success, nil);
+ break;
+ case 0x96:
+ //设置DPI回复
+ [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]];
+ self.setDpi(success, nil);
+ break;
+ case 0x9c:
+ { //鼠标状态查询回复
+ uint16_t version;
+ [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2];
+
+ uint16_t DPI;
+ [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2];
+
+ uint16_t batteryState;
+ [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1];
+
+ uint16_t batteryValue;
+ [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1];
+
+ TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init];
+ info.version = version;
+ info.DPI = DPI;
+ info.batteryState = batteryState;
+ info.batteryValue = batteryValue;
+ [self showLog:info.description];
+ self.getMouseInfo(info, nil);
+ }
+ break;
+ case 0x9f:
+ {
+ //鼠标按键上报
+ NSData *subData = [data subdataWithRange:NSMakeRange(3, 1)];
+ [self sendCmdData:subData];
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#pragma mark - hid
+- (void)hidReceiveDeviceData:(NSData *)data{
+// 0x05fff1fec09f0114ef
+ uint8 type;
+ [[data subdataWithRange:NSMakeRange(5, 1)] getBytes:&type length:1];
+
+ NSData *lenData = [data subdataWithRange:NSMakeRange(6, 1)];
+ uint8 len;
+ [lenData getBytes:&len length:1];
+
+ switch (type) {
+ case 0x6A:
+ {
+ //语音开启回复
+ uint8 state;
+ [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+ BOOL success = state == 0x02;
+ [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]];
+ self.openRecord(success, nil);
+ [self startRecord];
+ break;
+ }
+
+ case 0x6B:
+ {
+ //语音停止回复
+ uint8 state;
+ [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+ BOOL success = state == 0x02;
+
+ [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]];
+ [self stopRecord];
+ self.closeRecord(success, nil);
+ break;
+ }
+ case 0x96:
+ {
+ //设置DPI回复
+ uint8 state;
+ [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1];
+ BOOL success = state == 0x02;
+
+ [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]];
+ self.setDpi(success, nil);
+ break;
+ }
+ case 0x9c:
+ { //鼠标状态查询回复
+ uint16_t version;
+ [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2];
+
+ uint16_t DPI;
+ [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2];
+
+ uint16_t batteryState;
+ [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1];
+
+ uint16_t batteryValue;
+ [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1];
+
+ TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init];
+ info.version = version;
+ info.DPI = DPI;
+ info.batteryState = batteryState;
+ info.batteryValue = batteryValue;
+ [self showLog:info.description];
+ self.getMouseInfo(info, nil);
+ }
+ break;
+ case 0x9f:
+ {
+ NSData *subData = [data subdataWithRange:NSMakeRange(7, 1)];
+ [self sendCmdData:subData];
+
+ uint8 cmd;
+ [subData getBytes:&cmd length:1];
+
+ if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){
+ self.recordManager.startCmd = subData;
+ [self startRecord];
+ }
+ else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){
+ [self stopRecord];
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+- (NSData *)getHidHeader{
+ uint8_t head1 = 0x05;
+ uint16_t head2 = 0xFFF1;
+ uint8_t head3 = 0xfe;
+ uint8_t head4 = 0x00;
+
+ NSMutableData *mData = NSMutableData.data;
+ [mData appendBytes:&head1 length:1];
+ [mData appendBytes:&head2 length:2];
+ [mData appendBytes:&head3 length:1];
+ [mData appendBytes:&head4 length:1];
+
+ return mData;
+}
+#pragma mark - other
+- (void)sendRecordData:(NSData *)data{
+ [self showLog:[NSString stringWithFormat:@"[send][audio] %@",data]];
+ if (self.delegate) [self.delegate tlwMouseSendRecordData:data];
+}
+
+- (void)sendCmdData:(NSData *)data{
+ [self showLog:[NSString stringWithFormat:@"[send] %@",data]];
+ if (self.delegate) [self.delegate tlwMouseSendCmdData:data];
+}
+
+- (void)showLog:(NSString *)log{
+ if (self.delegate && [self.delegate respondsToSelector:@selector(aiMouseAddLog:)]) {
+ [self.delegate aiMouseAddLog:[NSString stringWithFormat:@"[AiMouseSdk]%@",log]];
+ }
+ if(self.logLevel == TlwMouseLogLevel_info){
+ NSLog(@"[AiMouseSdk]%@",log);
+ }
+}
+
+
+- (void)sendToDevice:(NSData *)data{
+ [self showLog:[NSString stringWithFormat:@"[send][info] %@",data]];
+ [self.delegate tlwMouseSendDeviceInfoData:data];
+}
+#pragma mark - deviceInfo
+- (void)startRecord{
+ [self.recordManager start];
+}
+
+- (void)stopRecord{
+ if (self.recordManager.recording ) {
+ [self.recordManager stop];
+ }
+}
+
+#pragma mark - TlwConnectManagerDelegate
+-(void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected{
+ _connected = connected;
+ if([self.delegate respondsToSelector:@selector(aiMouseConnectStateDidChange:)]) [self.delegate aiMouseConnectStateDidChange:connected];
+}
+-(void)TlwConnectManagerReSendData:(TlwConnectManager *)manager{
+// __weak typeof(self) ws = self;
+// [self.deviceInfoManager getVersion:^(NSString * _Nonnull version, NSError * _Nonnull error) {
+// if (error.code == AiMouseErrorCode_timeOut) {
+// [ws.connectManager addTimeOut];
+// }
+// }];
+}
+-(void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log{
+ [self showLog:log];
+}
+@end
+
--- /dev/null
+//
+// TlwRecordManager.h
+// AudioRecorder
+//
+// Created by incar on 2024/3/29.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwRecordManager : NSObject
+@property (nonatomic,assign)BOOL recording;
+@property (nonatomic,strong)NSData *startCmd;
+@property (nonatomic,readonly)NSData *stopCmd;
+@property (nonatomic,strong)void (^didRecordData)(NSData *data);
+
+- (void)checkPermissions;
+
+- (void)start;
+
+- (void)stop;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwRecordManager.m
+// AudioRecorder
+//
+// Created by incar on 2024/3/29.
+//
+
+#import "TlwRecordManager.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface TlwRecordManager()
+@property (nonatomic,assign)BOOL isAllowRecord;
+@property (nonatomic,strong)AVAudioEngine *audioEngine;
+@property (nonatomic,strong)AVAudioFile *outputFile;
+
+@end
+
+@implementation TlwRecordManager
+//+(TlwRecordManager *)shared{
+// static TlwRecordManager *manager = nil;
+// static dispatch_once_t oncePredicate;
+// dispatch_once(&oncePredicate, ^{
+// manager = [[TlwRecordManager alloc]init];
+// });
+// return manager;
+//}
+
+
+-(instancetype)init{
+ if (self = [super init ]) {
+ self.isAllowRecord = false;
+// [self checkPermissions];
+ }
+ return self;
+}
+
+- (void)start{
+ if (self.isAllowRecord == false) {
+ return;
+ }
+ // let audioSession = AVAudioSession.sharedInstance()
+ // do {
+ // try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker)
+ // try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
+ // catch let error as NSError {
+ // print("ERROR:", error)
+ // }
+ self.recording = true;
+
+ self.audioEngine = [[AVAudioEngine alloc] init];
+ AVAudioInputNode *input = self.audioEngine.inputNode;
+ AVAudioNodeBus bus = 0;
+ AVAudioFormat *inputFormat = [input inputFormatForBus:bus];
+ // NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",NSTemporaryDirectory()];
+ // NSURL *fileURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject URLByAppendingPathComponent:@"record.caf"];
+ // [self showLog:fileURL.absoluteString];
+ // NSError *err0;
+ // self.outputFile = [[AVAudioFile alloc] initForWriting:fileURL settings:inputFormat.settings commonFormat:inputFormat.commonFormat interleaved:inputFormat.isInterleaved error:&err0];
+ // if (err0) {
+ // [self showLog:[NSString stringWithFormat:@"设置写入路径 %@", err0.description]];
+ // }
+ // else{
+ // [self showLog:@"写入路径设置成功"];
+ // }
+ // self.recordData = [[NSMutableData alloc] init];
+ __weak typeof(self) ws = self;
+ [input installTapOnBus:bus bufferSize:32 format:inputFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
+ NSData *data = [ws pcm_downsample:buffer];
+ if (ws.didRecordData) {
+ ws.didRecordData(data);
+ }
+// NSLog(@"%@",data);
+// NSError *err0;
+ // [ws.outputFile writeFromBuffer:buffer error:&err0];
+ // if (err0) {
+ // [self showLog:[NSString stringWithFormat:@"写入失败 %@", err0.description]];
+ // }
+ // else{
+ // [self showLog:@"写入成功"];
+ // }
+ }];
+
+ NSError *err1;
+ [self.audioEngine prepare];
+ [self.audioEngine startAndReturnError:&err1];
+ if (err1) {
+ [self showLog:[NSString stringWithFormat:@"启动录音失败 %@", err1.description]];
+ }
+ else{
+ [self showLog:@"启动成功"];
+ }
+}
+
+- (void)stop{
+ [self.audioEngine stop];
+ self.recording = false;
+ // [self finishRecored:self.recordData];
+}
+
+- (void)checkPermissions{
+ switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
+ case AVAuthorizationStatusAuthorized:
+ self.isAllowRecord = true;
+ [self showLog:@"录音已经授权"];
+ break;
+ case AVAuthorizationStatusNotDetermined:
+ {
+ [self showLog:@"录音未授权"];
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
+ if (granted) {
+ self.isAllowRecord = true;
+ [self showLog:@"录音已经授权"];
+ }
+ else{
+ [self showLog:@"已拒绝"];
+ }
+ }];
+ }
+ break;
+ case AVAuthorizationStatusDenied:
+ [self showLog:@"已拒绝"];
+ [self openSettings];
+ break;
+ default:
+ [self showLog:@"已拒绝"];
+ [self openSettings];
+ break;
+ }
+}
+
+- (void)openSettings{
+// NSURL *url = [NSURL URLWithString:@""];
+}
+
+- (void)showLog:(NSString *)log{
+// NSLog(@"%@",log);
+}
+
+//将录音数据保存
+- (void)finishRecored:(NSData *)data{
+ NSString *doc = NSTemporaryDirectory();
+ NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];
+ NSError *err;
+ [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];
+ if (err) {
+ [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];
+ }
+ else{
+ [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];
+ }
+}
+
+-(NSData*) pcm_downsample:(AVAudioPCMBuffer*)buffer {
+ //位深转换 float32 转 int16_t
+ NSData* pData = [[NSData alloc] initWithBytes:buffer.floatChannelData[0] length:buffer.frameLength * 4];
+ Byte*dataByte = (Byte*)[pData bytes];
+ NSUInteger srcSize = pData.length/4;
+ Float32 vf32[srcSize];
+ memcpy(vf32, &dataByte[0], pData.length);
+ int16_t bArr[srcSize];
+ int r;
+ for(size_t i =0; i < srcSize; ++i) {
+ float x = vf32[i];
+ float c;
+ c = ((x < -1) ? -1: ((x >1) ?1: x));
+ c = c +1;
+ r = (int)(c *32767.5f);
+ r = r -32768;
+ bArr[i] = (short)r;
+ }
+ //码率转换 44100 转 16000
+ uint32_t last_pos = (uint32_t)(srcSize -1);
+ uint32_t dstSize = (srcSize/44100.0)*16000;
+ int16_t bArr16[dstSize];
+ for(uint32_t idx =0; idx < dstSize; idx++) {
+ float index = ((float) idx *44100.0) / (16000.0);
+ uint32_t p1 = (uint32_t) index;
+ float coef = index - p1;
+ uint32_t p2 = (p1 == last_pos) ? last_pos : p1 +1;
+ bArr16[idx] = (int16_t) ((1.0f- coef) * bArr[p1] + coef * bArr[p2]);
+ }
+ NSMutableData *data1 = [[NSMutableData alloc] init];
+ for(NSUInteger i =0; i < dstSize; i++) {
+ unsigned char valChar[2];
+ valChar[0] =0xff& bArr16[i];
+ valChar[1] = (0xff00& bArr16[i]) >>8;
+ [data1 appendBytes:valChar length:2];
+ }
+ return data1;
+}
+
+
+
+@end
--- /dev/null
+//
+// TlwTools.h
+// AiMouse
+//
+// Created by incar on 2024/2/28.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwTools : NSObject
++ (NSArray *)stringToArray:(NSString *)jsonString;
++ (NSString *)arrayToString:(NSArray *)arr;
+
++ (NSDictionary *)stringToDictionary:(NSString *)jsonString;
++ (NSString *)dictionaryToString:(NSDictionary *)dic;
+
++ (NSDictionary *)dataToDictionary:(NSData *)jsonData;
++ (NSData *)dictionaryToData:(NSDictionary *)dicJson;
+
+
++ (NSString *)dataToHexString:(NSData *)data;
+
++ (NSString *)dataToHexString_placeholder:(NSData *)data;
+
++ (NSData *)hexStringToData:(NSString *)hexStr ;
+
++ (NSString *)dataToUtf8String:(NSData *)data;
+
++ (NSString *)stringTomd5:(NSString *)str;
+
++ (NSString *)stringToHmacSha1:(NSString*)key text:(NSString*)text;
+
++ (NSString *)MD5ForUpper32:(NSString *)str;
+
++ (NSData *)dataWithHexString:(NSString *)hexStr;
+
++(NSString *)randomString:(NSInteger)length;
+
++(NSString *)getCurrentTime;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwTools.m
+// AiMouse
+//
+// Created by incar on 2024/2/28.
+//
+
+#import "TlwTools.h"
+#import <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonHMAC.h>
+
+@implementation TlwTools
++ (NSArray *)stringToArray:(NSString *)jsonString{
+ NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+ NSError *error;
+ NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
+ return arr;
+}
++ (NSString *)arrayToString:(NSArray *)arr{
+ NSError *error;
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:NSJSONWritingPrettyPrinted error:&error];
+ NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ return string;
+}
++ (NSDictionary *)stringToDictionary:(NSString *)jsonString{
+ NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+ if (jsonData == nil) {
+ return nil;
+ }
+ NSError *error;
+ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
+ return dict;
+}
++ (NSString *)dictionaryToString:(NSDictionary *)dic{
+ if ([dic isKindOfClass:[NSDictionary class]]) {
+ NSError *error;
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];
+ NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ return string;
+ }
+ else{
+ return @"";
+ }
+}
+
++ (NSDictionary *)dataToDictionary:(NSData *)jsonData{
+ if (![jsonData isKindOfClass:[NSData class]] || jsonData.length < 1) {
+ return nil;
+ }
+ id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
+ if (![jsonObj isKindOfClass:[NSDictionary class]]) {
+ return nil;
+ }
+ return [NSDictionary dictionaryWithDictionary:(NSDictionary *)jsonObj];
+}
+
+
++ (NSData *)dictionaryToData:(NSDictionary *)dicJson{
+ if (![dicJson isKindOfClass:[NSDictionary class]]) {
+ return nil;
+ }
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dicJson options:0 error:nil];
+ if (![jsonData isKindOfClass:[NSData class]]) {
+ return nil;
+ }
+ return jsonData;
+}
+
++ (NSString *)dataToHexString:(NSData *)data {
+ NSUInteger length = data.length;
+ NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+ const unsigned char *byte = data.bytes;
+ for (int i = 0; i < length; i++, byte++) {
+ [result appendFormat:@"%02X", *byte];
+ }
+ return result;
+}
+
++ (NSString *)dataToHexString_placeholder:(NSData *)data {
+ NSUInteger length = data.length;
+ NSString *placeholder = @" ";
+ NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+ const unsigned char *byte = data.bytes;
+ for (int i = 0; i < length; i++, byte++) {
+ [result appendFormat:@"%02X%@", *byte, placeholder];
+ }
+ return result;
+}
+
++ (NSData *)hexStringToData:(NSString *)hexStr {
+ hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""];
+ hexStr = [hexStr lowercaseString];
+ NSUInteger len = hexStr.length;
+ if (!len) return nil;
+ unichar *buf = malloc(sizeof(unichar) * len);
+ if (!buf) return nil;
+ [hexStr getCharacters:buf range:NSMakeRange(0, len)];
+
+ NSMutableData *result = [NSMutableData data];
+ unsigned char bytes;
+ char str[3] = { '\0', '\0', '\0' };
+ int i;
+ for (i = 0; i < len / 2; i++) {
+ str[0] = buf[i * 2];
+ str[1] = buf[i * 2 + 1];
+ bytes = strtol(str, NULL, 16);
+ [result appendBytes:&bytes length:1];
+ }
+ free(buf);
+ return result;
+}
+
++ (NSString *)dataToUtf8String:(NSData *)data {
+ if (data.length > 0) {
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ }
+ return @"";
+}
+
+
++ (NSString *)stringTomd5:(NSString *)str {
+ if (str.length == 0) {
+ return nil;
+ }
+ // 初始化C字符数组
+ const char* original_str = (const char *)[[str dataUsingEncoding:NSUTF8StringEncoding] bytes];
+ // 盛放数字校验的字符数组(长度为16bytes)
+ unsigned char digist[CC_MD5_DIGEST_LENGTH]; //CC_MD5_DIGEST_LENGTH = 16
+ CC_MD5(original_str, (uint)strlen(original_str), digist);
+ // NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10];
+ NSMutableString* outPutStr = [NSMutableString new];
+ for(int i =0; i<CC_MD5_DIGEST_LENGTH;i++){
+ [outPutStr appendFormat:@"%02X",digist[i]];//小写x表示输出的是小写MD5,大写X表示输出的是大写MD5
+ }
+ return outPutStr;
+}
+
+//HmacSHA1加密
++ (NSString *)stringToHmacSha1:(NSString*)key text:(NSString*)text{
+ const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
+ const char *cData = [text cStringUsingEncoding:NSUTF8StringEncoding];
+ uint8_t cHMAC[CC_SHA1_DIGEST_LENGTH];
+ CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
+ //NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:CC_SHA1_DIGEST_LENGTH];
+ NSString *hash;
+ NSMutableString * output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
+ for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
+ [output appendFormat:@"%02X", cHMAC[i]];
+ hash = output;
+ return hash;
+}
+
++ (NSString *)MD5ForUpper32:(NSString *)str {
+ const char* input = [str UTF8String];
+ unsigned char result[CC_MD5_DIGEST_LENGTH];
+ CC_MD5(input, (CC_LONG)strlen(input), result);
+ // 'CC_MD5' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).
+ NSMutableString *digest = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
+ for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
+ [digest appendFormat:@"%02X", result[i]];
+ }
+
+ return digest;
+}
+
++ (NSData *)dataWithHexString:(NSString *)hexStr {
+ hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""];
+ hexStr = [hexStr lowercaseString];
+ NSUInteger len = hexStr.length;
+ if (!len) return nil;
+ unichar *buf = malloc(sizeof(unichar) * len);
+ if (!buf) return nil;
+ [hexStr getCharacters:buf range:NSMakeRange(0, len)];
+
+ NSMutableData *result = [NSMutableData data];
+ unsigned char bytes;
+ char str[3] = { '\0', '\0', '\0' };
+ int i;
+ for (i = 0; i < len / 2; i++) {
+ str[0] = buf[i * 2];
+ str[1] = buf[i * 2 + 1];
+ bytes = strtol(str, NULL, 16);
+ [result appendBytes:&bytes length:1];
+ }
+ free(buf);
+ return result;
+}
+
++(NSString *)randomString:(NSInteger)length {
+ char letters[52] = "1234567891234567891234567891234567891234567891234567";
+// char letters[10] = "012345678901234567890123456789012345678901234567890123456789";
+ char *output = malloc(sizeof(char) * (length + 1));
+ for (int i = 0; i < length; i++) {
+ int index = arc4random_uniform(52);
+ output[i] = letters[index];
+ }
+ output[length] = '\0';
+
+ return [[NSString alloc] initWithBytesNoCopy:output length:length encoding:NSUTF8StringEncoding freeWhenDone:YES];
+}
+
++(NSString *)getCurrentTime{
+// @"2024-08-05T10:07:39Z"
+ NSDate *date = [NSDate date];
+// NSLog(@"当前零时区时间 %@", date);
+
+ NSDateFormatter *dateFormaterA = [[NSDateFormatter alloc]init];
+ [dateFormaterA setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // 设置时区为格林尼治标准时区
+ [dateFormaterA setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+ NSString *dateStrA = [dateFormaterA stringFromDate:date];
+ NSString *text1 = [[dateStrA componentsSeparatedByString:@" "] componentsJoinedByString:@"T"];
+ NSString *text2 = [text1 stringByAppendingString:@"Z"];
+// NSLog(@"%@",text2);
+
+
+ // 如果你需要将这个UTC时间显示为格林尼治时间,你可以这样做:
+// NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+// [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // 设置时区为格林尼治标准时区
+// NSString *gmTimeString = [dateFormatter stringFromDate:date]; // 将UTC日期转换为字符串表示形式,时区设置为GMT
+// NSLog(@"%@ == ",gmTimeString);
+
+ return text2;
+}
+@end
+
--- /dev/null
+/********************************************************************************************************
+ * @file adpcm.c
+ *
+ * @brief This is the source file for BLE SDK
+ *
+ * @author BLE GROUP
+ * @date 06,2020
+ *
+ * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Unless for usage inside a TELINK integrated circuit, redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * 3. Neither the name of TELINK, nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ * TELINK integrated circuit. All other usages are subject to written permission
+ * from TELINK and different commercial license may apply.
+ *
+ * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
+ * relating to such deletion(s), modification(s) or alteration(s).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************************************/
+
+#include "adpcm.h"
+
+static const signed char idxtbl[] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
+static const unsigned short steptbl[] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };
+
+#define NUM_OF_ORIG_SAMPLE 2
+
+void adpcm_to_pcm (signed short *ps, signed short *pd, int len)
+{
+ int i;
+ //byte2,byte1: predict; byte3: predict_idx; byte4:adpcm data len
+ int predict = ps[0];
+ int predict_idx = ps[1] & 0xff;
+// int adpcm_len = (ps[1]>>8) & 0xff;
+ unsigned char *pcode = (unsigned char *) (ps + NUM_OF_ORIG_SAMPLE);
+ unsigned char code;
+ code = *pcode ++;
+
+ //byte5- byte128: 124 byte(62 sample) adpcm data
+ for (i=0; i<len; i++) {
+
+ if (1) {
+ int step = steptbl[predict_idx];
+
+ int diffq = step >> 3;
+
+ if (code & 4) {
+ diffq = diffq + step;
+ }
+ step = step >> 1;
+ if (code & 2) {
+ diffq = diffq + step;
+ }
+ step = step >> 1;
+ if (code & 1) {
+ diffq = diffq + step;
+ }
+
+ if (code & 8) {
+ predict = predict - diffq;
+ }
+ else {
+ predict = predict + diffq;
+ }
+
+ if (predict > 32767) {
+ predict = 32767;
+ }
+ else if (predict < -32768) {
+ predict = -32768;
+ }
+
+ predict_idx = predict_idx + idxtbl[code & 15];
+
+ if(predict_idx < 0) {
+ predict_idx = 0;
+ }
+ else if(predict_idx > 88) {
+ predict_idx = 88;
+ }
+
+ if (i&1) {
+ code = *pcode ++;
+ }
+ else {
+ code = code >> 4;
+ }
+ }
+
+ if (0 && i < NUM_OF_ORIG_SAMPLE) {
+ *pd++ = ps[i];
+ }
+ else {
+ *pd++ = predict;
+ }
+ }
+}
+
+//例子
+#if 0
+#define MIC_ADPCM_FRAME_SIZE 128 //一包 adpcm 的数据长度 128 byte
+#define MIC_SHORT_DEC_SIZE 248
+#define DEC_BUFFER_SIZE (MIC_SHORT_DEC_SIZE<<2)
+
+unsigned char abuf_dec_wptr; // 0~3 , 4个缓冲 ,循环使用
+unsigned char abuf_mic[MIC_ADPCM_FRAME_SIZE * 4]; //压缩的语音数据adpcm缓冲区 ,4个缓冲 ,每个缓冲区 128 byte , 循环使用
+signed short abuf_dec[DEC_BUFFER_SIZE]; //解压后存放的数据,注意数组类型是 16 bit,4个缓冲,循环使用
+
+//abuf_mic 源 , abuf_dec 目的 , 每次解压 128 byte adpcm
+adpcm_to_pcm ((signed short *)(abuf_mic + (abuf_dec_wptr & 3) * MIC_ADPCM_FRAME_SIZE), abuf_dec + (abuf_dec_wptr & 3) * MIC_SHORT_DEC_SIZE, MIC_SHORT_DEC_SIZE );
+
+#endif
+
--- /dev/null
+/********************************************************************************************************
+ * @file adpcm.h
+ *
+ * @brief This is the header file for BLE SDK
+ *
+ * @author BLE GROUP
+ * @date 06,2020
+ *
+ * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Unless for usage inside a TELINK integrated circuit, redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * 3. Neither the name of TELINK, nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ * TELINK integrated circuit. All other usages are subject to written permission
+ * from TELINK and different commercial license may apply.
+ *
+ * 5. Licensee shall be solely responsible for any claim to the extent arising out of or
+ * relating to such deletion(s), modification(s) or alteration(s).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************************************/
+#ifndef ADPCM_H_
+#define ADPCM_H_
+
+void adpcm_to_pcm(signed short *ps, signed short *pd, int len);
+
+#endif /* ADPCM_H_ */
18BF1DF02B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DEF2B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m */; };
18BF1E222B83066200B8D90F /* BleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E212B83066200B8D90F /* BleManager.m */; };
18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E752B832D2E00B8D90F /* ScanVC.m */; };
+ 18EF7B0A2C7605BF00AC5223 /* MouseSdkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */; };
+ 18EF7B0D2C76079E00AC5223 /* TlwVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B0C2C76079E00AC5223 /* TlwVC.m */; };
+ 18EF7B102C7607C100AC5223 /* FrkVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B0F2C7607C100AC5223 /* FrkVC.m */; };
+ 18EF7B172C760A5B00AC5223 /* TlwBleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */; };
+ 18EF7B182C760A5B00AC5223 /* TlwHidManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */; };
+ 18EF7B192C760A5B00AC5223 /* TlwDeviceManger.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */; };
+ 18EF7B1D2C760FF700AC5223 /* TlwScanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */; };
+ 18EF7B1E2C760FF700AC5223 /* TlwScanVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */; };
18F3235D2B8ED78300CABA2E /* ScanCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F3235C2B8ED78300CABA2E /* ScanCell.m */; };
18F328D52B98569D00CABA2E /* Config.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F328D42B98569D00CABA2E /* Config.m */; };
8A6657BD1406314D4CBB7CEF /* libPods-MouseSdk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0311DF3AD67ED792549B96BD /* libPods-MouseSdk.a */; };
18BF1E752B832D2E00B8D90F /* ScanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanVC.m; sourceTree = "<group>"; };
18C31D6F2BBAA6CC0089D656 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
18CB080B2BA401D0005F5854 /* liblzma.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.5.tbd; path = usr/lib/liblzma.5.tbd; sourceTree = SDKROOT; };
+ 18EF7B082C7605BF00AC5223 /* MouseSdkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MouseSdkManager.h; sourceTree = "<group>"; };
+ 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MouseSdkManager.m; sourceTree = "<group>"; };
+ 18EF7B0B2C76079E00AC5223 /* TlwVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TlwVC.h; sourceTree = "<group>"; };
+ 18EF7B0C2C76079E00AC5223 /* TlwVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TlwVC.m; sourceTree = "<group>"; };
+ 18EF7B0E2C7607C100AC5223 /* FrkVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrkVC.h; sourceTree = "<group>"; };
+ 18EF7B0F2C7607C100AC5223 /* FrkVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrkVC.m; sourceTree = "<group>"; };
+ 18EF7B112C760A5A00AC5223 /* TlwHidManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwHidManager.h; sourceTree = "<group>"; };
+ 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwBleManager.m; sourceTree = "<group>"; };
+ 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwHidManager.m; sourceTree = "<group>"; };
+ 18EF7B142C760A5A00AC5223 /* TlwDeviceManger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwDeviceManger.h; sourceTree = "<group>"; };
+ 18EF7B152C760A5A00AC5223 /* TlwBleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwBleManager.h; sourceTree = "<group>"; };
+ 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwDeviceManger.m; sourceTree = "<group>"; };
+ 18EF7B1A2C760FF700AC5223 /* TlwScanVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TlwScanVC.h; sourceTree = "<group>"; };
+ 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TlwScanVC.m; sourceTree = "<group>"; };
+ 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TlwScanVC.xib; sourceTree = "<group>"; };
18F3235B2B8ED78300CABA2E /* ScanCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanCell.h; sourceTree = "<group>"; };
18F3235C2B8ED78300CABA2E /* ScanCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanCell.m; sourceTree = "<group>"; };
18F3249D2B9337D500CABA2E /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
18BF1DCC2B81AB7C00B8D90F /* MouseSdk */ = {
isa = PBXGroup;
children = (
+ 18EF7B072C7604BF00AC5223 /* Ble */,
+ 18EF7B062C7604AA00AC5223 /* Tlw */,
18C31D6F2BBAA6CC0089D656 /* Info.plist */,
- 18BF1E202B83066200B8D90F /* BleManager.h */,
- 18BF1E212B83066200B8D90F /* BleManager.m */,
- 1867E5A72BB2706F008FC5FB /* HidManager.h */,
- 1867E5A82BB2706F008FC5FB /* HidManager.m */,
- 182B548B2BB7EC9B009F53B6 /* DeviceManger.h */,
- 182B548C2BB7EC9B009F53B6 /* DeviceManger.m */,
18BF1DCD2B81AB7C00B8D90F /* AppDelegate.h */,
18BF1DCE2B81AB7C00B8D90F /* AppDelegate.m */,
+ 18EF7B082C7605BF00AC5223 /* MouseSdkManager.h */,
+ 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */,
18BF1DD02B81AB7C00B8D90F /* ViewController.h */,
18BF1DD12B81AB7C00B8D90F /* ViewController.m */,
+ 18EF7B0B2C76079E00AC5223 /* TlwVC.h */,
+ 18EF7B0C2C76079E00AC5223 /* TlwVC.m */,
+ 18EF7B0E2C7607C100AC5223 /* FrkVC.h */,
+ 18EF7B0F2C7607C100AC5223 /* FrkVC.m */,
+ 18EF7B1A2C760FF700AC5223 /* TlwScanVC.h */,
+ 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */,
+ 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */,
18BF1E742B832D2E00B8D90F /* ScanVC.h */,
18BF1E752B832D2E00B8D90F /* ScanVC.m */,
18F3235B2B8ED78300CABA2E /* ScanCell.h */,
path = MouseSdkUITests;
sourceTree = "<group>";
};
+ 18EF7B062C7604AA00AC5223 /* Tlw */ = {
+ isa = PBXGroup;
+ children = (
+ 18EF7B142C760A5A00AC5223 /* TlwDeviceManger.h */,
+ 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */,
+ 18EF7B152C760A5A00AC5223 /* TlwBleManager.h */,
+ 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */,
+ 18EF7B112C760A5A00AC5223 /* TlwHidManager.h */,
+ 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */,
+ );
+ path = Tlw;
+ sourceTree = "<group>";
+ };
+ 18EF7B072C7604BF00AC5223 /* Ble */ = {
+ isa = PBXGroup;
+ children = (
+ 182B548B2BB7EC9B009F53B6 /* DeviceManger.h */,
+ 182B548C2BB7EC9B009F53B6 /* DeviceManger.m */,
+ 18BF1E202B83066200B8D90F /* BleManager.h */,
+ 18BF1E212B83066200B8D90F /* BleManager.m */,
+ 1867E5A72BB2706F008FC5FB /* HidManager.h */,
+ 1867E5A82BB2706F008FC5FB /* HidManager.m */,
+ );
+ path = Ble;
+ sourceTree = "<group>";
+ };
543EC4E5F9FC310D6B850947 /* Frameworks */ = {
isa = PBXGroup;
children = (
files = (
18BF1DD42B81AB7F00B8D90F /* Assets.xcassets in Resources */,
18BF1DD72B81AB7F00B8D90F /* Main.storyboard in Resources */,
+ 18EF7B1E2C760FF700AC5223 /* TlwScanVC.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 18EF7B192C760A5B00AC5223 /* TlwDeviceManger.m in Sources */,
18F3235D2B8ED78300CABA2E /* ScanCell.m in Sources */,
18BF1DD22B81AB7C00B8D90F /* ViewController.m in Sources */,
18BF1DD92B81AB7F00B8D90F /* main.m in Sources */,
+ 18EF7B172C760A5B00AC5223 /* TlwBleManager.m in Sources */,
18F328D52B98569D00CABA2E /* Config.m in Sources */,
+ 18EF7B182C760A5B00AC5223 /* TlwHidManager.m in Sources */,
+ 18EF7B0D2C76079E00AC5223 /* TlwVC.m in Sources */,
182B548D2BB7EC9B009F53B6 /* DeviceManger.m in Sources */,
+ 18EF7B102C7607C100AC5223 /* FrkVC.m in Sources */,
18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */,
+ 18EF7B1D2C760FF700AC5223 /* TlwScanVC.m in Sources */,
+ 18EF7B0A2C7605BF00AC5223 /* MouseSdkManager.m in Sources */,
18BF1E222B83066200B8D90F /* BleManager.m in Sources */,
18BF1DCF2B81AB7C00B8D90F /* AppDelegate.m in Sources */,
1867E5A92BB2706F008FC5FB /* HidManager.m in Sources */,
SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
- USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"";
+ USER_HEADER_SEARCH_PATHS = (
+ "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"",
+ "\"$(SRCROOT)/AiMouseSdk/TlwMouseSdk\"",
+ );
};
name = Debug;
};
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h";
SWIFT_VERSION = 5.0;
- USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"";
+ USER_HEADER_SEARCH_PATHS = (
+ "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"",
+ "\"$(SRCROOT)/AiMouseSdk/TlwMouseSdk\"",
+ );
};
name = Release;
};
</Locations>
</BreakpointContent>
</BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+ <BreakpointContent
+ uuid = "BE1E231B-12D2-47AA-A5E0-AAAA81898EEC"
+ shouldBeEnabled = "No"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ filePath = "MouseSdk/TlwVC.m"
+ startingColumnNumber = "9223372036854775807"
+ endingColumnNumber = "9223372036854775807"
+ startingLineNumber = "23"
+ endingLineNumber = "23"
+ landmarkName = "TlwVC"
+ landmarkType = "3">
+ </BreakpointContent>
+ </BreakpointProxy>
</Breakpoints>
</Bucket>
#import <CoreBluetooth/CoreBluetooth.h>
#import "AiMouseSdk.h"
-#define kDidConnectBleNotification @"kDidConnectBleNotification"
-#define kDidDisconnectBleNotification @"kDidDisconnectBleNotification"
+#define kTlwDidConnectBleNotification @"kTlwDidConnectBleNotification"
+#define kTlwDidDisconnectBleNotification @"kTlwDidDisconnectBleNotification"
@property (nonatomic,strong)void (^didConnectDevice)(CBPeripheral *peripheral);/// 连接成功
@property (nonatomic,strong)void (^didDisConnectDevice)(CBPeripheral *peripheral);/// 断开连接
@property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调
-/// 单利
-+(BleManager *)shared;
/// 扫描
- (void)startScanDevice;
@end
@implementation BleManager
-+(BleManager *)shared{
- static BleManager *manager = nil;
- static dispatch_once_t oncePredicate;
- dispatch_once(&oncePredicate, ^{
- manager = [[BleManager alloc]init];
- });
- return manager;
-}
-
- (instancetype)init{
self = [super init];
if (self) {
peripheral.delegate = self;
[peripheral discoverServices:nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil];
}
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
[self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]];
- [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil];
if(self.didDisConnectDevice){
self.didDisConnectDevice(peripheral);
}
// 连接状态改变
- (void)aiMouseConnectStateDidChange:(BOOL)connected{
if (connected) {
- [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil];
}
else{
- [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil];
}
}
#pragma mark - public
#import <Foundation/Foundation.h>
#import "AiMouseSdk.h"
+#import "HidManager.h"
+#import "BleManager.h"
-NS_ASSUME_NONNULL_BEGIN
@interface DeviceManger : NSObject
+@property (nonatomic,strong) BleManager *ble;
+@property (nonatomic,strong) HidManager *hid;
+
@property (nonatomic, readonly) AiMouseConnectType connectType;//连接类型
@property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调
@property (nonatomic,strong)void (^connectStateCallBack)(AiMouseConnectType connectType,BOOL isConnect);///是否连接成功
+(DeviceManger *)shared;
+//开始,使用该sdk需要调用该方法
+- (void)start;
+
+//停止,停止使用sdk需要调用该方法
+- (void)stop;
+
/// 切换连接类型
/// - Parameter type: 连接类型
- (void)switchConnect:(AiMouseConnectType)type;
@end
-NS_ASSUME_NONNULL_END
//
#import "DeviceManger.h"
-#import "HidManager.h"
-#import "BleManager.h"
+
@interface DeviceManger()
-@property (nonatomic,strong) BleManager *ble;
-@property (nonatomic,strong) HidManager *hid;
@end
@implementation DeviceManger
return manager;
}
+//开始,使用该sdk需要调用该方法
+- (void)start{
+ [self setupBle];
+ [self setupHid];
+}
+
+//停止,停止使用sdk需要调用该方法
+- (void)stop{
+ self.ble = nil;
+ self.hid = nil;
+}
+
- (instancetype)init{
self = [super init];
if (self) {
[[AiMouse shared] setLogLevel:AiMouseLogLevel_info];
- [self setupBle];
- [self setupHid];
}
return self;
}
}
- (void)setupHid{
- self.hid = [HidManager shared];
+ self.hid = [[HidManager alloc] init];
__weak typeof(self) ws = self;
self.hid.authCallBack = ^(BOOL isSuccess) {
if(ws.authCallBack)ws.authCallBack(isSuccess);
}
- (void)setupBle{
- self.ble = [BleManager shared];
+ self.ble = [[BleManager alloc] init];
__weak typeof(self) ws = self;
self.ble.authCallBack = ^(BOOL isSuccess) {
if(ws.authCallBack)ws.authCallBack(isSuccess);
#pragma mark - notication
- (void)setupBleNotification{
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kDidConnectBleNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kDidDisconnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kTlwDidConnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kTlwDidDisconnectBleNotification object:nil];
}
- (void)bleDidConnect:(NSNotification *)notification{
@property (nonatomic,strong)void (^connectStateCallBack)(BOOL isConnnect);/// 设备是否连上
@property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调
-+(HidManager *)shared;
-
/// 获取设备信息
- (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo *deviceInfo,NSError *error))completation;
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
#import "Config.h"
+#import "DeviceManger.h"
//供应商id
const long vendorID = 0x6675;
@end
@implementation HidManager
-+(HidManager *)shared{
- static HidManager *manager = nil;
- static dispatch_once_t oncePredicate;
- dispatch_once(&oncePredicate, ^{
- manager = [[HidManager alloc]init];
- });
- return manager;
-}
- (instancetype)init{
self = [super init];
IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L);
if (ret != kIOReturnSuccess) {
- NSLog(@"打开设备失败!");
+ NSLog(@"打开hid失败!");
}else{
- NSLog(@"打开设备成功!");
+ NSLog(@"打开hid成功!");
}
//设置匹配设备
NSMutableDictionary* dict= [NSMutableDictionary dictionary];
#pragma mark - Hid CallBack
/// 匹配到设备回调
static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
- [[HidManager shared] showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]];
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]];
uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey));
uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey));
if(PrimaryUsagePage == 1 && PrimaryUsage == 0){
//设备信息
- [HidManager shared].deviceInfoCha = inIOHIDDeviceRef;
- [[HidManager shared] connectDevice];
+ [DeviceManger shared].hid.deviceInfoCha = inIOHIDDeviceRef;
+ [[DeviceManger shared].hid connectDevice];
//注册消息回调
unsigned char InputBuffer[128];
IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL);
}
else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){
//ai
- [HidManager shared].aiCha = inIOHIDDeviceRef;
+ [DeviceManger shared].hid.aiCha = inIOHIDDeviceRef;
//注册消息回调
unsigned char InputBuffer[128];
IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL);
/// 设备移除回调
static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
- [[HidManager shared] showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]];
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]];
uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey));
uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey));
if(PrimaryUsagePage == 1 && PrimaryUsage == 0){
- [HidManager shared].deviceInfoCha = nil;
- [HidManager shared].aiCha = nil;
+ [DeviceManger shared].hid.deviceInfoCha = nil;
+ [DeviceManger shared].hid.aiCha = nil;
[[AiMouse shared] didDisconnect];
}
}
static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){
-// [[HidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]];
+// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]];
NSInteger len = get_data_len(report, reportLength);
NSData *data = [[NSData alloc] initWithBytes:report length:len];
-// [[HidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]];
+// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]];
uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsagePageKey));
uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsageKey));
// 连接状态改变
- (void)aiMouseConnectStateDidChange:(BOOL)connected{
- [HidManager shared].connectStateCallBack(connected);
+ [DeviceManger shared].hid.connectStateCallBack(connected);
}
#pragma mark - public
-(void)connectDevice{
if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) {
Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue);
if (!result) {
- [[HidManager shared] showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]];
+ [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]];
}
}
}
--- /dev/null
+//
+// FrkVC.h
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FrkVC : NSViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// FrkVC.m
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import "FrkVC.h"
+#import "ScanVC.h"
+#import "DeviceManger.h"
+
+@interface FrkVC ()
+@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态
+@property(nonatomic,strong)NSTextField *connectLabel;///连接状态
+@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态
+@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息
+@end
+
+@implementation FrkVC
+
+-(instancetype)init{
+ if (self = [super init]) {
+// [[DeviceManger shared] start];
+ }
+ return self;
+}
+
+-(void)dealloc{
+ [[DeviceManger shared] stop];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Do any additional setup after loading the view.
+ [[DeviceManger shared] start];
+ [self setupUI];
+ [self setupDeviceManger];
+}
+
+- (void)loadView{
+ self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
+}
+
+- (void)setupUI{
+ CALayer *viewLayer = [CALayer layer];
+ NSView *backgroundView= [[NSView alloc]init];
+ [backgroundView setWantsLayer:YES];
+ [backgroundView setLayer:viewLayer];
+ backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor;
+ [backgroundView setNeedsDisplay:YES];
+ [self.view addSubview:backgroundView];
+
+ self.title = @"AI SDK";
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"蓝牙扫描";
+ [btn setTarget:self];
+ [btn setAction:@selector(scanAction)];
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"获取设备信息";
+ [btn setTarget:self];
+ [btn setAction:@selector(geviceInfoAction)];
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"连接模式:--";
+ label.backgroundColor = NSColor.clearColor;
+ self.typeLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"连接状态:未连接";
+ label.backgroundColor = NSColor.clearColor;
+ self.connectLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"鉴权状态:unknow";
+ label.backgroundColor = NSColor.clearColor;
+ self.authLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"";
+ label.backgroundColor = NSColor.clearColor;
+ self.deviceInfoLabel = label;
+ [self.view addSubview:label];
+ }
+}
+
+#pragma mark - action
+- (void)scanAction{
+ ScanVC *vc = [[ScanVC alloc] initWithNibName:nil bundle:nil];
+ [self presentViewControllerAsModalWindow:vc];
+}
+
+/// 获取设备信息
+- (void)geviceInfoAction{
+ [[DeviceManger shared] getDeviceInfo:^(AiMouseDeviceInfo *deviceInfo, NSError *error) {
+ if (deviceInfo) {
+ NSString *info = deviceInfo.description;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.deviceInfoLabel.stringValue = info;
+ });
+ }
+ else{
+ NSString *info = [NSString stringWithFormat:@"error == %@",error.description];
+ NSLog(@"%@",info);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.deviceInfoLabel.stringValue = info;
+ });
+ }
+ }];
+}
+- (void)showAuthResult:(BOOL)isSuccess{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.authLabel.stringValue = isSuccess?@"鉴权状态:鉴权成功":@"鉴权状态:鉴权失败";
+ if(isSuccess){
+ [self geviceInfoAction];
+ }
+ });
+}
+- (void)connectStateDidChange:(AiMouseConnectType)type connectState:(BOOL)isConnected{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(isConnected){
+ self.typeLabel.stringValue = type == AiMouseConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid";
+ self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接";
+ [self geviceInfoAction];
+ }
+ else{
+ self.typeLabel.stringValue = @"连接模式:--";
+ self.connectLabel.stringValue = @"连接状态:未连接";
+ }
+ });
+}
+
+- (void)setupDeviceManger{
+ DeviceManger *manager = [DeviceManger shared];
+ __weak typeof(self) ws = self;
+ manager.authCallBack = ^(BOOL isSuccess) {
+ [ws showAuthResult:isSuccess];
+ };
+ manager.connectStateCallBack = ^(AiMouseConnectType type,BOOL isConnected) {
+ [ws connectStateDidChange:type connectState:isConnected];
+ };
+}
+
+@end
--- /dev/null
+//
+// MouseSdkManager.h
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger,MouseSdkType) {
+ TlwMouseLogLevel_frk = 1,//富瑞坤
+ TlwMouseLogLevel_tlw = 2,//泰凌微
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+
+@interface MouseSdkManager : NSObject
+@property (nonatomic,assign)MouseSdkType type;//默认是富瑞坤
+
++(MouseSdkManager *)shared;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// MouseSdkManager.m
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import "MouseSdkManager.h"
+
+@implementation MouseSdkManager
++(MouseSdkManager *)shared{
+ static MouseSdkManager *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[MouseSdkManager alloc]init];
+ });
+ return manager;
+}
+
+- (instancetype)init{
+ self = [super init];
+ if (self) {
+ self.type = TlwMouseLogLevel_frk;
+ }
+ return self;
+}
+@end
#import "ScanVC.h"
#import "ScanCell.h"
-#import "BleManager.h"
+#import "DeviceManger.h"
@interface ScanVC ()<NSTableViewDelegate,NSTableViewDataSource>
@property (nonatomic,strong) NSTableView *tableView;
@property (nonatomic,strong) NSMutableArray<CBPeripheral *> *scanArray;
}
- (void)setupBleManger{
- BleManager *manager = [BleManager shared];
+ BleManager *manager = [DeviceManger shared].ble;
__weak typeof(self) weakSelf= self;
__weak typeof(BleManager) *weakManager = manager;
manager.discoverDevice = ^(CBPeripheral *peripheral){
}
- (void)tapDevice:(CBPeripheral *)peripheral{
- [[BleManager shared] connectPeripheral:peripheral];
+ [[DeviceManger shared].ble connectPeripheral:peripheral];
}
- (void)connectSuccess{
- [[BleManager shared] stopScanDevice];
+ [[DeviceManger shared].ble stopScanDevice];
dispatch_async(dispatch_get_main_queue(), ^{
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"确定"];
}
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
-// NSString *columnID = tableColumn.identifier;
-// NSString *strIdt = @"NSTableCellView";
-// NSTableCellView *cell = [tableView makeViewWithIdentifier:strIdt owner:self];
-// if (!cell) {
-// cell = [[NSTableCellView alloc]init];
-// cell.identifier = strIdt;
-// }
-// cell.wantsLayer = YES;
-// cell.textField.stringValue = [NSString stringWithFormat:@"cell %ld",(long)row];
-//
-// return cell;
ScanCell *cell = [[ScanCell alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
cell.peripheral = self.scanArray[row];
__weak typeof(self) weakSelf = self;
--- /dev/null
+//
+// TlwBleManager.h
+// AliIotSdkDemo
+//
+// Created by incar on 2024/1/30.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreBluetooth/CoreBluetooth.h>
+#import "TlwMouseSdk.h"
+
+#define kDidConnectBleNotification @"kDidConnectBleNotification"
+#define kDidDisconnectBleNotification @"kDidDisconnectBleNotification"
+
+
+
+@interface TlwBleManager : NSObject
+@property (nonatomic,strong)void (^discoverDevice)(CBPeripheral *peripheral) ;/// 扫描到设备
+@property (nonatomic,strong)void (^didConnectDevice)(CBPeripheral *peripheral);/// 连接成功
+@property (nonatomic,strong)void (^didDisConnectDevice)(CBPeripheral *peripheral);/// 断开连接
+/// 单利
++(TlwBleManager *)shared;
+
+/// 扫描
+- (void)startScanDevice;
+
+/// 停止扫描
+- (void)stopScanDevice;
+
+/// 连接设备
+/// - Parameter peripheral: 蓝牙设备
+- (void)connectPeripheral:(CBPeripheral *)peripheral;
+
+/// 获取设备信息
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished;
+
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+@end
+
--- /dev/null
+//
+// TlwBleManager.m
+// AliIotSdkDemo
+//
+// Created by incar on 2024/1/30.
+//
+
+#import "TlwBleManager.h"
+#import "TlwMouseSdk.h"
+#import "Config.h"
+
+// 服务id
+#define kServiceUUID @"FFF0"
+//ai特征
+#define kAiUUID @"00010203-0405-0607-0809-0A0B0C0D2B18"
+//写设备信息特征
+#define kSetDeviceInfoUUID @"FFF1"
+//通知设备信息特征
+#define kNotifyDeviceInfoUUID @"FFF2"
+
+@interface TlwBleManager ()<CBCentralManagerDelegate,CBPeripheralDelegate,TlwMouseDelegate>
+@property(nonatomic, strong) CBCentralManager *centralManager;///蓝牙管理
+@property(nonatomic, strong) CBPeripheral *connectPeripheral;//连接的设备
+@property(nonatomic, strong) CBCharacteristic *deviceInfoCha;//设备信息特征
+@property (nonatomic,strong) NSMutableData *recordData;//录音的数据
+@end
+
+@implementation TlwBleManager
++(TlwBleManager *)shared{
+ static TlwBleManager *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[TlwBleManager alloc]init];
+ });
+ return manager;
+}
+
+- (instancetype)init{
+ self = [super init];
+ if (self) {
+ self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
+ }
+ return self;
+}
+
+#pragma mark - CBCentralManagerDelegate
+- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
+ [self showLog:[NSString stringWithFormat:@"蓝牙状态 = %@",@(central.state)]];
+ switch (central.state) {
+ case CBManagerStateUnknown:
+
+ break;
+ case CBManagerStateResetting:
+
+ break;
+ case CBManagerStateUnsupported:
+
+ break;
+ case CBManagerStateUnauthorized:
+
+ break;
+ case CBManagerStatePoweredOff:
+
+ break;
+ case CBManagerStatePoweredOn:
+ [self autoConnectDevice];
+ break;
+ }
+}
+
+- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
+// [self showLog:[NSString stringWithFormat:@"扫描到设备 = %@, data = %@",peripheral,advertisementData]];
+ if(self.discoverDevice){
+ self.discoverDevice(peripheral);
+ }
+}
+
+- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
+ [self showLog:[NSString stringWithFormat:@"连接成功 name = %@",peripheral.name]];
+ if(self.didConnectDevice){
+ self.didConnectDevice(peripheral);
+ }
+ peripheral.delegate = self;
+ [peripheral discoverServices:nil];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil];
+}
+-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
+ [self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil];
+ if(self.didDisConnectDevice){
+ self.didDisConnectDevice(peripheral);
+ }
+// [self aiMouseConnectStateDidChange:false];
+ [[TlwMouse shared] didDisconnect];
+}
+- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
+ [self showLog:[NSString stringWithFormat:@"连接失败。。。%@",error.description]];
+}
+#pragma mark - CBPeripheralDelegate
+//发现服务
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
+ for (CBService *service in peripheral.services){
+ [peripheral discoverCharacteristics:nil forService:service];
+ }
+}
+// 发现特征
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
+ for (CBCharacteristic *characteristic in service.characteristics){
+ if ([characteristic.UUID.UUIDString isEqualToString:kAiUUID]) {
+ //语音特征
+ [peripheral setNotifyValue:YES forCharacteristic:characteristic];
+ }
+ else if ([characteristic.UUID.UUIDString isEqualToString:kNotifyDeviceInfoUUID]) {
+ //通知设备信息特征
+ [peripheral setNotifyValue:YES forCharacteristic:characteristic];
+ }
+ else if ([characteristic.UUID.UUIDString isEqualToString:kSetDeviceInfoUUID]) {
+ //设备信息特征
+ self.deviceInfoCha = characteristic;
+ //切换代理
+ [TlwMouse shared].delegate = self;
+ //切换当前连接类型
+ [[TlwMouse shared] switchConnect:TlwConnectType_bluetooth];
+ }
+ }
+}
+//收到蓝牙数据
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
+ NSData *data = characteristic.value;
+ if([characteristic.UUID.UUIDString isEqualToString:kAiUUID]){
+ //AI数据
+ [[TlwMouse shared] sendAiDataToSdk:data];
+ }
+ else if([characteristic.UUID.UUIDString isEqualToString:kNotifyDeviceInfoUUID]){
+ //设备信息数据
+ [[TlwMouse shared] sendDeviceDataToSdk:data];
+ }
+}
+
+#pragma mark - AiDelegate
+
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备
+/// - Parameter data: 数据
+-(void)tlwMouseSendDeviceInfoData:(NSData *)data{
+ [self.connectPeripheral writeValue:data forCharacteristic:self.deviceInfoCha type:CBCharacteristicWriteWithResponse];
+}
+
+/// 鼠标按键指令回调
+/// - Parameter data: 数据。
+/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音)
+/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音)
+/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音)
+/// 其他未定义
+- (void)tlwMouseSendCmdData:(NSData *)data{
+ uint8 cmd;
+ [data getBytes:&cmd length:1];
+
+ if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){
+ [self showLog:@"开始录音"];
+ self.recordData = [[NSMutableData alloc] init];
+ }
+ else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){
+ [self showLog:@"结束录音"];
+ [self finishRecored:self.recordData];
+ }
+}
+
+/// 录音回调
+/// - Parameter data: pcm格式语音数据
+- (void)tlwMouseSendRecordData:(NSData *)data{
+ [self.recordData appendData:data];
+}
+/// 输出日志
+- (void)aiMouseAddLog:(NSString *)log {
+// [self showLog:log];
+}
+
+// 连接状态改变
+- (void)aiMouseConnectStateDidChange:(BOOL)connected{
+ if (connected) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil];
+ }
+ else{
+ [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil];
+ }
+}
+#pragma mark - public
+/// 连接设备
+- (void)startScanDevice{
+ [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO]}];
+}
+
+/// 扫描设备
+- (void)stopScanDevice{
+ [self.centralManager stopScan];
+}
+
+/// 连接设备
+- (void)connectPeripheral:(CBPeripheral *)peripheral{
+ self.connectPeripheral = peripheral;
+ [self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : [NSNumber numberWithBool:YES]}];
+}
+
+/// 获取设备信息
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo * _Nonnull, NSError * _Nonnull))finished{
+ [[TlwMouse shared] getDeviceInfo:finished];
+}
+
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] setDPI:value completation:finished];
+}
+
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] openRecord:finished];
+}
+
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] closeRecord:finished];
+}
+#pragma mark - other
+//自动连接,获取已配对的蓝牙,取第一个连接
+- (void)autoConnectDevice{
+ //如果是已连接或者正在连接,则不再重连
+ if (self.connectPeripheral && (self.connectPeripheral.state == CBPeripheralStateConnected || self.connectPeripheral.state == CBPeripheralStateConnecting)) {
+ return;
+ }
+ NSArray<CBPeripheral *> *devices = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]];
+ if (devices.count > 0) {
+ CBPeripheral *peripheral = devices[0];
+ [self showLog:[NSString stringWithFormat:@"匹配过的设备%@",peripheral.name]];
+ [self connectPeripheral:peripheral];
+ }
+ // 10s自动重连
+ [self performSelector:@selector(autoConnectDevice) withObject:nil afterDelay:10];
+}
+- (void)showLog:(NSString *)log{
+ NSLog(@"%@",log);
+}
+
+//将录音数据保存
+- (void)finishRecored:(NSData *)data{
+ NSString *doc = NSTemporaryDirectory();
+ NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];
+ NSError *err;
+ [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];
+ if (err) {
+ [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];
+ }
+ else{
+ [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];
+ }
+}
+
+- (NSString *)dataToHexString:(NSData *)data {
+ NSUInteger length = data.length;
+ NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+ const unsigned char *byte = data.bytes;
+ for (int i = 0; i < length; i++, byte++) {
+ [result appendFormat:@"%02X", *byte];
+ }
+ return result;
+}
+@end
--- /dev/null
+//
+// TlwDeviceManger.h
+// MouseSdk
+//
+// Created by incar on 2024/3/30.
+//
+
+#import <Foundation/Foundation.h>
+#import "TlwMouseSdk.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwDeviceManger : NSObject
+@property (nonatomic, readonly) TlwConnectType connectType;//连接类型
+@property (nonatomic,strong)void (^connectStateCallBack)(TlwConnectType connectType,BOOL isConnect);///是否连接成功
+
++(TlwDeviceManger *)shared;
+
+/// 切换连接类型
+/// - Parameter type: 连接类型
+- (void)switchConnect:(TlwConnectType)type;
+
+/// 获取设备信息
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished;
+
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwDeviceManger.m
+// MouseSdk
+//
+// Created by incar on 2024/3/30.
+//
+
+#import "TlwDeviceManger.h"
+#import "TlwHidManager.h"
+#import "TlwBleManager.h"
+
+@interface TlwDeviceManger()
+
+@property (nonatomic,strong) TlwBleManager *ble;
+@property (nonatomic,strong) TlwHidManager *hid;
+@end
+
+@implementation TlwDeviceManger
++(TlwDeviceManger *)shared{
+ static TlwDeviceManger *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[TlwDeviceManger alloc]init];
+ });
+ return manager;
+}
+
+- (instancetype)init{
+ self = [super init];
+ if (self) {
+ [[TlwMouse shared] setLogLevel:TlwMouseLogLevel_info];
+ [self setupBle];
+ [self setupHid];
+ }
+ return self;
+}
+
+-(TlwConnectType)connectType{
+ return [TlwMouse shared].connectType;
+}
+
+- (void)switchConnect:(TlwConnectType)type{
+ [[TlwMouse shared] switchConnect:type];
+
+ if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) {
+ [self setupBle];
+ }
+ else{
+ [self setupHid];
+ }
+}
+
+- (void)setupHid{
+ self.hid = [TlwHidManager shared];
+ __weak typeof(self) ws = self;
+ self.hid.connectStateCallBack = ^(BOOL isConnnect) {
+ if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_hid,isConnnect);
+ };
+}
+
+- (void)setupBle{
+ self.ble = [TlwBleManager shared];
+ __weak typeof(self) ws = self;
+ self.ble.didConnectDevice = ^(CBPeripheral *peripheral) {
+ if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_bluetooth,true);
+ };
+ self.ble.didDisConnectDevice = ^(CBPeripheral *peripheral) {
+ if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_bluetooth,false);
+ };
+ [self setupBleNotification];
+}
+
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished{
+ if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) {
+ [self.ble getDeviceInfo:finished];
+ }
+ else{
+ [self.hid getDeviceInfo:finished];
+ }
+}
+
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) {
+ [self.ble setDPI:value completation:finished];
+ }
+ else{
+ [self.hid setDPI:value completation:finished];
+ }
+}
+
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) {
+ [self.ble openRecord:finished];
+ }
+ else{
+ [self.hid openRecord:finished];
+ }
+}
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) {
+ [self.ble closeRecord:finished];
+ }
+ else{
+ [self.hid closeRecord:finished];
+ }
+}
+#pragma mark - notication
+- (void)setupBleNotification{
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kDidConnectBleNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kDidDisconnectBleNotification object:nil];
+}
+
+- (void)bleDidConnect:(NSNotification *)notification{
+ if(self.connectStateCallBack)self.connectStateCallBack(TlwConnectType_bluetooth,true);
+}
+
+- (void)bleDidDisconnect:(NSNotification *)notification{
+ if(self.connectStateCallBack)self.connectStateCallBack(TlwConnectType_bluetooth,false);
+ [[TlwMouse shared] switchConnect:TlwConnectType_hid];
+}
+
+@end
--- /dev/null
+//
+// TlwHidManager.h
+// MouseSdk
+//
+// Created by incar on 2024/3/26.
+//
+
+#import <Foundation/Foundation.h>
+#import "TlwMouseSdk.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwHidManager : NSObject
+@property (nonatomic,strong)void (^connectStateCallBack)(BOOL isConnnect);/// 设备是否连上
+
++(TlwHidManager *)shared;
+
+- (void)showLog:(NSString *)log;
+
+/// 获取设备信息
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished;
+
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// HidManager.m
+// MouseSdk
+//
+// Created by incar on 2024/3/26.
+//
+
+#import "TlwHidManager.h"
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#import "Config.h"
+
+
+
+//供应商id
+//const long vendorID = 0x6675;
+//const long productID = 0x8601;
+
+@interface TlwHidManager ()<TlwMouseDelegate>
+
+@property(nonatomic, assign) IOHIDManagerRef hidManager;///hid管理
+@property(nonatomic, assign) IOHIDDeviceRef deviceInfoCha;//设备信息通道
+@property (nonatomic,strong) NSMutableData *recordData;//录音的数据
+@end
+
+@implementation TlwHidManager
++(TlwHidManager *)shared{
+ static TlwHidManager *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[TlwHidManager alloc]init];
+ });
+ return manager;
+}
+
+- (instancetype)init{
+ self = [super init];
+ if (self) {
+ [self initHid];
+ [self open];
+ }
+ return self;
+}
+
+- (void)initHid{
+
+}
+
+- (void)open{
+ self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L);
+ if (ret != kIOReturnSuccess) {
+ NSLog(@"打开设备失败!");
+ }else{
+ NSLog(@"打开设备成功!");
+ }
+ //设置匹配设备
+ //供应商id
+ const long vendorID = 0x10D6;
+ //产品id
+ const long productID = 0xB008;
+ NSMutableDictionary* dict= [NSMutableDictionary dictionary];
+ [dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];
+ [dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];
+ IOHIDManagerSetDeviceMatching(self.hidManager, (__bridge CFMutableDictionaryRef)dict);
+ //设置匹配到设备回调
+ IOHIDManagerRegisterDeviceMatchingCallback(self.hidManager, &hidAppearDevice, NULL);
+ //设置移除设备回调
+ IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, &hidDisAppearDevice, NULL);
+}
+
+- (void)close{
+ IOReturn ret = IOHIDManagerClose(self.hidManager, 0);
+ if (ret != kIOReturnSuccess) {
+ NSLog(@"关闭设备失败!");
+ }else{
+ NSLog(@"关闭设备成功!");
+ }
+}
+
+#pragma mark - Hid CallBack
+/// 匹配到设备回调
+static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
+ [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]];
+ [TlwHidManager shared].deviceInfoCha = inIOHIDDeviceRef;
+ [[TlwHidManager shared] connectDevice];
+ //注册消息回调
+ unsigned char InputBuffer[64];
+ IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL);
+ IOReturn result = IOHIDDeviceOpen(inIOHIDDeviceRef, kIOHIDOptionsTypeNone);
+ if (result != kIOReturnSuccess) {
+ NSLog([NSString stringWithFormat:@"打开设备信息失败! %d",result]);
+ }else{
+ NSLog(@"打开设备信息成功!");
+ }
+ CFRetain(inIOHIDDeviceRef);
+ IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+}
+
+/// 设备移除回调
+static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
+ [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]];
+ uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey));
+ uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey));
+
+ if(PrimaryUsagePage == 1 && PrimaryUsage == 0){
+ [TlwHidManager shared].deviceInfoCha = nil;
+ [[TlwMouse shared] didDisconnect];
+ }
+}
+
+static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){
+ if (report[0] == 0x01 || report[0] == 0x02) {
+ //鼠标键盘活动事件
+ return;
+ }
+ [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[[TlwHidManager shared] dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]];
+
+ NSInteger len = get_data_len(report, reportLength);
+ NSData *data = [[NSData alloc] initWithBytes:report length:len];
+ [[TlwMouse shared] sendDeviceDataToSdk:data];
+}
+
+#pragma mark - AiDelegate
+/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备
+/// - Parameter data: 数据
+-(void)tlwMouseSendDeviceInfoData:(NSData *)data{
+ [self sendData:data toDevice:self.deviceInfoCha];
+}
+
+/// 鼠标按键指令回调
+/// - Parameter data: 数据。
+/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音)
+/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音)
+/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音)
+/// 其他未定义
+- (void)tlwMouseSendCmdData:(NSData *)data{
+ uint8 cmd;
+ [data getBytes:&cmd length:1];
+
+ if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){
+ [self showLog:@"开始录音"];
+ self.recordData = [[NSMutableData alloc] init];
+ }
+ else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){
+ [self showLog:@"结束录音"];
+ [self finishRecored:self.recordData];
+ }
+}
+/// 录音回调
+/// - Parameter data: pcm格式语音数据
+- (void)tlwMouseSendRecordData:(NSData *)data{
+ [self.recordData appendData:data];
+}
+/// 输出日志
+- (void)aiMouseAddLog:(NSString *)log {
+// [self showLog:log];
+}
+
+// 连接状态改变
+- (void)aiMouseConnectStateDidChange:(BOOL)connected{
+ [TlwHidManager shared].connectStateCallBack(connected);
+}
+
+#pragma mark - public
+/// 获取设备信息
+- (void)getDeviceInfo:(void (^)(TlwDeviceInfo * _Nonnull, NSError * _Nonnull))completation{
+ [[TlwMouse shared] getDeviceInfo:completation];
+}
+/// setDPI
+- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] setDPI:value completation:finished];
+}
+/// 打开录音
+- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] openRecord:finished];
+}
+/// 停止录音
+- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{
+ [[TlwMouse shared] closeRecord:finished];
+}
+
+#pragma mark - other
+-(void)connectDevice{
+ //设置代理
+ [TlwMouse shared].delegate = self;
+ //切换当前连接类型
+ [[TlwMouse shared] switchConnect:TlwConnectType_hid];
+}
+
+/// 发送数据给设备
+-(void)sendData:(NSData *)data toDevice:(IOHIDDeviceRef)device{
+ if(device == nil){
+ [self showLog:@"请先连上设备"];
+ return;
+ }
+
+ IOReturn ret = IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, 0, data.bytes, data.length);
+ if (ret != kIOReturnSuccess) {
+ [self showLog:[NSString stringWithFormat:@"数据发送失败! code = %d",ret]];
+ }else{
+ [self showLog:@"数据发送成功!"];
+ }
+}
+
+//将录音数据保存
+- (void)finishRecored:(NSData *)data{
+ NSString *doc = NSTemporaryDirectory();
+ NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc];
+ NSError *err;
+ [data writeToFile:fullPath options:NSDataWritingAtomic error:&err];
+ if (err) {
+ [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]];
+ }
+ else{
+ [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]];
+ }
+}
+- (void)showLog:(NSString *)log{
+ NSLog(@"%@",log);
+}
+
+
+/// 实际到数据长度
+static inline int get_data_len(uint8_t *buf, u_long len) {
+ if (len <= 0 ) return 0;
+ int url_len = 0;
+ while (buf[url_len] != 0x00) url_len++;
+ return url_len;
+}
+
+/// 获取对应属性值
+static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey) {
+ uint32_t outValue = 0;
+ if (inIOHIDDeviceRef != NULL) {
+ CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey);
+ if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) {
+ Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue);
+ if (!result) {
+ [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]];
+ }
+ }
+ }
+ return outValue;
+}
+- (NSString *)dataToHexString:(NSData *)data {
+ NSUInteger length = data.length;
+ NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
+ const unsigned char *byte = data.bytes;
+ for (int i = 0; i < length; i++, byte++) {
+ [result appendFormat:@"%02X", *byte];
+ }
+ return result;
+}
+@end
--- /dev/null
+//
+// TlwScanVC.h
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwScanVC : NSViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwScanVC.m
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import "TlwScanVC.h"
+
+#import "ScanCell.h"
+#import "TlwDeviceManger.h"
+@interface TlwScanVC ()<NSTableViewDelegate,NSTableViewDataSource>
+@property (nonatomic,strong) NSTableView *tableView;
+@property (nonatomic,strong) NSMutableArray<CBPeripheral *> *scanArray;
+@end
+
+@implementation TlwScanVC
+-(NSMutableArray<CBPeripheral *> *)scanArray{
+ if (_scanArray == nil) {
+ _scanArray = [[NSMutableArray alloc] init];
+ }
+ return _scanArray;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do view setup here.
+ [self setupUI];
+ [self setupBleManger];
+}
+- (void)loadView{
+ self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
+}
+
+- (void)setupUI{
+ CALayer *viewLayer = [CALayer layer];
+ NSView *backgroundView= [[NSView alloc]init];
+ [backgroundView setWantsLayer:YES];
+ [backgroundView setLayer:viewLayer];
+ backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor;
+ [backgroundView setNeedsDisplay:YES];
+ [self.view addSubview:backgroundView];
+
+ //实现tableview的滚动效果
+ NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:self.view.bounds];
+ scrollView.backgroundColor = [NSColor redColor];
+ [scrollView setDrawsBackground:NO];//不画背景(背景默认画成白色)
+ [scrollView setHasVerticalScroller:YES];//有垂直滚动条
+ //[_tableContainer setHasHorizontalScroller:YES]; //有水平滚动条
+ scrollView.autohidesScrollers = YES;//自动隐藏滚动条(滚动的时候出现)
+ [self.view addSubview:scrollView];
+
+ self.tableView = [[NSTableView alloc] initWithFrame:scrollView.bounds];
+ [scrollView setDocumentView:self.tableView];
+
+ NSTableColumn *column1 = [[NSTableColumn alloc] initWithIdentifier:@"columnFrist"];
+ column1.title = @"设备名称";
+ [column1 setWidth:self.tableView.bounds.size.width];
+ [self.tableView addTableColumn:column1];
+
+// self.tableView.focusRingType = NSFocusRingTypeNone;//tableview获得焦点时的风格
+// self.tableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;//行高亮的风格
+// self.tableView.backgroundColor = [NSColor greenColor];
+// self.tableView.usesAlternatingRowBackgroundColors = YES; //背景颜色的交替,一行白色,一行灰色。设置后,原来设置的 backgroundColor 就无效了。
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ self.tableView.gridStyleMask = NSTableViewSolidHorizontalGridLineMask;
+}
+
+- (void)setupBleManger{
+// TlwBleManager *manager = [TlwDeviceManger shared].ble;
+// __weak typeof(self) weakSelf= self;
+// manager.discoverDevice = ^(CBPeripheral *peripheral){
+// if ([weakSelf.scanArray containsObject:peripheral]){
+// return;
+// }
+// if(peripheral.name.length == 0){
+// return;
+// }
+// [weakSelf.scanArray addObject:peripheral];
+// [weakSelf.tableView reloadData];
+// };
+// manager.didConnectDevice = ^(CBPeripheral * _Nonnull peripheral) {
+// [weakSelf connectSuccess];
+// };
+// [manager startScanDevice];
+}
+
+- (void)tapDevice:(CBPeripheral *)peripheral{
+// [[TlwDeviceManger shared].ble connectPeripheral:peripheral];
+}
+
+- (void)connectSuccess{
+// [[TlwDeviceManger shared].ble stopScanDevice];
+// dispatch_async(dispatch_get_main_queue(), ^{
+// NSAlert *alert = [NSAlert new];
+// [alert addButtonWithTitle:@"确定"];
+// [alert setMessageText:@"连接成功"];
+// [alert setAlertStyle:NSAlertStyleInformational];
+// [alert beginSheetModalForWindow:[self.view window] completionHandler:^(NSModalResponse returnCode) {
+// [self dismissViewController:self];
+// }];
+// });
+}
+#pragma mark - NSTableViewDelegate,NSTableViewDataSource
+-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
+ return self.scanArray.count;
+}
+
+-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
+ ScanCell *cell = [[ScanCell alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
+ cell.peripheral = self.scanArray[row];
+ __weak typeof(self) weakSelf = self;
+ cell.connecBtnActionCallBack = ^(CBPeripheral * _Nonnull peripheral) {
+ [weakSelf tapDevice:peripheral];
+ };
+ return cell;
+}
+-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{
+ return 44;
+}
+
+@end
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="TlwScanVC">
+ <connections>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ </customView>
+ </objects>
+</document>
--- /dev/null
+//
+// TlwVC.h
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TlwVC : NSViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// TlwVC.m
+// MouseSdk
+//
+// Created by incar on 2024/8/21.
+//
+
+#import "TlwVC.h"
+#import "TlwScanVC.h"
+#import "TlwDeviceManger.h"
+
+@interface TlwVC ()
+@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态
+@property(nonatomic,strong)NSTextField *connectLabel;///连接状态
+@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态
+@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息
+@end
+
+@implementation TlwVC
+
+//-(instancetype)init{
+// if (self = [super init]) {
+//
+// }
+// return self;
+//}
+
+-(void)dealloc{
+// [[TlwDeviceManger shared] stop];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Do any additional setup after loading the view.
+
+ [self setupUI];
+ [self setupTlwDeviceManger];
+}
+
+- (void)loadView{
+ self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
+}
+
+- (void)setupUI{
+ CALayer *viewLayer = [CALayer layer];
+ NSView *backgroundView= [[NSView alloc]init];
+ [backgroundView setWantsLayer:YES];
+ [backgroundView setLayer:viewLayer];
+ backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor;
+ [backgroundView setNeedsDisplay:YES];
+ [self.view addSubview:backgroundView];
+ self.title = @"AI SDK";
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"蓝牙扫描";
+ [btn setTarget:self];
+ [btn setAction:@selector(scanAction)];
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"选择指令";
+ [btn setTarget:self];
+ [btn setAction:@selector(showPopupMenu:)];
+
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"连接模式:--";
+ label.backgroundColor = NSColor.clearColor;
+ self.typeLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"连接状态:未连接";
+ label.backgroundColor = NSColor.clearColor;
+ self.connectLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"鉴权状态:unknow";
+ label.backgroundColor = NSColor.clearColor;
+ self.authLabel = label;
+ [self.view addSubview:label];
+ }
+ {
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)];
+ label.editable = NO;
+ label.bordered = NO;
+ label.stringValue = @"";
+ label.backgroundColor = NSColor.clearColor;
+ self.deviceInfoLabel = label;
+ [self.view addSubview:label];
+ }
+}
+
+#pragma mark - action
+- (void)scanAction{
+ TlwScanVC *vc = [[TlwScanVC alloc] initWithNibName:nil bundle:nil];
+ [self presentViewControllerAsModalWindow:vc];
+}
+
+- (void)connectStateDidChange:(TlwConnectType)type connectState:(BOOL)isConnected{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(isConnected){
+ self.typeLabel.stringValue = type == TlwConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid";
+ self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接";
+ }
+ else{
+ self.typeLabel.stringValue = @"连接模式:--";
+ self.connectLabel.stringValue = @"连接状态:未连接";
+ }
+ });
+}
+
+- (void)setupTlwDeviceManger{
+ TlwDeviceManger *manager = [TlwDeviceManger shared];
+// [manager start];
+ __weak typeof(self) ws = self;
+ manager.connectStateCallBack = ^(TlwConnectType type,BOOL isConnected) {
+ [ws connectStateDidChange:type connectState:isConnected];
+ };
+}
+
+
+- (void)showPopupMenu:(NSButton *)sender {
+ NSMenu *popupMenu = [[NSMenu alloc] initWithTitle:@"Popup Menu"];
+ NSMenuItem *item1 = [[NSMenuItem alloc] initWithTitle:@"语音开启" action:@selector(voiceStart:) keyEquivalent:@"1"];
+ NSMenuItem *item2 = [[NSMenuItem alloc] initWithTitle:@"语音停止" action:@selector(voiceStop:) keyEquivalent:@"2"];
+ NSMenuItem *item3 = [[NSMenuItem alloc] initWithTitle:@"设置DPI" action:@selector(setDPI:) keyEquivalent:@"3"];
+ NSMenuItem *item4 = [[NSMenuItem alloc] initWithTitle:@"鼠标状态查询" action:@selector(searchState:) keyEquivalent:@"3"];
+// NSMenuItem *item5 = [[NSMenuItem alloc] initWithTitle:@"向鼠标告知APP下载地址" action:@selector(setDownLoadURL:) keyEquivalent:@"3"];
+// NSMenuItem *item8 = [[NSMenuItem alloc] initWithTitle:@":读取鼠标序列号" action:@selector(getSN:) keyEquivalent:@"3"];
+// NSMenuItem *item9 = [[NSMenuItem alloc] initWithTitle:@":发送弹UR开关命令" action:@selector(sendUR:) keyEquivalent:@"3"];
+
+ [popupMenu addItem:item1];
+ [popupMenu addItem:item2];
+ [popupMenu addItem:item3];
+ [popupMenu addItem:item4];
+
+ NSPoint buttonLocation = [sender convertPoint:NSZeroPoint toView:self.view];
+ [popupMenu popUpMenuPositioningItem:nil atLocation:buttonLocation inView:self.view];
+}
+
+- (void)voiceStart:(NSMenuItem*)sender {
+ [[TlwDeviceManger shared] openRecord:^(BOOL isSuccess, NSError * _Nonnull error) {
+
+ }];
+}
+- (void)voiceStop:(NSMenuItem*)sender {
+ [[TlwDeviceManger shared] closeRecord:^(BOOL isSuccess, NSError * _Nonnull error) {
+
+ }];
+}
+- (void)setDPI:(NSMenuItem*)sender {
+ [[TlwDeviceManger shared] setDPI:2 completation:^(BOOL isSuccess, NSError * _Nonnull error) {
+
+ }];
+}
+- (void)searchState:(NSMenuItem*)sender {
+ [[TlwDeviceManger shared] getDeviceInfo:^(TlwDeviceInfo *deviceInfo, NSError *error) {
+// if (deviceInfo) {
+// NSString *info = deviceInfo.description;
+// dispatch_async(dispatch_get_main_queue(), ^{
+// self.deviceInfoLabel.stringValue = info;
+// });
+// }
+// else{
+// NSString *info = [NSString stringWithFormat:@"error == %@",error.description];
+// NSLog(@"%@",info);
+// dispatch_async(dispatch_get_main_queue(), ^{
+// self.deviceInfoLabel.stringValue = info;
+// });
+// }
+ }];
+}
+
+@end
//
#import "ViewController.h"
+#import "FrkVC.h"
+#import "TlwVC.h"
#import "ScanVC.h"
-#import "DeviceManger.h"
+#import "TlwHidManager.h"
@interface ViewController ()
-@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态
-@property(nonatomic,strong)NSTextField *connectLabel;///连接状态
-@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态
-@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息
@end
@implementation ViewController
// Do any additional setup after loading the view.
[self setupUI];
- [self setupDeviceManger];
}
- (void)setRepresentedObject:(id)representedObject {
{
NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)];
btn.bordered = true;
- btn.title = @"蓝牙扫描";
+ btn.title = @"富瑞坤";
[btn setTarget:self];
- [btn setAction:@selector(scanAction)];
+ [btn setAction:@selector(frkAction)];
btn.state = 1;
[self.view addSubview:btn];
}
{
- NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)];
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 200, 100, 45)];
btn.bordered = true;
- btn.title = @"获取设备信息";
+ btn.title = @"泰凌微";
[btn setTarget:self];
- [btn setAction:@selector(geviceInfoAction)];
+ [btn setAction:@selector(tlwAction)];
btn.state = 1;
[self.view addSubview:btn];
}
- {
- NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)];
- label.editable = NO;
- label.bordered = NO;
- label.stringValue = @"连接模式:--";
- label.backgroundColor = NSColor.clearColor;
- self.typeLabel = label;
- [self.view addSubview:label];
- }
- {
- NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)];
- label.editable = NO;
- label.bordered = NO;
- label.stringValue = @"连接状态:未连接";
- label.backgroundColor = NSColor.clearColor;
- self.connectLabel = label;
- [self.view addSubview:label];
- }
- {
- NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)];
- label.editable = NO;
- label.bordered = NO;
- label.stringValue = @"鉴权状态:unknow";
- label.backgroundColor = NSColor.clearColor;
- self.authLabel = label;
- [self.view addSubview:label];
- }
- {
- NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)];
- label.editable = NO;
- label.bordered = NO;
- label.stringValue = @"";
- label.backgroundColor = NSColor.clearColor;
- self.deviceInfoLabel = label;
- [self.view addSubview:label];
- }
}
#pragma mark - action
-- (void)scanAction{
- ScanVC *vc = [[ScanVC alloc] initWithNibName:nil bundle:nil];
+- (void)frkAction{
+ FrkVC *vc = [[FrkVC alloc] initWithNibName:nil bundle:nil];
[self presentViewControllerAsModalWindow:vc];
}
-
-/// 获取设备信息
-- (void)geviceInfoAction{
- [[DeviceManger shared] getDeviceInfo:^(AiMouseDeviceInfo *deviceInfo, NSError *error) {
- if (deviceInfo) {
- NSString *info = deviceInfo.description;
- dispatch_async(dispatch_get_main_queue(), ^{
- self.deviceInfoLabel.stringValue = info;
- });
- }
- else{
- NSString *info = [NSString stringWithFormat:@"error == %@",error.description];
- NSLog(@"%@",info);
- dispatch_async(dispatch_get_main_queue(), ^{
- self.deviceInfoLabel.stringValue = info;
- });
- }
- }];
-}
-- (void)showAuthResult:(BOOL)isSuccess{
- dispatch_async(dispatch_get_main_queue(), ^{
- self.authLabel.stringValue = isSuccess?@"鉴权状态:鉴权成功":@"鉴权状态:鉴权失败";
- if(isSuccess){
- [self geviceInfoAction];
- }
- });
-}
-- (void)connectStateDidChange:(AiMouseConnectType)type connectState:(BOOL)isConnected{
- dispatch_async(dispatch_get_main_queue(), ^{
- if(isConnected){
- self.typeLabel.stringValue = type == AiMouseConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid";
- self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接";
- [self geviceInfoAction];
- }
- else{
- self.typeLabel.stringValue = @"连接模式:--";
- self.connectLabel.stringValue = @"连接状态:未连接";
- }
- });
-}
-
-- (void)setupDeviceManger{
- DeviceManger *manager = [DeviceManger shared];
- __weak typeof(self) ws = self;
- manager.authCallBack = ^(BOOL isSuccess) {
- [ws showAuthResult:isSuccess];
- };
- manager.connectStateCallBack = ^(AiMouseConnectType type,BOOL isConnected) {
- [ws connectStateDidChange:type connectState:isConnected];
- };
+- (void)tlwAction{
+ [TlwHidManager shared];
+ return;
+ TlwVC *vc = [[TlwVC alloc] initWithNibName:nil bundle:nil];
+ [self presentViewControllerAsModalWindow:vc];
+
}
-
@end