开发者成长激励计划-基于TencentOS Tiny 的物联网小车机械臂

2022-08-06 10:55:31 浏览数 (2)

一、概述

随之物联网的发展,各类设备都能通过物联网进行控制,本次方案尝试了通过腾讯物联网平台实现设备控制设备的功能,使用了小型机械臂和小车进行测试,验证控制的物联网控制的实时性。机械臂由5个舵机实现5个自由度。小车由一个转向舵机和一个驱动电机组成。控制端采用WCH沁恒RISC-V TencentOS Tiny CH32V_EVB_AIoT RevB02开发套件负责读取电位器和姿态传感器数据并上传到云端,执行端由STM32L431RCT6控制器负责驱动电机和舵机。

二、方案框架

方案框架方案框架

方案的亮点:

1、实现沁恒开发板对MPU6050传感器的读取。

2、实现5路ADC转换功能,对5个电位器进行读取。

3、实现STM32开发板对5路机械臂舵机、1路小车舵机和电机的正反转功能。

4、实现通过物联网传输数据和远程控制,即沁恒开发板通过获取姿态数据控制STM32开发板的PWM输出和IO控制。

5、通过规则引擎串联两个开发板,实现设备控制设备功能。

6、通过腾讯连连小程序查看数据和控制机械臂和小车。

基于TencentOS Tiny 的物联网小车机械臂.pptx

源码链接gitee地址:https://gitee.com/flytianya2010/tencent-os_-tiny_-io-t_-robot-arm-control/tree/master/

源码链接github地址:https://github.com/flytianya2021/TencentOS_Tiny_IoT_RobotArmControl.git

视频演示:

设备-物联网平台-设备:小车机械臂控制

沁恒开发板控制端沁恒开发板控制端
STM32开发板执行端STM32开发板执行端

三、硬件设计

1、控制端系统

1)沁恒开发板套件

CH32V_EVB是腾讯物联网操作系统TencentOS tiny 团队联合沁恒微电子设计的一款物联网评估板,用于TencentOS tiny 基础内核、RISC-V IP核架构和IoT组件功能体验和评估。 开发板实物如下图:

开发板特性

  • 内置TencentOS Tiny开源物联网操作系统
  • 开发板采用沁恒RISC-V MCU CH32V307VCT6芯片,CH32V305/7系列是基于沁恒自研RISC-V架构微处理器青稞V4系列设计的32位工业级互联型微控制器,配备了硬件堆栈区、快速中断入口,在标准RISC-V基础上大大提高了中断响应速度。加入单精度浮点指令集,扩充堆栈区,具有更高的运算性能。扩展串口UART数量到8组,定时器到10组,其中4组高级定时器。提供USB2.0高速接口(480Mbps)并内置了PHY收发器,以太网MAC升级到千兆并集成了10M-PHY模块。
  • · 64KB SRAM,256KB Flash
  • · 板载Type-C接口WCH-LINK仿真器
  • · 板载esp8266 WiFi模组,支持腾讯云固件
  • · 板载以太网接口
  • · 板载物联网俱乐部WAN Interface接口,可支持NB-IoT、WiFi、4G cat1、LoRa等模组
  • · 板载物联网俱乐部E53 Interface接口,可扩展全系E53传感器以及音频模块;
  • · 板载标准24P DVP摄像头接口,可支持最高500万像素摄像头;
  • · 板载1.54寸 IPS高清显示屏,支持240*240分辨率;
  • · 预留SD卡、用户按键、SPI Flash,
  • · 扩展IO口,方便开发者扩展硬件模块

2)E53模块

采用E53_SC2智慧井盖模块,使用到上面的姿态传感器MPU6050来获取X轴和Y轴的数据,X轴控制小车的前进和后退。Y轴控制小车的舵机转向。

3)电位器

该电位器主要用于模拟机械臂的关节旋转角度,通过控制电位器来控制相应的机械臂的舵机实现摆臂等一系列操作,总共需要5个电位器实现5个自由度。

