每一个物联网系统都是由不同的智能单品组合起来的,智能单品在智能家居系统最常见,包含了空气净化器、智能灯泡、智能门锁、智能电表、智能水表、智能燃气表、智能门窗等。我们经过调研发现大部分的设备都需要具备3个基本的功能:远程、控制、监测。
本项目以智能遥控小车为例,帮助大家理解掌握智能单品的开发过程以及LongTooth(长牙)通信技术的嵌入使用流程,通过对该技术的应用,最终安全高效的实现远程、控制、监测3大功能。
LongTooth(长牙)
LongTooth(长牙)是N22开发的专利技术。是一种基于应用层,通过服务响应的方式,为服务与服务之间提供双向多通道的Internet安全直连通信技术。开发者仅需几行代码上就可以在任何设备之间建立一个双向通信通道,无需另外建立专用服务器,建立一个分布式的IOT环境,服务交互的双方可以直接通过Internet双向通信。极大的降低了物联网各类应用的开发难度、成本,独特的分布式架构让服务交付更加简洁、高效、安全。
现如今,LongTooth(长牙)技术在物联网、工业物联网、智慧城市、无边界通信等领域得到了初步的应用。
项目准备
1、编译工具
APP(此项目以IOS为例)代码编译工具:Xcode
Raspberry Pi代码编译工具: putty(连接)、filezilla(FTP)、notepad++(代码编辑)
在Raspberry Pi上运行程序方法:程序完成后,在Raspberry Pi上运行程序即可实现对应功能,实现方法见《01-LongTooth(长牙)智能车代码加载.doc》
2、配件清单
3、硬件处理
几个硬件部件之间的接线示意图如下:
经过处理,效果如下:
具体实现
1、动力系统实现
案例中我们的配件清单中已经购置了遥控小车,我们需要对遥控小车进行升级,通过树莓派控制板实现远程遥控。即对小车的动力系统主要部件:马达以及步进电机实现远程遥控。
(1)硬件控制流程
Ø 电源给Raspberry板供电;
Ø Raspberry板通过GPIO针脚给L298N模块供电且控制L298N;
Ø L298N控制小车马达。
(2)Raspberry板与L298N板的连接
Ø 经实测,L298N的5V电源针脚须供电3.3V;GND针脚须和Raspberry板的GND相连(共地);
Ø M1和M2模块的两个接头分别接小车的前后轮马达;
Ø 其他针脚的连接由代码决定。
(3) 软件控制流程
Ø Raspberry板上运行着一个LongTooth的demo的服务端;
Ø 手机端运行一个LongTooth的demo的客户端;
Ø 客户端发送指令,通过LongTooth传送到服务端,从而控制小车。
经过分析,我们得到小车的控制逻辑图如下:
2、 通信系统实现
(1)Raspberry设备端通电、联网
Ø 初始化配置见3.2 Raspberry Pi系统配置具体介绍;
Ø 网络配置{有线网络转成无线网络,具体操作见(3)无线网络配置}
(2)LongTooth(长牙)服务注册
APP控制端:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
UITapGestureRecognizer *gestureTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(viewTapped:)];
gestureTap.delegate = self;
[self.view addGestureRecognizer:gestureTap];
//启动注册长牙
[self registerLongTooth];
//创建UI
[self createUI];
}
Raspberry设备端:
int output_run(int id)
{
// 长牙启动入口
…………
printf("app ---------> longtooth -- Start begin --\r\n");
lt_lan_mode_set(true);
lt_register_host_set("118.178.233.149", 53199);
lt_start(devid, appid, appkey, machineid, lt_event_handler_impl);
// 注册服务关键代码
printf("%s lt_start ltid:%s\r\n",__FUNCTION__,lt_id());
……
while(1){
sleep(1);
}
return 0;
}
(3)无线网络配置
本课程中我们要实现小车的远程操作,所以小车需要能够连接无线网络,才能实现远程操作。
APP控制端:
Ø 发送请求获取小车无线网列表
[self.longtoothHandler sendInsWithRemoteLtid:self.carIdString ServiceName:LONGTOOTH_SERVICEWIFI insData:nil block:^(NSString *returnStr) {
}];
Ø 发送对应网络密码
[self.longtoothHandler sendInsWithRemoteLtid:self.carIdString ServiceName:LONGTOOTH_SERVICEWIFI_PASSWORD insData:data block:^(NSString *returnStr) {
if ([returnStr isEqualToString:LONGTOOTH_SERVICEWIFI_PASSWORD]) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIAlertController *alertCtrl1 = [UIAlertController alertControllerWithTitle:@"无线网络" message:@"设定成功" preferredStyle:UIAlertControllerStyleAlert];
[alertCtrl1 addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertCtrl1 animated:YES completion:nil];
});
}
}];
Tips:以上内容选择小车发回来的无线网名称,将对应的密码发回给小车,小车进行无线网配置。
Raspberry设备端
lt_service_add(service_connection_wirelesswifi, lt_connection_wirelesswifi);//serching wifi and send to app
lt_service_add(service_recv_wifipassword, lt_recv_wifipassword);// recv account and password from app
最终效果:
(4)通信建立、身份认证
APP控制端:
Ø 核实确认Raspberry设备端ID,在App界面输入确认连接
Tips:上图中的步骤:1、长牙启动后本地状态;2、当前设备ID;3、远程设备ID;4、确认连接。
Ø 发送连接请求
#pragma mark - 确定连接
- (void)confirmConnectAction
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.view endEditing:YES];
self.hud = [OmniBoxAlertHUD loadingHUDWithMessage:[NSString stringWithFormat:@"%@",@"连接中..."]];
[self.hud show];
});
//网络请求加载提示,UI显示
[self.longtoothHandler sendInsWithRemoteLtid:self.LTCaridTextField.text ServiceName:LONGTOOTH_CONNECTION insData:nil block:^(NSString *returnStr) {
if ([returnStr isEqualToString:LONGTOOTH_CONNECTION]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.hud performSelector:@selector(dismiss) withObject:nil afterDelay:0];
//跳转到小车控制界面
PlayerVideoController *playCtrl = [[PlayerVideoController alloc] init];
playCtrl.carLongToothID = self.LTCaridTextField.text;
[self presentViewController:playCtrl animated:YES completion:nil];
});
}
}];
}
Raspberry设备端:
收到App端连接请求,并反馈给App连接状态
void lt_connection_confirm(const lt_tunnel ltt,
const char* ltid_str,
const char* service_str,
int data_type,
const char* args,
size_t argslen)
{
printf("request from ltid:%s args:%s\r\n",ltid_str,service_str);
char *resp_buf = "alive";
int ret = lt_respond(ltt, LT_ARGUMENTS, resp_buf, strlen(resp_buf), NULL,NULL);
if(ret == 0){
printf("app ----> lt_respond succeed -- [%d] : %s ret:%d\n", strlen("alive"), "alive",ret);
}else{
printf("app ----> lt_respond failed -- [%d] : %s ret:%d\n", strlen("alive"), "alive",ret);
}
return ;
}
Tips: 当小车收到App的连接请求时,会触发此函数。并通过lt_respond方法告诉App连接成功。
3、指令传输
APP控制端:
Ø 操作按钮控制小车;
Ø 点击按钮发送指令,以前进为例。
__block NSArray *prizeArr = @[@"前进", @"右转", @"后退", @"左转"];
self.carControlButton = [[CarControlButton alloc] initWithFrame:CGRectMake(100, 100, s_height-40, s_height-40) prizeArr:prizeArr progress:^(NSInteger currentProgress, NSInteger totalProgress) {
// weakSelf.progess.progress = currentProgress / (float)totalProgress;
} completion:^(NSInteger index) {
NSLog(@"%ld",index);
if (index == 1) {//前进
self.speedLabel.text = [NSString stringWithFormat:@"前进速度:%d",30];
self.speedPogressView.progress=0.3;
_isRunAhead = YES;
_speedNumbers = 30;
NSString *str = [NSString stringWithFormat:@"a %d",30];
[self.longtoothHandler sendInsWithRemoteLtid:self.carLongToothID ServiceName:LONGTOOTH_CARCONTROL insData:[str dataUsingEncoding:NSUTF8StringEncoding] block:^(NSString *returnStr) {
if ([returnStr isEqualToString:LONGTOOTH_CARCONTROL]) {
NSLog(@"16");
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.parameterDataArr insertObject:returnStr atIndex:0];
[self.carParameterTbView reloadData];
});
}];
}else if (index == 2){//后退
Tips:以上代码第一个蓝色字体部分为创建控制小车的操作板,前进,后退,左转,右转,启动,暂停。以前进为例,第二个蓝色字体部分为发送前进指令后给的初始速度显示在当前界面上。第三个蓝色字体部分为发送指令,调用LongTooth(长牙)。指令参数:self.carLongToothID;//连接的小车ID;LONGTOOTH_CARCONTROL;//小车控制服务;str;//为发送指令参数。前进:a 30(初始速度)。
Raspberry设备端:
void lt_carcontrol(const lt_tunnel ltt,
const char* ltid_str,
const char* service_str,
int data_type,
const char* args,
size_t argslen)
{
int val = 0, ret = 0, flag = 0;// error flag
int my_data_type = LT_ARGUMENTS;
char respond_buf[50] = "Command OK";
// get command
char command;
if(args == NULL ){
printf("%s args error\r\n",__FUNCTION__);
return ;
}
strncpy(&command, args, 1);
// get value
char val_str[3];
memset(val_str ,0, 3);
if(argslen > 2){
strncpy(val_str, args + 2, argslen - 2);
val = atoi(val_str);
}else{
val = 0;
}
printf("args:%s arglen:%d val:%d val_str:%d\r\n",args,argslen,val,val_str);
if(val < 0 || val > 100){
printf("value invaid, it should bewteen 0 and 100, included\n");
flag = EVALUEERROR;
}
Tips:以上代码中的
lt_carcontrol()函数为控制小车的函数,当app控制小车时会进入此函数,根据app发来的数据(数据主要包括commond,val两个部分)进行解析,判断,从而对小车进行相应的操作。commond为命令(控制小车前进,后退等操作),val为值(控制小车速度)。4、动作执行
APP控制端:
Ø 小车发送前进指令后可查看当前前进速度;
Ø 可打开视频开关按钮,查看小车采集的录像。
如下图:
Raspberry设备端:
if(flag == 0){
switch(command){
case 'a':// run Ahead
car_run_ahead(val);
sprintf(respond_buf, "run ahead, speed = %d", val);
break;
case 'b':// run Back
car_run_back(val);
sprintf(respond_buf, "run back, speed = %d", val);
break;
case 'l':// turn Left
car_turn_left(val);
sprintf(respond_buf, "turn left");
break;
case 'r':// turn Right
car_turn_right(val);
sprintf(respond_buf, "turn right");
break;
case 'h':// Halt
car_turn_stop();
sprintf(respond_buf, "turn stop");
break;
case 'p':// Park(stop)
car_motor_dc_stop();
sprintf(respond_buf, "car stop");
break;
default:
flag = ECOMMANDERROR;
printf("error command\n");
break;
}
}
Tips:Raspberry 解析App控制指令后通过car_run_ahead(var)来输出L298N小车前进,后退等信号,然后驱使马达运转。
5、指令反馈
Raspberry设备端:
ret = lt_respond(ltt, my_data_type, respond_buf, strlen(respond_buf), NULL,NULL);
if(ret == 0){
printf("app ----> lt_respond succeed -- [%d] : %s ret:%d\n", strlen(respond_buf), respond_buf,ret);
}else{
printf("app ----> lt_respond failed -- [%d] : %s\n ret :%d", strlen(respond_buf), respond_buf,ret);
}
Tips:动作执行完以后,再通过lt_respond(给App一个响应)。
APP控制端:
Ø 小车收到指令给出响应触发App响应函数(方法);
Ø App收到响应参数通过Block回调刷新UI,在界面显示;
Ø
self.returnBlock(str);//block回调更新UI 响应状态@implementation LongToothServiceResponseHandlerImpl
- (void)handle:(id<longtoothtunnel>)ltt withLongToothId:(NSString *)ltid withServiceName:(NSString *)service dataTypeIs:(NSInteger)dataType withArguments:(NSData *)args attach:(id<longtoothattachment>)attachment{/<longtoothattachment>/<longtoothtunnel>
NSLog(@"1234");
if ([service isEqualToString:LONGTOOTH_CARCONTROL]) {
NSString *str = [[NSString alloc] initWithData:args encoding:NSUTF8StringEncoding];
NSLog(@"%@",str);
self.returnBlock(str);
结语
以上内容为您分享了如何利用Raspberry Pi、遥控小车、LongTooth(长牙)通信技术来开发远程遥控小车。通过本项目帮助大家理解掌握智能单品的开发过程以及LongTooth(长牙)通信技术的嵌入使用流程,通过对该技术的应用,最终安全高效的实现远程、控制、监测3大功能。
更多内容,尽在LongTooth
閱讀更多 LongTooth長牙Robin 的文章