智能窗帘机器人——软件功能实现篇
Posted 三明治开发社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能窗帘机器人——软件功能实现篇相关的知识,希望对你有一定的参考价值。
智能窗帘机器人软件方案介绍
本demo的GitHub仓库链接🔗为:
1.功能需求
功能名称 | 功能详细解释 | 备注 |
---|---|---|
普通控制 | 有打开、关闭和停止三个按钮。 | 停止按钮在任何情况下都能保证使电机停转。 |
百分比控制 | 按照窗帘长度的百分比来控制。 | |
自动测量窗帘长度 | 自动测量窗帘机器人跑全程所需时间。 | |
电机正反转 | 改变电机运行时默认的正反转方向。 | 改变电机打开窗帘为正转或反转 |
动作检测 | 检测到手动开,将窗帘全部打开。检测到手动关,将窗帘全部关闭。 | APP上名称为:自启动开关 |
光照强度显示 | 显示测量到的当前光照强度。 | 单位为:lux。 |
光照控制 | 低于设置的光照值时,关闭窗帘。 | 设置的光照值为lux单位下除以十。例:光照值设置为10,当检测到光照值低于100lux 时将会关闭窗帘。(如果用户不愿意关闭窗帘,打开两次将会自动关闭该功能) |
电量显示 | 显示电机当前的电量。 |
2.功能实现:普通控制和百分比控制
普通控制和百分比控制都需要在已经知道窗帘的长度的情况下来进行实现的,由于电机的转速是一定的,在测量窗帘长度的时候其实是测试从 0% 到 100% 需要多少时间,然后根据电机当前所处位置和要到达的位置来计算需要运行的时间,即可实现控制窗帘机器人的移动到指定位置。
百分比控制在接收到app下发的DP点后先调用void curtain_percent_control(unsigned char current_position, unsigned char target_position)
函数使窗帘机器人向对应的方向运行,计算需要运行的时间存入全局变量。void curtain_percent_control_stop_task(void)
函数需要被一直执行,当检测到运行时间已达到的时候,停止电机运行,将当前位置存入到FLASH中,上报当前电机位置。百分比控制部分代码示例:
void curtain_percent_control(unsigned char current_position, unsigned char target_position)
{
unsigned long total_time;
if ((current_position == target_position) || \\
(current_position < 0) || (current_position > 100) || \\
(target_position < 0) || (target_position > 100)) {
TUYA_APP_LOG_ERROR("input error");
*curtain_robot.dp_percent_state.dp_data = current_position;
return;
}
if (curtain_robot.motor_run_mode == RUN_MODE_IDLE) {
curtain_robot.motor_run_mode = RUN_MODE_PERCENT_CONTROL;
} else {
TUYA_APP_LOG_ERROR("motor_run_mode != RUN_MODE_IDLE");
*curtain_robot.dp_percent_state.dp_data = current_position;
return;
}
total_time = curtain_robot.dp_time_total.dp_data[0];
total_time = (total_time << 8) | curtain_robot.dp_time_total.dp_data[1];
if (total_time == 0) {
*curtain_robot.dp_percent_state.dp_data = current_position;
return;
}
if (current_position < target_position) {
curtain_robot.run_end_time = ((target_position - current_position) * (total_time * 10)); //*10=*1000/100,ms->us
curtain_robot.run_start_time = clock_time();
curtain_close();
} else {
curtain_robot.run_end_time = (current_position - target_position) * (total_time * 10);
curtain_robot.run_start_time = clock_time();
curtain_open();
}
/* percent control state target position */
percent_target_value = target_position;
*curtain_robot.dp_percent_control.dp_data = target_position;
/* percent control */
dp_update(curtain_robot.dp_percent_control.dp_id, \\
curtain_robot.dp_percent_control.dp_type, \\
curtain_robot.dp_percent_control.dp_data, \\
curtain_robot.dp_percent_control.dp_data_len);
}
void curtain_percent_control_stop_task(void)
{
if (((curtain_robot.motor_run_mode == RUN_MODE_PERCENT_CONTROL)) && \\
(clock_time_exceed(curtain_robot.run_start_time, curtain_robot.run_end_time))) {
curtain_pause();
curtain_robot.motor_run_mode = RUN_MODE_IDLE;
save_device_data();
/* percent state */
*curtain_robot.dp_percent_state.dp_data = percent_target_value;
dp_update(curtain_robot.dp_percent_state.dp_id, \\
curtain_robot.dp_percent_state.dp_type, \\
curtain_robot.dp_percent_state.dp_data, \\
curtain_robot.dp_percent_state.dp_data_len);
}
}
3.功能实现:自动测量窗帘长度
自动测量窗帘长度实际上是测量窗帘杆子的长度,那么如何判断窗帘机器人是否到达终点也就是两端是测量的关键,后续其他功能的实现基本都是基于此功能来实现的。
判断窗帘机器人到达终点,在该硬件上有两种方法来判断。
- 一是通过测量电机驱动芯片中的MO_CUR引脚,当电机堵转的时MO_CUR引脚将会比正常运行时的电压高,通过判断该引脚电压可以知道电机是否发生堵转,从而确认是否到达终点。
-
二是通过三轴加速度传感器 lis2dw12 来判断是否到达终点。根据 lis2dw12 在硬件上的放置位置来看,判断窗帘机器人的运行状态主要依靠三轴加速度传感器中的x轴来判断。下图为窗帘机器人在运行过程中三轴加速度传感器x轴的数据,静止状态基本无波动,启动后到到达终点前上下波动较大,在到达终点撞击的那一刻x轴数据明显的一个凸起的上升,到达终点后电机未停止运行可以看到x轴的数据比静止时平均要高,且数据上下浮动比运行过程中要较小。
通过上面的两种方式可以看到,使用电压比较的方式来判断是否到达终点相较于使用 lis2dw12 来判断来看是较为简单。但使用三轴加速度计 lis2dw12 可以清晰的知道当前电机处于何种状态。
4.功能实现:动作检测
通过观察波形可以知道,当你向某一方向拉窗帘时,总体来看虽呈波动状态,但最开始时的值与拉取的方向有关,所以该部分功能的实现是通过判断窗帘机器人被拉动后最开始的一部分数值来确定将要跑动的方向。
示例代码:
short x_data_buf[100] = {0};
unsigned int clean_x_buf_count = 0;
unsigned char x_data_index = 0;
void auto_power_task(void)
{
short x_axis_data = 0;
unsigned char i = 0;
unsigned char open_count = 0, close_count = 0;
if (*(curtain_robot.dp_auto_power.dp_data) == TRUE) {
if (clock_time_exceed(curtain_robot.get_lis2dw12_data_time, 10 * 1000)) {
curtain_robot.get_lis2dw12_data_time = clock_time();
if (curtain_robot.motor_run_mode != RUN_MODE_IDLE) {
return;
}
x_axis_data = get_lis2dw12_x_value();
if (x_axis_data > 100 || x_axis_data < -100) {
x_data_buf[x_data_index] = x_axis_data;
x_data_index++;
clean_x_buf_count = 0;
} else {
clean_x_buf_count++;
}
if ((clean_x_buf_count > 20) && (x_data_index >= 20)) { //At this point it has levelled off
for (i=0; i < 7; i++) {
if (x_data_buf[i] >= 0) {
open_count++;
} else {
close_count++;
}
}
if (*curtain_robot.dp_percent_state.dp_data == 0) {
curtain_robot.auto_power_state = AUTO_POWER_CLOSE;
} else if (*curtain_robot.dp_percent_state.dp_data == 100) {
curtain_robot.auto_power_state = AUTO_POWER_OPEN;
} else if (open_count < close_count) {
curtain_robot.auto_power_state = AUTO_POWER_CLOSE;
} else {
curtain_robot.auto_power_state = AUTO_POWER_OPEN;
}
//clean flag
clean_x_buf_count= 0;
x_data_index=0;
}
if (curtain_robot.auto_power_state != AUTO_POWER_IDLE && clean_x_buf_count>= 100) {
if (curtain_robot.auto_power_state == AUTO_POWER_OPEN) {
curtain_percent_control(*curtain_robot.dp_percent_state.dp_data, 0);
} else if (curtain_robot.auto_power_state == AUTO_POWER_CLOSE) {
curtain_percent_control(*curtain_robot.dp_percent_state.dp_data, 100);
}
curtain_robot.auto_power_state=AUTO_POWER_IDLE;
clean_x_buf_count = 0;
}
if (clean_x_buf_count > 500) {
clean_x_buf_count = 0;
x_data_index=0;
}
}
}
}
5.功能实现:光照强度显示
检测光照强度的传感器使用的是opt3004,通过IIC协议,通过读取 opt3004 的 result 寄存器获取当前的光照值。
示例代码:
#define I2C_CLK_SPEED 200000
short lsb_size_tab[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048};
unsigned char opt3004_init(void)
{
unsigned char device_id_data_buf[2] = {0};
unsigned char manufacturer_id_data_buf[2] = {0};
unsigned char write_data[2] = {0xcc, 0x10};
unsigned long delay_time = 0;
i2c_gpio_set(I2C_GPIO_GROUP_C0C1);
i2c_master_init(OPT3004_I2C_ADDR_W, (unsigned char)(CLOCK_SYS_CLOCK_HZ / (4 * I2C_CLK_SPEED)));
i2c_write_series(OPT3004_CONFIG_REGISTER_ADDR, 1, write_data, 2);
i2c_master_init(OPT3004_I2C_ADDR_R, (unsigned char)(CLOCK_SYS_CLOCK_HZ / (4 * I2C_CLK_SPEED)));
i2c_read_series(OPT3004_DEVICE_ID_REGISTER_ADDR, 1, device_id_data_buf, 2);
delay_time = clock_time();
while (!clock_time_exceed(delay_time, 100)); //100us delay
i2c_read_series(OPT3004_MANUFACTURER_ID_REGISTER_ADDR, 1, manufacturer_id_data_buf, 2);
if ((((device_id_data_buf[0]<<8) + device_id_data_buf[1]) != DEVICE_ID) || \\
(((manufacturer_id_data_buf[0]<<8) + manufacturer_id_data_buf[1]) != MANUFACTURER_ID)) {
return 0;
}
return 1;
}
short get_opt3004_value(void)
{
short ret_value = -1;
int result_value = 0;
short result_data_e = 0, result_data_r = 0;
unsigned char opt3004_cfg_data[2] = {0};
unsigned char opt3004_result_data[2] = {0};
i2c_master_init(OPT3004_I2C_ADDR_R, (unsigned char)(CLOCK_SYS_CLOCK_HZ / (4 * I2C_CLK_SPEED)));
i2c_read_series(OPT3004_CONFIG_REGISTER_ADDR, 1, opt3004_cfg_data, 2);
if (opt3004_cfg_data[1]&0x80) {
i2c_read_series(OPT3004_RESULT_REGISTER_ADDR, 1, opt3004_result_data, 2);
result_value = (opt3004_result_data[0]<<8) + opt3004_result_data[1];
result_data_e = ((result_value & 0xF000) >> 12);
result_data_r = (result_value & 0x0FFF);
ret_value = lsb_size_tab[result_data_e] * result_data_r / 100;
TUYA_APP_LOG_DEBUG("ret_value:%d", ret_value);
}
return ret_value;
}
以上是关于智能窗帘机器人——软件功能实现篇的主要内容,如果未能解决你的问题,请参考以下文章