2019年8月16日 星期五

寫入資料到RFID卡片

寫入資料到RFID卡片



參考資料:

RFID卡片中的資料是存在內部的記憶體中,所以,要對卡片進行讀寫,必須先認識卡片內部記憶體的結構。
RFID卡片有幾種不同的類型,Mifare 1最常用且與悠遊卡相容。 

Mifare 1 卡片說明:

記憶體結構

  • 資料記憶體容量為8K EEPROM
  • 分為16個扇區(sector 0~15)
  • 1個扇區有4個區塊(block0~4)
  • 1個區塊有16 Byte(密碼A:6 Bytes;存取控制:4 Bytes:密碼B:6 Bytes)
  • 最右側為區塊絕對編號。(範例程式將會使用此編號)


記憶體使用

  • 扇區0的區塊0的固定存放廠商代碼,不可更改。
  • 每個扇區的區塊0~2為使用者資料區塊,可以存入任何資料。
  • 每個扇區的區塊3為控制區塊,規定依序存放密碼A(6 Bytes)、讀寫控制位元(6 Bytes)及密碼B(6 Bytes)

存取控制

  • 每個扇區都有獨立的"密碼"及"存取控制位元"來控制記憶體資料的存取。
  • 換句話說,要存取區塊內資料需先提供正確的"密碼"及"存取控制位元",否則,資料是不可被隨意存取的。

範例程式說明


(本範例所需函式都包含在同一檔案中,不要與另一篇文章<RFID 讀卡模組運用 >所提MFRC522 Library混用)

硬體接線:

參考http://arduino.cc/en/Reference/SPI,配合所用的Arduino板將7條線接妥。

 RFID 模組 Arduino ICSP
 RST Reset
 3V3 不用接
 MISO MISO
 MISI
(電路板印刷有誤,應為MOSI)
 MOSI
 SCK SCK
 SS pin 10(或可自訂)
 VIN         Vcc     
 GND Gnd

軟體修改:

搜尋下列程式碼,修改數值以配合硬體實際腳位
const int chipSelectPin = 10;    //SS 接腳位置
const int NRSTPD = 5;            //RST接腳位置

此範例主要做以下幾件事情:

  1. 尋找卡片,回傳卡片型態代號。程式行#166~
  2. 呼叫防衝撞程序(Anti-collision loop),回傳卡片序號。程式行#177~
  3. 選擇作用卡片 。程式行#196 ~ 
  4. 寫入新密碼到區塊11(控制區塊),寫入數值100到區塊8(資料區塊)的位址。程式行#206 ~ 
  5. 讀取區塊8的資料。程式行#230 ~ 
  6. 讀取區塊8,減18,運算完成將結果重新寫入。程式行#250 ~
  7. 讀取區塊8,加10,運算完成將結果重新寫入。程式行#278 ~
  8. 中止卡片存取,進入睡眠模式。程式行#308:MFRC522_Halt();

完整的RFID卡片讀寫程序

要對卡片進行讀寫,必須經過一連串的步驟,如下圖所示:




以下簡單說明上圖所示程序所用之函式:

尋找卡片/複位應答(Answer to Request)

呼叫MFRC522_Request(),需要傳入2個參數:
  1. 搜尋模式
  2. 卡片型態回傳位址
參考程式行#718~
/*
* Function:MFRC522_Request
* Description:Find card and read its type
* Input values:reqMode--way to find the card;
*                         TagType--return type of card:
*                                 0x4400 = Mifare_UltraLight
*                                0x0400 = Mifare_One(S50)
*                                0x0200 = Mifare_One(S70)
*                                0x0800 = Mifare_Pro(X)
*                                0x4403 = Mifare_DESFire
* Output value:successful return MI_OK
*/

讀取卡片序號/防衝撞機制

呼叫MFRC522_Anticoll(),使用防衝撞程序來完成讀取卡號。
需要傳入1個位址(指標)參數,以接收序號回傳結果。
參考程式行#603~
/*
* Function:MFRC522_Anticoll
* Description:Anti-collision detection and card serial number reading
* Input values:serNum--return the four byte card serial number, the first five bytes of the checksum
* Output value:Successful return MI_OK
*/

