65.9K
CodeProject 正在变化。 阅读更多。
Home

利用 Arduino Mega 2560+WiFi R3 中的 ESP8266

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2020 年 11 月 3 日

MIT

9分钟阅读

viewsIcon

23853

downloadIcon

322

通过一种巧妙的配置板的方法来加速您的设备

引言

免责声明:此过程将覆盖此板上 WiFi 模块的出厂固件。一旦覆盖,将无法轻易恢复到出厂状态,并且用于与该模块通信的默认库“WiFiEsp”将不再与其一起工作。我还读到,但尚未验证,一些廉价制造的 ESP-01 只能刷新有限次数,因此存在一定风险,但我认为这不一定适用于这些 Arduino 及其衍生板。

Arduino

最近,我在这里 发布了一篇关于 Arduino Mega 2560+WiFi R3 入门的简短文章。在那篇文章中,我暗示了板载 WiFi 拥有自己的 CPU,并且该 CPU 的功能比 Arduino 板上的主 ATMega2560 CPU 强大得多。事实上,它非常强大,即使在处理 TCP/IP 堆栈时,仍有足够的处理周期来超越 Mega。

在本文中,我旨在向您展示如何利用更强大的 CPU,并将 I/O 任务委托给功能较弱的 CPU。

此项目包含 Arduino IDE 项目的 .ino 文件,用于 ESP8266 演示代码和 ATMega2560 演示代码。两者都必须上传到开发板,因为它们协同工作。该项目还包含一个 C# 实用程序,用于将 HTML 文件转换为 C 风格字符串。这有助于编辑 Web 服务器代码。

概念化这个混乱的局面

计划

在这块特定的 Arduino 板上,有两个 CPU。在典型配置中,8 位 ATMega2560 CPU 以 16MHz 运行,作为主 CPU。它通过串行 UART 与 ESP8266 通信,ESP8266 包含其自己的 32 位 Tensilica XDS 106Micro,运行频率为 80 MHz。显然,后者在各个方面都优于前者。如果我们在此 CPU 上运行代码,理论上其执行速度将比在另一个 CPU 上快。

将 106Micro CPU 用作主处理器的主要限制是,它唯一的 I/O 是一个串行 UART,以及对 WiFi 和 TCP/IP 堆栈的访问。这意味着它与另一个 CPU 以及 Arduino 板其余部分通信的唯一方式是通过单个串行连接。

现在,如果您的应用程序进行大量的 WiFi 和 CPU 计算,但对于与板上所有其他 I/O 进行通信,最高可承受 115200 波特率,那么这种配置将非常适合您。

主要缺点是您需要编写额外的代码,并且现在需要刷新 ESP8266 和主 Arduino。

另一个缺点是您的代码会变得更复杂。

在这里,ESP8266 负责处理所有互联网事务以及任何需要发生的主计算,如果需要访问其他 I/O,则必须通过其硬件串行连接与 ATMega2560 通信。ATMega2560 再将串行数据用于读取或操作它控制的所有 I/O,并在必要时向 106Micro CPU 报告。

高级系统也可以让 ATMega2560 进行一些协处理,特别是如果需要处理大量 I/O。

这使得编码变得复杂,因为这意味着您必须创建一个串行协议来在两个 CPU 之间进行双向通信。

另一方面,这意味着您 8 位 16MHz 的 CPU 只负责 I/O,而您的 32 位 80MHz 的 CPU 现在可以执行更繁重的工作。

设置 Arduino IDE

为了实现这一目标,我们必须将 Arduino IDE 设置为使用此配置。首先要做的是通过转到 **文件 | 首选项...** 来添加 ESP8266“开发板管理器”,然后找到“附加开发板管理器 URL”并逐字添加 URL http://arduino.esp8266.com/stable/package_esp8266com_index.json。如果其中已经有其他 URL,可以用逗号分隔它们。

这将在 **工具** 菜单下添加一些内容,但这还无关紧要——我们稍后会讲到。

如何处理编码 - 开发周期

开发周期由于需要根据您正在编码的 CPU 来翻转 DIP 开关而变得有些复杂。最好先设计您的串行协议,然后编写可能更简单的 ATMega2560 端代码,然后再处理 106Micro 端。您能做的任何限制双向操作的事情都有帮助,因为它减少了您需要玩 DIP 开关的次数。

