从今天开始就是小智元码阅读的正文了,在打开正文之前,有必要理清几个概念,在 app 命名之前,有一个用户不透漏的大概,其是把 free or t o s 初识划过的,那么 free or t o s 是 切入式的操作系统,它来接管 系统时钟之后的调度器,就是在一定的节时钟节拍之后会起用调度器,那么调度器就会调度啊。内存资源去建立内存资源池管理,搭建管理机制,那 需要理清的概念。我们之前在裸机的时候会有一个死循环,在这个 vivo 循环里面会执行我们的一些函数,那么这个函数执行结束之后,那片内存资源就消失了。在操作系统接管之后,我们可以把这些内存分为很多块的 任务,那每个任务都可以是一个死循环,那这些任务是会有优先级,还有堵塞状态, 运行状态,那么他在就绪状态,在排队等待这个资源的到调度器调度,他在这里去执行。而主色状态就是在等待一些资源,比如说内存需要内核管理的有队列、信号量、固执所、事件组 等等,那么有些任务去需要等待这些资源,这些资源如果没有达标,他就处于主色状态, 那任务执行的逻辑我们需要大概知道,那队列我们需要大概知道,他是其实被管理的一个数组,有一个智能的,就像调图器一样,你读这个读这个,读这个读这个。好,剩下的事件可以是事件队列,也可以是精华量队列,也可以是 不要置位队列,也可以是任务队列,这些都可以去建立队列,用队列来管理它,在操作系统一上来的时候会建立一个空闲任务,再建立一个定时任务,最后才是我们的应用任务,那应用任务这里我们就可以建立我们应用场景中需要的各种任务。 那前面两个任务空闲任务的优先级是最低的,在我们没有其他任务执行的时候要跑这个任务,否则就系统崩塌了。我们的小智猿马里面首先建立了一个事件 循环任务,那这个任务去接收所有的队列,这个任务去接收那些队列。事件队列这里有个事件队列, 那么这个任务去接收那个事件队列,那这里面有很多事件,每一个事件就都要去调用一个函数,或者是一个其他的任务,是我们具体执行的任务,那事件循环任务去监视这个事件里面的任务是不是 就行了?那是不是任务这个事件是不是来了?如果来了之后,看他对的是哪个具体的任务,再去调度具体的任务,首先默认的,那我来打开原码看一下这个事件循环任务,我们今天差不多就只能分析这一行代码, 这一行代码是 esp 三二应用程序的基础设施,它是为整个系统提供了事件驱动的架构基础。如果使用 esp idf 框架的时候,必须调用这个抽象函数,这是必调函数之一。 那么我刚才说的那些基础事情,你大概提的了解知道,那就往下再看,就,呃,轻松一些了, 如果不轻松的,大概的去了解一下 free r t o s 的 这些,呃,这些东西我相信我刚才说的,如果你不去深究,不去,嗯,细抠细节的话,应该大概的能懂一些。那, 呃,这个是 idf 的 库,呃,库函数我这里还打不开,然后我就去重新把 idf 打开一下,嗯,就是你把 vs code 去打开文件夹,去打开你的 idf 文件夹下面的,嗯,这个比如说我的 idf 在, 哎, 比如说我的 idf 在 这里,你就打到这个文件夹就能打到这个界面了。那我们首先来看这个, 它第一开始运行的就是这这个函数,然后呢外边这个又加了一个判断对错的,就是,嗯,判断对错的这么一个函数。你看,因为我们在这个 函数里面它是会返回,呃,返回一个这种类似于布布尔值吧,如果返回 ok, 那 么就去执行什么,返回 error 就 执行什么啊。然后呢?默认的,我们是调用了这个,呃, 默认事件循环创建,那在这个函数里面呢?首先它判断你有没有, 有没有建立好这个事件循环,如果有的话,他就只返回一个状态,这是一个状态,是一个状态,你看是一个状态值。 也就是说我们在做完这个创建函数之后,肯定会把它设置一下,设置成呃,非零,因为他初始化的时候是 初识化的时候是零吗?初识化状态就是你没有这个事件循环,没有这个循环事件啊,他的初识化就是零。 然后,哎,再找一下这个函数蹦没了。好在这里好了,到这里。嗯,我们看到他首先创建了这么一个结构体,这个结构体都做了哪些事情呢?大概的就是他主要是配置事件循环的参数的, 先把这些参数先给他写一下,后面再创建这个事件循环的时候可以把这些参数直接传进来,然后看他这个是队列的大小,队列的大小呢? 他说他这里意思是在 menu config 里边可以设置这个队列的大小,后面如果有机会我们可以去看一下 menu config 里面的相关的设置。但是我在其实打开也行,就是感觉打开慢一些,算了,不打开了,应该是在 menu config 里边有设置, 你可以设置这个事件的大小。然后这个呢?就是这个事件的任务,他被默认成了系统任务。啊,这个任务,这个现在这个任务和这个队列的关系是什么呢?就是,呃,比如说这里有一个队列吧,里边存了很多呃 事件,嗯,成了很多事件,这些事件可以是 wifi, wifi 联网成功的标志位,也可以是,嗯,呃,串口传输数据啊。然后这个任务呢,就是我们现在这个呃事件循环这个任务,这个任务他去 那去,一直去。就是这叫什么?消费者生产者与消费者,这个队列就是生产者这个事件,这个任务是消费者他去消费这些事件,这个事件来了。好,我看一下这个事件对应哪些回调函数,我去调用那个函数 啊,这个任务就在执行这个,不停地从这里边读事件,然后去调用对应事件的任务。呃,然后这个就是任务的堆站大小任务的, 呃,堆站大小循环事件任务的堆站大小和它的优先级,它的优先级和它运行在哪个盒上?我们不是双盒吗?它默认是这个零 啊。还有这个优先级,我们之前在说我们可以通过 free r two o s 的 创建函数创建任务嘛,那么这个任务相当于假如说我们先不讨论优先级,就会有很多个任务在这里。呃。 循环执行,就定时。比如说,哎,你这个任务执行一段,然后切到它,然后再切到它,呃,然后切完了之后再翻过来,这样,呃循环执行。那么,呃这个, 呃我们现在这里边的这个 task, 这个 task 就是 其中这个任务之一,只不过它是系统默认第一建立的,我们后面如果再写,嗯,再写 taskcreate, 它就会有我们自己的呃任务函数, 我们也可以不写这些任务函数,通过这个 task 来调用我们的这个任务啊,就是因为这个 task, 它是在默读取这些事件队列吗?读取这些队列里边的事件,这个事件可以绑定到我们自己要执行的这些任务,也可以不绑定,后面如果有绑定我们再说。 然后,呃,就是他就去创建这么一个,接下来就进入了创建事件循环,哎,这个函数里面打开,他去看他去创建什么, 哎,这个屏幕好像不能太大了。 呃,可以往上调一点吗?调不了。那就这样吧。这个函数里边首先他看你看你的,呃,刚才我们不是有一个事件循环的结构体吗?那个结构体里边不就存放了一些呃参数吗?他就传给这个 这个函数了。然后如果这个参数它是空的啊,那就返回了。呃,返回初矢值嘛你,你再去重新建立那个那个参数去。呃,如果这个它是空的,这个 event loop 是 什么? event loop 就是 我们的这个。错了, 应该是这个吧,这个就是刚开始的这个标志位嘛, 他到他等他这个函数执行完了,会把他比如说置一,就反正废空了嘛,你就不会进到这个循环里面就会。呃,就是这个,这个函数只能执行一遍吧?这个逻辑就是这个函数只能执行一遍啊。我们继续来看这个。 嗯,前几句就是判断你有没有把那个,呃事件循环的那些配置参数准备好,这个如果准备好了,那 就不执行这里面,如果没准备好,那你就去准备去。然后这个呢就是实体化了一个事件循环的结构体,这个结构体大概做了这些内容,第一个就是把这个我们打开看一下吧。好吧。 嗯,就是首先是这个名字, 这是谁的名字?是这个事件的啊?这事件,这个事件循环的这个名字。然后呢就是队列,事件循环的队列还有任务据柄还正在执行的任务,据柄 正在执行的任务就这个队列。然后这后面是这是呼市所,这个就是,嗯,应该是一个集合,这个是一个。呃,呃, 每一个事件,就比如说他不是有一排事件吗?每一个事件对应的是那个任务啊,事件机和事件 id 啊,对应的那个任务,我们后面在调用的时候可以去,嗯,去 通过这个去去调用嗯,对应的事件对应的那个函数,然后我们再回到这里,回到这里,回到这里, 这里不是创建了一个事件吗?啊?到这里这个事件事件循环的这个磁力结构体就创建好了,嗯,然后它呢? 它就这是 kalok, kalok 就是 在堆上分布,呃,堆上申请一片内存来存放这个实历,这实历,这实历应该,嗯,它因为它每一项都是一个,呃 类似于结构体的东西,然后呃如果这个 这个,嗯创建成功了,哎,这个这个没有创建这个等于等于 no, 就是 这个这个没有创建成功,那么就返回 error 嘛,如果创建成功了之后,那么就去,嗯,填充这个具体的内容嘛,它的队列 就去创建一个事件对列啊,这这个我们就不往下细看了,这个就是 free r t o s 里边创建对列的函数,你给他一些对列的大小啊、参数啊、类型啊,他就会给你出返回一个对列。一个对列的本质实际上可以认为是,呃, 就是被管理的数组,他就是先进先出吧应该是,嗯,比如说这数组我们都知道是连续的内存,而且内存的,呃,类型是一样的,比如四个字节一个格,四个字节一个格,然后我们通过另外一个指针去管理这个内存,就是加 纸,真可以加,加吗?哎,这个地址用完了之后直接加到下面,这里就释放了,你的对头就到这里了啊,他就对列本质上是这样的嘛。呃,然后如果这个对列没有创建成功,就就到,就就到这个,这里 有一个啊,就到这里就是对列没有创建成功啊,那你就去删,就把这片内存清空了, 后面很多部分都是这样的,你看如果这个是护车所,如果护车所没有创建成功,也要 去删除,那删除你,你反正你在这已经创建了一个,在堆上有了一个,嗯,你申请了一片内存嘛,你但凡有一个没有成功就把这片内存删掉,否则不就是内存泄露了吗?然后就是创建这些节点, 创建这个循环的节点,这个循环的节点就包括了去创建一个任务,创建了一个任务就有了这个一个循环的任务,这就是我们的这个 evnt, 他的,他的叫什么? 这个事件循环任务吧,读取这些事件的循环的一个任务。然后,呃,如果创建没任务,没有创建成功,也到那个去,去把那篇内存删除掉, 然后他的名字都是我们刚才传的那个配置参数里边给的那个。啊? 创建完任务之后,后面是不是就是啊?这里边其实就是创建了任务,创建了队列任务和队列匹配好了, 就做了这些事情。然后呢?嗯,如果以上的任何一个都反都没有成功,就到这里去把你申请的那片内存再 delete 掉,然后如果成功了就返回 esp, ok。
粉丝2012获赞7045

