Smart ward scheme design based on embedded Internet of Things technology


foreword

1. Requirements

Design a smart ward management system based on Internet of Things technology. Assume that there are 1-floor wards in the inpatient department of the hospital (the wards are distributed in parallel on both sides of the corridor), and there are at most 60 wards, each with 3 beds, numbered from 1 to 180. The equipment that can be used in each ward is as follows: 1 STM32F103 development board, 1 set of room temperature and humidity acquisition module (I2C interface, AHT20 module), room automatic light switch controller (controlled by PWM, gradually brightens at 7:00 every morning, 22 o'clock fades away), 3 sets of patient pulse & blood oxygen detectors (UART interface output pulse + blood oxygen digital value), 3 bedside emergency call button switches (press to call).
The STM32F103 development board in each ward is connected to the PC (upper computer) in the nurse monitoring room through the UART to 485 interface in the form of mobus networking. The PC computer can receive the temperature and humidity data of each ward (the cycle is 5 minutes), the emergency call signal at the bedside, and the patient's pulse and blood oxygen data (collected once every 30 minutes under normal conditions; when the pulse exceeds 120 or the blood oxygen value is lower than 90) switch to the critical state, real-time collection), displayed on the screen and saved to the MySQL database.

2. System design

For this solution, the original idea is to set up an rtos multi-tasking framework, and then put all the frameworks in the requirements into the thread to complete, including AHT temperature and humidity reading, pulse & blood oxygen data reading, key emergency call, timing switch lights and other tasks.

3. Functional modules

1. Detection of pulse & blood oxygen data: when the pulse exceeds 120 or the blood oxygen is lower than 90, it will be collected in real time, otherwise it will be collected every 30 minutes.
2. Press the button to realize emergency call: a GPIO interface judges the high and low level of the input signal.
3. The AHT20 module reads the temperature and humidity information: once every 5 minutes.
4. PWM control lights to turn on and off at regular intervals: gradually brighten at 7 o'clock in the morning and gradually dim at 10 o'clock in the evening.
5. Realize modbus communication through UART to 485.


3. System function module diagram

insert image description here

1. Schematic diagram of stm32 control module

STM32 minimum schematic diagram:

insert image description here
AHT20 schematic diagram:

insert image description here
Circuit schematic diagram:
insert image description here

Second, the realization of each functional module

1. The basic configuration of the whole system

  • RCC settings:

insert image description here

  • SYS settings

insert image description here

  • UART set serial port mode

insert image description here
insert image description here

  • I2C set DMA mode:

insert image description here
insert image description here

  • TIME3:

insert image description here
TIME2:
insert image description here
NVIC:
insert image description here
GPIO:
insert image description here
Clock tree setup:
insert image description here
Transplant RT-thread Nano:
insert image description here

  • Setup time:
    insert image description here

2. RTOS multitasking

Refer to the specific transplantation process:
transplant RT-thread Nano to complete a modbus interface temperature and humidity Slave device, let the upper computer PC obtain the temperature and humidity through the modbus protocol

1. Design thread

  • Create a new app_rt_thread.c file under the Application/USER folder
    1. Import the required header files:
#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"

Here we set up 4 sub-processes. The main process is to send data to the host computer through the serial port. The sub-processes include temperature and humidity acquisition, pulse & blood oxygen data measurement, button emergency call, and LED on and off.

 
struct rt_thread led1_thread;
rt_uint8_t rt_led1_thread_stack[128];
void led1_task_entry(void *parameter);

struct rt_thread usart1_thread;
rt_uint8_t rt_usart1_thread_stack[128];
void usart1_task_entry(void *parameter);

struct rt_thread usart2_thread;
rt_uint8_t rt_usart2_thread_stack[128];
void usart2_task_entry(void *parameter);

struct rt_thread button_thread;
rt_uint8_t rt_button_thread_stack[128];
void button_task_entry(void *parameter);
 
//初始化线程函数
void MX_RT_Thread_Init(void)
{
    
    
	//初始化LED1线程
rt_thread_init(&led1_thread,"led1",led1_task_entry,RT_NULL,&rt_led1_thread_stack[0],sizeof(rt_led1_thread_stack),3,20);
 rt_thread_init(&usart1_thread,"usart1",usart1_task_entry,RT_NULL,&rt_usart1_thread_stack[0],sizeof(rt_usart1_thread_stack),3,20);
 rt_thread_init(&usart2_thread,"usart2",usart2_task_entry,RT_NULL,&rt_usart2_thread_stack[0],sizeof(rt_usart2_thread_stack),3,20);
 rt_thread_init(&button_thread,"button",button_task_entry,RT_NULL,&rt_button_thread_stack[0],sizeof(rt_button_thread_stack),3,20);
	//开启线程调度
	rt_thread_startup(&led1_thread);
    rt_thread_startup(&usart1_thread);
    rt_thread_startup(&usart2_thread);
    rt_thread_startup(&button_thread);
}
 
//主任务
void MX_RT_Thread_Process(void)
{
    
    
	printf("系统正在运行!!!");
	rt_thread_delay(2000);
}
 
//LED1任务
void led1_task_entry(void *parameter)
{
    
    
}
//读取温湿度
void usart1_task_entry(void *parameter)
{
    
    
}
//读取脉搏&血氧
void usart2_task_entry(void *parameter)
{
    
    
}
//按键
void button_task_entry(void *parameter)
{
    
    
}

2. Configure the main function code

1. Add header files and import corresponding functions in main and c files;
insert image description here
2. Initialize thread and execute main program
insert image description here

3. Temperature and humidity reading module (I2C)

Specific process:
complete the data collection of AHT20 temperature and humidity sensor based on I2C protocol through STM32Cube configuration

Temperature and humidity reading module The sensor we use here is AHT20 and I2C protocol . The I2C communication protocol (Inter-Integrated Circuit) was developed by Philps. Because it has few pins, simple hardware implementation, and strong scalability, it does not require external transceiver devices such as USART and CAN. It is now widely used
in Communication between multiple integrated circuits (ICs) in a system.

1. After power on, wait for a period of time and wait for the device to work normally before reading the temperature and humidity:

  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
  uint32_t CT_data[2]={
    
    0,0};	
  volatile int  c1,t1;
  rt_thread_delay(50);
  AHT20_Init();
  rt_thread_delay(2500);

2. Read the temperature and humidity data after the CRC check, and then delay for 5 minutes


    AHT20_Read_CTdata_crc(CT_data);  //crc校验后,读取AHT20的温度和湿度数据 
	  c1 = CT_data[0]*100*10/1024/1024;  //计算得到湿度值c1(放大了10倍)
	  t1 = CT_data[1]*200*10/1024/1024-500;//计算得到温度值t1(放大了10倍)	
		printf("湿度:%d%s",c1/10,"%");
	  printf("温度:%d%s",t1/10,"℃");
	  printf("\r\n");
		rt_thread_mdelay(300000);//线程睡眠大约5分钟,挂起执行其他操作

4. LED timing switch light (pwm)

Here we use the time2 timer configured above plus PWM.

Get date and time:

RTC_DateTypeDef Data; //Get date structure
RTC_TimeTypeDef Time; //Get time structure

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	while(1){
    
    
		HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
		if(Time.Hours == 7&&Time.Minutes == 0&&Time.Seconds == 0){
    
    
			for(uint16_t i=1;i<500;i++){
    
    //灯渐亮
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
		else if(Time.Hours == 22&&Time.Minutes == 0&&Time.Seconds == 0){
    
    
			for(uint16_t i=499;i>=1;i--){
    
    //灯渐灭
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
	}

Get the real time once as a standard before initialization:

void getRealTime(void)
{
    
    
	HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &datebuff, RTC_FORMAT_BIN);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);
}
 
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
    
    
	if(hrtc->Instance == RTC)
	{
    
    
		getRealTime();	
	}
}

5. Press the button to realize the alarm signal

Because we set the PB5 pin as an input at will, which is a button, and the button is pressed to send an alarm signal. Here we use the high and low levels of the PA4, PA5, and PA6 pins as the alarm signals of the three buttons:

Button sub-thread code:

while(1){
    
    
		switch(KEY_Scan(0))
		{
    
    				 
			case KEY1_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);
				break;
			case KEY2_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
				break;
			case KEY3_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_6);
				break;
			default:
				break;
		}
	}