针对 ATMega2560 部分进行编码(部署配置)

(这也是设备部署时使用的配置。)

  1. 确保您的 DIP 开关设置如下:1-4 为 ON,5-8 为 OFF。
  2. 确保 TXD0/TXD3 开关设置为 TXD3
  3. 确保在 Arduino IDE 的 **工具 | 开发板:** 下显示“Arduino Mega 或 Mega 2560”,在 **工具 | 处理器:** 下显示“ATmega2560 (AT Mega)”
  4. 仔细检查以上设置!

在您的草图中,主处理器通过 Serial3 连接到此处理器。USB COM 端口是 Serial

您需要从 Serial3 读取和写入数据,根据您已经设计好的协议设置 I/O 引脚或路由到 Serial,甚至可能包括 Serial1Serial2。您 *已经* 设计好您的协议了,对吗?

这是此处理器的工作,除非您还有协处理任务。

请注意,在上述状态下上传代码时,开发板将重置并自动运行新代码。以这种方式进行编码应该您很熟悉。

针对 XDS 106Micro 进行编码

  1. 确保您的 DIP 开关设置如下:1-4 和 8 为 OFF,5-7 为 ON。
  2. 确保在 Arduino IDE 的 **工具 | 开发板:** 下显示“Generic ESP8266 Module”
  3. 确保 **工具 | 闪存大小:** 设置为“4 MB (FS: 2MB OTA:~1019KB)”
  4. 仔细检查它!

代码可以上传到 XDS 106Micro,但在该模式下不会运行。有两种方法可以运行代码。要么将其切换回前面列出的部署配置,要么将 DIP 开关设置如下:1-4 和 7-8 为 OFF,5-6 为 ON。这将把 USB COM 端口直接桥接到 XDS 106Micro 的 UART,这对于从中获取调试输出很有用。

显然,针对此 CPU 进行编码会稍微复杂一些,因为您必须翻转 DIP 开关才能使其运行。不幸的是,这只是设计的一部分。

测试整个系统(两个 CPU 同时工作)的唯一方法是将其置于部署配置。

编写这个混乱的程序

CPU 之间的通信

您首先需要做的就是开发一个协议,以便在 ATmega2560 和 XDS 106Micro 之间以 115200 波特率通过串行进行通信。

让我们考虑一个场景,我们想从 XDS 106Micro 处理器读取或写入 I/O 引脚。我们会这样做:

Serial.write(op_write); // op_read or op_write
Serial.write(pin); 
Serial.write(value);

然后,如果操作码是 op_read

Serial.write(op_read);
Serial.write(pin);
while(!Serial.available());
byte value = (byte)Serial.read(); 

您还需要为其他操作添加更多的操作码,但它们是什么取决于您做什么。上面的例子只是一个示例,并且可能比您在实际应用程序中使用的要低级别,是逐个引脚操作。

上面使用的是 Serial,因为那段代码是为 XDS 106Micro 处理器及其单个串行端口编写的。

在另一端——ATmega2560 端,我们将使用 Serial3 作为此链路的另一端,因为它们是硬连线连接的。

另一个主要的问题是使用 ESP8266 的 WiFi 功能。幸运的是, there's the ESP8266WiFi library,它专门设计用于我们这种情况,即我们在 ESP8266 的 CPU 本身上运行。它非常易于使用,入门信息 在这里

主 ESP8266

在此项目中,有一个简单的 Web 服务器,设计运行在 ESP8266 上。它允许您读取板上的 3 个引脚,并通过一系列复选框设置或清除 LED 灯。

我厚颜无耻地从 **文件 | 示例** 下的一个内置示例中复制代码,并为演示目的进行了修改。

Web 服务器将在 http://esp8266.local 上广播,如果您的网络支持 mDNS(组播 DNS),否则 IP 地址将通过 COM 端口报告。

基本上,它所做的就是连接到给定的网络(通过 SSID),然后设置 Web 服务器和组播域(如果您的网络支持),以便为您提供一个表单。该表单显示 Arduino 板上的引脚以及主 LED。您可以设置 LED,但无法读取它。您可以读取其他引脚,但它们无法写入——至少默认情况下是这样。您可以编辑代码轻松更改这一点。

这是主处理程序代码的略微缩减版

