0
2014年4月的一天,老老板用一顿饭把我带出来,告诉我她要参加大创,有个好的Idea,现在只有一只程序猿。作为一个典型的计院宅男,我不明白也不重视。但是饭已经吃了,嘴软了。我答应了。
最初的想法是关于家庭成员的互动APP。我们带着这个毫无价值的想法去找投资者侯老师。她仔细地听着,礼貌地说你的想法简直就是这样bull shit在大创中获得排名简直是梦话。
我们重新构思了几个方案。后来,我们的注意力集中在水幕上。这个想法似乎更具展示性。简言之,水幕版本的不要踩白块与真实版本的区别在于显示屏已经变成了水幕。我们谈论各种奇怪的想法。
我发誓我绝对可以写这个程序。事实上,我自己也没有底。因为如果你想控制水幕,你必须有一个电磁阀,它必须由单片机控制。但我没有学到太多。
1
我回去读了一些书,发现它比我预期的要麻烦得多。单片机有很多限制,很难实现这个想法。推迟了两天,写下了程序的初始版本。
#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单片机上跑的,其实程序挺简单,如果我老老实实地画程序流程图,就是小一天的工作量。麻烦就是在单片机的定时器和输入检测。
由于还没有实物,我们就只能用单片机去给负责检查的老师看。老师当时的表情是很嫌弃的。。
不管怎样,这说明了我们还是干活的,所以项目也得以成活了。看起来我接下来的活也比较轻松了,毕竟主体的逻辑应该不会大改,无非是让单片机输出音乐和添加别的一些功能了。但是突然有一天,老上司跟我说董啊,我们不能这么玩了,因为我们的核心部件,控制水流的电磁阀,单价实在是太高了。以我们要求的精度,可能一百元以上才能满足需求。这还是最低报价。而要形成水幕,没有三四十个估计够呛。
2
转眼到了十一,我回家的时候。老上司给我打了个电话,跟我说,嗨嗨嗨董我有一个新的Idea,又跟我说她在某某展会上看到了‘可爱的可以弹奏的苹果’,我们也可以实现啊。
那好吧,首先要给单片机加上的功能是吧。。这个难了我们一段时间,但是后来在另一种单片机Arduino上找到了解决方案,简单地说,是在单片机几个模拟输入口上每一个都连出来一个大电阻,电阻另一端接5V电压。这样在正常情况下这个端口上的电压是最大的,如果人手触碰了输入口的话,电压会有一个不太明显的降幅。这是可以检测出来的。
另外一个额外的收获是,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文件是经过转换的音乐文件,对应相应的音调。
3
这时候在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。。
4
有一天老上司又跟我打电话,说董啊,这个东西也许我们还得再改改。我说咋了,她说这东西弹奏的时候后一个音符会打乱第二个音符,这个并不是我们想要的效果。还有啊,候妈我们提了一些建议,比如说: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其实就是它的单片机版本),欣喜地发现靠四则运算应该是可以解决的。
后来尝试的时候发现库的主体是用汇编写的。而且基本没有什么注释。。。。那个时候我们已经上过微机原理与接口这门课,汇编我也会一些。可是后来发现自己想尝试去修改还是太难了。
这几天其实是人最不好的日子,最后的检查就要到了,我们做的基本上和三个月前没有什么该进。而很多组都已经做得比较成熟了。我的心里也很动摇。
5
事情还是发生了转机,在我们那天在大创基地里面焊线的时候,我看到了一个盒子,上面写着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个输入口全部都响应了。 我们还尝试想设计各种模型,基本的想法是让水在中间断流。。但是后来的结论是,除非做到绝对的严密性,否则不可能。 【留图】 那一次的验收也非常不开心。。唉不说了。
6
现在怎么办?我的心基本上是崩溃的。。。大家都快哭了。 费劲了这么多心思,到最后却要放弃了么? 我们纠结了很久,有人提出了折衷的方案,就是光传感。原理就是检测到物体在某个范围之内就触发低电平。
我其实心里是不情愿的,因为这个违背了和水的“直接交互”。我和云鹏心里已经萌生退意了,我建议,干脆弃了得了,当时确实有很多人已经放弃了项目。可是老上司不同意。
考虑到我们的水竖琴大小问题,我没有继续使用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的结合中,树莓派只负责了播放器音乐的部分,由于线程的独立性,我们已经不用担心第二个音符打断第一个音符这个问题了。最后一个循环检测中有额外的逻辑,是为了让。
7
接下来,我还有数据上传和网站的工作要做。 树莓派上的代码
# -*- 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