2012/04/05

Arduino練習:猜猜哪一個

有5個開關,一開始會隨機選出一個當做正確答案,然後讓使用者猜猜看按按看哪個才是對的,若按中正確的就點亮LED;另外有個開關是重置鍵。

接線:
從Arduino板的5V與GND接到麵包板。

Arduino腳位2、3、4、5、6、9,各接到開關的右上腳,
開關的右下腳接電阻後接地,
開關的左下腳接5V。

Arduino腳位8接電阻後接LED長腳,LED短腳接地。



程式碼如下(可到這裡下載):

/* inpin1到inpin5,讓使用者猜猜哪個才是正確的 */
int inpin1=2;
int inpin2=3;
int inpin3=4;
int inpin4=5;
int inpin5=6;

int led=8; /* LED的腳位 */
int inpinReset = 9; /* 重置 */

int randnum; /* 代表哪個才是正確答案 */

/* 重置,以亂數從新選出一個正確答案 */
/* 並熄滅LED */
void reset() {
        randnum = random(1, 6);
        digitalWrite(led, LOW);
        Serial.print("randnum=");
        Serial.println(randnum);
        delay(500);
}

void setup() {
    pinMode(inpin1, INPUT);
    pinMode(inpin2, INPUT);
    pinMode(inpin3, INPUT);
    pinMode(inpin4, INPUT);
    pinMode(inpin5, INPUT);


    pinMode(led, OUTPUT);
    pinMode(inpinReset, INPUT);
    Serial.begin(115200);
   
    reset();
}

void loop() {
    /* 檢查使用者有沒有按下正確的答案(開關) */
    switch (randnum) {
        case 1:
            if(digitalRead(inpin1) == HIGH){
                digitalWrite(led, HIGH);
                Serial.println("inpin1");
            }
            break;
        case 2:
            if(digitalRead(inpin2) == HIGH){
                digitalWrite(led, HIGH);
                Serial.println("inpin2");
            }
            break;
        case 3:
            if(digitalRead(inpin3) == HIGH){
                digitalWrite(led, HIGH);
                Serial.println("inpin3");
            }
            break;
        case 4:
            if(digitalRead(inpin4) == HIGH){
                digitalWrite(led, HIGH);
                Serial.println("inpin4");
            }
            break;
        case 5:
            if(digitalRead(inpin5) == HIGH){
                digitalWrite(led, HIGH);
                Serial.println("inpin5");
            }
            break;
    }

    /* 若按下inpinReset就重置 */
    if(digitalRead(inpinReset) == HIGH){
        Serial.println("inpinReset");
        reset();
    }
}

上面這份程式碼,還有很多可以改進的地方。

首先是亂數的部分,應該要先用randomSeed(analogRead(0)),才不會每次產生出來的亂數序列都是一樣的。

然後是switch的部份,可利用陣列儲存腳位,就不需要重複寫那麼多個case了。

另外,開關會有bounce問題,不過就這裡的情況來說,沒關係。

另外,可以增加「最多猜三次」這樣的行為,三次都猜錯後就失敗、重置。

可以增加揚聲器,發出猜對、猜錯的音效