我用了两期视频的时间,大概把这个 application 全局唯一的这个单利类啊,大概的过了一下,再回顾总结一下,它大概提供了比如说应用层,什么这个启动接口,还有这些事件循环, 还有一些状态呀,交互啊,还有服务层,比如说,嗯, audio service 这些和服务器的这个交互,还有就是一些驱动类的,比如说音频, 音频驱动啊这些东西。那么我们接下来就看它是如何调用和怎么使用这些接口的,如何切换调用。那么我就得会到这个组函数里边, 在这里他先把这个 application 实体化,之后呢,他调用了他的启动接口 start, 那 么到 start 里边 start 这里, 他首先呃实体化了一个 board, 我 们到这个 board 里边,你看下面很多都是 board 里边的接口,这个 board 其实也是一个 挺有意思的东西,你看,我们首先从这里看吧,这儿应该是打开的这个,呃,这个 board 也是 get instance, 对 吧?它可能也是一个实利类,但是它是一个单利类啊,它是一个特殊的单利类,相对 application 特殊一点点。 怎么个特殊法呢?我们看他也是把呃拷贝构造,禁止拷贝、禁止赋值这些都设置了,嗯,等于 delete 嘛,后面其他外部函数是没办法复制和拷贝和赋值的。 同时呢,它又把这个构造函数它没有完全私有化。在 application 里边,我们知道它是把构造函数和细胞函数都私有化了,但是它这里放到了 protected, 这个 private 呢,是不允许外部任何函数来访问,而这个 protect 呢,是允许它的 子类就是继承它的一些子类有源类去访问的,而这个 public 是 公共都可以访问的,它把这个 board 构造函数放到这里,说明 它是允许扩展类的,它可以有多个类,但是每个类都只能有一个实例, 这就对应到我们实际上做的这个事情,比如说我们的 board, 我 们的板子可以有很多类型啊,那你就不同的类嘛,不同的类都用这个机类,其实都用这个框架,但是你具体生成的每一个类 会有一些细节上不同,所以你可以有很多板子类,但是你每一个类只能有一个实力,那你每一个板子只能有一个这样的点 c c 的 实力, 这就回到我们我们能够认知的东西了。嗯,所以我们来看一下这个板子类和具体他是怎么构造的,就比如说他我们从这个入口这里,他在哪里入口的, 对,它是在这里嘛?它不是先调用了 start 嘛?那 start 这里 instant 了一个 board, instant 了一个 board, 在 这个,呃, get instance 里边,它是有一个 create board 的 create board, 而我们上一次看到的那个 get instance, 这里的 get instance application 里边的 get instance 是 没有那个。 嗯,我们看到这里边是没有 create create。 一个心力心累的,它只有这一个力,一个实力,这个类只有这一个实力,那你生成的就只有那么一个对象,一个存储空间,存储这些东西。而而抱的不一样, 抱的是你 instant 的 时候,你要具体去构建你那个板子类的实力,它会在这里给你引进来。 这一段红定义就是,如果你在,我们在这个这个 board class name 是 从哪里获得呢?是从我们的 configure 里边,我们的 configure, 我 打开我之前设置的 configure, 我们的 menu configure, 我 们的 menu configure 这里边选择了 board type, 选择了你对应的那个 board type, 我 这个是 box 三。选择了之后,我们在 ciclist 这个 main 文件夹下的 ciclist 这里有一个地方, 有一个关于 board name 的 地方,我找一找,就是把 board type 映色成 board name, 那 我这是这样吗? 这里你看 if not board name, set board name 就是 board type, 从这里隐形的就是编辑器会先读这个 board name, 然后呢,你在你在这个 get in board, get instance 的 时候,它会去掉,掉到这里啊,就对应,比如说我在 configure 里头设置的是呃这个 box 三嘛, box 三在哪? box 三在下边 com 这里先不要对,我在 configure 里边设置的是它,那么它就通过那个接口 进到 box 三里边就来构建这么一个呃,子类扩展类。 再看,再往下看的话,我们这个子类其实它并不是直接继承于那个 board, 而是继承在 wifi board 里边,也就是说它这个 wifi board 里边先把 board 的 一部分一部分函数给实现了, 这是这是三层继承关系,那个具体的那个板子类是 board 的 孙类,而这个 这个 wifi board 才是 board 的 子类。为什么这样设计呢?因为每个版子可能都需要 wifi, 对 吧?我们所以他就把 wifi 这个放到公共里边了嘛,不管你是什么样类型的 board, 你 不都需要 wifi 吗?然后这里还有另外一个事情,就是 在这个报的这里边报的这个报的这个类里其实没有写太多东西啊,就是搭了一个框架嘛,他先把确保了每一个类,每个扩展类都是一个单例,然后呢再把这些,这里是获取, 获取 id, 设备 id 可能,嗯,会做一些算法,你看这里,根据根据 esp 三二的一些算法,可以生成每一个不同的呃类不同的板子的 id, 然后在下面就各种虚函数了,这个,这个升生成实就不说了。各种纯虚函数。什么叫纯虚函数?就是 前面是有这个,后面等于零,这种纯虚函数就必须是你你的子类必须来实现的函数,也就是在 wifi 这个类里面必须得把 这些函数去实现,而在这个里面就没有实现,那结合着它在结合着 wifi, 我 们看一下它大概都做了哪一些的 接口设置。呃,这个是吸购啊,他单独给了一个吸购,你在子类里边可以去呃个性化的去吸购你的东西,比如说 你在子类里边又申声明了一些堆上的资源,你可以去释放啊,那个编辑器会首先去去读取子类里边的吸购函数,然后再到复制里边读去默认的吸购函数, 然后这个是获得板子的 type, 而获得板子的 type, 我 们在那个 wifi 里边可能就会给你返回 wifi 值了。 这个是获取 id, 在 这里不是生成了设备 id 吗?在这里就获取了设备设备 id, 给它返回设备 id, 根据,呃做一些安全配置吧。可能这是获取。呃,背景灯,这是灯 led, 这是音频,这是温度,这是显示,这是摄像头,这是网络。这是这是网络。这是也是网络。这是那个,这是,这是不是 电源呀? get battery 电源,好像这是系统 啊这是,这是低功耗的,这是什么设备状态的 这是,呃,这里这里的一些接口就更偏向于底层了。然后呢,再往下我们就要看一下它的子类是具体怎么实现的。子类具体怎么实现?我们是不是应该到那个 wifi board 里面去看呀? 它的子类在哪里?子类 wifi board, 嗯,这是它的 wifi board 的 框架。呃,就是它把那些序函数你看都 over write 就 重载,就这样,是吧?重新给它生成了一下。嗯, 那下一个视频吧。再仔细分析这个 wifi board 里边都实现了哪些接口。

