Arduino 代码拆分

在项目中,有时我们需要很多的代码。如果将这些代码全部都存放于一个文件中,那么该文件将会过长,因此我们可以将 Arduino 代码进行拆分,存放于多个文件中。而一些可能以后会服复用到的文件,也可以直接将文件粘贴过来,而不需要修改,就可以使用。

比如,我们在使用 Esp8266 系列的时候,经常需要将 Esp8266 联网,而在以前,我们是将 WiFi 信息在代码中写死的。如果想要将 WiFi 设置为配置式,可参考 https://www.jianshu.com/p/5dc724e39fd1 这篇文章。但是这里代码很长,如果我们在这里进行修改,以后再其他项目中还需要进行修改。所以我们可以将配置 WiFi 这一功能,做成一个通用的模块,这样我们就可以将其通用化,以后随用随取,不必另行修改。

比如我们一个很简单的 Blink 代码:

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

总共不超过 10 行,而配置 WiFi 的代码就将近 300 行,将两者合并到一个文件中,不仅主业务流程读起来不清晰,还不方便进行修改。

而我们可以将文章中配置 WiFi 的代码稍作修改,存储于一个独立的文件中,以后不管哪个项目中需要使用,只需要将此文件拷贝到相应的项目文件夹中就可以使用了。
WiFi 配置代码修改如下:

//声明
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <ESP8266mDNS.h>

int EPPROM_Address = 512;

// WiFi,可以写入在这里预先输入自己最常用的 WiFi 信息,也可以设置为空字符串
String WiFi_SSID = "";
String WiFi_Password = "";

//Ap 设置热点信息
const char* AP_Host = "esp8266";
const char* AP_SSID = "Esp8266AP";
const char* AP_Password = "12345678";

// web
String webContent;
int webStatusCode;
String wifiScanContent;

ESP8266WebServer WebServer(80);

void initWiFi() {
  EEPROM.begin(EPPROM_Address);
  readWiFiInfoFromEEPROM();

  bool connectStatus = tryToConnectToWiFi();
  if (connectStatus)
  {
    createConnectedWeb();
  }
  else
  {
    ScanNetwork();
    SetupToAPMode();
    connectStatus = tryToConnectToWiFi();
  }

  WebServer.begin();
  delay(100);
}


void wifiTask() {
  WebServer.handleClient();
}


/********* 从存储器中读取 WiFi 信息 ***************/
void readWiFiInfoFromEEPROM()
{
  //--- 从 EEPROM 中读取 WiFi 的 SSID 和密码

  //--- 读取 wifi ssid
  WiFi_SSID = "";
  for (int i = 0; i < 32; ++i)
  {
    WiFi_SSID += char(EEPROM.read(i));
  }
  delay(100);

  //--- 读取 wifi 密码
  WiFi_Password = "";
  for (int i = 32; i < 96; ++i)
  {
    WiFi_Password += char(EEPROM.read(i));
  }
  delay(100);

}

/********* 尝试连接 WiFi ***************/
bool tryToConnectToWiFi()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WiFi_SSID.c_str(), WiFi_Password.c_str());
  delay(100);

  // WiFi 连接需要时间,因此这里相当于持续检测 10 秒
  int times = 0;
  while ( times < 10 ) {
    if (WiFi.status() == WL_CONNECTED) {
      return true;
    }
    delay(1000);
    times++;
  }
  return false;
}

/********* 设置为 AP 模式 ***************/
void SetupToAPMode() {
  WiFi.mode(WIFI_AP);
  WiFi.softAP(AP_SSID, AP_Password, 6, 0);
  createApModeWeb();

  //貌似 dns 不生效
  if (MDNS.begin(AP_Host))
  {
    MDNS.addService("http", "tcp", 80);
  }
}

