news 2026/4/29 13:43:29

用STM32F103C8T6和JDY-18蓝牙模块,手把手教你做个手机APP调光台灯(附完整源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用STM32F103C8T6和JDY-18蓝牙模块,手把手教你做个手机APP调光台灯(附完整源码)

从零打造智能调光台灯:STM32与JDY-18蓝牙的完美结合

周末工作室的灯光总是不够理想?传统台灯无法满足你的创意照明需求?今天我们将用一杯咖啡的成本,打造一款可通过手机APP自由调节亮度与色彩的智能台灯。这个项目不仅适合电子爱好者练手,更能为你的工作台增添一抹科技感。

1. 硬件选型与搭建

在开始编码之前,我们需要精心挑选每个硬件组件。STM32F103C8T6作为性价比极高的ARM Cortex-M3内核微控制器,配合JDY-18蓝牙模块,构成了这个项目的核心。

核心组件清单:

  • STM32F103C8T6最小系统板(蓝色药丸)
  • JDY-18蓝牙4.2模块
  • 5mm RGB LED灯珠(共阴)
  • 220Ω电阻×3
  • 面包板与杜邦线

提示:JDY-18模块默认波特率为9600,无需额外配置即可与手机配对,极大简化了开发流程。

硬件连接示意图:

STM32引脚JDY-18引脚LED连接
PB10TX-
PB11RX-
PA6-PWM控制线(红)
PC13-绿色LED
PC14-红色LED
3.3VVCC共阴极
GNDGNDLED接地
// 示例:GPIO初始化代码片段 void LED_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); // PWM输出引脚配置 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 普通LED控制引脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStruct); }

2. 开发环境配置与基础工程搭建

工欲善其事,必先利其器。我们需要搭建完整的STM32开发环境,这里推荐使用Keil MDK作为主要开发工具。

开发工具准备:

  1. Keil MDK-ARM(建议V5.25以上版本)
  2. STM32CubeMX(用于生成初始化代码)
  3. ST-Link/V2调试器
  4. 串口调试助手(如SSCOM或XCOM)

创建工程的关键步骤:

  • 在CubeMX中选择STM32F103C8T6型号
  • 配置系统时钟为72MHz
  • 启用USART3(PB10/PB11)
  • 配置TIM3 Channel1(PWM输出)
  • 生成Keil工程
// PWM初始化示例 void PWM_Init(uint16_t arr, uint16_t psc) { TIM_TimeBaseInitTypeDef TIM_BaseInitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_BaseInitStruct.TIM_Period = arr; TIM_BaseInitStruct.TIM_Prescaler = psc; TIM_BaseInitStruct.TIM_ClockDivision = 0; TIM_BaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_BaseInitStruct); TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse = 0; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStruct); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }

注意:PWM频率计算公式为:f = 72MHz / (arr+1) / (psc+1)。对于LED调光,推荐频率在1kHz-5kHz之间。

3. 蓝牙通信协议设计

JDY-18模块采用标准串口通信,我们需要设计一套简洁有效的协议来实现手机与STM32的交互。考虑到实际使用场景,协议应该具备以下特性:

  • 单字节指令,便于快速解析
  • 支持亮度分级调节
  • 支持单独颜色控制
  • 包含全开全关指令

通信协议设计:

指令功能描述参数范围
0x30全关-
0x31全开-
0x32设置PWM亮度级别1固定值
0x33设置PWM亮度级别2固定值
0x34切换红色LED状态-
0x35切换绿色LED状态-
0x36切换蓝色LED状态-
0x37自定义PWM值0-255
// 蓝牙数据处理示例 void ProcessBluetoothData(uint8_t *buf, uint16_t len) { if(len == 0) return; switch(buf[0]) { case '0': // 全关 TIM_SetCompare1(TIM3, 0); GPIO_SetBits(GPIOC, GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); break; case '1': // 全开 TIM_SetCompare1(TIM3, 255); GPIO_ResetBits(GPIOC, GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); break; case '2': // 亮度级别1 TIM_SetCompare1(TIM3, 80); break; case '3': // 亮度级别2 TIM_SetCompare1(TIM3, 160); break; case '4': // 切换红色 GPIO_WriteBit(GPIOC, GPIO_Pin_14, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_14))); break; case '5': // 切换绿色 GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13))); break; case '6': // 切换蓝色 GPIO_WriteBit(GPIOC, GPIO_Pin_15, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_15))); break; default: if(buf[0] > '6' && buf[0] <= '9') { uint16_t pwmVal = (buf[0]-'0') * 25; TIM_SetCompare1(TIM3, pwmVal); } } }

4. 手机APP开发实战

使用MIT App Inventor 2可以快速开发出功能完善的蓝牙控制APP,无需专业的Android开发经验。这个可视化编程工具让APP开发变得像搭积木一样简单。

APP主要功能模块:

  • 蓝牙设备连接/断开按钮
  • 亮度调节滑块(0-100%)
  • 三色LED独立开关
  • 预设亮度快捷按钮
  • 连接状态显示

APP开发关键步骤:

  1. 在Designer界面添加以下组件:

    • ListPicker(用于选择蓝牙设备)
    • BluetoothClient(非可视组件)
    • 多个Button(功能控制)
    • Slider(亮度调节)
    • Label(状态显示)
  2. 在Blocks界面构建逻辑:

// 伪代码表示APP逻辑 当 选择设备按钮 被点击时 调用 BluetoothClient.设备列表 结束 当 BluetoothClient.连接完成时 如果 连接成功 则 设置 状态标签.文本 = "已连接" 否则 设置 状态标签.文本 = "连接失败" 结束 当 亮度滑块 位置改变时 发送字符 "7" + 滑块值 通过蓝牙 结束 当 红色开关 被点击时 发送字符 "4" 通过蓝牙 结束

提示:App Inventor的蓝牙组件默认使用SPP协议,与JDY-18完全兼容。测试时建议先使用官方示例程序验证基本功能。

5. 系统优化与功能扩展

基础功能实现后,我们可以考虑以下增强功能,让你的智能台灯更加出色:

亮度记忆功能:

// 在EEPROM中保存亮度设置 void SaveBrightness(uint8_t val) { FLASH_Unlock(); FLASH_ErasePage(0x0800FC00); FLASH_ProgramHalfWord(0x0800FC00, val); FLASH_Lock(); } uint8_t LoadBrightness(void) { return *(uint16_t*)0x0800FC00; }

渐变调光效果:

void SmoothDimming(uint8_t target) { uint8_t current = TIM_GetCapture1(TIM3); while(current != target) { if(current < target) current++; else current--; TIM_SetCompare1(TIM3, current); DelayMs(20); } }

定时关闭功能:

  • 在APP端添加定时设置界面
  • 发送特定指令格式如"T300"表示300秒后关闭
  • STM32端使用定时器实现倒计时

能耗优化技巧:

  1. 当灯关闭时,将STM32切换到低功耗模式
  2. 通过蓝牙唤醒功能保持连接
  3. 优化PWM频率降低开关损耗
  4. 使用LED恒流驱动提高效率
// 低功耗模式示例 void EnterLowPowerMode(void) { // 关闭所有外设时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_ALL, DISABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ALL, DISABLE); // 配置唤醒源为蓝牙串口 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); NVIC_EnableIRQ(USART3_IRQn); // 进入停止模式 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后重新初始化系统时钟 SystemInit(); }

6. 常见问题解决指南

在实际制作过程中,你可能会遇到以下典型问题:

蓝牙连接不稳定:

  • 检查天线是否完全展开
  • 确保模块与手机距离在10米内
  • 避免2.4GHz频段干扰(如WiFi路由器)
  • 在JDY-18的AT命令模式下执行"AT+RFPA=5"提高发射功率

PWM调光闪烁:

  • 确认PWM频率不低于100Hz
  • 检查电源供应是否充足
  • 尝试在LED两端并联0.1μF电容
  • 确保接地回路阻抗足够低

APP连接失败:

  1. 确认手机已配对JDY-18模块
  2. 检查串口波特率是否为9600
  3. 验证TX/RX线序是否正确
  4. 尝试重启蓝牙模块

电流过大的处理方案:

  • 测量各LED电流(应在20mA左右)
  • 增加限流电阻值
  • 考虑使用MOSFET驱动大功率LED
  • 添加散热片防止过热

调试技巧表格:

现象可能原因解决方法
灯完全不亮电源接反检查极性连接
只有部分LED工作GPIO配置错误重新检查初始化代码
蓝牙能连但无控制串口中断优先级问题调整NVIC优先级
PWM亮度变化不线性人眼对数特性使用gamma校正表
APP按钮无响应协议不匹配抓取串口数据验证指令格式
// Gamma校正表示例 const uint8_t gammaTable[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, // ...中间省略... 220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235, 236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,255 }; void SetGammaCorrectedPWM(uint8_t brightness) { TIM_SetCompare1(TIM3, gammaTable[brightness]); }

7. 项目进阶方向

完成基础版本后,可以考虑以下升级方案:

硬件升级选项:

  • 替换为WS2812B可编程LED灯带
  • 增加光敏传感器实现自动调光
  • 添加旋转编码器用于本地控制
  • 使用锂电池供电增加便携性

软件功能增强:

  • 实现音乐节奏同步光效
  • 开发情景照明模式(阅读/休息/专注)
  • 添加OTA无线升级功能
  • 接入智能语音助手控制

外观设计建议:

  1. 3D打印个性化灯罩
  2. 使用亚克力板制作导光结构
  3. 加入触摸感应控制
  4. 设计磁吸式底座方便摆放

成本优化方案:

  • 用STM32F030系列替代F103
  • 选择JDY-08等更便宜模块
  • 采用SMD元件缩小PCB尺寸
  • 批量采购降低单件成本
// 音乐光效示例代码 void MusicVisualizer(void) { while(1) { uint16_t audioLevel = GetAudioLevel(); // 假设的音频采集函数 uint8_t brightness = audioLevel / 40; // 映射到0-255范围 TIM_SetCompare1(TIM3, brightness); DelayMs(50); } }

在最终测试阶段,建议使用不同品牌手机验证APP兼容性,特别是华为EMUI和小米MIUI系统对蓝牙权限的特殊要求需要额外处理。实际使用中发现,添加简单的LED渐亮/渐灭效果能显著提升用户体验,这可以通过修改PWM值时的步进速度来实现。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 13:41:24

秒杀系统的幂等,只做一层Redis判重远远不够

面试经常被问到幂等设计&#xff0c;很多人的回答是&#xff1a;用Redis的SETNX做个判重就行了。这个回答不能说错&#xff0c;但只对了三分之一。秒杀专栏第15篇写完了&#xff0c;专门讲幂等设计。在实际的秒杀系统中&#xff0c;幂等做一层是扛不住的&#xff0c;我们的方案…

作者头像 李华
网站建设 2026/4/29 13:38:38

RH850 F1开发避坑指南:选项字节配置不当,我的程序怎么都烧不进去?

RH850 F1开发实战&#xff1a;选项字节配置陷阱与看门狗调试全解析 第一次将编译好的程序烧录进RH850 F1系列MCU时&#xff0c;我盯着纹丝不动的调试器界面&#xff0c;后背渗出一层冷汗——JTAG接口毫无反应&#xff0c;仿佛芯片成了一块砖头。这种场景对许多从STM32转向瑞萨平…

作者头像 李华
网站建设 2026/4/29 13:36:29

GetQzonehistory:永久保存QQ空间青春记忆的终极指南

GetQzonehistory&#xff1a;永久保存QQ空间青春记忆的终极指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾经翻看QQ空间里那些承载着青春回忆的说说&#xff0c;却担心它们…

作者头像 李华
网站建设 2026/4/29 13:32:11

PHP V6 单商户常见问题——云编译报SSL证书错误的处理方案

云编译报SSL certificate problem的处理方案 有些小伙伴在云编译的时候会出现 SSL certificate problem 这样的报错&#xff0c;如下图所示&#xff1a; 遇到这个问题&#xff0c;只需要把文件中云服务请求改成http请求即可&#xff1a;niucloud/core/util/niucloud/CloudSe…

作者头像 李华
网站建设 2026/4/29 13:31:26

XUAN-Bike自平衡自行车:AI与机械的完美融合

1. XUAN-Bike自平衡自行车项目概述作为一名长期关注智能硬件开发的工程师&#xff0c;当我第一次看到XUAN-Bike这个项目时&#xff0c;立刻被其巧妙的设计思路所吸引。这个由华为工程师彭志辉&#xff08;Peng Zhihui&#xff09;主导开发的自平衡自行车项目&#xff0c;完美融…

作者头像 李华