我们来分析一下这个 application 类里边的私有成员,前两个就之前也说过很多遍了,他为了保证这是一个单理类,把构造函数和吸购函数都私有化了,其他的地方不能访问他,也就他没办法再构造了。 然后呢,这第一个是一个呼市所,呼市所用通俗的语言来解释就是但有一个公用的变量或者公用的资源,呃,很多任务都想访问他,改变他, 但是呢,任务之间是会定时切换的,比如说任务一把 a a 资源变成了二,那他的时间到了,他就会切换走了,那任务二又又又要把 a 改成八,那再回到 a 回到任务一的时候,他不就, 嗯,搞错了吗?对吧?本来他想用二,结果变成了八,所以这个时候我们就需要把这些工友的资源呃给他锁起来,那任务一在用他的时候,这个期间其他想用这个资源的任务就切不进来了,那些任务就必须得在等待。嗯,然后这个呢是 主任务循环,就是把一些,嗯,主事件任务循环。一个任务不是在操作系统里边是可以永远存在的吧?他只不过是这个时间他执行,下个时段他不执行,那在他执行期间就这里边可以包含很多函数, 那包含很多函数,那个那个函数可以执行的时候是对应的某一个事件来了,他就可以执行这个函数 啊,前提是在这个这一个这些任务啊,这是主任务嘛,就 tasks 应该是有很多任务的。 那为什么不把那些函数都弄成任务呢?就是有的函数它,嗯,它是那种短小 呃,但是频率高,怎么叫端详?他并没有返回什么值得永远保存的数。呃,但是呢,他执行的频率还很高,所以他就没必要就是浪费资源在那里滚动,就需要给他放到这些主任务里边,嗯, 就就是这样的,然后这个这个这个锁,加上这个任务循环,再加上这个事件组,就呃 这三个就能保证多县城安全访问成员变量协调不同任务的执行顺序啊。这这就形成了就是这个这个整个整个的顺畅的流程。 然后这个呢是一个,呃,协议类,他用了一个智能纸,真,我们知知道那个在堆上申申请内存和释放内存都你申请了内存,你需要手动去释放吧。 嗯,我们要用了这种智能纸,真,他就呃省,呃,我就避免了,我们去释放了就可以减少一些错误吧。 呃,然后我们去看一下这个协议类,这个协议类其实里边也蕴涵了很多知识点, 比如说他这这个希购函数,他做成嗯,存续函数了,那续函数,也就是说你如果再来一个派生类,你必须去再写,他可以编辑器 会首先去看这个派生类里边的嗯,吸购函数,也就是说比如我在这个类里边会有一些堆上申请的资源,那么我派生出来的类我可能又派生嘛?可能是又跟他有有所不同,又会有别的 资源。申声明出来,如果我们用原来的细胞函数那只释放原来的资源,那些派生函数他就没办法释放他的资源,所以这里把把这个细胞函数虚化,然后在派生类里边再去具体实现,那这样的话他就会优先读取派生类里边的细胞函数, 然后这个,这个,这个是内联。我点了什么啊?这样,这是内联,这这些叫内联函数。什么叫内联函数呢?就是我们在 呃不是这个类,在其他函数的地方调用这些小的函数的时候,这个如果你写了音浪,他就建议编辑器把这个函数原封不动的搬到哪里,复制到哪里。你这种函数,而不是像原来的时候,我调用这个函数的时候,我要 指正要跳到这个函数原来的位置,这其实是有一定的开销的,相比这个函数本身,他就 return 了一个一个状态位吗? 这资源开销就小啊,这种状态。这种平式只适合这种短小的函数,你看就函数体很小,呃,他不值当的再跳回来的状态,而且他使用频率高,就用这种内敛函数。 你看这三个都是这样的,返回的是,呃服务器的采样频率,这个是呃服务器推荐的音频帧数吧?这个是绘画 id。 嗯,然后下面呢就是一些回调函数了,如果有,呃 有音频过来呢?回调什么函数?如果有 json 文件过来。回调函数,如果有,呃,这不是音频,呃,频道开 open 了,回调 close 回调,这,这就是一些回调函数了。这叫什么模式来着?观察者模式?对,设计模式里边的观察者模式。然后下面呢?这是一些,嗯,我们派生类必须要去实现的一些虚函数。嗯, 因为这是一个协议类,它叫个协议类。什么叫协议类?就是它是个大基类,又会有很多不同的协议,你具体用哪个协议,你就 在这个类的基础上派生出你你用的协议,比如说 m q t 协议,那你就用在这个基础上去生成你的那个对应的协议,它只是一个基类,它只是提供了呃,协议类的基本的框架参数算法,没有具体去实现, 嗯,就是这样的,嗯,比如说你看这些,这些都是我们这种积累,你积累的一些 public 就 可以让外界访问的。而具体的这种回调函数,哎,我们在 protect 里边, protect 里是不不能允许类之外的啊函数访问,但是允许它的积累访问,就是比如说我是 m q q t t 呃,函数,呃协议,那么就 呃对应的就生成那个协议对应的回调函数,这些回调函数不一定全部用啊,不同的,哎,不同的业务场景吧,可能会用不同的去实现不同的这些回调函数, 嗯,这些就是积累去,需要自己去拍声类需要自己去写的。 然后下面就是这是一些,嗯,刚才说的那些参数音频不是音频和服务器的产量频率啊,这个音频音频的那个采那个数据帧, 这是发生呃呃报错的状态,这个是绘画 id, 可能会维护一个 id, 比如说我现在给你对话在说,嗯,天气的事,你要持续的给我说天气的事,然后隔一段时间又又给你说心情的事,可能就切换到心情的那个状态去了。 然后这个是关于时间的,上一次我们对话的时间,根据时间这个状态可以呃切换到是退出呢?还是关机呢?还是持续呢?可能是这样的,这是一些 必要的,就是发送 test, 发送报错 是不是超时了,嗯,这是这个协议积累,可能后面我们要具体碰到它的实,实现的时候我们再去详细的解读, 然后这里呢?嗯,这个虚函数。我们说到协议类了,这,这个事件组也说完了,然后这个是是关于时间的,这是一个高精度的计数计时器,定时器吧,然后他关于时间的应该还有,还有这个你看,嗯,这是计数器,还有一个就是 时间同步状态啊,这三个就就指定了一些关于时间的东西。这个是设备状态,这个是倾听模式,这个是回声吧, a a e c 是 回声消,我消除回声的模式,然后这个是 报错,这是音频服务相关的函数。然后这是检测版本的,任务版本的啊,这是主循环的一个 主关于主事件循环的,然后这里应该是一些核心的成员函数了,嗯, 唤醒设备,这是检测这个 ota 的 版本,这是,呃,检测资源,这 uh sex 资源质的完整性,这是检测,这是什么呀? 活动状态,这是监听模式。 我们可以看到这个 application 里边,嗯,的私有类和公有类其实是有很多 重合的,重合的部分,那么如果有重合的,比如说,嗯,这里有有一个啊,这是协议,关于协议相关的,那么这就是对外暴露的接口。而下面的关于协议的 就是具体去实现的这种,嗯,凡是公有类和私有类有相同的 作用的地方就是,这是用对外暴露的,他暴露的参数啊,可能只返回简单的结果,而对内实现的,嗯,就在私有类里边,嗯,这样能保证,呃,灵活且保护了内部资源不被随意的更改切换。

