- 2024-12-03
-
加入了学习《【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器》,观看 【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器
- 2024-12-01
-
加入了学习《FollowMe 第二季:3 - EK_RA6M5 开发板入门》,观看 EK-RA6M5 开发板入门
-
发表了主题帖:
【Follow me第二季第3期】EK_RA6M5入门任务——搭建环境,下载调试示例程序,Blink...
本帖最后由 MioChan 于 2024-12-1 15:58 编辑
本期Follow me的板子是瑞萨的EK_RA6M5,这种类型的开发板也是我第一次折腾,需要使用瑞萨的开发环境。
下面就简单分享一下环境搭建的方法,切记保证fsp版本一一对应,不要下错版本,否则后面会出现各种问题。
首先我们要下载对应fsp的e2studio,github的下载页面在这里:
https://github.com/renesas/fsp/releases/tag/v5.5.0
如果是windows用户可以直接访问 https://github.com/renesas/fsp/releases/download/v5.5.0/setup_fsp_v5_5_0_e2s_v2024-07.exe 直接进行下载。
下载后文件名是setup_fsp_v5_5_0_e2s_v2024-07.exe,运行进行安装即可,安装位置随意,并不一定要装到C盘,安装过程中主要改动以下两个地方选择,剩下的都是直接下一步就行
安装类型选择自定义
这一步把终端TM Terminal前的勾选上就行了
之后一直下一步就安装完成了。
下面我们还要下载官方提供的示例工程,对应的github仓库在这里:https://github.com/renesas/ra-fsp-examples/releases
主要容易踩坑的地方就是下错fsp版本,我们前面使用的是v5.5.0的fsp所以这里需要对应好。windows用户可以直接点这里直接下载,已经帮找好了:https://github.com/renesas/ra-fsp-examples/releases/download/v5.5.0.example.2/r20an0619eu0141-ek-ra6m5-exampleprojects.zip
下载后是一个压缩包,文件名为r20an0619eu0141-ek-ra6m5-exampleprojects.zip
解压后文件夹中的_quickstart就是我们需要的工程文件了。之后打开e2 studio导入这个工程的文件夹就可以了,搭建环境完毕。
下面我们开始下载调试示例程序,首先来编译工程,点击这个锤子后稍等一下,然后出现构建完成的提示后就可以了
接下来把线接到开发板的debug口,开始调试,细节可以看图片上的说明
到这里入门任务就完成了~
- 2024-10-25
-
回复了主题帖:
【2024 DigiKey 创意大赛】AI全功能环境监测站
秦天qintian0303 发表于 2024-10-25 13:29
这个透明的屏幕是什么啊?看着很高级啊
上一届大赛做的全息显示器
-
加入了学习《【2024 DigiKey 创意大赛】AI全功能环境监测站作品功能演示视频》,观看 【2024 DigiKey 创意大赛】AI全功能环境监测站作品功能演示视频
- 2024-10-24
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5套件开箱贴
lugl4313820 发表于 2024-10-24 18:58
不会吧,我的一个多星期了。还没有发货的消息!
看了一下我是17号下单的,不行工作时间去得捷官网找客服问问
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5套件开箱贴
lugl4313820 发表于 2024-10-24 12:47
你的用了几天呀,我下单了,还没有看到有发货信息。
第一时间下单,昨天到的
-
发表了主题帖:
【Follow me第二季第3期】EK-RA6M5套件开箱贴
本帖最后由 MioChan 于 2024-10-24 09:37 编辑
很高兴能入围Follow me第二季第3期的活动,收到入围邮件后火速去得捷下单了,很快就收到了板子,趁热来个开箱贴
外包装的纸箱还是一如既往的大,为了方便拿在快递站就把外箱扔了,直接来看本体吧,整个套件的包装盒是纯白色的
打开后有一封欢迎信
下面就是配件盒,里面一共有三条线,分别是Micro USB数据线、OTG线、网线
再往下一层就是开发板本体了,第一次接触这家的板子,总之接口很丰富,做工看着也很好
- 2024-10-23
-
发表了主题帖:
【2024 DigiKey 创意大赛】AI全功能环境监测站
本帖最后由 MioChan 于 2024-10-23 20:09 编辑
一、作品简介
1.1作品功能介绍
本次创意大赛中,我设计并实现了一个AI全能环境监测站。该设备通过集成多款高精度气体传感器,能够全面监测环境中的多种气体成分,如温湿度、空气质量、二氧化碳和各类有害气体等。所有环境数据不仅可以实时显示在 Matrix RGB点阵屏上,还能够通过 Adafruit IO云平台上传,实现多设备多应用间的环境监测数据共享。用户可以通过网页端查看不同环境指标的实时数据及其变化趋势,获取清晰的历史数据图表和变化曲线。
为了增强用户体验,该系统还加入了智能语音提醒与建议功能。基于 Adafruit MatrixPortal M4开发板,系统不仅完成了环境数据的采集、显示和上传,还通过开发的 Unity 3D虚拟人物程序,实现了与用户的互动。虚拟人物能够根据环境数据做出及时反馈,通过语音提醒用户注意环境变化,例如空气质量下降时提供改善建议或提醒开窗通风等。
系统的AI核心在于本地部署的大型语言模型(LLM)和文字转语音(TTS)技术。LLM采用的是本地部署的Chat-GLM模型,负责分析采集到的环境数据,生成相应的文字反馈,TTS则将这些文字转换为语音,使虚拟人物能够与用户进行自然的语音互动。
该项目具有广泛的应用场景和前景,不仅可以应用于智能家居和室内环境监测,还可以在智能城市以及各种需要实时环境监控和反馈的场景中发挥作用。整个项目的子系统的设计保持了高内聚低耦合的特点,所有系统复用都十分方便。通过结合主流的AI技术和传感器硬件,本项目为未来的环境监测设备提供了一种智能化、全方位的解决方案。
1.2物料清单
本项目的物料清单包括多种硬件和软件组件,确保了系统的多功能性和稳定运行。首先介绍本次大赛由得捷报销的物料:
Adafruit MatrixPortal M4开发板:境监测站硬件部分的核心控制器。这款开发板专为驱动RGB点阵屏设计,具备内置的HUB75接口,直接支持与点阵屏的连接,极大简化了硬件布线。此外,内置的ESP32模块为设备提供了Wi-Fi功能,能够通过无线网络上传数据至云端,适合环境监控场景下的数据远程传输与共享。
在数据处理和传感器方面,项目采用了多款环境传感器。BME680传感器是一款集温度、湿度、气压和空气质量(VOC)检测于一体的传感器,它为系统提供了全面的环境监测功能。该传感器特别适合对室内空气质量的检测,并能实时反馈空气污染状况。SGP30传感器专注于监测二氧化碳和挥发性有机化合物(TVOC)的浓度,帮助用户及时了解室内空气质量变化,确保健康和安全。为了进一步提升监测的全面性,系统还集成了MiCS-5524传感器,用于检测环境中的C2H5OH,C3H8,C4H10,CH4,CO,H2,H2S,NH3的存在。这些气体可能对人体健康产生危害,因此MiCS-5524的加入极大增强了系统的安全性预警能力。
以下是本人自备物料,包括所用软件:
Adafruit RGB LED Matrix 64x32点阵屏。通过MatrixPortal M4的驱动,该点阵屏实时显示采集到的环境数据。64x32的分辨率允许在小面积内展示丰富的内容,并且支持动态滚动,用户可以通过网络实现自定义文字弹幕的远程控制,使得数据展示更加灵活和多样化。高亮度的LED点阵屏适合用于各种光线条件下的展示,确保数据可读性。
全息显示器。上一届创意大赛的硬件作品,本次也拿来继续使用了。不过与之前不同的是,上一次项目虚拟人物部分的实现使用的是Live2D,虽然有3D的感觉,但本质上还是2D模型,所有看似立体的动画只是在平面图形上做变形,很难实现与3D物体的交互。而在本项目中我重写了一套Unity 3D程序,直接驱动了一个VRM 3D人物模型,并且使用了Meta的Lip Sync技术实现人物口型与语音的同步,除了获取天气预报外,还加入了从IO云服务器获取环境监测站空气指标的功能,但空气质量变差,AI助手会语音提示用户。
GPU计算设备。在AI方面,本项目使用了本地部署的LLM大语言模型。LLM作为虚拟人物的灵魂,为虚拟人物赋能交互能力,本项目还集成了TTS(文字转语音)模型,将LLM生成的文字通过自然流畅的语音形式播报出来,增强了虚拟人物的交互能力,用户可以通过语音形式了解当前环境状况及相应的建议。
二、系统框图
系统的整体框架可以分为硬件部分、Unity 3D程序、AI服务端与Adafruit IO平台。硬件部分主要负责环境数据的采集与显示。通过MatrixPortal M4开发板,系统能够连接多种气体传感器,实时获取环境指标数据,这些数据不仅会通过点阵屏进行展示,还会定时上传到Adafruit IO平台,供用户在网页端查看历史数据和变化趋势。同时,MatrixPortal M4也会接收来自Adafruit IO的弹幕信息,并滚动显示在点阵屏上。
Unity 3D程序是该系统的重要交互界面。通过3D虚拟人物,用户可以更加直观地获取环境数据和反馈。虚拟人物能够根据当前的环境指标与用户互动,并展示气象预报等其他信息。此外,Unity程序还负责与AI服务端的通信,将用户的消息发送到AI服务端进行处理,由AI服务端的GLM大型语言模型生成智能响应。这些响应再通过文字转语音(TTS)技术转化为语音输出,实现虚拟人物的口型同步和语音播报。
AI服务端的智能处理能力体现在对环境数据和用户交互的深度分析上。通过GLM模型,AI不仅能够理解用户的输入,还能够根据环境数据提供合理的建议或提醒。结合TTS技术,AI服务端能够生成自然流畅的语音反馈,进一步提升用户的交互体验。
三、各部分功能说明
3.1硬件部分
在本项目中,硬件平台选用了 Adafruit MatrixPortal M4,这是专门为驱动RGB点阵屏设计的开发板。其最大优势在于板载的 HUB75 接口,使其能够直接插入并驱动RGB点阵屏,无需复杂的电线连接。此外,MatrixPortal M4还具备Wi-Fi功能,方便将采集到的环境数据通过互联网上传至 Adafruit IO 平台。MatrixPortal M4还集成了 I2C接口,支持与多种传感器的连接。本项目使用的传感器包括 BME680(用于检测温湿度、气压及VOC)、SGP30(检测CO2及总VOC)、以及 MiCS-5524(专门监测一氧化碳等有害气体)。这些传感器通过I2C接口实现与主板的通信。主板负责读取这些传感器的数据,并将其展示在RGB点阵屏上。此外,通过Wi-Fi连接,这些数据会实时上传到Adafruit IO平台,方便用户远程查看。
RGB点阵屏是系统的主要显示方式。除了展示环境数据,点阵屏还会滚动显示从Adafruit IO接收到的弹幕信息,实现远程与用户的交互。硬件部分主要实现的功能如下:
显示环境信息:
设计了一套UI,屏幕会滚动显示各项环境指标,每一屏最多显示三项,滚动播放。对于部分指标数值过长,导致单位显示不下的问题,对单位也额外增加了相应的逻辑,用来滚动显示这些显示不下的内容。
自定义弹幕文字显示:
该功能主要是用来滚动显示一些弹幕文字,通过网络功能实现。本来想和之前的项目一样做一个接受POST请求的API服务器,但这个版本的CircuitPy似乎对网络底层库支持的不太好,使用之前在Adafruit ESP32S3 TFT Feather的代码总是出问题,最后看了一下官方文档发现adafruit_matrixportal 已经包含了一些网络功能的实现,最后想了想决定通过Adafruit IO https://io.adafruit.com/ 来解决这个问题,这既简化了网络通信的复杂性,又能利用已经成熟的工具来处理数据。Adafruit IO 提供了一个相对容易上手的接口,可以通过publish和subscribe来接收和显示文本信息。在项目中,我将设备连接到Adafruit IO的服务中,创建了两个feed一个用于接受弹幕内容另一个定义弹幕颜色。通过订阅feed,我能够实时地从远程设备或应用发送数据,并在点阵屏上滚动显示。并且这样也解决了之前只能内网访问的问题,通过Adafruit IO服务托管这些信息,我们可以在任意位置的连接互联网的电脑上发布或订阅这些内容。最重要的是Adafruit IO也有相应的python库,我们也可以在其他python服务程序中更新这些内容。当然,已经做了中文字库支持。
传感器接入:
建立好显示环境信息的UI后接下来就是实际连接传感器了,更新面板的数据了。下面是我用到的气体传感器。
BME680 是来自 Bosch Sensortec 的环境传感器,能够同时检测多种环境参数,广泛应用于空气质量监测。它集成了气体、湿度、压力和温度传感器。
SGP30 是 Sensirion 开发的一款空气质量传感器,主要用于检测空气中的二氧化碳 (CO₂) 和总挥发性有机物 (TVOC)。
MiCS-5524 是一款半导体气体传感器,专门用于检测一氧化碳 (CO) 和甲烷 (CH₄) 等有害气体。它基于金属氧化物半导体 (MOS) 技术,能够在低浓度下对一氧化碳表现出高度灵敏性。因为不支持i2c所以直接用飞线连接到了A0引脚。
将上面的传感器连接到开发板即可。
顺便一提,Adafruit这些i2c接口的连接线(上)和常用的4p80mm连接线(下)的线序并不一样,我们需要在另一端做一个交叉。简单来说,用尖嘴镊子把接线端子顶出来,换到镜像的位置即可。
数据记录功能:
既然是环境监测站自然要有保存历史数据的功能,这里我最初的是想法是将传感器数据写入到板载存储器中。不过这样有个很大的问题,这些数据只有连接到这个板子的电脑才能读取,用网络实现不仅麻烦,如果外部设备频繁请求这些信息也会大量消耗板子有限的计算资源。最后还是想到了用Adafruit IO,我们可以在IO中创建不同环境指标的feed,让板子定时上传这些数据到IO上,这样不仅能记录历史数据,其他应用能非常方便的获取这些数据,还能直接在IO创建一个环境监测面板直接查看这些数据,也不需要再额外设计一个软件来画图了。
下面是监测面板的效果:
底座制作:
这个LED点阵屏接上开发板后有点重心不稳,本身是考虑给它直接做一个外壳嵌进去,不过在测量数据的过程中发现,这个板子的后面还是有点热的,直接把传感器都放进去,板子后面的积热会让温度测量不准,还得考虑通风问题。所以最后索性直接设计一个底座就好了,把线卡到后面,即能让屏幕更稳固也解决了散热问题。
模型建好后直接用3D打印制作就好了,因为插销部分有悬空,切片时加了个支撑
3.2 Unity3D程序部分
系统中的 Unity程序负责虚拟人物的创建和驱动。虚拟人物不仅只是拥有皮囊,通过大语言模型(LLM)和文本转语音(TTS)让其还具有灵魂,通过lip sync技术还能使得3D人物角色进行口型同步,让虚拟人物在发声时口型与语音内容相一致,使其更加自然逼真。
VRM系统是这个虚拟系统的核心,用来驱动虚拟角色的展示。VRM模型是基于Unity中的动画状态机来进行管理的。通过动画状态机,我们能够定义虚拟角色的各种动作和状态切换。每一个状态代表虚拟角色在某种特定情境下的表现,比如迎宾、待机、或是发出环境警告。当用户与虚拟角色进行交互时,状态机会根据设定好的触发条件切换到对应的动作状态。例如,当用户点击虚拟角色时,程序会通过触发器(trigger)切换到迎宾动画。而在系统监测到环境数据出现异常,如空气质量下降时,状态机会切换到警告状态,角色通过特定的动画和表情来提醒用户。动画状态机的使用保证了虚拟角色在不同场景下的灵活表现,使其行为更自然、更贴近实际。因为我本身是VRChat玩家,所以手上也有不少人物模型可以使用,本项目所使用的模型是本人在Booth上自费购买的,无版权问题。
在网络请求部分,程序与多个后端服务进行交互,主要包括文本转语音(TTS)服务和大语言模型(LLM)的智能对话功能。对于TTS服务,程序会根据当前的系统状态生成语音文本,并通过HTTP POST请求将生成的文本发送到后端TTS服务。TTS服务处理后返回语音数据,程序会将该数据同步播放,并结合Meta Lipsync实现虚拟角色的口型同步,保证角色能够准确表达语音内容。大语言模型部分,当用户提出问题或进行交互时,程序会将用户的文本请求发送给LLM,后端模型生成智能回复后,程序会将其显示在UI中并交由TTS服务播报给用户。这样,虚拟角色不仅能够播报环境数据,还能与用户进行智能化的对话和互动,提升了系统的实用性和娱乐性。
Meta Lipsync 技术在系统中的作用是通过语音合成时生成的音频数据与虚拟角色的口型进行同步。系统会解析TTS生成的语音数据,计算音频中的元音、辅音等关键音素,并实时调整虚拟角色的嘴部动画,以匹配发音的变化。这一技术保证了虚拟角色的口型动作能够与语音保持高度一致,使其在语音播放过程中看起来更加自然逼真。用户在观看角色时,不仅可以听到与环境数据相关的语音信息,还能看到与之同步的口型动作,增强了虚拟角色的表现力。
在环境指标获取方面,程序集成了两个主要的数据来源:和风天气API和Adafruit IO平台。通过和风天气API,程序能够实时获取当前的天气数据,包括室外温度、未来降水情况,并根据这些数据对UI进行相应更新。此外,程序还通过Adafruit IO平台获取空气质量监测站的上传数据,包括室内温度、湿度、气压、有害气体等环境信息。这些数据通过网络发送到虚拟角色所在的系统,实时更新虚拟角色的环境感知。当传感器检测到有害气体超标时,程序会触发虚拟角色的警告机制,角色会通过语音和动画动作提醒用户,增强了系统的实用性和安全性。
事件系统是这个程序的核心控制逻辑,负责管理用户与虚拟角色的交互、UI显示、以及摄像机的控制。用户可以通过拖拽摄像机来调整视角,观察虚拟角色的不同动作和表情。事件系统会根据用户的操作,适时调整虚拟角色的状态。当用户点击角色或特定UI按钮时,系统会响应这些交互,执行相应的功能或动画切换。例如,当检测到空气质量变差时,事件系统会触发虚拟角色的警告状态,更新UI信息,显示当前环境中的空气质量数据,并提醒用户采取行动。除此之外,在搭配我上一届大赛制作的全息显示器使用时,也可以对画面进行镜像处理,保证用户在全息模式下能够正确查看虚拟角色和文字UI。
3.3 LLM服务
ChatGLM 是一款基于 Transformer 架构的大语言模型,专门为中文和英文双语环境设计。它借鉴了 GPT(Generative Pre-trained Transformer)家族的模型结构,并在此基础上进行优化,使其能够在对话和任务处理等场景中表现出色。与 GPT-3 类似,ChatGLM 使用了自回归语言建模技术,基于大规模的语料库进行预训练,然后经过微调,能够处理生成式对话、文本摘要、翻译等自然语言任务。
ChatGLM 模型的核心基于 Transformer 架构,Transformer 是一种深度神经网络结构,尤其擅长处理序列数据,如文本。在 Transformer 架构中,最主要的部分是多头自注意力机制(Multi-Head Self-Attention),它能够有效地捕捉序列中不同位置的词语间的关系。通过多头自注意力机制,模型可以从多个不同的“关注点”来理解输入文本中的含义,这让 ChatGLM 能够生成连贯且逻辑清晰的长文本段落。
在预训练阶段,ChatGLM 会处理大量的文本数据,包括百科、新闻、社交媒体等,学习到语法结构、语义关联以及世界知识。通过这种大规模预训练,模型掌握了语言的基本构造规则和常识信息。在微调阶段,ChatGLM 进一步被调整以适应具体任务,比如对话生成。在对话场景下,它不仅能够理解输入的语境,还可以生成相关性很强的回复。
ChatGLM 的特点之一是支持双语处理,它能够同时处理和生成中文与英文文本。这是通过在预训练过程中使用大量中英双语数据来实现的。与许多单语模型相比,ChatGLM 在双语场景中具有明显优势,尤其是在多语言对话任务中,它能够在中英文间自然地切换。
在实际应用中,ChatGLM 能够理解复杂的上下文并生成连贯的对话内容。例如,当用户提出一个问题时,模型能够基于问题提供符合逻辑且具备信息性的回复。此外,ChatGLM 还具备一定的知识推理能力,能够帮助用户解决复杂的问题,如技术支持、信息查询等。
在本系统中,ChatGLM 主要负责处理用户与虚拟角色之间的智能对话任务。用户输入问题或指令后,系统通过 HTTP 请求将文本发送给 ChatGLM 模型,后端处理后返回生成的文本回复。生成的回复内容既可以通过 UI 显示给用户,也可以通过 TTS 转换为语音,配合虚拟角色的口型同步,形成完整的语音交互体验。
3.4 TTS服务
在本项目使用的TTS算法是VITS,这是一个基于生成式对抗网络(GAN)和变分推断技术的文本转语音(TTS)系统。相比于传统的TTS模型,VITS 的核心优势在于它可以通过少量的数据生成自然流畅的语音,并且在语音质量和语音自然度方面有显著提升。它结合了基于流模型(Flow-based model)和 GAN 的优点,能够在高效生成语音的同时,确保输出语音的高保真度。VITS 的设计理念融合了几种关键技术,主要有变分推断:它用于从文本中捕捉潜在的音素和韵律信息,通过推断潜在的语音特征来生成与文本相对应的语音表现。这种技术允许模型有效地处理文本到语音的复杂映射,使生成的语音在自然性和表现力上更接近真实人类发音;自回归和非自回归生成模型的结合:自回归模型依赖于先前生成的语音片段来生成后续的语音片段,而非自回归模型则能够并行生成所有语音帧。VITS 结合了两者的优势,利用自回归模型的高质量生成能力和非自回归模型的高效性,使得生成过程更加流畅和快速;Flow 模型:VITS 利用基于流的模型来建模语音的分布,这种模型能够在不牺牲质量的情况下,通过变分推断进行有效的语音生成。流模型擅长处理复杂的概率分布,因此在捕捉语音的多样性和细节表现方面效果尤为突出;GAN(生成式对抗网络):GAN 技术使 VITS 在语音生成过程中可以通过判别器的反馈不断优化生成器的输出。生成器负责生成语音,判别器则评估语音的自然度,通过这种对抗机制,VITS 的生成器能够逐步提升语音的质量,生成更加逼真的声音。
在本系统中,VITS 主要负责将虚拟角色的文本回复转换为自然流畅的语音。首先,当用户输入文本时,ChatGLM 模型会生成相应的回复文本,接着该文本被发送到 TTS 服务,VITS 模型基于输入文本生成对应的语音数据。生成的语音数据可以实时播放,使得虚拟角色不仅能够通过文本回复与用户互动,还能够通过自然的语音与用户进行对话。这种人机交互方式极大地提升了系统的交互体验,使虚拟角色在用户眼中更具生命力和真实感。
四、作品源码
https://download.eeworld.com.cn/detail/eew_nkXjf8/634608
五、作品功能演示视频
时间索引:
物料展示 00:24
设计思路 01:55
功能演示 02:38
六、项目总结
首先,还是感谢EEWORLD和得捷能一直举办这类活动。这次比赛不仅让我有机会发挥自己的创造力,还让我在实践中学到了很多新知识,收获颇丰。整个项目涉及了硬件开发、软件开发、网络通信、3D模型驱动、3D建模、3D打印、大语言模型以及文本转语音技术等多个方面,虽然从整体来看每个项目难度可能并不算太高,但它让我能用平时接触到的各个领域的新技术,真正的动手进行实践,极大地拓展了我的知识面,也让我积累了不少经验。
在硬件开发方面,通过对Adafruit MatrixPortal M4开发板进行环境数据的采集和显示等功能的实现,让我对Circuitpy的应用和底层原理有了更深入的理解。在本项目之前我其实从未接触过气体传感器,在完成本项目后我对气体传感器的认识和使用也得到了进一步提高。除此之外,本项目中我也是第一次尝试把硬件设备接入Adafruit IO这类的云平台,以往我都是直接在内网构建HTTP API请求服务器来完成这些操作,在完成这个项目后我也体会到了使用云平台的便利性。并且,在这个项目的开发中我也真的明白了项目充分的前期调研的重要性,因为这些气体传感器应用方面的库很多都只有Arduino的,而我使用Circuitpy开发就会遇到很多阻碍。发现这个问题的时候代码几百行都写完了,最后只能想办法硬着头皮实现。
此外,软件开发的部分让我深刻体会到了软硬件结合的重要性,从编写用于驱动点阵屏显示的代码,到实现实时数据上传和远程数据监控,每一步都让我对系统的整体架构有了更加系统的认识。在项目的Unity 3D程序部分,我成功驱动了一个3D人物模型,在这个过程中真的有很多细节需要学习,比如animator、animation的使用,如何给人物加载外部的fbx动作等等。通过本项目的实操,让我对Unity的使用也变得愈发熟练。同时,项目中我本地部署了一个大语言模型,并通过文本转语音技术,使得虚拟人物能够根据环境数据做出智能语音反馈。这一部分让我对人工智能与现实场景的结合有了更深刻的理解,也让我体验到了机器智能为人机交互带来的便捷与创新。我本人的研究方向就是AI相关,这也让我对自己的研究领域更深一步的热爱。
通过这个比赛,我不仅拓展了自己的技能,还更加理解了跨领域知识融合的意义。再次感谢大赛的机会,这段经历将成为我未来创意与开发之路上的宝贵财富。
帖子汇总:
【2024 DigiKey 创意大赛】物料开箱帖 https://bbs.eeworld.com.cn/thread-1290188-1-1.html
【2024 DigiKey 创意大赛】AI环境监测站-硬件部分 https://bbs.eeworld.com.cn/thread-1296178-1-1.html
七、其他
这里我想补充一点,虽然Adafruit IO为C、Python提供了相关的库函数,但诸如Unity 开发中C#的语言并没有这样的库,而对于如何使用HTTP调用API官方文档也写的很模糊,这里直接给出一个Python的请求代码,其它语言只要参考一下就能直接使用而不依赖官方库了。
修改IO上某一Feed的值
import requests
# 设置必要的参数
io_key = "aio_XXXX"
username = "XXXX"
feed_key = "XXXX"
url = f"https://io.adafruit.com/api/v2/{username}/feeds/{feed_key}/data"
# 要上传的值
data = {
"value": "20000"
}
# 设置请求头,包含 API 密钥
headers = {
"X-AIO-Key": io_key
}
# 发送 POST 请求上传数据
response = requests.post(url, headers=headers, data=data)
# 检查返回结果
if response.status_code == 200:
print("Data uploaded successfully!")
else:
print(f"Error: {response.status_code} - {response.text}")
获取IO上某一feed的值
import requests
# 设置必要的参数
io_key = "aio_XXX"
username = "XXX"
feed_key = "XXX"
url = f"https://io.adafruit.com/api/v2/{username}/feeds/{feed_key}/data"
# 设置请求头,包含 API 密钥
headers = {
"X-AIO-Key": io_key
}
# 发送 GET 请求
response = requests.get(url, headers=headers)
# 解析返回的数据
if response.status_code == 200:
data = response.json()
if data:
latest_value = data[0]["value"] # 获取最新的 feed 值
print(f"Latest feed value: {latest_value}")
else:
print("No data available.")
else:
print(f"Error: {response.status_code} - {response.text}")
-
上传了资料:
【2024 DigiKey 创意大赛】AI全功能环境监测站作品源码
- 2024-10-16
-
回复了主题帖:
【2024 DigiKey 创意大赛】AI环境监测站-硬件部分
wangerxian 发表于 2024-10-15 18:20
气体传感器一般都不会返回具体浓度,都得自己转换,有些值还和温度有关。
关键这些库都没有Circuitpy的就很麻烦
- 2024-10-15
-
发表了主题帖:
【2024 DigiKey 创意大赛】AI环境监测站-硬件部分
本帖最后由 MioChan 于 2024-10-15 16:22 编辑
本次创意大赛我要制作的是一个AI环境监测站,我选择的开发板是Adafruit MatrixPortal M4,整个硬件方面的实现基于Circuit Python。选这个板子主要是因为它专门为驱动点阵屏设计,自带HUB75接口,可以直接插到点阵屏上。如果是用其他开发板需要自己连接差不多16条线,对于IO引脚少的板子再拓展其他功能也很麻烦。另外这个板子还带有一个i2c接口,对于Adafruit的传感器可以直接用4p连接线连接。
本帖主要是分享一下环境数据采集及其显示相关的硬件部分的设计过程,至于AI是一套单独的系统,后面再做介绍。我是比较喜欢高内聚低耦合的设计,一套系统就做一件事情,几套系统间可以互现协作,哪一套也都能拿来单独使用。
首先是点阵屏的驱动,因为板子自带了HUB75接口,直接扣到点阵屏上连接好屏幕的电源线即可。
板子出厂自带了一个重力感应的沙漏程序,可以简单测试一下板子功能是否正常,确定好没问题后,即可开始刷入Circuitpy固件了。
我使用的是CircuitPython 9.1.4 可以在这里下载https://circuitpython.org/board/matrixportal_m4/
烧录过程也很简单,连接开发板到电脑,双击开发板上的reset按钮,NeoPixel RGB LED变为绿色后,电脑中会出现一个MATRIXBOOT的磁盘,将刚刚下载好的.uf2文件拖到MATRIXBOOT中即可。接下来开发板会重启,然后出现一个新的名为 CIRCUITPY的磁盘,接下来在里面就可以写代码了。
该开发板主要实现的功能如下:
1.显示环境信息:
设计了一套UI,屏幕会滚动显示各项环境指标,每一屏最多显示三项,滚动播放。对于部分指标数值过长,导致单位显示不下的问题,也额外增加了相应的逻辑,用来滚动显示这些显示不下的内容。
2.自定义弹幕文字显示:
该功能主要是用来滚动显示一些弹幕文字,通过网络功能实现。本来想和之前的项目一样做一个接受POST请求的API服务器,但这个版本的CircuitPy似乎对网络底层库支持的不太好,使用之前在Adafruit ESP32S3 TFT Feather的代码总是出问题,最后看了一下官方文档发现adafruit_matrixportal 已经包含了一些网络功能的实现,最后想了想决定通过Adafruit IO https://io.adafruit.com/ 来解决这个问题,这既简化了网络通信的复杂性,又能利用已经成熟的工具来处理数据。Adafruit IO 提供了一个相对容易上手的接口,可以通过publish和subscribe来接收和显示文本信息。在项目中,我将设备连接到Adafruit IO的服务中,创建了两个feed一个用于接受弹幕内容另一个定义弹幕颜色。通过订阅feed,我能够实时地从远程设备或应用发送数据,并在点阵屏上滚动显示。并且这样也解决了之前只能内网访问的问题,通过Adafruit IO服务托管这些信息,我们可以在任意位置的连接互联网的电脑上发布或订阅这些内容。最重要的是Adafruit IO也有相应的python库,我们也可以在其他python服务程序中更新这些内容。当然,已经做了中文字库支持。
比较简单的例子是我们可以直接在io的dashboard创建一个面板为feed手动赋值。
[localvideo]76c1c87d153eba290d29031939a4ad69[/localvideo]
3.传感器接入:
建立好显示环境信息的UI后接下来就是实际连接传感器了,更新面板的数据了
下面是我用到的气体传感器
BME680 是来自 Bosch Sensortec 的环境传感器,能够同时检测多种环境参数,广泛应用于空气质量监测。它集成了气体、湿度、压力和温度传感器。
SGP30 是 Sensirion 开发的一款空气质量传感器,主要用于检测空气中的二氧化碳 (CO₂) 和总挥发性有机物 (TVOC)。
MiCS-5524 是一款半导体气体传感器,专门用于检测一氧化碳 (CO) 和甲烷 (CH₄) 等有害气体。它基于金属氧化物半导体 (MOS) 技术,能够在低浓度下对一氧化碳表现出高度灵敏性。因为不支持i2c所以直接用飞线连接到了A0引脚。
将上面的传感器连接到开发板即可。
顺便一提,Adafruit这些i2c接口的连接线(上)和常用的4p80mm连接线(下)的线序并不一样,我们需要在另一端做一个交叉。简单来说,用尖嘴镊子把接线端子顶出来,换到镜像的位置即可。
4.数据记录功能:
既然是环境监测站自然要有保存历史数据的功能,这里我最初的是想法是将传感器数据写入到板载存储器中。不过这样有个很大的问题,这些数据只有连接到这个板子的电脑才能读取,用网络实现不仅麻烦,如果外部设备频繁请求这些信息也会大量消耗板子有限的计算资源。最后还是想到了用Adafruit IO,我们可以在IO中创建不同环境指标的feed,让板子定时上传这些数据到IO上,这样不仅能记录历史数据,其他应用能非常方便的获取这些数据,还能直接在IO创建一个环境监测面板直接查看这些数据,也不需要再额外设计一个软件来画图了。
下面是监测面板的效果:
遇到的坑:
这里也顺便分享一下目前为止折腾这个项目遇到的坑。首先是传感器部分,因为这几个传感器也是第一次玩,实际上手后才发现,感觉被描述欺骗了。
比如BME680,虽然手册上生成可以测VOC,但是传感器只能返回气体电阻值,当VOC上升这个值会减小,反之则上升。也就是说这个传感器无法测得实际的VOC。有一个BSEC库倒是能够将气体电阻值转换为标准的空气质量指数 (IAQ) ,但是这个库是闭源的,也没有Circuitpy版本,但发现这个问题的时候几百行代码都写完了,当初立项也说的是用Crcuitpy来做,也不好换Arduino。还有就是MiCS-5524虽然文档说能测各种气体,但实际上只能测这些气体是否存在,而不能准确分辨是哪种气体,只能当一个有害气体警报器用。
还有最大的一个坑是VScode中Circuitpy插件的,git上好久没维护了,现在最新版本的完全用不了,git上也全是在反应这个bug的也没人解决。
换成官方推荐的Mu编辑器虽然能用,但代码补全真的差VScode那个插件太多了,后续再做项目还是考虑Micropy或者Arduino吧
后续计划:
其实主体上项目已经完成了,后续打算为这个点阵屏设计一个底座并3D打印出来。
关于AI部分,主要是想接入一个LLM(大语言模型)。实现让AI助手能读取这些环境信息,给出一些建议之类的,并通过TTS读出来。
此外,有余力的话也想继续用Unity实现一个3D模型,给LLM一个皮囊,放到我之前做的那个全息显示器中。
- 2024-10-12
-
回复了主题帖:
《动手学深度学习PyTorch版》阅读分享四 手写识别小试牛刀(基于CNN)
保存整个模型(序列化模型和权重,依赖python环境,不灵活)
torch.save(model, "model.pth")
加载整个模型
model = torch.load("model.pth")
只保存权重文件(模型在代码里写好,保存和加载模型和优化器权重,更灵活)
save_dict = {}
save_dict[f'model_state_dict'] = model.state_dict()
save_dict[f'optimizer_state_dict'] = optimizer.state_dict()
torch.save(save_dict, "state_dict.pth")
加载权重文件
checkpoint = torch.load("state_dict.pth")
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
- 2024-09-25
-
加入了学习《【DigiKey“智造万物,快乐不停”】基于LLM大语言模型和VITS文本转语音的全息AI助手演示》,观看 基于LLM大语言模型和VITS文本转语音的全息AI助手演示
-
加入了学习《【Follow me第二季第1期】全部任务演示》,观看 全部任务演示2.0
- 2024-08-30
-
发表了主题帖:
《拥抱AIGC 应用ChatGPT和OpenAI API》 ChatGPT的应用
本帖最后由 MioChan 于 2024-8-30 17:06 编辑
最近抽时间把本书的第二部分读完了,这一部分主要就是关于ChatGPT的应用。
第三章 熟悉 ChatGPT
这一章主要是介绍如何使用 ChatGPT,包括如何注册账户、用户界面介绍以及组织对话等,主要是面向新用户而言。
第一节详细介绍了如何注册和设置ChatGPT 账户,确保新用户能够快速开始使用,通过实际步骤,比如如何选择账号类型(免费或付费)、如何进行身份验证,以及设置个人偏好,读者能够更清楚地了解如何进入和使用 ChatGPT。另外,书中没有提到的是,目前最新的ChatGPT相比之前已经大大放宽了注册流程,也不需要验证外区手机号,即使不注册也能进行会话,就是每次刷新浏览器后都会被重置。
下一节则讲解了 ChatGPT 的用户界面,包括聊天窗口、工具栏、历史记录以及个性化设置等功能区域。通过对界面的各个部分进行详细介绍,读者可以更快地找到所需的功能。例如,用户可以在左侧的对话栏中找到历史对话记录,方便回顾和继续先前的对话。同时,通过个性化设置,用户可以调整界面显示风格、对话语言、自定义GPT等,打造更符合个人习惯的使用体验。
最后本章还介绍了如何通过良好的对话组织来提高与 ChatGPT 互动的效率和效果。例如,在进行复杂任务时,用户可以将问题分解成多个小问题,逐步与 ChatGPT 讨论,最终得到更加精准和全面的答案。此外,使用明确和简洁的语言来提出问题,可以帮助 ChatGPT 更好地理解用户意图,生成更有用的回复。举个例子,用户在请求某个产品的评测时,可以首先说明产品的基本信息,然后逐步询问产品的性能、价格和用户评价等细节。
第四章 了解提示设计
本章重点介绍了如何通过设计有效的提示(Prompt)来引导 ChatGPT 生成更精准和符合预期的内容。提示设计在使用大型语言模型时至关重要,因为它能够直接影响模型输出的质量和相关性。本章共分为三个主要部分,深入探讨了提示的概念、重要性,以及如何利用零样本学习、一次样本学习和少样本学习来提升生成效果,并特别强调了 Transformer 模型在这些任务中的作用。
什么是提示,提示为什么重要
这一节首先定义了“提示”的概念。在人工智能领域,提示是向语言模型输入的文本片段或问题,它起到了“指示牌”的作用,引导模型生成期望的答案或内容。提示的设计直接影响生成的效果。有效的提示能够帮助模型更好地理解上下文,过滤出相关信息,进而生成符合需求的内容。
提示的重要性在于它不仅仅是一个问题的表达方式,更是影响模型输出的一个关键因素。例如,当用户希望生成一份关于气候变化的报告时,一个简单的提示如“写一篇关于气候变化的文章”可能会得到一个过于宽泛的答案,而如果提示被精细化为“写一篇关于2024年气候变化对沿海城市的经济影响的分析”,则生成结果会更加具体和有针对性。这体现了提示设计在优化内容生成中的重要作用。
零样本学习、一次样本学习和少样本学习——Transformer 模型的典型功能
零样本学习指模型在没有任何示例的情况下,直接依赖提示生成答案的能力。例如,用户可能会给出一个完全新领域的问题,模型需通过理解问题的语境进行推理,从而给出一个合理的答案。此能力对于处理不常见或未知领域的信息非常重要。一次样本学习则是给出一个示例来指导模型学习。例如,用户想要写一封商业邮件,可以先提供一个样例邮件,模型将根据这个单一示例理解格式、语调和内容结构,并生成类似的文本。少样本学习提供少量示例来帮助模型更准确地理解任务。例如,用户提供了几条客户评论,希望模型根据这些评论生成产品描述。少样本学习通过这些示例让模型学习到更加复杂的模式和语境,从而生成更符合需求的输出。
定义明确的提示规则,以获得相关和一致结果
在提示中清楚说明你想要的结果和目标。例如,想要获取某个特定领域的技术报告,应在提示中明确指定领域和技术要求。
为模型提供足够的背景信息,以帮助它更好地理解问题。例如,用户希望生成一段关于某个历史事件的描述,可以先简单概述事件的时间和地点。
在提示中添加限制条件,例如字数、格式或风格,以确保输出结果符合要求。明确的提示能够引导模型更精准地生成内容,并符合用户的特定需求。
通过上述方法可以获得更具体的回复。
除此之外,PLUS订阅用户现在可以直接自定义GPT,直接让其能面向不同的领域。例如,下面这个是我之前尝试的一个猫娘版GPT,能产生一些比较有趣的回复。
另外,就算不是Plus用户也可以在探索GPTs中找到别人已经做好的GPT使用
第五章到第八章 助力各领域应用:提升生产力与效率
后面这几章主要展示了 ChatGPT 在多个领域中的多种应用,涵盖了日常工作、软件开发、营销策略和科学研究等不同场景。
比如ChatGPT 可以作为日常助手,通过文本生成、写作技能提升、优化翻译效果和加速信息检索等功能,为用户提供实用的支持。例如,它可以帮助用户撰写商业邮件、生成创意文案,或为复杂主题撰写分析报告。ChatGPT 还可以作为个人学习助手,优化学习计划,提高工作和生活中的整体效率。
在软件开发领域,ChatGPT 可用来生成、优化和调试代码,同时也能生成技术文档和代码注释,帮助开发者更高效地进行编程工作。ChatGPT 还可以帮助理解复杂的编程语言模型,甚至在不同编程语言之间进行转换。对于开发者来说,利用 ChatGPT 可以显著提升开发效率,减少出错率,并确保代码质量更高。例如下面就是用GPT帮助写一个快排算法。
在营销领域,ChatGPT 可以支持 A/B 测试、SEO 优化、情绪分析,以及新品研发等活动,帮助企业更好地了解客户需求和市场趋势。通过 ChatGPT 生成的内容,可以更好地进行市场宣传、提升客户满意度,最终带来更高的品牌影响力和商业收益。
对于科学研究人员来说,ChatGPT 也提供了多种实用功能,可以显著简化研究过程。例如,它可以收集和总结文献资料,设计实验框架,生成和格式化参考文献,对现有结果进行辅助分析,甚至帮助撰写研究论文。利用 ChatGPT,科研人员可以更专注于核心研究内容,同时减少在琐碎任务上的时间消耗,使研究工作更加高效和精准。
总体来看,这部分内容的最大亮点在于其全面性和适用性。从理论到实际,深入到各个专业领域,帮助不同背景的读者在工作中发挥 AI 的最大效用。但阅读过程中我也发现很多问题,书中很多图片分辨率太低,看不清文字,而且书中与 GPT 的对话示例大多使用英文,但作为一本面向中文用户的书籍,这样的设计对于那些不熟悉英文的读者显得不太友好,起码应该提供这些英文对话的中文翻译或对话中包含的主要要点翻译。
- 2024-08-14
-
发表了主题帖:
《拥抱AIGC 应用ChatGPT和OpenAI API》 整书速览及其第一部分基础知识阅读
本帖最后由 MioChan 于 2024-8-14 16:19 编辑
前几天终于收到了《拥抱AIGC 应用ChatGPT和OpenAI API》这本书,简单翻了一下,总的来看是一本探讨生成式人工智能及其应用的书籍,像是比较难懂的理论知识其实很少,主要都是在介绍怎么应用,甚至我感觉就是一本ChatGPT和OpenAI API的使用说明书。自从ChatGPT推出之后我就其实一直在用了,让GPT帮忙写一下废话或者逻辑比较清晰的代码还是很方便。
第一部分差不多就是绪论吧,主要介绍了生成式人工智能的概念、发展历程以及相关技术背后的数学原理。AIGC简单来说就是一类通过学习大量数据来生成新内容的人工智能模型,像是第一章也列举了常见的几种AIGC应用,比如ChatGPT这种大语言模型、图片生成模型以及音乐生成模型,事实上现在的AIGC应用太多了,除了这些最常用的还有语音方向的例如TTS这种语音生成模型,而且除了二维图片现在3D模型以及视频方面的AIGC也在迅速发展。
至于生成式AI的研究历史,书中主要列举了生成对抗网络(GAN)、变分自动编码器(VAE)、TransFormer等等,其实生成式AI再往早一点说,还有基于统计方法,如朴素贝叶斯模型和隐马尔可夫模型(HMM)这些。Transformer提出应该算是一个里程碑的事件,可以说是彻底取代了RNN,模型结构图如上所示,因为采用了自注意力机制,使得模型能够高效地捕捉序列数据中的长程依赖关系,在自然语言处理任务中表现出色,成为许多生成模型的基础。随后OpenAI提出了GPT模型,GPT也是基于Transformer架构的大型语言模型实现的,核心思想是预训练和微调。GPT-1是首个GPT模型,展示了预训练与微调结合的有效性。GPT-2的模型规模显著增加,能够生成高质量、连贯的长文本,展现了更好的生成能力。GPT-3的模型参数达到1750亿,进一步提升了生成文本的质量和多样性,能够在多个任务上实现零样本和少样本学习能力。GPT-4继续在模型规模和生成能力上取得突破,支持多模态输入(如图像和文本),在跨领域任务中展现了更广泛的应用。后续的具体原理因为OpenAI逐渐变成了CloseAI,也没有开源所以详细的细节也不得而知。
我记得当时注册Oppo AI账号还要验证手机号之类的,步骤还比较麻烦,但现在似乎不验证手机号了,注册还是很容易,而且现在国内的LLM发展的也算可以,如果没玩过还是值得体验一下。最后附上一张LLM的发展历史图供大家学习参考。
- 2024-08-10
-
发表了主题帖:
【2024 DigiKey 创意大赛】物料开箱帖
很高兴能入围今年的创意大赛,这次我想做的是一个AI全功能气象站,所以选择的物料基本都是各种气体传感器,还有一块Adafruit的esp32点阵屏驱动版,用于驱动点阵屏,用于显示气象站各个传感器的数值。
下面是开箱照:
不得不说,得捷每次的包装是真的好
Adafruit的板子还是一如即往的精致
顺便简单介绍一下这些传感器的功能,涵盖了各种气体和挥发物
BME680:温度、湿度、气压、VOC
SEN0440:C2H5OH,C3H8,C4H10,CH4,CO,H2,H2S,NH3
SGP30:H2、TVOC、eCO2
- 2024-08-05
-
回复了主题帖:
共读入围名单(第二轮):《拥抱AIGC 应用ChatGPT和OpenAI API》
个人信息无误,确认可以完成评测计划。
- 2024-08-01
-
发表了主题帖:
【Follow me第二季第1期】汇总提交帖:全部任务
本帖最后由 MioChan 于 2024-8-8 13:52 编辑
从收到板子到今天终于完成了所有任务,Follow Me也参加了好几期,真的是很好的活动。本期的Adafruit Circuit Playground Express这块板子,总的来说真的很不错,集成度非常高,各种传感器应有尽有,确实配称之为Playground,最后感谢社区和得捷提供的机会,期待后续活动再能出现一些有意思的板子~
项目视频演示
本项目完成了本期活动的全部内容,所有任务的展示如下:
视频快捷索引:
入门任务:00:54
基础任务一:01:06
基础任务二:01:18
基础任务三:01:40
进阶任务:01:54
创意任务一:02:03
创意任务二:03:33
创意任务三:03:48
物料展示
我购买物料为活动必备物料开发板、推荐物料舵机、一条MicroUSB的线缆用于连接开发板和计算机
物料全家福
任务成果展示
入门任务:开发环境搭建,板载LED点亮
对应论坛帖:【Follow me第二季第1期】物料开箱、开发环境搭建以及板载LED点亮 https://bbs.eeworld.com.cn/thread-1288882-1-1.html
开发环境搭建:
本次我使用Arduino来完成全部任务,Arduino环境搭建比较简单
1.官网下载IDE,https://www.arduino.cc/en/software
2.安装完成后,打开IDE,在左侧栏的Boards Manager,安装Arduino SAMD
3.连接开发板,就能看见上面正确显示了板子的型号Adafruit Circuit Playground
板载LED点亮:
所使用的板载元件:红色LED
只要知道红色LED对应13号引脚,点灯任务非常简单,下面给出代码和演示
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
基础任务一:控制板载炫彩LED,跑马灯点亮和颜色变换
对应论坛帖:【Follow me第二季第1期】基础任务1-3与进阶任务https://bbs.eeworld.com.cn/thread-1288902-1-1.html
所使用的板载元件:彩色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展示舒适程度
对应论坛帖:【Follow me第二季第1期】基础任务1-3与进阶任务https://bbs.eeworld.com.cn/thread-1288902-1-1.html
所使用的板载元件:彩色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展示,检测到入侵时,发起声音报警
对应论坛帖:【Follow me第二季第1期】基础任务1-3与进阶任务https://bbs.eeworld.com.cn/thread-1288902-1-1.html
所使用的板载元件:彩色LED、蜂鸣器、IR LED、IR接收器
通过IR LED发射38KHZ的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
}
演示:
进阶任务:制作不倒翁——展示不倒翁运动过程中的不同灯光效果
对应论坛帖:【Follow me第二季第1期】基础任务1-3与进阶任务https://bbs.eeworld.com.cn/thread-1288902-1-1.html
所使用的板载元件:彩色LED、蜂鸣器、IR LED、IR接收器
流程图:
代码:
#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);
}
演示:
创意任务一:有创意的可穿戴装饰——可结合多种传感器和灯光效果展示
对应论坛帖:【Follow me第二季第1期】创意任务一:可以跟随MIDI音符律动的音乐摆件 https://bbs.eeworld.com.cn/thread-1288904-1-1.html
所使用的板载元件:彩色LED、滑动开关、按钮
该任务实现了一个可以跟着MIDI音符律动或显示电平的指示器,开关在左边为电平指示,在右为律动,可以在运行中实时切换,板子上那两个按钮左边的可以减小灯珠的亮度,右边则是增加。
流程图:
代码:
#include <Adafruit_CircuitPlayground.h>
#include <MIDIUSB.h>
bool slideSwitch;
int brightness = 100;
// 定义颜色数组
uint32_t gradientColors[10] = {
CircuitPlayground.colorWheel(90),
CircuitPlayground.colorWheel(80),
CircuitPlayground.colorWheel(70),
CircuitPlayground.colorWheel(60),
CircuitPlayground.colorWheel(50),
CircuitPlayground.colorWheel(40),
CircuitPlayground.colorWheel(30),
CircuitPlayground.colorWheel(20),
CircuitPlayground.colorWheel(10),
CircuitPlayground.colorWheel(0)
};
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
randomSeed(analogRead(0)); // 随机数生成器
}
void loop() {
slideSwitch = CircuitPlayground.slideSwitch();
bool noteHandled = false; // 标记是否处理过音符
midiEventPacket_t rx;
if (CircuitPlayground.leftButton()) {
brightness = max(0, brightness - 10); // 减少亮度
CircuitPlayground.setBrightness(brightness);
delay(100);
}
if (CircuitPlayground.rightButton()) {
brightness = min(255, brightness + 10); // 增加亮度
CircuitPlayground.setBrightness(brightness);
delay(100);
}
if (slideSwitch) {
do {
rx = MidiUSB.read();
if (rx.header != 0) {
Serial.print("Received: ");
Serial.print(rx.header);
Serial.print("-");
Serial.print(rx.byte1);
Serial.print("-");
Serial.print(rx.byte2);
Serial.print("-");
Serial.println(rx.byte3);
// 检查是否是 NOTE ON 消息
if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 != 0) {
int velocity = rx.byte3; // 获取音符力度
updateLEDs(velocity);
} else if ((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) {
// 处理 NOTE OFF 消息或者 NOTE ON 力度为 0
turnOffAllLEDs();
}
}
} while (rx.header != 0);
delay(5);
} else {
do {
rx = MidiUSB.read();
if (rx.header != 0) {
Serial.print("Received: ");
Serial.print(rx.header);
Serial.print("-");
Serial.print(rx.byte1);
Serial.print("-");
Serial.print(rx.byte2);
Serial.print("-");
Serial.println(rx.byte3);
if (!noteHandled) {
if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 != 0) {
int velocity = rx.byte3;
lightRandomLED(velocity);
noteHandled = true;
} else if ((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) {
turnOffAllLEDs();
}
}
}
} while (rx.header != 0 && !noteHandled);
delay(5);
}
}
void updateLEDs(int velocity) {
// 计算亮灯的数量
int ledCountToLight = map(velocity, 0, 127, 0, 10);
Serial.print("Velocity: ");
Serial.print(velocity);
Serial.print(", LED Count: ");
Serial.println(ledCountToLight);
// 更新 LED
for (int i = 0; i < 10; i++) {
if (MidiUSB.read().header != 0) {
turnOffAllLEDs();
return; // 检查是否有新的 MIDI 输入
}
delay(20);
if (i < ledCountToLight) {
CircuitPlayground.setPixelColor(i, gradientColors[i]);
} else {
CircuitPlayground.setPixelColor(i, 0);
}
}
for (int i = ledCountToLight; i >= 0; i--) {
if (MidiUSB.read().header != 0) {
turnOffAllLEDs();
return; // 检查是否有新的 MIDI 输入
}
delay(20);
CircuitPlayground.setPixelColor(i, 0);
}
}
void lightRandomLED(int velocity) {
// 计算亮度
int brightness = map(velocity, 0, 127, 0, 150);
// 生成随机 LED 和颜色
int randomLED = random(0, 10);
uint32_t randomColor = random(0, 254);
CircuitPlayground.setBrightness(brightness);
// 点亮随机 LED
CircuitPlayground.setPixelColor(randomLED, CircuitPlayground.colorWheel( randomColor));
Serial.print("Random LED: ");
Serial.print(randomLED);
Serial.print(", Color: ");
Serial.print((randomColor >> 16) & 0xFF);
Serial.print(",");
Serial.print((randomColor >> 8) & 0xFF);
Serial.print(",");
Serial.println(randomColor & 0xFF);
}
void turnOffAllLEDs() {
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 0); // 关闭每个 LED
}
}
演示:
创意任务二:章鱼哥——章鱼哥的触角根据环境声音的大小,章鱼哥的触角可舒展或者收缩
对应论坛帖:【Follow me第二季第1期】创意任务二:害怕声音的纸章鱼~https://bbs.eeworld.com.cn/thread-1289007-1-1.html
所使用的元件:板载麦克风以及舵机
这个任务就是要用开发板识别环境中的声音大小,然后控制舵机作出相应的动作。我是自己折了个纸章鱼,将其头部贴到快递纸板上,背面用回形针+风筝线拉到两个触角上,给纸板打个洞挂到舵机上,就能实现触角收缩的效果了。
流程图:
代码:
#include <Adafruit_CircuitPlayground.h>
#include <Servo.h>
// 滤波系数
const float alpha = 0.3;
float filteredSoundLevel = 0;
// 舵机定义
const int servoPin = A3; // 舵机连接的引脚
Servo myServo;
const int neutralSpeed = 90; // 中立位置速度
const int leftSpeed = 84; // 左转速度
const int rightSpeed = 94; // 右转速度
const int turnDuration = 700; // 转动持续时间
const int delayAfterTurn = 5000; // 等待时间
void setup() {
CircuitPlayground.begin();
Serial.begin(115200); // 用于调试
myServo.attach(servoPin); // 附加舵机到指定引脚
myServo.write(neutralSpeed); // 设置舵机初始速度为停止
}
void loop() {
checkButtons();
float soundLevel = CircuitPlayground.mic.soundPressureLevel(10);
// 滤波
filteredSoundLevel = alpha * soundLevel + (1 - alpha) * filteredSoundLevel;
Serial.print("Raw Sound Level: ");
Serial.print(soundLevel);
Serial.print(", Filtered Sound Level: ");
Serial.println(filteredSoundLevel);
updateLEDs(filteredSoundLevel);
myServo.write(neutralSpeed);
delay(200);
}
void updateLEDs(float soundLevel) {
// 计算亮灯的数量
int ledCountToLight = map(soundLevel, 55, 70, 0, 10);
Serial.print("LED Count: ");
Serial.println(ledCountToLight);
if (ledCountToLight >= 9) {
turnLeft180();
delay(delayAfterTurn);
turnRight180();
}
}
void turnLeft180() {
myServo.write(leftSpeed); // 设置舵机左转速度
delay(1900); // 转动持续时间
myServo.write(neutralSpeed); // 停止舵机
}
void turnRight180() {
myServo.write(rightSpeed); // 设置舵机右转速度
delay(turnDuration); // 转动持续时间
myServo.write(neutralSpeed); // 停止舵机
}
void checkButtons() {//按钮控制
if (CircuitPlayground.leftButton()) {
turnLeft180();
delay(500);
}
if (CircuitPlayground.rightButton()) {
turnRight180();
delay(500);
}
}
演示:
创意任务三:水果钢琴——通过触摸水果弹奏音乐,并配合灯光效果
对应论坛帖:【Follow me第二季第1期】创意任务三: 触控&MIDI钢琴https://bbs.eeworld.com.cn/thread-1289066-1-1.html
所使用的板载元件:彩色LED、蜂鸣器、按钮、TouchPad
水果钢琴原理就是当手触摸水果时,检测其对应引脚的电容发生的变化,当其大于某个阈值时,执行if内语句,可以说是把水果当成一个大号“按钮”。除了A0其余引脚都支持这个触摸检测的功能,用CircuitPlayground.readCap(Pin)就可以读取引脚的电容值。
代码:
#include <Adafruit_CircuitPlayground.h>
// 触控引脚及其音符频率
int touchPins[] = {A1, A2, A3, A4, A5, A6, A7};
int touchFrequencies[] = {262, 294, 330, 349, 392, 440, 494};
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
void loop() {
checkTouch();
delay(5);
}
void checkTouch() {
for (int i = 0; i < 7; i++) {
int pin = touchPins[i];
int touchValue = CircuitPlayground.readCap(pin);
if (touchValue > 1000) {
Serial.print("Touched pad A");
Serial.println(pin - A0);
playTouchNoteAndLight(i);
return;
}
}
}
void playTouchNoteAndLight(int pad) {
int frequency = touchFrequencies[pad];
uint32_t randomColor = CircuitPlayground.colorWheel(random(0, 255));
tone(A0, frequency);//播放
for (int i = 0; i < 10; i++) { //亮灯
CircuitPlayground.setPixelColor(i, randomColor);
}
delay(600); // 播放600毫秒
noTone(A0); //停止播放
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 0);
}
}
演示:
还实现了一个进阶版,可以根据MIDI音符信号,使用板载蜂鸣器进行播放。程序会自动识别传入MIDI音符的音调,时值以及力度,会像创意任务一中的一样用彩色LED给出力度的电平指示,可以作为外部MIDI乐器用DAW或MIDI键盘直接控制。此外,还手动实现了一个PWM发生器,可以通过板子上的两个按钮实时的调整蜂鸣器音量大小。
流程图:
代码:
#include <Adafruit_CircuitPlayground.h>
#include <MIDIUSB.h>
// MIDI音符到频率的映射,A0到C8
const int noteFrequencies[] = {
27, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186
};
// 定义渐变颜色数组
uint32_t gradientColors[10] = {
CircuitPlayground.colorWheel(90),
CircuitPlayground.colorWheel(80),
CircuitPlayground.colorWheel(70),
CircuitPlayground.colorWheel(60),
CircuitPlayground.colorWheel(50),
CircuitPlayground.colorWheel(40),
CircuitPlayground.colorWheel(30),
CircuitPlayground.colorWheel(20),
CircuitPlayground.colorWheel(10),
CircuitPlayground.colorWheel(0)
};
int volume = 100; // 初始音量
int currentNote = -1;
unsigned long noteStartTime = 0;
unsigned long noteDuration = 0;
int currentFrequency = 0;
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
}
void loop() {
checkButtons(); // 检查按钮状态
midiEventPacket_t rx;
do {
rx = MidiUSB.read();
if (rx.header != 0) {
Serial.print("Received: ");
Serial.print(rx.header);
Serial.print("-");
Serial.print(rx.byte1);
Serial.print("-");
Serial.print(rx.byte2);
Serial.print("-");
Serial.println(rx.byte3);
// 检查NOTE ON 消息
if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 != 0) {
int note = rx.byte2; // 获取音符音高
int velocity = rx.byte3; // 获取音符力度
playNoteAndLight(note, velocity);
} else if ((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) {
stopNoteAndLight();
}
}
} while (rx.header != 0);
if (currentNote != -1 && millis() - noteStartTime >= noteDuration) {
stopNoteAndLight();
}
delay(5);
}
void playNoteAndLight(int note, int velocity) {
if (note >= 21 && note <= 108) { // 确保音符在有效范围内
int frequency = noteFrequencies[note - 21];
currentFrequency = frequency;
currentNote = note;
noteStartTime = millis();
noteDuration = 1000;
// 调用PWM函数来播放音符
startPWM(A0, frequency, volume);
// 计算亮灯的数量
int ledCountToLight = map(velocity, 0, 127, 0, 10);
Serial.print("Velocity: ");
Serial.print(velocity);
Serial.print(", LED Count: ");
Serial.println(ledCountToLight);
// 更新 LED 灯的亮度
for (int i = 0; i < 10; i++) {
if (i < ledCountToLight) {
// 点亮 LED
CircuitPlayground.setPixelColor(i, gradientColors[i]);
} else {
// 关闭 LED
CircuitPlayground.setPixelColor(i, 0);
}
}
}
}
void stopNoteAndLight() {
stopPWM(A0); // 停止自PWM
turnOffAllLEDs(); // 关闭所有 LED 灯
currentNote = -1;
}
void turnOffAllLEDs() {
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 0); // 关闭 LED
}
}
void checkButtons() {
if (CircuitPlayground.leftButton()) {
volume = max(volume - 5, 0); // 减小音量
Serial.print("Volume Down: ");
Serial.println(volume);
delay(200);
}
if (CircuitPlayground.rightButton()) {
volume = min(volume + 5, 127); // 增大音量
Serial.print("Volume Up: ");
Serial.println(volume);
delay(200);
}
}
void startPWM(int pin, int frequency, int volume) {
int dutyCycle = map(volume, 0, 127, 0, 255);
analogWrite(pin, dutyCycle);
tone(pin, frequency);
}
void stopPWM(int pin) {
noTone(pin);
}
演示:
项目总结
总的来说,在这次活动中,我尝试了 Adafruit Circuit Playground 板上的各种功能,并将其应用于不同的任务。通过实现不同的功能模块,包括音符播放、LED 视觉反馈和各种传感器的使用,我深入了解了这块板子的多样性和强大功能。通过这些尝试,我对硬件编程有了更全面的理解,对如何利用现有资源进行创意性的实现有了更深的认识。这次项目不仅提升了我在实际操作中的技能,也让我认识到了系统设计中的细节和优化的重要性。每一次的测试和调试都让我获得了宝贵的经验,尤其是在调节和优化过程中学到的实际问题解决技巧。对于未来的活动,我希望能够看到更多有趣和创新的硬件开发板。这将激发硬件爱好者的创意和兴趣,让我们能够在更广泛的应用场景中进行尝试和实现,使得每次活动都充满新鲜感和探索的乐趣。最后再次感谢EEWorld和得捷电子提供的机会,让我们能够深入体验和探索 Adafruit Circuit Playground 板的各种功能,期待未来能够继续参与类似的活动,并探索更多有趣的硬件和技术。
项目源码
https://download.eeworld.com.cn/detail/eew_nkXjf8/633913