站内搜索
发作品签到
智能家居控制系统
专业版

智能家居控制系统

简介

基于STM32的智能家居控制系统,通过嵌入式软硬件电路设计与物联网通信相结合,实现了对室内环境数据的实时采集与云端显示。用户可以借助手机APP或者Web网页在远程对相关的设备进行开关控制。

简介:基于STM32的智能家居控制系统,通过嵌入式软硬件电路设计与物联网通信相结合,实现了对室内环境数据的实时采集与云端显示。用户可以借助手机APP或者Web网页在远程对相关的设备进行开关控制。
复刻成本:50

开源协议

GPL 3.0

创建时间:2024-10-25 12:00:08更新时间:2025-08-06 10:22:07

描述

项目简介

基于STM32的智能家居控制系统,通过嵌入式软硬件电路设计与物联网通信相结合,实现了对室内环境数据的实时采集与云端显示。用户可以借助手机APP或者Web网页在远程对相关的设备进行开关控制,同时在硬件端的TFT彩屏上可以实时显示日期、时间、室内环境数据等信息。该系统不仅具备一定的可玩性,同时还具有相当可观的实用价值。

项目参数

  • 本项目使用STM32F103C8T6微处理器作为整个系统的主控。STM32依靠ADC等外设实现通过传感器对室内环境数据的采集,利用SPI接口实现数据在屏幕上的显示,最后借助UART接口和GPIO实现数据到物联网云服务器的上传以及设备的控制。
    STM32.jpg
  • 本项目采用ESP12-F作为物联网通信模组。该模块基于ESP8266芯片设计,支持标准的WiFi协议以及完整的TCP/IP协议栈,同时具备高可靠性以及低功耗的特点。该模组引出了ESP8266自带的UART、ADC、PWM、SPI等外设资源,在本项目中,ESP12-F模组通过自身的TCP/IP协议栈,同时借助本地局域网作为一个MQTT客户端接入物联网云平台,利用UART与STM32进行数据交互,将相关的数据上传至物联网云平台并将用户的控制指令发送给STM32,从而实现用户在云端对室内环境的监控以及对相关设备的远程控制。
    封面.jpg
  • 本项目采用1.8寸TFT彩色显示屏进行数据的可视化展示,该显示屏采用ST7735驱动芯片,具备128×160的分辨率,通过SPI串行接口与STM32单片机进行通信。该显示屏具备控制接口简单,功耗低,显示清晰,刷新快等优点,被广泛用于血氧仪,智能手环,门锁等智能产品。
    封面.jpg

原理解析(硬件说明)

