问题描述
我对编码完全陌生,这甚至是我在这里的第一篇文章。我尝试这样做是因为没有人出售我想要/需要的东西;-)
我已经取得了相当多的成就,但此刻我迷失了很多东西(我在过去的 8 天里阅读了很多关于一般编码和特别使用 Arduino 的内容)......但让我解释一下首先我对这个项目的意图是:
我想构建一个“Stomp Box”来使Behringer X32 Rack(无线)频道/静音组/总线静音,只是静音开/关......没有别的。
这个 Box 应该有 4-6 个“stompers”(按钮),每个按钮应该有不同的静音功能。
此外,如果未静音,则通道/静音组/总线的当前状态应由 LED 指示为绿色,如果静音则为红色。
因此盒子需要评估指定通道/静音组/总线的当前状态,因为它也可能从其他远程设备更改。
然后在按下/踩到指定按钮时切换到相反的状态。
我想要代码可以轻松更改按钮的操作,例如:
button1 = /ch/01/mix/on,i 1
button2 = /config/mute/1,i 1
button3 = /dca/1/on,i 1
所以如果我需要一个不同的频道/静音组/总线用于另一个事件,只需编辑和重新编码我的 ESP32 Node Kit
这是我已经拥有的代码:
#include "WiFi.h"
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <SPI.h>
#include <OScmessage.h> //https://github.com/CNMAT/OSC
#define WIFI_NETWORK "xxxxxxxxxx" //SSID of you Wifi
#define WIFI_PASSWORD "xxxxxxxxxxx" //Your Wifi Password
#define WIFI_TIMEOUT_MS 20000 // 20 second WiFi connection timeout
#define WIFI_RECOVER_TIME_MS 30000 // Wait 30 seconds after a Failed connection attempt
int muteOn = 0;// 0=Mute
int muteOff = 1;// 1=Unmute
int input;
WiFiUDP Udp;
const IPAddress outIp (192,168,10,129); //mixers IP
const unsigned int outPort = 10023; //X32 Port
//variables for blinking an LED with Millis
const int led = 2; // ESP32 Pin to which onboard LED is connected
unsigned long prevIoUsMillis = 0; // will store last time LED was updated
const long interval = 300; // interval at which to blink (milliseconds)
int ledState = LOW; // ledState used to set the LED
void connectToWiFi(){
Serial.print("Zu WLAN verbinden...");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NETWORK,WIFI_PASSWORD);
unsigned long startAttemptTime = millis();
while(WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT_MS){
Serial.println(".");
delay(100);
}
if(WiFi.status() != WL_CONNECTED){
Serial.println("Nicht Verbunden!");
//optional take action
}else{
Serial.print("WLAN Verbunden mit ");
Serial.println(WIFI_NETWORK);
Serial.println(WiFi.localIP( ));
}
}
void setup() {
Serial.begin(115200);
connectToWiFi();
Udp.begin(8888);
pinMode(led,OUTPUT);
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
// ArduinoOTA.setHostname("myesp32");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress,unsigned int total) {
Serial.printf("Progress: %u%%\r",(progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ",error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop(){
ArduinoOTA.handle();
unsigned long currentMillis = millis();
if (currentMillis - prevIoUsMillis >= interval) {
// save the last time you blinked the LED
prevIoUsMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
ledState = not(ledState);
// set the LED with the ledState of the variable:
digitalWrite(led,ledState);
}
input=Serial.read();
if (input=='0'){
// welcher status hat der kanal?
// wenn Kanal gemutet dann unmute und umgekehrt
Serial.println("Mute!");
delay(100);
sendMute(); //send Mute to mixer
Serial.println("...");
}
if (input=='1'){
Serial.println("UnMute!");
delay(100);
sendUnMute();
Serial.println("...");
}
}
void sendMute() {
//the message wants an OSC address as first argument
OScmessage msg("/ch/01/mix/on");
msg.add(muteOn);
Udp.beginPacket(outIp,outPort);
msg.send(Udp); // send the bytes to the SLIP stream
Udp.endPacket(); // mark the end of the OSC Packet
msg.empty(); // free space occupied by message
delay(20);
}
void sendUnMute() {
//the message wants an OSC address as first argument
OScmessage msg("/ch/01/mix/on");
msg.add(muteOff);
Udp.beginPacket(outIp,outPort);
msg.send(Udp); // send the bytes to the SLIP stream
Udp.endPacket(); // mark the end of the OSC Packet
msg.empty(); // free space occupied by message
delay(20);
}
所以我通过串行监视器对此进行了测试,当我输入“0”并单击发送时,混音器将通道 1 静音,并且在输入“1”通道 1 上变为未静音,到目前为止一切顺利... (OScmessage msg("/ch/01/mix/on");
。 .. 部分。
这里特别困扰我的是,我不得不硬编码命令“/ch/01/mix/on”,因为我无法声明一个变量? strong> 对于这个 字符串? 我已经很困惑了,我什至不知道我的术语是否正确:-(
顺便说一句:有很多解决方案可以用 MIDI 来实现,但 MIDI 不是无线的,我认为我的项目有点矫枉过正。我也对github.com/CNMAT/OSC做了一些研究,但我不明白......(哭泣)......
我还发现了一个帖子 here,但这也没有帮助... :-(
关于我如何实现目标的任何建议?--
非常感谢任何帮助...即使是德语(我的母语...)
PS:是的,我是初学者,我承认这一点。但至少在过去的 8 天里,我设法通过 OTA 连接和刷机,所以请放轻松。
解决方法
不想硬编码你的命令是一种很好的本能。
Arduino 语言是 C++,它(主要)是 C 的超集。C 和 C++ 使用预处理器,它允许您定义常量并测试它们的存在。
例如,你可以写:
#define CHAN01_MIX_ON_COMMAND "/ch/01/mix/on"
然后在任何想要使用该常量的地方使用 CHAN01_MIX_ON_COMMAND
,如下所示:
void sendMute() {
//the message wants an OSC address as first argument
OSCMessage msg(CHAN01_MIX_ON_COMMAND);
然后,如果您需要更改字符串 "/ch/01/mix/on"
,您只需在一个位置更改它,而不必担心在代码中找到它的每个实例。
在 #define
语句中写入名称是人们通常遵循的惯例,以便更清楚地表明它们是常量。
您必须在使用您定义的常量之前编写 #define
行,因此将它放在文件的开头(在任何 #include
行之后和您的第一个函数之前)是一个好习惯.或者,如果您有几个,您可以将它们全部放在自己的名为 commands.h
之类的文件中(.h
表示 header
文件),然后将其包含在任何需要它的文件的开头像这样:
#include "commands.h"
此 #include
语句会将文件 commands.h
的内容插入到该语句所在的文件中。
当您有多个 #define
语句时,将它们放在一个地方(无论是在文件的顶部还是在它们自己的文件中)也是一种很好的做法,这样您就可以在一个中心位置找到并在需要时更新它们。
有些人会将字符串常量分配给变量,如下所示:
char *channel01_mix_on_cmd = "/ch/01/mix/on";
这里的 char
表示“一个字符”——比如一个字母、数字或符号。 *
表示指向,它允许您使用字符数组。 C 和 C++ 中的简单字符串只是字符数组(或指向第一个字符的指针),末尾的特殊隐藏字符设置为数值 0(不是字符 '0'
)。 C++ 也有一个名为 std::string
的字符串数据类型,而 Arduino 程序有 String
,但这些在这里都有些矫枉过正。它们都可以让您使用字符串; String
比 char *
更容易使用,但两者都有优点和缺点。
与 #define
一样,您也可以将其放置在靠近文件开头的函数之外。它定义了一个全局变量,可供任何引用它的函数使用。
您还可以在需要字符串的任何地方使用该变量。这与使用 #define
的想法相同,只是做法略有不同。例如:
void sendMute() {
//the message wants an OSC address as first argument
OSCMessage msg(channel01_mix_on_cmd);
在这里使用变量是为了通过没有字符串的多个副本来节省存储空间。这不是必需的; C/C++ 编译器很长时间以来都检测到了这一点,并且只存储了该字符串的一个副本。如果你的代码被分成多个文件,它可能会节省空间。
在 ESP32 和 ESP8266 等 CPU 上节省空间很重要,因为它们的内存很少。 #define
在这里很好,因为编译器会自动为您完成。
您可以使用 sprintf 创建命令字符串。 例如:
#define CHANNELON "on"
#define CHANNELOFF "off"
int channel;
int mute;
char messageString[100];
// some code that calculates the channel number and the mute state:
channel = 1;
mute = 1;
// then check the mute state and create the command string:
if (mute)
{
// to turn off a channel:
sprintf(messageString,"/ch/%02d/mix/%s",channel,CHANNELOFF);
}
else
{
// to turn on a channel:
sprintf(messageString,CHANNELON);
}
// send the command:
OSCMessage msg(messageString);
%02d 将用前面的零替换一个整数, 如果它小于 10 并且总是 2 个字符长。 所以如果 channel 是 1,结果就是 01