【问题标题】:Extracting values from an incoming bluetooth serial on an arduino从 arduino 上的传入蓝牙序列中提取值
【发布时间】:2019-06-26 11:22:07
【问题描述】:

我目前正在开展一个项目,该项目涉及一个通过 arduino 控制 LED 灯条的 android 应用程序。这是通过蓝牙模块完成的。我遇到的问题是该应用程序涉及控制 LED 灯条的亮度和色调等功能。因此,当应用程序上的任何这些组件发生更改时,都会向 arduino 发送一个值,以便它更新这些值。发送诸如“bt+50!”之类的消息其中前两个字母标识更改了哪个组件(bt = 亮度),“+”表示实际值的开始,“!”表示结束。

我想知道如何拆分和拼接传入的消息,以便我可以首先确定哪个组件被更改,然后能够提取整数值,例如:

if(message_id == 'bt'){
    brightness = message_value;
}

目前我只是在普通的单色 LED 灯上进行测试。而且我只实现了非常简单的代码,可以处理从蓝牙串口传入的单字母消息。

我尝试过使用内置的 C 字符串函数,但由于我是 C 语言的新手,而且我来自 python 背景,所以我很难让事情正常工作。

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

char BT_input; // to store input character received via BT.

void setup()  
{
  pinMode(13, OUTPUT);     // Arduino Board LED Pin
  BlueTooth.begin(9600);  
}

void loop() 
{
  if (BlueTooth.available())
  {
    BT_input=(BlueTooth.read());
    if (BT_input=='n')
    {
      digitalWrite(13, HIGH);
      BlueTooth.println("Now LED is ON");
    }
    else if (BT_input=='f')
    {
      digitalWrite(13, LOW);
      BlueTooth.println("Now LED is OFF");
    }
  }
}

我希望从传入的消息中提取 message_id 和 message_value,以便能够更新我的 LED 灯条。

【问题讨论】:

  • Arduino 是 C++,而不是 C。使用 std::string 的慢速 C++ 解决方案是否足够或必须使用 C 字符串完成?

标签: c bluetooth arduino


【解决方案1】:

您可以拥有string 变量,它将所有read 值存储到其中,一旦读取!,它将开始处理它。

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

char input; // to store input character received via BT.
String data;

void setup()  
{
  pinMode(13, OUTPUT);     // Arduino Board LED Pin
  BlueTooth.begin(9600);  
}

void loop() 
{
  if (BlueTooth.available())
  {
    input=(BlueTooth.read());

      if (input != '!') {
          data += input;
      }
      else{
          String message_id = String(data.substring(0,2)); //gets only "bt"
          data.remove(0,3); //data becomes "50" since '!' is not added to data

          int message_value = data.toInt();

          if(message_id == "bt"){
               brightness = message_value;
          }
      }
  }
}

【讨论】:

  • 谢谢。这正是我的想法,我只是不知道要使用的功能。所以谢谢你的建议。
  • 还有一个问题,我如何区分单字母消息和类似这样的消息?例如,我目前使用“n”来表示开灯功能,如您所见,我正在使用多字符串来表示亮度等。我该如何设置两者?我是否只是检查 bluetooth.available 是否大于 1?
【解决方案2】:

您可以使用案例中使用的典型技巧,在单个 unsigned int 中编码多字节字符文字,在 Arduino uno 和 Mega 2560 上是 16 位小端格式。

参考 C 标准 ISO/IEC 9899:201x § “6.4.4.4 字符常量”

第 10 小段解释了我们的案例:

整数字符常量的类型为 int。整数的值 包含单个字符的字符常量,该字符映射到 单字节执行字符是 映射字符的表示解释为整数。这 包含多个字符的整数字符常量的值 字符(例如,'ab'),或包含字符或转义序列 不映射到单字节执行字符,是 实现定义。如果一个整数字符常量包含一个 单个字符或转义序列,其值是结果 当一个 char 类型的对象的值是单 字符或转义序列转换为 int 类型。

在我们的例子中,“实现定义”的管理方式如下所述。

在这种情况下,多字节字符常量'bt'可以编码为16位整数0x6274,其中'b'=0x62't'=0x74

编译器还应该足够聪明,可以将多字节字符序列转换为 int 值。

在下面的 sn-p 中,我们认为 char 数组 msg 保存接收到的消息,并且我们使用一个简单而实用的 switch 语句(需要一个整数值)将 msg 变量转换为无符号整数。:

char msg[10];
...
switch (*((unsigned int *)msg))
{
    case 'tb':     //Note the reverse order of command characters due to endianess
        int value = atoi(msg+2);    //Convert number to int
        ....     //do something
    break;

    ....    //other cases
}

msg 指针变量转换为指向无符号整数的指针,编译器将以上述方式将前 2 个字符解释为整数,并根据它们的值执行开关。