選擇作用卡片

呼叫MFRC522_SelectTag(),需要傳入序號參數,以告知哪一張卡片被選擇了。
參考程式行#680~
/*
* Function:MFRC522_SelectTag
* Description:Election card, read memory capacity
* Input values:serNum--incoming card serial number
* Output value:Successful return of card capacity
*/

認證(Authentication)

一般不管讀取或寫入資料到卡片,都要先經過密碼認證:(參考程式行#208)
呼叫MFRC522_Auth(),需要傳入4個參數:
  1. authMode:認證模式(PICC_AUTHENT1A)
  2. BlockAddr:區塊(絕對)編號
  3. Sectorkey:密碼
  4. serNum:卡片序號
見程式行#718~
/*
* Function:MFRC522_Auth
* Description:Verify card password
* Input values:authMode--Passowrd authentication mode
0x60 = Verify A key
0x61 = Verify B key
BlockAddr--Block address
Sectorkey--Sector's password
serNum--Card serial number, 4 bytes
* Output value:Successful return MI_OK
*/

寫入資料

呼叫MFRC522_Write(),需要傳入2個參數:
  1. 區塊編號
  2. 欲寫入資料
函式原始碼及說明見程式行#783~
/*
* Function:MFRC522_Write
* Description:Write block data
* Input values:blockAddr--block address;writeData--16 bytes of data to be written to block
* Output value:Successful return MI_OK
*/

讀取資料

呼叫MFRC522_Read(),需要傳入2個參數:
  1. 區塊編號
  2. 結果回傳位址
/*
* Function:MFRC522_Read
* Description:Read block data
* Input values:blockAddr--block address;recvData--read a block of data
* Output value:Successful return MI_OK
*/

中止

呼叫MFRC522_Halt()
Ċ
Jonathan Cheng, 
2013年12月1日 上午2:59
ċ
RFID.ino 
(24k)
Jonathan Cheng, 
2013年12月1日 上午6:48
Ċ
mifare_ic.pdf 
(555k)
Jonathan Cheng, 
2013年12月5日 上午6:29

RFID 讀卡模組運用

RFID 讀卡模組運用






硬體介紹

  • 本RFID模組採用MFRC522晶片,支援SPI、UART和I2C三種通訊模式。
  • 可讀取卡片類型有:mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire。
  • 此模組電源電壓可支援5V及3.3V兩種。

通訊模式設置

通訊模式
指撥開關 1
指撥開關 2
SPI
(目前僅找到支援SPI通訊的函式庫)
NO
OFF
UART
OFF
NO
I2C
NO
特殊:選擇I2C位址

SPI通訊硬體接線


RFID模組
Arduino UNO
GND
GND
VIN
5V
SS
10
SCK
13
MISI(錯誤,應為MOSI)
11
MISO
12
3V3
不用接
RST
5

函式庫

  • MFRC522.cpp
  • MFRC522.h
下載附件,解壓縮至Arduino安裝路徑(或使用者資料夾)下的/libraries
目前這個函式庫可以使用在Uno中,可能無法使用在Due中。

範例

Arduino IDE中,File>Examples>MFRC522>Examples>Read_RFID_Card

程式步驟

1. 引入函式庫 #include <MFRC522.h>
2. 宣告MRFC522變數(建構MFRC522實體) - MFRC522 rfid(PinSS, PinRST);
3. 呼叫初始化函式init() - rfid.Init()
4. 搜尋RFID卡片,Request()
5. 讀取卡號,Anticoll()
6. 命令RFID卡片進入睡眠模式,Halt()

函式說明


MFRC522(int PinSS, int PinRST)  (ps.建構子如回傳值)
  • PinSS:SlaveSelect的引腳,SPI通訊時會將此引腳拉低,表示通訊對象為此裝置。實體接線就是模組上標示SS的接腳。 
  • PinRST:Reset,重置 
unsigned char MFRC522::Request(uchar reqMode, uchar *TagType) 
  • regMode:搜尋模式,定義在MFRC522.h中 
  • TagType:返回卡片型態代碼,存入TagType所指向的記憶體位址中,通常宣告一個字串變數來儲存傳回值。 
  • 返回值:MI_OK表示成功 
uchar MFRC522::Anticoll(uchar *serNum) 
  • serNum:回傳4 bytes卡號存入SerNum中,一般宣告一個char[]來儲放。

MFRC522函式庫解析

底層SPI讀寫通訊函式

  • uchar Read_MFRC522(uchar addr) 
  • void Write_MFRC522(uchar addr, uchar val) 
這兩個函式使用SPI.transfer()將資料傳送到晶片外部引腳

基本功能函式:

  • ClearBitMask()-清除(為0)暫存器之指定位元 
  • SetBitMask()-設定(為1)暫存器之指定位元 
ClearBitMask(uchar reg, uchar mask) 

按mask中指定為'1'的位元,將reg中的相應位元清除為'0' (其他位元保持不變)

SetBitMask(uchar reg, uchar mask) 

按mask中指定為'1'的位元,將reg中的相應位元設定為'1' (其他位元保持不變)

範例:欲將CommIrqRegbit7清除為'0',其餘位元保持原先狀態。
程式碼:
ClearBitMask(CommIrqReg, 0x80);
功能說明:將Set1設定為'0',以清除bit[6..0]中標示為'1'的中斷旗標。
範例:欲將BitFramingRegbit7設定為'1',其餘位元保持原先狀態。
程式碼:
SetBitMask(BitFramingReg, 0x80);
功能說明:將StartSend設定為'1'會驅使MFRC522晶片開始執行Transceive命令

程式設計會直接運用到的函示:

  • uchar MFRC522_Request(uchar reqMode, uchar *TagType) 
  • uchar MFRC522_Anticoll(uchar *serNum) 
  • void MFRC522_Halt(void)

ESP8266 Multisensor Shield with Node-RED

ESP8266 Multisensor Shield with Node-RED

















===========Esp8266程式================
/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com 
*********/

// Load libraries
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Update these with values suitable for your network.
//const char *ssid = "PTS-2F";
//const char *pass = "PTS6662594";
const char *ssid = "74170287";
const char *pass = "24063173";
//const char *ssid = "alex9ufo";
//const char *pass = "alex9981";

// Change the variable to your Raspberry Pi IP address, so it connects to your MQTT broker
const char* mqtt_server = "broker.mqtt-dashboard.com";  //ip address or hostname of the mqtt broker
// MQTT Broker IP example
//const char* mqtt_server = "192.168.1.144";

// Initializes the espClient. You should change the espClient name if you have multiple ESPs running in your home automation system
WiFiClient espClient;
PubSubClient client(espClient);

// Variable to hold the temperature reading
String temperatureString = "";
#define BUILTIN_LED  D0  // Arduino standard is GPIO13 but lolin nodeMCU is 2

// Set GPIOs for: output variable, status LED, PIR Motion Sensor, and LDR
const int output = D1  ;
const int statusLed = BUILTIN_LED ;
const int motionSensor = D2;
const int ldr = A0;
// Store the current output state
String outputState = "off";

// GPIO where the DS18B20 is connected to
const int oneWireBus = D4;         
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);

