智能家具系统分为两个不同版本系列:
①系列一:手机app远程控制、远程检测温湿度显示在app,(云平台) ---------本文章
②系列二:语音识别控制 https://blog.csdn.net/m0_59113542/article/details/123742383
3、 OLED显示万年历及外设的工作状态
实现:本设计由STM32驱动相关外设,ESP-01S(8266)连接云平台(巴法云),手机app也连接云平台,以此进行远程信息交互。
远程控制:手机app连接巴法云平台,esp-01s(esp8266)也连接平台,当手机按键按下时向云平台发送控制信息,esp8266接收到该信息,并传输给stm32,由stm32完成相应的控制功能。
温度显示:本项目使用esp-01s(esp8266)驱动dht11测温,然后直接上传云平台数据,手机订阅平台,从而显示温湿度。
(大白话就是:手机将数据放到云平台,stm32通过esp联网去云平台读数据)
OLED屏显示:本项目额外开发显示功能,屏幕具有两种显示:其一显示万年历,其二显示外设的工作状态,两种显示使用按键K0切换。
①显示万年历:上电后自动显示,当时间不对时,可点击K1按键进入时间设置界面,然后通过K2、K3按键进行时间修改。
②显示外设工作状态:当手机端发送控制指令时,STM32完成相关驱动后,屏幕会进入定时显示界面,待定时结束后进入通体显示界面。
1、总体展示图:

2、

总体显示界面 定时显示界面 万年历显示界面 时间修改界面
了解完工作过程,接下来就和闯关游戏一样,一关关的闯,打怪升级,消灭怪兽,营救公主,最终和公主殿下过上了幸福的生活。。。。。哈哈哈,扯远了,说正事、说正事。
大家可以想一下万恶的新冠病毒的传播,传染源→传播途径→接收端感染人群。。。。病毒传染首先得有传染源吧,不可能和孙悟空一样从石头里蹦出来吧。(与本工程信号产生装置贴合,也就是手机app) 然后是传播途径(也就是远程传输 巴法云平台) 然后是接受端(也就是我们的stm32)
系统的开发分为以下三步:
1、手机app制作及如何向云平台发送信息。
2、联网(既然要远程控制,肯定是需要走网络的啊,让我们的信号通过网络“飞过去”,这是esp-01s的工作)
3、stm32接收到命令后的外设驱动
接下来就是具体的闯关打怪时间。
闯关吗,肯定要有法宝啊,在这里向大家介绍一个很nice的"法宝",appinventor,一款麻省理工研发的开发安卓app的平台,是不是一听"麻省理工",感觉嘎嘎高大上,是我们能学会的吗!!!!,其实appinventor嘎嘎简单,比我们用的c语言简单一万倍。
大白话说说他的功能,就是将一些按键、文本框拖入手机界面,然后图形化编程当按键按下的时候实现什么功能就行。

右边这个图像就是编程的,用模块化编程,拼积木都会吧,嘎嘎简单吧!!!!
然后如何让这个app联网呢,这就是另一个"法宝" 巴法云平台了。

大家登录网站后,点击我标记的地方,这里有详细的介绍如何使用。
esp-01s本来就是一个wifi模块,它是通过连接周围的wifi从而上网,连接巴法云平台的。
且esp-01s可以用AT指令进行配置,也可以用arduino进行二次开发,本人选择二次开发,我会将程序放在下面,大家直接简单修改几个参数就能用了。

