`编程`分类下的文章

编程

蓝牙测速

之前的文章中介绍过蓝牙的速度和踏频协议,在那篇文章中也顺便介绍了BLE的一些小名词.这一篇文章就来说说炫轮App 3.0中的测速模块是怎么构成的吧.

Context

炫轮App可以获取到哪些传感器的数据呢?

  1. 炫轮车灯自身的码表数据
  2. 炫轮踏频器
  3. 满足公有踏频速度协议的传感器
  4. GPS

他们的数据是些什么?

1.炫轮车灯的数据

炫轮车灯会通过蓝牙得到每转一圈需要的时间

2.炫轮踏频器

这个产品不测速,所以先不多说~

3.满足公有协议的踏频速度传感器

通过蓝牙得到在第x秒转了y圈的信息

4.GPS

GPS数据本身就携带了速度信息,只不过,如果GPS信号不强,那这个数据就会不精准

再来个附加功能

大多数的骑行App的一次运动运动时间是手动点击开始,暂停,保存来记录的.这样不是很难过嘛?我想要个自动开始,自动暂停的码表!

不用往下翻,我没写~~这只是打个广告

Structure

所以和速度有关的传感器目前为止大概是那么3类: 炫轮车灯,满足公有协议的速度计,GPS

既然,GPS那么不稳定,干脆砍掉算了.

所以问题就变成了,如何根据炫轮车灯和速度计来计算出较为准确地速度.


Structure-Context

App 可以连接两个炫轮车灯,一个速度计

码表模块是和界面分离的,把他当做服务来处理(这里我写了一个单例),这个类可以收数据,也可以被获取数据.收到的自然是解析后的炫轮数据和速度计的数据,提供获取接口的是多种单位下的:当前速度,平均速度,总路程,总运动时间,(当然还有踏频和心率,只是这里不用,所以就不说了)


全局变量

计时器1枚,记录炫轮数据的数组2枚,记录速度计的数据的数组1枚.

其他的变量这篇文章用不到,就当他不存在了

-(void)updateXuanwheelSpeed:(NSTimeInterval)timeinterval ForPosition:(int)position
{
    switch (position) {
        case 0:
        {
            //########
            [xuanWheelTemporaryList addObject:@(timeinterval)];
            xuanwheelRevolution1++;
            //########
            break;
        }
        case 1:
        {
            //########
            [xuanWheelTemporaryList addObject:@(timeinterval)];
            xuanwheelRevolution2++;
            //########
            break;
        }
        default:
            break;
    }    
}

这是处理炫轮数据的方法,把一圈的时间间隔记录下来,分开写是因为要分别记录两个轮子转的圈数,帮助计算距离.

-(void)updateSpeeder:(Float32)frequency
{
    //########
    [publicTemporaryList addObject:@(frequency)];
}

公有协议的数据,我在这之前就一经计算好了转一圈的频率.已知在第X1秒转了Y1圈,在X2秒转了Y2圈.那么频率就是(X1-X2)/(Y1-Y2)

记录完毕数据,就该计算相关信息了

Calculate