void handleRoot() {
  // if the form parameters are specified
  // set them high, otherwise set them low
  // note we do this for all pins, not just
  // the LED. That's okay, it just doesn't
  // actually do anything for those pins
  String sza = server.arg("Pin1");
  if(0!=sza && 0!=sza[0])
    writePin(2,HIGH);
  else
    writePin(2,LOW);
  sza = server.arg("Pin2");
  if(0!=sza && 0!=sza[0])
    writePin(3,HIGH);
  else
    writePin(3,LOW);
  sza = server.arg("Pin3");
  if(0!=sza && 0!=sza[0])
    writePin(4,HIGH);
  else
    writePin(4,LOW);
  sza = server.arg("PinLED");
  if(0!=sza && 0!=sza[0])
    writePin(13,HIGH);
  else
    writePin(13,LOW);
  char sz[1024];
  sz[0]=0;
  // prepare the checked=\"checked\"
  // attribute for the relevant
  // checkboxes
  char su[] = "";
  char sc[] = " checked=\"checked\"";
  char* s1=(0!=readPin(2))?sc:su;
  char* s2=(0!=readPin(3))?sc:su;
  char* s3=(0!=readPin(4))?sc:su;
  char* s4=(0!=readPin(13))?sc:su;
  // omitted part of the string
  // below for display
  // don't copy/paste this code!
  sprintf(sz,"<!DOCTYPE html>\r\n\r\n<html...",s1,s2,s3,s4);
  server.send(200, "text/html", sz);
}

通信协议

我们的协议简单方便。它将 ESP8266 的所有串行数据转发到 ATMega2560。当 ATMega2560 CPU 接收到数据时,它会将其转发到主串行端口。例外情况是,如果它接收到值为 254 或 255 的字节。如果接收到前者,则表示要读取引脚的代码。紧随其后的是一个包含引脚 ID 的字节。如果接收到 255,则表示要写入引脚的信号。随后的字节包括一个字节用于引脚 ID,下一个字节用于值。这种读写操作基本上与前面两个图所示的代码相同。它仅适用于数字引脚,没有办法从服务器设置引脚方向,也无法使用此实现处理模拟引脚或其他串行 UART。您需要为这些情况创建更复杂的解决方案。

以下是 ESP8266 Web 服务器代码用于向从设备发送 I/O 命令的两个方法

int readPin(int pin) {
  Serial.write((byte)254);
  Serial.write((byte)pin);
  while(0==Serial.available());
  int ret = Serial.read();
  return ret;
}
int writePin(int pin,int value) {
  Serial.write((byte)255);
  Serial.write((byte)pin);
  Serial.write((byte)value);
}

这些方法很简单。反之亦然,在后面的从设备代码中有所体现,我们将在接下来介绍。

从设备 ATMega2560

此 CPU 仅负责转发串行输出或设置和获取数字 I/O 引脚值。此代码仅执行此操作。与之前一样,它转发除字节 254 或 255 之外的所有内容,这些字节是用于读取和写入引脚的转义符。这是代码

void setup() {
  // initialize the serial ports
  Serial.begin(115200);
  Serial3.begin(115200);
}
void loop() {
  int pin;
  // wait for data
  while(0==Serial3.available());
  // get the next byte
  int b = (byte)Serial3.read();
  
  if(254==b) { // read
    // wait for data
    while(0==Serial3.available());
    // read the pin id
    pin = Serial3.read();
    // read the pin
    int val = digitalRead(pin);
    // write the result back to the other CPU
    Serial3.write((byte)(0==val?0:1));
  } else if(255==b) { // write 
    // wait for data
    while(0==Serial3.available());
    // read the pin id
    pin = Serial3.read();
    // wait for data
    while(0==Serial3.available());
    // read the value to set
    int val = Serial3.read();
    // set the pin
    digitalWrite(pin,0!=val?HIGH:LOW);
  } else // simply forward
    Serial.write((byte)b);
  return;
}

关注点

现在您可以获得的强大的 ESP32 板,其出现很大程度上归功于爱好者对 ESP8266 的黑客行为,包括上述项目。虽然这里使用的开发板和这项技术有些过时,但它们将帮助您获得有关这些设备编码的背景知识。

历史

  • 2020 年 11 月 2 日 - 首次提交
© . All rights reserved.