備忘錄_20160105(定位) 修改 回首頁

程式 2020-03-23 00:41:02 1584895262 100
Arduino UNO R3 練習-打卡系統

Arduino UNO R3 練習-打卡系統

介面提點

參考文章 https://makerpro.cc/2016/07/learning-interfaces-about-uart-i2c-spi/

初次使用提點

開發工具與函數庫

接線說明

  1. Arduino UNO R3

  2. W5100 擴展版(用SPI) 網路模組

    5V/3.3V ── 5V/3.3V (隱)
    GND ── GND (隱)
    SS/CS/SCS ── pin10 (隱) [SPI]
    MOSI/MO ── pin11 (隱) [SPI]
    MISO/MI ── pin12 (隱) [SPI]
    SCK/SCLK ── pin13 (隱) [SPI]
    Reset ── 未接
  3. 開關模組 (含電阻)

    SWITCH A 端點 ── 5V
    SWITCH B 端點 ── E 端點
    電阻10K歐姆 C 端點 ── E 端點
    電阻10K歐姆 D 端點 ── GND
    E 端點 ── pin2
  4. LED燈1 (上班)

    長腳 ── pin3
    短腳 ── GND
  5. LED燈2 (下班)

    長腳 ── pin4
    短腳 ── GND
  6. DS1302 時鐘模組

    VCC ── 5V
    GND ── GND
    CLK ── pin5
    DAT ── pin6
    RST ── pin7
  7. 無源揚聲器

    ── 5V
    ── GND
    Signal ── pin8
  8. RFID-RC522 模組(用SPI)

    3.3V ── 3.3V
    RST ── pinA0
    GND ── GND
    IRQ ── 未接
    MISO ── pin12 [SPI]
    MOSI ── pin11 [SPI]
    SCK ── pin13 [SPI]
    SDA ── pin9 [SPI]
  9. LCD 1602 I2C 顯示模組(用I2C) - 背後有一個旋鈕,可調整對比度。

    GND ── GND
    VCC ── 5V
    SDA ── pinA4 [I2C]
    SCL ── pinA5 [I2C]

test.ino

#include <SPI.h>               // W5100 擴展版(用SPI) 網路模組, RFID-RC522 模組(用SPI)
#include <Ethernet.h>          // W5100 擴展版(用SPI) 網路模組
#include <Wire.h>              // W5100 擴展版(用SPI) 網路模組
#include <DS1302.h>            // D1302 時鐘模組
#include <MFRC522.h>           // RFID-RC522 模組(用SPI) 
#include <LiquidCrystal_I2C.h> // LCD 1602 I2C 顯示模組(用I2C)

