monsieurvechai gửi vào
- 80074 lượt xem
Ở nhà nhiều roài, Tới giờ đi học à nha! Nhưng mà lỡ đang đi học mà nhà có chuyện gì (cháy nhà) thì sao? Cùng bắt tay vào làm hệ thống cảnh báo cháy qua email với tui nha!
Nguyên lý
Các bạn ở cộng đồng Arduino Việt Nam thì quá quen với việc đo nhiệt độ rồi đúng hem? Nhưng mà đo xong để làm gì? Một ứng dụng đơn giản là nếu trên 60 độ thì chắc là có chuyện không lành rồi. Cách kiểm tra chắc ăn nhất là chạy dzìa coi, nhưng mà đây là thời buổi IoT rồi. Với Raspberry thì việc đơn giản hơn nhiều: tự nó sẽ đọc nhiệt độ Arduino khai báo qua cổng Serial và gửi hình ảnh qua email cho bạn nếu nhiệt độ trên 60. Đây là gợi ý ứng dụng kết hợp giữa Pi và Arduino. Ai kiu 2 bạn này là nước với lửa nào?. Thay vì mua thêm Ethernet shield đắt tiền, tại sao không mua Pizero, giá thành rẻ hơn mà lại nhiều chức năng hơn với Python.
Chuẩn bị
Bạn nối theo như bài đã viết trên diễn đàn nha:http://arduino.vn/bai-viet/977-huong-dan-su-dung-cam-bien-nhiet-do-ds18b20-55degc-den-125degc-sai-so-05degc
Sau đó nối Arduino với USB của Raspberry Pi là xong.
Code Arduino
Đây là code thầy tui đưa. Có sao ghi dzậy
#include <OneWire.h> // OneWire DS18S20, DS18B20, DS1822 Temperature Example // // http://www.pjrc.com/teensy/td_libs_OneWire.html // // The DallasTemperature library can do all this work for you! // http://milesburton.com/Dallas_Temperature_Control_Library OneWire ds(2); // on pin 2CM0'CO (a 4.7K resistor is necessary) void setup(void) { Serial.begin(9600); } void loop(void) { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]; float celsius; if ( !ds.search(addr)) { Serial.println("No more addresses."); Serial.println(); ds.reset_search(); delay(250); return; } Serial.print("ROM ="); for( i = 0; i < 8; i++) { Serial.write(' '); Serial.print(addr[i], HEX); } if (OneWire::crc8(addr, 7) != addr[7]) { Serial.println("CRC is not valid!"); return; } Serial.println(); // the first ROM byte indicates which chip switch (addr[0]) { case 0x10: Serial.println("Chip = DS18S20"); // or old DS1820 type_s = 1; break; case 0x28: Serial.println("Chip = DS18B20"); type_s = 0; break; case 0x22: Serial.println("Chip = DS1822"); type_s = 0; break; default: Serial.println("Device is not a DS18x20 family device."); return; } ds.reset(); ds.select(addr); ds.write(0x44, 1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad Serial.print("Data = "); Serial.print(present, HEX); Serial.print(" "); for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); Serial.print(data[i], HEX); Serial.print(" "); } Serial.print("CRC="); Serial.print(OneWire::crc8(data, 8), HEX); Serial.println(); // Convert the data to actual temperature // because the result is a 16 bit signed integer, it should // be stored to an "int16_t" type, which is always 16 bits // even when compiled on a 32 bit processor. int16_t raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { // "count remain" gives full 12 bit resolution raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); // at lower res, the low bits are undefined, so let's zero them if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms //// default is 12 bit resolution, 750 ms conversion time } celsius = (float)raw / 16.0; Serial.print("Temperature = "); Serial.print(celsius); Serial.print(" Celsius, "); }
Code trên Raspberry pi
#!/usr/bin/bash #By MonsieurVechai import time import webbrowser import os, subprocess import re, serial
import smtplib from email import Encoders from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText
''' serial stuff returned: ROM = 22 D 8D 16 0 0 0 C9 4B 46 7F FF 2 10 71 CRC=71 Temperature = 22.87 Celsius, 73.18 Fahrenheit No more addresses. ''' class ds18x20: def __init__(self): try: self.ser = serial.Serial('COM1', 9600, timeout=1) except: print "Cannot open serial port" exit(1) print "ds18x20 device serial link opened" self.ser.flushInput() def readTemperature(self): isTemp = False while isTemp == False: line = self.ser.readline() if line.startswith("Temperature"): isTemp = True re_list = re.findall('\d+.\d+',line) # find the decimal numbers in the string and put to list using re (regular expression) temperature = float(re_list[0]) return temperature def close(self): self.ser.close() print 'Device closed' def send_mail(file, temperature): UserName = "somethingd@gmail.com" Password = "something" Recipient = "something" msg = MIMEMultipart() msg['From'] = UserName msg['To'] = Recipient msg['Subject'] = "High temperature detected on " + datetime.now().strftime("%Y_%m_%d_%H_%M_%S.h264") text = "The house may be burning now. Temperature is: " + str(temperature) msg.attach( MIMEText(text) ) part = MIMEBase("application", "octet-stream") fo=open(file,"rb") part.set_payload(fo.read() ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' %os.path.basename(file)) msg.attach(part) s = smtplib.SMTP('smtp.gmail.com:587') s.ehlo() s.starttls() s.login(UserName, Password) s.sendmail(UserName, Recipient, msg.as_string()) s.close() def read_temperature(): dev = ds18x20() #Sometimes IndexError happens and we need to take the temperature measurement again try: temp = dev.readTemperature() except IndexError: temp = dev.readTemperature() dev.close() #Have to close device after use return float(temp) def main(): dev = ds18x20() print "Arduino is ready." camera = PiCamera() print "Pi Camera is ready." try: # Loop until users quit with CTRL-C while True : # Read Temperature from Arduino temperature = read_temperature() print "Current Temperature: " + str(temperature) if temperature > 60: print "High temperature detected. Start recording movie." filename = datetime.now().strftime("%Y_%m_%d_%H_%M_%S.h264") camera.start_recording(filename) time.sleep(20) camera.stop_recording() path_of_movie = os.path.abspath(filename) #Sending email try: send_mail(path_of_movie, temperature) print "Sending email done. Ready to read new temperature." except IOError: print "Something wrong. Mail not sent." time.sleep(0.01) except KeyboardInterrupt: print "Quit program" if __name__ == "__main__": main()
Chỉnh sửa trên code Python
Thay đổi địa chỉ email bằng địa chỉ email và password của bạn (phải là Gmail (cho dễ) nhé). Nhớ là phải để trong ngoặc kép nha:
Tìm cổng serial của Arduino bằng cách nhập lệnh:
- cd /dev
Rút Arduino ra và gõ ls, bạn sẽ thấy bảng sau:
Cắm Arduino vào và tiếp tục gõ ls, bạn sẽ thấy có cổng khác xuất hiện. Giả sử đó là COM2. Thay thế COM2 trong ô đỏ sau của Python code:
Lưu code lại. Mở terminal trong thư mục bạn lưu file python và nhập lệnh:
python di_hoc_thoi.py
Lưu ý
- Code này chỉ hoạt động được trên tài khoản gmail thôi nha! Và bạn phải vào https://www.google.com/settings/secu..., chọn "Turn on" cho phần "Access for less secure apps" thì mới gửi mail bằng Python được.
- Bạn nên tạo 1 tài khoản gmail email riêng vì bất cứ ai mở file python đều có thể đọc được mật khẩu hộp thư cá nhân của bạn.
- Lần đầu làm bạn nên comment # cho đoạn send_mail(path_of_movie) để tránh tài khoảng bị khóa hộp thư vì bị hiểu nhầm là spam.
- Bạn có thể chỉnh sửa thời gian quay ở đây, nhưng mà quay ít ít thoai, nặng quá là không gửi qua email được đâu (max 20MB):
- camera.start_recording(filename)
- time.sleep(20)
Gợi ý nâng cao
- Kết hợp với các cảm biến khác như khí gas, nước
- Kết hợp với module SIM để gọi điện thoại
Bài tới tui sẽ hướng dẫn dùng camera để quay time-lapse khi các bạn đang ở trường và email hình mỗi 15 phút.