如何编程自己的 Wifi “静音” Stomp Box 来远程控制 Behringer X32 Rack?

问题描述

我对编码完全陌生,这甚至是我在这里的第一篇文章。我尝试这样做是因为没有人出售我想要/需要的东西;-)

我已经取得了相当多的成就,但此刻我迷失了很多东西(我在过去的 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,但这些在这里都有些矫枉过正。它们都可以让您使用字符串; Stringchar * 更容易使用,但两者都有优点和缺点。

#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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...