使用巴法云实现微信消息推送(Arduino,ESP32,HTTPClient HTTPS GET、POST)——文章最后有点补充更新

一、为什么要做微信消息推送

首先,这篇不是广告,是我在玩ESP32过程中的一则笔记。

我在玩ESP32的过程中,想把一些信息推送到手机上去。说起来有很多种方式,把ESP32接入到各种云平台上去,然后都可以实现向手机推送消息。

但是我想最好能实现微信消息推送,毕竟微信大家用得比较多,容易接收。但是,微信出于安全原因对消息的推送是有严格限制的,不是企业小程序,个人编写的小程序是不能做消息推送的。甚至企业的小程序对推送消息类型、用户订阅选择都是有很多限制。像我这种个人玩玩来说,操作起来难度太大了。

偶然了解到巴法平台可以作为一个消息代理,进行微信消息推送。原理是个人通过微信关注巴法的微信公众号,然后在设备端调用消息推送接口,巴法平台就能把你从设备端发送的消息通过微信公众号推送到你的微信上。

详细信息可搜索巴法,到其网站上了解。

二、如何实现

设备端调用巴法的微信消息推送接口可以通过HTTP协议的GET和POST两种方法。

1.关于使用哪个HTTPClient库

要用HTTP,那肯定要使用HTTPClient库。

Arduino下乐鑫为ESP32提供了一个HTTPClient库;另外Arduino也提供了一个第三方库ArduinoHttpClient。两者的使用方法上区别比较大。你在网上搜索出来的代码要注意区别别人究竟是使用的哪个库。

如果你的Arduino环境同时有两个库,那么要注意,代码开头的:

#include <HTTPClient.h>
#include <WiFiClientSecure.h>

这两个头文件是不是你所使用的库目录里面的。把你的鼠标放到该代码行,Arduino会提示你该文件的目录,请仔细检查。如果有问题,你最好是写头文件的完整路径,或者删除不使用的库。

这里我选择的是乐鑫提供的HTTPClient库。也就是你的Arduino环境配置好后,在File->examples->exampls for ESP32下面看到的那几个代码示例所使用的库。该库并不是需要手动安装的第三方库。

但是该库的示例代码实在是太过简单了,就一个GET方法。如果要用好该库,自己最好把库目录里面的HTTPClient.h和HTTPClient.c两个文件打开来自己研究一下其代码究竟怎么写的、各种方法调用接口的参数形式是怎么样的,对使用该库帮助很大。

2.HTTP协议

开始工作前,你得需要搜索了解一下HTTP协议,GET和POST是什么意思,这里只是大概讲一下。

(1)主机名和路径

主机名和路径在HTTPClient是两个东西。你用浏览器访问一个网址,是由主机名和路径两部分组成的。

比如https://api.bemfa.com/api/wechat/v1/weget.php里面,api.bemfa.com是主机名,/api/wechat/v1/weget.php是路径,有时也叫route。

(2)GET方法

GET方法是设计用来从服务器获取数据的。

当你想访问api.bemfa.com这个网站,当你在浏览器的地址栏输入该地址的时候并敲下回车键,浏览器就发出一个HTTP GET请求给该网站服务器。

当你不指定获取特定网页时,浏览器发出的请求里所带的路径将是“/”也就是根目录,服务器会发送默认的主页面数据给浏览器。

当指定路径的时候,浏览器会把路径也发送出去,比如你地址栏输入的是api.bemfa.com/api/wechat/v1/weget.php,那么路径就是/api/wechat/v1/weget.php。

当然你想获取特定的数据,那么你需要传一些参数给服务器,这是就是通过GET方法传数据。

HTTP里面是通过在路径后面加?来附加数据的。比如https://api.bemfa.com/api/wechat/v1/weget.php?uid=123456,这时就是把uid这个参数传给服务器,其值是123456。多个参数通过&符号链接,比如https://api.bemfa.com/api/wechat/v1/weget.php?uid=123456&device=esp32

浏览器是直接把这个链接地址整体作为GET的字符串发送给服务器的,参数解析工作由服务器完成。

这里可以看出来,GET虽然设计来获取服务器数据的,但是其实是可以通过参数向服务器传递少量数据的。

