【问题标题】:Is there any way to have an Arduino Server send an existing .html file to the Arduino client?有没有办法让 Arduino 服务器将现有的 .html 文件发送到 Arduino 客户端?
【发布时间】:2019-01-25 03:53:48
【问题描述】:

我正在为 Arduino 构建一个单页应用程序。它以图形方式在连接 wifi 的平板电脑上显示模拟引脚值。

我已经制作了草图,但想清理它。我已经能够将草图上传到我的(Uno Wifi Rev 2)Arduino,初始化 Wifi,并用平板电脑连接到它。我可以将静态页面“框架”发送到平板电脑。 该静态框架能够使用 XMLHttpRequest 对象请求和接收 Arduino 模拟引脚值。

但是发送庞大的静态页面很笨重。教程可以做类似的事情,

client.println("<html><body>");
client.println("Hello World!");
client.println("</body></html>");

我试图弄巧成拙并创建一个 FileText.h 头文件:

#define constFileText=
"<html><body>"
"Hello World!"
"</body></html>";

并将其与:

#include "FileText.h"
client.println(constFileText);

我想做的是创建一个标准的 FileText.html:

你好世界!

然后用类似的东西处理它:

ifstream hFile ("FileText.html");
while (getline(hFile, strLine))
  client.println(strLine);

这将使编辑 html 文件变得更加容易。它将消除包含所有这些 serial.println 调用的浪费。它还将消除对常量值的最大长度限制。

有什么方法可以向 Arduino 编译器提供一个文本文件,并让 Arduino 服务器将其发送给 Arduino 的客户端?

【问题讨论】:

  • 您的 Arduino 上是否连接了 SD 卡?然后你可以把你的 HTML 文件放在那里。否则您可以尝试将其存储在 EEPROM 中(Atmega328p 有 1KB EEPROM)
  • 不,我没有使用 SD 卡。但即使使用 SD 卡,将文件发送给客户端仍然存在挑战。 EEPROM 不工作;我计划将大部分功能放在浏览器中。我希望 Arduino 尽可能多地对模拟输入进行采样。到目前为止,我正在使用上传的 49152 字节中的 14952 字节。这包括实时图形,我希望我能够让浏览器保存数据以供以后分析。

标签: file text import arduino arduino-uno


【解决方案1】:

您可以使用xxd 工具从您的 HTML 生成包含文件。比如给一个文件test.html

<html><body>
Hello World!
</body></html>

使用xxd -i test.html &gt; test_html.h 会导致test_html.h 包含:

unsigned char test_html[] = {
  0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e,
  0x0d, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
  0x64, 0x21, 0x0d, 0x0a, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
  0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e
};
unsigned int test_html_len = 42;

然后您可以在您的草图中#include "test_html.h" 并将数组传递给client.print()。这绕过了字符串大小的限制。不幸的是,您失去了逐行循环遍历数组的能力,因此如果需要,您必须自己编写一个函数来执行此操作。

xxd 是一个 *nix 工具,但如果您需要,也有 Windows ports

【讨论】:

  • 谢谢。我在 Win 10 上运行这个。安装 xxd 似乎很痛苦,所以我将编写一个 Python 脚本来生成格式良好的 *.h 文件。
  • 我还查看了 Powershell 的 Format-Hex,但找不到好的文档。
  • 实际上,这不适用于我的应用程序。我创建了一个 Python 程序来重新创建 xxd 输出。编译器不喜欢,Global variables use 11571 bytes (188%) of dynamic memory, leaving -5427 bytes for local variables. Maximum is 6144 bytes. 当我给const 关键字加上前缀时,编译器说,Global variables use 409 bytes (6%) of dynamic memory, leaving 5735 bytes for local variables. Maximum is 6144 bytes.
  • 抱歉,是的 - xxd 方法会将数组放入 RAM。您的 Python 方法的优点是您可以将其设为 const(甚至使用 PROGMEM,因为您使用的是 Arduino/AVR)。
【解决方案2】:

C++ 有“原始字符串文字”。您可以在源代码中的开始和结束“标签”之间放置一个常量字符串,而无需转义特殊字符。您可以将标签选择为不在原始字符串中的内容。在以下示例中,标签是=====

const char* s1 = R"=====(Hello
"World")=====";

相同
const char* s2 = "Hello\n\"World\"";

这样,您可以将大字符串放入单独的 .h 文件中并包含它们。在 AVR 上使用 PROGMEM 来保存 RAM。