目前使用如下两轴操作杆电位器模拟电位器数据的读取。

实际是需要如下电位器安装一个主机械臂的关节上面,然后通过读取摆动机械臂的关节数据上传到云端实现远程控制。

2、执行端系统

1)控制主板

小熊派的STM32L431物联网开发板。

小熊派开发板小熊派开发板

2)机械臂

该机械臂由5个舵机组成。实现左右转动,上下,前后和夹紧等动作。

机械臂机械臂

3)WAN模块

采用的是ESP8266模块,实现联网功能。该模块已经烧录腾讯云固件。

ESP8266ESP8266

4)电机驱动电路设计

手工焊接了个MOS管全桥驱动,可以实现电机的正反转。

手工焊接MOS管全桥驱动手工焊接MOS管全桥驱动

原理图如下:

MOS管驱动电机电路MOS管驱动电机电路

5)舵机控制转向。

利用舵机实现对小车的转向控制。改造原小车的电机转向传动机构。

6)供电电源

充电宝或电池供电。小车是由遥控赛车改装而来,本身是电磁供电的。另外我做了外界充电宝的接口,充电宝续航较长,电流大,可以带动电机舵机等耗电大的部件。

四、软件设计

1、控制端软件设计

通过模拟IIC读取将姿态传感器MPU6050和电位器数据读取创建在一个任务里面,通过结构体将数据传到MQTT任务里面。实现对相关传感器数据的上传。在上传之前已经将数据进行了换算,确保执行控制端可以直接使用,或者减少换算量。

功能如下

上传数据模板

代码语言:javascript复制
#define REPORT_DATA_TEMPLATE_MPU    "{\"method\":\"report\"\,\"clientToken\":\"00000001\"\,\"params\":{\"MPU6050_Ax\":%d\,\"MPU6050_Ay\":%d\}}"
#define REPORT_DATA_TEMPLATE_ADC    "{\"method\":\"report\"\,\"clientToken\":\"00000002\"\,\"params\":{\"ADC01\":%d\,\"ADC02\":%d\,\"ADC03\":%d\,\"ADC04\":%d\,\"ADC05\":%d\}}"

MPU6050数据和ADC数据读取

代码语言:javascript复制
#define BACKCOLOR BLACK
void MPU6050_main(void *arg)
{
    int X=0,Y=0,Z=0;
    int X1=0,Y1=0;
    short ACC_x,ACC_y,ACC_z;
    char A_Temp[]={0};
    char G_Temp[]={0};
    char T_Temp[]={0};
    LCD_ShowString(0,200,"MPU6060 Task Init...  ",BLUE,BACKCOLOR,24,0);
    Delay_Init();
    MPU_Init();                //初始化MPU6050
    ADC1_Init();
    LCD_ShowString(0,200,"MPU6050 Init OK!       ",BLUE,BACKCOLOR,24,0);

    while(1)
    {

        Get_ADC_Val_5Channel();//5个AD通道
        LCD_ShowString(0,200,"MPU6050/AD Running/",BLUE,BACKCOLOR,24,0);
        MPU6050_Data.temp=MPU_Get_Temperature(); //得到温度值
        LCD_ShowString(0,200,"MPU6050/AD Running--",BLUE,BACKCOLOR,24,0);
        MPU_Get_Accelerometer(& ACC_x,& ACC_y, &ACC_z);   //得到加速度传感器数据
        MPU6050_Data.aacx=ACC_x/140;
        MPU6050_Data.aacy=ACC_y/140;
        MPU6050_Data.aacz=ACC_z/140;
        LCD_ShowString(0,200,"MPU6050/AD Running ",BLUE,BACKCOLOR,24,0);
        sprintf(T_Temp,"Temperature:%d.%d    ", MPU6050_Data.temp/100,MPU6050_Data.temp0);
        LCD_ShowString(0,30,T_Temp,BLUE,BACKCOLOR,24,0);

        sprintf(A_Temp,"Ax:%d,Ay:%d      ",  MPU6050_Data.aacx, MPU6050_Data.aacy);
        LCD_ShowString(0,55,A_Temp,BLUE,BACKCOLOR,24,0);
        sprintf(G_Temp ,"AD1:%d,AD2:%d      ",  ADC_Data.ADC01, ADC_Data.ADC02);
        LCD_ShowString(0,120,G_Temp,BLUE,BACKCOLOR,16,0);

        sprintf(G_Temp,"AD3:%d,AD4:%d,AD5:%d   ",  ADC_Data.ADC03,ADC_Data.ADC04, ADC_Data.ADC05);
        LCD_ShowString(0,145,G_Temp,BLUE,BACKCOLOR,16,0);
        LCD_ShowString(0,200,"MPU6050/AD Running| ",BLUE,BACKCOLOR,24,0);
           tos_task_delay(5);
          }
}
代码语言:javascript复制
#include "ADC01_demo.h"
#include "lcd.h"
#include "debug.h" 
struct ADC_Data_TypeDef ADC_Data;
/* Global Variable */
s16 Calibrattion_Val = 0;//校准值
u16 val[5];