以下示例使用您为使用开关而修改的代码。它假定命令具有等于MAX_MSG_LEN 的固定长度(命令2 个字符,值和消息结尾2 个字符):

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

#define MAX_MSG_LEN 5       //Max message length
#define OFFSET_TO_VALUE     //Offset in input buffer to value

char BT_input[10];  // to store input characters received via BT.

void setup()
{
    pinMode(13, OUTPUT);    // Arduino Board LED Pin
    BlueTooth.begin(9600);
}

void loop()
{
    if (BlueTooth.available())
    {
        /*
         * Read in the message up to the '!'
         */
        int i=0;
        do
        {
            BT_input[i] = (BlueTooth.read());
        } while (i<MAX_MSG_LEN && BT_input[i++]!='!');

        /*
         * If message length is exactly what we expect
         * we can process the message.
         * Note that because of endianess the command
         * chare are rversed.
         */
        if (i == MAX_MSG_LEN)
        {
            switch (*((unsigned int *)BT_input))
            {
                case 'tb':  // command 'bt'
                    process_brigthness(atoi(BT_input + OFFSET_TO_VALUE));
                    break;

                case 'no':  // command 'on'
                {
                    digitalWrite(13, HIGH);
                    BlueTooth.println("Now LED is ON");
                    break;
                }

                case 'fo':  // command 'of' for off
                {
                    digitalWrite(13, LOW);
                    BlueTooth.println("Now LED is OFF");
                    break;
                }

                default:    // unknown command
                {
                    unknown_command();
                    break;
                }
            }
        }
        else
        {
            /*
             * Process communication error
             */
            communication_error();
        }
    }
}

或者使用输入流和命令结构的联合:

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

#define MAX_MSG_LEN 5       //Max message length
#define OFFSET_TO_VALUE     //Offset in input buffer to value

union tag_BT_input              // to store input characters received via BT.
{
    char   stream[MAX_MSG_LEN];
    struct
    {
        unsigned int cmd;       //Command
        char         val[2];    //value
        char         eom;       //End of message marker '!'
    }msg;
} BT_input;

void setup()
{
    pinMode(13, OUTPUT);    // Arduino Board LED Pin
    BlueTooth.begin(9600);
}

void loop()
{
    if (BlueTooth.available())
    {
        /*
         * Read in the message up to the '!'
         */
        int i=0;
        do
        {
            BT_input.stream[i] = (BlueTooth.read());
        } while (i<MAX_MSG_LEN && BT_input.stream[i++]!='!');

        /*
         * If message length is exactly what we expect
         * we can process the message.
         * Note that because of endianess the command
         * chare are rversed.
         */
        if (i == MAX_MSG_LEN)
        {
            switch (BT_input.msg.cmd)
            {
                case 'tb':  // command 'bt'
                    process_brigthness(atoi(BT_input.msg.val));
                    break;

                case 'no':  // command 'on'
                {
                    digitalWrite(13, HIGH);
                    BlueTooth.println("Now LED is ON");
                    break;
                }

                case 'fo':  // command 'of' for off
                {
                    digitalWrite(13, LOW);
                    BlueTooth.println("Now LED is OFF");
                    break;
                }

                default:    // unknown command
                {
                    unknown_command();
                    break;
                }
            }
        }
        else
        {
            /*
             * Process communication error
             */
            communication_error();
        }
    }
}

【讨论】:

  • 多么糟糕的建议!希望我有多个downvote option.1。哪个编译器允许case 'bt': 这个? 2.对可读性少留情。 3. *((unsigned int *)msg 是由于严格的别名规则导致的未定义行为并且列表继续。
  • 如果您不知道答案,请不要误导新手。继续前进。
  • @KBlr 首先我建议你在表达任何纯粹的意见之前检查你确认的内容。 1) 每个兼容编译器都接受case 'bt':,因为标准允许它。 Arduino 上使用的 GCC 也不例外。只需在 Arduino IDE 中编写并编译即可。你只会得到一个警告 "warning: multi-character character constant [-Wmultichar] case 'bt':"。 2)这是一种简单的铸造形式。 经验丰富的程序员在大公司的代码中遇到过更糟糕的情况。我停止了列表。
  • @KBlr 编译用“ATMEL Studio 7”和 ECLIPSE 检查。这里警告不会出现。在这个解决方案中,唯一值得商榷的一点是它使用了实现定义的限制。但这必须仅针对 Arduino 特定系统进行编译....(使用标准编译器)。
  • @Frankie_C 谢谢你的建议,但我担心它对于我所追求的来说有点太复杂了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 1970-01-01
  • 2015-01-09
  • 2019-02-13
  • 1970-01-01
相关资源
最近更新 更多