// Timers - Auxiliary variables
unsigned long now = millis();
unsigned long lastMeasure = 0;
boolean startTimer = false;
unsigned long currentTime = millis();
unsigned long previousTime = 0;
//==========================================================================================
// Don't change the function below.
// This function connects your ESP8266 to your router
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
}
//==========================================================================================
// This functions is executed when some device publishes a message to a topic that your ESP8266 is subscribed to
// Change the function below to add logic to your program, so when a device publishes a message to a topic that
// your ESP8266 is subscribed you can actually do something
void callback(String topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
 
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT
  // If a message is received on the topic esp8266/output, you check if the message is either on or off.
  // Turns the output according to the message received
  if(topic=="alex9ufo/esp8266/output"){
    Serial.print("Changing output to ");
    if(messageTemp == "#on"){
      digitalWrite(output, LOW);
      Serial.print("#on");
    }
    else if(messageTemp == "#off"){
      digitalWrite(output, HIGH);
      Serial.print("#off");
    }
  }
  Serial.println();
}
//==========================================================================================
// This functions reconnects your ESP8266 to your MQTT broker
// Change the function below if you want to subscribe to more topics with your ESP8266
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected"); 
      // Subscribe or resubscribe to a topic
      // You can subscribe to more topics (to control more outputs)
      client.subscribe("alex9ufo/esp8266/output"); 
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
//==========================================================================================
// Checks if motion was detected and the sensors are armed. Then, starts a timer.
void detectsMovement() {
  Serial.println("MOTION DETECTED!");
  client.publish("alex9ufo/esp8266/motion", "MOTION DETECTED!");
  previousTime = millis();
  startTimer = true;
}
//==========================================================================================
void setup() {
  // Start the DS18B20 sensor
  sensors.begin();

  // Serial port for debugging purposes
  Serial.begin(115200);

  // PIR Motion Sensor mode, then set interrupt function and RISING mode
  pinMode(motionSensor, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
 
  // Initialize the output variable and the LED as OUTPUTs
  pinMode(output, OUTPUT);
  pinMode(statusLed, OUTPUT);
  digitalWrite(output, HIGH);
  digitalWrite(statusLed, LOW);
 
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}
//==========================================================================================
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
 
  // Timer variable with current time
  now = millis();

  // Publishes new temperature and LDR readings every 30 seconds
  if (now - lastMeasure > 30000) {
    lastMeasure = now;
    sensors.requestTemperatures();
    // Temperature in Celsius degrees
    temperatureString = String(sensors.getTempCByIndex(0));
    // Uncomment the next line for temperature in Fahrenheit degrees
    //temperatureString = String(sensors.getTempFByIndex(0));
    // Publishes Temperature values
    client.publish("alex9ufo/esp8266/temperature", temperatureString.c_str());
    Serial.println("Temperature published");
   
    // Publishes LDR values
    client.publish("alex9ufo/esp8266/ldr", String(analogRead(ldr)).c_str());
    Serial.println("LDR values published");   
  }
  // After 10 seconds have passed since motion was detected, publishes a "No motion" message
  if ((now - previousTime > 10000) && startTimer) {
    client.publish("alex9ufo/esp8266/motion", "No motion");
    Serial.println("Motion stopped");
    startTimer = false;
  }
}
//==========================================================================================
=================Node-RED 程式====================

