从零开始开发物联网项目(6)——Arduino和ESP8266自动数据上传终端

2023-05-16

前面几节介绍了Mqtt协议以及用ESP8266模块作为客户端连接Mqtt服务器并实现数据的发布和订阅。这一节我们就正式的开始制作第一个物联网终端,基于Arduino和ESP8266模块。之所以选择了Arduino是因为它的开发比较方便,并且后期直接可以自己用Atmega芯片搭建最小系统来实现终端的产品化。

以下就是基于Arudino和ESP8266的自动数据采集和上传终端,前期为了调试数据的需要,我直接使用了Arduino mega2560,因为它有多个串口可以使用,当然Arduino UNO也完全没问题,但是由于只有一个硬串口,而软串口支持不了115200波特率,所以ESP模块只能接在硬串口上了,这就导致前期用UNO来开发就会很麻烦,每次改程序就要拔掉ESP模块。所以直接用了2560开发就方便多了。

硬件的连接非常的简单,我这里将ESP模块接到了串口1上,所以接线为:

Esp8266 Arduino Mega2560

Vcc ------------- 3V3

CH_PD ------------ 3V3

GND ------------- GND

URXD ------------- TXD1

UTXD ------------- RXD1

我上传的数据为模拟口A0采集到的模拟电压值,你可以接一个Lm35温度传感器上去,不过我这里仅仅为了测试Mqtt数据传输,就不接了,所以每次测量出来的数据都是随机的。

下面主要就是程序了,思路也很简单,就是完全模拟上一节时手动输入AT指令和Mqtt指令,只不过交由程序来自动实现罢了,好了,直接看程序吧,具体细节我也直接写在程序的注释里了。

//注意,本程序需要Arduino mega2560
//下面填入你自己的IP地址
String tcplinkstr = "AT+CIPSTART=\"TCP\",\"10.0.0.252\",1883";
//你的wifi名称
String wifiname = "******";
//wifi密码
String wifipsd = "******";
String wifilinkstr, tempstr,constr = "";
//mqttlink为mqtt连接指令,参考之前的内容
unsigned char mqttlink[27] = {0x10, 0x19, 0x00, 0x06, 0x4d, 0x51, 0x49, 0x73, 0x64, 0x70, 0x03, 0xc2, 0x01, 0x2c, 0x00, 0x05, 0x4d, 0x61, 0x6b, 0x65, 0x72, 0x00, 0x01, 0x72, 0x00, 0x01, 0x72};
//mqttsubscribe为mqtt数据发布指令,同样参考之前的,这里发送的数据为"t:****",采集到数据后替换掉*
unsigned char mqttpublish[13] = {0x32, 0x0b, 0x00, 0x01, 0x72, 0x00, 0x01, 0x74, 0x3a, 0x2a, 0x2a, 0x2a, 0x2a};
unsigned char mqttheart[2] = {0xc0, 0x00};
unsigned char mqttdata[4];
int mi = 0, sndi = 0, mode = 0, at = 0, espok = 0, temp,rtn;
long timenow, timebefor = 0, tm;


