7. KẾT CẤU CỦA ĐỒ ÁN
4.11 Khởi tạo và Thực hiện chương trình (Tham khảo tại Readme.md)
Bước 1: Tạo Application cho Web [9]
Bước 2: Thu thập các Nhiệm vụ mà Web sẽ phải thực hiện 4.12 Khởi động Gate Way trên Raspberry pi:
Để khởi động Gate Way trên Raspberry thì cần sử dụng 3 dịng code trong terminal:
• cd ~/Desktop/bluetag
• source env/bin/activate
• python3 main.py
Sau khi đánh xong dòng lệnh thứ 3, đoạn code sẽ hiện lên IP address của Gate Way, click chuột phải vào IP address và click open URL để vào Gate Way
37
Chương 5
THI CÔNG
5.1 Sử dụng Gate Way Bluetag: 5.1.1 Trang Login: 5.1.1 Trang Login:
Sau khi vào được Gate Way có tên là Bluetag, trang đầu tiên hiện lên trên màn hình sẽ trang Login ( đăng nhập):
Hình 5.1: Trang Login của Gate Way
Login name: bluetag Password: bluetag
Sau khi nhập thành công Login name và Password, Gate Way sẽ chuyển tiếp sang giao diện mới, trang này có chức năng quản lý phần hiển thị của các bảng giá đã được kết nối, cập nhật tên, giá của bảng giá cũ hoặc thêm bảng giá mới
38
5.1.2 Trang chào:
Hình 5.2: Giao diện trang chào
5.1.3 Trang Làm việc các kết nối cũ:
Hình 5.3: Giao diện quản lý thiết bị Trang này được nằm mục Slave ở bên trái màn hình Trang này được nằm mục Slave ở bên trái màn hình
39
• Scan for new slaves: Dùng để quét thiết bị mới
• Refresh slave list: làm mới danh sách thiết bị đã kết nối
• Edit: Cập nhật tên hoặc giá cho nhiều màn hình
Trong trường hợp muốn cập nhật tên và giá của 1 màn hình, click vào Details của thiết bị muốn được cập nhật
Hình 5.4: Giao diện chi tiết của thiết bị đã kết nối Trong giao diện này có 3 thơng tin có thể thay đổi hoặc cập nhật Trong giao diện này có 3 thơng tin có thể thay đổi hoặc cập nhật
• Product name: hiển thị tên sản phẩm
• Product price: hiển thị giá sản phẩm
• Device name: tên của bảng giá hiển thị 5.1.4 Trang làm việc với kết nối mới
40
Hình 5.5: Giao diện hiển thị kết nối mới
Nút Re-scan có chức năng để quét lại, làm mới danh sách thiết để thuận tiện hơn cho việc tìm kiếm kết nối mới.
Một khi chọn được thiết bị mới để kết nối, bấm vào nút Detail, sau đó web sẽ hiện lên thơng tin chi tiết để đặt tên cho thiết bị đó. Ở mục Add device, Device name, bằng cách đặt tên cho thiết bị mới (tên: màn hình 6) và bấm nút Add this
device. Toàn bộ giao thức này được sử dụng trên nền tảng WAP-HTML và cơng cụ
41
Hình 5.6: Thơng tin chi tiết kết nối mới
Sau đó thiết bị này sẽ được lưu lại và cho vào danh sách thiết bị đã kết nối.
42
5.1.5 Trang làm việc với nhiều thiết bị cùng 1 lúc:
Trong trường hợp muốn cập nhật tên và giá từ 2 màn hình trở lên, click vào Edit, mục Product name và Product price sẽ có thể cập nhật được tên và giá mới ngay trên danh sách thiết bị đã nối thay vì phải click vào Detail của từng thiết bị.
Sau khi nhập xong tên và giá mới, click vào Display, gate way sẽ gửi dữ liệu mới vào các màn hình cần phải cập nhật tên và giá mới.
43
5.1.6 Trang Đổi pass
Trang đổi mật khẩu được nằm trong mục System ở bên trái màn hình
Hình 5.9: Giao diện đổi mật khẩu
• Login name: Tên đăng nhập.
• Old password: Mật khẩu cũ.
• New password: Mật khẩu mới.
• Re-type new password: Nhập lại mật khẩu mới. 5.2 Thao tác trang Web với màn hình E-inks:
Sau khi nhập tên và giá cho sản phẩm , click vào Display product name và
Display product price, Gate Way sẽ truyền dữ liệu cho bảng giá hiển thị qua bluetooth
44
Hình 5.10: Hiện thị tên và giá trên màn hình E-ink
Refresh Display được dùng cho trường hợp khi chữ hiển thị trên màn hình E-ink bị
mờ đi hoặc theo thời gian, giúp làm mới màn hình và hiển thị chữ rõ như ban đầu.
45
Chương 6
ĐÁNH GIÁ KẾT QUẢ, KẾT LUẬN
6.1 Kết quả đạt được của hệ thống:
Khả năng truyền nhận của Gate way với màn hình E-ink ổn Dữ liệu hoạt động ngoài thực tế:
Khoảng cách truyền nhận 10 – 13 m Thời gian nhãn kệ thay đổi 1 – 3 giây Số lượng kết nối cùng lúc 10+
Bảng 6.1: Số liệu ngoài thực tế Sự đổi mới :
Thay vì phải sử dụng mã code arduino và internet cho mỗi lần thay đổi thì với Website này, ta có thể tiết kiệm được một khoảng thời gian và không cần phải chi trả thêm cho kết nối internet bằng dây cáp mạng.
Điều đó sẽ thay đổi một phần khơng nhỏ đến kinh tế và sự chính xác trong khâu bán hàng , quản lí
6.2 Lỗi mà hệ thống gặp phải:
Theo kết quả quan sát được, nhóm đã nhận định lỗi mà Gate way gặp phải khi truyền dữ liệu đồng loạt cho nhiều màn hình E-ink (2 loại lỗi)
Thứ nhất là lỗi ưu tiên, nghĩa là trong quá trình thiết lập kết nối với màn hình E-ink mới thì Gate way sẽ làm tạo kết nối với từng màn hình E-ink riêng biệt. Nhưng khi đổi hay cập nhật dữ của các màn hình E-ink đó thì Gate way chỉ ưu tiên và cho phép thiết bị kết nối đầu tiên (ở trên cùng của danh sách thiết bị đã kết nối) đổi thành dữ liệu mới, cịn các màn hình
Thứ hai là lỗi dung lượng truyền. Đây là hiện tượng trang web nhận 1 lượng dung lượng thông tin nhất định nhưng không chia luồng và gói. Dẫn đến tình trạng tồn bộ dung lượng thơng tin truyền thẳng vào màn hình ưu tiên từ đó dẫn đến số
46
lượng thơng tin cịn lại bị xem là thừa , bộ xử lí phía màn hình tự động loại bỏ khi nhận đủ thơng tin của riêng nó.
6.3 Phương pháp giải quyết và khắc phục:
Đối với lỗi thứ nhất:
Nhóm nhận thấy việc ưu tiên thường do kiểu truyền thông tin nối tiếp . Vậy nên việc thay đổi thành kiểu truyền song hành sẽ là lựa chọn thích hợp tại Pi.
Đối với lỗi thứ hai:
Nhóm nhận thấy việc ngắt lượng thơng tin thành gói thơng tin sẽ cho phép màn hình nhận đúng phần gói thơng tin của nó và tránh bị lược bỏ gói thơng tin của những màn hình khác.
Vì tình trạng dịch bệnh nên nhóm hiện tại vẫn chưa thể tương tác trực tiếp để sửa chữa lỗi .
6.4 Hướng phát triển của đề tài:
Sự phát triển của cơng nghệ là địi hỏi tốc độ và quy mơ ảnh hưởng nên đồ án này có thể được nâng lên khơng cịn quản lí ở một cửa hàng nhỏ trong một chuỗi cửa hàng.
Sự đi lên của đồ án sẽ là một server cho cả một hệ thống : - Điều khiển từ rất xa thông qua một trang web có miền - Điều khiển được rất nhiều Raspberry
- Nắm giữ thông tin và sự thay đổi cấp thiết , nhanh chóng và chính xác của tồn hệ thống
- Hệ thống có thể biến hóa theo mong muốn của các cơng ty mà vẫn giữ được giá trị cốt lõi của đồ án
47
48
TÀI LIỆU THAM KHẢO
Với tài liệu báo chí:
1. R. Fernández, E. Z. Casanova and I. G. Alonso. “Review of display technologies focusing on power consumption”. Sustainability, vol. 7, no. 8,
pp. 10854-10875, Aug. 2015.
2. J. K. Borchardt. “Developments in organic displays”. Mater. Today, vol. 7, no. 9, pp. 42-46, Sep. 2004.
3. J. Sung. “End of paper labels: Emerging smart labels toward Internet of Things”. Proc. IEEE 2nd World Forum Internet Things (WF-IoT), Dec. 2015, pp. 216-221.
Với tài liệu có từ Internet
1. Trang chủ : W3S :Ngơn ngữ và hướng dẫn cơ bản lập trình HTML.
https://www.w3schools.com/tags/default.asp
Ngày truy cập: 20/5/2021
2. Youtube: HTML5 & CSS Development: Learn How to Build a Professional Website | Udemy, Jordan Hudgens – Tác giả : Udemy.
https://www.youtube.com/watch?v=5bMdjkfvONE
Ngày truy cập: 20/5/2021
3. Youtube : Python Website Full Tutorial - Flask, Authentication, Databases & More – Tác giả : Tech with Tim.
https://www.youtube.com/watch?v=dam0GPOAvVI
Ngày truy cập: 24/5/2021
4. Youtube: How To Make Website Using HTML & CSS | Full Responsive Multi Page Website Design Step by Step – Tác giả : Easy Tutorials
https://www.youtube.com/watch?v=oYRda7UtuhA
Ngày truy cập: 24/5/2021
5. Trang chủ : Raspberry OS - Raspberry ORG.
https://www.raspberrypi.org/software/
49
6. Diễn đàn công nghệ thông tin: Chủ đề: Thiết kế web
https://github.com/topics/website-design
Ngày truy cập: 10/6/2021
7. Trang chủ nền tảng xây dụng web: Phần mềm - Flask
https://flask.palletsprojects.com/en/2.0.x/
Ngày truy cập: 12/6/2021
8. Youtube : BLE DEMO ON PI – Tác giả : Tomy Vithayathil
https://www.youtube.com/watch?v=RxH5nM3eP5Q\
Ngày truy cập: 15/6/2021
9. Diễn đàn công nghệ thông tin: Chủ đề: Sử dụng code để thiết lập nhiệm vụ cho BLE
https://github.com/search?q=ble
Ngày truy cập: 15/6/2021
10. Trang chủ: Phần mềm : Bleak, Bleak Introduction.
https://buildmedia.readthedocs.org/media/pdf/bleak/stable/bleak.pdf
Ngày truy cập:18/6/2021
50
PHỤ LỤC
1. Phụ lục linh kiện:
Thông số kỹ thuật của raspberry pi zero w:
• 802.11 b/g/n wireless LAN
• Bluetooth 4.1
• Bluetooth Low Energy (BLE)
• Phần cịn lại, máy tính nhúng Raspberry Pi Zero W có cấu hình tương tự người tiền nhiệm:
• Chip BCM2835 tốc độ 1GHz (nhanh hơn 40% cho với Pi 1)
• 512MB RAM
• Cổng Mini HDMI
• Cổng USB On-The-Go
• Cổng nguồn Micro USB
• Chân gắm GPIO 40 chân
• Có chân cắm cổng Composite video và reset máy.
• Cổng CSI cho camera
• Kích thước: 66.0mm x 30.5mm x 5.0mm
Thơng số kỹ thuật của raspberry pi 4:
• Broadcom BCM2711, Quad core Cortex-A72 (ARM v8) 64-bit SoC @ 1.5GHz
• Có 3 lựa chọn RAM: 2GB, 4GB hoặc 8GB LPDDR4-2400 SDRAM • Wifi chuẩn 2.4 GHz và 5.0 GHz IEEE 802.11ac. Bluetooth 5.0, BLE • Cổng mạng Gigabit Ethernet
• 2 cổng USB 3.0 và 2 cổng USB 2.0
• Chuẩn 40 chân GPIO, tương thích với các phiên bản trước
• Hỗ trợ 2 cổng ra màn hình chuẩn Micro HDMI với độ phân giải lên tới 4K • Cổng MIPI DSI
51 • Cổng MIPI CSI
• Cổng AV 4 chân
• H.265 (4kp60 decode), H264 (1080p60 decode, 1080p30 encode) • OpenGL ES 3.0 graphics
• Khe cắm Micro-SD cho hệ điều hành và lưu trữ • Nguồn điện DC 5V – 3A DC chuẩn USB-C • 5V DC via GPIO header (minimum 3A*)
• Hỗ trợ Power over Ethernet (PoE) (yêu cầu có PoE HAT)
2. Phụ lục code:
.vscode launch.json {
// Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0",
"configurations": [ {
"name": "Python: Flask", "type": "python", "request": "launch", "module": "flask", "env": { "FLASK_APP": "app.py", "FLASK_ENV": "development", "FLASK_DEBUG": "1", }, "args": [ "run",
52 "--no-debugger" ], "jinja": true } ] } settings.json { "python.pythonPath": "env/bin/python" } __pycache__
app.cpython-37.pyc (tài liệu mã hoá) api
__pycache__
auth.cpython-37.pyc (tài liệu mã hoá) slave.cpython-37.pyc (tài liệu mã hoá) system.cpython-37.pyc (tài liệu mã hoá) auth.py
from flask import request, Blueprint, current_app import datetime
from functools import wraps
from core.data import responses as resp from core.cypherutil import *
from core.datetimeutil import * from core import logging as log
from core.data.database import *
53
bp = Blueprint("api/auth", __name__, url_prefix="/api/auth")
def authorize(f): @wraps(f)
def decorator(*args, **kwargs): try:
token=None
if 'access-token' in request.headers: token = request.headers['access-token'] if not token: return resp.create('-2', 'Token is missing')
data = jwt.decode(token, current_app.config['SECRET_KEY']) db= dbconnect() user = db.execute(dbquerry.get_user()).fetchone() dbclose(db) if user['login_name'] != data['login_name']: return resp.create('-2', 'Wrong user name')
if data['expired'] < datenum(datetime.datetime.utcnow()): return resp.create('-2', 'Token expired')
return f(*args, **kwargs) except:
log.error("Authorize error")
return resp.create('-2', 'Authorize error') return decorator
54 def login():
request_data = request.get_json()
if not request_data: return resp.create('-1', 'Missing request body') if not 'name' in request_data: return resp.create('-1', 'The field \'name\' is required')
if not 'pwd' in request_data: return resp.create('-1', 'The field \'pwd\' is required') hashpwd = create_hash(request_data['pwd']) login_name= request_data['name'] login_pwd= hashpwd db= dbconnect() user = db.execute(dbquerry.get_user()).fetchone() dbclose(db)
if user['login_name'] != login_name: return resp.create('-1', 'Wrong user name') if user['login_pwd'] != login_pwd: return resp.create('-1', 'Wrong password login')
# todo: login with real info
now= datetime.datetime.utcnow() + datetime.timedelta(minutes= 30) ip= request.environ.get('HTTP_X_REAL_IP', request.remote_addr) token_data= {
'login_name': login_name, 'ip': ip,
55 }
tokenBytes = token_encode(token_data, current_app.config['SECRET_KEY']) token = tokenBytes.decode('utf-8')
log.event(f'{login_name} logged in, loc: {ip}') return resp.create('0', 'Login success', {
'token': token,
'expired': formatted_date_str(now) })
slave.py
from flask import * import threading
from core.data import responses as resp from core.data.database import dbconnect from core import bleutils as ble
from core.data.database import *
from core.data import querry as dbquerry import uuid
import traceback
from api.auth import authorize
from app import get_threads, init_threads, remove_threads import time
bp = Blueprint("api/slave", __name__, url_prefix="/api/slave")
@bp.route('/all', methods=['GET']) @authorize
56 data= [] init_threads() db= dbconnect() dbdata= db.execute(dbquerry.get_slave()) for row in dbdata:
runtime= 0
def x(y): ble.bleappendthread(y)
threading.Thread(target=x(row['mac'])).start()
threads= get_threads() for thread in threads:
if thread['mac'] == row['mac']: runtime= 1 data.append({ 'mac': row['mac'], 'device_name': row['device_name'], 'product_name': row['product_name'], 'product_price': row['product_price'], 'run_time': runtime }) dbclose(db) print(get_threads())
57 @bp.route('/get', methods=['GET']) @authorize def get(): mac = request.args.get('mac')
if mac == None: return resp.create('-1', 'Provide a mac address to peform this action')
data= None db= dbconnect()
current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
if current_slave == None: return resp.create('-1', f'Slave with mac equals to {mac} not found')
data= { 'mac': current_slave['mac'], 'device_name': current_slave['device_name'], 'product_name': current_slave['product_name'], 'product_price': current_slave['product_price'] } dbclose(db)
return resp.success(data= data)
@bp.route('/remove', methods=['POST']) @authorize def remove(): mac = request.args.get('mac') threads= get_threads()
58 if len(threads) != 0:
for thread in threads: if thread['mac'] == mac: curr= get_threads(mac) remove_threads(curr)
db= dbconnect()
current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
if current_slave == None: return resp.create('-1', f'Slave with mac equals to {mac} not found')
db.execute(dbquerry.update_slave(current_slave['uid'], mac= mac, status_code= '-1')) db.commit() dbclose(db) return resp.success() @bp.route('/update', methods=['POST']) @authorize def update(): mac = request.args.get('mac') name = request.args.get('name') db= dbconnect() current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
if current_slave == None: return resp.create('-1', f'Slave with mac equals to {mac} not found')
db.execute(dbquerry.update_slave(current_slave['uid'], device_name = name)) db.commit()
59 dbclose(db) return resp.success() @bp.route('/scan', methods=['GET']) @authorize def scan(): device_list= ble.blescan() db= dbconnect()
for device in device_list[::-1]:
current_slave = db.execute(dbquerry.get_slave(device['mac'])).fetchone() if current_slave != None: device_list.remove(device) dbclose(db)
return resp.success(data= device_list) @bp.route('/add', methods=['POST']) @authorize def add(): req_json = request.get_json() mac= req_json['mac'] device_name= req_json['device_name'] product_name= req_json['product_name'] product_price= req_json['product_price'] ble.bleappendthread(mac)
60 db= dbconnect()
current_slave = db.execute(dbquerry.get_slave(mac)).fetchone() if current_slave != None:
dbclose(db)
return resp.create('-1', f'Slave with mac {mac} is already exists, remove it first.')
result= db.execute(dbquerry.add_slave(str(uuid.uuid4()), mac, device_name, product_name, product_price, '')) db.commit() dbclose(db) return resp.success() @bp.route('/disp-product', methods=['POST']) @authorize def display_product(): try: req_json = request.get_json() mac= req_json['mac'] product_name= req_json['product_name'] product_price= req_json['product_price'] # bleclient= ble.bleconnect(mac) listble= get_threads() # print(listble) if len(listble) == 0:
61 for x in listble: if x['mac'] == mac: bleclient = x['client'] ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, f'#{product_name}') ble.blewrite(bleclient, f'!{product_price}') ble.blewrite(bleclient, '@e') # ble.bledisconnect(bleclient) db= dbconnect() current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
result= db.execute(dbquerry.update_slave(current_slave['uid'], None, None, product_name, product_price, None, None))
db.commit() dbclose(db) return resp.success() except: traceback.print_exc() return resp.failed() @bp.route('/disp-product-name', methods=['POST']) @authorize def display_product_name(): try:
62 req_json = request.get_json() mac= req_json['mac'] product_name= req_json['product_name'] type_func= req_json['type'] if type_func == 'test': bleclient= ble.bleconnect(mac) ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, f'#{product_name}') ble.blewrite(bleclient, '@e') ble.bledisconnect(bleclient) if type_func == 'notest': listble= get_threads() # print(listble) if len(listble) == 0: return resp.failed(); for x in listble: if x['mac'] == mac: bleclient = x['client'] ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, f'#{product_name}') ble.blewrite(bleclient, '@e') db= dbconnect() current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
63
result= db.execute(dbquerry.update_slave(current_slave['uid'], None, None, product_name, None, None, None))
db.commit() dbclose(db) return resp.success() except: traceback.print_exc() return resp.failed() @bp.route('/disp-product-price', methods=['POST']) @authorize def display_product_price(): try: req_json = request.get_json() mac= req_json['mac'] product_price= req_json['product_price'] type_func= req_json['type'] if type_func == 'test': bleclient= ble.bleconnect(mac) ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, f'#{product_price}') ble.blewrite(bleclient, '@e') ble.bledisconnect(bleclient) if type_func == 'notest': # bleclient= ble.bleconnect(mac)
64 listble= get_threads() if len(listble) == 0: return resp.failed(); # print(listble) for x in listble: if x['mac'] == mac: bleclient = x['client'] ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, f'!{product_price}') ble.blewrite(bleclient, '@e') # ble.bledisconnect(bleclient) db= dbconnect() current_slave = db.execute(dbquerry.get_slave(mac)).fetchone()
result= db.execute(dbquerry.update_slave(current_slave['uid'], None, None, None, product_price, None, None))
db.commit() dbclose(db) return resp.success() except: return resp.failed() @bp.route('/disp-refresh', methods=['POST']) @authorize def display_refresh(): try: req_json = request.get_json()
65 mac= req_json['mac'] type_func= req_json['type'] if type_func == 'test': bleclient= ble.bleconnect(mac) ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, '@dr -') ble.blewrite(bleclient, '@e') ble.bledisconnect(bleclient) if type_func == 'notest': # bleclient= ble.bleconnect(mac) listble= get_threads() if len(listble) == 0: return resp.failed(); # print(listble) for x in listble: if x['mac'] == mac: bleclient = x['client'] ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, '@dr -') ble.blewrite(bleclient, '@e') # ble.bledisconnect(bleclient) return resp.success() except: return resp.failed()
66 @bp.route('/disp-refresh-all', methods=['POST']) @authorize def display_refresh_all(): try: db= dbconnect() all_slave = db.execute(dbquerry.get_slave()).fetchall() dbclose(db)
for slave in all_slave:
# bleclient= ble.bleconnect(slave['mac']) listble= get_threads()
if len(listble) == 0:
return resp.failed("No device connected"); # print(listble) for x in listble: if x['mac'] == slave['mac']: bleclient = x['client'] ble.blewrite(bleclient, '@s') ble.blewrite(bleclient, '@dr -') ble.blewrite(bleclient, '@e') # ble.bledisconnect(bleclient) return resp.success() except: return resp.failed() @bp.route('/connect', methods=['POST']) @authorize def connectSlave():
67 try: mac= request.args.get('mac') bleclient= ble.bleappendthread(mac) if bleclient is None: return resp.failed() return resp.success() except: return resp.failed() system.py
from flask import Blueprint, request from core.data import responses as resp from core.data.database import *
from core.cypherutil import *
from core.data import querry as dbquerry