-(void)optimizeSpeed
{
    if (######) {
        //....
    }else
    {
        //#####
        //Reason1:只有一组数据,还怎么计算平均速度;Reason2:#####
        if (publicTemporaryList.count>1||xuanWheelTemporaryList.count>1) {
            int count=0;
            //time用来存放这1秒内平均的转过1圈需要的时间
            NSTimeInterval time=0;
            for(NSNumber *item in publicTemporaryList)
            {
                //这里来计算公有协议的速度信息
                if (item.floatValue>5) {
                    //这么慢还是忽略吧
                    continue;
                }
                if (item.floatValue<=0) {
                    //谁解析的数据!!
                    continue;
                }
                time+=1.f/item.floatValue;
                count++;
            }
            if (publicTemporaryList.count>0) {
                if (publicTemporaryList.count>1) {
                    [publicTemporaryList removeObjectsInRange:NSMakeRange(0, publicTemporaryList.count-1)];
                }else
                {
                    [publicTemporaryList removeAllObjects];
                }
                //算完删光,留下一组数据,为下一秒做帮助,防止突变
            }
            for(NSNumber *item in xuanWheelTemporaryList)
            {
                //这里来计算炫轮的速度信息
                if (item.floatValue>5) {
                    continue;
                }
                if (item.floatValue<=0) {
                    continue;
                }
                time+=item.floatValue;
                count++;
            }
            if (xuanWheelTemporaryList.count>0) {
                if (xuanWheelTemporaryList.count>1) {
                    [xuanWheelTemporaryList removeObjectsInRange:NSMakeRange(0, xuanWheelTemporaryList.count-1)];
                }else
                {
                    [xuanWheelTemporaryList removeAllObjects];
                }
                //Again,留一组数据来备用
            }
            if (count==0||time==0) {
                currentS=0;
            }else
                currentS=[PublicResource sharedObject].roundLength*count/time*3600;
                //这个就是这1秒的速度了咯
        }else
        {
            //#####
        }
    }
//最新的最高速度
    if (currentS>max_inner_speed) {
        max_inner_speed=currentS;
    }
//防止速度突变
    if (currentS>lastSpeed+30) {
        lastSpeed=currentS;
        currentS=0;
    }
//强制限速
    if (currentS>80) {
        currentS=80;
    }
}

差不多,速度就是这么计算的,在实际测试中,感觉还是满准确地~

阅读剩下更多

编程

Python 处理字节流

python处理字节的点点滴滴

python有多爽,我就不说了,反正想得到的事情,基本都有办法用python来解决,当然还有一个很重要的优点: 写python不用编译~

Context

  1. 之前毕业设计做的是SMS4的实现与安全性分析.安全性分析是什么鬼?查了查书,好像是用有特征的原文来进行测试……,具体的我也忘记了.当时就琢磨,怎么样可以做一个酷炫的安全性分析呢?
  2. 公司的新产品是一款智能硬件,它会通过TCP给服务器发送指令(当然不是熟悉的HTTP请求,是传说中的字节流)

Requirement

  1. commands+re : 这个组合可以轻轻松松的提取出命令行程序的有用信息
  2. binascii : 这个东西可以轻松的产生各种各样和二进制有关或者和ascii有关的字符串
  3. struct : 超级牛逼的解包模块,瞬间把一个字节流解成元组

OK, Let’s go

commands+re

commands.output(cmd),cmd是shell命令我也不知道windows的命令可以不可以~~
这一条可以把运行结果返回出来(字符串形式)

然后用re来做正则匹配,再搭配一些split(),replace()之类的操作字符串的函数,就可以提取出一个命令行程序中有用的信息了.

binascii

binascii.b2a_hex(data)
binascii.hexlify(data)

data就是原始字符串,假设就是”abcde”
返回的也是字符串,不过是16进制的字符串”6162636465”

binascii.a2b_hex(hexstr)
binascii.unhexlify(hexstr)

这个函数就是反过来的
‘hexstr’是16进制的字符串,假定说是”6162636465”
返回的字符串就是他们所对应的ascii的字符,就是”abcde”

这有啥用嘞?

做测试!

让下位机发送一条指令可能还蛮复杂的,或者蛮浪费资源什么的,这样调试服务器代价会比较大.使用binascii来制作测试数据再好不过了.

假设下位机发送的数据是”0xaa,0xbb,0xcc,0x12,0x34,0xdd,0xee,0xff”

我们就可以模拟一条

a="aabbcc1234ddeeff"
b=asciibin.a2b_hex(a)

b就是模拟了服务器收到socket字节流.真方便

struct

收到字节流,根据协议,得知其中的8个字节是double,4个字节是float.

嗯,字节转整形还是有办法的,那么转成浮点型应该怎么转呢??

import struct吧

将python数据封装成字节流

struct.pack(format,argvs...)