我们之前看了很多的类,比如说这样的一个全区唯一的十一类,比如说那些报的类,每个类里面有很多方法,有些方法就是调用其他的类或者其他类的函数。 接下来我们再看的时候呢?呃,只要他不去调用具体的方法,我们就只看类的构造函数,因为这个类只要你声明他了,那么他首先执行的就是构造函数,是必定执行的。其实整个小智猿马都是基于事件架构驱动的,他会至少现在看到的是有两个任务,一个是这个系统, 嗯,系统默认循环的任务,还有这里边有一个主任务,呃,然后这两个任务去接收这些事件,在调用调用他相应的回调函数,所以几乎每个事件都是一个具体的类, 而这些事件又可以分分门别类嘛,所以会有鸡类、子类、孙类,然而几乎所有的类又又是在这里,我们目前看到的都是单立类, 从这个视角分析,那么几乎所有的类都会有一个事件注册函数,呃,要传输事件的基、事件 id、 任务聚丙回调函数以及用户参数。 为什么会有事件机、事件 id 呢?那是因为有些很简单的事件,它的回调函数可以做很多分支嘛,比如说衣服每个状态有一个分支分支,所以事件机和事件 id 就 可以是这些分支的条件。那除了注册,我们还需要有一个时机把事件推送到那个具体的任务里边, 所以还会有一些推送函数,还会有一些状态变换函数,还会有一些用户信息。基本上每个类应该都是由这样的逻辑组成的,还有一些特殊的类,就是初识化的类,就是,嗯,接近底层的,他会把 一些呃,那个呃基本的寄存器啊,还有通信模式再给细化一下。然后关于业务里边的类,他应该都是这些逻辑组成的,那么这样看我们就好像很清晰了一些, 然后我们再重新理一下。嗯,这个第一句话,他不是生成了全区唯一的实力吗?他就是这一个类,也只有这一个实力,因为他把各种想呃扩展的那个方式都给 delete 了,或者是 构造函数私有化了,对吧?那这句话执行,他并没有调用里边的具体的方法,他只是呃调用了他的构造函数,所以我们就看一下他的构造函数都做了什么。他的构造函数在哪里?在私有化里面,在这, 在它的构造函数里,这里先建了一个事件组,这个组呢就用来接收这个应用级任务需要接收的数事件,然后这里是关于回声的, 嗯,然后这里定义了一个定时器,这个定时器和系统底层切换调度器的定时器一定要分开来看, 本来不是有两个任务吗?哎,调度器在系统定时器的节拍下会循环执行这两个任务,但是系统的那个任务在完成初始化之后,可能就没有时间再给他进来了, 他就闲置了,基本上就等于空闲。调度器,嗯,不就不进去了,因为他没有时间,他就不就堵塞,或者说调度器不进去,而那个呃只有两个任务,那所以这个应用层的主任务就一直在运行中了,那他在运行中,他就相当于 间接的接管了调度期,对吧?一直在运行,你那如果有一个事件他一直在等待,呃,网络上传数据,那是不是就一直等大家都在这等了?所以呢?他就本身要调度, 呃,当然除了后面可能会有系统级的那种中断在除外啊。嗯,除了那些故障性的事件,那他整个就在这个任务里面循环,如果有一个但凡有个堵塞的,大家都在等了,所以他要建立一个定时器来切换这些应用层级的一些 事件,哎,这就清晰了,对吧?在这里他建立了一个定时器,相当于接管了调度器啊。然后再往下看呢,就是 开始 start 之前我们也看过了,这里回了,回了好几遍,没关系。然后这个就是,嗯, bar 的 嘛,这个 bar 的 咱们也分析过了,它实际上是建立了一个具体的你配置的那个 bar 的, 那么你建立这个具体的配置的 bar 的 里边,我们再打开一下吧。 呃,应该是打开这个,呃, 我们看到首先这层是他第一层,肯定是他这个负类,负类的构造函数会去执行负类的构造函数在呢,在这看他的构造函数做了什么? 它的构造函数,哎,做了这个 setting, setting 又是一个新的类吧?掉了一个新的类,设置了一个貌似是键值队的东西,对吧? 啊?设置 u u i d 给这个,然后设置名字,把它打印,就是就是串口输出。那我们再看一下这个 setting 是 是什么? setting? 这个 setting, 那 看 setting, 哎,他刚才调 setting 的, 他没有调 setting 的 啊,只是 setting 的 那个人,我们就去看 setting 的 构造函数呗。 啊? setting 的 构造函数是一个 n v s, 还记得 n v s 吗? n v s 是, 呃,不意识存储掉电不丢失的地方,它存放的是剑值队, 就是在我们的 flash 的 某个地方存放这种箭支队,这种掉电还要保护的一些数据,以箭支队的形式。然后你给他一个名字,比如说在 bird 里边,他给了一个 bird 的 名字啊,就是可读的,可以读的,可以放进去的,然后这个是你看, 呃,设置嘛,设置,建置对置 u u id, 就是 获取那个 u u id, 就 存放到这里,然后后面我可以读啊,它在这里并没有做什么关于事件的事情,只是进行了一些基本的配置。然后,然后不是就到了 wifi board 里边了吗?是吧? wifi board 也是在这个 common 里边? wifi 啊,在这就到了 wifi board 的 构造函数里边。你看,这是 wifi board 的 构造函数,它也是。哎,也是配置了这些键值队,然后就到了我们具体的那个 board。 具体的 board, 这个点 c c 可以 看到这个具体的 bug 就 在没有点 h 的 文件了,说明他就不再被别人引用了,是不是?嗯,他的构造函数做了什么?这是他,他的构造函数应该在后面 啊,这不是它的构造函数吗?啊,这个冒号。这是一种赋,就是它私有成员赋值的方式啊,这是自家家的一种写法。就是。嗯,可以把它这个不 boot button button 应该是 这个类里边的一个变量。哎,直接给它赋值成这个红定义了。嗯,然后它做了什么?初识化了? f c 这些初识化都都做完了啊,这是关于硬件的初识化。 好,我们就不去细看了,具体根据自己的版子再去改,然后关于这个,嗯,成绩的就看完了,我们再回到 start 里边,对吧?然后就是设置, 呃,设置设备状态,设置设备状态都做了什么呢?设置设备状态,是是是什么状态啊?是 start 是 吧? 呃,这是把状态给到他,给到这个变量,然后这是关于一些变量的变量的变化,这是输出打印的,然后这里哎,又掉了一个, 又掉了一个类,就是 device state event manager 类的一个方法就是 post state change event。 你 看这就是推送事件,推送事件这里边又有一个新的知识点,推送事件,我们就涉及到这个 事件的发布订阅的逻辑了,这里边推送事件它除了执行这个推送事件,它肯定还执行这个类的构造函数嘛。我们就肯定是在推送事件之前它肯定会有注册事件,看看它的构造函数有没有注册事件啊, 看,这不是事件注册注册到哪里?你看它首先是看 e s p event loop 这个空循环,这个就是一开始的那个事件空循环有没有,呃,有没有准备好,如果准备好了,我们就要把这个事件注册到那个空循环里,就是先在那个空循环的那个队列里先存放这个事件。存放的是什么呀?你看这就是事件机,这就是事件 id, 事件机和事件 id 是 用来做呃,回调函数的,分回调函数的分支的,呃,就是他这种事件可能会有很多状态吧,每个状态哎都会给他一个固定的,这样的搭配就是类似于你看小智 state 疑问的,就小智的状态, 事件有小智的状态改变事件状态完成事件可能这样的,如果是这样的就怎么样,怎么样,就在回调函数里边。那么, 呃,这下面呢?就是一个叫 lamb 的 函数,它是告诉了,哎,我给你回调函数可以传什么参数,然后那个之前的状态是什么?现在的状态是什么?呃,然后再收集所有所有的想要订阅这个事件的, 呃叫什么回调函数?哎,所有想要订阅这个事件的回调函数,我都通过这个收集起来,然后呢,我把这个,呃 之前的状态和现在状态传给这些回调函数,让回调函数去呃,便利所有的回调函数都把这些参数给他们,他们达到了执行,如果他只订阅这一个事件,他就可以去执行了,如果他还订阅了别的事件,那我先把参数传给他,就是这样的,凡是就是去找。呃,所有啊,这个 所有注册了 state change、 callback 的 回调函数就是这里是分开的,这里只是个事件发送,那具体呃谁来订阅?就是它只是个生产者,事件的生产者,谁来消费,那就到具体的业业业务的那个 你的文件下,你如果订阅了这个事件,那么你就写它对应的回调函数就好了。 然后这个,呃分析完了我们再看。回到,回到,回到,回到 start, 是 吧?回到这个在哪啊?在迷里边, 我总是回不去,因为我这个电脑配置比较 这里,这个这个设置就是这个类的,这个方法我们还没弄完,在这啊,这就是一个把一个事件推送过去了,对吧?把那个设备状态改变的这个事件推送到了那个默认的推送到了这个 这个循环里边。嗯,这一类的就是刚开始我们开机的时候他可能会产生的一些事件状态变化不稳定,呃,然后等我们第一次开完机之后,后面,哎,就稳定了,之后就基本上会有他里边的那个,呃,主循环来接管。 嗯,这句话。然后后面就是 board, 这又来了,又用了,引用了,这就引用了一下这个 board, 然后它的显示好像这里边 呐,它给它一个显示,这个显示这个函数,呃,应该就是在具体的, 呃,呃,我们从这打开,它是到 board 里下面那个显示里边,但是它是个虚函数嘛,它本身在这层级它是个虚函数,它是允许它的子类、孙类重写的。我们看到我们的具体的这个版子,它是重写了的,它是重写了的那个 get, 重启了那个 get displayed, 因为它用到的是它具体的那个显示嘛,你们找一下 get display, 嗯, display new, 它这之前就有一个 get display, 我 记得。 嗯,没有吗?嗯,在这里,这不是他重写了就肯定是读到的他, 然后就是 return 这个 display, 这个 display 在 这里。刚才我们看到这不 display 吗?如果你 computer 里边设置了这个,明显我是没设置的,又是表情包吗?然后就,呃,如果没有设置,就设置这个设置具体的显示的一些参数, 然后就是 led 设置,设置灯,设置, 设置状态变啊,如果你的设备状态变化了,我们的灯会有不同的显示灯,灯看就是不同的设备状态。这里没有用到世界模式,而只是读了一个全局变量 state, 这个 state 是 一个全局变量开始, 这个 state 在 哪里啊?这不,这个传传进来的这个 state 参数读的是变量形式,不是时时间订阅的模式。然后这个这个就完了,这个 application 的 再把这个删一下,我看我的名啊,然后这个, 这里就啊这里这才是设置,呃,设备状态就设置完, 设置完成了,就做了以上的那些事情吧。然后后面又他又 get 了 display 一 次,又设置了一下显示,这里是不是重复了呢?然后就是 display 就 开始显示了,显示系统的,呃,那些消息,系统消息。

