VNC Viewer
连接树莓派4B
远程桌面提示错误“Cannot currently show the dekstop
”,如下图: 继续阅读使用VNC Viewer连接树莓派4B远程桌面提示错误“Cannot currently show the dekstop”VNC Viewer
连接树莓派4B
远程桌面提示错误“Cannot currently show the dekstop
”,如下图: 继续阅读使用VNC Viewer连接树莓派4B远程桌面提示错误“Cannot currently show the dekstop”If you're building software for the Raspberry Pi (like I sometimes do), it can be a pain to have to constantly keep Pi hardware around and spotting Pi-specific problems can be difficult until too late.
One option (and the one I most like) is to emulate a Raspberry Pi locally before ever hitting the device. Why?
Given I'm next-to-useless at Python, that last one is pretty important as it allows me to install every Python debugging and testing tool known to man on my virtual Pi while my end-product hardware stays comparatively pristine.
First, you'll need a few prerequisites:
qemu-system-arm
)You can find all the packages for your chosen platform on the QEMU website and is installable across Linux, macOS and even Windows.
Simply download the copy of Raspbian you need from the official site. Personally, I used the 2018-11-13
version of Raspbian Lite, since I don't need an X server.
Since the standard RPi kernel can't be booted out of the box on QEMU, we'll need a custom kernel. We'll cover that in the next step.
First, you'll need to download a kernel. Personally, I (along with most people) use the dhruvvyas90/qemu-rpi-kernel repository's kernels. Either clone the repo:
1 |
$ git clone https://github.com/dhruvvyas90/qemu-rpi-kernel.git |
or download a kernel directly:
1 |
$ wget https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu-4.4.34-jessie |
or download a snapshot from my website directly:
1 |
$ wget https://www.mobibrw.com/wp-content/uploads/2019/03/qemu-rpi-kernel.zip |
For the rest of these steps I'm going to be using the kernel-qemu-4.4.34-jessie
kernel, so update the commands as needed if you're using another version.
This step is optional, but recommended
When you download the Raspbian image it will be in the raw format, a plain disk image (generally with an .img
extension).
A more efficient option is to convert this to a qcow2 image first. Use the qemu-img
command to do this:
1 |
$ qemu-img convert -f raw -O qcow2 2018-11-13-raspbian-stretch-lite.img raspbian-stretch-lite.qcow |
Now we can also easily expand the image:
1 |
$ qemu-img resize raspbian-stretch-lite.qcow +6G |
You can check on your image using the
qemu-img info
command
You've got everything you need now: a kernel, a disk image, and QEMU!
Actually running the virtual Pi is done using the qemu-system-arm
command and it can be quite complicated. The full command is this (don't worry it's explained below):
1 2 3 4 5 6 7 8 9 |
$ sudo qemu-system-arm \ -kernel ./kernel-qemu-4.4.34-jessie \ -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \ -hda raspbian-stretch-lite.qcow \ -cpu arm1176 -m 256 \ -M versatilepb \ -no-reboot \ -serial stdio \ -net nic -net user |
如果需要指定上网方式的话,执行如下命令:
1 2 3 4 5 6 7 8 9 10 |
$ sudo qemu-system-arm \ -kernel ./kernel-qemu-4.4.34-jessie \ -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \ -hda raspbian-stretch-lite.qcow \ -cpu arm1176 -m 256 \ -M versatilepb \ -no-reboot \ -serial stdio \ -net nic -net user \ -net tap,ifname=vnet0,script=no,downscript=no |
So, in order:
sudo qemu-system-arm
: you need to run QEMU as root
-kernel
: this is the path to the QEMU kernel we downloaded in the previous step-append
: here we are providing the boot args direct to the kernel, telling it where to find it's root filesytem and what type it is-hda
: here we're attaching the disk image itself-cpu
/-m
: this sets the CPU type and RAM limit to match a Raspberry Pi-M
: this sets the machine we are emulating. versatilepb
is the 'ARM Versatile/PB' machine-no-reboot
: just tells QEMU to exit rather than rebooting the machine-serial
: redirects the machine's virtual serial port to our host's stdio-net
: this configures the machine's network stack to attach a NIC, use the user-mode stack, connect the host's vnet0
TAP device to the new NIC and don't use config scripts.If it's all gone well, you should now have a QEMU window pop up and you should see the familiar Raspberry Pi boot screen show up.
Now, go get yourself a drink to celebrate, because it might take a little while.
Now, that's all well and good, but without networking, we may as well be back on hardware. When the machine started, it will have attached a NIC and connected it to the host's vnet0
TAP device. If we configure that device with an IP and add it to a bridge on our host, you should be able to reliably access it like any other virtual machine.
This will vary by host, but on my Fedora machine, for example, there is a pre-configured virbr0
bridge interface with an address in the 192.168.122.0/24
space:
1 2 3 |
virbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 ether 00:00:00:1e:77:43 txqueuelen 1000 (Ethernet) |
I'm going to use this bridge and just pick a static address for my Pi: 192.168.122.200
Reusing an existing (pre-configured) bridge means you won't need to sort your own routing
NOTE: I'm assuming Stretch here.
Open /etc/dhcpcd.conf
in your new virtual Pi and configure the eth0
interface with a static address in your bridge's subnet. For example, for my bridge:
1 2 3 4 5 |
# in /etc/dhcpcd.conf interface eth0 static ip_address=192.168.122.200/24 static routers=192.168.122.254 static domain_name_servers=8.8.8.8 8.8.4.4 |
You may need to reboot for this to take effect
Finally, add the machine's TAP interface to your chosen bridge with the brctl
command:
1 |
$ sudo brctl addif virbr0 vnet0 |
Now, on your host, you should be able to ping 192.168.122.200
(or your Pi's address).
Now, in your machine, you can run sudo raspi-config
and enable the SSH server (in the "Interfacing Options" menu at time of writing).
Make sure you change the password from default while you're there!
Finally, on your host, run ssh-copy-id pi@192.168.122.200
to copy your SSH key into the Pi's pi
user and you can now SSH directly into your Pi without a password prompt.
usbip
的目的是为了开发一个在局域网内共享的USB设备,也就是说你可以直接访问局域网内其他计算机的USB
设备。
下面我们看一下如何在ubuntu 16.04
跟树莓派(raspberry pi
)以及树莓派之间实现USB
设备在局域网的共享。
树莓派端配置为服务端,我们把USB
设备接入到树莓派上,服务端的树莓派上执行如下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 升级内核以及附属驱动模块 $ sudo rpi-update $ sudo apt-get install usbip # 加载驱动模块 $ sudo modprobe usbip-core $ sudo modprobe vhci-hcd $ sudo modprobe usbip_host # 打开监听服务接收来自其他机器的请求 $ sudo usbipd -D # 列出本机的USB设备列表 $ sudo usbip list -l # 通知底层驱动,绑定设备,远端可看到我们绑定过的设备,我们的设备ID为1-1.3 $ sudo usbip --debug bind -b 1-1.3 |
客户端的树莓派或者ubuntu 16.04
,执行如下操作查看以及操作服务端的设备。
树莓派
1 2 3 4 5 6 7 8 |
$ sudo apt-get install usbip # 'sudo usbip version' usbip (usbip-utils 2.0) raspberry pi # 服务器IP地址 192.168.1.201 ,列出服务端的设备列表 $ sudo usbip list -r 192.168.1.201 --debug # 服务器IP地址 192.168.1.201 ,设备ID 1-1.3 $ sudo usbip attach -r 192.168.1.201 -b 1-1.3 |
ubuntu 16.04
1 2 3 4 5 6 7 8 |
$ sudo apt-get install linux-tools-`uname -r` # 'sudo /usr/lib/linux-tools/`uname -r`/usbip version' usbip (usbip-utils 2.0) raspberry pi # 服务器IP地址 192.168.1.201 ,列出服务端的设备列表 $ sudo /usr/lib/linux-tools/`uname -r`/usbip list -r 192.168.1.201 --debug # 服务器IP地址 192.168.1.201 ,设备ID 1-1.3 $ sudo /usr/lib/linux-tools/`uname -r`/usbip attach -r 192.168.1.201 -b 1-1.3 |
如果出错信息如下:
1 2 3 4 5 |
pi@raspberrypi:~ $ sudo usbip port usbip: error: failed to open /usr/share/hwdata//usb.ids libusbip: error: udev_device_new_from_subsystem_sysname failed usbip: error: open vhci_driver usbip: error: list imported devices |
出错的原因为内核驱动没有正确加载,解决方法为:
1 2 3 4 |
# 加载驱动模块 $ sudo modprobe usbip-core $ sudo modprobe vhci-hcd |
如果出错信息如下(比如ubuntu 16.04
):
1 2 3 4 5 6 7 8 |
$ sudo usbip --list 192.168.1.201 --debug usbip dbg: usbip_network.c: 221 (tcp_connect ) trying 192.168.1.201 port 3240 usbip dbg: usbip_network.c: 241 (tcp_connect ) connected to 192.168.1.201:3240 - 192.168.1.201 usbip err: usbip_network.c: 119 (usbip_recv_op_common) recv op_common, -1 usbip err: vhci_attach.c: 202 (query_exported_devices) recv op_common usbip err: vhci_attach.c: 417 (show_exported_devices) query |
此时查看软件版本,可以看到如下:
1 2 |
$ sudo usbip -v usbip 0.1.7 ($Id: vhci_attach.c 42 2007-09-07 12:07:51Z hirofuchi $) |
这个原因是由于软件安装的是很早的一个版本,无法跟现在最新的版本进行通信。
这个驱动很早就以及整合进入了Linux
内核,控制软件也是内核提供。使用如下命令安装跟当前内核匹配的版本:
1 2 3 |
$ sudo apt-get install linux-tools-`uname -r` $ sudo /usr/lib/linux-tools/`uname -r`/usbip list -r 192.168.1.201 --debug |
如果出错信息如下:
1 2 3 4 5 |
$ sudo usbip --debug bind -b 1-1.3 usbip: debug: usbip.c:141:[run_command] running command: `bind' usbip: debug: sysfs_utils.c:18:[write_sysfs_attribute] error opening attribute /sys/bus/usb/drivers/usbip-host/match_busid usbip: debug: utils.c:50:[modify_match_busid] failed to write match_busid: No such file or directory usbip: error: unable to bind device on 1-1.3 |
原因为usbip_host.ko
这个内核驱动没有加载,使用如下命令加载驱动:
1 |
$ sudo modprobe usbip_host |
最近在参照 树莓派实时系统下脚本语言的选择(应当使用Lua而不是Python) 调用调试lua-zmq
的时候,发现使用PAIR
模式进行线程之间通信,长时间运行后会出现死锁的情况。
当我们需要跟踪问题的时候,使用apt-get
安装的版本缺乏必要的调试信息。
我们可以手工安装调试信息包,如下:
1 2 3 4 5 6 7 8 9 10 |
$ sudo apt install libzmq-dbg # 系统默认的lua 5.1的符号信息使用如下命令安装 $ sudo apt-get install liblua5.1-0-dbg # libreadline提供了多个版本(5,6,7),根据需要下载对应的版本 $ sudo apt-get install libreadline6-dbg # sudo apt-get install search libtinfo* 根据返回的结果安装 $ sudo apt-get install libtinfo5-dbg |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 打开源代码的源 $ sudo sed -i "s/^#deb-src/deb-src/g" /etc/apt/sources.list # 安装必要的编译工具 $ sudo apt-get install build-essential devscripts lintian $ cd ~ # 下载源代码,此处不可sudo,否则后续操作会出现权限问题 $ apt-get source libzmq-dev $ cd zeromq*/ # 安装依赖项 $ sudo apt-get install libpgm-dev $ sudo apt-get install uuid-dev $ debuild -us -uc -b $ cd .. $ sudo dpkg -i libzmq-dev*.deb $ sudo dpkg -i libzmq-dbg*.deb $ sudo ldconfig |
Debian
安装包来进行的处理,然而对于luarocks
安装的插件来说,默认luarocks
是不能编译调试版本的,此时就需要我们手工编译了。我们以lua-zmq
为例子,参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ sudo apt-get -y install cmake $ git clone git://github.com/Neopallium/lua-zmq.git $ git checkout v1.1 $ cd lua-zmq ; mkdir build ; cd build $ cmake -DCMAKE_BUILD_TYPE=Debug .. $ make $ sudo make install $ sudo ldconfig |
在树莓派实时系统下脚本语言的选择(应当使用Lua而不是Python)中,我们没有使用rpi-gpio,而是使用了lua-periphery来解决Lua
语言下操作树莓派GPIO
的问题。
当时选择lua-periphery的原因在于rpi-gpio在Raspberry PI Zero W
中使用的时候会崩溃。这个原因是在于cpuinfo.c这个文件中缺少对于BCM2835
这颗新的CPU
的判断,只判断了BCM2708
(估计写这个库的时候,只有BCM2708
)。导致RPi_GPIO_Lua_module.c在初始化GPIO
的时候抛出了异常。这个已经有人提交了代码合并请求,估计很快会修复。
但是在lua-periphery中,没有对于GPIO
进行重新映射,导致跟rpi-gpio以及树莓派自带的Python
库在设置GPIO
的时候,端口号对应不一致。比如,在Python
中设置GPIO 22
,执行命令观察ls /sys/class/gpio/
,会发现系统创建的是GPIO 25
这个对应关系就是通过查表获取的。如下图:
继续阅读解决Raspberry PI Zero W中Lua使用lua-periphery与Python中设置的GPIO端口不一致的问题
最近在树莓派上需要安装libgtk2.0-dev
,执行如下命令:
1 |
$ sudo apt-get install libgtk2.0-dev |
出错信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Reading package lists... Done Building dependency tree Reading state information... Done Some packages could not be installed. This may mean that you have requested an impossible situation or if you are using the unstable distribution that some required packages have not yet been created or been moved out of Incoming. The following information may help to resolve the situation: The following packages have unmet dependencies: libgtk2.0-dev : Depends: libpango1.0-dev (>= 1.20) but it is not going to be installed Depends: libcairo2-dev (>= 1.6.4-6.1) but it is not going to be installed E: Unable to correct problems, you have held broken packages. |
根据出错信息,明显是软件源中出现了安装包缺失的问题。这个现象是不应该出现的。网上查询了不少地方,最终找到解决方法:
1 |
$ sudo vim /etc/apt/sources.list.d/raspi.list |
可以看到如下内容:
1 2 3 |
#deb http://archive.raspberrypi.org/debian/ stretch main ui staging # Uncomment line below then 'apt-get update' to enable 'apt-get source' #deb-src http://archive.raspberrypi.org/debian/ stretch main ui |
默认里面的内容都是被注释掉的,我们需要做的就是把这个源打开即可。
也可以直接执行如下命令来开启:
1 2 3 4 |
#先备份配置文件 $ sudo cp /etc/apt/sources.list.d/raspi.list /etc/apt/sources.list.d/raspi.list.bak $ sudo sed -i "s/^\#deb/deb/g" /etc/apt/sources.list.d/raspi.list |
最新在使用的Raspberry Pi Zero W
V1.3
在使用目前(2018.09.26
)的系统的时候发现无法正常输出内容到屏幕上面,屏幕一直黑屏无信号。
原因在于Raspberry Pi Zero W
在启动的时候没有正确检测到屏幕信号,导致没有正常输出。
解决方法是打开启动配置文件/boot/config.txt
, 找到如下内容:
1 2 3 4 5 6 |
.................. # uncomment if hdmi display is not detected and composite is being output #hdmi_force_hotplug=1 ................. |
1 2 |
# uncomment if hdmi display is not detected and composite is being output hdmi_force_hotplug=1 |
最近在使用树莓派与其他设备通过SPI
接口进行通信,使用一个GPIO
管脚触发读取数据的信号,为了简化开发,使用了Python
。
在实际运行过程中,发现当长时间运行的是,会出现中断管脚信号丢失的情况,在参考 Ubuntu 16.04 (x64)下从源代码为Raspberry Pi Zero W编译实时内核 更换为实时内核之后,短时间运行已经可以正常,但是在十几个小时之后,依然出现了中断丢失的现象。
这个现象初步评估为Python
的GC
动作时间过长导致的中断信号丢失。Python
本身并不是为实时系统设计的,因此在GC
进行垃圾回收的时候,是没有实时性的考虑的,因此在严格要求实时性的系统环境下,不是非常的合适。更何况很多的IO
操作默认都是阻塞的,更加容易导致实时性问题。
由于树莓派本身也是支持Lua
脚本的,默认安装的Lua
引擎默认是5.1.4
。Lua
本身在游戏中使用较多,而游戏本身对于实时性的要求是很高的。
尤其是Lua 5.1
开始使用最新的GC
已经能很好的解决实时性问题。
关于Lua
的GC
相关信息,参考如下的文章:
首先参考 Ubuntu 16.04 (x64)树莓派B+ (Raspberry Pi B+)源代码编译 保证能够成功编译标准内核的源代码,然后切换到实时内核分支,并执行如下编译命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
$ export PATH=$PATH:~/rpi/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin $ cd ~/rpi/rpi-linux/ $ git checkout rpi-4.14.y-rt $ git reset --hard $ KERNEL=kernel $ make clean $ make mrproper $ rm -rf .config #调整内核切换频率,增加实时性 $ sed -i '$a\CONFIG_HZ_1000=y' arch/arm/configs/bcmrpi_defconfig #Raspberry Pi Zero W的CPU是BCM2835 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcmrpi_defconfig $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs -j8 $ mkdir rt_kernel $ make modules_install ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=./rt_kernel -j8 $ make dtbs_install ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_DTBS_PATH=./rt_kernel -j8 $ ./scripts/mkknlimg ./arch/arm/boot/zImage ./rt_kernel/kernel.img |
Ubuntu
下面,SD
卡会自动挂载,默认挂载到了/media/
目录下面,如果是使用NOOBS
安装的话,系统目录是固定的,执行如下命令拷贝到目标SD
卡上面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ cd ~/rpi/rpi-linux #备份需要修改的文件 $ mv /media/`whoami`/boot/kernel.img /media/`whoami`/boot/kernel_old.img $ mv /media/`whoami`/boot/overlays /media/`whoami`/boot/overlays.old #拷贝内核 $ cp rt_kernel/kernel.img /media/`whoami`/boot/kernel.img #拷贝硬件配置 $ cp rt_kernel/bcm2835*.dtb /media/`whoami`/boot/ #拷贝overlays $ cp -r rt_kernel/overlays /media/`whoami`/boot/ #拷贝内核模块 $ sudo cp -r rt_kernel/lib/modules/* /media/`whoami`/rootfs/lib/modules/ #卸载设备 $ sudo umount -A -R -a /media/`whoami`/boot |
有时,我们需要单独编译新下载的内核驱动,这个时候,就可以使用如下的方式进行单独内核模块的编译。
下面,我们以ASIX AX88772
系列的USB
有线网卡驱动的编译为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ export PATH=$PATH:~/rpi/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin $ cd ~/rpi/rpi-linux # wget https://www.mobibrw.com/wp-content/uploads/2018/09/AX88772C_772B_772A_760_772_178_LINUX_DRIVER_v4.22.0_Source.tar.bz2 $ wget http://www.asix.com.tw/FrootAttach/driver/AX88772C_772B_772A_760_772_178_LINUX_DRIVER_v4.22.0_Source.tar.bz2 $ tar xvf AX88772C_772B_772A_760_772_178_LINUX_DRIVER_v4.22.0_Source.tar.bz2 $ cd AX88772C_772B_772A_760_772_178_LINUX_DRIVER_v4.22.0_Source $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C ../ M=`pwd` #当前目录下就会生成 asix.ko 这个内核模块,生成的内核模块拷贝到指定的目录即可正常工作 |
使用上面的命令安装完成内核后,目前(2018.09.26
)遇到的问题为,当插入ASIX AX88772
系列的USB
有线网卡之后,会导致内核崩溃,启动失败。
设备信息如下:
1 |
Bus 001 Device 034: ID 0b95:7720 ASIX Electronics Corp. AX88772 |
初步怀疑是USB
设备的驱动依赖关系不正确导致内核崩溃。
目前的临时解决方法为要求设备启动时候优先加载USB
设备相关的驱动,而不是等到网卡插入的时候再去加载。
也就是在/boot/config.txt
文件尾部新增加一行dtoverlay=dwc2
。这段代码本来是为树莓派通过USB
访问网络的虚拟网卡准备的(是的,你没看错,树莓派本身可以不借助网卡直接通过USB
接口跟电脑共享方式上网,不过需要设置一堆东西,最简单的还是外接真正的USB
网卡)。我们加载这个模块,但是并不使用这个功能,造成的结果就是重新调整了模块加载顺序,规避了后续的问题。
1 |
$ sed -i '$a\\ndtoverlay=dwc2' /media/`whoami`/boot/config.txt |
上述修改后,依旧存在动态插拔网卡,设备会重启的问题,不过已经不影响正常使用。