STM32主控电路
STM32主控电路如图4所示,该部分电路包括STM32芯片的最小系统电路,晶振电路以及按键复位电路。除此之外,增加了一个用户按键和用来指示状态的两颗LED灯。STM32采用3.3V供电,3.3V和GND之间利用10uF和0.1uF两种不同容值的电容实现电源滤波。同时,将按键通过10K电阻上拉至3.3V,使按键按下时,PA0引脚的输入为低电平,并且给按键并联一个0.1uF的电容,实现按键的硬件消抖,从而减小误触发的概率。状态指示LED分别连接到STM32的PC13和PA7引脚,相关引脚输出高电平时,点亮对应的LED。
封面.jpg
ESP12-F物联网通信电路
物联网通信电路如图5所示,该部分电路以ESP12-F模组为核心,根据模组用户手册给出的外围电路参考建议,将EN引脚通过10K电阻上拉到3.3V,同时将IO15引脚下拉到GND,RST复位引脚通过10K电阻上拉到VCC,同时连接到模组的复位按键,引出模组的TXD0、RXD0引脚到双排针,同时通过单排针以及跳线帽控制IO0的上下拉状态(如图中的H2和H3所示)。当需要给模组更新程序固件时,调节IO0的跳线帽排针,使引脚处于下拉状态,通过串口给模组烧录固件;当正常运行时,调节跳线帽使IO0处于上拉状态,同时短接TXD0和STM32的RXD1,以及RXD0和STM32的TXD1,使双方能够建立串口通信,ESP12-F从报文中解析相应的数据,并将其上传到物联网云平台。
封面.jpg
电源电路
本项目采用DC5.0接头供电,供电电压为12V,降压电路如图6所示,分为两级,第一级通过PW2335开关降压芯片(如图6中的U19所示)使12V输入转换为5V输出,用来驱动继电器的正常工作,第二级采用RT9193线性稳压器(如图6中的U22所示),将5V转换为稳定的3.3V输出,用来驱动STM32以及ESP12-F的稳定工作。PW2335是一种高效率的DCDC降压芯片,其最大输出电流达到3A,能够满足整个项目的供电要求;RT9193是一种常用的线性稳压器,具有体积小,输出纹波小,输出电流大的优点,因而被广泛应用于各种嵌入式系统中。
封面.jpg
TFT液晶显示屏接口电路如图7所示,该显示屏采用3.3V供电,显示屏的SCL、SDA、RES、DC、CS、BL接口分别于STM32的PB9、PB8、PB7、PB6、PB5、PB4引脚连接,STM32通过上述GPIO模拟SPI通信的时序来实现屏幕显示的控制。其中SCL作为SPI总线的时钟信号输入端,SDA作为SPI总线的数据信号输入端,RES作为屏幕的复位信号输入端,DC用来控制写入的是数据还是命令,当DC为高电平时表示写数据,DC为低电平时表示写命令,CS作为SPI总线的片选信号输入,低电平有效,BL作为屏幕的背光控制引脚,默认高电平。
封面.jpg
继电器驱动电路
继电器电路如图8所示,继电器由AO3400场效应管进行控制,继电器的型号是SRD-05VDC-SL-C,如图9所示。
封面.jpg
封面.jpg
在图8所示的继电器驱动电路中,当场效应管的栅极输入为高电平时,场效应管的漏极与源极之间导通,继电器的COM端与NO端吸合,同时指示灯亮起;当栅极输入为低电平时,漏极与源极直接截止,COM端复位,指示灯熄灭,通过场效应管与继电器的搭配,实现了弱电隔离控制强电的效果。
本项目的PCB设计布局如图10所示,为了PCB的美观以及节省元器件所占用空间,采用0603的小封装阻容的同时,将STM32主控芯片以及蜂鸣器放在TFT屏幕的下方,传感器接口、ESP12-F通信模组以及继电器和接线端子均放置在PCB的边缘。
为了保证电源能够正常工作,且能够承受较大的电流,电源部分12V输入以及5V输出均采用填充处理,并且在电源芯片下方添加散热孔,最大程度地使得电源系统产生的热量能够快速地散发出去。
最后,PCB顶面进行3.3V铺铜,底面进行GND铺铜,尽可能保证电源走线的宽度尽可能的大,同时,继电器与接线端子的接线也采用加粗处理,保证这部分能够承受大功率电器工作时的大电流,最后,对于ESP12-F模组,为了保证信号的稳定性,对模组板载的PCB天线下方区域进行挖空处理,使其不含有任何金属成分,最大限度地杜绝整个系统对WiFi信号的干扰。
封面.jpg
PCB在四个角预留M3螺丝孔,方便后期能够更好地固定到电器控制箱中。

软件代码

STM32主控的程序设计
STM32主控的程序设计在ARM-MDK进行,采用HAL库的框架进行开发。这部分程序设计的主要内容有串口通信、ADC模拟量采集、GPIO的输入输出。
串口通信的程序设计主要是为了解析ESP12-F模组以及GPS模块发过来的数据报文,采用DMA+空闲中断的方式进行数据接收,以最大限度地降低通信过程中的丢包情况,DMA+空闲中断的程序设计如程序1所示。
程序1:

void USART1_IRQHandler(void)
{
  	HAL_UART_IRQHandler(&huart1);
uint32_t temp;//获取DMA当前还有多少未填充
   if (USART1 == huart1.Instance)//判断是否为串口1中断
   {
      if(RESET!=__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)) 
      {
      		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除中断标志
          HAL_UART_DMAStop(&huart1);//停止DMA接收
          temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); 
          Rx1DataLen = 1024 - temp; //计算串口接收到的数据个数
          memcpy(ReceiveBuff_Huart1,Rx1Data,strlen((char *)Rx1Data));
Rx1DataLen = 0;//接收数据长度清零
          HAL_UART_Receive_DMA(&huart1, Rx1Data, 1024);
      }
   }
}