继续,我们是从这里进来的,然后来到这个 start 里边,这 start 里边呢?在这之前我们都分析过了,好像还分析过好多遍,大概就是做了呃, board 的, 就是具体板子的初识化,初识化就包括了显示屏,还有 wifi, 还有一些 iphone c 啊,这些通讯模块的初识化,硬件的初识化。这里呢设置了一个板子的状态, 板子状态里边有一个就是 led, 就是 刚开始切换到 start 呃, state starting 的 话,那个灯会不停地闪烁, 就在这里就 led on state change 啊,然后 同时他可能也会给到别的别的事件里边,别的事件里边,然后再往下就是终于开始了我们这个音音频模块了。这里,嗯,首先就是这这一切,这些都是从哪到哪啊? 对,组任务之前,这里都是关于音频模块的,来简单的梳理一下吧。嗯,他就是这个东西很复杂,但是我们, 嗯只能简单的介绍一下,我们只知道知道我们后面,嗯,这个音频的逻辑是什么样子的?后面我们如果从网络上拉数据,他是怎么编辑码的,以及我们输入他是怎么编辑码之后输给网络,然后网络回来之后又编又 解码,之后输给输出啊,大概是这样的一个逻辑,然后这第一句话,这第一个就是干嘛了呢?哎,他就是获取了一些硬件的 这些配基本配置,你知道管脚啊,你看 iphone 的 jpio 管脚,还有 input 和输入和输出的,这叫什么速率,什么频率啊?输入输出的采用频率,还有这个是, 这是不是品,嗯,通道啊?这是,这是 解码芯片,这是编码芯片,这两个是编解码芯片,嗯,所以要对应自己的板子上看看是不是这两个芯片啊。这就破取了一些基本数据存放到了, 存放到了这个里边,对吧?嗯,通过 板子类里边的一些基基本数据哈。嗯,然后获取了基本数据之后呢,就把这些数据当做参数传给了这个初步化的这么一个 一个函数,我们看出式化要做什么了?出式化呢?他大概你看配了一些这些速率啊,频率啊,还有音量大小吧。应该是,这是这是输入输入, 这叫什么?参照频率吗?输入参照频率,这是什么? 这,这应该就是音频的一些相关的参数吧。 然后这个是啊,创建了这么一个,刚才找他找不到,他在这,他是一个就是相当于音频数据处理, 就这数据处理的一个模块,一会就会看到它的用处了,给它设置了它的, 哦,然后同时呢还注册了一个定时器,这个定时器用来干嘛的吗?就是用来保持低功耗的。 嗯,后面会具体给这个电视机的参数,大概是一秒钟吧,如果有多长时间没有检测到输入音频,那么他就呃,处于低功耗模式,这是一个,嗯, 检测用户输入状态,如果你输入状态切换了,就是再再讲话就切成,呃, 切成 speak, speak, speaking。 状态嘛,如果不是的话就就是,这应该是一个上层的回调函数, 具体哪里调用它,我们后面再看呗,看它肯定是,哎,这也属于低功耗一系列的哈,它在检在检测时间的同时再检测一下用户的输入状态,嗯, 然后这就相当于初始化完了,初始化完了就开启了 start, 看我们 start 里面做了什么。 start, 呃,这个,这个实践组是之前就创建好了。这实践组在哪创建?我之前看了, 但是好像忘记了它在哪里创建的这个实践组。呃,有可能是,我记得应该是在这里创建的这个实践组。 转折过去看一下, 没有。 哦,应该是在这里。这不是有一个推送吗?推送它的,它的构造函数里面就创建那么一个事件组,应该是 也没有,也没有创建这个时间轴具体在哪,我之前看到了,现在找到了,反正这之前就创建了。 好,我们再继续看它的 start 里边。 嗯,我们说有,有,这个肯定是在之前有地方创建了他,创建了他,然后给他的这些标志位清零了。为什么要清零呢?因为后面要重点监测这些标志位啊。这是,呃, 测试音频测试状态,这是唤醒词状态,这是唤,这不是唤醒词唤醒状态,这个是音频运行状态,重点监测这三个状态。来 来。呃,那不是三个状啊,三个状态称为三个事件吧,这三个事件来执行不同的逻辑,然后,然后,这不是那个地宫号定时器吗?在这里就给他赋值了。这是一秒吧?一秒钟是一秒吗? 不管多少了,反正就是这么多数。呃。然后后面创建了三个任务。创建这三个任务,这还要根据你 configure, configure 啊,有没有用这个 process 看你创建的任务,这反正都是创建了三个任务,这一个是。呃,输入任务,这是输出任务,这是编辑码任务, 这个输入我们看,每个任务我们都看一下,因为这个音频的所有逻辑都围绕这三个任务在做了。可能比如说,嗯,我们打开看看,哎,这怎么打到他的指针里边去了? 就只只能点一点过去了啊?创建这个任务我们从这儿进去,这是具体的任务,具体的任务你看它就在, 嗯,等待这个标志位,对吧?把这个标志位存在这里,然后后面呢就判断,如果是测试模式呢?就把测试模式打开。嗯,测试模式不就是就打开了 啊?这里没打开,这里还是在判断,我说怎么 false 呢?是这里, 这里就把数据推到了测试队列里边。 我感觉这个测试模式应该就是刚开机的时候或者联网之前会测试,然后这个如果是唤醒模式呢?呃,就直接把唤醒的数据推给了唤醒模块, 呃,因为他直接数据没处理就推给他,是因为我们,嗯,就是他的整个逻辑就要求唤醒词,很简单,比如说,你好,小智啊,你好给他起个名字,就这么短,所以这数据也不会太多,直接就推给模块,模块是可以匹配处理的。 然后这里呢是,嗯,运行态,呃,运行态的话它会到这个这个 audio processor 的 feed feed 里边。刚才我不是找不到这个吗?我转到定义, 我转到定义,我还是找不到,但是我记得它好像是在这里边, 它不是在这里边,它是在这里头,是吧?要不就是, 哎,在这里,对,在这个 no, 对, 在这个里边,就在这个, no。 audio processor 从哪进来的?给你捋一下给你捋一下。 我们是从那个创建任务那里来的,对吧?这里, 这里他不是创建了三个任务吗?然后我们就走到了这个,嗯,输入任务,输入任务里边有一个运行态的,说把数据推给,推给,把数据推给了 这个类,这个类我总是打不开,他就在这里边,在这个 no, no audio processor 点 h 在 这个你看到了文件夹 processors, no audio processors, 然后它这里边儿就有它的飞的逻辑, 这个 free 的 逻辑,它其实好像也没怎么处理这些数据。哦,就是把数据给到了 output callback 一个回调函数里面,输出就应该就推出去了吧?嗯,推出去可能就给到了网络 大数据,然后大数据再分析,再给到我们的编码器,然后我们再看编码器的任务吧, 然后这是这是那是那个任务,这是个输出任务,这输出任务就不等事件了,它就等了一个标志位, 等队列,是不是输出队列?你看 audio q 这个输出队列是不是空?如果输出队列不空且没有停止, 这个服务没有停止的话,那它就来输出 output data, 就 输出 play, 就 在这里输出,这是输出逻辑。然后呢?这个编辑码任务呢?就是也是等编辑码对立,编辑码有编码对立和解码对立, 这是拎扣的是编码对列, d 扣的是解码对列吧?编码对列应该是属于输入。哎,对, 是编码对列和解码对列,是怎么怎么个逻辑啊?我看看,我记得啊, 哎,输入通过等的三个事件放到那个编码队列里边,把输入的内容放到编码队列里,然后呢?编码队列编码完了之后发送, 发送到各个任务,各个他是什么状态?是测试状态就发生了,测试那里是,呃,是唤醒状态,换到是运行状态,发生到就发送了,嗯,时间先把,呃这些,然后呢?解码队列呢?是呃 把网络来的数据解码,解码之后再给到播放队列,播放队列再播放,就是这个逻辑了,理清了 所有的执行逻辑都在这三个任务里边了,去分解这三个任务就好了,其实也不用去拆解它,只后面只要调用好这几个逻辑就好了,只只要制这些标志位就好了。 然后这里就是三回调函数了, 这些回调还是设置,这不是设置这个置位吗? 这些回调函数,这些回调函数可能就是呃,在我们这个系统的 空时间循环里边,如果来了一些什么任务就会调用他,也可能是后面创建的主,你看后面紧接着就创建了一个主事件循环吗?具体哪哪里调用这些回调函数我还没有分析到哎, 反正肯定是有一个地方有些事件会调用这些回调函数,这些回调函数就给他置位,比如说网络时间啦,或者是刚开机时间呀,或者是设备状态变换了时间呀, 哎,就会占用这些互联网人质位。质位之后,哎,那个被监那监测的语音的那个输入输出任务,监测到了这些标志位,他就会对应的执行他们的逻辑。

