Arduino 中的 LED 环 - 如何在 Processing IDE 到 select 颜色中制作界面

LED ring in Arduino - How to make an interface in Processing IDE to select a color

我有 2 个不同的 LED 环。 (一个有 16 个 LED,另一个有 24 个) 我想通过处理 IDE 创建一个界面,在那里我可以 select 一种颜色并将这种颜色发送到 selected 环。这是arduino代码:

#include <FastLED.h>
#define LED_PIN1 3
#define LED_PIN2 12           
#define NUM_LEDS1 16       
#define NUM_LEDS2 24

CRGB leds1[NUM_LEDS1];      
CRGB leds2[NUM_LEDS2];  

     int r,g,b;

boolean state = false;


void setup() {


FastLED.addLeds<WS2812, LED_PIN1, GRB>(leds1, NUM_LEDS1);
FastLED.addLeds<WS2812, LED_PIN2, GRB>(leds2, NUM_LEDS2);

Serial.begin(9600);


}


void loop() {

  String returnedInput = rgbInput();

  String red = returnedInput.substring(0,3); //get 3 values like 255
  String green = returnedInput.substring(4,7);
  String blue = returnedInput.substring(8,11);
  

  Serial.println(red);
  Serial.println(green);
  Serial.println(blue);

  int r = red.toInt();
  int g = green.toInt();
  int b = blue.toInt();

 if (Serial.available()){

      char val = Serial.read();

      if(val == '2') { //selects the second LED ring
        if(state == false) {
          state = true;

          for(int i = 0 ; i < 24 ; i++ ){
            leds2[i] = CRGB(r, g, b); //turn on all the LEDs on the ring to the selected color
            FastLED.show(); 
            FastLED.clear();
            FastLED.show();
            
          }}}
}}

  
  String rgbInput() {

    //prompt for input
    Serial.println("ready");

    while(!Serial.available()) {
      //if 0, it keeps waiting for the user to enter sth.
    }

  String userInput = Serial.readStringUntil("\n");

  return userInput;
}    
  

我为第二个 LED 写了这个,如果我能管理这个,我会为另一个做同样的事情,但它不起作用。

这里是处理代码:

import controlP5.*; //import ControlP5 library
import processing.serial.*;

Serial port;

ControlP5 cp5; //create ControlP5 object
PFont font;
PFont font2;

color col;
Serial serialMonitor;
String prompt;

ColorPicker cp;

void setup(){ //Same as setup in arduino
  
  size(900, 900);                          //Window size, (width, height)
  port = new Serial(this, "COM4", 9600);   //Change this to your port
  
  cp5 = new ControlP5(this);
  
  font = createFont ("Georgia Bold", 20);
  font2 = createFont ("Georgia Bold",15);
  
cp = cp5.addColorPicker("PICKER")
        .setPosition(500,100)
        .setColorValue(color(255,128,0,128))
        ;
       
  
Group configGroup = cp5.addGroup("CONFIGURATION")
    .setPosition(90,100)
    .setWidth(150)
    .setHeight(30)
    .setFont(font2)
    .setBackgroundColor(color(0,0))
  ;
  
  cp5.addButton("PICK_ALL")  // The button
    .setPosition(10, 10)     // x and y relative to the group
    .setSize(160, 150)       // (width, height)
    .setFont(font)
    .setGroup(configGroup)   // add it to the group
  ;     
  
  
  cp5.addButton("PICK_ONE")  // The button
    .setPosition(10, 200)    // x and y relative to the group
    .setSize(160, 150)       // (width, height)
    .setFont(font) 
    .setGroup(configGroup)   // add it to the group
  ;   
  
}


void draw(){  //Same as loop in arduino

  background(150, 0 , 150); //Background colour of window (r, g, b) or (0 to 255)
    
}

public void controlEvent(ControlEvent c){
  
  if(c.isFrom(cp)){
    int r = int(c.getArrayValue(0));
    int g = int(c.getArrayValue(1));
    int b = int(c.getArrayValue(2));
    int a = int(c.getArrayValue(3));
    col = color(r,g,b,a);
  }
}

void keyPressed(){
  
  while(serialMonitor.available() > 0)
  {
    prompt = serialMonitor.readStringUntil (10);
  }
  
  
  println(keyCode);
  
  String sendColor = nf(int(red(col)),3) + "," + nf(int(green(col)),3) + "," + nf(int(blue(col)),3);
  
  println(sendColor);
  serialMonitor.write(sendColor);
  
}

void PICKER(){
  port.write('2');
}

void PICK_ALL(){
  
  port.write('t');
  
}

void PICK_ONE(){
  
  port.write('l');
  
}

我不太清楚如何获取 RGB 值并在 CRGB 函数中使用它们。使用使用 3 个引脚的单个 RGB LED 时要容易得多。但是我无法将它实现到仅使用 1 个引脚的 LED 环。

这里是拾色处理界面。我可以 select 颜色,但 Arduino 上的 LED 环没有任何变化。

