2014年四月份的一天,老上司用一顿饭把我引出来,告诉我,她要参加大创,有一个好的Idea,如今就差一只程序猿了。做为一个典型的计院宅男,我当初并不明白也不看重。可是饭已经吃了,嘴已经软了。我仍是答应了。javascript
开始的设想是一个关于家庭成员互动的APP。咱们拿着这个不值钱的想法去找‘投资人’侯老师,她很认真地听了一番,而后很礼貌地说大家这个想法简直bull shit要想在大创上拿名次简直是梦话。php
咱们从新构思了几个方案,后来,咱们的注意力集中到了水幕身上。这个想法看起来展现性强了不少,简单地说就是一个水幕版本的“别踩白块”,和正版的区别是显示的屏幕变成了水幕而已。咱们七嘴八舌,又意淫出了各类奇奇怪怪的想法。css
我信誓旦旦地保证这个程序我绝对能够写得出来,其实我自个也没有什么底。由于要想控制水幕就得有电磁阀,这玩意得用单片机控制。可我并无怎么学过这玩意。html
我回去看了些书,发现比我预想的还要麻烦很多。单片机上限制多,想法要实现有难度。拖延了两天,写出了这个程序的第一版。前端
#include<REG52.H>
#include<INTRINS.H>
//本例采用89C52, 晶振为11.0592MHZ
#define GPIO_KEY P0 //独立键盘用P1口 即为之后的输入
#define GPIO_LED P1 //led使用P0口 led模拟喷嘴 和之后的喷嘴一一对应
#define GPIO_NUM P2 //
//这里的n是持续时间,自己别踩白块的某个喷头持续时间是必定的,可是为了之后的多样性,将其设为变量
unsigned char last_time=250;
unsigned char left_time=0;
unsigned char cout=0;
unsigned char cjudge=0;//代断定的数组下标比输出数组下标小1
bit success=0;//成功标志位初始化为0
bit dead_flag=0;
//定义咱们要读取的数组 由于相似于P1,GPIP_KEY的数据类型能够转化为unsighed char,一组输出既能够用这样的一个unsigned char 表示
unsigned char code block_map[]={ //12组输出
//0x00,//开始位为0
0x01,0x08,0x04,0x08,
0x01,0x04,0x02,0x01,
0x02,0x04,0x01,0x08,
0x01,0x02,0x04,0x08,
0x02,0x04,0x02,0x01,
0x02,0x04,0x01,0x08,
0x01,0x02,0x04,0x08,
0x01,0x04,0x02,0x01,
0x02,0x04,0x01,0x08,
0x01,0x02,0x04,0x08,
0x02,0x04,0x02,0x01,
0x02,0x04,0x01,0x08,
0x00
};
unsigned char code DIG_CODE[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71};
//0~F段码 00111111 00000110 01011010 01001111
//0:0011_1111
unsigned char n=3000;
//unsigned char code block_hold_map[]={};//12组输出,每组输出的持续时间
unsigned char keyscan();
void judge(unsigned char);
void Delay10ms(unsigned int c);
//这个中断用于控制一组输出的持续时间
void int0() interrupt 1 //采用中断0 控制节拍
{
TH0=0xd8;
TL0=0xef;//相隔10000us
n--;
if(n==0){
cout++;
cjudge++;//到了时间则输出更新
if(success==0){
dead_flag=1;//时间已到而未断定成功,游戏结束
}
else{
success=0;//若是断定成功,则将标志位从新置0,准备下一周期的断定
}
}
}
//毫秒延时的子程序
void delay(unsigned char a) //毫秒延时
{
unsigned char b=1000;
while(--a){
b=1000;
while(--b);
}; //采用while(--a) 不要采用while(a--);
}
//主程序入口
void main(){
unsigned char temp;
unsigned char keyValue;
//计时器初始化
TMOD&=0x0f;
TMOD|=0x01;
TH0=0xd8;TL0=0xef;
IE=0x82;
TR0=1;
play:
while(1){
//dead_flag为1表明游戏失败 或者 游戏还未开始
if(dead_flag==1){
GPIO_LED=0x0f;
delay(250);delay(250);delay(250);delay(250);
GPIO_LED=0x00;
delay(250);delay(250);delay(250);delay(250);
}
//游戏正在进行
else{
temp=block_map[cout];
if(temp==0x00){//碰到告终束符,则竖起死亡flag,游戏结束
//在这里能够添加的功能是显示最终成绩
cout=1;
cjudge=1;
}
else{//在正常输出的阶段
TR0=1;
GPIO_LED=temp;
while(n!=0){
keyValue= keyscan();
judge(keyValue);
}
TR0=0;
n=3000;
}
}
}
}
unsigned char keyscan(){
unsigned char keyValue = 0 , i; //保存键值
//--检测按键1--//
if (GPIO_KEY != 0xFF) //检测按键K1是否按下
{
keyValue = GPIO_KEY;
//i = 0;
//while ((i<50) && (GPIO_KEY != 0xFF)) //检测按键是否松开 延时50ms或者松开了按键以后退出
//{
// Delay10ms(1);
// i++;
//}
}
else{
keyValue=0xFF;
}
return keyValue; //将读取到键值的值返回
}
void judge(unsigned char keyValue){
if(keyValue==0xFF){
Delay10ms(1);
return; }
else if(keyValue==~block_map[cout]){
success=1;
GPIO_NUM=~DIG_CODE[cout];
}
else{
success=0;
}
}
void Delay10ms(unsigned int c) //偏差 0us
{
unsigned char a, b;
//--c已经在传递过来的时候已经赋值了,因此在for语句第一句就不用赋值了--//
for (;c>0;c--)
{
for (b=38;b>0;b--)
{
for (a=130;a>0;a--);
}
}
}
代码是在52单片机上跑的,其实程序挺简单,若是我老老实实地画程序流程图,就是小一天的工做量。麻烦就是在单片机的定时器和输入检测。java
因为尚未实物,咱们就只能用单片机去给负责检查的老师看。老师当时的表情是很嫌弃的。。python
无论怎样,这说明了咱们仍是干活的,因此项目也得以成活了。看起来我接下来的活也比较轻松了,毕竟主体的逻辑应该不会大改,无非是让单片机输出音乐和添加别的一些功能了。可是忽然有一天,老上司跟我说董啊,咱们不能这么玩了,由于咱们的核心部件,控制水流的电磁阀,单价实在是过高了。以咱们要求的精度,可能一百元以上才能知足需求。这仍是最低报价。而要造成水幕,没有三四十个估计够呛。mysql
转眼到了十一,我回家的时候。老上司给我打了个电话,跟我说,嗨嗨嗨董我有一个新的Idea,又跟我说她在某某展会上看到了‘可爱的能够弹奏的苹果’,咱们也能够实现啊。jquery
那好吧,首先要给单片机加上侦测到触摸的功能是吧。。这个难了咱们一段时间,可是后来在另外一种单片机Arduino上找到了解决方案,简单地说,是在单片机几个模拟输入口上每个都连出来一个大电阻,电阻另外一端接5V电压。这样在正常状况下这个端口上的电压是最大的,若是人手触碰了输入口的话,电压会有一个不太明显的降幅。这是能够检测出来的。git
另一个额外的收获是,Arduino的接口够丰富,咱们甚至能够自制一个简单的读卡器来播放一首歌。官方库在这里。
这时候咱们的想法已经变成了一个检测人输入-去播放音符的钢琴了。前面写的代码只能扔到垃圾堆里了。。因而我磨了两天,又写了这一版。
#include<SimpleSDAudio.h>
int InData1 = 0, InData2 = 0, InData3 = 0, InData4 = 0, InData5 = 0, InData0 = 0; //触摸输入值暂存
int TouchSensitivity = 30; //触摸灵敏度。0~1023,越大越不灵敏
char AudioFileName[16];
int noteDuration=1000;
// Create static buffer
#define BIGBUFSIZE (2*512) // bigger than 2*512 is often only possible on Arduino megas!
uint8_t bigbuf[BIGBUFSIZE];
void setup()
{
Serial.begin(9600);
for(int i = A0; i <= A5; i++)
{
pinMode(i, INPUT); //A0~A5端设置为输入
//digitalWrite(i, HIGH); //而且上拉
}
TIMSK0 &= !(1 << TOIE0);
SdPlay.setWorkBuffer(bigbuf, BIGBUFSIZE);
SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO);
}
void loop()
{
//读取全部引脚电压值,而且因为上拉电阻缘由,
//默认全部引脚为最高电平1023,经过触摸拉低引脚电平。
//因此数值由1024-analogRead(A0);
InData0 = 1024 - analogRead(A0);
InData1 = 1024 - analogRead(A1);
InData2 = 1024 - analogRead(A2);
InData3 = 1024 - analogRead(A3);
InData4 = 1024 - analogRead(A4);
InData5 = 1024 - analogRead(A5);
//按照各类可能触发键盘事件
Serial.print("InData0=");
Serial.println(InData0);
Serial.print("InData1=");
Serial.println(InData1);
Serial.print("InData2=");
Serial.println(InData2);
Serial.print("InData3=");
Serial.println(InData3);
Serial.print("InData4=");
Serial.println(InData4);
Serial.print("InData5=");
Serial.println(InData5);
if(InData0 >= TouchSensitivity)
{
Serial.print("0");
SdPlay.setFile("1.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
if(InData1 >= TouchSensitivity)
{
Serial.print("1");
SdPlay.setFile("2.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
if(InData2 >= TouchSensitivity)
{
Serial.print("2");
SdPlay.setFile("3.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
if(InData3 >= TouchSensitivity)
{
Serial.print("3");
SdPlay.setFile("4.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
if(InData4 >= TouchSensitivity)
{
Serial.print("4");
SdPlay.setFile("5.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
if(InData5 >= TouchSensitivity)
{
Serial.println("5");
SdPlay.setFile("6.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
SdPlay.worker();
}
}
}
补充说明一下,这几个AFM文件是通过转换的音乐文件,对应相应的音调。
这时候在Arduino上运行效果已经有了。可是有一些比较棘手的问题没有解决:好比,若是人手一直摸着这根线的话,这个音乐会被重复播放。。这里仍是缺一个逻辑检测人手的放开。还有一点须要注意的是,Arduino兼容板的端口稳定性并不怎么好,各个端口的检测值就不同。。并且常常出现波动。
因而又对初版进行修改,边改边试,这里多说一句:好恶心。。由于有些时候你检查了不少遍代码,但是后来发现是稳定性问题。。
代码有些地方很冗余,是须要甄别、批判的
#include<SimpleSDAudio.h>
int InData1 = 0, InData2 = 0, InData3 = 0, InData4 = 0, InData5 = 0, InData0 = 0; //touch value
int flag0=1,flag1=1,flag2=1,flag3=1,flag4=1,flag5=1;//
int empty[5]={0,0,0,0,0};
int TouchSensitivity = 23; //0~1023
char AudioFileName[16];
//follow state is going to
int sub=0;
int a0[3]={0};
int test=0;
const int test_time=100;
const int empty_counter=10;
int flags=0;
const int flag_counters=10;
// Create static buffer
#define BIGBUFSIZE (2*512) // bigger than 2*512 is often only possible on Arduino megas!
uint8_t bigbuf[BIGBUFSIZE];
/* int i=23; int j=28; int testother(int cur){ i=23; j=28; while(i<cur){ if(analogRead(i)<1000) { return 0; } i++; } while(j>cur){ if(analogRead(j)<1000) { return 0; } j--; } return 1; } */
void setup()
{
Serial.begin(9600);
for(int i = A0; i <= A5; i++)
{
pinMode(i, INPUT); //A0~A5端设置为输入
//digitalWrite(i, HIGH); //而且上拉
}
TIMSK0 &= !(1 << TOIE0);
SdPlay.setWorkBuffer(bigbuf, BIGBUFSIZE);
if(! SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO)){
Serial.println(SdPlay.getLastError());
}
else Serial.println("initial complete");
}
void loop()
{
//读取全部引脚电压值,而且因为上拉电阻缘由,
//默认全部引脚为最高电平1023,经过触摸拉低引脚电平。
//因此数值由1024-analogRead(A0);
InData0 = 1024 - analogRead(A0);
InData1 = 1024 - analogRead(A1);
InData2 = 1024 - analogRead(A2);
InData3 = 1024 - analogRead(A3);
InData4 = 1024 - analogRead(A4);
InData5 = 1024 - analogRead(A5);
//按照各类可能触发键盘事件
Serial.print("InData0=");
Serial.println(InData0);
Serial.print("InData1=");
Serial.println(InData1);
Serial.print("InData2=");
Serial.println(InData2);
Serial.print("InData3=");
Serial.println(InData3);
Serial.print("InData4=");
Serial.println(InData4);
Serial.print("InData5=");
Serial.println(InData5);
/*if the system detector that there is no signal during one time,then it give the everyone right*/
if(InData0<30&&InData1<30&&InData2<30&&InData3<30&&InData4<30&&InData5<30)
{
flags++;
if(flags==flag_counters){
flags=0;
flag0=flag1=flag2=flag3=flag4=flag5=1;
Serial.println("no keys on");
}
}
else{
flags=0;
}
Serial.print("flags=");
Serial.println(flags);
if(InData0 >= TouchSensitivity)
{
Serial.print("0");
if(flag0){
flag0=0;
SdPlay.setFile("1.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A1)>1000&&analogRead(A2)>1000&&analogRead(A3)>1000&&analogRead(A4)>1000&&analogRead(A5)>1000)){
flag0=1;
Serial.println("other key!");
break;
}
if(analogRead(A0)>1020){
empty[0]++;
if(empty[0]==empty_counter){
empty[0]=0;
flag0=1;
Serial.println("you have put away your hand!");
break;
}
}
else{
empty[0]=0;
}
}
SdPlay.worker();
}
}
}
if(InData1 >= TouchSensitivity)
{
Serial.print("1");
if(flag1){
flag1=0;
SdPlay.setFile("2.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A0)>1000&&analogRead(A2)>1000&&analogRead(A3)>1000&&analogRead(A4)>1000&&analogRead(A5)>1000)){
flag1=1;
break;
}
if(analogRead(A1)>1020){
empty[1]++;
if(empty[1]==empty_counter){
empty[1]=0;
flag1=1;
Serial.println("you have put away your hand from 1");
break;
}
}
else{
empty[1]=0;
}
//if you move your hand from this key away during the note is played,then make flag true
}
SdPlay.worker();
}
}
}
if(InData2 >= TouchSensitivity)
{
Serial.print("2");
if(flag2){
flag2=0;
SdPlay.setFile("3.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A1)>1000&&analogRead(A0)>1000&&analogRead(A3)>1000&&analogRead(A4)>1000&&analogRead(A5)>1000)){
flag2=1;
break;
}
if(analogRead(A2)>1020){
empty[2]++;
if(empty[2]==empty_counter){
empty[2]=0;
flag2=1;
Serial.println("you have put away your hand!");
break;
}
}
else{
empty[2]=0;
}
/* if(analogRead(A2)>1000){ flag2=1; } else if(analogRead(A2)<1000&&flag2){ break; } */
}
SdPlay.worker();
}
}
}
if(InData3 >= TouchSensitivity)
{
Serial.print("3");
if(flag3){
flag3=0;
SdPlay.setFile("4.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A1)>1000&&analogRead(A2)>1000&&analogRead(A0)>1000&&analogRead(A4)>1000&&analogRead(A5)>1000)){
flag3=1;
break;
}
if(analogRead(A3)>1020){
empty[3]++;
if(empty[3]==empty_counter){
empty[3]=0;
flag3=1;
Serial.println("you have put away your hand!");
break;
}
}
else{
empty[3]=0;
}
/* if(analogRead(A3)>1000){ flag3=1; } else if(analogRead(A3)<1000&&flag3){ break; }*/
}
SdPlay.worker();
}
}
}
if(InData4 >= TouchSensitivity)
{
Serial.print("4");
if(flag4){
flag4=0;
SdPlay.setFile("5.AFM");
SdPlay.play();
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A1)>990&&analogRead(A2)>990&&analogRead(A3)>990&&analogRead(A0)>990&&analogRead(A5)>990)){
flag4=1;
break;
}
if(analogRead(A4)>1020){
empty[4]++;
if(empty[4]==empty_counter){
empty[4]=0;
flag4=1;
Serial.println("you have put away your hand!");
break;
}
}
else{
empty[4]=0;
}
/*if(analogRead(A4)>1000){ flag4=1; } else if(analogRead(A4)<1000&&flag4){ break; }*/
}
SdPlay.worker();
}
}
}
if(InData5 >= TouchSensitivity)
{
Serial.println("5");
SdPlay.setFile("6.AFM");
SdPlay.play();
if(flag5){
flag5=0;
while(!SdPlay.isStopped()) {
test++;
if(test==test_time){
test=0;
if(!(analogRead(A1)>1000&&analogRead(A2)>1000&&analogRead(A3)>1000&&analogRead(A4)>1000&&analogRead(A0)>1000)){
flag5=1;
break;
}
if(analogRead(A5)>1020){
empty[5]++;
if(empty[5]==empty_counter){
empty[5]=0;
flag5=1;
Serial.println("you have put away your hand!");
break;
}
}
else{
empty[5]=0;
}
/*if(analogRead(A5)>1000){ flag5=1; } else if(analogRead(A5)<1000&&flag5){ break; }*/
}
SdPlay.worker();
}
}
}
}
简单地说,新增长的部分就是在播放的期间检测别的按键的值,若是有的话,就跳出当前音乐的播放。
当初还出了一件特变尴尬的事情,咱们在寒假以前其实就已经把这个Demo作出来了,可是后来在寒假管理人员清理的时候被看成垃圾处理掉了。。谁让咱们当初给老师检查以后就开开心心跑了,没有把它锁柜子里。Sigh。。
有一天老上司又跟我打电话,说董啊,这个东西也许咱们还得再改改。我说咋了,她说这东西弹奏的时候后一个音符会打乱第二个音符,这个并非咱们想要的效果。还有啊,候妈咱们提了一些建议,好比说:PM2.5上传?温湿度上传?哎呀,反正都要有的啦。。
作乙方好苦啊哭。。。
这些东西想要搞,那就得能联网对吧。。并且还得过学校的网关。WTF。真的不是在为难单片机么。
把wifi模块接上其实就已是一件使人蛋碎的过程。咱们选用的wifi模块是esp8266,我还记得当初用的这个驱动库
照着说明书接了以后,仍是有问题。后来换了好几个库,都很差用。后来转回来,把原文中提到的两个端口2和3试探性地换成了别的口,竟然成了。。。
网关的问题也解决了,我去寻找了一下校园网登录的脚本,发现基本原理实际上是向网关发送一个POST请求。因而我又去下了一个WireShark,而后截到了这个请求
登录网关所发送的数据 POST / HTTP/1.1 Host: 10.3.8.211 Connection: keep-alive Content-Length: 47 Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Origin: http://10.3.8.211 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 BIDUBrowser/6.x Safari/537.36 Content-Type: application/x-www-form-urlencoded Referer: http://10.3.8.211/ Accept-Encoding: gzip,deflate Accept-Language: zh-CN,zh;q=0.8 Cookie: myusername=2012211289; pwd=260077; username=2012211289; smartdot=260077 DDDDD=2012211289&upass=260077&savePWD=0&0MKKey=
当初作的wireshark笔记也贴一下,知足一下个人虚荣心嘛~
【wireshark 过滤规则】
1.例子:
ip.src eq 192.168.1.107 or ip.dst eq 192.168.1.107
或者
ip.addr eq 192.168.1.107 // 都能显示来源IP和目标IP
2.端 口
例子:
tcp.port eq 80 // 无论端口是来源的仍是目标的都显示
tcp.port == 80
tcp.port eq 2722
tcp.port eq 80 or udp.port eq 80
tcp.dstport == 80 // 只显tcp协议的目标端口80
tcp.srcport == 80 // 只显tcp协议的来源端口80
3.协议
tcp udp arp icmp http ftp dns ip
6.http 模式
过滤
例子:
http.request.method == "GET"
http.request.method == "POST"
http.request.uri == "/img/logo-edu.gif"
http contains "GET"
http contains "HTTP/1."
// GET包
http.request.method == "GET" && http contains "Host: "
http.request.method == "GET" && http contains "User-Agent: " // POST包
http.request.method == "POST" && http contains "Host: "
http.request.method == "POST" && http contains "User-Agent: " // 响应包
http contains "HTTP/1.1 200 OK" && http contains "Content-Type: "
http contains "HTTP/1.0 200 OK" && http contains "Content-Type: "
一 定包含以下
Content-Type:
例子:
udp.length == 26 这个长度是指udp自己固定长度8加上udp下面那块数据包之和
tcp.len >= 7 指的是ip数据包(tcp下面那块数据),不包括tcp自己
ip.len == 94 除了以太网头固定长度14,其它都算是ip.len,即从ip自己到最后
frame.len == 119 整个数据包长度,从eth开始到最后
eth ---> ip or arp ---> tcp or udp ---> da
ta
当时个人心里激动不已,最难的问题终于解决了!诶,等等,还有“哎呀,这个音符播放时会打断上一个的播放啊”这个问题呢。。个人第一想法是用线程去实现,可是尝试了一下网上少有的可怜的Arduino线程库以后发现并不行得通。
因而我作打死想去修改别人的库,就是前面提到的那个。个人想法是,既然第二个音符会打断第一个音符,是否是能够把第一个音符断点的后半部分加到第二个音符文件要播放的缓存区里面。。
我还专门去查了一下音乐的相关知识和WAV的文件格式(AFM其实就是它的单片机版本),欣喜地发现靠四则运算应该是能够解决的。
后来尝试的时候发现库的主体是用汇编写的。并且基本没有什么注释。。。。那个时候咱们已经上过微机原理与接口这门课,汇编我也会一些。但是后来发现本身想尝试去修改仍是太难了。
这几天实际上是人最很差的日子,最后的检查就要到了,咱们作的基本上和三个月前没有什么该进。而不少组都已经作得比较成熟了。个人内心也很动摇。
事情仍是发生了起色,在咱们那天在大创基地里面焊线的时候,我看到了一个盒子,上面写着Raspberry,出于好奇,我就搜索了这个玩意。。结果发现。。这个卡片大小的“单片机”,竟然跑的是Liunx!
这意味着这个东西是一台完整的拥有操做系统的计算机,并且,我可使用Python!
这里说明一点,其实以前我对Python并没有多少认识,只是见识过一些方便好用的Python脚本。好比我以前提到的在网上看到的网关登陆,就是一个鲜明、好看的Python实例。
那一天晚上咱们正好开会,我忍不住把这个发现告诉了个人老上司。老上司心情也很差,随意说了一句,那你动手去作它啊。我怔了一下,内心又有些犹豫。咱们如今的虽然有些简陋,可是已经作了大半了,真的是要从新推翻重来么,并且,时间在这里摆着,来得及么。
我回去仔细想了想,觉着剩下的那些任务用Arduino也不是不能作,可是可靠性堪忧。颇有可能一个小问题就把咱们拖死在上面了。衡量了一下以后,我决定仍是把以前的代码都推翻,在树莓派上从新用Python来写。
次日我就钻到了大创底下,借了基本树莓派的书,用了小半天时间把它跑起来。边玩边赞叹,HDMI,3.5mm,USBhub,这些东西一个都很多。接上显示器你根本想象不来它的本体其实只有身份证那么大。
个人想法是,获取输入的那一部分工做依然由Arduino来作,而后经过Serial将消息传给树莓派,由树莓派实现播放文件的工做。Arduino简化后的代码我就不贴出来了,看看树莓派的。
import serial
import pygame
s=serial.Serial('/dev/ttyUSB0',9600)
pygame.mixer.init()
t=[]
t1=pygame.mixer.Sound("/home/pi/ogg/1.ogg")
t2=pygame.mixer.Sound("/home/pi/ogg/2.ogg")
t3=pygame.mixer.Sound("/home/pi/ogg/3.ogg")
t4=pygame.mixer.Sound("/home/pi/ogg/4.ogg")
t5=pygame.mixer.Sound("/home/pi/ogg/5.ogg")
t6=pygame.mixer.Sound("/home/pi/ogg/6.ogg")
t7=pygame.mixer.Sound("/home/pi/ogg/7.ogg")
t.append(t1);
t.append(t2);
t.append(t3);
t.append(t4);
t.append(t5);
t.append(t6);
t.append(t7);
while 1:
c=s.read()
print c
t[int(c)].play()
有没有发现。很是简单!其中比较重要的一个类是pygame,这里有它的文档。
测试了一下,状况很不错。咱们都挺开心的。老上司说咱们如今的任务是把水加上。咱们以为实现也应该没有什么问题,如今的输入模式是人手碰导线。咱们以前测试过,若是把水浇在导线头上,用手去碰水,也是能够响应的!
结果仍是发现了一个灰常灰常致命的问题!
问题就出如今水泵上!
由于水泵工做时和人同样自己就是接地的,水泵在工做中其实就会扮演了人的角色。。。这还输入?都用不着输入了,8个输入口所有都响应了。
咱们还尝试想设计各类模型,基本的想法是让水在中间断流。。可是后来的结论是,除非作到绝对的严密性,不然不可能。
【留图】
那一次的验收也很是不开心。。唉不说了。
如今怎么办?个人心基本上是崩溃的。。。你们都快哭了。
费劲了这么多心思,到最后却要放弃了么?
咱们纠结了好久,有人提出了折衷的方案,就是光传感。原理就是检测到物体在某个范围以内就触发低电平。
我其实内心是不情愿的,由于这个违背了和水的“直接交互”。我和云鹏内心已经萌生退意了,我建议,干脆弃了得了,当时确实有不少人已经放弃了项目。但是老上司不一样意。
考虑到咱们的水竖琴大小问题,我没有继续使用Arduino来作传感部分了,而是直接把光传感器接到了树莓派上(这实际上是致使了一些小问题的,后面再说)。
import RPi.GPIO as gpio
import time
import pygame
MAX_TIME=5000
gpio.setwarnings(False)
gpio.setmode(gpio.BOARD)
time.sleep(1)
pin=[]
flag=[]
dur=[]
pin.append(7)
pin.append(11)
pin.append(12)
pin.append(13)
pin.append(15)
pin.append(16)
pin.append(18)
pin.append(22)
pygame.mixer.init()
t1=pygame.mixer.Sound("/home/pi/ogg/1.ogg")
t2=pygame.mixer.Sound("/home/pi/ogg/2.ogg")
t3=pygame.mixer.Sound("/home/pi/ogg/3.ogg")
t4=pygame.mixer.Sound("/home/pi/ogg/4.ogg")
t5=pygame.mixer.Sound("/home/pi/ogg/5.ogg")
t6=pygame.mixer.Sound("/home/pi/ogg/6.ogg")
t7=pygame.mixer.Sound("/home/pi/ogg/7.ogg")
t8=pygame.mixer.Sound("/home/pi/ogg/8.ogg")
t=[]
t.append(t1)
t.append(t2)
t.append(t3)
t.append(t4)
t.append(t5)
t.append(t6)
t.append(t7)
t.append(t8)
for i in range(0,7):
flag.append(0)
dur.append(0)
gpio.setup(pin[i],gpio.IN)
print('makey2 has started')
while 1:
for i in range(0,7):
if gpio.input(pin[i])==0:
if flag[i]==0:
t[i].play()
flag[i]=1
print(pin[i])
else:
if flag[i]==1:
dur[i]+=1
if dur[i]==MAX_TIME:
flag[i]=0
dur[i]=0
注意,上一个方案树莓派和Arduino的结合中,树莓派只负责了播放器音乐的部分,因为线程的独立性,咱们已经不用担忧第二个音符打断第一个音符这个问题了。最后一个循环检测中有额外的逻辑,是为了让一段时间以内音乐只能奏响一次。
接下来,我还有数据上传和网站的工做要作。
树莓派上的代码
# -*- coding: utf-8 -*-
import socket
import json
import RPi.GPIO as gpio
import time
pin=11
gpio.setwarnings(False)
gpio.setmode(gpio.BOARD)
time.sleep(1)
data=[]
def delay(i): #20*i usdelay
a=0
for j in range(i):
a+1
j=0
#start work
while 1:
time.sleep(0.05)
gpio.setup(pin,gpio.OUT)
gpio.output(pin,gpio.HIGH)
time.sleep(0.1)
gpio.output(pin,gpio.LOW)
time.sleep(0.05)
gpio.output(pin,gpio.HIGH)
i=1
i=1
#wait to response
gpio.setup(pin,gpio.IN)
while gpio.input(pin)==1:
continue
while gpio.input(pin)==0:
continue
while gpio.input(pin)==1:
continue
#get data
while j<40:
k=0
while gpio.input(pin)==0:
continue
while gpio.input(pin)==1:
k+=1
if k>100:break
if k<5:
data.append(0)
else:
data.append(1)
j+=1
print "Sensor is working"
#get temperature
humidity_bit=data[0:8]
humidity_point_bit=data[8:16]
temperature_bit=data[16:24]
temperature_point_bit=data[24:32]
check_bit=data[32:40]
humidity=0
humidity_point=0
temperature=0
temperature_point=0
check=0
for i in range(8):
humidity+=humidity_bit[i]*2**(7-i)
humidity_point+=humidity_point_bit[i]*2**(7-i)
temperature+=temperature_bit[i]*2**(7-i)
temperature_point+=temperature_point_bit[i]*2**(7-i)
check+=check_bit[i]*2**(7-i)
tmp=humidity+humidity_point+temperature+temperature_point
if check==tmp:
print "temperature is ", temperature,"wet is ",humidity,"%"
# timestamp=time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))
obj={'temperature':temperature,'humidity':humidity}
encodejson=json.dumps(obj)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('10.110.211.207', 10009))
time.sleep(2)
sock.send(encodejson)
sock.close()
else:
print "something is worong the humidity,humidity_point,temperature,temperature_point,check is",humidity,humidity_point,temperature,temperature_point,check
网站的部分
还好我以前自学过一点BootStrap和php,这个并无太难倒我。
最后实现的网站在这里。(如今没有新的数据上传上去,显示会有些不正常)
信息接受部分
<?php //这个版本能够保存收集到的信息 在data.txt底下 //确保在链接客户端时不会超时 //链接数据库 include_once '/conn/conn.php'; set_time_limit(0); date_default_timezone_set('PRC'); $ip = '23.226.230.106'; $port = 10011; /* +------------------------------- * @socket通讯整个过程 +------------------------------- * @socket_create * @socket_bind * @socket_listen * @socket_accept * @socket_read * @socket_write * @socket_close +-------------------------------- */ /*---------------- 如下操做都是手册上的 -------------------*/ if(($sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) { echo "socket_create() cause:".socket_strerror($sock)."\n"; } if(($ret = socket_bind($sock,$ip,$port)) < 0) { echo "socket_bind() cause:".socket_strerror($ret)."\n"; } if(($ret = socket_listen($sock,4)) < 0) { echo "socket_listen() cause:".socket_strerror($ret)."\n"; } do { if (($msgsock = socket_accept($sock)) < 0) { echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n"; break; } else { //发到客户端 $msg ="I am server\n"; socket_write($msgsock, $msg, strlen($msg)); $buf = socket_read($msgsock,8192); $talkback = "$buf"; echo $talkback; // $datafile='data.txt' // $fp=fopen($datafile,a); // fwrite($fp,$talkback); // fclose($fp); // //将talkback中的值写入到数据库中去 // $data=explode(" ",$talkback); // print_r($data); $data=json_decode($talkback); var_dump($data); $currentday=date("Y-m-d-H-i-s"); $query="insert into pi values ('$currentday',$data->temperature,$data->humidity,0);"; $result=mysql_query($query); var_dump($result); } //echo $buf; socket_close($msgsock); } while (1); socket_close($sock); ?>
conn.php
<?php $con = mysql_connect("localhost","root",""); if (!$con) { die('Could not connect: ' . mysql_error()); } mysql_select_db("data",$con); ?>
前端
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <!-- Bootstrap --> <link href="css/bootstrap.css" rel="stylesheet"> <!--viewport--> <meta name="viewport" content="width=device-width, initial-scale=1,height=device-height"> <!--shit jianmingzihaoyanghuo--> <link rel="stylesheet" href="css/shit.css"> <!--Chartjs--> </head> <script> var jsdata=new Array(); var jsdate=new Array(); var date=""; var temp=0; //---3-26---- var humidity; var temperature; //---end of 3-26 </script> <?php date_default_timezone_set('PRC'); $currentday=date("Y-m-d"); // echo '当前日期是'.$currentday; $yesterday_time=strtotime("-8 days"); $yesterday=date("Y-m-d",$yesterday_time); // echo '昨天的日期是'.$yesterday; include_once 'conn/conn.php'; $query="select * from pmdata where date >'".$yesterday."' order by date;"; // echo $query; $result=mysql_query($query); while($row =mysql_fetch_array($result,MYSQL_ASSOC)){ ?> <script> temp=<?php echo $row['pm']?>; date="<?php echo $row['date']?>"; jsdata.push(temp); jsdate.push(date); </script> <?php } ?> <?php $query2="select max(date),temperature,humidity from pi;"; $result2=mysql_query($query2); $row2=mysql_fetch_array($result2,MYSQL_ASSOC); ?> <script> humidity=<?php echo $row2['humidity'] ?>; temperature=<?php echo $row2['temperature'] ?>; </script> <!-- //数据图表 --> <!-- <div style="width: 50%"> <canvas id="canvas" height="450" width="600"></canvas> </div> --> <!-- //新的数据图表 本周的天气 --> <!-- <h1>你好,世界!</h1> --> <div id="carousel-example-generic" class="carousel slide" data-ride="carousel" id="slide"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <li data-target="#carousel-example-generic" data-slide-to="1"></li> <li data-target="#carousel-example-generic" data-slide-to="2"></li> </ol> <!-- Wrapper for slides --> <div class="carousel-inner" role="listbox" id="slide"> <div class="item active slide1" id="slide1"> <div class="canvas_container"> <canvas id="canvas" height="50%" width="100%"></canvas> </div> <div class="carousel-caption"> <h2 font="40px">近一周数据</h2> <!-- <canvas style="z-index:5" id="canvas" height="450" width="600"></canvas> --> </div> </div> <div class="item slide2" > <div class="container pic_container"> <h1 id="todaydata"> </h1> </div> <div class="carousel-caption"> <h2>今日最新数据</h2> </div> </div> <div class="item slide3" > <div class="container pic_container"> <h1 id="temperature"></h1> <h1 id ="humidity"></h1> </div> <div class="carousel-caption"> <h2>目前室内温度 湿度</h2> </div> </div> <!-- Controls --> <a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span> <span class="sr-only">Previous</span> </a> <a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"> <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> <span class="sr-only">Next</span> </a> </div> <script src="js/Chart.js"></script> <script src="js/myChart.js"></script> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="js/bootstrap.min.js"></script> </body> </html>
Chart.js是一个Bootstrap推荐的图表库。
myChart.js
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
var lineChartData = {
labels:jsdate,
datasets : [
{
fillColor : "rgba(220,220,220,0.5)",
strokeColor : "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data:jsdata
}
]
}
// lineChartData.labels=jsdate;
// lineChartData.datasets.data=jsdata;
var afunction =function(){
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx).Line(lineChartData, {
responsive : true
});
}
var bfunction=function(){
var todaypm=jsdata[jsdata.length-1];
document.getElementById("todaydata").innerHTML=todaypm+"\npm";
}
var cfunction=function(){
document.getElementById("temperature").innerHTML=temperature+"℃";
document.getElementById("humidity").innerHTML=humidity+"%";
}
// var dfunction=function(){
// document.getElementById("slide1").height=window.screen.height;
// document.getElementById("slide2").height=window.screen.height;
// document.getElementById("slide3").height=window.screen.height;
// }
window.onload = function(){
afunction();
bfunction();
cfunction();
// dfunction();
}