【问题标题】:Arduino and object instantiation in setup()setup() 中的 Arduino 和对象实例化
【发布时间】:2020-01-06 18:39:08
【问题描述】:

我对 C 和 Arduino 非常陌生,但在高级语言方面经验丰富。我试图在文档中找到解决方案,但找不到足够复杂的东西来理解 arduino 的基础

所以,这里是我试图理解的示例: https://learn.adafruit.com/mqtt-adafruit-io-and-you/arduino-plus-library-setup

示例名为 mqtt_esp8266

相关部分:

#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

#define AIO_USERNAME    "your AIO username"

WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");

void setup() {
...
}

void loop() {
...
photocell.publish(something);
}

我的问题:

  • WiFiClient 客户端; 表示定义了对象还是实例化了对象?如果被实例化,它被实例化成什么?
  • Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell"); 我猜这里是使用 mqtt 客户端定义的前一行。我也想我不能使用“运行时”信息来声明一个主题,因为我不在“设置”功能中,对吗?
  • AIO_USERNAME "/feeds/photocell" 这到底是什么语法?它们如何使用空格连接字符串?

我想要实现的是使用运行时信息来构建主题。简化,我想发布到 AIO_USERNAME "/feeds" + WiFi.macAddress() + "/photocell" 但没有任何财富。特别是,我不知道如何在全局范围内定义 Adafruit_MQTT_Publish 以便我可以在 setup() 和 loop() 函数中使用它,但在 setup 函数中实例化以便我可以使用 WiFi.macAddress()输出以作为提要名称的前缀

【问题讨论】:

  • 仅供参考,您使用的是 C++,而不是 C。其次,反复试验可能不是学习语言的最佳方式:The Definitive C++ Book Guide and List
  • 每个问题回答一个问题
  • #define AIO_USERNAME "your AIO username" 的特别标注。它是一个宏。这是 1970 年代的遗留物,因此请尽量避开它们。在代码中的任何地方都可以找到令牌AIO_USERNAME,无论这样做是否有意义,它将被"your AIO username" 替换。就宏而言,AIO_USERNAME 还不错,因为它不太可能被重用,但过去人们使用诸如 maxminl 之类的东西,以及其他经常显示为变量的东西名称,功能或你有什么。由此产生的错误可能很难诊断。
  • 已更改有关宏语法的问题。问题不在于宏,而是使用空格连接 2 个字符串的事实。不应该是“+”吗?顺便说一句,“+”不起作用
  • @user4581301 宏在嵌入式环境中非常常见,其中存在大量寄存器屏蔽和位旋转。此外,由于它们在编译时被替换,因此它们不会占用有时只有 4kB 内存的嵌入式控制器上的宝贵内存。

标签: c++ arduino mqtt


【解决方案1】:
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");

以上三行实例化了全局范围的新变量。变量client 使用WiFiClient 类的默认构造函数进行实例化。 mqtt 使用构造函数实例化,该构造函数接受指向客户端的指针、一些服务器常量等。

查看Adafruit_MQTT_Publish 的代码后:https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/Adafruit_MQTT.cpp 我看到它只是一个方便的包装类,它包含一个指向Adafruit_MQTT 对象、qos(默认为0)和topic 的指针。它实现了几个不同的publish 方法,这些方法因接收到的参数类型而异。例如这是uint32_t的方法:

bool Adafruit_MQTT_Publish::publish(uint32_t i) {
  char payload[11];
  ultoa(i, payload, 10);
  return mqtt->publish(topic, payload, qos);
}

您可以简单地自己实现相同的功能:

WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
char whatever[] = "What ever";

void loop()
{
  char topic[64]; //Note, if the input can be longer than 64 characters long, change to topic[128] for example...
  strcpy(topic, AIO_USERNAME);
  strcat(topic, whatever);
  char something[] = "asdf";
  mqtt.publish(topic,something,0);
}

至于你的语法问题

AIO_USERNAME "/feeds/photocell"

预处理器会将其变成:

"your AIO username" "/feeds/photocell"

然后编译器变成一个长字符串:

"your AIO username/feeds/photocell"

这种连接仅适用于字符串文字(例如"string")。要在运行时连接内容,您必须使用库函数,例如 strcatstrcpy。请参考string.hhttps://en.cppreference.com/w/c/string/byte

【讨论】:

  • 好的,让我解释一下我真的不明白的部分:''' WiFiClient client; Adafruit_MQTT_Publish 光电管; ''' 为什么客户端被实例化而光电管不被实例化?实际上,该代码给了我错误:错误:没有调用'Adafruit_MQTT_Publish::Adafruit_MQTT_Publish()'的匹配函数注意:候选人是:Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT*, const char*, uint8_t) 候选人需要3个参数,提供0因此,即使您的代码也不能解决在全局范围内声明对象而是在 setup() 函数上初始化的问题
  • @Wishper 是的,你是对的。在运行时创建Adafruit_MQTT_Publish 的瞬间似乎没有“非hacky”方式。但幸运的是,这不是必需的,因为您可以自己轻松实现相同的功能。如果您真的需要在运行时创建它,您可以为它做一些技巧,而无需使用堆。例如,您可以为它预先分配空间并使用放置新。或者,您可以将其设为静态变量。或者您可以自己编辑 arduino 代码并添加功能以在运行时更改主题,或者简单地复制他们的类并修改代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多