关于该课程说明
1)本课程目标
通过web浏览器访问服务器,实现登录、注册、数据库操作、远程操控硬件、采集环境信息、远程监控、拍照、图片显示等功能。
将单片机、linux、html、摄像头、数据库等知识点融入到一个项目中。
2)什么群体适合学习该课程?
3)课程特色
yikouepng
密 码:1
卸载该程序,然后重新用管理员权限安装一遍,包括破解软件也要用管理权限打开
设置vmware usb设备为3.0
编译cgi程序,然后将*.cgi文件拷贝到/www/cgi-bin下
touch /app
sudo chmod 777 /app
首先确认协调器和endpoint烧写的文件是否正确,
先用 串口工具 xcom 查看串口有没有数据上传
串口必须是协调器
还有就是串口不要被vmware抓取
如果串口有数据,那么让vmware抓取协调器的串口,然后运行主控程序
主控程序会实时打印接收到的环境数据
将cgi工程的文件全部编译一遍,然后拷贝*.cgi文件拷贝到/www/cgi-bin下
主要原因是误码了,优化该功能需要增加字段,通过7e头和尾将信令包含进来,
同时数据帧内增加校验码
1) 先用cheese测试摄像头,如果不可以用,
检查vmware是否抓取设备摄像头
2) 检查是否有video1设备
该项目基于CC2530+Linux+web,实现远程数据采集、远程设备控制和视频监控等功能
基于TI公司的CC2530 soc实现zigbee组网
协调器采集终端节点环境信息数据,并通过电日发送给上位机主控程序,同时协调器接收上位机通过串口下发的命令,
通过zigbee网络发送给终端节点
涉及到外设包括:LED,key,继电器,beep,人体红外,光敏,烟雾传感器,LCD屏幕
主控程序通过消息队列接收boa服务器fork的cgi程序洗发的设备操作命令,然后分派给串口发送线程和摄像头操作线程,
主控程序的串口接收线程负责从串口解析出有效环境数据并发送给共享内存刷新线程,后者解析出来环境数据然后将最终数据刷新到共享内存
涉及技术:
文件IO、多线程编程、多进程编程、信号量、共享内存、消息队列、互斥锁、条件变量、链表操作、视频流服务器移植、boa服务器移植、
通过一款请谅解嵌入式web server提供网页浏览服务,
实现账号登录、注册,显示环境信息,视频监控,历史图片浏览,远程控制设备,账号信息设置等功能。
涉及技术:
数据库sqlite、html、css、javascript、cgi程序编写、linux目录操作
采集的数据如何保证正确上传给上位机?
0 1 2 3 4
|----|----|----|----|----|
0x7e dev op crc 0x7e
增加编码难度
https://mp.weixin.qq.com/s?__biz=MzUxMjEyNDgyNw==&mid=2247498985&idx=1&sn=93241e573ec662b826696535f02f32d8&chksm=f96b8e1dce1c070b6216cea54e8b459a28fe3aea98cd7d8e2337ae208e1be02920078808fcd1&scene=21&token=1832549024&lang=zh_CN#wechat_redirect
指定数据上传的规则,定时上传,休眠省电
找一个支持高并发的web服务器
数据速率 H.265
布局在单片机上
可以的,但是有的功能(数据库,摄像头)要check一下
介绍项目应该是问的最多的,在介绍时首先要有条理,让别人听得懂,并且讲出在这个项目中你担任了什么角色,做了什么,用了哪些传感器实现了哪些功能,数据是怎样传输的。
示例:
在无线传感网这个项目中,我担任项目负责人的角色。我首先对该项目进行了规划,把该项目分为了三部分:下位机、上位机、web端。其中下位机的硬件部分,由我带领我的师弟来完成,下位机的软件部分,以及上位机的主控程序由我来完成,web端由我同门来完成。
在项目规划好后,我对传感器进行了选型。根据要实现的功能(具体介绍…)我选择了XXXXXX。
终端结点搭载的传感器采集到的数据通过zigbee传送到协调器,协调器通过串口传送到上位机。在上位机,我实现了一个主进程,其中包括五个线程,分为数据上传与命令下发。终端结点采集到的环境数据上传到web端进行显示,web端也可以下发命令到终端节点。
五个线程分别是:(这里可以简略说下,如果面试官让详细介绍五个线程可以仿照以下进行介绍)
① 数据读取线程通过串口读取数据,送入链表中,并给数据上传线程发送条件变量信号。
② 数据上传线程收到信号后从链表中读取数据送入共享内存中,共享内存中数据供web端读取。
③ 在web端与上位机端还有一个消息队列,上位机下发命令送入消息队列中,由命令下发线程从消息队列中取出数据进行甄别,如果是下位机的命令则送入链表中并给下位机线程发送条件变量信号。
④ 下位机线程收到信号后取出命令,并通过串口送入协调器,协调器通过zigbee网络发送到终端结点并执行。
⑤ 在上位机端还有一个摄像头监控,若是摄像头的拍照命令,则由摄像头线程直接取走拍照命令并执行。
在详细介绍后,面试官会根据项目介绍的内容进行提问。主要在以下几个方面:
你能说一下串口的协议吗
波特率:事先确定好波特率,一般为9600,19200,115200等,代表每秒传输这么多个比特位数(bit)
起始位: 先发出一个逻辑0的信号,表示传输数据的开始。
数据位: 可以是5-8位,一般是8位 小端传输
校验位: 数据加上这一位后,使得1的位数为奇数或者偶数,以此来校验数据传送的正确性。
停止位: 字符数据结束标志 变为1。 可以是1位、1.5位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。
空闲位: 处于逻辑1状态,表示当前线路上没有资料传送。
2.数据发送
比如将ox41 即 01000001 发送给PC机
双方约定好波特了(每一位占据的时间)
ARM将原本的高电平拉低,保持1bit时间,表示开始。PC机在低电平开始处计时。
ARM根据数据依次驱动TxD引脚电平发送数据,同时PC依次读取RxD引脚电平获得数据。
数据发送完后发送一个停止位。
1bit 开始位
115200,8n1 代表 波特了115200 8位数据为,没有校验位,1位停止位。
即发送每一个字节数据需要10位。每秒发送115200位,即11520个字节数据
zigbee能耗低、有大规模的组网能力。在可靠性方面。物理层采用了扩频技术,能够在一定程度上抵抗干扰,MAC应用层(APS部分)有应答重传功能。当ZigBee网络受到外界干扰,无法正常工作时,整个网络可以动态的切换到另一个工作信道上。
下位机CC2530部分的开发主要用到IAR,生成编译后的文件是二进制文件.bin。
(如果写的硬件部分是自己做的,可能会问到这个问题)
结合应用场景、需要实现的功能、成本等,言之有理即可
(视频监控具体是怎么做的?用的哪些模块?)
(你对这个mjpg_stream有多少了解?可不可以介绍一下这个代码?它的功能是什么?主要是用来做什么的?在你这个项目中起到了一个什么作用?)
摄像头这里主要是移植了mjpg-streamer这样一个视频图像开源库。它可以通过HTTP的方式访问linux上面的兼容摄像头,从而做到远程视频传输的效果。并且从摄像头上采集图像,把他们以流的形式通过IP的网络传输到拥有浏览器的移动设备。
可以获取静态图片、视频流,并且自带了图片定时抓拍的功能。
参数中可以设置帧数,默认30帧、指定视频大小,如320×240,指定画质,默认80,以及输出参数,如端口、网页目录,并且可以设置密码。
自带的功能只能实现单拍,我给实现了连拍
就按默认的30帧,分辨率根据网速进行调试的 最后是320×240
在项目中用的比较多的有哪几种?能简单介绍一下怎么用到吗?
有名管道:fifo 全双工 可在任意进程间使用
无名管道:pipe 半双工、只能在具有亲缘关系的进程间使用
信号:唯一的异步通信方式
信号量:配合共享内存使用,实现同步和互斥
共享内存:效率最高(之间访问内存),需要同步与互斥机制。
消息队列:常用于cs模型中,按消息类型访问,可有优先级。
套接字:用于不同机器间的进程通信。
在项目中主要用到共享内存和消息队列,主要用于上位机和web端的交互。
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何的数据拷贝。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间,进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率,由于多个进程共享一段内存,因此也需要依靠某种互斥机制,如互斥锁和信号量。
使用步骤:
消息队列是IPC的一种,由消息队列ID来唯一标识。消息队列就是一个消息的列表,用户可以在消息队列中添加消息、读取消息。消息队列可以按照类型来发送接收消息
12. 两个进程同时对共享内存读写的时候会不会出现一些问题?
使用信号量进行保护
13.linux创建线程的方法是什么?
pthread_create()
pthread_join() 等待回收
互斥锁、自旋锁、读写锁、条件锁(条件变量)
读写锁分为读锁和写锁。
在一个线程读的时候,其它线程也可以读,是共享的。
在一个线程写的时候,其它线程既不能读也不能写是互斥的。
互斥锁是互斥的,仅有一个线程可以占用,其它不能获得锁的线程将会睡眠等待。
(开放性问题,言之有理即可,准备1-2个)
例:
主要在数据上传部分,要实现数据能上传上去,并且要保证数据正确。
首先,XXXXX
其次,XXXXX
(开放性问题,言之有理即可,准备1-2个)
结合项目应用场景进行准备
结合实际应用场景进行回答,如智能家居、农业园等
结合应用场景,如:当温度计温度过高时自动下发命令开启风扇等降温系统xxxxxxxx
(开放性问题,言之有理即可)
(1) 引用不可以为空,指针可以为空
(2) 引用不可以改变指向,指针可以改变指向,指向其它对象
(3) 引用的大小是所指向对象的大小,因此引用只是一个别名,指针是指针本身的大小,四个字节。
(4) 指针使用时要加*,引用可以直接使用
(5)程序为指针分配内存区域,引用不分配内存区域。
悬空指针:一个指针的指向对象已被删除,就成了悬空指针
野指针:指向不可用内存的指针(没有初始化的指针)。
空指针:没有存储任何的内存地址的指针是空指针,指向NULL
许多计算机系统对基本类型数据在内存中存放的位置有限制,要求数据的起始地址需是k(对齐数)的倍数,这样可以提升读取数据的速度。
平台原因:不是所有的硬件平台能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐,是因为为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要访问一次
结构体占用的空间为最大对齐数的整数倍。
(1) sizeof()既是关键字也是运算符,strelen()是函数。
(2) sizeof()可以用类型作为参数,也可以是变量名,如果是类型必须加括号,strlen()只能用char*作为参数,而且必须以“\0”结尾。
(3) sizeof()是在编译期的时候计算的,可以通过sizeof(x)来定义数组的维数,strlen()是在运行期计算的,用来计算字符串的实际长度,不是类型占内存的大小。
译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
(1) volatile
(2) static
(3) extern
(4) const
(5) sizeof
(6) typedef
(7) struct
(8) register
(9) union
(1)#define语句只是一个简单的替换,define常量的生命周期止于编译器,不分配内存空间,它存在于程序的代码段。而const常量存在于程序的数据段,并在堆栈中分配了空间,可以被调用、传递。
(2)主要区别: define是没有数据类型的,const修饰的话是有明确类型的,编译器可以对const常量进行类型安全检查,而define不行。
typedef和define都是替一个对象取别名,以此来增强程序的可读性。
(1) 原理不同
define是预处理指令,在预处理时进行简单的字符串替换,不做正确性检查,只要在编译已被展开的源程序时,才会发现可能的错误并报错。
typedef是关键字,它在编译时处理,具有类型检查的功能。
(2) 功能不同
typedef用来定义类型的别名,不仅包含内部类型还包括自定义类型,可以起到使类型易于记忆的功能。
define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
(3) 作用域不同
define没有限定的作用域,只要之前预定义过的宏以后程序中都可以使用。
typedef有自己的作用域。
(4) 对指针操作不同
修饰指针类型时作用不同。define进行宏替换是不含任何意义的替换,仅仅为字符串替换,而用typedef是一种数据类型起的别名,带有一定含义。
(1) 代码段 通常用来存放程序执行代码的一段区域,通常只读
(2) 数据段(常量区) 通常用来存放程序中已初始化的全局变量和初始化的非0静态变量,属于静态内存分配
(3) bss段(全局区) 未初始化的全局变量和未初始化的静态变量或初始化为0的静态变量,属于静态内存分配
(4) 堆 用来存放进程中被动态分配的内存段
(5) 栈 用来存放程序临时创建的变量
malloc
malloc需要计算申请内存的大小,可申请的最大内存的具体数值受到操作系统版本、程序本身的大小、用到的动态/共享库数量、大小,程序栈数量、大小的影响。最大可申请的大小大约在2G
申请后不释放容易造成内存泄漏,最终导致系统崩溃。
如果重复释放容易把其它程序占用的内存空间给释放掉。
所有成员长度之和,再考虑字节对齐,最后的总和。
(1)野指针是指向不可用内存的指针,当指针被创建时,指针不可能自动指向NULL,这时,默认值是随机的,此时的指针称为野指针。
(2)当指针指向的内存被free或delete释放掉时,如果没有把指针设置为NULL,则会产生野指针,因为释放掉的仅仅是指针指向的内存,并没有把指针本身释放掉。
(3)指针操作超越了变量作用的范围。
(1) 对指针进行初始化
(2) 指针用完后释放内存,并将指针指向NULL
(1)联合体中所有成员共用一块地址空间 ,即联合体只存放了一个被选中的成员,而结构体中所有成员占用空间是累加的,所有成员都存在。结构体内存空间大小等于所有成员长度之和再加上对齐对齐部分,而联合体占用空间大小等于最长成员的长度在加上字节对齐部分。
(2)对联合体不同成员赋值,将会对其它成员重写,原本成员的值就不存在了,而对结构体不同成员赋值互不影响。
(1)C++调用C
加上 extern “C” 会指示编译器这部分代码按照C语言进行编译
(1)指针定义后要初始化,没有指向时指向NULL。
(2)指针赋值时要保证类型匹配
(3)两个指针最好不用指向同一块内存,一个指针释放会导致另外一个指针成为悬空指针。
(4)释放指针指向的内存后要将该指针指向NULL
(5)指针分配成功后才可以使用,要注意分配后的大小,不要越界。