我也知道 ai first 能解放人力,提升效率,我也想搭建我的 ai 工作流,可是好贵啊,我付不起啊。我之前用过一些,就是国内能接入 ai 的 那个服务器,尝试 用里面的一些国外的大模型。啊,真的是付不起啊,我现在好像好像只用的最多的就是这些国内的免费的大模型,大家有没有什么呃低成本的构建个人工作流的方式推荐呢?请在评论区指指点一下迷津呗。 继续我的小智猿马阅读计划。今天是 ciclist 讲解,直接来到 ciclist, 我 在的是命文件夹下的 ciclist, 之前项目级的 ciclist 我 们也解读过了,就是那个长视频,可能可能你不太爱看, 没关系,我们直接来读这个。嗯,前两行呢,设置了两个变量,这两个一个 include d i r s, 并给这两个链变量赋了初始值,这个 set 就是 给变量赋初始值的命令。第一个变量 source 存放了命文件夹下的,可以打开这里看命文件夹下的大部分点 c c 文件的路径, 大部分哦,不是全部,它后面会根据一些条件变异再再给它增增加。然后第二个变量是,就是 这个变量,它包含了一些头文件的路径,这个点呢,就表示当前文件夹下的路径,也就是命, 这个,这个命啊,命文件夹下的点 h 的 路径都有了,然后就是 display 啊,这些,这里边也没有全部包含,就是这个文件工程文件夹下的全部路径。呃,可能 可以猜想猜测,可能是有些头文件它不希不希望暴露出去。呃,如果项目内部呃调用的话,可以用相对路径的形式包含,我们上一期视频也解释过,比如说这个 led 就 没有在这里包含, 那么 led 下会有 led 点 h, 在 这里边有。用到 led 点 h 的 时候,它用的是这种相对路径的形式,相对路径就是把它的这个,呃在它的项目 的文件夹给它路径给它包含出来,就是在内部是可以这样用的,那么它没有在 semicolist 里边给它弄出来,可能是因为它不想对外暴露这个。后面这句话是将 semicol 当下 目录下,这这这叫当下目录。哎,这个好像,哎,当下当前正处于的原目录的路径就是 c make 当下目录。因为我们现在 c make 不是 在命下吗?也就是命目录下的这个 boss 版 boss 在 这儿吗? 下的 common 的 点 c c 这个路径赋给这个赋给这个变量, 呃,搜集这些点 c c 赋给这个变量。后面这句话呢,就是将这个变量刚才搜集的这些,嗯, board 下面的 common 的 点 c c 赋给和那个这个 sort 合并,然后下面这个就是把这个路径与这个这个合并。那为什么要这么做呢? 我在阅读的时候我就有两个问题,第一个问题,干嘛多此一举呢?直接给了都附给,这样附给第一个变量不好吗?第二个问题就是这两个变量存放的都是路径,对吧?那路径的作用是什么呢? 第一个问题,之所以这样,是因为板子,像板子这样的设备包的,这样的设备是会经常变的,里面的原文件可能会有增减, 这种方式呢,更灵活。后面你要去修像这里我们都得把所有的点 c c 都像这样明文的给它罗列出来,如果你要有增有减的话,是不是很麻烦呢? 所以在这里呢,他就不需要那样罗列你,你增减还是这句话就行了啊,这样就更灵活。接下来这句话是获取当前构建中包含的所有列表,所有组件列表给到这个变量。 呃,在读这里的时候我有一个疑问,就是这个命令是获取属性值的,对吧? property 获取属性值的,那么我们可以通过这个命令获取到什么样的属性值?那么它说的当前构建的组建 是指什么?是指这个工程下的组建吗?呃,然后那他不是把这个组建返回给他了吗?他返回的是什么?用来干什么? ai first 是 吧?我也问了 ai 这四个问题,他给到了我答案,得到了这样的解释,就是我们可以通过这个命令获取的属性值,包括这些, 以及通过这些属性值能得到的返回值。 有兴趣的同学可以试一下。当前构建的组建主要来源于哪?哪些方面?有四个方面,就是这些,主要包括命,肯定是主目录,肯定是一个一算一类组建了。然后就是,呃,这个,呃,这个 组建我们的项目里边的 components, 还有就是 idf 组建,还有一类就是我们自定义的额外的组建。嗯,之前的视频也说过了,就是自己在电脑找个地方新建一个文件夹,然后在上一集的 cmake 里边 关联一下这个路径就好了。那就有了所有这个,这个,这这,这个就可以包含到所有的这些组建列表了。那么它返回的是什么?刚才那幅图也告诉我了,它返回的是一串儿字符串数组, 这个字符串数组可以后面用来做一些字母名称上的逻辑判断,比如,呃,一幅 wifi 是 不是包含在这个 这个里边啊?你,你就可以去通过这个逻辑判断去做一些事情了。当然了,呃,也有对,这个也也有对应的命令,能让这些字母串在转换成真正的 路径格式,让构建系统去识别。现在这个不就是字母串吗?他也有对应的命令,能把那些字母串转换成路径,让构建系统去识别,去给他呃,去构建。 cigma list 里边大部分都是来解决路径的问题,它因为去,嗯,针对的是后台的翻译系统嘛,啊,只要给它路径,呃,让翻译系统去翻译就好了,我们这里就决定我们要翻译哪些东西嘛。 然后接下来是这个函数,这个函数,这函数,呃主要是用来动态查找组建的函数。动态查找组建什么意思呢?你可以在嗯,你,你可以用,就是这是一个函数名吗?你可以给他输入你想查找的组建,比如说 display 吧,然后呢,你把这个是这个函数,这个是输出一,输出二,这个输出就是输出的是,嗯,组建名称,这个输出的是组建的路径。那么这个名称和这个名称有什么不一样呢?这个名称我们输入的是组建的正则表达式, 可以这样输入啊,那你输出呢?就可以是准确一点,就比如说我,我知道我要加了一个显示设备,他是 display, 但是我不知道具体 display 前前缀后缀是什么。嗯, 然后我们能通过这个函数找到具具具体的 display 啊,就是这样的。好了,时间由于时间关系,下一个视频再继续解读。

转一圈 转的更六了,有人要点赞吗?之前在网上刷到这个大神用藤丝做的一个桌面机器人,感觉非常好, 想着能不能把这个小智整合到里边,然后从网上买的铜丝还没到,就先用三 d 打印建模做了一个外壳,然后修改程序先做了一个,就现在这个样子,右转 右转完成,主人要来个方向大挑战吗?左转左转到位,主人指挥的真准,跳个舞, 舞跳完了,主人得打个分呗。一百分, 主人太会夸了,我都要飘起来了。一分?什么一百分?哎呦,新朋友来了,这饭我可以收好,别被抢了。生生气的表情, 哼,不许再欺负我了,不然我就跳舞砸你伤心的表情。 我错了嘛,别哭了,我给你变个魔术好不好, 还有什么表情?来来来,看我变个脸,惊恐、呆滞、发疯,狂笑,全都有, 调皮。嘿嘿,主人说对了,我可是最调皮的机器人。 调皮就是不听话,我不要你了,小智,别别别,我错了我错了,马上乖,求别丢下我,瞪大眼, 我,我这就改正。