可靠的串行通信并非易事。理想情况下,您可以让自己的二进制通信协议设置一个字节数据包,其中 header 描述了实际有多少后续字节有数据,甚至可能是 checksum.

字符串可以帮助您入门,您使用 nf() 让数据更易于解析真是太好了。

一个潜在的陷阱可能是将一个字符串放在一起 (rgbInput()) 和一次读取一个字符 (char val = Serial.read();) 之间的切换。

我建议将问题分解成更小的更简单的部分,testing/debugging 每个部分,然后一次一个地将这些部分重新组合在一起以避免集成错误。

例如,主要的挑战似乎是串行通信,所以我会编写 Processing 和 Arduino 草图以确保在添加 LED 控制之前可靠地工作。

让我们选择 \n 终止字符串选项,即使它发送了一个多余的额外字符(例如 port.write("2\n");),它也会使缓冲更简单:

  • 总是缓冲直到换行
  • trim()换行字符串
  • 如果修剪后的字符串的长度为 1,则它是一个命令(如“2”、'l'、't'),否则它是一个颜色三元组

这是一个使用上述想法的基本 Arduino 草图(以及您的一些代码;)):

void setup() {
  Serial.begin(9600);
}

void loop() {
  // check if there are at least two characters to receive
  if(Serial.available() > 1){
    // buffer the full string until a new line character
    String returnedInput = Serial.readStringUntil('\n');
    // remove white space (new line char)
    returnedInput.trim();
    // if it's a single command 
    if(returnedInput.length() == 1){
      char state = returnedInput.charAt(0);
      
      switch(state){
        
        case '2':
          Serial.println("parsed 2 command");
        break;

        case 't':
          Serial.println("parsed t command");
        break;

        case 'l':
          Serial.println("parsed l command");
        break;
        
        default:
          Serial.print("unknown state:");
          Serial.println(state);
        break;
      }
      
    }
    // if it's a RGB triplet
    else if(returnedInput.length() == 11){
      String redString   = returnedInput.substring(0, 3); //get 3 values like 255
      String greenString = returnedInput.substring(4, 7);
      String blueString  = returnedInput.substring(8, 11);

      int r = redString.toInt();
      int g = greenString.toInt();
      int b = blueString.toInt();

      // constrain values to bytes
      r = constrain(r, 0, 255);
      g = constrain(g, 0, 255);
      b = constrain(b, 0, 255);
      // do something with the values (e.g. store globally, etc.)
      Serial.print("parsed RGB: #");
      Serial.print(r, HEX);
      Serial.print(g, HEX);
      Serial.print(b, HEX);
      Serial.println();
    }
    // otherwise error message ?
    else{
      Serial.print("Uknown command: ");
      Serial.println(returnedInput);  
    }
  }
}

这应该处理带有新行终止符的字符串消息,并根据修剪后的长度解析单个字符命令和 11 个字符的 RRR、GGG、BBB 字符串。

可以直接用Arduino的Serial Monitor测试

在您的 Processing sketch 中,不清楚为什么有两个串行端口(portserialMonitor)。

这里是您的 Processing sketch 的一个稍微修改的版本,它发送几个单字符命令或颜色字符串:

import controlP5.*; //import ControlP5 library
import processing.serial.*;


PFont font;
PFont font2;

// Arduino serial port
Serial port;
// colour picker values to send to Arduino
int r;
int g;
int b;

// GUI variables
ControlP5 cp5; //create ControlP5 object
ColorPicker cp;


void setup() { //Same as setup in arduino

  size(900, 900);                          //Window size, (width, height)
  try {
    port = new Serial(this, "/dev/tty.usbserial-A104WS3R", 9600);   //Change this to your port
    // buffer until new line: this plugs in nicely with serialEvent()
    port.bufferUntil('\n');
  }catch(Exception e) {
    println("error opening serial");
    e.printStackTrace();
  }
  
  cp5 = new ControlP5(this);

  font = createFont ("Georgia Bold", 20);
  font2 = createFont ("Georgia Bold", 15);

  cp = cp5.addColorPicker("PICKER")
    .setPosition(500, 100)
    .setColorValue(color(255, 128, 0, 128))
    ;

  Group configGroup = cp5.addGroup("CONFIGURATION")
    .setPosition(90, 100)
    .setWidth(150)
    .setHeight(30)
    .setFont(font2)
    .setBackgroundColor(color(0, 0))
    ;

  cp5.addButton("PICK_ALL")  // The button
    .setPosition(10, 10)     // x and y relative to the group
    .setSize(160, 150)       // (width, height)
    .setFont(font)
    .setGroup(configGroup)   // add it to the group
    ;     


  cp5.addButton("PICK_ONE")  // The button
    .setPosition(10, 200)    // x and y relative to the group
    .setSize(160, 150)       // (width, height)
    .setFont(font) 
    .setGroup(configGroup)   // add it to the group
    ;
}


void draw() {  //Same as loop in arduino

  background(150, 0, 150); //Background colour of window (r, g, b) or (0 to 255)
}

