【Follow me第二季第1期】基础任务1-3与进阶任务
[复制链接]
本帖最后由 MioChan 于 2024-7-31 00:09 编辑
基础任务一(必做):控制板载炫彩LED,跑马灯点亮和颜色变换
这个用Adafruit_NeoPixel.h这个库就能实现,还加入了一些额外的功能,左边的按钮可以切换亮度,右边的按钮可以切换灯效,一共设置了五种灯效,分别是三种常亮,追逐,彩虹。除此之外,按下按钮蜂鸣器也会依次发出do, re, mi, fa, sol, la, si的音调,通过调节占空比我们还能控制蜂鸣器音量。
#include <Adafruit_NeoPixel.h>
#define PIN 8
// 定义按钮引脚
#define BUTTON_BRIGHTNESS 4 // 调节亮度
#define BUTTON_EFFECT 5 // 切换效果
// 定义音调
const int tones[] = {262, 294, 330, 349, 392, 440, 494}; // do, re, mi, fa, sol, la, si
int toneIndex = 0; // 当前音调
int volume = 30; // 音量
Adafruit_NeoPixel strip = Adafruit_NeoPixel(10, PIN, NEO_GRB + NEO_KHZ400);
int currentEffect = 0;
int brightness = 50;
int lastButtonBrightnessState = LOW;
int lastButtonEffectState = LOW;
void setup() {
pinMode(BUTTON_BRIGHTNESS, INPUT); // 设置按钮
pinMode(BUTTON_EFFECT, INPUT);
pinMode(A0, OUTPUT); // 设置扬声器
strip.begin();
strip.setBrightness(brightness);
strip.show();
Serial.begin(9600);
}
void loop() {
// 检查按钮状态
checkButtons();
// 执行当前效果
switch (currentEffect) {
case 0:
colorWipe(strip.Color(255, 0, 0), 50);
break;
case 1:
colorWipe(strip.Color(0, 255, 0), 50);
break;
case 2:
colorWipe(strip.Color(0, 0, 255), 50);
break;
case 3:
theaterChase(strip.Color(127, 127, 127), 50);
break;
case 4:
rainbowCycle(20);
break;
}
}
bool checkButtons() {
bool buttonPressed = false;
// 检查D4按钮是否被按下以更改亮度
int buttonBrightnessState = digitalRead(BUTTON_BRIGHTNESS);
if (buttonBrightnessState == HIGH && lastButtonBrightnessState == LOW) {
brightness = (brightness + 30) % 128; // 增加亮度
strip.setBrightness(brightness);
strip.show();
playTone(); // 播放音调
buttonPressed = true;
delay(200);
}
lastButtonBrightnessState = buttonBrightnessState;
// 检查D5按钮是否被按下以更改效果
int buttonEffectState = digitalRead(BUTTON_EFFECT);
if (buttonEffectState == HIGH && lastButtonEffectState == LOW) {
currentEffect = (currentEffect + 1) % 5; // 效果循环
playTone(); // 播放音调
buttonPressed = true;
delay(200);
}
lastButtonEffectState = buttonEffectState;
return buttonPressed;
}
// 用一种颜色逐个填充像素点
void colorWipe(uint32_t c, uint8_t wait) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
if (checkButtons()) return; // 如果按钮被按下,则中断循环
}
}
// 使彩虹色均匀分布
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for (j = 0; j < 256 * 5; j++) {
for (i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
if (checkButtons()) return; // 如果按钮被按下,则中断循环
}
}
// 剧院风格的爬行灯
void theaterChase(uint32_t c, uint8_t wait) {
for (int j = 0; j < 10; j++) {
for (int q = 0; q < 3; q++) {
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, c); // 每三个像素点亮一个
}
strip.show();
delay(wait);
if (checkButtons()) return; // 如果按钮被按下,则中断循环
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0);
}
}
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
// 播放音调
void playTone() {
int frequency = tones[toneIndex];
int period = 1000000L / frequency;
int pulse = (period * volume) / 256;
for (long i = 0; i < 200L * frequency / 1000L; i++) {
digitalWrite(A0, HIGH);
delayMicroseconds(pulse);
digitalWrite(A0, LOW);
delayMicroseconds(period - pulse);
}
toneIndex = (toneIndex + 1) % 7; // 循环切换音调
}
基础任务二(必做):监测环境温度和光线,通过板载LED展示舒适程度
这里直接用到了#include <Adafruit_CircuitPlayground.h>这个库,传感器已经封装好了,直接获取数值就行。这里对灯珠做了一个划分,左边五颗用来显示温度,亮三颗处于绿色表示在人体舒服的20-24度,红色表示过热,蓝色表示过冷(因为MCU多少会发热,这个温度传感器放的太靠近了导致测得不准,我对比了一下温度计会比我环境高个五度所以代码里简单粗暴减了5)。右边五颗白色灯珠对应当前环境亮度,板子上所有led也会跟着亮度进行变化(环境亮度比较低的时候,LED也会变暗)
目前环境灯光温度都很适宜
把台灯关了右边亮度就剩一格了
压了一个冰盒到开发版上,可以看见温度显示为蓝色
#include <Adafruit_CircuitPlayground.h>
int prevTempLedCount = -1; // 初始化
int prevBrightnessLedCount = -1; // 初始化
void setup() {
CircuitPlayground.begin();
}
void loop() {
displayTemperature();
displayBrightness();
delay(1000); // 每秒更新
}
void displayTemperature() {
float temperature = CircuitPlayground.temperature() - 6;
int ledCount = 3; // 20-24度时亮三颗
uint32_t color = CircuitPlayground.colorWheel(85); // 绿色
if (temperature < 20) {
ledCount = max(0, 3 - (20 - temperature) / 5);
color = CircuitPlayground.colorWheel(170); // 蓝色
} else if (temperature > 24) {
ledCount = min(5, 3 + (temperature - 24) / 5);
color = CircuitPlayground.colorWheel(0); // 红色
}
// 如果LED数量没有变化,则不更新
if (ledCount != prevTempLedCount) {
for (int i = 0; i < 5; i++) {
if (i < ledCount) {
CircuitPlayground.setPixelColor(4 - i, color);
} else {
CircuitPlayground.setPixelColor(4 - i, 0); // 关闭LED
}
}
prevTempLedCount = ledCount; // 更新上一次的LED数量
}
}
void displayBrightness() {
int brightness = CircuitPlayground.lightSensor();
int ledCount = min(5, brightness / 50); // 亮度每增加50 LED增加一颗
uint8_t overallBrightness = map(brightness, 0, 500, 0, 100);
// 如果LED数量没有变化,则不更新
if (ledCount != prevBrightnessLedCount) {
CircuitPlayground.setBrightness(overallBrightness); //调整所有灯珠亮度
for (int i = 0; i < 5; i++) {
if (i < ledCount) {
CircuitPlayground.setPixelColor(5 + i, 255, 255, 255); // 白色
} else {
CircuitPlayground.setPixelColor(5 + i, 0); // 关闭LED
}
}
prevBrightnessLedCount = ledCount; // 更新上一次的LED数量
}
}
基础任务三(必做):接近检测——设定安全距离并通过板载LED展示,检测到入侵时,发起声音报警
前面已经用过光照传感器了,这里发现红外还没玩,所以这里我用红外传感器来做,通过IR LED发射38KHZ的IR脉冲,再读取接收器测得的模拟值,当模拟值增大即有物体靠近。没有物体接近时灯珠全灭或显示一个,随着物体接近灯珠会亮的越来越多,最后全亮蜂鸣器报警。
下面是文档中的IR介绍:
没有物体靠近时
用手靠近时,灯珠全亮以及蜂鸣器报警
#include <Adafruit_CircuitPlayground.h>
#define SAFE_DISTANCE 450 // 定义安全距离
const int alertTone = 1000; // 警报音调
const int irTransmitterPin = 25; //引脚定义
const int irReceiverPin = A10;
void setup() {
CircuitPlayground.begin();
Serial.begin(9600); //
pinMode(irReceiverPin, INPUT); // 红外传感器输入
pinMode(irTransmitterPin, OUTPUT);// 红外led输出
delay(100);
}
void loop() {
sendIRPulse();
int distance = analogRead(irReceiverPin); // 读取红外传感器的值
displayDistance(distance);
checkForIntrusion(distance);
delay(300);
}
void displayDistance(int distance) {
int ledCount = map(distance, 290, SAFE_DISTANCE, 1, 10); // 将距离值映射到0-10的LED数量
Serial.print("Distance: ");
Serial.print(distance);
Serial.print(", LED Count: ");
Serial.println(ledCount);
for (int i = 0; i < 10; i++) {
if (i < ledCount) {
CircuitPlayground.setPixelColor(i, 0, 255, 0);
} else {
CircuitPlayground.setPixelColor(i, 0);
}
}
}
void checkForIntrusion(int distance) {
if (distance > SAFE_DISTANCE) {
Serial.println("Intrusion detected!");
playAlertTone();
}
}
void sendIRPulse() {
for (int i = 0; i < 32; i++) {
digitalWrite(irTransmitterPin, HIGH);
delayMicroseconds(13);
digitalWrite(irTransmitterPin, LOW);
delayMicroseconds(13);
}
}
void playAlertTone() {
CircuitPlayground.playTone(alertTone, 500); // 播放警报音500ms
}
进阶任务(必做):制作不倒翁——展示不倒翁运动过程中的不同灯光效果
这个任务本质上就是用板载陀螺仪检测板子的姿态,然后给出对应的灯光效果。我实现的是根据板子的倾斜方向亮起对应的灯,当板子水平时所有灯全亮。唯一有点麻烦的是,要搞清楚不同x y z值对应的真实方向,为了方便处理我直接计算了x y两个方向对于x y水平面的夹角。
angleX = atan2(x, z) * 180 / PI; angleY = atan2(y, z) * 180 / PI;
水平时所有灯都亮,并为绿色(这里是对着桌面俯拍)
朝不同方向倾斜的效果,可以看见都能很好的识别,灯珠的颜色是根据灯珠编号定义的 CircuitPlayground.colorWheel(LED Number),这样刚好构成了一个色环
直接把板子垂直也能正确识别
拆了个家里的不倒翁,把开发版安在了底座上
下面是代码
#include <Adafruit_CircuitPlayground.h>
#include <math.h>
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
}
void loop() {
float x = CircuitPlayground.motionX();
float y = CircuitPlayground.motionY();
float z = CircuitPlayground.motionZ();
// 计算与水平面夹角
float angleX = atan2(x, z) * 180 / PI;
float angleY = atan2(y, z) * 180 / PI;
int ledToLight = -1;
//映射到彩色灯珠
if(angleX>11){
if(angleY<-11) ledToLight = 1;
if(angleY<11 && angleY>-11) ledToLight = 2;
if(angleY>11) ledToLight =3;
}
if(angleX>-11&& angleX<11){
if(angleY>11)
{
ledToLight =4;
// CircuitPlayground.setPixelColor(5, CircuitPlayground.colorWheel(85));
}
if(angleY<-11)
{
ledToLight = 0; // X: 0, Y: -9, Z: 0
// CircuitPlayground.setPixelColor(9, CircuitPlayground.colorWheel(85));
}
}
if(angleX<-11){
if(angleY<-11) ledToLight = 8;
if(angleY<11 && angleY>-11) ledToLight = 7;
if(angleY>11) ledToLight =6;
}
uint8_t color= map( ledToLight, 0, 9, 0, 255);
if (ledToLight == -1) {
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, CircuitPlayground.colorWheel(85)); //水平时都点亮
}
} else {
for (int i = 0; i < 10; i++) {
if (i == ledToLight || (ledToLight == 4 && i == 5) || (ledToLight == 0 && i == 9)) {
CircuitPlayground.setPixelColor(i, CircuitPlayground.colorWheel(color));//点亮对应位置的
} else {
CircuitPlayground.setPixelColor(i, 0);
}
}
}
Serial.print("X: ");
Serial.print(x);
Serial.print(" - Y: ");
Serial.print(y);
Serial.print(" - Z: ");
Serial.print(z);
Serial.print(" - angleX: ");
Serial.print(angleX);
Serial.print(" - angleY: ");
Serial.print(angleY);
Serial.print(" - LED To Light: ");
Serial.println(ledToLight);
delay(100);
}
到这里基本任务就做完了,开始折腾创意任务啦,三个打算尽量都做做看,视频到时候会放到汇总贴里
|