(3)POST方法

POST设计本来就是向服务器发送大量数据用的,一般页面上面有表单(form)时,点击表单提交按钮时,浏览器就会调用POST方法将表单数据发送到服务器。

POST方法与GET方法在主机和路径上是一样的,不同之处在于,POST不是将所要传递的数据放在URL里面,而是在TCP连接建立之后,单独向服务器发送的。

当然,所要传输的数据可以有很多种格式,用得最多的是form表单。数据的类型需要在HTTP协议的Header中去明确。表单数据的话,header的样子应该差不多是这个样子:

multipart/form-data

(4)辅助调试工具

为了便于调试HTTPClient,你可以使用专门的HTTP协议调试工具,比如Apifox、Postman等,来帮助一调试HTTP,加深对协议实现过程的理解。

3.编码实现

还是先上代码,把API操作定义成一个类bemFaCloudOper,其中有2两个方法:

int sendMsg_GET(void)是通过GET方法调用巴法API;

int sendMsg_POST(void)是通过POST方法调用巴法API。

代码只是做测试用,有很多不规范和需要改进的,鉴于这只是一篇笔记,请不要计较那么多。

#include <HTTPClient.h>
#include <WiFiClientSecure.h>



class bemFaCloudOper
{
  //2024/1/4,目前已调通的接口:
  // GET type=1 成功,GET TYPE=2失败
  // POST type=1成功,POST TYPE=2成功
  public:
    String bemFaServerAddress = "api.bemfa.com";//主机部分,注意前面不能加"https://",是否使用HTTPS在后面begin函数的最后一个参数确定
    String bemFaServerRoutePath="/api/wechat/v1/weget.php";//路径部分
    int  port=443;//https端口号443

    WiFiClientSecure * client_wifi = new WiFiClientSecure;
    HTTPClient client_https;

    //巴法平台API所必须的4个参数
    String uid="xxxxxxxxxxxx";//UID在巴法平台的控制台里面去查
    String type="1";//type=1设备预警;type=2设备提醒
    String device="ESP";
    String msg="启动";
    //String msg2="hehe";msg2在微信消息中无法显示出来,无用

    int sendMsg_GET(void)
    {
      if(client_wifi)
      {
        //client_wifi->setCACert(bemFaRootCACertificate);//该方式有问题无法调通。如果使用该方式,可能需要私钥,详见库文件中的HTTPClient.c关于该方式的定义。索性不用进行证书验证
        client_wifi->setInsecure();//不验证服务器证书
      }
      else
      {
        Serial.println("need init client_wifi");
      }
      bemFaServerRoutePath=bemFaServerRoutePath+"?uid="+uid+"&type="+type+"&device="+device+"&msg="+msg;//拼接GET的URL
      Serial.print("bemFaServerRoutePath=");Serial.println(bemFaServerRoutePath);
      int statusCode;
      String response;

      if (client_https.begin(*client_wifi, bemFaServerAddress,443,bemFaServerRoutePath,true)) //最后一个参数true确定是否使用https
      {
        statusCode=client_https.GET();
        if (statusCode > 0) 
        {
          if (statusCode == HTTP_CODE_OK || statusCode == HTTP_CODE_MOVED_PERMANENTLY) 
          {
            response = client_https.getString();
          }
        }
        else
        {
          Serial.printf("HTTPS GET... failed, error: %s\n", client_https.errorToString(statusCode).c_str());
        }
        client_https.end();
      }
      else
      {
        Serial.println("HTTPS Unable to connect\n");
      }

      Serial.print("bemFa Status code: ");
      Serial.println(statusCode);
      Serial.print("bemFa Response: ");
      Serial.println(response);

      delete client_wifi;
      return statusCode;
    }