将字节流解析成python数据类型

struct.unpack(format,string)

format很有意思,简单说就是解析规则.

比如解析成float,就写’f’

解析成double,就写成’d’

那么一个数据流里面既有float又有double怎么办呢?

嗯,截取字符串嘛!

struct.unpack('f',s1[0:4])
struct.unpack('d',s1[4:12])

哈哈,我一开始也是这样写的,后面章鱼兄跟我说,format里面可以写很多东西

struct.unpack('fd',s1)

这个会直接返回(r1,r2),其中r1就是那个float,r2就是那个double

真的好方便

附表一张

format 类型
f float
d double
c char
i int
l long
3s 长度为3的字符串
4s 长度为4的字符串
5s 长度为5的字符串

至于struct.pack()嘛,既然format都有了,那么应该不难理解了,在python里面试一试就明白了

阅读剩下更多

编程

微信服务号的消息处理

新产品会和微信公众号有交互,所以就来尝尝鲜,做一做微信公众号的各种东西.

首先:注册,认证,bla bla bla,设置一堆东西

在获得了开发权限之后,就可以仔细阅读开发文档了.

和微信的交互有两种:

  1. 服务器给微信发送消息
  2. 微信给服务器发送消息

说说他们的区别.

服务器给微信发消息,很方便,发表单格式的就行了,微信回复的是json格式的

微信服务器主动给服务器发消息,就很恶心了.

  1. 如果在配置页面设置了消息模式是加密的,那么收到了微信的消息之后需要先对包进行解密,才能得知微信发送的消息的确切内容.
  2. 微信的每次请求,都需要按照和它一样的变态格式回复.
  3. 还得验证验证消息到底是不是微信发的~

先来看看微信给服务器发的是什么样子的东西

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>

哦,这个是没加密的,加密的更难看…

应该用正则来提取内容吧,我把他们都写了出来,方便方便

REGEX_TOUSERNAME = r'\<ToUserName\>\<\!\[CDATA\[(.*)\]\]\>\<\/ToUserName\>'
REGEX_FROMUSERNAME = r'\<FromUserName\>\<\!\[CDATA\[(.*)\]\]\>\<\/FromUserName\>'
REGEX_MSGTYPE = r'\<MsgType\>\<\!\[CDATA\[(.*)\]\]\>\<\/MsgType\>'
REGEX_EVENT = r'\<Event\>\<\!\[CDATA\[(.*)\]\]\>\<\/Event\>'
REGEX_EVENT_KEY = r'\<EventKey\>\<\!\[CDATA\[(.*)\]\]\>\<\/EventKey\>'
REGEX_SCANTYPE = r'\<ScanType\>\<\!\[CDATA\[(.*)\]\]\>\<\/ScanType\>'
REGEX_SCANRESULT = r'\<ScanResult\>\<\!\[CDATA\[(.*)\]\]\>\<\/ScanResult\>'
REGEX_CREATETIME = r'\<CreateTime\>(.*)\<\/CreateTime\>'
REGEX_CONTENT = r'\<Content\>\<\!\[CDATA\[(.*)\]\]\>\<\/Content\>'

还傻乎乎的搞了个类来解析这坨东西

class wechat_xml_object(object):
    def __init__(self, xml):
        self.origin = str(xml)

    @property
    def toUserName(self):
        return self.returnValueForKey(REGEX_TOUSERNAME)

    @property
    def fromUserName(self):
        return self.returnValueForKey(REGEX_FROMUSERNAME)

    @property
    def msgType(self):
        return self.returnValueForKey(REGEX_MSGTYPE)

    @property
    def event(self):
        return self.returnValueForKey(REGEX_EVENT)

    @property
    def eventKey(self):
        return self.returnValueForKey(REGEX_EVENT_KEY)

    @property
    def scanType(self):
        return self.returnValueForKey(REGEX_SCANTYPE)

    @property
    def scanResult(self):
        return self.returnValueForKey(REGEX_SCANRESULT)

    @property
    def createTime(self):
        return self.returnValueForKey(REGEX_CREATETIME)

    @property
    def content(self):
        return self.returnValueForKey(REGEX_CONTENT)

    def returnValueForKey(self, regex):
        match = re.search(regex,self.origin)
        if match:
            return match.group(1)
        else:
            return None

