基于Arduino与Neopixel的红外选通开关语句问题求助
问题:单Arduino Uno实现Neopixel图案播放与红外信号接收的冲突解决
项目背景
基于Arduino Uno的项目:外接红外接收器,用遥控器选择Neopixel灯带的预设图案(图案取自Neopixel Strand test示例)。
当前代码
//Always comment your code like a violent psychopath will be maintaining it and they know where you live #include <Adafruit_NeoPixel.h> //Neopixel Library #include <IRLibAll.h> //IR reciever Library IRrecvLoop myReceiver(2); //IR receiver on IO pin 2 IRdecode myDecoder; //Decoder object unsigned long oldCode = 00000000; //Comparison variable to evaluate the execution of the 'Check' function later Adafruit_NeoPixel strip (3,3,NEO_RGBW + NEO_KHZ800); //Creates the Pixel strip as an object in the code void setup() { strip.begin(); //Initialise the Neopixel strip strip.show(); //Initialise all pixels to 'off' myReceiver.enableIRIn(); // Start the IR receiver } void loop() { check(); //Run the check function delay(20); //Delay before running the loop again by 20 milliseconds giving time to recieve signals } void check() { //check Function: Checks for an IR signal before nominating which of the test displays to run if (oldCode = myDecoder.value){ //Evaluates if the IR code recieved from the remote matches 'oldCode' and if it does.... return; //Terminates the check Function returning its values } if (myReceiver.getResults()) { myDecoder.decode(); if (myDecoder.protocolNum == NEC) { switch(myDecoder.value) { //Activate this switch statement based on the value 'myDecoder' holds case 0xFFA25D: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFE21D: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF629D: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF22DD: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF02FD: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFC23D: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFE01F: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFA857: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF906F: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF9867: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFB04F: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF6897: colorWipe(strip.Color( 0, 0, 0), 50); // Black/off "0" Serial.println("0 - Black/off"); break; case 0xFF30CF: colorWipe(strip.Color(255, 0, 0), 50); // Red "1" Serial.println("1 - All red"); break; case 0xFF18E7: colorWipe(strip.Color( 0, 255, 0), 50); // Green "2" Serial.println("2 - All green"); break; case 0xFF7A85: colorWipe(strip.Color( 0, 0, 255), 50); // Blue "3" Serial.println("3 - All blue"); break; case 0xFF10EF: theaterChase(strip.Color(127, 127, 127), 50); // White "4" Serial.println("4 - All white"); break; case 0xFF38C7: theaterChase(strip.Color(127, 0, 0), 50); // Red "5" Serial.println("5"); break; case 0xFF5AA5: theaterChase(strip.Color( 0, 0, 127), 50); // Blue "6" Serial.println("6"); break; case 0xFF42BD: rainbow(10); // "7" Serial.println("7"); break; case 0xFF4AB5: theaterChaseRainbow(50); // "8" Serial.println("8"); break; case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break; case 0xFFFFFFFF: Serial.println("Please release button and reselect"); break; } } oldCode = myDecoder.value; //make the new button state equal the old buttonstate preventing the button from activating if statement } } void colorWipe(uint32_t color, int wait) { //Colour wipe test while(true) { for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip... strip.setPixelColor(i, color); // Set pixel's color (in RAM) strip.show(); // Update strip to match check(); delay(wait); // Pause for a moment } } } void theaterChase(uint32_t color, int wait) { //Theatre Chase test while(true) { for(int a=0; a<10; a++) { // Repeat 10 times... for(int b=0; b<3; b++) { // 'b' counts from 0 to 2... strip.clear(); // Set all pixels in RAM to 0 (off) for(int c=b; c<strip.numPixels(); c += 3) { // 'c' counts up from 'b' to end of strip in steps of 3... strip.setPixelColor(c, color); // Set pixel 'c' to value 'color' check(); //Run the check function } strip.show(); // Update strip with new contents delay(wait); // Pause for a moment } } } } void rainbow(int wait) { //Rainbow test function while(true) { //while this function is active for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) { //Sets differing colours for the rainbow for(int i=0; i<strip.numPixels(); i++) { //For each pixel in strip... int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels()); //balances the colour pattern along the length of the strip strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue))); //run the colour pattern along the strip check(); //run the check function } strip.show(); //Update strip with new contents delay(wait); //Pause for a moment } } } void theaterChaseRainbow(int wait) { while(true) { int firstPixelHue = 0; //First pixel starts at red for(int a=0; a<30; a++) { //Repeat 30 times... for(int b=0; b<3; b++) { //'b' counts from 0 to 2... strip.clear(); //Set all pixels to off for(int c=b; c<strip.numPixels(); c += 3) { //'c' counts up from 'b' to end of strip in increments of 3, hue of pixel 'c' is offset by an amount to make one full revolution of the color wheel (range 65536) along the length of the strip (strip.numPixels() steps): int hue = firstPixelHue + c * 65536L / strip.numPixels(); //create the hue variable and balance the rainbow colouring across the strip uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB strip.setPixelColor(c, color); // Set pixel 'c' to value 'color' check(); //Run the check function } strip.show(); // Update strip with new contents delay(wait); // Pause for a moment firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames } } } }
问题描述
试过两种写法:主loop调用check函数判断红外输入,或者直接把switch语句放主loop里。核心问题是:灯带播放图案时,程序陷入图案函数的while(true)死循环,导致红外接收器无法正常接收信号;但图案需要循环才能持续播放。曾考虑用两个Arduino分别处理,但项目要求必须用单个Arduino Uno实现。
解决方案
核心思路
去掉图案函数里的while(true)死循环,改用状态机模式:在主loop中维护当前播放的图案状态,每次loop只执行图案的一小步,同时检查红外输入。
修改步骤
- 定义状态变量:在全局范围添加变量记录当前选中的图案及执行进度:
// 定义图案状态枚举 typedef enum { STATE_OFF, STATE_COLOR_WIPE_RED, STATE_COLOR_WIPE_GREEN, STATE_COLOR_WIPE_BLUE, STATE_THEATER_CHASE_WHITE, STATE_THEATER_CHASE_RED, STATE_THEATER_CHASE_BLUE, STATE_RAINBOW, STATE_THEATER_CHASE_RAINBOW } PatternState; PatternState currentState = STATE_OFF; // 默认初始状态 // 图案执行进度跟踪变量 unsigned long lastUpdateTime = 0; int colorWipeStep = 0; int theaterChaseA = 0; int theaterChaseB = 0; long rainbowHue = 0; int theaterChaseRainbowA = 0; int theaterChaseRainbowB = 0; long theaterChaseRainbowHue = 0;
- 重构图案函数:将每个图案函数改为执行单步操作,不再包含死循环:
// 单步执行colorWipe void colorWipeStep(uint32_t color, int wait) { if(millis() - lastUpdateTime >= wait) { strip.setPixelColor(colorWipeStep, color); strip.show(); colorWipeStep++; if(colorWipeStep >= strip.numPixels()) { colorWipeStep = 0; strip.clear(); } lastUpdateTime = millis(); } } // 单步执行theaterChase void theaterChaseStep(uint32_t color, int wait) { if(millis() - lastUpdateTime >= wait) { strip.clear(); for(int c = theaterChaseB; c < strip.numPixels(); c += 3) { strip.setPixelColor(c, color); } strip.show(); theaterChaseB++; if(theaterChaseB >= 3) { theaterChaseB = 0; theaterChaseA++; if(theaterChaseA >= 10) { theaterChaseA = 0; } } lastUpdateTime = millis(); } } // 单步执行rainbow void rainbowStep(int wait) { if(millis() - lastUpdateTime >= wait) { for(int i = 0; i < strip.numPixels(); i++) { int pixelHue = rainbowHue + (i * 65536L / strip.numPixels()); strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue))); } strip.show(); rainbowHue += 256; if(rainbowHue >= 3*65536) { rainbowHue = 0; } lastUpdateTime = millis(); } } // 单步执行theaterChaseRainbow void theaterChaseRainbowStep(int wait) { if(millis() - lastUpdateTime >= wait) { strip.clear(); for(int c = theaterChaseRainbowB; c < strip.numPixels(); c += 3) { int hue = theaterChaseRainbowHue + c * 65536L / strip.numPixels(); uint32_t color = strip.gamma32(strip.ColorHSV(hue)); strip.setPixelColor(c, color); } strip.show(); theaterChaseRainbowB++; if(theaterChaseRainbowB >= 3) { theaterChaseRainbowB = 0; theaterChaseRainbowA++; if(theaterChaseRainbowA >= 30) { theaterChaseRainbowA = 0; } theaterChaseRainbowHue += 65536 / 90; } lastUpdateTime = millis(); } }
- 修改check函数:不再直接调用死循环图案函数,而是切换状态变量并重置进度:
void check() { if(myReceiver.getResults()) { myDecoder.decode(); if(myDecoder.protocolNum == NEC && myDecoder.value != oldCode && myDecoder.value != 0xFFFFFFFF) { switch(myDecoder.value) { case 0xFFA25D: case 0xFFE21D: case 0xFF629D: case 0xFF22DD: case 0xFF02FD: case 0xFFC23D: case 0xFFE01F: case 0xFFA857: case 0xFF906F: case 0xFF9867: case 0xFFB04F: case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break; case 0xFF6897: currentState = STATE_OFF; strip.clear(); strip.show(); Serial.println("0 - Black/off"); break; case 0xFF30CF: currentState = STATE_COLOR_WIPE_RED; colorWipeStep = 0; Serial.println("1 - All red"); break; case 0xFF18E7: currentState = STATE_COLOR_WIPE_GREEN; colorWipeStep = 0; Serial.println("2 - All green"); break; case 0xFF7A85: currentState = STATE_COLOR_WIPE_BLUE; colorWipeStep = 0; Serial.println("3 - All blue"); break; case 0xFF10EF: currentState = STATE_THEATER_CHASE_WHITE; theaterChaseA = 0; theaterChaseB = 0; Serial.println("4 - All white"); break; case 0xFF38C7: currentState = STATE_THEATER_CHASE_RED; theaterChaseA = 0; theaterChaseB = 0; Serial.println("5"); break; case 0xFF5AA5: currentState = STATE_THEATER_CHASE_BLUE; theaterChaseA = 0; theaterChaseB = 0; Serial.println("6"); break; case 0xFF42BD: currentState = STATE_RAINBOW; rainbowHue = 0; Serial.println("7"); break; case 0xFF4AB5: currentState = STATE_THEATER_CHASE_RAINBOW; theaterChaseRainbowA = 0; theaterChaseRainbowB = 0; theaterChaseRainbowHue = 0; Serial.println("8"); break; } oldCode = myDecoder.value; myReceiver.resume(); // 重启红外接收 } } }
- 修改主loop:每次loop先检查红外输入,再根据当前状态执行对应图案的单步操作:
void loop() { check(); // 优先检查红外输入 // 根据当前状态执行对应图案的单步 switch(currentState) { case STATE_OFF: break; case STATE_COLOR_WIPE_RED: colorWipeStep
相关产品推荐
相关产品推荐