void setup() {
 //连接电脑串口,用于程序编写时观察程序的运行
  Serial.begin(9600);         
  //用于连接esp8266      
  Serial1.begin(115200);            
 //用于连接wifi的AT指令
  wifilinkstr = "AT+CWJAP=\"";        
  wifilinkstr += wifiname;
  wifilinkstr += "\",\"";
  wifilinkstr += wifipsd;
  wifilinkstr += "\"";
   //通电后等待一段时间让esp8266启动完毕      
  delay(5000);                       
 //一通电就发送网络状态查询,因为esp8266会自动记录wifi,所以可能一开机就连接上了wifi
  Serial1.println("AT+CIPSTATUS");  
}
//*******以下为loop主程序,主要用mode来判断程序此刻的功能*********
void loop() {
 //mode=0时为连接tcp服务器状态,此时为AT指令阶段,esp8266返回的都是字符串
  if (mode == 0) {      
//读取来自串口1的数据,来自esp8266,最后存在constr字符串中             
    while (Serial1.available())      
    {
      constr += char(Serial1.read());
      delay(2);
    }
 //如果constr字符串有数据了,则运行tcplink程序
    if (constr.length() > 0)         
    {
      tcplink();             
    }
  }
   //已经进入透传模式可以发送数据
  else if (mode == 1) {     
  //当接收mqtt服务器返回的数据时是16进制      
    while (Serial1.available())    
    {
//将读取的16进制数据保存到mqttdata数组中
      mqttdata[mi] = byte(Serial1.read());        
      mi++;
      delay(2);
    }
    mqttsend();
  }
}
//************tcplink程序,主要用于esp8266AT模式时与模块的互动******
void tcplink() {
  if (espok == 0)
  {
//用于相应网络连接信息返回值,2、4说明wifi已经连上
    if (constr.indexOf("STATUS:2") || constr.indexOf("STATUS:4"))       
    {
      Serial.println("STATUS:2、4");
      delay(500);
  //直接让at=4,跳过连接wifi的AT指令
      at = 4;                
 //有返回说明esp8266模块没问题                   
      espok = 1;           
  //进入AT发送指令程序,由at值来决定发哪句                    
      esp8266at(at);                           
    }
 //5说明WiFi还没有连上,需要从头开始AT指令
    else if (constr.indexOf("STATUS:5"))       
    {
      Serial.println("STATUS:5");
      delay(500);
      at = 1;
      espok = 1;
      esp8266at(at);
    }
//3说明TCP已经连上,直接进入透传模式即可
    else if (constr.indexOf("STATUS:3"))       
    {
      Serial.println("STATUS:3");
      delay(500);
      at = 7;
      espok = 1;
      esp8266at(at);
    }
  }
  else
  {
//如果模块返回OK,则继续下一条AT指令
    if (at < 6 && constr.indexOf("OK"))            
    {
      Serial.println("OK");
       //收到返回后到下一条需要延时一段时间,不然esp模块会反应不过来  
      delay(500); 
      at++;
//执行AT指令
      esp8266at(at);
      }
//当AT指令到第6条也就是连接TCP服务器后,返回CONNECT说明连接成功
    else if (at == 6 && constr.indexOf("CONNECT"))     
    {
      Serial.println("CONNECT");
      delay(500);
      at++;
      esp8266at(at);
    }
//当AT指令到第6条也就是连接TCP服务器后,返回CLOSED说明连接不成功,at不增加,会再次连接
    else if (at == 6 && constr.indexOf("CLOSED")) 
    {
      Serial.println("CLOSED");
      delay(500);
      esp8266at(at);
    }
//连接成功后收到">"说明进入了透传模式
    else if (at == 7 && constr.indexOf(">"))     
    {
      Serial.println("TCPlinked!");
      delay(1000);
 //模式mode=1,进入了mqtt数据发送模式
      mode = 1;                                
    }
  }
  constr="";//每次处理完数据都要清空
}
//***********************mqtt数据发送**********
void mqttsend() {
//用于计时,从而实现每隔一定时间发送一个数据
  timenow = millis();                     
  tm = timenow - timebefor;    
  temp = analogRead(A0);                   //采集模拟口A0的数据
  tempstr = String(temp);                //将采集到的数据变成字符串
  int mqtti = tempstr.length();          //读取字符串长度
  String mqttch; 
  //下面的程序用来将采集到的数据添加到mqtt发送的16进制数组中
  for (int j = 0; j < mqtti; j++)             
  {
    mqttch = tempstr.substring(j, j + 1);
//将字符转变为ascii码的16进制
    mqttpublish[j + 9] = chartobyte(mqttch);    
  }
  for (int j = mqtti; j < 4; j++)
  {
 //为了mqtt数据发送方便,这里控制发送的字符数每次都相同,后面非数字部分用“*”来填充
    mqttpublish[j + 9] = 0x2a;                   
  }
 //如果是刚进入透传模式
  if (sndi == 0)                              
  {
    Serial.print("send mqttlink");
//发送mqtt的连接指令,由于是16进制发送,这里需要用到write
    Serial1.write(mqttlink, 27);               
    sndi = 1;
  }
  if (tm > 10000)                            //每次计时到10秒时
  { 
    Serial1.write(mqttheart, 2);             //发送心跳指令
    timebefor = millis();
  }
  if (mi > 0)                             //如果收到了mqtt服务器返回的数据
  {
    mi = 0;
//方便起见,只判断返回数据的第一位
    if (mqttdata[0] == 0x20 || mqttdata[0] == 0xd0)     
    {
      Serial1.write(mqttpublish, 13);         //发布数据
    }
 //由于用了QoS=01,所以mqtt服务器会返回数据
    else if (mqttdata[0] == 0x40)               
    {
      timebefor = millis();                     //收到数据后重新计时
    }
  }
}
//*****************esp8266AT指令**********************
void esp8266at(int eat)
{
  switch (eat) {
    case 0: {
        Serial1.println("AT");            //测试模块是否正常
      } break;
    case 1: {
        Serial1.println("AT+CWMODE=1");    //设置模块模式
      } break;
    case 2: {
        Serial1.println(wifilinkstr);             //连接WIFI路由器
      } break;
    case 3: {
        Serial1.println("AT");     //查询
      } break;
    case 4: {
        Serial1.println("AT+CIPMUX=0");   //设置单连接
      } break;
    case 5: {
        Serial1.println("AT+CIPMODE=1");   //设置传输模式为透传
      } break;
    case 6: {
        Serial1.println(tcplinkstr);         //连接tcp服务器
      } break;
    case 7: {
        Serial1.println("AT+CIPSEND");         //进入tcp透传模式
      } break;
  }
}
//*******************字符转换为asscii码,用于mqtt数据发送*****
unsigned char chartobyte(String chr) {
  if (chr == "#")
    return 0x23;
  else if (chr == ":")
    return 0x3a;
  else if (chr == "$")
    return 0x24;
  else if (chr == "0")
    return 0x30;
  else if (chr == "1")
    return 0x31;
  else if (chr == "2")
    return 0x32;
  else if (chr == "3")
    return 0x33;
  else if (chr == "4")
    return 0x34;
  else if (chr == "5")
    return 0x35;
  else if (chr == "6")
    return 0x36;
  else if (chr == "7")
    return 0x37;
  else if (chr == "8")
    return 0x38;
  else if (chr == "9")
    return 0x39;
  else if (chr == "@")
    return 0x40;
}

