您肯定走在正确的轨道上:delay() 确实会阻止其余代码执行,您可以使用millis() 来绕过该限制。
millis() 返回自 arduino 代码开始运行以来的毫秒数。您可以使用额外的变量来构建类似秒表的临时机制:
- 在
setup() 中会将当前的millis() 存储在一个变量中。对于那个变量,时间暂时冻结了:)
- 在
loop() 中,如果您不断调用millis(),您将获得不断增加的价值。如果您将当前时间(最近的millis() 呼叫)与较早的“冻结”毫秒结果进行比较,您可以及时分辨出两个时刻之间的差异。这有点类似于在现实世界中,您会有一个圈速计时器,按下/更新按钮会在时间流逝的同时拍摄经过的时间快照。
这是一个非常基本的草图来说明这个想法:
// a variable to store millis at a set time
long lastMillis;
// 5 seconds in millis
const long fiveSeconds = 5 * 1000;
void setup() {
Serial.begin(9600);
// remember the millis right now
lastMillis = millis();
}
void loop() {
// get current millis - ever increasing
long millisNow = millis();
// calculate the difference
long millisDifference = millisNow - lastMillis;
// print debug text: open Serial Monitor to view
Serial.print("time between setup complete and now:");
Serial.println(millisDifference);
// test after 5 seconds
if(millisDifference >= fiveSeconds){
Serial.println("5 seconds or more passed");
}
}
如果您打开串行监视器并将波特率设置为 9600,您应该会看到一些调试文本出现。希望评论文本能说明以上几点。
要做的第三件事是重置/更新lastMillis,以便每 5 秒满足条件,而不是像上面的代码那样在 5 秒后连续满足条件:
// a variable to store millis at a set time
long lastMillis;
// 5 seconds in millis
const long fiveSeconds = 5 * 1000;
void setup() {
Serial.begin(9600);
// remember the millis right now
lastMillis = millis();
}
void loop() {
// get current millis - ever increasing
long millisNow = millis();
// calculate the difference
long millisDifference = millisNow - lastMillis;
// print debug text: open Serial Monitor to view
Serial.print("time between setup complete and now:");
Serial.println(millisDifference);
// test after 5 seconds
if(millisDifference >= fiveSeconds){
Serial.println("5 seconds or more passed");
// update millis snaphot so this happens every 5 seconds
lastMillis = millis();
}
}
您可以使用它来构建一个系统以在 0 到 20 之间移动扫描,而无需使用 for/delay 块循环,而只需在每次 loop() 迭代时递增:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// a variable to store millis at a set time
long lastMillis;
// seconds to millis
const long INTERVAL = 4 * 1000;
int pos = 0; // variable to store the servo position
int targetPos = 0;// the target position rotate towards
void setup() {
Serial.begin(9600);
// remember the millis right now
lastMillis = millis();
myservo.attach(9); // attaches the servo on pin 9 to the servo object
myservo.write(pos);
pinMode(LED_BUILTIN, OUTPUT);//LedR
}
void loop() {
// get current millis - ever increasing
long millisNow = millis();
// calculate the difference
long millisDifference = millisNow - lastMillis;
// print debug text: open Serial Monitor to view
Serial.print("time between setup complete and now:");
Serial.println(millisDifference);
// test after 4 seconds
if(millisDifference >= INTERVAL){
Serial.println("4 seconds or more passed");
// update millis snaphot so this happens every 5 seconds
lastMillis = millis();
// flip between 0 and 20 servo target position by subtracting the current position from the maximum position
// e.g. 20 - 0 = 20, otherwise, 20 - 20 = 0
targetPos = 20 - targetPos;
}
// update servo position
updateServo();
}
// update servo without a blocking delay
// you could use an extra millis() based system
void updateServo(){
// difference between the current servo position and the next (target) servo position
int positionDifference = targetPos - pos;
// check the sign of the difference to tell if the servo should increment or decrement positions
// otherwise ignore
if(positionDifference > 0){
// the target position is greater than the current therefore increase
// feel free to change the increment to something nicer
pos++;
myservo.write(pos);
}else{
// the target position is smaller than the current therefore decrea
pos--;
myservo.write(pos);
}
}
注意以上代码未经测试,因此不保证可以工作,但希望能说明意图。
你还需要把作业分解成更小的步骤
我有一个 Arduino Uno、一个伺服电机和 2 个 LED(绿色和红色)。伺服电机每 4 秒旋转 20 度并返回。
我希望红色 LED (LedR) 在代码的前 4 秒内亮起,然后在接下来的 12 秒内变低。
我希望绿色 LED (ledG) 从代码的第 8 秒一直亮到第 12 秒,然后在接下来的 12 秒内保持低电平。
所以你的任务变成:
- 弄清楚如何每 X 秒触发一次动作(非阻塞):按上面排序
- 弄清楚如何移动伺服(非阻塞):上面排序
- 找出如何在 12 秒内以 4 秒间隔控制 LED 模式
这里的关键字是pattern。伺服器每 4 秒运行一次,LED 亮 4 秒然后灭 12 秒,这很好。
有多种方法可以解决此问题,但一种方法可以利用 12 可被 4 整除这一事实。您可以使用单个 4 秒,而不是为每个计时器使用基于毫秒的额外变量和条件计时器并制作与之相关的所有内容,就像鼓机上的节拍一样。
将您的任务视为 808 模式 Joey Bada$$ 会引以为豪不是很有趣吗? :P
这就是我的意思:
time(s): 4, 8, 12, 16
servo: [ 0][20][ 0][20]
red: [ 1][ 0][ 0][ 0]
green: [ 0][ 1][ 0][ 0]
您可以使用 4 秒间隔的计数器和 %(modulo) 运算符来检查您处于哪个 4 秒增量(每 4 秒、8 秒、12 秒等)以控制 LED。如果您想轻松更改模式,这会更节省内存,但灵活性/乐趣会降低。
您可以这样做,但要知道它会浪费更多宝贵的内存:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// a variable to store millis at a set time
long lastMillis;
// seconds to millis
const int SECONDS = 4;
const long INTERVAL_MILLIS = SECONDS * 1000;
int pos = 0; // variable to store the servo position
int targetPos = 0;// the target position rotate towards
/*
have an Arduino Uno, a Servo Motor & 2 LEDs (Green & Red). The Servo motor rotates 20 degrees and back every 4 seconds.
I would like the Red led (LedR) to be on for the first 4 seconds of code then low for the next 12 seconds.
I would like the Green led (ledG) to be on from the 8th second of code until the 12th then low for the next 12 seconds.
time(s): 4, 8, 12, 16
servo: [ 0][20][ 0][20]
red: [ 1][ 0][ 0][ 0]
green: [ 0][ 1][ 0][ 0]
*/
int intervalIndex;
// rotates 20 degrees and back every 4 seconds.
const int SERVO_PATTERN[4] = {0, 20, 0, 20};
// on for the first 4 seconds of code then low for the next 12 seconds.
const bool RED_PATTERN[4] = {1, 0, 0, 0};
// on from the 8th second of code until the 12th then low for the next 12 seconds.
const bool GREEN_PATTERN[4] = {0, 1, 0, 0};
const int LED_PIN_RED = LED_BUILTIN;
//LedG, maybe pin 12: TODO update to what you've got on your breadboard
const int LED_PIN_GREEN = 12;
void setup() {
Serial.begin(9600);
// remember the millis right now
lastMillis = millis();
myservo.attach(9); // attaches the servo on pin 9 to the servo object
myservo.write(pos);
pinMode(LED_PIN_RED, OUTPUT);//LedR
pinMode(LED_PIN_GREEN, OUTPUT);//
}
void loop() {
// get current millis - ever increasing
long millisNow = millis();
// calculate the difference
long millisDifference = millisNow - lastMillis;
// print debug text: open Serial Monitor to view
Serial.print("time between setup complete and now:");
Serial.println(millisDifference);
// test after 4 seconds
if(millisDifference >= INTERVAL_MILLIS){
Serial.println("4 seconds or more passed");
// update millis snaphot so this happens every 4 seconds
lastMillis = millis();
// update interval counter
intervalIndex++;
// reset every 4 => 0, 1, 2, 3, reset (perfect as array index)
if(intervalIndex > 3){
intervalIndex = 0;
}
// update servo target position
targetPos = SERVO_PATTERN[intervalIndex];
// update the red LED
digitalWrite(LED_PIN_RED, RED_PATTERN[intervalIndex]);
// update the green LED
digitalWrite(LED_PIN_GREEN, GREEN_PATTERN[intervalIndex]);
}
// update servo position
updateServo();
}
// update servo without a blocking delay
// you could use an extra millis() based system
void updateServo(){
// difference between the current servo position and the next (target) servo position
int positionDifference = targetPos - pos;
// check the sign of the difference to tell if the servo should increment or decrement positions
// otherwise ignore
if(positionDifference > 0){
// the target position is greater than the current therefore increase
// feel free to change the increment to something nicer
pos++;
myservo.write(pos);
}else{
// the target position is smaller than the current therefore decrea
pos--;
myservo.write(pos);
}
}
注意 和以前一样,我没有在 arduino 板上测试过代码,你需要测试/仔细检查接线、LED 引脚号等。希望 cmets 帮助解释整体概念。
这种方法的优点是代码比使用 3 个计时器要简单一些,并且您可以轻松更改模式(例如,开、关、关、开等)
bool 有效,因为HIGH/LOW 是真正的布尔值(true(1)、false(0))。
为了进一步说明,您可以使用 p5.js 运行上述逻辑:
var lastMillis;
// seconds to millis
const SECONDS = 4;
const INTERVAL_MILLIS = SECONDS * 1000;
var pos = 0; // variable to store the servo position
var targetPos = 0; // the target position rotate towards
/*
have an Arduino Uno, a Servo Motor & 2 LEDs (Green & Red). The Servo motor rotates 20 degrees and back every 4 seconds.
I would like the Red led (LedR) to be on for the first 4 seconds of code then low for the next 12 seconds.
I would like the Green led (ledG) to be on from the 8th second of code until the 12th then low for the next 12 seconds.
time(s): 4, 8, 12, 16
servo: [ 0][20][ 0][20]
red: [ 1][ 0][ 0][ 0]
green: [ 0][ 1][ 0][ 0]
*/
var intervalIndex = 0;
// rotates 20 degrees and back every 4 seconds.
const SERVO_PATTERN = [0, 20, 0, 20];
// on for the first 4 seconds of code then low for the next 12 seconds.
const RED_PATTERN = [1, 0, 0, 0];
// on from the 8th second of code until the 12th then low for the next 12 seconds.
const GREEN_PATTERN = [0, 1, 0, 0];
const LED_PIN_RED = 13;
const LED_PIN_GREEN = 12;
function setup() {
createCanvas(300, 300);
stroke(255);
textFont("Courier New",10);
lastMillis = millis();
}
function draw() {
// clear drawing
background(0);
// get current millis - ever increasing
var millisNow = millis();
// calculate the difference
var millisDifference = millisNow - lastMillis;
// print debug text: open Serial Monitor to view
// test after x seconds
if (millisDifference >= INTERVAL_MILLIS) {
console.log(SECONDS,"seconds or more passed");
// update millis snaphot so this happens every 5 seconds
lastMillis = millis();
// update interval counter
intervalIndex++;
// reset every 4 => 0, 1, 2, 3, reset (perfect as array index)
if (intervalIndex > 3) {
intervalIndex = 0;
}
}
// update servo target position
targetPos = SERVO_PATTERN[intervalIndex];
// update the red LED
digitalWrite(LED_PIN_RED, RED_PATTERN[intervalIndex]);
// update the green LED
digitalWrite(LED_PIN_GREEN, GREEN_PATTERN[intervalIndex]);
// update servo position
updateServo();
// debug text
showDebug();
}
function updateServo() {
// difference between the current servo position and the next (target) servo position
var positionDifference = targetPos - pos;
// check the sign of the difference to tell if the servo should increment or decrement positions
// otherwise ignore
if (positionDifference > 0) {
// the target position is greater than the current therefore increase
// feel free to change the increment to something nicer
pos++;
} else {
// the target position is smaller than the current therefore decrea
pos--;
}
drawServo();
}
function drawServo(){
push();
translate(50, 150);
rotate(radians(targetPos));
triangle(0 ,-25, // top
50, 0, // right
0 , 25); // bottom
pop();
}
// hacky LED visualisation
function digitalWrite(pin, value) {
if (pin == LED_PIN_RED) {
fill(value ? color(192, 0, 0) : color(0));
ellipse(150, 150, 50, 50);
}
if (pin == LED_PIN_GREEN) {
fill(value ? color(0, 192, 0) : color(0));
ellipse(250, 150, 50, 50);
}
fill(0);
}
const dr = [112, 130, 148, 167];
function showDebug(){
fill(255);
text("intervalIndex: " + intervalIndex + " relative seconds: " + ((intervalIndex + 1) * 4) +
"\nSERVO_PATTERN = [" + SERVO_PATTERN.map(i => nf(i,2)) + "]" +
"\nRED_PATTERN = [" + RED_PATTERN.map(i => nf(i,2)) + "]" +
"\nGREEN_PATTERN = [" + GREEN_PATTERN.map(i => nf(i,2)) + "]" , 10, 15);
fill(255, 64);
// 111
rect(dr[intervalIndex], 21, 12, 35);
fill(0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
总结:
- 将您的问题分解为可食用的小块
- 单独编写/测试/迭代每个块,直到它可以工作并且整洁/干净以进行集成
- 集成每个块,一次一个,每次添加时再次测试
您正在处理基本 LED 和伺服库非常好,否则如果您有更复杂的 LED 芯片组要驱动(例如 NeoPixel 之类的 RGB LED),微秒级的时间会很紧,具体取决于伺服器和 RGB LED 的数量你会遇到一些讨厌的interrupt 计时情况。
如果你有时间尝试其他东西,也许你可以连接一个扬声器而不是伺服系统和灯光,然后尝试Tone library 或更好的(但更多资源密集型)Mozzi library 以获得一些节拍与这 3模式。