void ADC1_Init(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE );
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2, ENABLE );
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);                           //初始化ADC时钟,设置时钟为PCLK2的8分频,最大时钟为14MHz

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;           
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;               
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;          
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不启用外部触发源,启动软件触发
    ADC_InitStructureADC_DataAlign = ADC_DataAlign_Right;      
        ADC_InitStructure.ADC_NbrOfChannel = 1;                    
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_Cmd(ADC1, ENABLE);                                       
    ADC_BufferCmd(ADC1, DISABLE);                               
    //测量ADC校准数据,也可以不使用。
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    Calibrattion_Val = Get_CalibrationValue(ADC1);

    ADC_BufferCmd(ADC1, ENABLE);                               
}

 
uint16_t Get_ADC_Val_5Channel(void)
{


    ADC_RegularChannelConfig(ADC1, 8, 1, ADC_SampleTime_239Cycles5 );
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
    {
        tos_task_delay(1);
    }
    val[0] = ADC_GetConversionValue(ADC1);

    ADC_RegularChannelConfig(ADC1, 12, 1, ADC_SampleTime_239Cycles5 );
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
    {
           tos_task_delay(1);
    }
    val[1] = ADC_GetConversionValue(ADC1);


    ADC_RegularChannelConfig(ADC1, 13, 1, ADC_SampleTime_239Cycles5 );
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
    {
           tos_task_delay(1);
     }
    val[2] = ADC_GetConversionValue(ADC1);

    ADC_RegularChannelConfig(ADC1, 14, 1, ADC_SampleTime_239Cycles5 );
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
    {
           tos_task_delay(1);
       }
    val[3] = ADC_GetConversionValue(ADC1);

    ADC_RegularChannelConfig(ADC1, 15, 1, ADC_SampleTime_239Cycles5 );
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
    {
           tos_task_delay(1);
       }
    val[4] = ADC_GetConversionValue(ADC1);


    ADC_Data.ADC01=val[0]/11.3;
    ADC_Data.ADC02=val[1]/11.3;
    ADC_Data.ADC03=val[2]/11.3;
    ADC_Data.ADC04=val[3]/11.3;
    ADC_Data.ADC05=val[4]/11.3;
//    printf("-->ADC_Value8:%drn",  ADC_Data.ADC01);                              //返回ADC转换结果。
//    printf("  >ADC_Value12:%drn", ADC_Data.ADC02);                              //返回ADC转换结果。
//    printf("-->ADC_Value13:%drn", ADC_Data.ADC03);                              //返回ADC转换结果。
//    printf("  >ADC_Value14:%drn", ADC_Data.ADC04);                              //返回ADC转换结果。
//    printf("  >ADC_Value15:%drn", ADC_Data.ADC05);                              //返回ADC转换结果。

}

2、执行端软件设计:

