# led-clock **Repository Path**: com_shisan/led-clock ## Basic Information - **Project Name**: led-clock - **Description**: 树莓派+max7219 LED矩阵钟表(闹铃) - **Primary Language**: Java - **License**: Zlib - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-03-29 - **Last Updated**: 2025-11-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 基于MAX7219显示屏的闹铃小程序,在安装有GPIO库的LINUX上运行。 本项目基于树莓派硬件 + JVM(Java 11)+ Nginx(可选,如果对SpringMVC熟悉的话可以将打包好的前端页面扔到resources目录下也可以访问) 运行。 如果希望在Java8上运行,请将pi4j降级到1.2版本,这里是1.4版本。 降级时,需要移除openjfx的依赖。 ### 效果图 - 钟表态 ![钟表态](read_me_pic001.jpg) - 闹铃态 ![闹铃态](read_me_pic003.jpg) - 音乐播放态 ![音乐播放态](read_me_picIMG_20220502_230450.jpg) - 管理控制台 ![闹铃列表](read_me_picScreenshot_20220502_171351.jpg) ![闹铃设置](read_me_picScreenshot_20220502_232724.jpg) ### 准备工作 开发环境:oraclejdk 11 - 17皆可以(在开发环境编译打包),在树莓派上安装openjdk 11,(如果你有能力升级更高版本那更好)、maven(3.6及以上版本)、Intellij Idea/(My)Eclipse/NetBeans 、node.js、WebStorm/VsCode 运行环境:树莓派一枚(支持GPIO库和Linux操作系统,安装Nginx反向代理服务器),Max7219 LED8x8 12个,单个接线方式参见Pi8x8Util.java类,多个LED级联接线方式请继续阅读readme,下文有介绍。 如果使用Nginx托管html页面,参见Nginx配置 TODO 待补充 ### 功能 - 可播放MP3、WAV、OGG和FLAC(FFT电平效果较差)的音乐 - 基于chrome浏览器或其内核的 HTML+CSS 手机端的控制台(目前且仅支持chrome内核的浏览器或安卓系统的浏览器),前后端分离(见 https://gitee.com/com_shisan/led-clock-web项目) - LED显示屏可显示当前时间,包括年月日时分秒和星期;可显示音乐电平、歌曲名称(包括进度条、时间)、闹铃名称和当日温度区间(天气情况) - 支持不重复闹铃、间隔时间闹铃、自定义时间闹铃(有点像备忘录里的闹铃)、法定工作日和法定节假日、父亲节、母亲节和农历生日[注1]8种模式 - 支持整点报时(具备夜间免打扰功能) - 支持闹铃音量与音乐音量独立控制。 *注:仅支持LINUX,基于almixer命令,直接控制操作系统的音量 - 支持音乐本地化管理,可上传与删除 - 支持LED亮度可调节(0 - 15 共16等级) - 支持直接操作树莓派 debian系统的音量大小(不依赖音乐播放器的音量API) 注1:农历生日模式依赖一张数据索引表,索引最高到2050年的1月;如果首次启动会初始化该索引,会消耗一段时间。 ### MAX7219模块 使用8x8 LED矩阵串联起来的LED矩阵屏幕,当然是单色屏幕,本人使用12个8x8LED矩阵串联起来的32x24(4x3)像素作为屏显。 位于 `led-support` 模块下,核心类: - Pi8x8Util.java 基于GPIO库操作硬件的连通类 - SpiPoint.java 单个8x8矩阵操作类,用于将(x,y)坐标点转换为byte[]数组或long坐标[注1] - SpiToPointRotate180 和SpiPoint.java作用相同,只是将坐标180度旋转了过来 - MergedSpi.java 用于合并SpiPoint.java和SpiToPointRotate180.java的处理结果,将单个的8x8联合起来[注2] - MultipleFlushSpi.java 对MergedSpi.java进行了包装,使用Pi8x8Util.java将数据发送给硬件,所以该类是操作屏显的核心类[注3] - MultipleFlushSpiAsConsole.java 基于控制台的屏显模拟器,使用控制台来模拟屏显效果 - NumberUtil.java 使用int来包装(x,y)坐标,一切的(x,y)坐标与int的转换都基于此类[注4] - DefaultFont8x8.java 一些预设的基于一个8x8矩阵的字体 - TextPxCreater.java 用于读取文字像素点,基于这些读取的像素点结果来完成屏显操作 注1:一个8x8 LED矩阵使用byte[8]来描述,正因如此,本人使用了一个long来描述byte[8],就印证了8个字节:一个long可以完美表示一个8x8矩阵。 注2:本人的接线方式如下(因为这样可以节约布线成本): ![接线图](read_me_pic%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202022-05-02%20214918.png) 所以,需要对第二行的坐标进行旋转和镜像操作。目前没有支持像I2C OLED那样可以随意调整屏幕(0,0)坐标的功能,仅支持上述接线一种方式。 注3:你可以继承这个类,重写flushData()方法来实现模拟硬件的操作,比如项目中已存在的MultipleFlushSpiAsCanvas.java(使用javafx的Canvas进行模拟屏显)、MultipleFlushSpiAsConsole.java(使用控制台来模拟屏显)、MultipleFlushSpiNetty.java(在树莓派上开一个socket服务,然后使用该类向树莓派发送数据来模拟屏显) 注4:项目中几乎所有的地方都在用int来表达x,y坐标,你可以类比为使用int来表达rgba颜色一样,高2位表达x坐标,低2位表达y坐标,但它们仍然是x,y坐标,与Pi8x8Util.java里的long表达的坐标还是不同的,因为这里的long表达了64个灯珠的点亮情况。 ### 音乐基础支持模块 这里是音频解码的一些源码,位于 `led-music-lib` 模块下,基于这个开源项目(v2.3)来实现的:https://quippy.de/ 原生支持的格式要多一些,这里我砍掉了他的GUI模块和部分解码模块,精简到只有解码内容,不包括播放、快进快退等基本操作功能,仅支持OGG、MP3、WAV和Flac 4种格式[注]。因为我的那个版本(最新的没研究)存在着诸多bug,最典型的莫过于 暂停空轮询、线程资源没释放的各种奇怪问题。 注:原本是基于JAVE的java库来完成音频转码后播放的,目前maven中央仓库种没有一个适用于树莓派armv7l架构的JAVE库,难道就没有办法实现了吗?NO,时隔一个月重新拾起这个,进行研究,发现需要为树莓派单独安装适用的FFMPEG,同时让JAVE去调用系统安装的FFMPEG就可以了,这里搬个砖,资料来源: 你可能需要简单但煎熬的折腾,因为安装的速度没有那么快。 - 安装H.264和ffmpeg的资料:https://www.cnblogs.com/michaelcjl/p/14732849.html - libmp3lame资源下载地址,下载最新的:https://sourceforge.net/projects/lame/files/lame/ 等等,先别急着划走,资料篇中没启用libmp3lame,需要补充参数;如果不安装libmp3lame库,启用该库会报错,所以要注意,我这里重新总结下。 ```shell # 看下我的Linux版本,我的是debian,如果你的不是,那就自行百度吧 lsb_release -a # ==== 安装h.264开始 # 下载h.264源码,如果你想通过win git 然后rz,那就别费劲了,还是老老实实git命令吧 git clone --depth 1 https://code.videolan.org/videolan/x264 #如果你的git遇到证书问题就执行下面两个命令吧: git config --global http.sslverify false git config --global https.sslverify false cd x264 ./configure --host=arm-unknown-linux-gnueabi --enable-static --disable-opencl # 这一步花费时间相当的长 make -j4 sudo make install # ==== 安装h.264结束 # === 安装libmp3lame开始 curl -XGET https://nchc.dl.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz --output ./lame-3.100.tar.gz tar -zxvf libmp3lame.tar.gz cd lame-3.100 ./configure make sudo make install # === 安装libmp3lame结束 # ==== 安装ffmpeg开始 git clone git://source.ffmpeg.org/ffmpeg --depth=1 cd ffmpeg # 这一步,一定要启用 libmp3lame ./configure --arch=armel --target-os=linux --enable-gpl --enable-libx264 --enable-nonfree --enable-libmp3lame # 这一步花费的时间更久,够你喝一杯茶或抽5根烟了 make -j4 sudo make install # ==== 安装ffmpeg结束 ``` ### 组件模块 位于 `led-component` 模块下,可以说是对我的4x3 (32x24) 量身定做的一些显示组件,我的屏显操作是基于组件互相组合来实现的。 - FastIntList.java 一个精简的int[]数组包装,用于替代ArrayList,它不使用泛型,不使用包装类(少了开箱和封箱操作),clear()方法不做for循环处理(少了一个循环处理),因此new ArrayList<>()性能优于arrayList.clear()的说法也不存在了(少了频繁创建对象,频繁申请内存空间和释放内存空间加重JVM的垃圾回收开销的处理) ### 基础服务模块 这里是对闹铃基础服务、音乐基础服务的一些封装,位于 `led-simple-service` 模块下,包括音乐播放器(重新实现上述的被砍掉的播放功能)、基于sqlite + mybatisplus(baomidou)的闹铃数据存储、闹铃时间运算、定时器(quartz)、以及tianapi.com提供的免费的法定节假日接口(HttpClient、FastJson2.0)。 核心类有: - AudioLevelManager.java 用于管理音频播放的优先级,level 从1到Integer.MAXVAL,值越大优先级越小。这里整点报时优先级最高,被设置为1,闹铃播放level为2,用户音乐播放level为3,空播放器的level为4[注1]。其内置的接口AudioMixer是管理的关键,可参见它的注释说明。 - AudioPlayer.java 是一个抽象类,继承自AudioMixer接口,它实现了一些被管理的基础功能,包括中断、销毁、恢复操作。 - FlacPlayer.java、Mp3Player.java、OggPlayer.java和WavPlayer.java是继承了AudioPlayer.java,实现了播放解码的细节 - MixPlayer.java 这是一个融合了上述4个播放器为一体的包装类,对文件的后缀自动识别进行实例化对应的播放器 - MultiFilePlayer.java 这是一个多文件播放器,它虽然继承自AudioPlayer.java,但是它的逻辑是独立的。引入它的目的是因为一个闹铃可以有多个音乐,而同一个时间点可以有多个闹铃,这就导致要播放多个音乐文件,如果不做统一管理,有可能会导致这些音乐的播放不是一个整体,我希望这些音乐作为一个整体来完成,而不是被其他资源从中间截断。 - ComponentManager.java 对组件进行组合管理 - FastFFT.java 音乐播放时的电平 Effect(特效) 运算器 注1:代码设计的时候,将播放器和屏显进行了强绑定,所以,无奈使用了空播放器来进行无声屏显操作,无声屏显用于显示当前时间(钟表功能)。 level资源竞争说明:同等级资源竞争时,后来者会销毁正在运行的播放器,而后播放自己;如果当前有正在播放的线程,高等级来抢夺资源时会挂起正在播放的线程,然后播放自己,当播放结束时恢复这个线程。当低等级来抢夺这个线程时,自己会被挂起,当这个线程播放结束后恢复自己。 ![优先级图解](read_me_resource%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202022-05-02%20225809.png) 整点报时失效?需要在项目的同等级目录下创建一个music/clock文件夹,将整点报时音频放入其中,格式为000.wav 到 023.wav。 ### 网络服务模块 该模块用于将led-simple-service的管理功能进行对外暴漏,提供给管理页面调用,位于 `led-simple-web-manager` 模块下,基于SpringBoot(MVC) ### 真机启动器模块 位于 `led-real-machine-dataflush` 模块下,用于在真实的树莓派机器上启动用。 此模块实现了led-simiple-service提供的系统音量调节接口(VolumeSettingService.java) 如果希望启用自动读取法定节假日功能,需要添加一个配置: 请先前往 https://www.tianapi.com/apiview/139 申请一个key,而后在 resources目录下新建一个配置文件 application-gov.yml,在里面添加一行: ```yaml tianapi.holiday.key: 你申请的Key ``` 注:如果你不设置这个key,则默认的法定节假日是周末,工作日是周一到周五。 ### 屏显模拟模块 位于 `led-canvas-dataflush` 模块下,基于javafx的canvas来实现。除了它是一个模拟器外,它的功能与 led-real-machine-dataflush 一模一样,同样的需要启用法定节假日自动读取也需要完成一遍上述的操作。 注意,该模块不提供系统音量调节接口(VolumeSettingService.java)的实现,所以这里仅是一个空实现,你可根据自己的操作系统自行实现模拟 (windows下可以参考这个https://blog.csdn.net/wangmx1993328/article/details/80855770)。 ### 基于NETTY的前期屏显测试模块 这个模块是初期测试GPIO库是否调用正确而创建的,为了使代码在脱离真实环境的情况下依然能够观察真机的情况。位于 `gpio-test-client` 模块和 `gpio-test-service` 模块。 可能service名字叫的不准确,应该叫server才对,如名字所说,gpio-test-client跑在代码机器上,gpio-test-service跑在真机上,这里仅有非常简单且粗暴的实现,你可根据自己的需求自行开发定义甚至是删除此模块,它与其他模块不存在直接的联系。 ### i2c OLED 支撑模块 该模块的核心代码来自 https://github.com/entrusc/Pi-OLED 可点亮I2C OLED 128 x 64的屏幕,目前没有任何一个模块依赖使用它,仅仅是保留一个功能项。