虽然我知道有kissXML这种神器,但是我得装……

上面是把明文的XML解包出来,现在我们把想回复的数据给封装起来

class wechat_xml_builder(object):
    def __init__(self, data_type):
        self.type = data_type
        self.content = ''
        self.contains = []

    def addObject(self, key, value, data_mark):
        if key in self.contains:
            return False
        if data_mark:
            self.content += '<{0}><![CDATA[{1}]]></{2}>'.format(key, value, key)
        else:
            self.content += '<{0}>{1}</{2}>'.format(key, value, key)
        self.contains.append(key)
        return True

    def add_default_from(self, dictionary):
        fromUser = dictionary['toUser']
        openid = dictionary['openid']
        createTime = dictionary['createTime']
        self.addObject('ToUserName', openid, True)
        self.addObject('FromUserName', fromUser, True)
        self.addObject('CreateTime', createTime, False)

    def exportString(self):
        return '<xml>' + self.content + '</xml>'

我感觉这个类用起来还是蛮简单的.

至于说加密和解密,微信还是很良心的提供了一个库,可以直接操作.

下次再整理一下微信开发中其他的坑儿吧

阅读剩下更多

编程

ios 命令行单元测试

在开发iOS大项目的时候,由于模块太多,每个功能的模块也太多,在后续测试调试的时候,遇见了bug,就会花比较长的时间去定位bug所在的类或者所在的函数.如果使用了单元测试便可以缩小查找bug的范围.

XCode自带单元测试的命令行工具,可以编写UnitTest文件来自动测试工程,下面来介绍一下它的一些些用法.

首先得确定有xcodebuild命令(跳过,都是iOS开发者,肯定装了XCode咯)

$xcodebuild

肯定没这么简单,假设当前目录是有XCode的工程的,那么这个命令就会执行与在xcode里面点击build一样的效果.

使用xcodebuild运行测试

xcodebuild 命令就是执行了 XCode 里面的build,不过这个命令有一些其他的参数,用法太多这儿就不解释了.总之给他加点儿不同的参数就可以做不同的事情.使用 test 来测试.用-scheme指定操作的scheme.用 -destination 指定target.要在本地 iPhone 5模拟机 “iPhone 5” 测试某个工程的某个测试,使用如下命令来指定目标和架构:

xcodebuild test -project rct6updater.xcodeproj -scheme rct6updaterTest -destination 'platform=iOS ,name=iPhone 5'

如果想对真机操作,可以按照名称或id.比如,我的测试机叫做”VikingWarlock 5”,可以这样来测试代码:

xcodebuild test -workspace xuanwheel/xuanwheel.xcworkspace -scheme xuanwheelTests -destination 'platform=iOS,name=VikingWarlock 5'

测试也可以在 iOS模拟器上运行.使用模拟器可以应对不同的外形因素和操作系统版本.例如:

xcodebuild test -workspace xuanwheel/xuanwheel.xcworkspace -scheme xuanwheelTests -destination 'platform=iOS Simulator,name=iPhone,0S=7.0' -destination 

如果测试失败,xcodebuild 会有红色的 FAIL 提示,修改修改测试的代码,多加一些提醒,这样可以更方便的定位错误点.

等下

之前是-project的,怎么突然使用了-workspace嘞?因为我的工程使用cocoaPods了,所以测试的时候要使用workspace才行.

阅读剩下更多

编程

Sublime 开发arduino

把Sublime改装成arduino的IDE

参加一个物联网比赛,它要求使用的开发板是基于Arduino的.可是,Arduino自己做的IDE简直难过死了,没有自动不全,也不能输入中文所以我想了一下,强大的sublime一定是有arduino的插件的!