通过连接MQTT,订阅相关Topic,不断读取云台数据,解析Json数据后对机械臂舵机,机械臂舵机的控制是通过PWM来控制的,那么改变这些PWM值的是获取控制端上传的5路ADC值。而小车电机是通过PWM或IO来控制MOS全桥驱动电路实现前进后退功能和小车舵机控制也是由PWM控制,改变状态或数据的是控制端上传的MPU6050的数据实现,中间也做了些换算。

JSON数据解析代码:

代码语言:javascript复制
struct ADC_Data{
  uint16_t ADC01;
	uint16_t ADC02;
	uint16_t ADC03;
	uint16_t ADC04;
	uint16_t ADC05;
}ADCValue;
struct MPU6050_Data{
  int8_t Ax;
	int8_t Ay;
	int8_t Az;
	int8_t Gx;
	int8_t Gy;
	int8_t Gz;
}MPU6050;
static void tos_topic_handler(void* client, message_data_t* msg)
{
   (void) client;
   cJSON* cjson_root   = NULL;
   cJSON* cjson_status = NULL;
	 cJSON* cjson_AD01 = NULL;
	 cJSON* cjson_AD02 = NULL;
	 cJSON* cjson_AD03 = NULL;
	 cJSON* cjson_AD04 = NULL;
	 cJSON* cjson_AD05 = NULL;
	 cJSON* cjson_mpu6050_x = NULL;
	 cJSON* cjson_mpu6050_y = NULL;
	 cJSON* cjson_mpu6050_z = NULL;
   char* status = NULL;
   char* status_AD01 = NULL;
	 char* status_AD02 = NULL;
	 char* status_AD03 = NULL;
	 char* status_AD04 = NULL;
	 char* status_AD05 = NULL;
	 char* status_ACC_X = NULL;	 
	 char* status_ACC_Y = NULL;
	 char* status_ACC_Z = NULL;
    k_event_flag_t event_flag = report_fail;

    /* 打印日志 */
    MQTT_LOG_I("-----------------------------------------------------------------------------------");
    MQTT_LOG_I("%s:%d %s()...ntopic: %s, qos: %d. nmessage:nt%sn", __FILE__, __LINE__, __FUNCTION__, 
            msg->topic_name, msg->message->qos, (char*)msg->message->payload);
    MQTT_LOG_I("-----------------------------------------------------------------------------------n");
    
    /* 使用cjson解析上报响应数据 */
    cjson_root = cJSON_Parse((char*)msg->message->payload);
    if (cjson_root == NULL) {
        printf("report reply message parser failrn");
        event_flag = report_fail;
        goto exit;
    }
  
		/* 提取status状态 */
    cjson_status = cJSON_GetObjectItem(cjson_root, "params");
		cjson_AD01 = cJSON_GetObjectItem(cjson_status, "ADC01");
		cjson_AD02 = cJSON_GetObjectItem(cjson_status, "ADC02");
		cjson_AD03 = cJSON_GetObjectItem(cjson_status, "ADC03");
		cjson_AD04 = cJSON_GetObjectItem(cjson_status, "ADC04");
		cjson_AD05 = cJSON_GetObjectItem(cjson_status, "ADC05");
		  if (cjson_AD01 == NULL)
				{
        printf("cjson_AD01 data failrn");  
       }else   {	ADCValue.ADC01=cjson_AD01->valueint;}
		 if (cjson_AD02 == NULL)
				{
        printf("cjson_AD02 data failrn");
       }else   {ADCValue.ADC02=cjson_AD02->valueint;}
		 if (cjson_AD03 == NULL)
				{
         printf("cjson_AD03 data failrn");
       }else   {ADCValue.ADC03=cjson_AD03->valueint;}
		 if (cjson_AD04 == NULL)
				{
       printf("cjson_AD04 data failrn");
       }else   {}ADCValue.ADC04=cjson_AD04->valueint;
		 if (cjson_AD05 == NULL)
				{
        printf("cjson_AD05 data failrn");
       }else   {ADCValue.ADC05=cjson_AD05->valueint;}
//		printf("cjson_AD01:%drn",ADCValue.ADC01);
//		printf("cjson_AD02:%drn",ADCValue.ADC02);
//		printf("cjson_AD03:%drn",ADCValue.ADC03);
//		printf("cjson_AD04:%drn",ADCValue.ADC04);
//		printf("cjson_AD05:%drn",ADCValue.ADC05);
		//exitmpu6050:
		cjson_status = cJSON_GetObjectItem(cjson_root, "params");
		cjson_mpu6050_x = cJSON_GetObjectItem(cjson_status, "MPU6050_Ax");
		cjson_mpu6050_y = cJSON_GetObjectItem(cjson_status, "MPU6050_Ay");
//		cjson_mpu6050_z = cJSON_GetObjectItem(cjson_status, "MPU6050_Az");
		
		  if (cjson_mpu6050_x == NULL) 
				{
        printf("mpu6050_X data NULLrn");
        //goto exit;
        } else{MPU6050.Ax= (int8_t)cjson_mpu6050_x->valueint;}
		 if (cjson_mpu6050_y == NULL) 
				{
        printf("mpu6050_Y data NULLrn");
        //goto exit;
        }else{		MPU6050.Ay= (int8_t)cjson_mpu6050_y->valueint;}
//		MPU6050.Az= (int8_t)cjson_mpu6050_z->valueint;
//		printf("cjson_mpu6050_x:%drn",MPU6050.Ax);
//		printf("cjson_mpu6050_y:%drn",MPU6050.Ay);
//		printf("cjson_mpu6050_z:%drn",MPU6050.Az);
    /* 判断status状态 */
    if (strstr(status,"success")) {
        event_flag = report_success;
    }else {
        event_flag = report_fail;
    }
 
exit:
    cJSON_Delete(cjson_root);
    cjson_root = NULL;
    status = NULL;
    
    tos_event_post(&report_result_event, event_flag);
    
    return;
}

