主控仍旧是STM32F103C8T6,实时操作系统选择的是FreeRTOS。
主要功能:
①环境信息采集并上传至微信小程序
②微信小程序下发指令控制家电
③由雨滴传感器和步进电机能够实现下雨自动收起衣服,停雨自动晒出衣服(由于驱动板和步进电机不在身边,这里代码中就用舵机来模拟,之前的项目中有用stm32来驱动步进电机,代码在另外的工程里,有需要的话可以关注我私聊,无偿)
前几篇博客:
环境信息采集(HTTP+ONENET)
智能家居(MQTT+ONENET)
智能家居(MQTT+MQTT服务器)
将FreeRTOS移植到自己的工程项目里后(移植可以看上一篇博客),经过测试是可以用的,但是在我将之前的net文件夹移植过来之后编译就报错了,net文件夹里是与服务器通信的一些源文件(onenet.c、esp8266.c、MqttKit.c、cJSON.c),对net文件不了解的可以看这篇博客,有介绍,或者到文章最后下载源代码自己分析代码。下面贴出错误:
经过网上查找,这是由于RAM内存不够导致的,解决办法两种:
①更换开发板,换成STM32F103ZET6或者其他RAM大的开发板,亲测可以。
②修改FreeRTOSConfig.h文件:
当我将dht11的驱动代码移植过来之后,编译没有报错,抱着侥幸心理以为没问题,运行之后发现串口没有调试信息输出,然后进入调试,发现任务函数2执行到DHT11_Read_Data()时就不往后走了,每次停止的时候都会卡在不同的函数里,还真是棘手,无从下手。经过长时间问题找寻,并没有在网上找到答案,我就一步一步调试,发现每次就是延时函数出问题:
然后追溯回延时函数实现,我怀疑是因为这个延时函数使用到了系统时钟中断,然后导致和freertos起冲突,不过我只是怀疑,并不知道具体原因,然后我就将延时函数的源文件和头文件移除,使用定时器来实现延时,成功解决。
用定时器实现的延时函数如下,用这两个函数替换dht11.c和esp8266.c中的延时函数即可:
void TIM3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///使能TIM3时钟
TIM_TimeBaseInitStructure.TIM_Period = 50000-1; //自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = 60-1; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
}
//微秒级延时
void TIM3_Delayus(u16 xus)
{
TIM_Cmd(TIM3,ENABLE); //启动定时器
while(TIM3->CNT < xus);
TIM3->CNT = 0;
TIM_Cmd(TIM3,DISABLE); //关闭定时器
}
//毫秒级延时
void TIM3_Delayms(u16 xms)
{
int i;
for(i=0;i
用到的设备:sg90舵机、MG946舵机、MQ-2、MQ-4、MQ-9传感器、雨滴传感器、火焰传感器、继电器、风扇、水泵、DHT11、USB转TTL、J-;ink、STM32F103C8T6、Esp8266-01s、led。
①标准库移植FreeRTOS参考之前的一篇博客:https://blog.csdn.net/m0_71523511/article/details/135956912
②两个舵机由定时器二的通道0、1输出PWM进行控制,这里不会的可以参考:https://blog.csdn.net/m0_71523511/article/details/135851583
③MQ系列传感器由adc三个通道进行采集,adc知识可以看这个视频,我的代码直接搬过来改的:https://www.bilibili.com/video/BV1th411z7sn/?p=22&spm_id_from=pageDriver&vd_source=2a10d30b8351190ea06d85c5d0bfcb2a
④继电器的使用和接线参考这篇博客:https://blog.csdn.net/m0_71523511/article/details/135926339
⑤雨滴传感器和火焰传感器都直接接收DO输出口,直接读取数字量,当然也可以接AO口,用adc读取模拟量,可以自己尝试。
⑥DHT11原理可以百度也可以参考这篇博客里面的:https://blog.csdn.net/m0_71523511/article/details/135892908
⑦新买的esp8266-01s如果没法用的话可以参考:https://blog.csdn.net/m0_71523511/article/details/135887108
1、main.c:
创建了两个任务,高优先级的任务1(负责接收服务器下发指令),低优先级任务2(负责读取传感器数据并打包发送给服务器)。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "FreeRTOS.h"//操作系统头文件
#include "task.h"
#include "dht11.h"//外设头文件
#include "timer.h"
#include "usart.h"
#include "led.h"
#include "adc.h"
#include "Servo.h"//舵机
#include "PWM.h"
#include "jdq.h"
#include "esp8266.h"//网络相关头文件
#include "onenet.h"
#include //数学公式头文件
TaskHandle_t mytask1;
TaskHandle_t mytask2;
u8 humidityH; //湿度整数部分
u8 humidityL; //湿度小数部分
u8 temperatureH; //温度整数部分
u8 temperatureL; //温度小数部分
u32 gas;//天然气adc读回的值
u32 ranqi;//可燃气体adc读回的值
u32 yanwu;//烟雾adc读回的值
float w2;//天然气浓度电压值
float w3;//可燃气体浓度电压值
float w4;//烟雾浓度电压值
u32 ppm2;//天然气浓度值
u32 ppm3;//可燃气体浓度值
u32 ppm4;//烟雾浓度值
unsigned char hy,yd;//火焰
char PUB_BUF[256];//上传数据的buf
const char *devSubTopic[] = {"/mysmarthome/sub"};
const char devPubTopic[] = "/smarthome/pub";
u8 ESP8266_INIT_OK = 0;//esp8266初始化完成标志
unsigned char *dataPtr = NULL;
//任务1用来接收云平台下发数据
void myTask1( void *arg )
{
while(1)
{
dataPtr = ESP8266_GetIPD(10);
DEBUG_LOG("收到小程序下发指令");
if(dataPtr != NULL)
OneNet_RevPro(dataPtr);
vTaskDelay(200);
}
}
//任务2用于采集传感器数据并上云
void myTask2( void *arg )
{
while(1)
{
//读取火焰传感器数字io输出:PB7
hy = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
//读取雨滴传感器数字io输出:PB8
yd = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
//读取MQ传感器原始数据:PA6、PA7、PB0分别对应MQ-4、MQ-9、MQ-2
taskENTER_CRITICAL();
gas = AD_GetValue(ADC_Channel_6);
taskEXIT_CRITICAL();
w3 = gas*5.0/4095;
ppm2 = pow((7.2495*353.134*w3)/(23.5-4.7*w3),0.6077);
taskENTER_CRITICAL();
ranqi = AD_GetValue(ADC_Channel_7);
taskEXIT_CRITICAL();
w2 = ranqi*5.0/4095;
ppm3 = pow((24.5911*430.323*w2)/(50-10*w2),0.6934);
taskENTER_CRITICAL();
yanwu = AD_GetValue(ADC_Channel_8);
taskEXIT_CRITICAL();
w3 = yanwu*5.0/4095;
ppm4 = pow((24.5911*430.323*w3)/(50-10*w3),0.6934);
//读取DHT11数据:PA8
taskENTER_CRITICAL();//关闭中断,防止执行读取温湿度时被任务1打断
DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL);
taskEXIT_CRITICAL();
//发布数据
taskENTER_CRITICAL();
DEBUG_LOG("==================================================================================");
DEBUG_LOG("发布数据");
sprintf(PUB_BUF,"{\"Humi\":%d.%d,\"Temp\":%d.%d,\"huoyan\":%d,\"xiayu\":%d,\"gas\":%d,\"ranqi\":%d,\"yanwu\":%d}",humidityH,humidityL,temperatureH,temperatureL,!hy,yd,ppm2,ppm3,ppm4);
OneNet_Publish(devPubTopic, PUB_BUF);
DEBUG_LOG("==================================================================================");
taskEXIT_CRITICAL();
vTaskDelay(5000);
}
}
int main(void)
{
TIM3_Init();
Usart1_Init(115200);
DEBUG_LOG("串口1初始化完成\r\n");//串口打印调试信息:TX PA9 ; RX PA10
LED_Init();
DEBUG_LOG("led初始化完成\r\n");
Usart2_Init(115200);//stm32-esp8266通讯串口;TX PA2 ; RX PA3
DEBUG_LOG("串口2初始化完成\r\n");
DHT11_Init();
DEBUG_LOG("DHT11初始化完成\r\n");
AD_Init();
DEBUG_LOG("MQ-4、MQ-9初始化完成\r\n");
Servo_Init();
DEBUG_LOG("舵机初始化完成\r\n");//PA1
Jdq_GPIO_Init();
DEBUG_LOG("继电器初始化完成\r\n");//风扇、水泵:PB5、6
HY_GPIO_Init();
DEBUG_LOG("火焰传感器初始化完成\r\n");//火焰传感器:PB7
YD_GPIO_Init();
DEBUG_LOG("雨滴传感器初始化完成\r\n");//雨滴传感器:PB8
DEBUG_LOG("初始化ESP8266 WIFI模块...");
if(!ESP8266_INIT_OK){
DEBUG_LOG("WIFI连接中...");
}
ESP8266_Init(); //初始化ESP8266
DEBUG_LOG("服务器连接中...");
while(OneNet_DevLink()){//接入OneNET
TIM3_Delayms(500);
}
OneNet_Subscribe(devSubTopic, 1);//订阅上位机下发数据Topic
xTaskCreate(myTask1,"matask1",512,NULL,3,&mytask1);
xTaskCreate(myTask2,"matask2",512,NULL,2,&mytask2);
DEBUG_LOG("任务创建完成,开始任务调度\r\n");
vTaskStartScheduler();//开始任务调度,这之后的代码不会执行
while (1)
{
}
}
2、onenet.c中的OneNet_RevPro()函数:
这个函数负责解析微信小程序发送给服务器,又由服务器转发给下位机的指令:
void OneNet_RevPro(unsigned char *cmd)
{
MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包
char *req_payload = NULL;
char *cmdid_topic = NULL;
unsigned short topic_len = 0;
unsigned short req_len = 0;
unsigned char type = 0;
unsigned char qos = 0;
static unsigned short pkt_id = 0;
short result = 0;
char *dataPtr = NULL;
char numBuf[10];
int num = 0;
cJSON *json , *json_value;
type = MQTT_UnPacketRecv(cmd);
switch(type)
{
case MQTT_PKT_CMD: //命令下发
result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); //解出topic和消息体
if(result == 0)
{
DEBUG_LOG("cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);
MQTT_DeleteBuffer(&mqttPacket); //删包
}
break;
case MQTT_PKT_PUBLISH: //接收MQTT服务器转发的小程序下发数据
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);//解析MQTT数据包
if(result == 0)
{
DEBUG_LOG("topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
cmdid_topic, topic_len, req_payload, req_len);
// 对数据包内的req_payload(消息体)进行JSON格式解析
json = cJSON_Parse(req_payload);
if (!json)
UsartPrintf(USART_DEBUG,"Error before: [%s]\n",cJSON_GetErrorPtr());
else
{
json_value = cJSON_GetObjectItem(json , "target"); //在JSON对象json中查找“target”这个键的值,并返回对应的cjson指针 //{'target':'leds'},这就是键值对
//串口打印键值对
UsartPrintf(USART_DEBUG,"json_string: [%s]\n",json_value->string);//string-键
UsartPrintf(USART_DEBUG,"json_value: [%s]\n",json_value->valuestring);//valuestring-值
//strstr是标准字符串判断函数,判断前面查找到的“target”这个键的值是否等于leds
if(strstr(json_value->valuestring,"leds") != NULL)
{
json_value = cJSON_GetObjectItem(json , "value");//进行第二次get,这样获取的就是value:1
if(json_value->valueint)
GPIO_SetBits(GPIOC,GPIO_Pin_13);
else
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
else if(strstr(json_value->valuestring,"men") != NULL)
{
json_value = cJSON_GetObjectItem(json , "value");//进行第二次get,这样获取的就是value:1
if(json_value->valueint)
{
Servo_SetAngle(180);
TIM3_Delayms(20);
}
else
{
Servo_SetAngle(0);
TIM3_Delayms(20);
}
}
else if(strstr(json_value->valuestring,"beep") != NULL)//窗帘一档
{
json_value = cJSON_GetObjectItem(json,"value");
if(json_value->valueint)
{
Servo_SetAngle2(60);
TIM3_Delayms(20);
}
else
{
Servo_SetAngle2(60);
TIM3_Delayms(20);
}
}
else if(strstr(json_value->valuestring,"sandang") != NULL)//窗帘三档
{
json_value = cJSON_GetObjectItem(json,"value");
if(json_value->valueint)
{
Servo_SetAngle2(180);
TIM3_Delayms(20);
}
else
{
Servo_SetAngle2(180);
TIM3_Delayms(20);
}
}
else if(strstr(json_value->valuestring,"erdang") != NULL)//窗帘二档
{
json_value = cJSON_GetObjectItem(json,"value");
if(json_value->valueint)
{
Servo_SetAngle2(120);
TIM3_Delayms(20);
}
else
{
Servo_SetAngle2(120);
TIM3_Delayms(20);
}
}
else if(strstr(json_value->valuestring,"guanchuang") != NULL)//关闭窗帘
{
json_value = cJSON_GetObjectItem(json,"value");
if(json_value->valueint)
{
Servo_SetAngle2(0);
}
else
{
Servo_SetAngle2(0);
}
}
else if(strstr(json_value->valuestring,"fen") != NULL)//小程序下发开风扇指令
{
json_value = cJSON_GetObjectItem(json,"value");
if(json_value->valueint)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}
}
else if(strstr(json_value->valuestring,"water") != NULL)//小程序下发开风扇指令
{
json_value = cJSON_GetObjectItem(json,"value");//小程序下发开水阀指令
if(json_value->valueint)
{
GPIO_SetBits(GPIOB,GPIO_Pin_6);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
}
}
else
{
}
}
cJSON_Delete(json);
}
break;
case MQTT_PKT_PUBACK: //发送Publish消息,平台回复的Ack,这里只有消息服务质量等级升高时才会用到(qos)
if(MQTT_UnPacketPublishAck(cmd) == 0)
DEBUG_LOG("Tips: MQTT Publish Send OK\r\n");
break;
case MQTT_PKT_PUBREC: //发送Publish消息,平台回复的Rec,设备需回复Rel消息
if(MQTT_UnPacketPublishRec(cmd) == 0)
{
DEBUG_LOG("Tips: Rev PublishRec\r\n");
if(MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0)
{
DEBUG_LOG("Tips: Send PublishRel\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
}
break;
case MQTT_PKT_PUBREL: //收到Publish消息,设备回复Rec后,平台回复的Rel,设备需再回复Comp
if(MQTT_UnPacketPublishRel(cmd, pkt_id) == 0)
{
DEBUG_LOG("Tips: Rev PublishRel\r\n");
if(MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0)
{
DEBUG_LOG("Tips: Send PublishComp\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
}
break;
case MQTT_PKT_PUBCOMP: //发送Publish消息,平台返回Rec,设备回复Rel,平台再返回的Comp
if(MQTT_UnPacketPublishComp(cmd) == 0)
{
DEBUG_LOG("Tips: Rev PublishComp\r\n");
}
break;
case MQTT_PKT_SUBACK: //发送Subscribe消息的Ack
if(MQTT_UnPacketSubscribe(cmd) == 0)
DEBUG_LOG("Tips: MQTT Subscribe OK\r\n");
else
DEBUG_LOG("Tips: MQTT Subscribe Err\r\n");
break;
case MQTT_PKT_UNSUBACK: //发送UnSubscribe消息的Ack
if(MQTT_UnPacketUnSubscribe(cmd) == 0)
DEBUG_LOG("Tips: MQTT UnSubscribe OK\r\n");
else
DEBUG_LOG("Tips: MQTT UnSubscribe Err\r\n");
break;
default:
result = -1;
break;
}
ESP8266_Clear(); //清空缓存
if(result == -1)
return;
dataPtr = strchr(req_payload, '}'); //搜索'}'
if(dataPtr != NULL && result != -1) //如果找到了
{
dataPtr++;
while(*dataPtr >= '0' && *dataPtr <= '9') //判断是否是下发的命令控制数据
{
numBuf[num++] = *dataPtr++;
}
num = atoi((const char *)numBuf); //转为数值形式
}
if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
{
MQTT_FreeBuffer(cmdid_topic);
MQTT_FreeBuffer(req_payload);
}
}
1、index.js
// index.js
// 获取应用实例
const app = getApp()
const{ connect } = require('../../utils/mqtt')
const mqttHost = 'broker.emqx.io'
const mqttPort = 8084
const deviceSub = '/mysmarthome/sub' //小程序发布指令的Topic
const devicePub = '/smarthome/pub' //小程序接收数据的Topic
const mpSubTopic = devicePub //mp指小程序(这样更直观)
const mpPubTopic = deviceSub //小程序发布指令的Topic
Page({
data: {
client: null,
Humi:0,
Temp:0,
huoyan:1,
gas:0,
leds:false,
ranqi:0,
fen:false,
water:false,
men:false
},
//下发指令:led
onledsChange(event){
const that = this
console.log(event.detail.value);
const sw = event.detail.value
that.setData({leds:sw})
if(sw){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"leds",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开led')
console.log('{target:"leds",value:1}')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"leds",
value:0
}),function (err) {
if(!err){
console.log('成功下发指令关闭led')
console.log('{target:"leds",value:0}')
}
})
}
},
//下发指令:风扇
onfenChange(event){
const that = this
console.log(event.detail.value);
const sw = event.detail.value
that.setData({fen:sw})
if(sw){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"fen",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开排气扇')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"fen",
value:0
}),function (err) {
if(!err){
console.log('成功下发指令关闭排气扇')
}
})
}
},
//下发指令:水泵
onwaterChange(event){
const that = this
console.log(event.detail.value);
const sw = event.detail.value
that.setData({water:sw})
if(sw){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"water",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开水泵')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"water",
value:0
}),function (err) {
if(!err){
console.log('成功下发指令关闭水泵')
}
})
}
},
//下发指令:门
onmenChange(event){
const that = this
console.log(event.detail.value);
const sw = event.detail.value
that.setData({men:sw})
if(sw){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"men",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开门')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"men",
value:0
}),function (err) {
if(!err){
console.log('成功下发指令关闭门')
}
})
}
},
//舵机逻辑
//下发指令:窗帘一档
goToPage1(event){
const that = this
that.setData({cnt1:this.data.cnt1 + 1})
if(this.data.cnt1 > 0){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"beep",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开窗帘一档')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"beep",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令关闭窗帘一档')
}
})
}
},
//下发指令:二档
goToPage2(event){
const that = this
that.setData({cnt2:this.data.cnt2 + 1})
if(this.data.cnt2 >= 0){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"erdang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开窗帘二档')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"erdang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令关闭窗帘二档')
}
})
}
},
//下发指令:三档
goToPage3(event){
const that = this
that.setData({cnt3:this.data.cnt3 + 1})
if(this.data.cnt3 >= 0){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"sandang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令打开窗帘三档')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"sandang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令关闭窗帘s档')
}
})
}
},
goToPage4(event){
const that = this
that.setData({cnt4:this.data.cnt4 + 1})
if(this.data.cnt4 >= 0){
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"guanchuang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令关闭窗帘')
}
})
}else{
that.data.client.publish(mpPubTopic,JSON.stringify({
target:"guanchuang",
value:1
}),function (err) {
if(!err){
console.log('成功下发指令关闭窗帘')
}
})
}
},
//显示接受到的数据
onShow(){
const that = this
that.setData({
client:connect(`wxs://${mqttHost}:${mqttPort}/mqtt`)
})
that.data.client.on('connect',function (params) {
console.log('成功连接到mqtt服务器')
wx.showToast({
title: '连接成功',
icon:'success',
mask:true
})
that.data.client.subscribe(mpSubTopic,function (err) {
if(!err){
console.log('成功订阅设备上行数据Topic')
}
})
})
that.data.client.on('message',function (topic,message) {
console.log(topic);
let dataFromDev = {}
try {
dataFromDev = JSON.parse(message)
console.log(dataFromDev);
that.setData({
Temp:dataFromDev.Temp,
Humi:dataFromDev.Humi,
huoyan:dataFromDev.huoyan,
gas:dataFromDev.gas,
ranqi:dataFromDev.ranqi,
xiayu:dataFromDev.xiayu,
yanwu:dataFromDev.yanwu,
})
} catch (error) {
console.log('JSON解析失败',error)
}
})
},
})
2、index.wxml
战损版
智能家居
温度
{{ Temp }} ℃
湿度
{{ Humi }}%rh
可燃气浓度
{{ ranqi }} ppm
天然气浓度
{{ gas }} ppm
烟雾浓度
{{ yanwu }} ppm
火焰
无
有
雨
下雨
未下雨
排气扇
水泵
卧室灯
门
窗帘
3、index.wxss
/**小程序整个页面的设置**/
.page-container{
padding: 36rpx;
}
/**设置顶部大筐筐**/
.header-container{
background-color: #fff;
color: black;
box-shadow: #d6d6d6 0 0 20rpx;
border-radius: 36rpx;
padding: 3rpx 3rpx;
}
/**设置顶部大筐筐内的第一行字**/
.header-container .header-one{
display: flex;
justify-content: center;
padding: 10rpx;
font-size: 50rpx;
font: bold;
}
/**设置顶部大筐筐内的第二行字**/
.header-container .header-two{
display: flex;
justify-content: center;
padding: 10rpx;
font-size: 40rpx;
font: bold;
}
.choice {
display: flex;
flex-direction: row;
font-size: 20px;
justify-content: center;
color: rgb(38, 38, 39);
margin-top: 30rpx;
margin-bottom: 20rpx;
top: 3rpx;
box-shadow:0px 0px 10px #bfbfc0 inset;
}
.btn1 {
/* float:left;
background-color: rgb(112, 107, 107);
color: rgb(66, 63, 63); */
float:left;
top: 3rpx;
box-shadow:0px 0px 8px #999 inset;
}
.btn-class{
float:left;
top: 3rpx;
box-shadow:0px 0px 10px #b8b9bd inset;
}
.btn2 {
float:right;
top: 3rpx;
box-shadow:0px 0px 8px #999 inset;
}
.btn-class2{
float:right;
top: 3rpx;
box-shadow:0px 0px 10px #b8b9bd inset;
}
.btn3 {
float:left;
top: 3rpx;
box-shadow:0px 0px 8px #999 inset;
}
.btn-class3{
float:left;
top: 3rpx;
box-shadow:0px 0px 10px #b8b9bd inset;
}
.btn4 {
float:right;
top: 3rpx;
box-shadow:0px 0px 8px #999 inset;
}
.btn-class4{
float:right;
top: 3rpx;
box-shadow:0px 0px 10px #b8b9bd inset;
}
.s_view{
bottom: 0rpx;
width: 100%;
}
/**设置数据部分总体布局**/
.data-container{
margin-top: 30rpx;
display: grid;
justify-content: center;
grid-template-columns: repeat(auto-fill,300rpx);
grid-gap: 30rpx;
}
/**设置数据部分小卡片内的格式**/
.data-container .data-card{
position: relative;
background-color: #fff;
height: 140rpx;
box-shadow: #d6d6d6 0 0 8rpx;
border-radius: 36rpx;
display: flex;
justify-content: space-between;
padding: 16rpx;
}
/**以下是小卡片内的文本、图片、标题、数值设置**/
.data-container .data-card .data-card__text{
position: absolute;
top: 20rpx;
right: 24rpx;
text-align: right;
white-space: nowrap;
}
.data-container .data-card .data-card__icon{
position: absolute;
height: 72rpx;
width: 72rpx;
left: 32rpx;
top: 16rpx;
}
.data-container .data-card .data-card__value{
font-size: 40rpx;
margin-top: 20rpx;
}
.data-container .data-card .data-card__title{
font-size: 34rpx;
}
关注俺,私聊私发无偿。