就修改这几个参数,就能用啦,而且以后想做远程控制的东西,都可以这么用。一劳永逸是不是YYDS。嘿嘿嘿
语言总是最无力的,说再多也不一定能解释清楚,楼主呕心沥血给大家找了一个4分钟的视频,讲解的嘎嘎清楚。https://www.bilibili.com/video/BV1zy4y1k7Mn?spm_id_from=333.337.search-card.all.click
除此之外还有一个详细的介绍文章,用一句话说就是通透:
esp8266-01s接入巴法云,开源app远程控制,微信小程序控制 - 巴法云 - 博客园 (cnblogs.com)
esp8266-01s接收到平台数据后,通过串口发送给stm32,stm32要做的事就是解析命令,并驱动外设。
我们做的是智能家具的模型,不可能真的去改装家具,这里采用模拟的方式,用步进电机模拟窗帘,用舵机模拟开关门,led灯,风扇,继电器。
大家可以发挥自己的想象,用别的外设,这就需要大家自己探讨了。
通过USART3与esp8266-01s连接,接受数据,在主函数中解析命令,并相关操作。
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "usart3.h"
#include "esp8266.h"
#include "Servo_motor.h"
#include "step_motor.h"
#include "string.h"
#include "timer.h"
#include "led.h"
#include <stdio.h>
#include <string.h>
/*
项目的主要内容:STM32配合ESP8266模块与服务器数据交互
ESP8266的连接:USART3(PB10、PB11)
如何判断数据接收完全?
1、出现了换行符;
2、如果超过10ms了都没有下一条数据(TIM7来进行10ms的定时)。
*/
int main(void)
{
u16 rlen=0;//保存接收到的数据长度
// u16 t,len;//串口1调试
char data_tiqu[100];//将接收到的数据保存到该数组
u8 data[10];//最终提取的数据
int k=0,s=0;//保存cmd2开头的数据的下标
int i=0,j=0;
char cmd[]="msg";
int flag=1;//收到正确数据标志位,默认为1,
u8 timex=0;//每200发送1次心跳包的标志位
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
LED_Init();
servo_init();
Step_Motor_GPIO_Init();
uart_init(115200); //串口初始化为115200
usart3_init(115200);
servo_angle(80); //私服电机初始角度(关门状态)
while(1)
{
timex++;
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
rlen=USART3_RX_STA&0X7FFF; //得到本次接收到的数据长度
USART3_RX_BUF[rlen]=0; //添加结束符
//printf("%s",USART3_RX_BUF); //发送到串口
//数据提取
if(strncmp(USART3_RX_BUF,"cmd=2",5)==0)
{
for(i=0;i<strlen(USART3_RX_BUF)+1;i++)
{
data_tiqu[s]=USART3_RX_BUF[i];
s++;
}
printf("%s",data_tiqu);
for(i=0;i<strlen(data_tiqu);i++)
{
if(data_tiqu[i]==cmd[0])
{
k=i;
k++;
for(j=1;j<strlen(cmd);j++)
{
if(data_tiqu[k]==cmd[j])
{
k++;
flag=1;
}
else
{
flag=0;
}
}
}
}
s=0;
//数据提取结束
printf("\r\n\r\n");
if(flag==1)
{
for(i=k+1;i<strlen(data_tiqu)+1;i++) //此时 i为传输接受到数据的索引
{
data[s]=data_tiqu[i];
s++;
}
printf("%s",data);
printf("zaici"); //作用:程序定位
printf("\r\n");
//判断APP控制开关灯
if(data[0]=='1'&&data[1]=='1')
{
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //开LED0
GPIO_SetBits(GPIOB,GPIO_Pin_7); //关LED0
}
if(data[0]=='1'&&data[1]=='0')
{
GPIO_SetBits(GPIOC,GPIO_Pin_13); //关LED0
GPIO_ResetBits(GPIOB,GPIO_Pin_7); //开LED0
}
//判断APP控制开关风扇
if(data[0]=='2'&&data[1]=='1')
{
FS=1;
}
if(data[0]=='2'&&data[1]=='0')
{
FS=0;
}
//判断APP控制开关窗帘
if(data[0]=='3'&&data[1]=='1')
{
motorNcircle(64,(bool)1); //步进电机正传
}
if(data[0]=='3'&&data[1]=='0')
{
motorNcircle(64,(bool)0); //步进电机反传
}
//判断APP控制开关门
if(data[0]=='4'&&data[1]=='1')
{
servo_angle(175);
delay_ms(500);
}
if(data[0]=='4'&&data[1]=='0')
{
servo_angle(80);
delay_ms(500);
}
}
}
if(strncmp(USART3_RX_BUF,"cmd=0&res=1",11)==0)
{
printf("%s",USART3_RX_BUF);
}
USART3_RX_STA=0;
}
if((timex%200)==0)
{
u3_printf("cmd=0&msg=ping");//心跳包
timex=0;
}
}
}
舵机的驱动,舵机是用PWM的不同占空比来转动不同的角度
#include "Servo_motor.h"
#include "delay.h"
//高级定时器1pwm输出初始化
//arr:自动重装值(周期) psc:时钟预分频数
void tim1_pwmInit(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // 使能定时器1的外设时钟
RCC_APB2PeriphClockCmd(TIM1_CH1_GPIO_CLK, ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = TIM1_CH1_PIN; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TIM1_CH1_PORT, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
//TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
void servo_init(void)
{
tim1_pwmInit(SERVO_TIM_ARR,SERVO_TIM_PSC);
TIM_SetCompare1(TIM1,150); //使舵机恢复到中间位置
}
//0.5ms--0° 2.5ms--180°
void servo_angle(uint16_t angle)
{
uint16_t pulse;
//针对舵机可转角度限辐
if(angle <= 5)
angle = 5;
if(angle >= 175)
angle = 175;
//将角度值转换为脉冲值
pulse = (uint16_t)(50 + angle * 100/90.0); //此转换公式需根据pwm的arr及psc配置来做相应变化
TIM_SetCompare1(TIM1, pulse);
}
void servo_debug(void)
{
uint8_t i;
for(i = 0; i < 10; ++i)
{
delay_ms(500);
servo_angle(80); //中间位置
delay_ms(500);
servo_angle(175);
}
}
开关灯与开关风扇就不用多说了,就是GPIO输出高低的问题,还有步进电机,在我另一篇博客上有详细介绍
完整工程链接:https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-24450682672.14.7aa61b42erNczw&id=678372943522
欢迎大家指正交流,有空可以一起讨论代码啊。
制作不易,感谢大家支持,感谢!!!!!!
--------------一个正在努力的人
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article
我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c