控制电机和舵机代码:

代码语言:javascript复制
ADCValue.ADC01=60;
	ADCValue.ADC02=120;
	ADCValue.ADC03=120;
	ADCValue.ADC04=150;
	ADCValue.ADC05=90;
	
    while (1) 
	{
	
			//电机控制
		/*全速模式*/
		#if 1
		if(MPU6050.Ax>15&&MPU6050.Ax<=80)//前进
		{
     PWM01=MPU6050.Ax*10;
     if(PWM01>2000)PWM01=2000;
      LCD_ShowCharStr(10,100,"FORWARD     ",BACKCOLOR,RED,24);
      HAL_GPIO_WritePin(CAR_MOTOR_F_Port,CAR_MOTOR_F_Pin,GPIO_PIN_SET);
			HAL_GPIO_WritePin(CAR_MOTOR_R_Port,CAR_MOTOR_R_Pin,GPIO_PIN_RESET);
		}
		else if(MPU6050.Ax<-15&&MPU6050.Ax>=-80)//后退
		{
     PWM01=MPU6050.Ax*10;
     if(PWM01>2000)PWM01=2000;
      LCD_ShowCharStr(10,100,"BACK     ",BACKCOLOR,RED,24);
      HAL_GPIO_WritePin(CAR_MOTOR_F_Port,CAR_MOTOR_F_Pin,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(CAR_MOTOR_R_Port,CAR_MOTOR_R_Pin,GPIO_PIN_SET);
		}
		else
		{
			LCD_ShowCharStr(10,100,"STOP     ",BACKCOLOR,RED,24);
		 HAL_GPIO_WritePin(CAR_MOTOR_F_Port,CAR_MOTOR_F_Pin,GPIO_PIN_RESET);
		 HAL_GPIO_WritePin(CAR_MOTOR_R_Port,CAR_MOTOR_R_Pin,GPIO_PIN_RESET);
		}
		#else
		/*变速模式*/
		if(MPU6050.Ax>5)
		{
     PWM01=MPU6050.Ax*10;
     if(PWM01>2000)PWM01=2000;

      HAL_GPIO_WritePin(CAR_MOTOR_F_Port,CAR_MOTOR_F_Pin,GPIO_PIN_SET);
			HAL_GPIO_WritePin(CAR_MOTOR_R_Port,CAR_MOTOR_R_Pin,GPIO_PIN_RESET);

			HAL_GPIO_WritePin(BTS7960_EN_Port,BTS7960_EN_Pin,GPIO_PIN_RESET);
      __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, PWM01);//PA0
	    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 0);//PA1
		}
   else if(MPU6050.Ax<-5)
		{

    	 PWM02=MPU6050.Ax*10;
     	if(PWM02<-2000)PWM02=-2000;
     	  HAL_GPIO_WritePin(BTS7960_EN_Port,BTS7960_EN_Pin,GPIO_PIN_RESET);
    	 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0);
	 	__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, -PWM02);

		}
		else{
			HAL_GPIO_WritePin(BTS7960_EN_Port,BTS7960_EN_Pin,GPIO_PIN_SET);
			 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0);
	     __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 0);
		}
		#endif
		//车舵机控制
		AccY=MPU6050.Ay 90;//*3.2;
		if(AccY>=110)AccY=110;
		else if(AccY<=65)AccY=65;
	 if(AccY<110&&AccY>65)
		{
			TIM1_PWM01=AccY*3.2;
			if(TIM1_PWM01>=180)TIM1_PWM01=0;
     		snprintf(report_buf,sizeof(report_buf),"SERVER_PWM01:%d     ",AccY);
			LCD_ShowCharStr(10,150,report_buf,BACKCOLOR,GREEN,24);
  		 	 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, TIM1_PWM01);	   //PA8
		}else
		{ 		
			LCD_ShowCharStr(10,150,"   PWM ERRO          ",BACKCOLOR,RED,24);
		__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 288);	
		}
		if(ADCValue.ADC01>90)
		{
		  ADCValue.ADC01=90;
		}
		if(ADCValue.ADC02>90)
		{
			ADCValue.ADC02=90;
		}
		if(ADCValue.ADC03>90)
		{
			ADCValue.ADC03=90;
		}
		if(ADCValue.ADC04>150)
		{
			ADCValue.ADC04=150;
	  }
	  if(ADCValue.ADC05>90)
	 {
			ADCValue.ADC05=90;
		}
  	 if(ADCValue.ADC01>0&&ADCValue.ADC01<=90)		
	 {
		 ArmPWM01=ADCValue.ADC01*3.2;
	  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, ArmPWM01);	  //PA0
		  
	 }
	 if(ADCValue.ADC02>0&&ADCValue.ADC02<=90)		
	 {
	  ArmPWM02=ADCValue.ADC02*3.2;
	  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, ArmPWM02);	   //PA1
	 }
	 if(ADCValue.ADC03>0&&ADCValue.ADC03<=90)		
	 {
	  ArmPWM03=ADCValue.ADC03*3.2;
	  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, ArmPWM03);	 //PA2
	 }
	 if(ADCValue.ADC04>0&&ADCValue.ADC04<=90)		
	 {
	  ArmPWM04=ADCValue.ADC04*3.2;
	  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, ArmPWM04);	 //PA3  
	 }
	 if(ADCValue.ADC05>0&&ADCValue.ADC05<=90)		
	 {
	  ArmPWM05=ADCValue.ADC05*3.2;
	  __HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, ArmPWM05);	//PA6  
	 }

		/*	memset(report_buf,0,sizeof(report_buf));			
			snprintf(report_buf,sizeof(report_buf),"Ax:%d,Ay:%d       ",MPU6050.Ax,MPU6050.Ay);
			LCD_ShowCharStr(10,40,report_buf,BACKCOLOR,BLUE,24);
		
			LCD_ShowCharStr(180,180," | ",BACKCOLOR,RED,32);
		
			snprintf(report_buf,sizeof(report_buf),"ADC01:%d,ADC02:%d  ",ADCValue.ADC01,ADCValue.ADC02);
			LCD_ShowCharStr(10,75,report_buf,BACKCOLOR,BLUE,16);
		
			memset(report_buf,0,sizeof(report_buf));
			snprintf(report_buf,sizeof(report_buf),"ADC03:%d,ADC04:%d   ",ADCValue.ADC03,ADCValue.ADC04);
			LCD_ShowCharStr(10,95,report_buf,BACKCOLOR,BLUE,16);

			memset(report_buf,0,sizeof(report_buf));
			LCD_ShowCharStr(180,180," / ",BACKCOLOR,RED,32);
		
			snprintf(report_buf,sizeof(report_buf),"ADC05:%d   ",ADCValue.ADC05);
			LCD_ShowCharStr(10,115,report_buf,BACKCOLOR,BLUE,16);
			
			LCD_ShowCharStr(180,180,"--",BACKCOLOR,RED,32);
			
			snprintf(report_buf,sizeof(report_buf),"Count:%d   ",count  );
			LCD_ShowCharStr(10,200,report_buf,BACKCOLOR,BLUE,24);
			LCD_ShowCharStr(180,180,"  ",BACKCOLOR,RED,32);*/
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
      		tos_task_delay(100); 
        	memset(&msg, 0, sizeof(msg));

    }
}