将程序写入Arduino,然后打开emqx的websocket页面,连接上后订阅一个主题为“r“的消息,然后将Arduino断电再通电,就可以在页面上观察来自Arduino的消息了,当然你也可以在手机端订阅,同样可以收到消息。

由于没有使用任何的库函数,所以程序看起来有点长,不过各位同学正好可以了解清楚程序运行的全部过程。如果你使用了Arduino UNO模块的话,可以将程序中的Serial1全部改成Serial就可以了,当然还得把原来的Serial全部删除。

好了,这就是Arduino和ESP8266通过Mqtt服务器自动上传数据的终端示例,你可以接入自己的传感器,上传自己的数据。

现在虽然有了上传数据的终端,可是数据并没有保存起来。并且需要开启一台电脑来作为Mqtt服务器,也有点浪费,下一节我会来介绍如何使用一台树莓派来作为Mqtt服务器,再之后我们用Python编写一个小程序来将Arduino上传的数据存储到Mysql数据库里面,这样一个自动采集数据的物联网项目才能算完整!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零开始开发物联网项目(6)——Arduino和ESP8266自动数据上传终端 的相关文章

  • ADXL345 与 ESP32 I2C 垃圾值问题

    我已根据以下教程使用 I2C 接口将 ESP32 与 ADXL345 连接起来Tutorial http www esp32learning com code esp32 and adxl345 sensor example php 但是
  • 如何返回变量?

    我正在编写一个程序 将字母翻译成摩尔斯电码 然后将它们传输到 LED 并闪烁 我无法返回值 include
  • 递增数组值 - Arduino

    我正在尝试增加一些数组值 int counter 0 0 0 0 0 0 0 0 如果位置 0 的数字值达到 25 则位置 1 的值加 1 位置 0 重置为 0 依此类推 当索引位置 2 达到 25 时 位置 3 增加 1 并重置它的值 自
  • Windows IoT 和 DS3231 RTC 时钟

    对于我的项目 我需要当前时间和日期 不幸的是 当 RP2 关闭时 它就会失去一切 接下来的事情是 我将没有互联网连接来使用 NTP 为此 我需要实现 DS3231 RTC 模块 所有设备的通信都通过 I2C 运行 Raspberry Ard
  • 如何使用 PHP 读取串口 [重复]

    这个问题在这里已经有答案了 如何使用 php 从连接到我的服务器 运行 kali linux 的旧笔记本电脑 上的 COM 端口的 arduino 读取串行数据 以便我可以在网页上显示数据 我读过有关同一问题的其他问题 所有这些问题要么超级
  • C++ 递减单字节(易失性)数组的元素不是原子的!为什么? (另外:如何在 Atmel AVR mcus/Arduino 中强制原子性)

    由于试图通过一些我不知道的简单内容来调试我的代码 我损失了几天的时间 实际上是大约 25 小时的工作 事实证明 在 AVR ATmega328 8 位微控制器 Arduino 上 用 C 递减单字节数组的元素不是原子操作 并且需要原子访问防
  • Mac OS 10.9 不显示 Arduino 的 USB 调制解调器

    我正在尝试选择 dev tty usbmodem on my Arduino Lenardo设备 操作系统是Mac OSX 10 9 问题是它没有显示 我什至尝试安装FTDI http www ftdichip com Drivers VC
  • 读取串行端口 - 忽略在一定时间内写入串行端口的部分数据

    我想定期读取串行端口上的数据和Arduino 所以本质上是这样的 读一读 Wait 读一读 Wait Take etc 我面临的问题是端口将缓冲其信息 因此一旦我调用等待函数 串行端口上的数据就会开始缓冲 等待函数完成后 我尝试再次读取数据
  • Android 和 Arduino 都通过蓝牙接收奇怪的值

    我正在 Android 端使用 BluetoothChat 代码使用聊天框向 Arduino UNO 发送开 关信号 我发送 110 119 或下面定义的其他情况 但是当我显示接收到的值时 它显示奇怪的Arduino 串行监视器中的值 即
  • 如何在不连续重置的情况下将esp8266从深度睡眠中唤醒

    我正在使用 esp8266 构建 IR 到 wifi 桥接器 基本上 我正在构建一个连接到红外遥控器内的 esp8266 的红外接收器 以通过 wifi 将接收到的红外远程按键转发到服务器 我使用的是 Wemos D1 Mini 我已经让
  • 静态 uint8_t 数组的输入过程和类型

    我目前正在尝试将 Arduino IDE 中的整数变量转换为静态 uint8 t 数组的值 我在用 include
  • C++ Linux (Ubuntu) 正确写入串行(对于 Arduino)

    我想知道是否有一种标准方法可以与高效的串行设备进行通信 我应该使用标准库吗 如果有 是哪一个 现在我正在摆弄让 LED 根据输入的数字以给定的量亮起 Arduino 代码如下 只是练习一些东西 看我过于简单且低效的测试 include
  • C-如何使用PROGMEM存储和读取char数组

    我有三个字符数组 我不希望 Arduino 将它们存储在SRAM http en wikipedia org wiki Static random access memory 所以我想使用PROGMEM来存储和读入flash http en
  • Arduino从SD卡读取最后一行

    我对 Arduino 业务还很陌生 如何从 SD 卡读取最后一行 通过以下代码片段 我可以读取第一行 n 之前的所有字符 现在我想添加一个 向后 声明 或其他内容 到目前为止我的代码 include
  • C++:将成员函数作为普通函数指针传递的闭包

    我正在尝试调用外部库的成员函数 该函数接受函数指针作为参数 Timer every unsigned long period void callback void 但不幸的是我想传递的参数是一个成员函数 void MyClass the m
  • 终止导致设备或资源繁忙的进程:“/dev/ttyUSB0”?

    我使用以下 Python 代码连接到我的 Arduino 板 device glob glob dev ttyUSB 0 time sleep 1 arduino serial Serial device 115200 timeout 5
  • 是否有通用 I2C 命令来查看设备是否仍然存在于总线上?

    是否有通用的 I2C 命令来查看设备在初始化一次后是否仍然存在于总线上 例如 OLED 显示器 我问这个的原因是为了避免主程序由于库代码中存在无限循环而冻结 当设备断开连接时 例如 Wire 库 在 MCU 启动时 我想检查设备是否可用 并
  • HM10 ble改变特征值AT命令Arduino

    谁能帮我用AT命令写入特征值 或者如何使用Hm10模块将数据从arduino发送到另一个ble设备 HM10发送AT START后 会通告数据包 并且可以检测服务和特征 但特征值是默认的0x00 如何更改 多次检查数据表 但找不到能够执行相
  • 是否可以在 Arduino 上运行 ZeroMQ

    我想知道是否可以通过 ZeroMQ 与 Arduino 进行通信 我想使用 Arduino 来控制一些设备 并希望将大部分应用程序放在计算机上 为此 如果 Arduino 和控制器可以通过 Zeromq 以及 WiFi 或以太网扩展板进行通
  • 编译器:如果条件始终为真/假怎么办

    我想到了条件和编译器 我正在为 Arduino 编写一个应用程序 因此我需要该应用程序尽可能快 在我的代码中我有这个 define DEBUG false if DEBUG String pinName pinName Pin pinNam