在程序1中,在串口中断服务函数中通过DMA的方式将数据存入ReceiveBuff_Huart1缓冲区,在main.c中对ReceiveBuff_Huart1进行解析,主要解析的内容有ESP12-F获得的日期以及时间数据、天气预报的信息以及来自云平台对4个继电器的控制指令,解析完毕后清空字符串,这部分的程序设计如程序2所示。
程序2:

if(ReceiveBuff_Huart1[0]!='\0')
{
if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"DATE")!=NULL)
	{
	   DateProcess();
	}
	if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"TIME")!=NULL)
	{
		TimeProcess();
	}
	if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"WTH-D")!=NULL)
	{
		dWeatherProcess();
}
	if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"WTH-N")!=NULL)
{
		nWeatherProcess();
	}
	if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"LOW")!=NULL)
{
		hTempProcess();
	}
	if(strstr((const char *)ReceiveBuff_Huart1,(const char *)"HIG")!=NULL)
	{
		lTempProcess();
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY1=1")!=NULL)
	{
		RELAY(1,1);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY1=0")!=NULL)
	{
		RELAY(1,0);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY2=1")!=NULL)
	{
		RELAY(2,1);
	}
	if(strstrReceiveBuff_Huart1,(const char *)"RELAY2=0")!=NULL)
	{
		RELAY(2,0);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY3=1")!=NULL)
	{
		RELAY(3,1);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY3=0")!=NULL)
	{
		RELAY(3,0);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY4=1")!=NULL)
	{
		RELAY(4,1);
	}
	if(strstr(ReceiveBuff_Huart1,(const char *)"RELAY4=0")!=NULL)
	{
		RELAY(4,0);
	}
	Rx1BufferClear();
}

在程序2中,DateProcess()、TimeProcess()、dWeatherProcess()、nWeatherProcess()、hTempProcess()、lTempProcess()分别为从报文中解析日期、时间、昼夜天气以及最低与最高气温的函数。继电器的操作函数为void RELAY(int Number,int State),该函数有两个参数,Number参数为继电器的编号,取值为1到4,State参数为1时代表打开继电器,为0时代表关闭继电器,RELAY函数的具体定义如程序3所示。
程序3:

void RELAY(int Number,int State)
{
	if(State)
	{
		switch(Number)
		{
			case 1:
HAL_GPIO_WritePin(RELAY1_GPIO_Port,RELAY1_Pin,GPIO_PIN_SET);
			break;
			case 2:		HAL_GPIO_WritePin(RELAY2_GPIO_Port,RELAY2_Pin,GPIO_PIN_SET);
			break;
			case 3:			HAL_GPIO_WritePin(RELAY3_GPIO_Port,RELAY3_Pin,GPIO_PIN_SET);
			break;
			case 4:
HAL_GPIO_WritePin(RELAY4_GPIO_Port,RELAY4_Pin,GPIO_PIN_SET);		
			break;
			default:
			break;
		}	
	}
	else
	{
		switch(Number)
		{
			case 1:		HAL_GPIO_WritePin(RELAY1_GPIO_Port,RELAY1_Pin,GPIO_PIN_RESET);
			break;
			case 2:
HAL_GPIO_WritePin(RELAY2_GPIO_Port,RELAY2_Pin,GPIO_PIN_RESET);
			break;
			case 3:
HAL_GPIO_WritePin(RELAY3_GPIO_Port,RELAY3_Pin,GPIO_PIN_RESET);
			break;
			case 4:
	HAL_GPIO_WritePin(RELAY4_GPIO_Port,RELAY4_Pin,GPIO_PIN_RESET);				break;
			default:
			break;
		}	
	}
}

ADC模拟量采集的程序如程序4所示,这部分的代码主要是将MQ2传感器检测到的烟雾浓度以模拟电压的形式采集出来,其返回的结果为0.00-100.00之间的浮点数。
程序4:

float Read_Smoke_Data(uint32_t Channel)
{
	float Data=0.00;
	uint16_t Int16Data;
	ADC_ChannelConfTypeDef sConfig = {0};
	sConfig.Channel = Channel;                                         
	sConfig.Rank = ADC_REGULAR_RANK_1;                              
	sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;                  
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)             
	{
		Error_Handler();
	}
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
	Int16Data=(uint16_t)HAL_ADC_GetValue(&hadc1);
	Data=((Int16Data*3.30)/4095.00)/3.30;
	return Data*100.00;
}

GPIO的输入输出程序设计一方面是为了控制继电器,另一方面是模拟单总线通信的时序,从而读取DHT11传感器的温湿度值,DHT11传感器的读取如程序5所示。
程序5:

void DHT11_ReadData(float *Temperature,float *Humidity)
{
	uint8_t Buffer[5];
	uint8_t i;
	DHT11_Start();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)
		{
			Buffer[i]=DHT11_ReadByte();
		}
		if(Buffer[0]+Buffer[1]+Buffer[2]+Buffer[3]==Buffer[4])
		{
			*Humidity=(float)Buffer[0]+(float)Buffer[1]/10;
			*Temperature=(float)Buffer[2]+(float)Buffer[3]/10;
		}
	}
}

STM32将继电器的工作状态,采集到的传感器数据以及GPS经纬度数据发送到ESP12-F的同时,通过TFT显示屏直观地显示给用户,TFT显示屏的数据显示如程序6所示。
程序6:

void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode)
{         
	while(*p!='\0')
	{       
		LCD_ShowChar(x,y,*p,fc,bc,sizey,mode);
		x+=sizey/2;
		p++;
	}  
}