public void controlEvent(ControlEvent c) {

  if (c.isFrom(cp)) {
    r = int(c.getArrayValue(0));
    g = int(c.getArrayValue(1));
    b = int(c.getArrayValue(2));
  }
}

void keyPressed() {

  if(port == null){
    println("no serial, ignoring");
    return;
  }

  String sendColor = nf(r, 3) + "," + nf(g, 3) + "," + nf(b, 3) + '\n';
  println("sending to Arduino:", sendColor);
  
  port.write(sendColor);
}

void PICKER() {
  println("PICKER");
  if (port != null) port.write("2\n");
}

void PICK_ALL() {
  println("PICK_ALL");
  if (port != null) port.write("t\n");
}

void PICK_ONE() {
  println("PICK_ONE");
  if (port != null) port.write("l\n");
}

void serialEvent(Serial s){
  println("from Arduino:", s.readString());
}

总体注意错误检查位:使用 Serial 时总是一个好主意 :)

一旦按预期工作,您就可以组合 FastLED 控件。

这里有一个建议:

#include <FastLED.h>
#define LED_PIN1 3
#define LED_PIN2 12           
#define NUM_LEDS1 16       
#define NUM_LEDS2 24

CRGB leds1[NUM_LEDS1];      
CRGB leds2[NUM_LEDS2];  

// ring 1 color
int r1,g1,b1;
// ring 2 color
int r2,g2,b2;
// toggle wether to update r1,g1,b1 or r2,g2,b2 when a new colour arrives
boolean updateRing1 = true;

void setup() {
  // setup serial
  Serial.begin(9600);
  // setup LEDs
  FastLED.addLeds<WS2812, LED_PIN1, GRB>(leds1, NUM_LEDS1);
  FastLED.addLeds<WS2812, LED_PIN2, GRB>(leds2, NUM_LEDS2);
}

void loop() {
  handleSerial();
  driveLEDRings();
}

void handleSerial(){
  // check if there are at least two characters to receive
  if(Serial.available() > 1){
    // buffer the full string until a new line character
    String returnedInput = Serial.readStringUntil('\n');
    // remove white space (new line char)
    returnedInput.trim();
    // if it's a single command 
    if(returnedInput.length() == 1){
      char state = returnedInput.charAt(0);
      
      switch(state){
        
        case '2':
          Serial.println(F("parsed 2 command"));
        break;

        case 't':
          Serial.println(F("parsed t command: switching to ring #1"));
          updateRing1 = true;
        break;

        case 'l':
          Serial.println(F("parsed l command: switching to ring #2"));
          updateRing1 = false;
        break;
        
        default:
          Serial.print(F("unknown state:"));
          Serial.println(state);
        break;
      }
      
    }
    // if it's a RGB triplet
    else if(returnedInput.length() == 11){
      String redString   = returnedInput.substring(0, 3); //get 3 values like 255
      String greenString = returnedInput.substring(4, 7);
      String blueString  = returnedInput.substring(8, 11);

      int r = redString.toInt();
      int g = greenString.toInt();
      int b = blueString.toInt();

      // constrain values to bytes
      r = constrain(r, 0, 255);
      g = constrain(g, 0, 255);
      b = constrain(b, 0, 255);
      // do something with the values (e.g. store globally, etc.)
      Serial.print(F("parsed RGB: #"));
      Serial.print(r, HEX);
      Serial.print(g, HEX);
      Serial.print(b, HEX);
      Serial.println();

      // handle ring colour update
      if(updateRing1){
        r1 = r;
        g1 = g;
        b1 = b;  
      }else{
        r2 = r;
        g2 = g;
        b2 = b;
      }
    }
    // otherwise error message ?
    else{
      Serial.print("Uknown command: ");
      Serial.println(returnedInput);  
    }
  }  
}

void driveLEDRings(){
  //update ring 1
  for(int i = 0 ; i < NUM_LEDS1; i++){
    leds1[i] = CRGB(r1, g1, b1);
  }
  //update ring 2
  for(int i = 0 ; i < NUM_LEDS2; i++){
    leds2[i] = CRGB(r2, g2, b2);
  }
  // display
  FastLED.show();
}

注意:以上代码未使用有线 RGB LED 进行测试,因此它可能无法正常工作,但希望它能说明这个想法。 (您应该仔细检查波特率、引脚、RGB 颜色通道等)

如果以上使用串行监视器按预期工作,您可以返回测试处理接口草图。

另外请记住,9600 是一个很低的波特率。 您应该使用更高的波特率(例如 115200、57600)进行测试,如果它稳定,则使用它们来避免在缓冲串行数据时延迟驱动 LED。 一般来说,尽可能避免/尽量减少阻塞 (while) 循环。

总的来说,我们的想法是 delete/remove 代码中不需要的任何内容来深入解决问题:孤立地解决问题。一旦它被调试并可靠地工作,一次添加新代码,在每次添加后进行测试(否则你可能会引入更多错误而不是更难发现/修复的错误)。