Key debounce:

uint8_t KEY_Scan(uint8_t mode)
{
    
    
	static uint8_t key_up=1;//按键松开标志位
	if(key_up&&((KEY1==0)||(KEY2==0)||(KEY3==0)))
	{
    
    
		HAL_Delay(10);//过滤抖动时间
		key_up=0;
		if(KEY1==0)return KEY1_PRES;
		if(KEY2==0)return KEY2_PRES;
		if(KEY3==0)return KEY3_PRES;
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1)key_up=1;
 	return 0;//无按键按下
}

6. Pulse & blood oxygen data reading

Note: The pulse & oximeter here has never been used, and it should also communicate through the serial port using the modbus protocol:

For specific usart communication, please refer to: Complete a STM32 USART serial communication program through stm32CubeMX
to define the addresses and variables of pulse and blood oxygen:

#define REG_SPO2_ADDRESS     0x0001
#define REG_PULSE_RATE_ADDRESS  0x0002
uint16_t spo2_value;
uint16_t pulse_rate_value;

modbus read register request:

void modbus_handle_read_registers(uint8_t *data, uint16_t len)
{
    
    
    uint16_t start_address;
    uint16_t register_count;
    uint16_t i;
    // 解析读寄存器的请求报文
    start_address = (data[2] << 8) | data[3];
    register_count = (data[4] << 8) | data[5];
    // 根据地址和数量读取寄存器数据
    for (i = 0; i < register_count; i++)
    {
    
    
        if (start_address + i == REG_SPO2_ADDRESS)
        {
    
    
            data[9 + i * 2] = (spo2_value >> 8) & 0xFF;
            data[10 + i * 2] = spo2_value & 0xFF;
        }
        else if (start_address + i == REG_PULSE_RATE_ADDRESS)
        {
    
    
            data[9 + i * 2] = (pulse_rate_value >> 8) & 0xFF;
            data[10 + i * 2] = pulse_rate_value & 0xFF;
        }
    }

    // 发送读寄存器响应报文
    uart_send_data(data, 9 + register_count * 2);
}