ESP12-F模组的程序设计
ESP12-F在整个项目所发挥的作用是非常关键的,这部分的程序设计使用Arduino IDE进行开发,ESP12-F通过本地局域网以及MQTT协议接入物联网云平台,解析串口收到的STM32发过来的数据报文,并将这些报文上传到云平台,同时,将用户的控制指令通过串口转发给STM32主控,除此之外,ESP12-F通过知心天气的API以及NTP授时服务器获取日期以及天气信息,一并通过串口发送给STM32。
STM32以Json的格式将数据封装好,发送给ESP12-F,ESP12-F通过检测串口是否有数据发送实现数据解析,这部分的程序设计如程序7所示。
程序7:

if (Serial.available()) 
  {
        String jsonString = Serial.readStringUntil('\n'); 
        StaticJsonDocument<200> doc; // 确保大小足够
        DeserializationError error = deserializeJson(doc, jsonString);
        if (!error) 
        {
            temperature = doc["Temperature"];
            humidity = doc["Humidity"];
            smoke = doc["Smoke"];
            warning = doc["Warning"];
            pressure = doc["Pressure"];
            altitude = doc["Altitude"];
            longitude = doc["Longitude"];
            latitude = doc["Latitude"];
        }
  }

数据解析完毕后,调用AliyunIoTSDK::send(String Key,float Number)函数将数据上传到阿里云物联网平台,参数Key是平台中定义的数据模型名称,Number是对应的数值,这部分代码如程序8所示。
程序8:

AliyunIoTSDK::send("Temperature",temperature);
AliyunIoTSDK::send("Humidity",humidity);
AliyunIoTSDK::send("Smoke",smoke);
AliyunIoTSDK::send("Warning",warning);
AliyunIoTSDK::send("Pressure",pressure);
AliyunIoTSDK::send("Altitude",altitude);
AliyunIoTSDK::send("Longitude",longitude);
AliyunIoTSDK::send("Latitude",latitude);

定义Switch_CallBack()函数,ESP12-F在连接物联网云平台后与Switch_CallBack()绑定,当ESP12-F收到云平台的控制指令时,调用对应的Switch_CallBack()函数,在函数中向STM32发送对应的报文,实现用户的远程操作,这部分的程序设计如代码9所示。
程序9:

//Switch_CallBack()函数的绑定
AliyunIoTSDK::begin(ESPCLIENT,PRODUCT_KEY,DEVICE_NAME,DEVICE_SECRET, REGION_ID);
AliyunIoTSDK::bindData("Switch1",Switch_CallBack1);
AliyunIoTSDK::bindData("Switch2",Switch_CallBack2);
AliyunIoTSDK::bindData("Switch3",Switch_CallBack3);
AliyunIoTSDK::bindData("Switch4",Switch_CallBack4);
//Switch_CallBack()函数的具体定义
void Switch_CallBack1(JsonVariant Data)
{
  int SwitchValue=Data["Switch1"];
  Serial.printf("RELAY1=%d\r\n",SwitchValue);
}
void Switch_CallBack2(JsonVariant Data)
{
  int SwitchValue=Data["Switch2"];
  Serial.printf("RELAY2=%d\r\n",SwitchValue);
}
void Switch_CallBack3(JsonVariant Data)
{
  int SwitchValue=Data["Switch3"];
  Serial.printf("RELAY3=%d\r\n",SwitchValue);
}
void Switch_CallBack4(JsonVariant Data)
{
  int SwitchValue=Data["Switch4"];
  Serial.printf("RELAY4=%d\r\n",SwitchValue);
}

ESP12-F通过心知天气API,向服务器发送Http请求,从而获取特定区域的天气信息以及日期时间,获取完毕后进行解析,并按照特定格式进行封装,进而通过串口发送到STM32,这部分的代码如程序10所示。
程序10:

