将NDEF记录有效负载转换为用于EthernetClient print()的字符串

时间:2019-01-30 17:15:44

标签: c++ arduino nfc arduino-esp8266 ndef

我尝试使用从NDEF格式的MIFARE NFC标签读取的数据发出HTTP GET请求。我无法将标签中的字节数组数据转换为与以太网客户端print()函数配合使用的格式。

硬件设置是带有seeedstudio NFC Shield和Arduino以太网屏蔽的Arduino Uno。我利用了以太网,PN532和NfcAdapter库。

我尝试使用char *char[]而非String对象进行几种类型的转换,但均未成功。

为查明问题,我选择了Serial.print()给出了预期结果而client.print()没有给出预期结果的情况。

代码基于PN532 NDEF library example 'ReadTagExtended'

void loop(void)
{
    if (nfc.tagPresent()) // Do an NFC scan to see if an NFC tag is present
    {
        NfcTag tag = nfc.read(); // read the NFC tag
        if (tag.hasNdefMessage())
        {
            NdefMessage message = tag.getNdefMessage();
            for (int i = 0; i < message.getRecordCount(); i++)
            {
                NdefRecord record = message.getRecord(i);
                int payloadLength = record.getPayloadLength();
                byte payload[payloadLength];
                record.getPayload(payload);
                String tag_content = "";
                for(int i = 0; i<payloadLength; i++) {
                    tag_content += (char)payload[i];
                }
                Serial.println(tag_content); // prints the correct string
                request(tag_content);
            }
        }
    }
}

void request(String data) {
    EthernetClient client;

    // if you get a connection, report back via serial:
    if (client.connect(remote, 8080)) {
        client.print("GET /subaddress");
        client.print("?data=");
        client.print(data); // unfortunately empty
        client.println();
        client.println();

        while (client.connected()) {
        if (client.available()) {
            char c = client.read();
            Serial.print(c);
        }
    }
    client.stop();
    Serial.println(" OK");
    delay(100);
    } else {
        Serial.println("ERR");
        delay(100);
    }
}

通过上述设置,我使用Serial.println()得到了预期的输出。但是,在(成功的)请求中,data为空。


评论(摘要):

打印记录类型(record.getType())时会显示字母'U'。对于包含字符串“ def”的标签,payloadLength为4。

1 个答案:

答案 0 :(得分:1)

根据您在注释中提供的信息,标记包含URI记录(而URI记录又包含您的数据)。代码中的问题是,您直接将URI记录的全部有效负载用作字符串。但是,URI记录不仅包含字符串,还包含更多内容。特别是,有效负载的第一个字节是前缀字节(通常是不可打印的字符)。因此,看来Serial.println()(或更确切地说是您的串行接收器)只是跳过了该字符。 client.print()将在HTTP请求中包含该字符,并因此创建一个无效的HTTP请求(看起来好像省略了其余字节)。

因此,在使用有效负载之前,您必须遵循URI记录类型定义将有效负载解码为正确的URI:

NdefMessage message = tag.getNdefMessage();
for (int i = 0; i < message.getRecordCount(); ++i) {
    NdefRecord record = message.getRecord(i);
    if (record.getType() == "U") {
        String uri = "";
        int payloadLength = record.getPayloadLength();
        if (payloadLength > 0) {
            byte payload[payloadLength];
            record.getPayload(payload);

            switch (payload[0]) {
                case 0x000: break;
                case 0x001: uri += "http://www."; break;
                case 0x002: uri += "https://www."; break;
                case 0x003: uri += "http://"; break;
                case 0x004: uri += "https://"; break;
                case 0x005: uri += "tel:"; break;
                case 0x006: uri += "mailto:"; break;
                case 0x007: uri += "ftp://anonymous:anonymous@"; break;
                case 0x008: uri += "ftp://ftp."; break;
                case 0x009: uri += "ftps://"; break;
                case 0x00A: uri += "sftp://"; break;
                case 0x00B: uri += "smb://"; break;
                case 0x00C: uri += "nfs://"; break;
                case 0x00D: uri += "ftp://"; break;
                case 0x00E: uri += "dav://"; break;
                case 0x00F: uri += "news:"; break;
                case 0x010: uri += "telnet://"; break;
                case 0x011: uri += "imap:"; break;
                case 0x012: uri += "rtsp://"; break;
                case 0x013: uri += "urn:"; break;
                case 0x014: uri += "pop:"; break;
                case 0x015: uri += "sip:"; break;
                case 0x016: uri += "sips:"; break;
                case 0x017: uri += "tftp:"; break;
                case 0x018: uri += "btspp://"; break;
                case 0x019: uri += "btl2cap://"; break;
                case 0x01A: uri += "btgoep://"; break;
                case 0x01B: uri += "tcpobex://"; break;
                case 0x01C: uri += "irdaobex://"; break;
                case 0x01D: uri += "file://"; break;
                case 0x01E: uri += "urn:epc:id:"; break;
                case 0x01F: uri += "urn:epc:tag:"; break;
                case 0x020: uri += "urn:epc:pat:"; break;
                case 0x021: uri += "urn:epc:raw:"; break;
                case 0x022: uri += "urn:epc:"; break;
                case 0x023: uri += "urn:nfc:"; break;
                default: break;
            }
            for (int j = 1; j < payloadLength; ++j) {
                uri += (char)payload[i]; // NOTE: this is wrong since the string is UTF-8 encoded (but we translate it byte-by-byte)
            }
        }
        Serial.println(uri);
        request(uri);
    }
}

请注意,上述解决方案仍然存在一些问题:

  • URI的字符串部分(从偏移量1开始的字节)实际上是UTF-8编码的。但是,我们将其视为此处(或多或少)ASCII编码。我还没有找到任何简单的库可以在Arduino上正确地将字节数组转换为UTF-8编码的字符串表示形式。
  • 如果最终的URI包含URL中具有特殊含义的字符(例如“ /”,“:”,“&”等),则您需要先对它们进行urlencode(例如,参见here)将字符串附加到GET请求。否则,GET请求的结果URI可能不是您期望的。
  • 就内存效率而言,
  • String当然不是最佳选择。