40 comments:

  1. 請問一下,我想寫一個程式,然後有兩個不同的執行迴圈,我想在序列埠監控視窗輸入不同數字然後執行相對應的迴圈,本來想說試試看switch case 但我用了以後程式卻只能執行一次,而不能重複持續迴圈,可否給小弟一點如何寫這程式的提點,拜託了!

    ReplyDelete
    Replies
    1. 大概是這樣子吧
      int x = 0;
      void loop(){
      switch(x){
      case 0:
      break;
      case 1:
      迴圈一
      break;
      case 2:
      迴圈二
      break;
      }
      }
      從序列埠接收數字,然後設定全域變數x,
      根據x的值,執行相對應的迴圈。

      Delete
  2. 第一次接觸有個地方看不太懂
    從Arduino板的5V與GND接到麵包板,接在上面的正負極是要幹嘛?
    看沒有其他接線接過去,還是說上面的正負極和下面的正負極是相通的?
    煩請指教

    ReplyDelete
    Replies
    1. 是的,如你所說。文中照片沒有秀出來。

      Delete
  3. 如果要把上面的按鈕改成紅外線遙控器上的按鈕要怎麼辦

    ReplyDelete
    Replies
    1. 請參考
      Arduino練習:紅外線傳送與接收
      http://yehnan.blogspot.tw/2013/05/arduino.html

      Delete
  4. 請問這電阻是接多少的阿?
    圖片看不太出來
    謝謝

    ReplyDelete
    Replies
    1. 開關的電阻是10k,
      LED的電阻是220。

      Delete
    2. 請問這電阻是怎麼知道該使用多少的啊?
      如果我沒有這些可以用哪些電阻來代替嗎?
      謝謝

      Delete
    3. 請參閱 Arduino小知識:為什麼LED需要串聯的電阻值是220 ohm?
      http://yehnan.blogspot.tw/2012/03/arduinoled220-ohm.html

      以這篇文章裡的電路而言,220 ohm可以換成150~300,應該都可以,不過會影響LED亮度。10k ohm可以換成8k~12k,應該都可以。

      Delete
  5. 亂數的部分,應該要先用randomSeed(analogRead(0))
    請問這句能否在解釋的詳細一點呢
    遇到亂數一直都跑同一個的問題
    謝謝~

    ReplyDelete
    Replies
    1. Arduino提供的是假亂數(pseudo-random),

      你把它想像成:Arduino內建有一長串的亂數值,呼叫random時、預設會從第一個拿出來給你,下次呼叫random時會拿第二個給你,若如此,每次都會得到相同的亂數序列。
      所以要先呼叫randomSeed,作用是跳到一長串亂數值的某個位置,然後從該位置開始。

      當腳位A0沒有接東西時,呼叫analogRead讀取的話,會讀到空氣,值不定。

      Delete
    2. 了解!!
      非常感謝

      Delete
  6. 請問一下 如果不只要隨機一個亂數
    想要改成每次隨機個數不同
    不知能怎麼改呢 謝謝!!

    ReplyDelete
    Replies
    1. > 每次隨機個數不同
      你問的是這一篇嗎?

      你說的是需求,不是問題。


      Delete
    2. 對 我的意思是要每次隨機個數不同
      好像找不到這一篇的文章耶~
      不知道要怎麼改才有辦法達成
      謝謝

      Delete
    3. 喔,了解。

      請參考這篇
      Arduino練習:Simon Says請你跟我這樣做
      http://yehnan.blogspot.tw/2012/02/arduinosimon-says.html

      Delete
  7. 請問一下,不能讓每個開關共電阻嗎?

    ReplyDelete
    Replies
    1. 這樣不就互相影響?

      Delete
  8. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 只要跳到控制馬達的部分就好,看不太懂隨機的寫法,馬達該怎麼控制我已經知道了

      Delete
    2. 不斷地呼叫random(1, 3);取得介於1~3之間的亂數,
      使用switch根據該亂數值,執行你想要的馬達控制指令。

      Delete
  9. #include

    Servo myservo ;
    Servo myservo1 ;

    void setup() {
    myservo.attach(9);
    myservo1.attach(10);

    }
    void p()
    {
    myservo.write(25);
    myservo1.write(5);

    }
    void st()
    {
    myservo.write(150);
    myservo1.write(65);

    }
    void sc()
    {
    myservo.write(25);
    myservo1.write(75);

    }
    void loop(){
    }


    ReplyDelete
    Replies
    1. 請問要加上什麼程式碼才能隨機執行這三個迴圈呢?

      Delete
    2. switch(random(1,3)){
      case 1:
      p();
      break;
      case 2:
      st();
      break;
      case 3:
      sc();
      break;
      }

      大概如此吧。

      Delete
    3. 感謝,我試試看

      Delete
    4. 他只會跳到st耶
      為什麼會這樣?

      Delete
    5. #include

      Servo myservo ;
      Servo myservo1 ;
      Servo myservo2 ;
      Servo myservo3 ;
      Servo myservo4 ;

      void setup() {
      myservo.attach(9); // 食指 9
      myservo1.attach(10); // 大拇指 10
      myservo2.attach(11); // 中指 11
      myservo3.attach(6); // 無名指 6
      myservo4.attach(5); // 小拇指 5
      }
      void p()
      {
      myservo.write(25);
      myservo1.write(5);
      myservo2.write(40);
      myservo3.write(20);
      myservo4.write(15);
      delay(200);
      }
      void st()
      {
      myservo.write(150);
      myservo1.write(65);
      myservo2.write(155);
      myservo3.write(160);
      myservo4.write(80);
      delay(200);
      }
      void sc()
      {
      myservo.write(25);
      myservo1.write(75);
      myservo2.write(40);
      myservo3.write(160);
      myservo4.write(90);
      delay(200);
      }
      void loop(){
      switch(random(1,3)){
      case 1:
      p();
      break;

      case 2:
      st();
      break;

      case 3:
      sc();
      break;
      }
      }

      Delete
    6. 這是我完整的程式碼

      Delete
  10. 您好,我們想要用手機利用wifi控制門鎖
    網路都連得上,但還是沒反應
    想請問是哪裡出了問題? 謝謝!

    ReplyDelete
    Replies
    1. 哪裡沒反應?
      再說清楚一點,執行的流程為何,看到什麼訊息。

      Delete
    2. 序列阜監控裡面會跑出
      Contacting ESP8266...
      Got response from ESP8266.
      Connecting to wifi...
      Wifi connected.
      Setting up server...
      Server ready.
      然後我們用app inventer做門鎖開關
      結果沒辦法控制
      http://imgur.com/nr6WTAj 這是我們app inventer的樣子

      Delete
    3. 程式在下面 謝謝~!

      Delete
    4. 你這程式是怎麼來的?

      裏頭的WiFiServer與WiFiClient,這是用來控制Arduino WiFi Shield (https://www.arduino.cc/en/Main/ArduinoWiFiShield) 的吧,可是你又用SoftwareSerial控制ESP8266,兩者不一樣啊。

      我看到你以at指令控制ESP8266、連上網,然後就不管它了。
      WiFiServer的部分,並沒有連接網路,自然就不會執行到if (client) {裡面去了。

      Delete
    5. 我們是初學者,我們為了專題想要做出用APP可以開電子鎖
      這些程式是從網路上合併下來的
      我們現在遇到了瓶頸,不知道程式哪部分需要改進

      Delete
    6. 你是用ESP8266,可繼續用at指令跟它溝通,命令它建立server,接受client的連線。

      加油吧。

      Delete
  11. #include
    #include
    #include

    bool debugMode = true;
    int lControl = 13;
    const String SSID="kuo";
    const String PASSWORD="19960624";
    int keyIndex = 0;

    int status = WL_IDLE_STATUS;

    WiFiServer server(80);

    bool wifiAndServerReady = false;
    String logText = "";
    SoftwareSerial ESP8266(4,5);

    void setup() {
    Serial.begin(9600);
    ESP8266.begin(9600);
    ESP8266.setTimeout(2000);
    }
    void loop() {
    delay(1000);
    if(wifiAndServerReady){
    String line=readLine();
    if(line!=""){
    log("ESP8266: "+line);
    }
    proxyToESP8266();
    }else{
    if(!connectToWifi()){
    log("Retry in 2 seconds...");
    delay(2000);
    }
    }

    WiFiClient client = server.available();
    if (client) {
    String currentLine = "";
    while (client.connected()) {
    if (client.available()) {
    char c = client.read();
    if(c == '\n') {
    if (currentLine.length() == 0) {
    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html");
    client.println();
    client.print("Value at A0 is ");
    client.print(analogRead(A0));
    client.print("\n"); //

    client.println();
    break;
    }
    else {
    currentLine = "";
    }
    }
    else if (c != '\n'){ //'\r'
    currentLine += c;
    }
    if (currentLine.endsWith("GET /H")) {
    digitalWrite(lControl, HIGH);
    }
    if (currentLine.endsWith("GET /L")) {
    digitalWrite(lControl, LOW);
    }
    }
    }
    client.stop();
    }
    }

    bool waitForResponse(int timeout,String keyword1,String keyword2=""){
    bool found = false;
    while(true){
    String response = readLine();
    found = findKeyword(response,keyword1) || findKeyword(response,keyword2);
    if(found || timeout<=0){
    break;
    }else{
    delay(10);
    timeout-=10;
    if(response!=""){
    timeout+=100;
    }
    }
    }
    return found;
    }

    bool connectToWifi(){
    wifiAndServerReady=false;
    log("Contacting ESP8266...");
    sendCmd("AT");
    if(waitForResponse(1000,"OK\r\n")){
    log("Got response from ESP8266.");
    log("Connecting to wifi...");
    sendCmd("AT+CWJAP=\""+SSID+"\",\""+PASSWORD+"\"");
    if(waitForResponse(3000,"OK\r\n")){
    log("Wifi connected.");
    log("Setting up server...");
    sendCmd("AT+CWMODE=3\r\n"); // configure as STA+AP
    sendCmd("AT+CIFSR\r\n"); // get ip address
    sendCmd("AT+CIPMUX=1");
    waitForResponse(1000,"OK\r\n");
    sendCmd("AT+CIPSERVER=1,80");
    if(waitForResponse(3000,"OK\r\n","no change\r\n")){
    log("Server ready.");
    wifiAndServerReady=true;
    }else{
    log("ERROR: Failed to set up server.");
    }
    }else{
    log("ERROR: Failed to connect to Wifi.");
    }
    }else{
    log("ERROR: Failed to contact ESP8266.");
    }
    return wifiAndServerReady;
    }

    void sendCmd(String cmdString){
    cmdString=cmdString+"\r\n";
    ESP8266.print(cmdString);
    ESP8266.flush();
    }

    void proxyToESP8266(){
    while (Serial.available()) {
    int inByte = Serial.read();
    ESP8266.write(inByte);
    }
    }

    void log(String msg){
    logText=logText+msg+"\n";
    if(debugMode){
    Serial.println(msg);
    }
    }

    void printLog(){
    Serial.print(logText);
    }

    bool findKeyword(String text,String keyword){
    if(keyword==""){
    return false;
    }
    return text.lastIndexOf(keyword)>-1;
    }

    String readLine(){
    String lineString="";
    while(ESP8266.available()){
    char inChar = ESP8266.read();
    lineString=lineString+String(inChar);
    if(inChar=='\n'){
    break;
    }
    }
    return lineString;
    }

    ReplyDelete