CW32L083 Intelligent Temperature and Humidity Monitoring System
[Copy link]
【System functions】
CW32l083 is the main control of wireless terminal data transmission and reception. It runs the domestic RT-Thread operating system. The main function is to use E31-TTL-50 to receive data sent by each module, parse data, analyze data, display data, and realize information display, as well as abnormal situation display and warning functions.
The wireless terminal is mainly controlled by cw32L031, collects data from sht30 temperature and humidity sensors, and uploads the data through the E31-TTL-50 wireless module, achieving ultra-low power consumption of 5 microamperes of standby current.
【Functional Module】
Host:
- Receiving module: receives the temperature and humidity data sent by the wireless terminal module, analyzes the data, and updates the module data.
- Inspection module: Regularly inspect the data of each wireless terminal to determine the working status, update the display, and alarm signs.
- Display module: Generates display data according to the working status of each module and displays it on the TFT screen.
- Alarm module: drives the pwm module, loads the pwm reload value, and issues a warning sound.
Wireless Terminal:
- Temperature and humidity collection module: collects sht30 data.
- Sending module: Packs the data and sends it wirelessly.
- Sleep module: After sending data, it enters deep sleep state and is awakened by the AWT module at regular intervals.
【hardware】
Host:
- CW32L083VxTx StartKit REV01 development board.
- ST7735TFT display.
- E31-TTL-50 wireless serial port module.
Wireless Terminal:
- CW32L031 Development Board
- Sht30 temperature and humidity sensor.
- E31-TTL-50 wireless serial port module.
- Rechargeable lithium battery.
【Development environment】
- The code compilation environment is collected from ubuntu20.4;
- The code editing tool is vscode 1.79.2;
- The cross compiler is arm-nano-eabi-gcc;
- The firmware library is the firmware library provided by cw32;
- The gcc startup files and links are ported by the author from other software on cortex-M0+;
- The downloader is wch-link distributed by CW32;
- The code download software is pyocd;
- The debugging tool is gdb.
The compilation environment and tools of this development board all use open source tools.
【operating system】
For the main control of this project, the author transplanted RTT-Thread Nano 3.15 version. As a domestic open source and free operating system, RTT can provide powerful functions and provide strong support for the performance of CW32.
【Program flow chart】
- The host side starts two main tasks by RTT, which are used for data display and inspection, and uses serial port interrupts to process the received data in real time. GTIM starts the PWM task regularly to drive the BEEP on the development board. The flow chart is as follows:
- The wireless terminal adopts a single-line process, which mainly enters sleep mode after collecting data. Only by being extremely simple can the best power consumption control be achieved. The flow chart is as follows:
【Schematic diagram】
- Wireless terminal collection:
- Host side:
【Programming】
- Wireless acquisition terminal
- IIC initialization, using analog I2C main code is
- Host side
- The core of data processing on the host side is sht30 data. The structure declaration is as follows:
typedef struct _sht30_data
{
uint32_t ID;
int temp; //temperature value
int temp_upper_limit; //temperature upper limit
int temp_lower_limit; //temperature lower limit
uint16_t humi; //humidity
uint16_t humi_upper_limit; //humidity upper limit
uint16_t humi_lower_limit; //humidity upper limit
uint32_t time_tick; //update data timing
enum _sht30_errcode sht_errcode;
} SHT30_infor;
is mainly used to store data core. All future tasks will be performed on this module.
- At the same time, declare an enumeration to determine the status of the measurement point:
enum _sht30_errcode{
NORMAL=0,
ABNORMAL,
OFFLINE,
};
- First, we need to agree on some default parameters, including the maximum number of sensors, upper and lower limits of temperature and humidity alarms, and the initial value of the number of inspections:
#define maxID 2
#define MaxTime 300
#define
HUMI_LOWER 500 #define HUMI_UPPER 750
#define TMPE_LOWER 100
#define TMPE_UPPER 300.
Now our data structure design is completed.
- Clock initialization, since the host needs to process data at high speed, it is configured to 64MHz here:
void RCC_cofiguration(void)
{
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
// Enable PLL and multiply it to 64MHz through HSI
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8); //HSI default output 8MHz
///< When the clock source HCLK used is greater than 24M and less than or equal to 48MHz: set the FLASH read wait cycle to 2 cycles
///< When the clock source HCLK used is greater than 48M and less than or equal to 72MHz: set the FLASH read wait cycle to 3 cycles
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
// Clock switch to PLL
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}
- The wireless reception on the host side uses uart1, and the port selects PE8 and PE9 as TXD and RXD. The initialization code is:
void E31_UART_Init(void)
{
uint32_t PCLK_Freq;
GPIO_InitTypeDef GPIO_InitStructure = {0};
UART_InitTypeDef UART_InitStructure = {0};
PCLK_Freq = SystemCoreClock >> pow2_table[CW_SYSCTRL->CR0_f.HCLKPRS];
PCLK_Freq >>= pow2_table[CW_SYSCTRL->CR0_f.PCLKPRS];
// The debugging serial port uses UART3
// PA8->TX
// PA9<-RX
// Clock enable
RCC_AHBPeriphClk_Enable(E31_UART_GPIO_CLK, ENABLE);
E31_UART_APBClkENx(E31_UART_CLK, ENABLE);
// Set UART TX RX multiplexing first, then set GPIO properties to avoid glitches on the port line
E31_UART_AFTX;
E31_UART_AFRX;
GPIO_InitStructure.Pins = E31_UART_TX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(E31_UART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.Pins = E31_UART_RX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
GPIO_Init(E31_UART_RX_GPIO_PORT, &GPIO_InitStructure);
UART_InitStructure.UART_BaudRate = E31_UART_BaudRate;
UART_InitStructure.UART_Over = UART_Over_16;
UART_InitStructure.UART_Source = UART_Source_PCLK;
UART_InitStructure.UART_UclkFreq = PCLK_Freq;
UART_InitStructure.UART_StartBit = UART_StartBit _FE; UART_InitStructure.UART_StopBits
= UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(E31_UARTx, &UART_InitStructure);
//Priority, no priority grouping
NVIC_SetPriority(E31_UART_IRQ, 0);
//UARTx interrupt enable
NVIC_EnableIRQ(E31_UART_IRQ);
}
At the same time, configure the interrupt function. The main function is to determine whether the frame is received. If the frame tail is received, the data is handed over to the callback function for processing. The code is as follows:
void UART1_UART4_IRQHandler(void)
{
/* USER CODE BEGIN */
uint8_t TxRxBuffer;
if(UART_GetITStatus(CW_UART1, UART_IT_RC) != RESET)
{
TxRxBuffer = UART_ReceiveData_8bit(CW_UART1);
if(e31_rx_cnt < E31_RX_MAXLEN)
{
if ((TxRxBuffer == 0x0A) && (e31_rx_state == 1))
{
e31_rx_state = 2;
e31_exp_data();
}
else if ((TxRxBuffer == 0x0D) && (e31_rx_state == 0))
{
e31_rx_state = 1;
}
else if (e31_rx_state == 0)
{
e31_rx_buff[e31_rx_cnt] = TxRxBuffer;
e31_rx_cnt ++;
}
}
else
{
e31_rx_cnt = 0;
e31_rx_state = 0;
}
UART_ClearITPendingBit(CW_UART1, UART_IT_RC);
}
/* USER CODE END */
}
Meanwhile, callback function is used to update the sht30 data for processing and parsing data:
void e31_exp_data(void)
{
int temp;
uint16_t humi;
uint32_t ID;
if(e31_rx_state == 2)
{
if(e31_rx_cnt == 14)
{
temp = e31_rx_buff[10]<<8 | e31_rx_buff[11];
humi = e31_rx_buff[12]<<8 | e31_rx_buff[14];
ID = e31_rx_buff[6]<<24 | e31_rx_buff[7]<<16 | e31_rx_buff[8]<<8 | e31_rx_buff[9];
updata_sht30(temp, humi, ID);
rt_kprintf(" ID:%X, temp:%d, humi:%d\r\n", ID, temp, humi);
}
}
e31_rx_cnt = 0;
e31_rx_state = 0;
}
- The driver of ST7735 uses analog SPI for driving. For detailed driver information, see the project source code package.
- PWM drive, pwm uses PA6 as the pwm output terminal, initialized to 1KHz output to drive the onboard buzzer. After initializing the driver, we load the maximum load when the duty cycle is 100%, so that the buzzer stops. When the alarm sound needs to be input later, adjust it to 50% duty cycle to realize the buzzer alarm sound:
void init_beep(void)
{
GTIM_InitTypeDef GTIM_InitStruct = {0};
__RCC_GTIM1_CLK_ENABLE(); // GTIM2 clock enable
/* PA6 PWM output */
__RCC_GPIOA_CLK_ENABLE();
PA06_AFx_GTIM1CH1();
PA06_DIR_OUTPUT();
PA06_DIGTAL_ENABLE();
GTIM_InitStruct.Mode = GTIM_MODE_TIME;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV2;
// GTIM_InitStruct.ReloadValue = 60100UL - 1; // PWM frequency is 48M/60100=800Hz, SPWM period = 800/2/1000= 0.4Hz
GTIM_InitStruct.ReloadValue = 32000UL - 1; // PWM frequency is 64M/2/64000=1000Hz, SPWM period = 800/2/1000= 0.4Hz
GTIM_InitStruct.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL1, GTIM_OC_OUTPUT_PWM_HIGH);
GTIM_SetCompare1(CW_GTIM1, 32000-1);
GTIM_Cmd(CW_GTIM1, ENABLE);
}
void alarm_ON(void)
{
GTIM_SetCompare1(CW_GTIM1, 16000-1);;
}
void alarm_OFF(void)
{
GTIM_SetCompare1(CW_GTIM1, 32000-1);
}
- According to the program flow chart, we created two tasks, one for the inspection task to realize the data monitoring of the sensor module and update the working status. The code is as follows:
/* Inspection task*/
void thread_sht30_check_entry(void *parameter)
{
int i;
uint8_t alarm_sta;
while(1)
{ alarm_sta = 0;
for(i=0; i<maxID; i++)
{
if(sht30.time_tick == 0)
{
//Send offline warning
sht30.sht_errcode = OFFLINE;
sht30.temp = 0;
sht30.humi = 0;
alarm_sta ++;
}
else if (sht30.temp < sht30.temp_lower_limit \
|| sht30.temp > sht30.temp_upper_limit \
|| sht30.humi < sht30.humi_lower_limit \
|| sht30.humi > sht30.humi_upper_limit )
{
sht30.sht_errcode = ABNORMAL;
sht30.time_tick--;
alarm_sta++;
}
else
{
sht30.sht_errcode = NORMAL;
sht30.time_tick--;
}
}
if(alarm_sta > 0)
{
alarm_ON();
}
else
{
alarm_OFF();
}
rt_thread_mdelay(500);
}
}
/* Inspection task*/
void sht30_check(void)
{
rt_thread_init(&tid_check_sht30,
"sht30_check",
thread_sht30_check_entry,
RT_NULL,
&thread_sht30_check_stack[0],
sizeof(thread_sht30_check_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&tid_check_sht30);
}
- The display task is to display data according to the working status of the sensor at a fixed time. It mainly displays different colors according to the three states and whether the temperature and humidity exceed or fall below the limit. The code is as follows:
/* Thread display entry function*/
static void thread_lcd_entry(void *parameter)
{
sht30_data_Init();
char buff_temp[15];
char buff_humi[15];
uint16_t temp_background_color, temp_font_color;
uint16_t humi_background_color, humi_font_color;
int y_offset = 0;
int i = 0;
while (1)
{
y_offset = 46;
for(i=0;i<maxID;i++)
{
rt_kprintf("sensorID:%d stata: %d", i+1, sht30.sht_errcode);
y_offset = y_offset + i*70;
sprintf(buff_temp,"%d%d.%d",sht30.temp/100, sht30.temp/10%10, sht30.temp%10);
sprintf(buff_humi,"%d%d.%d",sht30.humi/100, sht30.humi/10%10, sht30.humi%10);
switch (sht30.sht_errcode)
{
case NORMAL:
temp_background_color = GRAY0;
temp_font_color = BLUE;
humi_background_color = GRAY0;
humi_font_color = BLUE;
break;
case OFFLINE:
temp_background_color = GRAY2;
temp_font_color = BLUE;
humi_background_color = GRAY2;
humi_font_color = BLUE;
sprintf(buff_temp, " ");
sprintf(buff_humi, " ");
break;
case ABNORMAL:
if(sht30.humi<sht30.humi_lower_limit || sht30.humi > sht30.humi_upper_limit)
{
humi_background_color = YELLOW;
humi_font_color = BLACK;
}
else
{
humi_background_color = GRAY0;
humi_font_color = BLUE;
}
if(sht30.temp<sht30.temp_lower_limit || sht30.temp > sht30.temp_upper_limit)
{
temp_background_color = YELLOW;
temp_font_color = BLACK;
}
else
{
temp_background_color = GRAY0;
temp_font_color = BLUE;
}
break;
default:
break;
}
Gui_DrawFont_GBK16(90,y_offset,temp_font_color,temp_background_color,buff_temp); //Update display
Gui_DrawFont_GBK16(90,y_offset+20,humi_font_color,humi_background_color,buff_humi);
}
rt_thread_mdelay(10000);
}
}
/* Display tasks */
void lcd_show(void)
{
rt_thread_init(&tid_show_sht30,
"lcd show",
thread_lcd_entry,
RT_NULL,
&thread_lcd_show_stack[0],
sizeof(thread_lcd_show_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&tid_show_sht30);
}
【Engineering effect】
- The wireless data acquisition terminal can collect data and realize long-term operation at ultra-long distance and ultra-low power consumption according to the set time. The power consumption is measured as follows:
From the above data, we can see that the standby current is about 7.5 microamperes. When data reporting is enabled once every two minutes, the maximum working current is 46.5mA, the average current is 110uA, and the average power is 362 microwatts. It can be inferred that a 1000mAH battery can continuously supply power for about 100 days. If we use caching within the normal range of temperature and humidity and upload data once every hour, it is expected that the working time can be extended by 30 times, that is, about 10 years of standby.
- On the host side, we can monitor the implementation status of the wireless data acquisition workstation in real time.
Offline alert:
Abnormal temperature:
【Project Summary】
After half a month of project development, we have mainly realized the transplantation of RT-Thread Nano, the driver of the thermometer and humidity meter, the wireless serial port module, and the LCD screen, and realized the basic functions of a temperature and humidity monitoring system.
【Project Expansion Plan】
The next step will be to continue to improve the monitoring system.
- For data storage.
- View historical data.
- Connect to the Internet and distribute data to the server.
|