// W5100 擴展版(用SPI) 網路模組
byte baMAC[]={0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // physical mac address
char caServerName[]="www.somewhere.com.tw";
EthernetClient oEthernetClient;

// 開關模組 (含電阻) + LED燈,切換上班/下班
byte bButtonPin=2;
byte bUpDn=0; // 0=上班(up); 1=下班(dn);
byte bLedUpPin=3;
byte bLedDnPin=4;

// DS1302 時鐘模組
// DS1302 rtc([RST],[DAT],[CLOCK]); 三個pin
String strLastTimeSwitched=""; // 最後一次 自動切換 上班/下班 的時間
DS1302 rtc(7,6,5);

// 無源揚聲器
byte bTonePin=8;

// RFID-RC522 模組(用SPI)
// mifare card uid => 4 bytes, 7 bytes, 10 bytes
#define SDA_PIN_4_RFID 9
#define RST_PIN_4_RFID A0
String strUID="";
MFRC522 mfrc522(SDA_PIN_4_RFID, RST_PIN_4_RFID);

// LCD 1602 I2C 顯示模組(用I2C)
// Set the pins on the I2C chip used for LCD connections: addr,en,rw,rs,d4,d5,d6,d7,bl,blpol
long lTimeCurrent=0, lTimeLastClockShown=0, lTimeGap4Clock;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 設定 LCD I2C 位址

void setup() 
{
  Serial.begin(9600);
  Serial.println("Program Start");
  
  // 先設定 pin mode
  pinMode(bButtonPin, INPUT);
  pinMode(bLedUpPin, OUTPUT);
  pinMode(bLedDnPin, OUTPUT);
  pinMode(bTonePin, OUTPUT);
  
  // W5100 擴展版(用SPI) 網路模組
  Serial.println("Ethernet Init");
  SPI.begin();
  ledShowError();
  if(Ethernet.begin(baMAC)==0)
  {
    Serial.println("Failed to configure Ethernet using DHCP!");
    while(true); // 網路不通,直接當機!
  }
  ledRefresh();

  // DS1302 時鐘模組
  Serial.println("Clock Init");
  rtc.halt(false); // 設定時鐘為正常執行模式
  // 以下是設定時間的方法,在電池用完之前,只要設定一次就行了
  // rtc.writeProtect(false); //取消寫入保護,設定日期時要這行
  // rtc.setDOW(FRIDAY);      // 設定週幾,如FRIDAY
  // rtc.setTime(22, 18, 0);  // 設定時間 時,分,秒 (24hr format)
  // rtc.setDate(3, 1, 2020); // 設定日期 日,月,年  
  
  // RFID-RC522 模組(用SPI)
  Serial.println("MFRC522 Init");
  mfrc522.PCD_Init();

  // LCD 1602 I2C 顯示模組(用I2C)
  Serial.println("LCD Init");
  lcd.begin(16, 2);      // 初始化 LCD,一行 16 個字元,共 2 行
  lcd.clear();
  lcd.backlight();       // 開啟背光
  //lcd.noBacklight();   // 關閉背光
  lcd.setCursor(0, 0);   // 設定游標位置在第 1 行行首 (x,y)
  lcd.print("Hello, world!");

  Serial.println("Setup Okay......");
}

void ledShowError()
{
  // 兩的 LED 恆亮,代表發生錯誤!
  digitalWrite(bLedUpPin, HIGH);
  digitalWrite(bLedDnPin, HIGH);
}

void ledRefresh()
{
  if(bUpDn==0)
  {
    // 上班
    digitalWrite(bLedUpPin, HIGH);
    digitalWrite(bLedDnPin, LOW);
  }
  else
  {
    // 下班
    digitalWrite(bLedUpPin, LOW);
    digitalWrite(bLedDnPin, HIGH);
  }
}

void checkButton()
{
  if(digitalRead(bButtonPin)==HIGH)
  {
    bUpDn=bUpDn ^ 1; // xor 運算
    ledRefresh();
  }
}

void showClock()
{
  lTimeGap4Clock=abs(lTimeCurrent-lTimeLastClockShown);

  if(lTimeGap4Clock>499) // 499 milliseconds
  {
    // Serial.print(rtc.getDOWStr());    // 週幾
    // Serial.print(rtc.getDateStr());   // 日期
    // Serial.println(rtc.getTimeStr()); // 時間

    lcd.setCursor(0, 1); // 設定游標位置在第 2 行行首 (x,y)
    lcd.print(rtc.getTimeStr());
    
    lTimeLastClockShown=lTimeCurrent;
  }
}

void makeSoundCore(float fFrequency, int iDuration)
{
  tone(bTonePin, fFrequency);
  delay(iDuration);
}

void makeSoundWithUpAndDn()
{
  // 上班/下班時間一到,會有一小段音樂。
  int iD=200;
  makeSoundCore(1975.5,iD*2);
  makeSoundCore(1760.0,iD/2);
  makeSoundCore(1568.0,iD);
  makeSoundCore(1480.0,iD);
  makeSoundCore(1318.5,iD);
  makeSoundCore(1568.0,iD);
  makeSoundCore(1174.7,iD*2);
  makeSoundCore(1174.7,iD);
  makeSoundCore(2093.0,iD);
  makeSoundCore(1760.0,iD*2);
  makeSoundCore(1174.7,iD);
  makeSoundCore(1975.5,iD);
  makeSoundCore(1568.0,iD*2);
  noTone(bTonePin);
}

void checkTimeAndSwitchUpDn()
{
  String strCurrentTime=rtc.getTimeStr(FORMAT_SHORT);

  if(strLastTimeSwitched==strCurrentTime) { return; }

  if(strCurrentTime=="05:00")
  {
    strLastTimeSwitched=strCurrentTime;
    bUpDn=0;
    ledRefresh();
    Serial.println("switch up");
  }
  else if(strCurrentTime=="08:30"
       || strCurrentTime=="13:30")
  {
    strLastTimeSwitched=strCurrentTime;
    bUpDn=0;
    ledRefresh();
    makeSoundWithUpAndDn();
    Serial.println("switch up");
  }
  else if(strCurrentTime=="12:30" 
       || strCurrentTime=="17:30")
  {
    strLastTimeSwitched=strCurrentTime;
    bUpDn=1;
    ledRefresh();
    makeSoundWithUpAndDn();
    Serial.println("switch down");
  }
}

// 一刷感應卡,傳送封包、燈光閃爍、音樂響起

void readUID()
{
  strUID=""; // 長度請用 strUID.length();

  if(mfrc522.PICC_IsNewCardPresent() &&
     mfrc522.PICC_ReadCardSerial())
  {
    byte *baId    = mfrc522.uid.uidByte;
    byte  bIdSize = mfrc522.uid.size;
    char caBuildBuffer[4] = {0};
    for(byte i=0; i<bIdSize; i++)
    {
      sprintf(caBuildBuffer, "%02X", (uint8_t)baId[i]);
      strUID.concat(caBuildBuffer);      
    }
    mfrc522.PICC_HaltA();
  }
}

void ledBlink()
{
  byte b1=0;
  for(int i=0; i<2; i++)
  {
    b1=b1 ^ 1;
    if(b1==0)
    {
      digitalWrite(bLedUpPin, HIGH);
      digitalWrite(bLedDnPin, LOW);
    }
    else
    {
      digitalWrite(bLedUpPin, LOW);
      digitalWrite(bLedDnPin, HIGH);
    }
    delay(50);
  }
}

void makeSoundAndBlinkLedWithSucceeded()
{
  tone(bTonePin, 523.25);
  //delay(100);
  ledBlink();
  tone(bTonePin, 1046.5);
  //delay(100);
  ledBlink();
  tone(bTonePin, 2093.0);
  //delay(100);
  ledBlink();
  noTone(bTonePin);
  ledRefresh();
}

void sendPacket()
{
  bool booSuccess=false;
  
  if(oEthernetClient.connect(caServerName, 8080)) // 8080 是 port number
  {
    Serial.println("Connected");
    String strDate=rtc.getDateStr();
    String strTime=rtc.getTimeStr();
    String strGet="GET /test.php?updn="+String(bUpDn)+"&uid="+strUID+"&date="+strDate+"&time="+strTime+" HTTP/1.0";
    oEthernetClient.println(strGet);
    oEthernetClient.println("Host: www.somewhere.com.tw");
    oEthernetClient.println(); //end of get request
    ledBlink();
    ledRefresh();
    booSuccess=true;
  }
  else
  {
    Serial.println("Connection Failed"); // error message if no client connect
    Serial.println();
    ledShowError();
    booSuccess=false;
  }

  while(oEthernetClient.connected() && !oEthernetClient.available()) 
  {
    // waits for data 等待資料傳送完畢!
    delay(1);
  }
  
  while(oEthernetClient.connected() || oEthernetClient.available()) 
  { 
    // connected or data available
    char c=oEthernetClient.read(); // gets byte from ethernet buffer
    Serial.print(c); // prints byte to serial monitor
  }

  Serial.println();
  Serial.println("Disconnecting.");
  Serial.println();
  oEthernetClient.stop(); // stop client

  if(booSuccess==true)
  {
    makeSoundAndBlinkLedWithSucceeded();
  }
}

void loop() 
{
  lTimeCurrent=millis();

  checkButton();
  showClock();
  checkTimeAndSwitchUpDn();
  
  readUID();
  if(strUID.length()>0)
  {
    sendPacket();
  }

  delay(1);
}