    int sendMsg_POST(void)
    {
      bemFaServerRoutePath="/api/wechat/v1/";
      device="device";
      msg="msg";
      type="2";//type=1设备预警;type=2设备提醒
      if(client_wifi)
      {
        //client_wifi->setCACert(bemFaRootCACertificate);//不验证服务器证书
        client_wifi->setInsecure();
      }
      else
      {
        Serial.println("need init client_wifi");
      }

      String payload="uid="+uid+"&type="+type+"&device="+device+"&msg="+msg+"\r\n";

      Serial.print("bemFaServerRoutePath= ");Serial.println(bemFaServerRoutePath);
      Serial.print("post payload= ");Serial.println(payload);
      int statusCode;
      String response;

      if (client_https.begin(*client_wifi, bemFaServerAddress,443,bemFaServerRoutePath,true)) //最后一个参数true确定是否使用https
      {
        client_https.addHeader("Content-Type","application/x-www-form-urlencoded");
        statusCode=client_https.POST(payload);
        if (statusCode > 0) 
        {
          if (statusCode == HTTP_CODE_OK || statusCode == HTTP_CODE_MOVED_PERMANENTLY) 
          {
            response = client_https.getString();
          }
        }
        else
        {
          Serial.printf("HTTPS POST... failed, error: %s\n", client_https.errorToString(statusCode).c_str());
        }
        client_https.end();
      }
      else
      {
        Serial.println("HTTPS Unable to connect\n");
      }

      Serial.print("bemFa Status code: ");
      Serial.println(statusCode);
      Serial.print("bemFa Response: ");
      Serial.println(response);

      delete client_wifi;
      return statusCode;
    }
};

static bemFaCloudOper objbemFaCloudOper;//请在你的代码中调用该对象中的方法发送数据

代码中有几个需要注意的关键点:

(1)这只是一个类的定义代码,要测试该代码请在你的ESP32完成WIFI连接,并确保你得ESP32时钟正常后(最好通过NTP进行校时)再调用该代码中的方法,否则可能调用失败。

(2)巴法平台API有4个必须的参数:uid、type、device、msg,具体什么值请到巴法平台查询。但是GET方法下的type=2的API接口我并没有测试通,我想可能并不是代码问题而是巴法平台问题,因为我使用HTTP调试工具调用GET type=2的API接口也不行。测试代码中几个参数被我写成了固定值,你可以修改代码将其拿出来作为参数传递进去。

(3)确保你在巴法平台账号所绑定的微信与你关注巴法公众号的微信是同一个,否则是收不到推送消息的。

(4)我使用的是HTTPS协议,按理说是需要进行服务器证书和用户秘钥验证的(建立SSL通道),但是有点麻烦。索性我就不验证证书了,所以代码里有一行client_wifi->setInsecure()跳过证书验证步骤,简化操作。如果你确实需要验证,请自行研究HTTPClient.c文件中的代码。

(5)client_https.begin(*client_wifi, bemFaServerAddress,443,bemFaServerRoutePath,true)该函数的调用可能与你再网上查到的其他代码中调用方法不太一样。这里我是为了使用HTTPS而在源文件HTTPClient.c中找到的一种调用方法,供参考。

(6)POST方法中,注意参数不再是作为URL的一部分,所以uid前面是没有“?”的。

GET方法中:bemFaServerRoutePath=bemFaServerRoutePath+"?uid="+uid+"&type="+type+"&device="+device+"&msg="+msg;

POST方法中:String payload="uid="+uid+"&type="+type+"&device="+device+"&msg="+msg+"\r\n";

多个参数之间仍然是通过“&”连接起来。

(7)POST方法中,必须人为添加HTTP header,毕竟你得告诉服务器你发送的是什么类型的数据嘛。所以比GET多了一行 

client_https.addHeader("Content-Type","application/x-www-form-urlencoded");

三、一些补充

1.前文提到的HTTPclient.c源文件,其实还有一个源文件WiFiClientSecure.c很重要,HTTPS中关于安全的内容全部都在WiFiClientSecure.c里面。

2.ESP32实测HTTP GET和POST很占用内存。如果创建一个前文代码中的objbemFaCloudOper对象,然后反复调用其sendMsg_get和sendMsg_post方法,极有可能导致堆栈不够用而CPU直接重启问题,重启后查询重启原因是TG1WD_SYS。其实我在连续调用post或get函数的第二次就发现CPU重启了,所以它真的很占堆栈空间。解决办法比较粗暴:调用一次post或get后就删除objbemFaCloudOper对象,再次调用之前再重新生成一个。

  • 22
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值