void HttpRequestWeather(String RequestInfo)
{
  WiFiClient client;
StringhttpRequest=String("GET")+RequestInfo+"HTTP/1.1\r\n"+"Host:"+XZ_Host+"\r\n"+"Connection: close\r\n\r\n";
  if (client.connect(XZ_Host, 80))
  {
    client.print(httpRequest); 
    String status_response=client.readStringUntil('\n');
    ParseWeatherInfo(client);
  }
}
void ParseWeatherInfo(WiFiClient Client)
{
Const size_t capacity=JSON_ARRAY_SIZE(1)+JSON_ARRAY_SIZE(3)+JSON_OBJECT_SIZE(1)+JSON_OBJECT_SIZE(3)+JSON_OBJECT_SIZE(6)+3*JSON_OBJECT_SIZE(14)+860;
  DynamicJsonDocument doc(capacity);
  deserializeJson(doc,Client);
  JsonObject results_0=doc["results"][0];
  JsonArray results_0_daily= results_0["daily"];
  JsonObject results_0_daily_0= results_0_daily[0];
  const char* results_0_daily_0_date=results_0_daily_0["date"]; 
  const char* results_0_daily_0_text_day=results_0_daily_0["text_day"]; 
  const char* results_0_daily_0_code_day=results_0_daily_0["code_day"];
  const char* results_0_daily_0_text_night=results_0_daily_0["text_night"]; 
  const char* results_0_daily_0_code_night=results_0_daily_0["code_night"]; 
  const char* results_0_daily_0_high=results_0_daily_0["high"];
  const char* results_0_daily_0_low=results_0_daily_0["low"]; 
  const char* results_0_daily_0_rainfall=results_0_daily_0["rainfall"];
  const char* results_0_daily_0_precip=results_0_daily_0["precip"]; 
  const char* results_0_daily_0_wind_direction=results_0_daily_0["wind_direction"]; 
  const char* results_0_daily_0_wind_direction_degree=results_0_daily_0["wind_direction_degree"];
  const char* results_0_daily_0_wind_speed=results_0_daily_0["wind_speed"];
  const char* results_0_daily_0_wind_scale=results_0_daily_0["wind_scale"];
  const char* results_0_daily_0_humidity=results_0_daily_0["humidity"];
  JsonObject results_0_daily_1=results_0_daily[1];
  const char* results_0_daily_1_date=results_0_daily_1["date"];
  const char* results_0_daily_1_text_day=results_0_daily_1["text_day"];
  const char* results_0_daily_1_code_day=results_0_daily_1["code_day"];
  const char* results_0_daily_1_text_night=results_0_daily_1["text_night"]; 
  const char* results_0_daily_1_code_night=results_0_daily_1["code_night"]; 
  const char* results_0_daily_1_high=results_0_daily_1["high"];
  const char* results_0_daily_1_low=results_0_daily_1["low"]; 
  const char* results_0_daily_1_rainfall=results_0_daily_1["rainfall"]; 
  const char* results_0_daily_1_precip=results_0_daily_1["precip"]; 
  const char* results_0_daily_1_wind_direction=results_0_daily_1["wind_direction"];
  const char* results_0_daily_1_wind_direction_degree=results_0_daily_1["wind_direction_degree"]; 
  const char* results_0_daily_1_wind_speed=results_0_daily_1["wind_speed"];
  const char* results_0_daily_1_wind_scale=results_0_daily_1["wind_scale"];
  const char* results_0_daily_1_humidity=results_0_daily_1["humidity"]; 
  JsonObject results_0_daily_2=results_0_daily[2];
  const char* results_0_daily_2_date=results_0_daily_2["date"];
  const char* results_0_daily_2_text_day=results_0_daily_2["text_day"];
  const char* results_0_daily_2_code_day=results_0_daily_2["code_day"];
  const char* results_0_daily_2_text_night=results_0_daily_2["text_night"];
  const char* results_0_daily_2_code_night=results_0_daily_2["code_night"];
  const char* results_0_daily_2_high=results_0_daily_2["high"]; 
  const char* results_0_daily_2_low=results_0_daily_2["low"]; 
  const char* results_0_daily_2_rainfall=results_0_daily_2["rainfall"];
  const char* results_0_daily_2_precip=results_0_daily_2["precip"]; 
  const char* results_0_daily_2_wind_direction=results_0_daily_2["wind_direction"]; 
  const char* results_0_daily_2_wind_direction_degree=results_0_daily_2["wind_direction_degree"]; 
  const char* results_0_daily_2_wind_speed=results_0_daily_2["wind_speed"];
  const char* results_0_daily_2_wind_scale=results_0_daily_2["wind_scale"]; 
  const char* results_0_daily_2_humidity=results_0_daily_2["humidity"]; 
  const char* results_0_last_update=results_0["last_update"]; 
  String results_0_daily_0_date_str=results_0_daily_0["date"].as();
  String  results_0_daily_0_text_day_str=results_0_daily_0["text_day"].as(); 
  int results_0_daily_0_code_day_int=results_0_daily_0["code_day"].as(); 
  String results_0_daily_0_text_night_str=results_0_daily_0["text_night"].as(); 
  int results_0_daily_0_code_night_int=results_0_daily_0["code_night"].as(); 
  int results_0_daily_0_high_int=results_0_daily_0["high"].as();
  int results_0_daily_0_low_int=results_0_daily_0["low"].as();
  String results_0_last_update_str=results_0["last_update"].as();
}

注意事项

  • DC-DC电路布局时留意填充区域。
  • 装配完成后需要对其进行固定处理。
  • 需要配置ESP-12F的正确工作模式。
  • 外壳装配完成后需要对齐进行填缝防水处理。
  • STM32使用MDK+CubeMx,ESP-12F使用Arduino框架,推荐vscode+PlateformIO的开发环境。

实物图

分别将程序烧录进STM32和ESP12-F,智能家居控制系统的硬件演示效果如图12所示。
封面.jpg
在图12中,ESP12-F发送一次报文,其板载的LED会闪烁一次,TFT屏幕从上到下显示的信息分别为日期、时间、气压、海拔、温湿度、坐标以及烟雾浓度。当继电器工作时,对应的指示区域会由绿色变为红色。
智能家居控制系统的云端数据显示效果如图13所示,所有的数据均能够正常的上传并可视化展示出来。
封面.jpg

实物图

本项目可以学习物联网开发,也可以当做毕设,后续的源代码会更新在评论区,有任何问题可以讨论:609550035。

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

3D模型

序号文件名称下载次数
暂无数据

附件

序号文件名称下载次数
暂无数据
克隆工程
添加到专辑
0
0
分享
侵权投诉
知识产权声明&复刻说明

本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。

请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。

底部导航