通过Serial

时间:2017-08-07 10:20:57

标签: c++ c string utf-8 arduino

在向我的arduino发送一些uft8编码的字符串时,我遇到了一些奇怪的行为。我开始使用this示例并对其进行了修改。我的程序如下:

我正在根据示例监听SerialEvent。通过这种方式,我不是在字符串中添加字符,而是以循环方式添加到char [500]数组中。有两个指针变量,一个用于读取,一个用于写入,它们都递增到499然后再设置为0.

void serialEvent() {
    while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();

        // add it to the inputString:
        if (inChar != '\r') {
            if (inChar == '\n') {
                commandsInQueue++;
                inChar = NULL;
            }
            inputBuffer[ptrInputWrite] = inChar;
            if (ptrInputWrite < 499) ptrInputWrite++; else ptrInputWrite = 0;
        }
    }
}

然后在循环函数内部有一个读者。在那里,它从该数组创建一个String,并通过引用将其交给解码器函数。在那里,我使用String的indexOf函数将命令拆分为它的部分。

if (commandsInQueue > 0) {
    String tmp = "";
    tmp.reserve(500);
    do {
        tmp += inputBuffer[ptrInputRead];
        if (ptrInputRead < 499) ptrInputRead++; else ptrInputRead = 0;
    } while (inputBuffer[ptrInputRead] != NULL);

    rxInterpreter(tmp);

    if (ptrInputRead < 499) ptrInputRead++; else ptrInputRead = 0;
    commandsInQueue--;
}

解码器:

void rxInterpreter(String cmd) {
    unsigned int lastSem = 2, nextSem = NULL;

    Serial.println("Processing command: " + cmd);

    if (connectionOpen) {
        switch (cmd[0])
        {
        // ...
        case 'T':
            textRefreshTime = millis();
            refreshBar = true;
            currentPos = 0;
            displayShift = 0;
            currentlyShowing = 0;

            {
                unsigned short len = cmd.length();
                for (short i = 0; i < len; i++) {
                    if ((unsigned long)cmd[i] > 255) {
                        unsigned long chr = (unsigned short)cmd[i] << 8 | (byte)cmd[i + 1];
                        switch (chr) {
                        case 0xc384: //Ä
                        case 0xc3a4: //ä
                            cmd[i] = (uint8_t)B11100001; break;
                        case 0xc396: //Ö
                        case 0xc3b6: //ö
                            cmd[i] = (uint8_t)B11101111; break;
                        case 0xc39c: //Ü
                        case 0xc3bc: //ü
                            cmd[i] = (uint8_t)B11110101; break;
                        case 0xc39f: //ß
                            cmd[i] = (uint8_t)B11100010; break;
                        default:
                            Serial.println("Unbekanntes Zeichen " + String(chr, HEX));
                        }

                        cmd.remove(i + 1, 1);
                        len--;
                    }
                }
            }
// ...

该过程的一部分是解释utf8:我的第一种方法是通过检查当前char的值是否大于0xC0来查找utf8字符的bit signature。这没用。经过一番研究后,我意识到,由于某种原因,从该位置返回的字符不是8,而是16位长。所以cmdString [i]通常会返回一个字节,但是如果该字符是一个uft8字符,那么它将返回连续的签名短路,其中上面的两个八位字节用1填充。

例如变音符号(ä),其utf8代码为0xc3a4。我希望cmdString [i]为0xc3,cmdString [i + 1]为0xa4。但是,实际上,[i]返回0xffc3(十进制为-61)和[i + 1] 0xffa4(十进制为-92)。因为它将这些值解释为有符号变量,所以检查它们是否大于0xc0会自然失败。

我通过打印Serial.println(String(cmdString[i], HEX));来测试它。 BINARY。它总是返回4/16位数,而不是2/8。

有人可以向我解释,为什么会这样?它与我(分别是arduino库)从字节数组创建字符串对象的方式有关吗?我一直认为,char在所有情况下都是无符号字节。我发现这甚至是一个错误吗?

我已经在我的代码中解决了这个问题。但我仍然想知道为什么会这样。

1 个答案:

答案 0 :(得分:1)

你可能遇到了C语言的一些怪癖(C ++并没有真正参与其中,它继承了这些规则)。

char类型与signed charunsigned char不同,并且已实现定义普通char是否可以具有负值(如果是, 哪一个)。显然,您的系统允许-91

第二个怪癖是char类型的表达式经常被提升为int,例如当您比较((char)0xC0) < 0时,右侧为0int,因此左侧也会提升为int

这不是UTF-8特有的。 ISO-8859-x也存在同样的问题。