安装Sublime

Sublime很牛逼,三个平台都有,百度上面一搜,去官网下载就好了,安装也不麻烦,所以就不说了

安装package-control

Sublime 有两个发行版Sublime Text 2 和 Sublime Text 3
针对这两个不同的版本方法也是不一样的

  1. Ctrl(Command) + ~ 呼出命令行
  2. 直接把下面的代码往里面怼,然后按Enter(Return)

Sublime Text 2 的安装方法:

import urllib2,os; pf='Package Control.sublime-package'; ipp =sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener(urllib2.ProxyHandler( ))); open( os.path.join( ipp, pf),'wb' ).write( urllib2.urlopen('http://sublime.wbond.net/' +pf.replace( '','%20' )).read()); print( 'Please restart Sublime Text to finish installation')

Sublime Text 3 的安装方法:

import urllib.request,os; pf = 'Package Control.sublime-package'; ipp=sublime.installed_packages_path();urllib.request.install_opener(urllib.request.build_opener(urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf),'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' +pf.replace(' ','%20')).read())

原来都是python语句(我总觉得sublime text的作者一定钟爱python)

安装stino

  1. Crtl+Shift+P 打开另一种命令行
  2. 输入Package Control: Add Repository,按个Enter(Return)
  3. 输入Stino的开源地址 https://github.com/gepd/Stino/tree/new-stino,按个Enter(Return)
  4. Ctrl+Shift+P 再一遍,输入Package Control: Install Package
  5. 等他更新完所有的源之后,输入stino,安装就好啦

后续

这个stino是可以代替arduino来进行交叉编译&上传代码的,不过这个功能必须要求Arduino IDE在当前平台也支持编译和上传某种板子(比赛用的板子就不支持Linux下的Arduino…)

stino安装完毕之后是需要选择Arduino IDE的安装位置,据说不要使用最新的Arduino,因为stino好像是不支持的.

小秘密

我感觉stino除了加了自动补全,以及一些基本arduino的语法补全之外,并没有什么特点.假设工程中需要调用其他的文件,其他的库,还是用的不爽……

阅读剩下更多

编程

BLE速度踏频公有协议

BLE(Bluetooth Low Energy)

不知道从哪一年开始,BLE突然火了起来,可能是苹果推动的iBeacon,推了BLE一把吧

BLE模块越做越小,越做越省点,于是他们就纷纷往智能硬件上面去集成了,比如说什么手环啊,码表啊,门锁,窗帘啊,灯啊,炫轮啊,之类的.

How does it work

这里不是说BLE在底层如何工作,只是从上层软件开发的角度,讲讲BLE怎么工作

BLE必须提到的几个名词

  1. peripheral
  2. central
  3. service
  4. characteristic
  5. UUID

Peripheral

传说中的外设,也就是那些个手环啊,码表啊,炫轮啊之类的东西,他们在不停地广播自己存在的信号,可以让别人知道自己存在,自己的名字,自己有些什么Service.别人也可以根据这个广播计算以下RSSI(信号强度)

Central

暂且叫他主机吧,因为在定义中,并不是叫Peripheral和Central,而是Master和Slave(太难听了,还是使用iOS中的外设中心吧).

Central可以接收到广播帧,并且知道它的Mac地址啊,拥有哪些Service啊,名字啊……

Service

服务,假设一个蓝牙可以提供很多信息,比如说,电量啊,心率啊,速度啊,还有自定义的一些数据.如果通过数据帧头来区分他们,会非常的麻烦.所以BLE有Service这个概念,一个Service负责一件事情,清晰明了.

Characteristic

特征值,每个服务可以拥有多个特征值,每个特征值都有权限,比如Read,Write,Write Without Response,Notify……

UUID

好吧,这么多东西,怎么标示嘞?用UUID咯…

BLE有专用UUID是16位的,然而标准的UUID是128位的.

