拍报机硬件构成
| 模块 | 选型/建议 | 说明 |
|---|---|---|
| 核心处理板 | RK3568 | 四核 A55 CPU + Mali-G52 GPU,负责摄像头采集、图像处理、UI 和打印控制 |
| 内存 | 2~4GB LPDDR4 | 确保预览、图像缓存和打印队列顺畅 |
| 存储 | 16~32GB eMMC 或 SD 卡 | 系统、照片缓存和日志存储 |
| 摄像头 | USB 3.0 相机:Razer 清姬 V2,1080p | 支持高帧率 1080p 拍摄,USB 3.0 带宽保证图像流畅,自动曝光/白平衡 |
| 镜头 / 光学 | 固定焦距,宽视角 | 景区拍照距离适中,保证人脸和半身清晰 |
| LED 补光 | 白光 / 柔光 LED | 提升低光环境下拍摄质量,避免阴影 |
| 显示屏 | 21 英寸触摸屏(可选非触控) | 大屏预览照片和界面,便于多人观看 |
| 用户交互 | 按键或触摸屏操作 | 拍照、确认打印、选择照片 |
| 打印机 | 彩色打印机:HP 5225 | USB / 网络接口,支持彩色打印,适合景区纪念照片 |
| 打印缓存 | JPEG 图片缓存 | 保证打印速度稳定,减少延迟 |
| 电源 | 12~19V DC 稳压 | 稳定供电,支持长期运行 |
| 散热 | 散热片 + 风扇(可选) | 保证 SOC 长时间运行不降频 |
| 网络接口 | Ethernet / Wi-Fi(可选) | 上传照片或统计数据 |
| 外设接口 | USB、串口、GPIO | 摄像头、打印机、按钮、指示灯扩展 |
APP 软件工时计算
| 模块 | 子功能 | 功能描述 | 估算工时(人天) |
|---|---|---|---|
| MQTT 通信模块 | 订阅主题/重连机制 | MQTT 连接、鉴权、断线重连、自动恢复 | 1.5 天 |
| 文件下发/应答 | 解析 server URL → 下载视频文件 → 覆盖保存 → 成功/失败上报 | 1 天 | |
| 设备登录/应答 | 上报主屏电流/电压 → 状态保持 → 应答机制 | 0.8 天 | |
| OTA 升级/应答 | 下载 apk → 校验 → 静默安装/提示安装 → 上报应答 | 1.5 天 | |
| 远程重启设备/应答 | 接收重启命令 → 执行 reboot → 应答 | 0.3 天 | |
| 运营时间设置 | 解析配置 → 系统定时开关机调度(精确到分钟) | 1 天 | |
| 视频播放模块 | 全屏循环播放 | 本地视频循环播放 + 卡顿恢复 + 断点切换 | 1 天 |
| 视频覆盖信息浮层 | 左下角:设备编号+APP版本;右下角:网络模式+信号强度 | 0.6 天 | |
| 视频文件管理 | 新视频覆盖旧视频、本地校验、播放文件校验 | 0.5 天 | |
| 业务入口控制 | 空闲广告播放逻辑 | 无人 → 播放广告;有人 → 切业务界面提示 | 1 天 |
| 调试页面 | 打印测试 | 简单按钮功能触发 | 0.3 天 |
| MQTT 地址切换 | 允许修改/保存/重连 MQTT 地址 | 0.5 天 | |
| 网络连通测试 | Ping/HTTP 测试,显示结果 | 0.5 天 | |
| 网络自检 | 开机自检 | 开机检测网络 → 无网自动重启(限制 3 次) | 1 天 |
| 心跳机制 | 每分钟心跳上报 | 定时任务 + 异常恢复机制 | 0.5 天 |
| 基础框架 | App 架构/日志系统 | MVVM/Service 前台服务/日志上传接口预留 | 1.5 天 |
| 稳定性、边缘情况处理 | Crash 监控、异常恢复 | 视频卡死、MQTT 卡死、网络抖动处理 | 1 天 |
| UI/交互开发 | 所有界面 UI | 主界面、调试界面、提示界面 | 1 天 |
| 测试与优化 | 自测与修复 | 全功能联调,本地循环测试,Bug 修复 | 2 天 |
USB 摄像头
打印机
HP CP5225 是一款 A3 彩色激光打印机 / 打印机设备(Color LaserJet 系列)
| 方式 | 接口 | 开发难度 | 优缺点 | 推荐程度 |
|---|---|---|---|---|
| HP 官方 Android SDK | USB / 网络 | 中等 | ✅ 支持 CP5225 ✅ 封装打印逻辑 ✅ 开发简单 ❌ SDK可能有兼容性限制 |
★★★★★ |
| 网络打印 IPP / JetDirect | Ethernet / Wi-Fi | 中等 | ✅ 不依赖 USB ✅ 稳定可靠 ❌ 需生成 PCL/PS 数据 |
★★★★☆ |
| USB 直连 + libusb/PCL | USB Host | 高 | ✅ 可直连打印 ❌ 需自己实现 PCL/PS协议 ❌ 开发复杂 |
★★★☆☆ |
| Android PrintManager | USB / 网络 | 低 | ✅ 简单调用 ❌ USB直连支持有限 ❌ 更适合 PDF/图片渲染 |
★★☆☆☆ |
可以用 Android 11 的以太网共享功能来打印,但打印机必须支持网络打印(IPP、JetDirect 9100、LPR 等)。USB-only 的打印机(无网卡)无法通过以太网共享直接变成网络打印机。
打印方案选择
设备作为景区拍报机时,使用 Wi-Fi 热点还是 以太网共享(Ethernet Tethering) 来连接 HP CP5225 打印机,哪个更适合。我们可以从 稳定性、部署成本、延迟、操作复杂度几个角度分析
| 维度 | Wi-Fi 热点 | 以太网共享 |
|---|---|---|
| 稳定性 | 中等 | 高 |
| 带宽 | 54~600 Mbps(视标准) | 100~1000 Mbps(有线) |
| 延迟 | 较高,偶尔掉线 | 低,基本无丢包 |
| 干扰 | 多,景区人多或有其他 Wi-Fi 可能冲突 | 无,物理线路保证 |
| 部署成本 | 不需要网线,灵活 | 需要网线,布线麻烦 |
| 适合距离 | 5~10 米 | 可长距离延伸,网线长度受限(100 m) |
| 移动性 | 高,可随拍报机移动 | 低,打印机位置固定 |
| 配置复杂度 | 简单,手机热点+打印机接入 | 简单,Ethernet Tethering 打开即可 |
| 打印可靠性 | 偶尔失败,需要重连 | 极高,不易掉线 |
APP 打印接口
| 接口 / 方法 | 适合场景 | 优缺点 |
|---|---|---|
| PrintManager + PrintDocumentAdapter | PDF / 图片打印 | ✅ 系统自带,API简单 ❌ 需要先生成 PDF/Bitmap |
| TCP Socket 9100(JetDirect) | 票据 / 自定义打印 | ✅ 直接发送 PCL / PS 命令 ❌ 需生成 PCL/PS |
| IPP | PDF / PostScript | ✅ 标准协议,跨平台 ❌ 需要实现 IPP 协议 |
| HP / Mopria 插件 | 普通文档打印 | ✅ 自动发现网络打印机 ❌ 依赖插件 |
景区拍报机 + CP5225 网络打印,推荐:
- 票据/票单打印 → JetDirect TCP 9100 + PCL/PS
- 图片 / PDF 打印 → PrintManager + PDF/Bitmap
PrintManager + PDF/Bitmap
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.pdf.PdfDocument
import android.print.PrintAttributes
import android.print.PrintManager
import android.print.pdf.PrintedPdfDocument
import android.print.PrintDocumentAdapter
import android.print.PrintDocumentInfo
fun printBitmap(context: Context, bitmap: Bitmap, jobName: String = "PrintJob") {
val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager
val adapter = object : PrintDocumentAdapter() {
override fun onLayout(
oldAttributes: PrintAttributes?,
newAttributes: PrintAttributes,
cancellationSignal: android.os.CancellationSignal,
callback: LayoutResultCallback,
metadata: android.os.Bundle?
) {
if (cancellationSignal.isCanceled) {
callback.onLayoutCancelled()
return
}
val info = PrintDocumentInfo.Builder("$jobName.pdf")
.setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
.setPageCount(1)
.build()
callback.onLayoutFinished(info, true)
}
override fun onWrite(
pages: Array<android.print.PageRange>,
destination: android.os.ParcelFileDescriptor,
cancellationSignal: android.os.CancellationSignal,
callback: WriteResultCallback
) {
if (cancellationSignal.isCanceled) {
callback.onWriteCancelled()
return
}
try {
val pdfDocument = PrintedPdfDocument(context, PrintAttributes.Builder().build())
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, 1).create()
val page = pdfDocument.startPage(pageInfo)
// 将 Bitmap 绘制到 PDF 页面
val canvas: Canvas = page.canvas
canvas.drawBitmap(bitmap, 0f, 0f, null)
pdfDocument.finishPage(page)
// 写入文件
pdfDocument.writeTo(destination.fileDescriptor)
pdfDocument.close()
callback.onWriteFinished(arrayOf(android.print.PageRange.ALL_PAGES))
} catch (e: Exception) {
callback.onWriteFailed(e.message)
}
}
}
printManager.print(jobName, adapter, null)
}
JetDirect TCP 9100 + PCL/PS
- 对于 彩色照片或广告图,你可以先把 Bitmap 渲染到 PDF,然后直接用 TCP 9100 发送 PDF 数据,这样保留彩色和高分辨率。
- 如果只打印票据、二维码、票单,可以直接用上述黑白 PCL 方法,简单高效
方式一
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.pdf.PdfDocument
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.net.Socket
import kotlin.concurrent.thread
/**
* 将 Bitmap 渲染为 PDF,然后通过 TCP 9100 发送到打印机
*
* @param context Context
* @param printerIp 打印机固定 IP
* @param bitmap 要打印的 Bitmap
* @param jobName 打印任务名称
*/
fun printBitmapAsPdfTCP9100(context: Context, printerIp: String, bitmap: Bitmap, jobName: String = "PrintJob") {
thread {
try {
// 1. 创建 PDF 文档
val pdfDocument = PdfDocument()
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, 1).create()
val page = pdfDocument.startPage(pageInfo)
val canvas: Canvas = page.canvas
canvas.drawBitmap(bitmap, 0f, 0f, null)
pdfDocument.finishPage(page)
// 2. 将 PDF 写入 ByteArray
val pdfStream = ByteArrayOutputStream()
pdfDocument.writeTo(pdfStream)
pdfDocument.close()
val pdfBytes = pdfStream.toByteArray()
// 3. 通过 TCP 9100 发送到打印机
val socket = Socket(printerIp, 9100)
val output: OutputStream = socket.getOutputStream()
output.write(pdfBytes)
output.flush()
output.close()
socket.close()
println("打印成功: $jobName")
} catch (e: Exception) {
e.printStackTrace()
println("打印失败: ${e.message}")
}
}
}
方式二
import android.graphics.Bitmap
import android.graphics.Color
import java.io.OutputStream
import java.net.Socket
import kotlin.concurrent.thread
/**
* 将 Bitmap 转换为 PCL 简单黑白位图命令,然后通过 TCP 9100 打印
*
* @param printerIp 打印机固定 IP
* @param bitmap 要打印的 Bitmap
*/
fun printBitmapTCP9100(printerIp: String, bitmap: Bitmap) {
thread {
try {
val socket = Socket(printerIp, 9100)
val output: OutputStream = socket.getOutputStream()
// PCL 打印头
val header = "\u001B%-12345X@PJL JOB\n@PJL ENTER LANGUAGE=PCL\n"
output.write(header.toByteArray())
// 将 Bitmap 转换为黑白位图
val width = bitmap.width
val height = bitmap.height
val bitmapData = ByteArray(width * height / 8)
var byteIndex = 0
var bitIndex = 0
var currentByte = 0
for (y in 0 until height) {
for (x in 0 until width) {
val pixel = bitmap.getPixel(x, y)
// 灰度值
val gray = (0.3 * Color.red(pixel) +
0.59 * Color.green(pixel) +
0.11 * Color.blue(pixel)).toInt()
val bit = if (gray < 128) 1 else 0
currentByte = currentByte or (bit shl (7 - bitIndex))
bitIndex++
if (bitIndex == 8) {
bitmapData[byteIndex++] = currentByte.toByte()
bitIndex = 0
currentByte = 0
}
}
}
// PCL 位图打印命令(简化版)
val pclBitmapStart = "\u001B*r1A"
val pclBitmapEnd = "\u001B*rB\n"
output.write(pclBitmapStart.toByteArray())
output.write(bitmapData)
output.write(pclBitmapEnd.toByteArray())
// 打印结束
val footer = "\u001B%-12345X"
output.write(footer.toByteArray())
output.flush()
output.close()
socket.close()
println("打印成功")
} catch (e: Exception) {
e.printStackTrace()
println("打印失败: ${e.message}")
}
}
}
21寸高亮屏幕选型
| 使用场景 | 典型亮度需求(cd/m² / nits) |
|---|---|
| 室内/阴天 | 200~300 nits |
| 半户外 / 阴天或有遮阳 | 500~700 nits |
| 户外阳光下(一般晴天) | 1000~1500 nits |
| 强阳光直射户外 | 2000~2500 nits |
12V LVDS 主板方案下,1000+ nits 基本无法直接实现
- 21 寸高亮屏(1500 nits)大约需要 40~60W 背光功率。
- 12V 电压下,电流 I = P / V ≈ 40~60W / 12V ≈ 3.3~5A
- 这样对主板电源设计要求很高,普通工业板可能无法提供
LVDS 对比 HDMI
| 特性 | 高亮 LVDS 屏 (~1500 nit) | 高亮 HDMI / 工业户外屏 (≥1000 nit) |
|---|---|---|
| 亮度 | ~1000–1500 nit | ≥1000 nit |
| 功耗 / 发热 | 高功耗,热量大,散热要求高 | 工业设计散热好,适合长时间高亮 |
| 接口 / 兼容性 | LVDS,依赖主板,升级/替换困难 | HDMI / 标准接口,兼容性好,易替换 |
| 环境适应性 | 室内/半户外可控环境 | 户外/半户外/强光/高温可用,防尘防水 |
| 优点 | 面板尺寸灵活,可嵌入机箱 | 散热好、稳定性高、模块化,适合长期户外使用 |
| 缺点 | 散热难,寿命受高温影响,主板依赖强 | 成本较高,体积大,需独立电源/散热 |