Main loop function:

void modbus_loop(void)
{
    
    
    uint8_t data[256];
    uint16_t len;
    uint16_t new_spo2;
    uint16_t new_pulse_rate;
    while (1)
    {
    
    
        // 等待接收数据
        len = uart_receive_data(data, sizeof(data));
        // 处理读寄存器请求
        if (data[0] == 0x01 && data[1] == 0x03)
        {
    
    
            modbus_handle_read_registers(data, len);
        }
        // 处理写寄存器请求
        else if (data[0] == 0x01 && data[1] == 0x10)
        {
    
    
            // 根据地址和数量读取寄存器数据
            new_spo2 = (data[9] << 8) | data[10];
            new_pulse_rate = (data[11] << 8) | data[12];
            update_spo2_and_pulse_rate(new_spo2, new_pulse_rate);
            // 发送写寄存器响应报文
            uart_send_data(data, 8);
        }
    }
}

Define the sampling state:

typedef enum {
    
    
    NORMAL,
    CRITICAL
} SamplingState;

SamplingState sampling_state = NORMAL;

Define the time pulse:

#define NORMAL_SAMPLING_INTERVAL 1800 // 30 分钟
#define CRITICAL_SAMPLING_INTERVAL 10 // 10 秒、算作实时
#define CRITICAL_PULSE_RATE_THRESHOLD 120//脉搏120
#define CRITICAL_SPO2_THRESHOLD 90//血氧90

The callback function implements state-by-state sampling:

void sampling_timer_callback(void)
{
    
    
    if (sampling_state == NORMAL)//正常状态
    {
    
    
        if (pulse_rate_value > CRITICAL_PULSE_RATE_THRESHOLD || spo2_value < CRITICAL_SPO2_THRESHOLD)
        {
    
    
            sampling_state = CRITICAL;
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
    
    
            // 正常状态下采样
            send_request_to_spo2_pulse_sensor();
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
    else
    {
    
    
        // 危重状态下采样
        send_request_to_spo2_pulse_sensor();
        if (pulse_rate_value <= CRITICAL_PULSE_RATE_THRESHOLD && spo2_value >= CRITICAL_SPO2_THRESHOLD)
        {
    
    
            sampling_state = NORMAL;
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
    
    
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
}

7. UART to 485 interface communicates with modbus

RS-485 communication uses serial port 3, here we find that we have set up one less serial port, the baud rate is 9600, and the sending DMA and receiving DMA of serial port 3 are enabled, and both select normal mode.

Modbus protocol sends data:

void RS485_BUS_SendData(u8 *buf, u8 len)
{
    
    
	u8 t;
	for(t=0;t<len;t++)
	{
    
    
		while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
		USART_SendData(UART5,buf[t]);
	}
	
	while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
	RS485_BUS_RXCNT = 0;
}

The modbus protocol accepts data:

void RS485_BUS_ReceiveData(u8 *buf,u8 *len)
{
    
    
	u8 rxlen=RS485_BUS_RXCNT;
	u8 i=0;
	*len=0;
	delay_ms(10);
	if((rxlen==RS485_BUS_RXCNT)&&rxlen)				//接收到了数据且接收完成
	{
    
    
		for(i=0;i<rxlen;i++)
		{
    
    
			buf[i]=RS485_BUS_RXBUF[i];
		}
		*len=RS485_BUS_RXCNT;
		RS485_BUS_RXCNT=0;
	}
}

3. Summary

This system design adopts the multi-tasking framework of RTthread-nano, which makes the route of the whole system clear at once, and can also set the priority level of threads, so as to plan the whole system well. At the same time, with this multi-threaded concurrent task processing, each thread has its own independent running space, that is, the thread stack, and each thread focuses on doing its own thing without interfering with each other and has high independence. And when each thread uses the delay function, the thread will suspend the task and automatically let the CPU be used by other threads that need it. We can safely use the delay function in the thread and at the same time the utilization of the CPU has been greatly improved. improve. The code of this system is only divided into the multitasking framework according to the designed modules, and the main code or part of each corresponding module is displayed. In fact, the implementation of each module still needs It takes a certain amount of time to achieve it, and you will encounter various difficulties. Although this time, the big homework is only a scheme design, but the content covers a lot of the content of this semester. During the design process, I found that some places were not so impressive, so I reviewed the corresponding part, and then looked back and found understanding It's different, but it still feels good!

Guess you like

Origin blog.csdn.net/qq_52215423/article/details/128683302