随机推荐

  • Matlab转C++代码入门————附带详细代码和示例

    文章目录 1 转换1 1 转换例子1 2 详细转换过程 2 注意事项3 实例 1 转换 一般Matlab中的代码 xff0c 有一部分方法是无法转换成C 43 43 的 xff0c 例如图像化UI 文件写出等接口 一般做转换的话 xff0c
  • ‘CMRESHandler‘ object has no attribute ‘_timer‘,socket.gaierror: [Errno 8] nodename nor servname pro

    文章目录 1 错误重现2 解决3 调用方法写入es4 扩展 1 错误重现 在Mac上调用Python的CMRESHandler进行elasticsearch的日志写入时 xff0c 遇到如下错误 一开始还以为是自己的语法出现了错误 xff0
  • Python写入日志到Elasticsearch(logging/loguru,可个性化定制写入信息)————附带详细代码和示例

    文章目录 0 结果1 准备2 调用2 1 调用方法1 xff08 使用logging xff09 2 2 调用方法2 xff08 使用logging xff0c 并使用配置 xff09 2 3 调用方法3 xff08 使用loguru xf
  • C++改写Matlab源码实践一之【显示图片】——————附带详细讲解和示例

    文章目录 0 背景1 Matlab有用代码提取2 转换为C 43 43 3 使用纯C 43 43 实现mat数据中的图片显示4 代码封装5 福利其他 0 背景 这里以显示Matlab变量struct中的图片程序代码为例 xff0c 示范如何
  • C++/Java调用C++动态链接库————附带示例和详细讲解

    文章目录 0 准备1 C 43 43 调用动态链接库2 Java调用C 43 43 动态链接库3 运行 0 准备 在CMake中 xff0c 使用如下的方法把代码编译成动态 静态链接库 xff1a span class token comm
  • 什么变量存放在栈和堆

    内容会持续更新 xff0c 有错误的地方欢迎指正 xff0c 谢谢 什么是栈区 xff0c 什么是堆区 栈区 xff08 stack xff09 xff1a 由编译器自动分配释放 xff0c 存放函数的参数值 局部变量的值等 xff0c 内
  • C++改写Matlab源码实践二之【传递表格数据】————附带详细讲解和示例

    文章目录 0 背景1 Matlab有用代码提取2 转换为C 43 43 2 1 设定变量类型2 2 预先和定义声明变量 3 改写转换后的C 43 43 代码3 1 指定输入的文件3 2 改写处理 96 Data 96 和 96 Data1
  • Kylin4.0下载安装指南———附带详细安装过程

    文章目录 0 结果1 下载1 1 下载kylin1 2 下载spark1 3 jdk hadoop hive zookeeper安装1 4 Mysql安装 2 配置Mysql数据存储2 1 配置mysql2 2 配置Zookeeper2 3
  • 2023年的专业408算法题

    文章目录 0 结果1 题目2 思路3 实现附录 0 结果 1 题目 对于一个有向图 xff0c 如果一个顶点的出度大于入度 xff0c 则这个顶点称为K顶点 有向图用邻接矩阵存储 xff0c 数据结构定义如下 xff1a span clas
  • 专业408历年算题大全(2009~2023年)——附带详细代码和多种思路

    文章目录 1 题型总结1 1 线性表 xff08 6年 xff09 1 1 1 常用思想 1 2 链表 xff08 4年 xff09 1 2 1 常用思想 1 3 树 xff08 3年 xff09 1 3 1 常用思想 1 4 图 xff0
  • 2022年专业408算法题

    文章目录 0 结果1 题目2 思路3 实现3 1 思路一 xff08 中序遍历搜查搜索数 xff09 3 2 思路二 xff08 二叉搜索树的定义 xff09 附录 0 结果 1 题目 2 思路 1 xff0c 首先明确二叉搜索树的定义 x
  • 2021年专业408算法题

    文章目录 0 结果1 题目2 思路3 实现附录 0 结果 1 题目 2 思路 遍历邻接矩阵的每一行和列的非零元素的个数 xff0c 来统计每个顶点的度 xff08 出度和入度和 xff09 xff0c 并记录度为奇数的顶点个数 xff0c
  • jetbrains软件打开后突然闪退

    文章目录 0 背景1 错误重现2 错误原因3 解决方法 0 背景 在使用jetbrains编程软件时 xff0c 突然出现打开就闪退的情况 一开始还以为是系统升级到macOS13 1的原因 xff0c 经过查阅网上资料 xff0c 发现ma
  • 2011年专业408算法题

    文章目录 0 结果1 题目2 思路2 1 思路1 xff08 暴力解 xff1a 排序 xff09 2 2 思路2 xff08 较优解 xff1a 归并合并数组 xff09 2 3 思路3 xff08 较优解 xff1a 数组指针后移 xf
  • 2014年408专业算法题

    文章目录 0 结果1 题目2 思路附录 0 结果 1 题目 2 思路 二叉树的带权路径长度 xff08 WPL xff09 的计算方法有两种 xff1a 1 xff0c 定义 xff1a W P L 61
  • 2016年专业408算法题

    文章目录 0 结果1 题目2 思路2 1 思路1 xff08 较优解 xff1a 排序 xff09 2 2 思路2 xff08 最优解 xff1a 类快排思想排序 xff09 附录 0 结果 较优解 xff1a 最优解 xff1a 1 题目
  • 2017年408专业算法题

    文章目录 0 结果1 题目2 思路附录 0 结果 1 题目 2 思路 因为要转换为中序表达式 xff0c 因此使用中序遍历 在中序遍历的过程中 xff0c 对于当前访问的非空结点p xff0c 则先输出 34 xff0c 然后递归调用左子树
  • Python面向对象编程

    文章目录 1 作用域1 1 局部作用域 2 类成员权限3 是否继承新式类4 多重继承5 虚拟子类6 内省 在运行时确定对象类型的能力 7 函数参数8 生成器 1 作用域 1 1 局部作用域 1 xff0c 当局部变量遮盖全局变量 xff0c
  • 增大整数———晴问算法

    文章目录 1 题目2 思路3 代码 1 题目 2 思路 首先把数字n转化为字符串s xff0c 然后把第一个字符转换为数字得到正整数的首位 如果输入的数位a大于首位 xff0c 则把字符串s拼接在字符串化的数位后面形成新字符串ans xff
  • 从零开始开发物联网项目(6)——Arduino和ESP8266自动数据上传终端

    前面几节介绍了Mqtt协议以及用ESP8266模块作为客户端连接Mqtt服务器并实现数据的发布和订阅 这一节我们就正式的开始制作第一个物联网终端 xff0c 基于Arduino和ESP8266模块 之所以选择了Arduino是因为它的开发比