五、云端设备

创建了两个云端设备,1是执行端的设备,2是控制端的设备。他们两个共用同一个数据模板,但是控制不冲突。

数据模板数据模板
创建两个设备创建两个设备
代码语言:javascript复制
{
  "version": "1.0",
  "properties": [
    {
      "id": "MPU6050_Temp",
      "name": "姿态仪_温度",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "float",
        "min": "-100",
        "max": "100",
        "start": "0",
        "step": "0.01",
        "unit": "℃"
      },
      "required": false
    },
    {
      "id": "MPU6050_Ax",
      "name": "姿态仪_加速计Ax",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "-130",
        "max": "130",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "ADC01",
      "name": "机械臂关节01",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "380",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "ADC04",
      "name": "机械臂关节04",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "380",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "ADC05",
      "name": "机械臂关节05",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "380",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "ADC02",
      "name": "机械臂关节02",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "380",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "ADC03",
      "name": "机械臂关节03",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "380",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    },
    {
      "id": "MPU6050_Ay",
      "name": "姿态仪_加速计Ay",
      "desc": "",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "-20",
        "max": "20",
        "start": "0",
        "step": "1",
        "unit": ""
      },
      "required": false
    }
  ],
  "events": [],
  "actions": [],
  "profile": {
    "ProductId": "8IT0644V94",
    "CategoryId": "1"
  }
}

六、规则引擎使用

打开规则引擎,将两个设备通过Topic传递数据,实现设备端到设备端的数据交互。也是本方案的关键控制环节。

规则必须禁止后才能编辑,执行端会获取params字段的json格式的数据,然后进行解析。

七、腾讯连连使用

采用了腾讯连连的可视化页面编辑,可以多个页面编辑,实现翻页。如下图是主页面,可以进行机械臂控制。如下图创建页面。

主页面,可以看到控制端上传的控制数据,如果没有控制端的控制时,腾讯连连就成为控制端对执行端进行控制。

点击右上角的小车图标,跳转到机械臂的控制页面对小车进行控制。如下图是翻页页面。

八、总结

本方案尝试一把物联网端对端的控制,因为上传数据和下发数据的延迟原因,控制上很不顺畅。但这是跨出的第一步,什么时候可以将数据实时传输进行实时控制,那才是物联网控制的终极目标,如果用4G或5G模块也许会更好些。但是只要有数据的中转就会存在延迟。

关于硬件的搭建都是一点一点搭起来的,利用一切可以利用的资源,我觉得这很符合开发者成长激励计划该有的样子了,那杜邦线飞来飞去的感觉回到了那年的智能车比赛。

0 人点赞