/********* 创建 AP 模式网页 ***************/
void createApModeWeb()
{
  //加载网页
  WebServer.on("/", []() {
    IPAddress ip = WiFi.softAPIP();
    webContent = "<!DOCTYPE HTML>";
    webContent += "<html>";
    webContent += "<title>WiFi 信息设置</title>";
    webContent += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
    webContent +=  "<p>请输入 WiFi 信息</p>";
    webContent += wifiScanContent;
    //&nbsp;代表插入一个空格, 重复5个就是插入5个空格
    webContent += "<br>";
    webContent += "<form method='get' action='setting'>";
    webContent += "<table border=\"0\"><tr><td><label>SSID</label></td><td><input type=\"text\" placeholder=\"请输入你要连接的 SSID\" name='ssid' maxlength=32 size=64></td></tr>";
    webContent += "<tr><td><label>PASSWORD</label></td><td><input type=\"text\" placeholder=\"请输入该 WiFi 密码\" name='pass' maxlength=64 size=64></td></tr></table>";
    //    webContent += "<input type=\"button\" value=\"重新扫描网络\" onclick=\"self.location.href='/rescannetwork'\">";
    webContent += "&nbsp;&nbsp;&nbsp;<input type='reset' value=\"重设\">&nbsp;&nbsp;&nbsp;<input type='submit' value=\"存储\"></form></html>";
    WebServer.send(200, "text/html", webContent);  //200代表服务器状态码为 OK, text/html代表用html网页类型, 不加这个会找不到网页
  });

  WebServer.on("/setting", []() {
    WiFi_SSID = WebServer.arg("ssid");
    WiFi_Password = WebServer.arg("pass");
    if (WiFi_SSID.length() > 0 && WiFi_Password.length() > 0)
    {
      if (WiFi_SSID.length() <= 32 && WiFi_Password.length() <= 64)
      {
        for (int i = 0; i < 96; ++i) {
          EEPROM.write(i, 0);
        }

        for (int i = 0; i < WiFi_SSID.length(); ++i)
        {
          EEPROM.write(i, WiFi_SSID[i]);
        }

        for (int i = 0; i < WiFi_Password.length(); ++i)
        {
          EEPROM.write(32 + i, WiFi_Password[i]);
        }
        EEPROM.commit(); //EEPROM.write 并不会马上写入 EEPROM, 而是要执行 EEPROM.commit()才会实际的写入EEPROM
        delay(50);

        webContent = "存储成功, 请按 RESET 键重新开机!";
        webStatusCode = 200;
      }
      else {
        webContent = "<p>输入错误!!! SSID 允许的最大长度为 32,PASSWORD 允许的最大长度为 64, 按上一页重新输入</p>";
        webContent += "<input type=\"button\" value=\"上一页\" onclick=\"self.location.href='/'\"></html>";
        webStatusCode = 404;
        }
    }
    else {
      webContent = "<p>输入错误!!! SSID 和 PASSWORD 都不允许为空, 按上一页重新输入</p>";
      webContent += "<input type=\"button\" value=\"上一页\" onclick=\"self.location.href='/'\"></html>";
      webStatusCode = 404;
    }
    WebServer.send(webStatusCode, "text/html", webContent);
  });
}

/********* 扫描无线网络 ***************/
void ScanNetwork() {
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  delay(100);
  int wifiCount = WiFi.scanNetworks();

  // 将 WiFi 信息构造成 html 格式
  wifiScanContent = "<ol type=\"1\" start=\"1\">";
  for (int i = 0; i < wifiCount; ++i)
  {
    // Print SSID and RSSI for each network found
    wifiScanContent += "<table border=\"0\"><tr><td width=\"300px\">";
    wifiScanContent += String(i + 1) + ". ";
    wifiScanContent += WiFi.SSID(i);
    wifiScanContent += " (";
    wifiScanContent += WiFi.RSSI(i);
    wifiScanContent += ")";
    wifiScanContent += "</td><td width=\"200px\">";

    byte encryption = WiFi.encryptionType(i);
    wifiScanContent += getEncryptionTypeString(encryption);
    wifiScanContent += "</td></tr>";
  }
  wifiScanContent += "</ol></table><br>";
}

String getEncryptionTypeString(byte type)
{
  String str = "";
  switch (type) {
    case 2: str = "TKIP(WPA)"; break;
    case 5: str = "WEP"; break;
    case 4: str = "CCMP(WPA)"; break;
    case 7: str = "NONE"; break;
    case 8: str = "AUTO(WPA or WPA2)"; break;
    default:
      str = "";
  }
  return str;
}

void createConnectedWeb()
{
  WebServer.on("/", []() {
    webContent = "<!DOCTYPE HTML>";
    webContent += "<html>";
    webContent += "<title>WiFi 信息</title>";
    webContent += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
    webContent +=  "<p>当前 WiFi 信息</p>";
    //    webContent += wifiScanContent;
    //&nbsp;代表插入一个空格, 重复5个就是插入5个空格
    webContent += "<br>";

    webContent += "<table border=\"0\"><tr><td><label>当前 SSID: </label></td>";
    webContent += "<td><label>" + WiFi_SSID + "</label></td></tr>";
    webContent += "<tr><td><label>当前 IP 地址: </label></td>";
    webContent += "<td><label>" +  WiFi.localIP().toString() + "</label></td></tr></table>";
    webContent += "</html>";
    WebServer.send(200, "text/html", webContent);
  });
}

**BLink 程序修改如下

void setup() {
  //初始化 WiFi
  initWiFi();

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  wifiTask();

  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

只需要在 setup() 函数中添加一行 initWiFi() ,在 Loop 中添加一行 wifiTask() 即可。

将上述 WiFi 文件复制到 Blink 项目文件夹内,重新打开 Blink 项目,即可加载。


项目结构
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容