[{"id":"61640f10.dda39","type":"ui_switch","z":"1577f936.cf42e7","name":"","label":"Output","group":"a0157d81.02851","order":0,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"","style":"","onvalue":"#on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"#off","offvalueType":"str","officon":"","offcolor":"","x":110,"y":80,"wires":[["8f3f0ccd.0be1e"]]},{"id":"8f3f0ccd.0be1e","type":"mqtt out","z":"1577f936.cf42e7","name":"","topic":"alex9ufo/esp8266/output","qos":"0","retain":"true","broker":"40bf4d5e.0395f4","x":390,"y":80,"wires":[]},{"id":"ee62fc37.23c7","type":"mqtt in","z":"1577f936.cf42e7","name":"","topic":"alex9ufo/esp8266/motion","qos":"2","broker":"40bf4d5e.0395f4","x":170,"y":160,"wires":[["b45cf5.b14e9308"]]},{"id":"b45cf5.b14e9308","type":"ui_text","z":"1577f936.cf42e7","group":"a0157d81.02851","order":0,"width":0,"height":0,"name":"","label":"PIR Status:","format":"{{msg.payload}}","layout":"row-center","x":430,"y":160,"wires":[]},{"id":"d585e276.d0e5d","type":"ui_gauge","z":"1577f936.cf42e7","name":"","group":"a0157d81.02851","order":0,"width":0,"height":0,"gtype":"gage","title":"LDR","label":"Luminosity","format":"{{value}}","min":0,"max":"1023","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":450,"y":240,"wires":[]},{"id":"948f9cf6.15409","type":"mqtt in","z":"1577f936.cf42e7","name":"","topic":"alex9ufo/esp8266/ldr","qos":"2","broker":"40bf4d5e.0395f4","x":150,"y":240,"wires":[["d585e276.d0e5d"]]},{"id":"6644f493.68bf4c","type":"mqtt in","z":"1577f936.cf42e7","name":"","topic":"alex9ufo/esp8266/temperature","qos":"2","broker":"40bf4d5e.0395f4","x":180,"y":320,"wires":[["44cb5724.b03ab8"]]},{"id":"44cb5724.b03ab8","type":"ui_chart","z":"1577f936.cf42e7","name":"","group":"a0157d81.02851","order":0,"width":0,"height":0,"label":"Temperature","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":430,"y":320,"wires":[[],[]]},{"id":"a0157d81.02851","type":"ui_group","z":"","name":"Main","tab":"8f361c82.458a5","disp":true,"width":"6","collapse":false},{"id":"40bf4d5e.0395f4","type":"mqtt-broker","broker":"broker.mqtt-dashboard.com","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":15,"cleansession":true,"birthQos":"0","willQos":"0"},{"id":"8f361c82.458a5","type":"ui_tab","z":"","name":"Dashboard","icon":"dashboard"}]

ESP8266 Multisensor Shield with Node-RED

ESP8266 Multisensor Shield with Node-RED

In this project you’ll discover how integrate the ESP8266 Multisensor Shield for the Wemos D1 Mini board with Node-RED. The ESP8266 Multisensor Shield will establish an MQTT connection with a Raspberry Pi running Node-RED and Mosquitto MQTT broker.

Project Overview

The Multisensor shield consists of a temperature sensor, a motion sensor, an LDR, and a 3 pin socket where you can connect any output, like a relay module for example.
We’ll use the Mosquitto broker installed on the same Raspberry Pi running Node-RED. The broker is responsible for receiving all messages, filtering the messages, decide who is interested in them and publishing the messages to all subscribed clients. To learn more about MQTT, you can read: What is MQTT and How It Works.
The following figure shows an overview of what we’re going to do in this tutorial.
  • The Node-RED application publishes messages (“on” or “off“) in the topic esp8266/output. The ESP8266 is subscribed to that topic. So, it receives the message with “on” or “off” to turn the output on or off.
  • The ESP8266 publishes temperature on the esp8266/temperature topic and the luminosity on the esp8266/ldr topic. The Node-RED application is subscribed to those topics. So, it receives temperature and luminosity readings that are displayed on a chart and gauge.
  • Node-RED is subscribed to the topic esp8266/motion. The ESP8266 publishes on that topic when motion is detected. When motion is detected the message “MOTION DETECTED!” is displayed for 10 seconds in the Node-RED Dashboard text widget.

Prerequisites

If you like home automation and you want to learn more about Node-RED, Raspberry Pi, ESP8266 and Arduino, we recommend that you download our course: Build a Home Automation System with Node-RED, ESP8266 and Arduino.

Resources

You can find all the resources needed to build this project in the links below (or you can visit the GitHub project):

Schematic

You can build the multisensor shield on a PCB, or you can build the multisensor shield circuit on a breadboard.
Here’s a list of parts you need to build the circuit:
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
After gathering all the needed parts, assemble the circuit by following the next schematic diagram:
Here’s how the circuit looks like on a breadboard:

Alternatively, you can build a PCB for the WeMos D1 Mini (learn how to Build a Multisensor Shield for ESP8266):
Here’s how the circuit looks like using the PCB shield with a 3D printed enclosure:

Preparing the Arduino IDE

We’ll program the ESP8266 using the Arduino IDE. In order to upload code to your ESP8266 using the Arduino IDE, you need to install the ESP8266 add-on (How to Install the ESP8266 Board in Arduino IDE). You’ll also need to install three additional libraries to run the code on the ESP8266.
Installing the PubSubClient Library
The PubSubClient library provides a client for doing simple publish/subscribe messaging with a server that supports MQTT (basically allows your ESP8266 to talk with Node-RED).
  1. Click here to download the PubSubClient library. You should have a .zipfolder in your Downloads folder
  2. Unzip the .zip folder and you should get pubsubclient-master folder
  3. Rename your folder from pubsubclient-master to pubsubclient
  4. Move the pubsubclient folder to your Arduino IDE installation librariesfolder
OneWire library
  1. Click here to download the OneWire library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get OneWire-master folder
  3. Rename your folder from OneWire-master to OneWire
  4. Move the OneWire folder to your Arduino IDE installation libraries folder
Dallas Temperature library
  1. Click here to download the DallasTemperature library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get Arduino-Temperature-Control-Library-master folder
  3. Rename your folder from Arduino-Temperature-Control-Library-master to DallasTemperature
  4. Move the DallasTemperature folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Uploading Code

Copy the following code to the Arduino IDE. The code is well commented on where you need to make changes. You need to edit the code with your own SSID, password and MQTT Broker IP Address (Raspberry Pi IP Address).
/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

// Load libraries
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Change the variable to your Raspberry Pi IP address, so it connects to your MQTT broker
const char* mqtt_server = "REPLACE_WITH_YOUR_MQTT_BROKER";
// MQTT Broker IP example
//const char* mqtt_server = "192.168.1.144";

// Initializes the espClient. You should change the espClient name if you have multiple ESPs running in your home automation system
WiFiClient espClient;
PubSubClient client(espClient);

// Variable to hold the temperature reading
String temperatureString = "";

// Set GPIOs for: output variable, status LED, PIR Motion Sensor, and LDR
const int output = 15;
const int statusLed = 12;
const int motionSensor = 5;
const int ldr = A0;
// Store the current output state
String outputState = "off";

// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;          
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);

// Timers - Auxiliary variables
unsigned long now = millis();
unsigned long lastMeasure = 0;
boolean startTimer = false;
unsigned long currentTime = millis();
unsigned long previousTime = 0; 

// Don't change the function below. 
// This function connects your ESP8266 to your router
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
}

