1 簡介
Hi,大家好,今天向大家介紹一個學長做的單片機項目
基于STM32的智能藥箱系統(tǒng)設(shè)計與實現(xiàn)
大家可用于 課程設(shè)計 或 畢業(yè)設(shè)計
2 緒論
2.1 課題背景
照顧老人, 特別是提醒老人準時吃藥已經(jīng)成為了一個社會關(guān)心的問題。長期記錄吃藥種類、 吃藥時間能為分析老人的病理提供有力的依據(jù)。
基于目 前市場需求, 學長設(shè)計了一款基于 STM32F103 并結(jié)合網(wǎng)絡(luò)來提醒老人按時吃藥的系統(tǒng)。
整個智能藥箱系統(tǒng)分為智能藥箱端與遠程服務(wù)器端兩個部分, 智能藥箱端用于提醒, 服務(wù)器端用于記錄監(jiān)督
2.2 實現(xiàn)功能
整個系統(tǒng)經(jīng)過多次測試和應用, 各功能模塊都可以正常運行, 符合設(shè)計要求。 具有的功
能包括: 從服務(wù)器端的數(shù)據(jù)庫中實時獲取藥物信息, 鬧鐘信息等并存儲。 播放音頻文件提醒老人用藥, 在液晶屏上提示吃藥信息, 老人可以根據(jù)顯示信息正確用藥。 智能藥箱端能記錄藥箱中每種藥的數(shù)目, 并進行藥量檢測, 在藥量不足時發(fā)出提醒, 提示用戶配藥。 另外還可以對老人取藥動作進行記錄, 將取藥狀態(tài)通過互聯(lián)網(wǎng)傳輸給服務(wù)器監(jiān)控端, 為以后看病做依據(jù)。 另外, 智能藥箱端通過程序界面方便的配置智能藥箱內(nèi)部的網(wǎng)絡(luò)信息,包括服務(wù)器地址、端口、 網(wǎng)關(guān)等。
它的主要功能分為下面幾個部分:
-
- 提醒功能
-
- 取藥監(jiān)督
-
- 記錄吃藥時間
-
- 對老人是否吃藥及時做出反應
-
- 顯示吃藥信息
服務(wù)器中記錄下的取藥時間可以作為以后看病的有力依據(jù), 使醫(yī)生能夠準確對老人的病情做出判斷, 減少一定的醫(yī)學失誤。
盡管智能藥箱的發(fā)展并沒有早期預期的那么快。 但是, 它的應用價值極高, 針對性也很
強, 可以有效的管理老年人用藥, 減少兒女對父母吃藥問題上的擔心。 所以, 隨著科技的發(fā)展, 智能藥箱的普及將指日可待。
3 系統(tǒng)設(shè)計
3.1 系統(tǒng)架構(gòu)
設(shè)計上必須從上圖架構(gòu)出發(fā), 從上圖可以得出智能藥箱系統(tǒng)的總體功能:
- a) 鬧鐘提醒吃藥
- b) 液晶顯示吃藥信息
- c) 實時采集取藥狀態(tài)
- d) 存儲從服務(wù)器端獲取的信息
- e) 配置界面配置藥箱端的網(wǎng)絡(luò)參數(shù)
- f) 實時檢測藥數(shù)量, 不足提醒用戶
- g) 增加服務(wù)器端, 記錄藥物信息, 鬧鐘, 取狀態(tài)藥等信息
3.2 硬件設(shè)計
3.2.1 wifi電路設(shè)計
3.2.2 其他電路設(shè)計
略,內(nèi)容相對冗余,也不是本文介紹的重點,需要詳細設(shè)計方案,可以在文章末尾聯(lián)系學長。
3.3 軟件設(shè)計
3.3.1 軟件工作流程
程序包括模塊的驅(qū)動與功能操作, 驅(qū)動主要是硬件的驅(qū)動, 每個模塊只有在硬件驅(qū)動成功后, 才能執(zhí)行其余功能。
3.3.2 鬧鈴提醒程序設(shè)計
下圖是鬧鈴提醒的程序流程圖, STM32 從 TF 卡中讀取 512 字節(jié)數(shù)據(jù), 按順序傳輸給VS1003 進行音頻轉(zhuǎn)換和播放。 然后, 依次讀取下一個扇區(qū)的 512 個字節(jié)進行播放。 如果播放過程中檢測到藥箱打開, 則停止鬧鈴并退出。
3.3.3 液晶顯示程序設(shè)計
下圖是鬧鈴提醒的程序流程圖, STM32 從 TF 卡中讀取 512 字節(jié)數(shù)據(jù), 按順序傳輸給VS1003 進行音頻轉(zhuǎn)換和播放。 然后, 依次讀取下一個扇區(qū)的 512 個字節(jié)進行播放。 如果播放過程中檢測到藥箱打開, 則停止鬧鈴并退出。
3.3.4 信息存儲程序設(shè)計
存儲信息的過程如下:
當智能藥箱接收網(wǎng)絡(luò)參數(shù), 藥物信息時, 首先對數(shù)據(jù)按協(xié)議正確處理, 然后向 24C08 發(fā)送存儲地址, 如果地址上已經(jīng)有數(shù)據(jù), 則讀取。 然后將地址上的數(shù)據(jù)清空, 將剛才接收并處理的信息與從存儲芯片讀取的數(shù)據(jù)進行統(tǒng)一處理, 保留不變的, 修改已改變的數(shù)據(jù), 再存入地址中。 如果需要讀取存儲芯片上的數(shù)據(jù), 則向 24C08 發(fā)送地址讀取數(shù)據(jù)即可, 程序流程圖如下圖所示。
3.3.5 報警電路程序設(shè)計
智能藥箱與服務(wù)器之間每半小時需要藥數(shù)目的校正, 以便兩者之間可以同步。 當藥箱內(nèi)部藥量不足時, 就會觸發(fā)蜂鳴器以示提醒。 蜂鳴器采用是有源蜂鳴器, 接通電流就會發(fā)聲,整個報警的程序流程圖如下圖所示。
3.4 部分實現(xiàn)代碼
#include <Arduino.h>
#include "MAX30105.h"
#include "heartRate.h"
#include "motor.h"
#define DEBUG
#define OUT_POSITION 1
#define TAKE_POSITION 2
#define OPEN_POSITION 5
bool isTaskTake = false;
int boxdisplacement = 0;
int pillsParameters[2] = {3, 1};
enum BOXNAME { BOX1 = 1, BOX2, BOX3, BOX4, BOX5, BOX6 };
TaskHandle_t taskGreenLEDHandler;
void rotateToOpen(int boxName);
void rotateToClose(int boxName);
void rotateToTake(int boxName);
void rotateToOut(int boxName);
void takePills(int boxName, int pillsNumber);
void taskTakePills(void *pvParameters);
void takePillsTest();
void setup() {
Serial.begin(115200);
delay(1000);
motor_init();
// xTaskCreate(taskPrint, "taskPrint", 1000, NULL, 1, NULL);
}
void loop() {
if (Serial.available()) {
char message = ' ';
message = Serial.read();
if (message == 't') {
if (!isTaskTake) {
isTaskTake = true;
Serial.println("*************\n*開始取藥過程*\n*************");
// xTaskCreate(taskTakePills, "taskTakePills", 20000,
// pillsParameters, 1, NULL);
takePills(3, 1);
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'i') { //開蓋
if (!isTaskTake) {
isTaskTake = true;
taskOpenBox(NULL);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'j') { //關(guān)蓋
if (!isTaskTake) {
isTaskTake = true;
taskCloseBox(NULL);
digitalWrite(PIN_ENABLE, HIGH);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'm') { //關(guān)蓋
if (!isTaskTake) {
isTaskTake = true;
digitalWrite(PIN_ENABLE, LOW);
takePillsTest();
digitalWrite(PIN_ENABLE, HIGH);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'g') { //左旋
if (!isTaskTake) {
isTaskTake = true;
digitalWrite(PIN_ENABLE, LOW);
taskRotate(LEFT);
boxdisplacement--;
digitalWrite(PIN_ENABLE, HIGH);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'h') { //右旋
if (!isTaskTake) {
isTaskTake = true;
digitalWrite(PIN_ENABLE, LOW);
taskRotate(RIGHT);
boxdisplacement++;
digitalWrite(PIN_ENABLE, HIGH);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == 'k') {
if (!isTaskTake) {
isTaskTake = true;
Serial.println("開始推出藥盒");
digitalWrite(PIN_ENABLE, LOW);
taskPushBoxOut(NULL);
delay(1000);
Serial.println("開始推入藥盒");
taskPullBoxIn(NULL);
digitalWrite(PIN_ENABLE, HIGH);
isTaskTake = false;
} else {
Serial.println("已經(jīng)有一個取藥任務(wù)了");
}
}
if (message == '3') {
taskPillsOut(NULL);
}
if (message == 'a') { //開氣泵
openPump();
}
if (message == 'b') { //關(guān)氣泵
closePump();
}
}
}
void rotateToOpen(int boxName) {
int currentPosition = boxName + boxdisplacement;
int targetRotation = OPEN_POSITION - currentPosition;
if (targetRotation >= 0) {
#ifdef DEBUG
Serial.println("rorate right " + String(targetRotation) +
" step to open.");
Serial.println("向右旋轉(zhuǎn) " + String(targetRotation) + " 步以開蓋.");
#endif
for (int i = 0; i < targetRotation; i++) {
taskRotate(RIGHT);
boxdisplacement++;
}
} else {
#ifdef DEBUG
Serial.println("rorate left " + String(-targetRotation) +
" step to open.");
Serial.println("向左旋轉(zhuǎn) " + String(-targetRotation) + " 步以開蓋.");
#endif
for (int i = 0; i < -targetRotation; i++) {
taskRotate(LEFT);
boxdisplacement--;
}
}
}
void rotateToClose(int boxName) {
int currentPosition = boxName + boxdisplacement;
int targetRotation = OPEN_POSITION - currentPosition;
if (targetRotation >= 0) {
#ifdef DEBUG
Serial.println("rorate right " + String(targetRotation) +
" step to close.");
Serial.println("向右旋轉(zhuǎn) " + String(targetRotation) + " 步以關(guān)蓋.");
#endif
for (int i = 0; i < targetRotation; i++) {
taskRotate(RIGHT);
boxdisplacement++;
}
} else {
#ifdef DEBUG
Serial.println("rorate right " + String(-targetRotation) +
" step to close.");
Serial.println("向左旋轉(zhuǎn) " + String(-targetRotation) + " 步以關(guān)蓋.");
#endif
for (int i = 0; i < -targetRotation; i++) {
taskRotate(LEFT);
boxdisplacement--;
}
}
}
void rotateToTake(int boxName) {
int currentPosition = boxName + boxdisplacement;
int targetRotation = TAKE_POSITION - currentPosition;
if (targetRotation >= 0) {
#ifdef DEBUG
Serial.println("rorate right " + String(targetRotation) +
" step to take pills.");
Serial.println("向右旋轉(zhuǎn) " + String(targetRotation) + " 步以取藥.");
#endif
for (int i = 0; i < targetRotation; i++) {
taskRotate(RIGHT);
boxdisplacement++;
}
} else {
#ifdef DEBUG
Serial.println("rorate right " + String(-targetRotation) +
" step to take pills.");
Serial.println("向左旋轉(zhuǎn) " + String(-targetRotation) + " 步以取藥.");
#endif
for (int i = 0; i < -targetRotation; i++) {
taskRotate(LEFT);
boxdisplacement--;
}
}
}
void rotateToOut(int boxName) {
int currentPosition = boxName + boxdisplacement;
int targetRotation = OUT_POSITION - currentPosition;
if (targetRotation >= 0) {
#ifdef DEBUG
Serial.println("rorate right " + String(targetRotation) +
" step to add pills.");
Serial.println("向右旋轉(zhuǎn) " + String(targetRotation) + " 步以換(加)藥.");
#endif
for (int i = 0; i < targetRotation; i++) {
taskRotate(RIGHT);
boxdisplacement++;
}
} else {
#ifdef DEBUG
Serial.println("rorate right " + String(-targetRotation) +
" step to add pills.");
Serial.println("向左旋轉(zhuǎn) " + String(-targetRotation) +
" 步以換(加)藥.");
#endif
for (int i = 0; i < -targetRotation; i++) {
taskRotate(LEFT);
boxdisplacement--;
}
}
}
void takePills(int boxName, int pillsNumber) {
digitalWrite(PIN_ENABLE, LOW);
// taskPillsUp(NULL);
rotateToOpen(boxName);
taskOpenBox(NULL);
rotateToTake(boxName);
// stepper2.runToNewPosition(5000);
midToLeft();
openPump();
// stepper2.runToNewPosition(6400);
delay(1000);
digitalWrite(PIN_STEPPER2_DIR, LOW);
for (int j = 0; j <= 5; j++) {
for (int i = 0; i < 500; i++) {
digitalWrite(PIN_STEPPER2_STEP, HIGH);
delayMicroseconds(400);
digitalWrite(PIN_STEPPER2_STEP, LOW);
delayMicroseconds(400);
}
delay(500);
digitalWrite(PIN_STEPPER2_DIR, HIGH);
for (int k = 0; k < 500; k++) {
digitalWrite(PIN_STEPPER2_STEP, HIGH);
delayMicroseconds(400);
digitalWrite(PIN_STEPPER2_STEP, LOW);
delayMicroseconds(400);
}
digitalWrite(PIN_STEPPER2_DIR, LOW);
}
// stepper2.setMaxSpeed(2500);
// stepper2.setAcceleration(1500);
// stepper2.runToNewPosition(-20000);
leftToMid();
taskMidToRight(NULL);
closePump();
openAir();
delay(3000);
closeAir();
delay(2000);
taskPillsOut(NULL);
taskRightToMid(NULL);
// xTaskCreate(taskRightToMid, "taskRightToMid", 1000, NULL, 1, NULL);
isTaskTake = false;
digitalWrite(PIN_ENABLE, HIGH);
}
void taskTakePills(void *pvParameters) {
int boxName = ((int *)pvParameters)[0];
int pillsNumber = ((int *)pvParameters)[1];
#ifdef DEBUG
Serial.println("boxName: " + String(boxName));
Serial.println("pillsNumber: " + String(pillsNumber));
#endif
takePills(boxName, pillsNumber);
vTaskDelete(NULL);
}
void takePillsTest() {
midToLeft();
openPump();
// stepper2.runToNewPosition(6400);
delay(1000);
digitalWrite(PIN_STEPPER2_DIR, LOW);
for (int j = 0; j <= 5; j++) {
for (int i = 0; i < 500; i++) {
digitalWrite(PIN_STEPPER2_STEP, HIGH);
delayMicroseconds(400);
digitalWrite(PIN_STEPPER2_STEP, LOW);
delayMicroseconds(400);
}
delay(500);
digitalWrite(PIN_STEPPER2_DIR, HIGH);
for (int k = 0; k < 500; k++) {
digitalWrite(PIN_STEPPER2_STEP, HIGH);
delayMicroseconds(400);
digitalWrite(PIN_STEPPER2_STEP, LOW);
delayMicroseconds(400);
}
digitalWrite(PIN_STEPPER2_DIR, LOW);
}
// stepper2.setMaxSpeed(2500);
// stepper2.setAcceleration(1500);
// stepper2.runToNewPosition(-20000);
leftToMid();
taskMidToRight(NULL);
closePump();
openAir();
delay(3000);
closeAir();
taskRightToMid(NULL);
}
作品展示:文章來源:http://www.zghlxwxcb.cn/news/detail-760015.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-760015.html
4 最后
到了這里,關(guān)于單片機畢設(shè) 基于STM32的智能藥箱系統(tǒng)設(shè)計與實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!