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();
}