【讨论】:

  • 我很想重写我的脚本以使用您的格式。它将消除编码逻辑并使生成的文件可读。但是,如果标签在文件中,您的方法将失败。也许脚本可以生成*%d* 格式的文件特定标签,脚本会在其中不断增加%d,直到找到不在目标文件中的*%d*
  • 标签是你的选择。任何长度的任何字符序列。
【解决方案3】:

使用@jfowkes 格式(但不是@Juraj)我创建了一个Python 程序。它适用于 Windows 和 Linux。

要使用它,请将所有文本文件(对我来说是 .html 和 .js)放在草图的子目录中。然后,从草图文件夹中运行python TextEncode.py "SubdirectoryName"。在草图的开头添加#include "SubdirectoryName.h" 行。该头文件包含一个函数void SendPage(WiFiClient hClient),它将子目录中的文件内容发送给客户端;在适当的时候调用它。

(它确实按字母顺序发送文件,所以我在文件前面加上“F210”之类的数字。我认为文件是模块。通过有很多这样的模块,我可以通过有选择地注释掉代码来禁用模块。我实际上有两个开发模块[一个.js和一个.html]和一个生产模块[一个.js];我有一个主草图中的SendPage函数的副本。通过选择性地注释掉代码,我可以选择是否我想看看 XMLHttpRequest 函数调用的结果。)

我知道这比任何其他提议的解决方案都复杂得多,但它有助于开发周期:(1) 在我最喜欢的 IDE 中编辑 html/js 代码 (2) 运行 python 程序 (3) 编译草图。

这是我的 TextEncode.py 的内容:

# program to convert text files to file with constant array of ascii code of file characters
# converted file is to be used by Arduino compiler to efficiently send html/js code to Arduino
# Usage:
# 1) place files to be encoded into subfolder, "ClientHtml"
# 2) from console, 'python TextEncode.py "ClientHtml"'.
# 

import os
import sys
import binascii

c_nCharsPerLine = 16
strFolderIn = sys.argv[1]
astrPseudos = []                                        # array of file pseudonyms.  to be used to create inclusive [ClientHtml].h 
if len(sys.argv) > 2:
    strClientHandle = sys.argv[2]
else:
    strClientHandle = "hClient"

for strFileIn in os.listdir(strFolderIn):
# encode each file in subdirectory
# it is easier to re encode every file than it is to check timestamps to re encode only updated files
    strFilePseudo = strFileIn.replace (".", "_")        # to be used in name of encoded file and name of variable with contents of file.
    astrPseudos.append(strFilePseudo)
    strContents = "";                                   # contents read from file itself, in pairs of hex digits

    with open(strFolderIn + "/" + strFileIn, "r") as fileIn:
        nChar = 0;
        for strLineIn in fileIn:
            for chIn in strLineIn:
#               strContents = strContents + chIn.encode("hex") + ","        # works on Linux
                strContents = strContents + hex(ord(chIn)) + ","
                nChar += 1
                if nChar % c_nCharsPerLine == 0:
                    strContents += "\n"
    # truncate trailing \n, if it exists
    if nChar % c_nCharsPerLine == 0:
        strContents = strContents[:-1]
    strContents += "0\n"

    with open (strFilePseudo + ".h", "w") as fileOut:
        fileOut.write("const unsigned char c_" + strFilePseudo + "[] = {\n")
#       fileOut.write("unsigned char c_" + strFilePseudo + "[] = {\n")
        fileOut.write(strContents)
        fileOut.write("};\n")

with open (strFolderIn + ".h", "w") as fileOut:
    fileOut.write("// .h files with encoded files to be included:\n")
    astrPseudos.sort()
    for strFilePseudo in astrPseudos:
        fileOut.write("#include \"" + strFilePseudo + ".h\"\n")
    fileOut.write("/*\n")
    fileOut.write("// Arduino Compiler function to send encoded files to web client:\n")
    fileOut.write("// Comment these out if you don't want to use the functionality\n")
    fileOut.write("void SendPage(WiFiClient " + strClientHandle + ")\n")
    fileOut.write("{\n")
    fileOut.write(" String strData;\n")
    for strFilePseudo in astrPseudos:
        fileOut.write(" strData=c_" + strFilePseudo + ";\n")
        fileOut.write(" " + strClientHandle + ".println(strData);\n")
    fileOut.write("}\n")
    fileOut.write("*/\n")

【讨论】:

  • 是的,这个解决方案超出了问题参数。要仅处理一个文件,请使用 for strFileIn ... 循环中的代码。或者,在子目录中只放置一个文件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多