总结一下

每一个外设(Peripheral)可以拥有很多的服务(Service),每一个服务(Service)可以拥有多个特征值(Characteristic),使用UUID来确定Service和Characteristic.

说说公有协议

由于BLE的Service和Characteristic的UUID是16位的,那就说有很多很多很多的UUID可以被定义.选择太多,就容易出乱子.所以大概是IEEE还是IEC还是SIG之类的组织专门定了一大堆公有协议

这个链接里面的Service是他们定的,貌似已经被大家接受了.

速度和踏频

为什么只说速度与踏频的公有协议呢?

因为我们刚好在做这个……

Service UUID: 0x1816
Characteristic UUID: 0x2A5B (至少得有Notify,Read权限)

有了这两个东西,你的App或者其他智能硬件就可以进行过滤扫描和连接了

具体协议

Flag(必须):8位
    0x01==(速度数据)
    0x02==(踏频数据)
    0x03= (速度数据+踏频数据)
速度数据:
    32位:累计轮胎圈数[溢出清零]
    16位:时间(精确到1/1024秒)[溢出清零]
踏频数据:
    16位:累计曲柄圈数[溢出清零]
    16位:时间(精确到1/1024秒)[溢出清零]

举个例子

轮子在3.7353秒转动了103圈,曲柄在54.1093秒转动了202

03 67 00 00 00 f1 0e 00 ca d8 70

阅读剩下更多

编程

删除静态库中的静态库~

iOS静态库开发的故事

假设我们要做一个SDK给别人用,有两个选择,framework包,a包.他们有啥区别?framework包里面可以包含图片资源.a包里面只能是纯代码.所以a文件的容量真的很小很小.

万一,我们制作静态库的时候调用了第三方库,在打包的时候,如果不把这些第三方库去掉,会有什么效果呢?

使用我们SDK的人如果也使用了相同的第三方库,就会导致在编译的时候,出现duplicate信号,因为两个相同的类出现了,所以呢,如果一定要使用a包,只能手动的将包内的第三方库给去掉.

OK, 我们来看看删掉一个.a静态库中的某个第三方静态库需要怎么搞

Context

我们做了一个.a静态库,其中包含了

Masonary
JFMinimalNotification
FCFileManager
.....

第一步:

lipo * -thin [platform] dir/new.a

platform是平台,什么x86啊,arm之类的

ar -t dir/new.a

解压出来

cd dir && ar xv new.a

跳转到那个文件夹

rm *MAS*
rm JF*
rm ...

删啊删啊删

cd ..
ar rcs lib_1.a dir/*.o 

重新再打包成lib_1.a

lipo -create ... -output new_clean_static_lib.a

最后生成多平台的静态库


这有什么问题嘛?

嗯,你发布的SDK肯定是多平台的,也就是说,你需要有多少个平台,上面的事情就得做多少次~~

So,我写了一个Python脚本专门来解决这个难过的事情,VKRemove

Function 1

python VKRemove.py -lp xxx.a

返回这个静态库里面包含的平台

This library contains:i386 armv7 x86_64 arm64

Function 2

python VKRemove.py -lb xxx.a

返回这个静态库里面所有的库

...'View+MASAdditions.o', 'ViewController+MASAdditions.o', 'XuanWheelBluetoothManager.o'...

感觉很高端了对吧?

Function 3

python VKRemove.py -i XuanWheelSDK.a -rm FCFileManager HMSegmentedControl- JFMinimalNotification   UIView+Round UIImage+ImageEffects.o MAS

这会生成一个xxx.a.new,它包含了之前所有的平台,并且去掉了指定的所有第三方库.

感觉更高端了吧?

Function 4

python VKRemove.py -i XuanWheelSDK.a -from clear.txt

每次都输入那么长的命令太难过了,不如从文件输入吧~

当然咯,Function 3中的-是干什么用的,可以直接去Github上面看,虽然是英文的~

阅读剩下更多

返回顶部