Rokid简介
Rokid是一家总部位于杭州的公司,与我同在一个城市,大家感兴趣的话,可以参见此链接进行了解下他们的发展历程(Rokid的故事)。
创始人来自阿里,他在成立的时候,希望是致力于人工智能行业,但是目前来看,大家都觉得他是搞音响的。我去,“高大上”的人工智能瞬间跌入了街头地摊,创始人也很无奈,其主要就是大众对他们的产品不太了解,从而造成了如此的误解。
同样,我对这家公司也知之甚少,只是通过Rokid官网才知道他们是2014年成立的,并与2014年11月15日进行了产品的内部展示,后来就将这一天被内部定为"Rokid日"。产品的原型和设计也都非常不错,获得了不少设计奖,在这一点上我们应该为他们点赞。
如果大家对他们感兴趣的话,可以去看看Rokid创始人的访谈,自行百度吧,这里不再写链接,以防止有推送广告之嫌。
废话不多说了,开始对我手头的这个MINI版的Rokid进行剖析吧,请大家接着来看!!
硬件系统分析
外观
我拿到的是最新推出Mini版,也是最便宜的一款Rokid(梵星系列:JD上399张毛爷爷),我的这个颜色为白色的,还有一款为枪色的。
外观图示
为了保持我的设备可用(防止将我的设备给我封闭了),我不得不将我的产品的ID进行打码处理。
因为硅胶上面的那些字体不好拍照,大家就将就这看吧,如下图:
这个角度好,亮亮的。
配件
这个是截图来自JD网站上的,上面的这款就是枪色的(那是枪黑色的),接口为USB TypeC。
硬件内部结构
硬件拆解
脚垫撕开后,里面是螺丝孔和锂电池插孔,为了移动方便,后续可以增加锂电。
上盖部分,里面应该是触屏按键和拾音话筒部分,通过连接线与主板相连。
底盖部分,就是主控板了。拆开上面的三颗螺丝后,就可以看到对应的主控芯片了。
上一个全图,脚垫和上盖,底盖部分,将底盖拆开后,会看到一个散热片和主控芯片部分。如下图:
主要芯片
主控芯片
BT+WIFI芯片
总体分析:
主控芯片:
Amlogic A113X quad core Cortex A53 processor
蓝牙+WIFI二合一芯片:
AP6212是正基AMPAK推出的一款低功耗高性能的WiFi+BT4.2模块,该模块符合802.11b/g/n标准,其中WiFi功能采用SDIO接口,蓝牙采用UART/I2S/PCM接口,具有Station Mode,SoftAP,P2P功能等,主要应用于OTT盒子、广告机、智能手机及便携式设备之中。
NAND FLASH:
SAMSUNG K9F4G08U0F-SCB0 4G (512mx8) Nand Flash
软件系统分析
通过Rokid的APP将Rokid mini 梵星配置到自己的Openwrt路由上,然后通过Nmap软件进行扫描这个IP以获取其开放的端口号。
端口号开放:80、4201、5355、5555、10004、15003、15005、15006。
登录到80端口上,能看到如下的界面:
从这个页面上点击“文档”会进入到rokid的github上去,从而可以看到RokidOS的源代码部分。感兴趣的可以进去瞅瞅吧。
具体的链接发布出来了:
https://rokid.github.io/rokidos-linux-docs/source/getting_started/overview.html
上面包含了Rokid的整体系统的架构部分,系统的编译、构建、刷机等等操作说明是一份非常完整的文档说明。
细节部分自己去摸索吧,我关心的是我的这个设备的调试部分,所以可以进入“系统调试”页面后,发现,原来USB TypeC实际上也是刷机线,直接将其接入到Windows系统中,会看到一个ADB的设备,通过ADB4.4即可进入的系统中。
ADB进入系统
其它
通过其它的命令,可以将这个系统的目录进行打包出来,获取其系统的BIN文件;
具体的信息:
rootfstype=ramfs init=/init console=ttyS0,115200 no_console_suspend earlycon=aml_uart,0xff803000 ramoops.pstore_en=1
ramoops.record_size=0x8000 ramoops.console_size=0x4000 logo=,loaded,androidboot.selinux=enforcing
androidboot.firstboot=1 jtag=apao androidboot.serialno=xXxXxXxXxXxXx androidboot.rokidseed=XxXxXxXxXxXxXx
bootmode= androidboot.hardware=amlogic androidboot.ftm=0 systemd.unit=default.target
androidboot.usid=1234567890 root=/dev/mtdblock5 rootfstype=squashfs ro init=/sbin/init
其整体系统的BIN文件为squashfs文件,解压Squashfs文件后,能获取整个系统的目录结构和实际运行的文件;
通过分析其整体系统的文件发现,在进行OTA升级的时候,设备会自动的下载一个tgz的包,然后擦除/data目录,将其重新解压至/data/以达到升级的目的;
OTA部分,大家可以自行的研究,我这里就贴一个OTA的API吧。
通过GET方法请求对应的SN号(这里我隐藏了我的SN号,购买的筒子们,自己填写自己的吧或者你去遍历这个生产的SN号也行):
https://homebase.rokid.com/packages/rokid-homebase/latest?env=release&sn=XxxxxxXxxx
RESULT回应json的TGZ包文件:
{"version":"rokid-homebase-2-0-0-5aab5f35-1c07ad.tgz","env":"release"}
通过curl 下载对应的TGZ包,并重新解压到/data/目录,达到Rokid MiNi升级的目的:
https://s.rokidcdn.com/homebase/node-pkg/rokid-homebase/rokid-homebase-2-0-0-5aab5f35-1c07ad.tgz
得到的这个TGZ包后,就获取了rokid的data部分下新的内容。
来来来,围观一下整体的目录吧!
下面的这个很重要,你们都懂的。。。
看到OTA没有,那个就是在线升级的部分了。
因为159行,展示不全,那就贴一下出来吧。
leekwen@eewiki:~/rokid-mini-fs-root/usr/lua/ota$ cat main.lua
require "env"
local props = require "core.prop"
if props["persist.sys.rokid.noota"] then
return
end
local sharedmem = require "core.sharedmem"
local json = require "cjson.safe"
local checker = require "network.gwrest.otacheck"
local util = require "core.util"
local dbus = require "core.dbus"
local upgrade = require "comp.upgrade"
local dbus = require "core.dbus"
local flock = require "core.flock"
local trackObj = require("comp.track")
local dir = upgrade.DIR
local EVENT_DOWNLOAD = "download"
local EVENT_DOWNLOAD_FAILED = "download_failed"
local EVENT_DOWNLOAD_FINISHED = "download_finished"
-- 是否已经有进程在运行
local err, locked, lock = upgrade.trylock()
print("lock file", err, res, lock)
-- 如果已经在运行直接返回
if not lock then
return
end
-- 锁失败也直接返回
if not locked then
print("lock failed, exit")
return
end
-- 使用wget下载镜像,支持断点续传
local function download(continue, src, dest)
print("downloading", src, continue)
local c = continue and " -c" or ""
return os.execute("wget " .. c .. " -O " .. dest .. " " .. src)
end
-- 制定路径的文件是否已存在
local function exists(path)
local f = io.open(path, "r")
local e = (f ~= nil)
if f then
f:close()
end
return e
end
-- ota dbus sender
local caller = dbus.sender("session", "com.rokid.ota")
.specify("/rokid/ota", "com.rokid.ota")
-- 向activation发送通知
local function notify(event, ...)
caller.signal(event, ...)
end
-- 向服务端请求最新的版本信息,第一个返回值是err,第二个是版本信息
-- 如果已经是最新版本,第二个返回值也是nil
local function checkVersion()
local reader = sharedmem.reader("/activation.account")
local err, txt = reader.read(60)
if err then
return err
end
local devInfo = json.decode(txt)
local err, result = checker.check(devInfo)
print(err, result)
if err then
return err
end
if result then
if not result.checksum or not result.imageUrl or not result.version then
return "service error"
else
trackObj.trackPrint("ota.check", "curVersion", props.version(), "newVersion", result.version)
return nil, result
end
end
-- up to date, return nothing
end
-- 下载镜像
local function downloadImage(dest, src, checksum)
local continue = false
local needDownload = false
if exists(dest) then
local stat = System.fstat(dest)
print("image file exists, size", stat.size)
if checksum ~= util.smd5(dest) then
print("need continue downloading")
needDownload = true
continue = true
end
else
needDownload = true
end
if needDownload then
trackObj.trackPrint("ota.downloading", "url", src, "dest", dest)
local success, err, num = download(false, src, dest)
if not success then
trackObj.trackPrint("ota.downloaded", "error", err)
print("download error", err, num)
return "download error"
end
end
trackObj.trackPrint("ota.downloaded", "error", "nil")
print("download finished")
if checksum ~= util.smd5(dest) then
print("file does not match the checksum, will remove", dest)
os.execute("rm " .. dest)
return "image error"
end
end
local err, result = checkVersion()
if err or not result then
return err
end
local checksum = string.lower(result.checksum)
os.execute("mkdir " .. upgrade.DIR)
local info = {
checksum = checksum,
size = 100 * 1024 * 1024,
version = result.version,
}
local dest = upgrade.imagePath(info)
if checksum ~= util.smd5(dest) then
info.status = "downloading"
-- TODO extra中清除其他img
if not upgrade.writeInfo(info, true) then
return "write info error"
end
notify(EVENT_DOWNLOAD)
local err = downloadImage(dest, result.imageUrl, checksum)
if err then
notify(EVENT_DOWNLOAD_FAILED)
return err
end
end
info.status = "downloaded"
upgrade.writeInfo(info, true)
notify(EVENT_DOWNLOAD_FINISHED)
至于里面的URL啊,API啊等等lua源码里面都有的。
如果感兴趣,我可以将这个squashfs解压的包共享出来,不知道Rokid会不会来咬我啊。O(∩_∩)O哈哈~。
启动时,资源部分在res里面,在activation启动时调用对应的ogg以播放对应的提示声音,比如,入网/启动/唤醒/关机等等。
下载下OTA的升级包吧,看看里面到底是什么鬼!
目录结构如上图。想看具体源码的你们自己下载吧,上面有API的返回链接地址。
好了,我就写到这里了。
总结一下:
智能硬件(音响)方面,大多是从android演变而来,将Android手机系统中语音助手的功能进行放大,并做好音响的功放功能,整合出来了这样的一个产品,从产品的实现上来说,实现并不困难,难的是能将这个产品做到极致。若琪算是一个还不错的产品了,如果能在安全性上做的更好的话!!
阿里出来的,对体验性要求比较高,但是若琪梵星这个的触摸按键,那个尿性,你能叫体验性高吗?功放的功能也是一般般吧。。产品内部的代码也没有多少做了加密处理,只在通讯上做了些认证,可是认证的整个实现方式,都能够看到源码,那么你那再好的认证,也相当于白搭。。如果通过中间人攻击OTA升级的部分的话,那么你家的这个东西会不会半夜里放出来鬼叫声,那就看看这个攻击者想不想搞你了。。
假如有一天,你睡着了,突然播放出了鬼叫声,那估计是你家的智能设备抽风了吧!!哈哈