STM32F103C8T6+ESP-01S+MQTT服务器实现数据的上传和接收(一)
文章目录
STM32F103C8T6+ESP-01S+MQTT服务器实现数据的上传和接收(二)
ESP-01S推荐使用安信可官网1471号固件,烧录配置如下图。
请替换其中<>里面的内容
AT+CWMODE=1
#设置模组进入STA模式
AT+CWJAP="<ssid>","<password>"
#连接wifi
AT+MQTTUSERCFG=0,1,"<Client ID>","<账号>","<密码>",0,0,""
#设置MQTT连接所需要的的参数,包括用户ID(不为空)、账号(admin)以及密码(public)
AT+MQTTCONN=0,"<服务器地址>",<端口号>,<是否重连标志>
#连接mqtt服务器,设置服务器地址,端口号和是否重连标志(0或1)
AT+MQTTPUB=0,"<topic名>","<topic内容>",<QOS>,<retain>
#发布一条topic
AT+MQTTSUB=0,"<topic名>",<QOS>
#订阅一条topic
enum ON_OFF{
OFF,
ON
};
enum Qos{
MAX_ONE,
MIN_ONE,
ONLY_ONE
};
enum BackFlag{
UNKNOW,
SUCC,
FAULT
};
enum ESP_MODE{
STA=1,
AP,
AP_STA
};
enum ATBACK_KEYWORDS{
NO,
WIFI_GOT,
};
#define ERROR "\r\nERROR\r\n"
#define OK "\r\nOK\r\n"
#define FAIL "\r\nFAIL\r\n"
#define WIFI_GOT_IP "WIFI GOT IP\r\n"
#define WIFI_CONNECTED "WIFI CONNECTED\r\n"
enum BackFlag back_flag=UNKNOW; //串口发送AT指令反馈标志 UNKNOW:未发送AT指令或者还未反馈、SUCC:8266反馈OK、FAULT:8266反馈ERROR
char USART_ReceiveString[300]; //串口接收到的数据
enum ATBACK_KEYWORDS ATBack_KeyWords; //用于判断WIFI连接状态
使用PA9、PA10分别连接ESP8266的RXD、TXD引脚。
void usart_init(uint32_t bound) //115200
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
由于每次发送的AT指令都会被8266通过串口再发送回来,为了缓解串口的压力,并且更好的检测到8266反馈的OK或者ERROR,建议关掉AT指令回显,不关掉理论上对库函数的调用不造成影响,没有进行测试,只作理论上说明。(为保证初始化8266正确进行,发送AT指令前后需要使用clearBackFlag()清除8266的反馈标志位,后续代码同理)
void AT_Init(){
clearBackFlag();
printf("ATE0\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("ATE0\r\n");
}
}
clearBackFlag();
}
设置为STA模式,8266作为设备连接其他AP热点或WIFI。
void Esp_Mode(enum ESP_MODE mode){
clearBackFlag();
printf("AT+CWMODE=%d\r\n",mode);//sta mode
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWMODE=%d\r\n",mode);
}
}
clearBackFlag();
}
系统上电后会等待5s,如果已经连上wifi就不需要进行配网,否则使用固定的wifi密码连接固定的wifi
void WIFI_Connect(char ssid[],char wifi_password[]){
delay_s(5);
if(ATBack_KeyWords!=WIFI_GOT){
Esp_Mode(STA);
clearBackFlag();
printf("AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,wifi_password);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,wifi_password);
}
}
clearBackFlag();
}else{
clearATBackKeyWords();
}
}
通过使用WIFI_Connect_Wechat()调用前面三个函数进行智能配网。
系统上电后会等待5s,如果连上了wifi就不需要进行配网,否则开始进行智能配网,当stm32f103c8t6板载led亮起后,开始进入最长1分钟的智能配网时间,配网成功后可提前结束,led熄灭表示配网结束。
void SmartConfig_ON(){ //开始智能配网
clearBackFlag();
printf("AT+CWSTARTSMART=3\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWSTARTSMART=3\r\n");
}
}
clearBackFlag();
}
uint16_t SmartConfig(){ //微信配网过程中 最长持续60s
uint16_t wifi_delay_i=0;
while(ATBack_KeyWords!=WIFI_GOT&&wifi_delay_i<20){
delay_s(3);
wifi_delay_i+=1;
}
return wifi_delay_i<10;
}
void SmartConfig_OFF(){ //停止微信配网
clearBackFlag();
printf("AT+CWSTOPSMART\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWSTOPSMART\r\n");
}
}
clearBackFlag();
}
void WIFI_Connect_Wechat(){
delay_s(5);
if(ATBack_KeyWords!=WIFI_GOT){ //如果没有自动连上wifi
Esp_Mode(STA);
SmartConfig_ON();
led_on();
SmartConfig();
led_off();
SmartConfig_OFF();
}else{
clearATBackKeyWords();
}
}
需要注意Client ID不能重复,建议配合时间戳设置Client ID。
void Mqtt_UserCFG(char *client_id,char *username,char *password){
clearBackFlag();
printf("AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",client_id,username,password);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",client_id,username,password);
}
}
clearBackFlag();
}
通过ip、端口号连接MQTT服务器。
void Mqtt_Connect(char *ip,char* port,enum ON_OFF reconnect){
clearBackFlag();
printf("AT+MQTTCONN=0,\"%s\",%s,%d\r\n",ip,port,reconnect);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTCONN=0,\"%s\",%s,%d\r\n",ip,port,reconnect);
}
}
clearBackFlag();
}
void Mqtt_SubTopic(char *Topic_Name,enum Qos qos){
clearBackFlag();
printf("AT+MQTTSUB=0,\"%s\",%d\r\n",Topic_Name,qos);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTSUB=0,\"%s\",%d\r\n",Topic_Name,qos);
}
}
clearBackFlag();
}
两种用于发布主题的函数,区别是第一种带有8266的反馈判断,如果反馈ERROR会继续发送;第二种没有反馈判断。
void Mqtt_PubTopic(char *Topic_Name,char* data,enum Qos qos,enum ON_OFF retain){
clearBackFlag();
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
}
}
clearBackFlag();
}
void Mqtt_PubTopic_UsartIT(char *Topic_Name,char* data,enum Qos qos,enum ON_OFF retain){
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
}
用于检测AT指令执行后返回结果。
uint16_t checkOK(char* res_txt,char*match_txt){
uint16_t res_len,mat_len,flag=1;
res_len=strlen(res_txt);
mat_len=strlen(match_txt);
for(int i=res_len-mat_len,j=0;i<res_len&&j<mat_len;i++,j++){
if(res_txt[i]!=match_txt[j]){
flag=0;
break;
}
}
return flag;
}
当串口接收到数据后,可用当前函数来检测是否是mqtt消息。可对串口数据进行分类处理。
uint16_t info_type(char USART_ReceiveString[]){ //1为mqtt消息 0为普通消息
uint16_t i,flag=1;
char head[13]="+MQTTSUBRECV"; //12+1 +MQTTSUBRECV+'\0'
for(i=0;USART_ReceiveString[i]!='\0'&&i<12;i++){
if(USART_ReceiveString[i]!=head[i]){
flag=0;
break;
}
}
return flag;
}
主要用于发送AT指令前后,辅助其他函数。
void clearBackFlag(){
back_flag=UNKNOW;
}
void clearATBackKeyWords(){
ATBack_KeyWords=NO;
}
该函数整合了MQTT初始化函数。订阅test/stm32_server主题的消息,最后会发布一条test/stm32_client话题,内容为{“type”:“init”,“msg”:“success”},表示初始化完成。
void mqtt_init(char *ip,char* port,char *username,char *password,char *client_id){
delay_s(1);
AT_Init();
WIFI_Connect_Wechat();
Mqtt_UserCFG(client_id,username,password);
Mqtt_Connect(ip,port,ON);
Mqtt_SubTopic("test/stm32_server",MAX_ONE);
Mqtt_PubTopic("test/stm32_client","{\\\"type\\\":\\\"init\\\"\\,\\\"msg\\\":\\\"success\\\"}",ONLY_ONE,OFF);
}
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我主要使用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服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_
您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除
我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD