若琪智能音响Rokid硬件拆解及系统架构分析
发布日期:2021-06-30 17:15:54 浏览次数:2 分类:技术文章

本文共 7442 字,大约阅读时间需要 24 分钟。

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的源代码部分。感兴趣的可以进去瞅瞅吧。

具体的链接发布出来了:

上面包含了Rokid的整体系统的架构部分,系统的编译、构建、刷机等等操作说明是一份非常完整的文档说明。

细节部分自己去摸索吧,我关心的是我的这个设备的调试部分,所以可以进入“系统调试”页面后,发现,原来USB TypeC实际上也是刷机线,直接将其接入到Windows系统中,会看到一个ADB的设备,通过ADB4.4即可进入的系统中。

ADB进入系统

通过dmesg获取启动过程的信息。

其它

通过其它的命令,可以将这个系统的目录进行打包出来,获取其系统的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=XxxxxxXxxxRESULT回应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.luarequire "env"local props = require "core.prop"if props["persist.sys.rokid.noota"] then    returnendlocal 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.DIRlocal 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    returnend-- 锁失败也直接返回if not locked then    print("lock failed, exit")    returnend-- 使用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 eend-- ota dbus senderlocal caller = dbus.sender("session", "com.rokid.ota")        .specify("/rokid/ota", "com.rokid.ota")-- 向activation发送通知local function notify(event, ...)        caller.signal(event, ...)end-- 向服务端请求最新的版本信息,第一个返回值是err,第二个是版本信息-- 如果已经是最新版本,第二个返回值也是nillocal 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 nothingend-- 下载镜像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"        endendlocal err, result = checkVersion()if err or not result then    return errendlocal 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    endendinfo.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升级的部分的话,那么你家的这个东西会不会半夜里放出来鬼叫声,那就看看这个攻击者想不想搞你了。。

假如有一天,你睡着了,突然播放出了鬼叫声,那估计是你家的智能设备抽风了吧!!哈哈

转载地址:https://leekwen.blog.csdn.net/article/details/79765854 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:天猫精灵设备拆机及系统分析
下一篇:Wireshark抓取网易音乐的下载地址

发表评论

最新留言

很好
[***.229.124.182]2024年04月12日 02时16分29秒