// This functions is executed when some device publishes a message to a topic that your ESP8266 is subscribed to
// Change the function below to add logic to your program, so when a device publishes a message to a topic that 
// your ESP8266 is subscribed you can actually do something
void callback(String topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT
  // If a message is received on the topic esp8266/output, you check if the message is either on or off. 
  // Turns the output according to the message received
  if(topic=="esp8266/output"){
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      digitalWrite(output, LOW);
      Serial.print("on");
    }
    else if(messageTemp == "off"){
      digitalWrite(output, HIGH);
      Serial.print("off");
    }
  }
  Serial.println();
}

// This functions reconnects your ESP8266 to your MQTT broker
// Change the function below if you want to subscribe to more topics with your ESP8266 
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");  
      // Subscribe or resubscribe to a topic
      // You can subscribe to more topics (to control more outputs)
      client.subscribe("esp8266/output");  
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// Checks if motion was detected and the sensors are armed. Then, starts a timer.
void detectsMovement() {
  Serial.println("MOTION DETECTED!");
  client.publish("esp8266/motion", "MOTION DETECTED!");
  previousTime = millis();
  startTimer = true;
}

void setup() {
  // Start the DS18B20 sensor
  sensors.begin();

  // Serial port for debugging purposes
  Serial.begin(115200);

  // PIR Motion Sensor mode, then set interrupt function and RISING mode
  pinMode(motionSensor, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
  
  // Initialize the output variable and the LED as OUTPUTs
  pinMode(output, OUTPUT);
  pinMode(statusLed, OUTPUT);
  digitalWrite(output, HIGH);
  digitalWrite(statusLed, LOW);
  
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
  // Timer variable with current time
  now = millis();

  // Publishes new temperature and LDR readings every 30 seconds
  if (now - lastMeasure > 30000) {
    lastMeasure = now;
    sensors.requestTemperatures(); 
    // Temperature in Celsius degrees
    temperatureString = String(sensors.getTempCByIndex(0));
    // Uncomment the next line for temperature in Fahrenheit degrees
    //temperatureString = String(sensors.getTempFByIndex(0));
    // Publishes Temperature values
    client.publish("esp8266/temperature", temperatureString.c_str());
    Serial.println("Temperature published");
    
    // Publishes LDR values
    client.publish("esp8266/ldr", String(analogRead(ldr)).c_str());
    Serial.println("LDR values published");    
  }
  // After 10 seconds have passed since motion was detected, publishes a "No motion" message
  if ((now - previousTime > 10000) && startTimer) { 
    client.publish("esp8266/motion", "No motion");
    Serial.println("Motion stopped");
    startTimer = false;
  }
}
This code is quite long to explain, so you can simply replace the following three variables with your details and the code will work straight away.
const char* ssid = "";
const char* password = "";
const char* mqtt_server = "";
If you want to learn how this code works, continue reading.
callback()
In the callback() function when a new message is received on the topic esp8266/output, you check if the message is either on or off to turn the output on or off accordingly.
if(topic=="esp8266/output"){
  Serial.print("Changing output to ");
  if(messageTemp == "on"){
    digitalWrite(output, LOW);
    Serial.print("on");
  }
  else if(messageTemp == "off"){
    digitalWrite(output, HIGH);
    Serial.print("off");
  }
}Publishing temperature and LDR readings
This section of code publishes new temperature and LDR readings every 30 seconds. The temperatures readings are published on the esp8266/temperaturetopic. The luminosity readings are published on the esp8266/ldr topic.
if (now - lastMeasure > 30000) {
  lastMeasure = now;
  sensors.requestTemperatures(); 
  // Temperature in Celsius degrees
  temperatureString = String(sensors.getTempCByIndex(0));
  // Uncomment the next line for temperature in Fahrenheit degrees
  //temperatureString = String(sensors.getTempFByIndex(0));
  // Publishes Temperature values
  client.publish("esp8266/temperature", temperatureString.c_str());
  Serial.println("Temperature published");

  // Publishes LDR values
  client.publish("esp8266/ldr", String(analogRead(ldr)).c_str());
  Serial.println("LDR values published"); 
}
Note: the temperature is being published in Celsius degrees, but you can uncomment the following line for temperature in Fahrenheit degrees:
temperatureString = String(sensors.getTempFByIndex(0));
Motion detection
When motion is detected it calls the detectsMovement() function, publishes the message “MOTION DETECTED!” on the esp8266/motion topic, and starts a timer:
void detectsMovement() {
  Serial.println("MOTION DETECTED!");
  client.publish("esp8266/motion", "MOTION DETECTED!");
  previousTime = millis();
  startTimer = true;
}
If motion is not detected for more than 10 seconds, a new message is published in the esp8266/motion with the text “No motion”:
 // After 10 seconds have passed since motion was detected, publishes a "No motion" message
if ((now - previousTime > 10000) && startTimer) { 
  client.publish("esp8266/motion", "No motion");
  Serial.println("Motion stopped");
  startTimer = false;
}
Those are the main sections of code that you need to worry about. You can modify the messages, subscribe to more topics to control more outputs, or publish other sensor readings.

Testing the connection

Upload the code to your board. After uploading it, and with the Raspberry Pi running your Node-RED application and the Mosquitto broker, open the Arduino IDE serial monitor at a baud rate of 115200 and see what’s happening in real time.
This is helpful to check if the ESP8266 has established a successful connection to your router and with the Mosquitto broker.

Creating the Node-RED Flow

Before creating the flow, you need to have installed in your Raspberry Pi:

Importing the Node-RED flow

To import the provided Node-RED flow, go to the GitHub repository or click the figure below to see the raw file, and copy the code provided.
Next, in the Node-RED window, at the top right corner, select the menu, and go to Import  > Clipboard.
Then, paste the code provided and click Import.
Here’s the imported flow that controls the ESP8266 and displays the published sensor readings.

Node-RED UI

After making all the necessary changes, click the Deploy button to save all the changes.
Now, your Node-RED application is ready. To access Node-RED UI and see how your application looks, access any browser in your local network and type:
http://Your_RPi_IP_address:1880/ui
Your application should look as in the following figure.
When motion is detected, it prints the message “MOTION DETECTED!” in the PIR Status field:

Wrapping Up

That’s it for this project. We hope you’ve found this project useful and you’re able to integrate the Multisensor Shield with your own home automation system using Node-RED.
If you like this project, you may also like our other projects:

2024_09 作業3 以Node-Red 為主

 2024_09 作業3  (以Node-Red 為主  Arduino 可能需要配合修改 ) Arduino 可能需要修改的部分 1)mqtt broker  2) 主題Topic (發行 接收) 3) WIFI ssid , password const char br...