遇到的树莓派蓝牙Serial的坑(RFCOMM)

第一次接触树莓派蓝牙,卡了好久,现在解决了,我就记录下来这些坑。

这边用NodeJS来做蓝牙的Server端,使用的是经典蓝牙,Python的话应该也是差不多的,毕竟就是读写串口。

首先是日常的安装依赖库

sudo apt install pi-bluetooth bluez bluez-firmware blueman

修改蓝牙的服务

sudo nano /etc/systemd/system/dbus-org.bluez.service

修改ExecStart,增加ExecStartPost,然后保存

ExecStart=/usr/lib/bluetooth/bluetoothd -C
ExecStartPost=/usr/bin/sdptool add SP

重启树莓派

NodeJS这边需要准备 serialport 库

npm install serialport

树莓派的蓝牙打开之后,手机的蓝牙串口工具可能没办法连接,这个时候需要监听蓝牙的hci0设备,如果没有其他串口,默认在/dev/rfcomm0,/dev/rfcomm0只有在有设备连接的时候才会自动创建,所以如果没有设备连接的时候打开串口会报错,需要做检测。

sudo rfcomm watch hci0

接下来就是代码了:


var SerialPort = require("serialport")
var bthServer = new SerialPort("/dev/rfcomm0",{
  baudRate:9600, //默认比特率为9600
  autoOpen:false, //是否自动开启,如果是的话下方的open函数就不需要了
}

bthServer.open(function(err){
  console.log("Is opened:",bthServer.isOpen)
  console.log("Error:",err) //如果有错误的话就开不了
}

bthServer.on("data",function(data){
  console.log(data) //输出蓝牙接收到的数据
  bthServer.write("Received") //往回发数据
}

8086汇编指令Wiki

数据传送指令

1.通用数据传送指令

指令含义中文描述
MOV Move 传送
PUSH Push onto the stack 进栈
POP Pop from the stack 出栈
XCHG Exchange 交换

2.累加器专用传送指令

指令含义中文描述
INInput 输入
OUTOutput 输出
XLATTranslate 换码

3.有效地址送寄存器指令

指令含义中文描述
LEALoad effective address有效地址送寄存器
LDSLoad DS with Pointer指针送寄存器和DS
LESLoad ES with Pointer指针送寄存器和ES

4.标志寄存器传送指令

指令含义中文描述
LAHFLoad AH with flags标志寄存器送进AH
SAHFStore AH into flagsAH 送进标志寄存器
PUSHFPush the flags标志进栈
POPFPop the flags标志出栈

算术指令

1.加法指令

指令含义中文描述
ADDAdd加法
ADCAdd with carry带进位加法
INCIncrement自加1

2.减法指令

指令含义中文描述
SUBSubtract减法
SBBSubtract with borrow带借位减法
DECDecrement自减1
NEGNegate求补
CMPCompare比较

3.乘法指令

指令含义中文描述
MULUnsigned Multiple无符号数乘法
IMULSigned Multiple带符号数乘法

4.除法指令

指令含义中文描述
DIVUnsigned divide无符号数除法
IDIVSigned divide带符号数除法
CBWConvert byte to word字节转换为字
CWDContert word to double word字转换为双字

逻辑指令

1.逻辑运算指令

指令含义中文描述
ANDAnd逻辑与
OROr逻辑或
NOTNot逻辑非
XORExclusive Or异或
TESTTest测试

2.移位指令

指令含义中文描述
SHLShift logical left逻辑左移
SALShift arithmetic left算术左移
SHRShift logical right逻辑右移
SARShift arithmetic right算术右移
ROLRotate left循环左移
RORRotate right循环右移
RCLRotate left through carry带进位循环左移
RCRRotate right through carry带进位循环右移

串处理指令

指令含义中文描述
MOVSMove string传送串
STOSStore string保存串
LODSLoad string加载串
CMPSCompare string比较串

控制转移指令

1.无条件转移指令

指令含义中文描述
JMPJump 无条件转移

2.条件转移指令

指令含义(转移条件)中文描述
JZ / JEJump if zero,
or equal
结果为零或相等
JNZ / JNEJump if not zero,
or not equal
结果不为零或不相等
JSJump if sign结果为负
JNSJump if not sign结果为正
JOJump if overflow溢出
JNOJump if not overflow不溢出
JP / JPEJump if parity,
or parity even
奇偶位为1
JNP / JPOJump if not parity,
or parity odd
奇偶位为0
JB / JNAE / JCJump if below,
or not above or equal,
or carry
低于,
或者不高于等于,
或进位位为1
JNB / JAE / JNCJump if not below,
or above or equal,
or not carry
不低于,
或者高于等于,
或进位位为0
JBE / JNAJump if below or equal,
or not above
低于等于,
或不高于
JNBE / JAJump if not below or equal,
or above
不低于等于,
或者高于
JL / LNGEJump if less,
or not greater or equal
带符号数小于,
或者不大于等于
JNL / JGEJump if not less,
or greater or equal
带符号数不小于,
或者大于等于
JLE / JNGJump if less or equal,
or not greater
带符号数小于等于,
或者不大于
JNLE / JGJump if not less or equal,
or greater
带符号数不小于等于,
或者大于
JCXZJump if CX register is zero CX寄存器的内容为零

3.循环指令

指令含义(循环条件)中文描述
LOOPLoop无条件
LOOPZ / LOOPELoop if zero当为零或相等时
LOOPNZ / LOOPNELoop if not zero当不为零或不相等时

4.子程序

指令含义中文描述
CALLCall调用指令
RETReturn返回指令

5.中断

指令含义中文描述
INTInterupt中断
INTOInterupt if overflow若溢出则中断
IRETReturn from interupt从中断返回指令

处理机控制指令

1.标志处理指令

指令含义中文描述
CLCClear carry进位位置0指令CF=0
CMCComplement carry进位位求反指令CF=Not CF
STCSet carry进位位置1指令CF=1
CLDClear direction方向标志置0指令DF=0
STDSet direction方向标志置1指令DF=1
CLIClear interrupt中断标志置0指令IF=0
STISet interrupt中断标志置1指令IF=1

2.其他处理机控制指令

指令含义中文描述
NOPNo Opreation无操作
HLTHalt停机
WAITWait等待
ESCEscape换码
LOCKLock封锁

Lua 5.1代码效率优化

用Lua的时候可能需要涉及到效率的情况,毕竟Lua代码量大了之后速度肯定会有所降低,这边就是经历了各种效率测试积累的经验并且记录下来。

辟谣:
1、lua中乘以0.5和除以2的效率是一样的,因此不需要纠结乘除法,反而除以2还少一个字节。

提示:
1、Lua访问函数的开销非常大,即尽量减少函数的调用
2、访问全局变量比局部变量慢,因为访问全局变量需要查全局表

数学算法加速:

--math.abs实现,效率提高至少5倍
y = x <= 0 and -x or x
--math.floor实现,效率提高至少5倍
y = x - x % 1
--math.ceil实现,效率提高至少2倍
y = x - x % 1 + ( ( x % 1 == 0) and 0 or 1 )
--math.max实现,效率提高至少8倍
y = a <= b and b or a
--math.min实现,效率提高至少8倍
y = a >= b and b or a
--math.deg实现,效率提高至少8倍
y = x*180/3.1415926535898
--math.rad实现,效率提高至少8倍
y = x/180*3.1415926535898

Lua在Linux上使用Sox进行批量处理MP3音频

首先需要Sox和MP3格式支持

apt install sox
apt install libsox-fmt-mp3

然后是lua程序

主程序 main.lua

local filelist = io.popen('find input -name "*.mp3"')	--扫描MP3
local fileContent = filelist:read("*all")	--读取文字段
filelist:close()	--关闭popen
os.execute("mkdir output")
os.execute("rm -rf SOXLUA")	--移除锁储存的目录
os.execute("mkdir SOXLUA")	--创建锁储存的目录

function split( str,reps )	--字符串分段
    local resultStrList = {}
    string.gsub(str,'[^'..reps..']+',function ( w )
        table.insert(resultStrList,w)
    end)
    return resultStrList
end

function getPart(str)	--大段路径转换成表
	local str,lin = string.gsub(str,"\r","")
	return split(str,"\n")
end
local ret = getPart(fileContent)
local totalLine = #ret	--总行数
local cnt = 0	--当前正在执行

function getPool()	--获取处理池,一旦有空池,立刻返回,否则阻塞主程序
	while(true) do
		os.execute("sleep 0.01")	--防止while造成高cpu使用率
		for i=1,12 do	--一共12个处理程序,循环检测
			local f = io.open("SOXLUA/SOXLUA"..i..".lock")	--检查锁文件是否存在
			if not f then return i end	--不存在则返回id,表示可用
			if f:read(2) == "ad" then	--如果存在,则读取这个锁是否已经被释放
				f:close()	--关闭检查的文件
				return i	--返回id,表示可用
			else
				f:close()	--关闭检查的文件
			end
		end
	end
end

for i,line in ipairs(ret) do	--主循环
	cnt = cnt+1	--每次循环+1表示当前处理进程
	local dir = split(line,"/")	--把路径写成表
	table.remove(dir,1)	--从表中移除"input"
	local fileName = dir[#dir]	--记录文件名
	table.remove(dir,#dir)	--从表中移除文件名
	local path = ""	--最终路径
	for i=1,#dir do	--层层创建路径,并且写入path变量
		path = path.."/"..dir[i]
		os.execute("mkdir 'output"..path.."'")	--创建目录
	end
	print("["..cnt.."/"..totalLine.."]"..line.." --> output"..path.."/"..fileName)	--打印进程数据
	local soxCommand = 'sox '..line..' -C 128 output/'..path..'/'..fileName	--转码的指令,!此处根据实际需求来修改代码!,传递给sox.lua的参数
	local poolID = getPool()	--获取空闲处理程序池
    io.popen("lua sox.lua '"..soxCommand.."' '".."SOXLUA/SOXLUA"..getPool()..".lock".."'")	--不加:close则为异步启动处理程序
end

子程序 sox.lua

local arg = {...}	--获取参数
local file = io.open(arg[2],"w")	--打开锁文件
file:write("a")	--写入a表示该锁已被使用
file:flush()	--立刻保存
io.popen(arg[1]):close()	--运行Sox指令
file:write("d")	--完成后,写入d表示该锁已经被释放
file:close()	--关闭锁

然后要把转码的文件丢到同目录下的input目录(没有自己创建),然后输入lua main.lua运行程序

处理完毕之后,输出在同目录下的output目录,并且保存目录结构

Lua批量重命名+序列化

一个一个手打太麻烦,win的cmd又不是很懂的。

编译完官网上下完lua配置完系统环境变量,直接使用如下代码

local path = "C:\\Users\\thisdp\\Pictures\\Camera Roll\\"
local filelist = io.popen('dir "'..path..'"'):read("*all")
local start_pos = 0
local count = 0
while true do
	_,end_pos,line = filelist:find("([^ \n\r]+.jpg)", start_pos)
	if not end_pos then break end
	count = count + 1
	local source,target = path..line,path.."Left_"..count..".jpg"
	print(source,target,os.rename(source,target))
	start_pos = end_pos + 1
end

使用DLL的Lua解释器编译

直接编译Lua会让所有代码都写入Lua.exe,,由于没有Lua解释器的动态链接库,所以只能使用完整的Lua静态库编译Module,这样编译的Module在使用require加载的时候会出现Multiple VM Detected的报错。

将Lua编译成DLL,会自动生成对应的.lib静态库。再将lua.c单独拿出来编译,导入编译DLL生成的静态库,生成一个依赖于动态链接库的Lua Console。用此静态库编译的Module将会依赖于Lua 解释器的动态链接库,完毕。