提问者:小点点

Arduino 中的 LED 环 - 如何在处理 IDE 中制作界面以选择颜色


我有2个不同的LED环。(一个有16个LED,另一个有24个)我想从处理IDE中制作一个界面,在那里我可以选择一种颜色并将这种颜色发送到选定的环。这是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 环上。

这是颜色拾取的处理界面。我可以选择颜色,但Arduino上的LED戒指没有任何变化。


共1个答案

匿名用户

可靠的串行通信并不简单。理想情况下,您可以使用自己的二进制通信协议设置一个字节包,其中包含一个标头,描述有多少后续字节实际具有数据,甚至可能还有校验和。

字符串可以开始使用,使用nf()使数据更易于解析非常好。

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

我建议将问题分解为更小的更简单的部分,测试/调试每个部分,然后一次将这些部分放回一起,以避免集成错误。

例如,主要的挑战似乎是串行通信,所以我会写一个处理和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的串行监视器进行测试。

在您的处理草图中,不清楚为什么有两个串行端口(端口串行监视器)。

下面是您的处理草图的一个稍微修改的版本,它发送两个单字符命令或颜色字符串:

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 LEDs的测试,所以它可能不工作,但希望它说明了这个想法。(您应该仔细检查波特率、引脚、RGB颜色通道等。)

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

另外bare in mind 9600是一个相当低的波特率。您应该使用更高的波特率(例如115200、57600)进行测试,如果稳定,则使用这些波特率来避免在缓冲串行数据时驱动led的延迟。一般来说,尽可能避免/最小化阻塞(while)循环。

总的来说,这个想法是删除/删除代码中不需要的任何东西来深入研究问题:孤立地解决它。一旦调试并可靠地工作,每次添加一位新代码,在每次添加后进行测试(否则您可能会引入更多错误,而不是更难发现/修复的错误)。