65.9K
CodeProject 正在变化。 阅读更多。
Home

DirectControl - 通过WiFi远程控制电气负载

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (7投票s)

2018 年 8 月 6 日

CPOL

4分钟阅读

viewsIcon

20796

downloadIcon

283

使用ESP8266模块远程控制大功率电器

引言

这是一篇入门文章,展示了如何使用廉价的电子元件,例如流行的 ESP8266 无线模块,轻松控制大功率电器。

背景

ESP8266 是一个带有完整 TCP/IP 堆栈和微控制器编程能力的 Wi-Fi 模块。ESP8266 模块可以作为接入点 (Access Point) 模式或客户端 (Station) 模式运行,或者同时以两种模式运行(混合模式),尽管后者不稳定。它还拥有 1MB 的闪存。

模块框图

模式详细技术规格

  • 802.11 b / g / n 无线标准
  • STA / AP 模式支持
  • TCP / IP 协议栈,一个套接字
  • 支持标准 TCP / UDP 服务器和客户端
  • 支持串口波特率配置:1200/2400/4800/9600/19200/38400/57600/74800/115200 bps
  • 支持串口数据位:5/6/7/8 位
  • 支持串口奇偶校验:无
  • 支持串口停止位:1/2 位
  • ESP8266 GPIO 引脚 0/2/4/5/9/10/12/13/14/15/16 / ADC / EN / * UART TX / UART RX
  • WiFi 工作电流:连续传输操作:≈70mA (200mA MAX),空闲模式:<200uA
  • 串口 WiFi 传输速率:110-460800bps

市面上有许多 ESP8266 模块的变种。我使用的是 ESP8266MOD,因为它有许多 GPIO 引脚从芯片引出,外部天线接口和坚固的金属外壳。

电路图

如果您熟悉电子学基础知识,您会发现构建此电路图非常容易。

您将需要:

  • ESP8266MOD
  • 3.3V 电源
  • Fortek SSR40-DA-H 固态继电器
  • 一系列排针连接器和电阻
  • USB-UART 3.3V 电平转换器
免责声明:本应用使用来自市电线的危险电压。请小心!调试时请断开市电连接!确保所有部分都绝缘良好!

准备开发环境

作为开发套件,我选择了Espressif ESP8266 非官方开发套件
Espressif ESP8266 非官方开发套件的安装和配置说明

  1. 下载 Espressif ESP8266 非官方开发套件 (148Mb) 并安装。
  2. 下载并安装 Java Runtime x86 或 x64 (jre-8uXXX-windows-xxx.exe)
  3. 下载并安装 Eclipse Neon x86Eclipse Neon x86_64 用于 C++ 开发。将压缩包解压到 C 盘根目录。
  4. 下载并安装 MinGW。运行 mingw-get-setup.exe,在安装过程中选择无 GUI,即取消勾选“... also install support for the graphical user interface”。
  5. 下载脚本以自动化 MinGW 包的安装。
  6. 运行 install-mingw-package.bat 文件。下载预加载的 MinGW 文件包可确保它们被安装,有时 MinGW 包所在的服务器不再可用,导致所需包未安装。
  7. c:\eclipse\eclipse.exe 目录启动 Eclipse Neon。
  8. 在 Eclipse 中,选择 File -> Import -> General -> Existing Project into Workspace,在 Select root directory 行,选择项目根目录并导入。
  9. 您需要用这个文件替换文件 c:\Espressif\examples\ESP8266\common_cpp.mkcommon_cpp.zip。这是为了确保正确的 C99 构建。

Using the Code

以下代码用于启动一个接入点

void ICACHE_FLASH_ATTR WifiConnectorInit()
{
	WifiEnterWorkMode();
	captdnsInit();
	WebInit(); // htttpdInit
}
void ICACHE_FLASH_ATTR WifiEnterWorkMode()
{
	wifi_set_opmode(SOFTAP_MODE);
		struct softap_config apconfig;
		if(wifi_softap_get_config(&apconfig))
		{
			wifi_softap_dhcps_stop();
			os_memset(apconfig.ssid, 0, sizeof(apconfig.ssid));
			os_memset(apconfig.password, 0, sizeof(apconfig.password));
			os_sprintf((char*)apconfig.ssid, "iqhub%ld", (long int)56); //our ssid name
			apconfig.ssid_len = os_strlen((char*)apconfig.ssid);
			apconfig.authmode = AUTH_OPEN;
			apconfig.ssid_hidden = 0;
			apconfig.channel = 9;
			apconfig.max_connection = 4;
			apconfig.beacon_interval = 100;
			if(!wifi_softap_set_config(&apconfig))
				os_printf("%s", "ESP8266 not set AP config!\r\n");
			struct ip_info ipinfo;
			wifi_get_ip_info(SOFTAP_IF, &ipinfo);
			IP4_ADDR(&ipinfo.ip, 192, 168, 4, 1);
			IP4_ADDR(&ipinfo.gw, 192, 168, 4, 1);
			IP4_ADDR(&ipinfo.netmask, 255, 255, 255, 0);
			wifi_set_ip_info(SOFTAP_IF, &ipinfo);
			wifi_softap_dhcps_start();
		}
		wifi_set_broadcast_if(3); //1:station; 2:soft-AP, 3:station+soft-APs
}

对于这个项目,我们的 URL 查询集将是这样的

HttpdBuiltInUrl builtInUrls[]={
	{"*", cgiRedirectApClientToHostname, "esp.nonet"},
	{"/", cgiRedirect, "/index.html"},
    {"/index.html", cgiIndex, NULL},
	{"/onButton", cgiOnButton, "/index.html"},
	{"/offButton", cgiOffButton, "/index.html"},
    {NULL, NULL, NULL}
};

void ICACHE_FLASH_ATTR WebInit() {
	httpdInit(builtInUrls, 80);
}

CGI 脚本是在 Web 服务器上运行的程序,它们根据客户端请求动态生成网页。
为了响应这些客户端请求中的一部分,我们需要实现几个自定义 CGI 脚本。

static char * g_web_index = { //... }; // Our web page as byte array,
				       // contains some format notations 
				       // such as "%s" to insert curr led state
static char * g_web_index_formated = NULL; //  Buffer for formatted web pages 
typedef struct {
    int arrayPos;
    char buff[128];
} LongPageSendState; 

bool g_ledState = false;

int ICACHE_FLASH_ATTR cgiIndex(HttpdConnData *connData) {

	LongPageSendState *state= (LongPageSendState *)connData->cgiData;
    int len;
    //If the browser unexpectedly closes the connection, the CGI will be called
    //with connData->conn=NULL. We can use this to clean up any data. It's pretty relevant
    //here because otherwise we may leak memory when the browser aborts the connection.
    if (connData->conn==NULL) {
        //Connection aborted. Clean up.
        if (state!=NULL) os_free(state);
        if(g_web_index_formated) {
        	os_free(g_web_index_formated);
        	g_web_index_formated = NULL;
        }
        return HTTPD_CGI_DONE;
    }
    if (state == NULL) {
        //This is the first call to the CGI for this webbrowser request.
        //Allocate a state structure.
        state= (LongPageSendState *)os_malloc(sizeof(LongPageSendState));
        g_web_index_formated = (char *)os_malloc(sizeof(g_web_index) + 10);
        //Save the ptr in connData so we get it passed the next time as well.
        connData->cgiData=state;
        //Set initial pointer to start of string
        state->arrayPos= 0;
        state->buff[0] = 0;
        //We need to send the headers before sending any data. Do that now.
        httpdStartResponse(connData, 200);
        httpdHeader(connData, "Content-Type", "text/html");
        httpdEndHeaders(connData);
    }
    //Figure out length of string to send. We will never send more than 128 bytes in this example.
    os_sprintf(g_web_index_formated, g_web_index,  g_ledState ? "ON" : "OFF");
    len = os_strlen(g_web_index_formated) - state->arrayPos; //Get remaining length
    if (len>128) len=128; //Never send more than 128 bytes
    if (len==0) {
    	os_free(state);
    	os_free(g_web_index_formated);
    	g_web_index_formated = NULL;
    	return HTTPD_CGI_DONE;
    }
    //substr
    for(int i = 0; i < len; i++){
    	state->buff[i] = g_web_index_formated[i + state->arrayPos];
    }
    //Send that amount of data
    httpdSend(connData, state->buff, len);
    //Adjust stringPos to first byte we haven't sent yet
    state->arrayPos+=len;
    //See if we need to send more
    if (len == 128) {
        //we have more to send; let the webserver call this function again.
        return HTTPD_CGI_MORE;
    } else {
        //We're done. Clean up here as well: if the CGI function returns HTTPD_CGI_DONE, it will
        //not be called again.
        os_free(state);
        os_free(g_web_index_formated);
        g_web_index_formated = NULL;
        return HTTPD_CGI_DONE;
    }
}

int ICACHE_FLASH_ATTR cgiOnButton(HttpdConnData *connData){
	g_ledState = true;
	ioLed(1);
	return cgiRedirect(connData); // Redirect to /index.html
}
int ICACHE_FLASH_ATTR cgiOffButton(HttpdConnData *connData){
	g_ledState = false;
	ioLed(0);
	return cgiRedirect(connData); // Redirect to /index.html
}

我们的网页 HTML 代码将是这样的

<html>
<head><meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:">
<style>html { text-align: center;} </style></head>
<body><h1>ESP8266 Web Server</h1>
<p>GPIO 5 - State %s</p>     
<p><a href="onButton"><button class="button">ON</button></a></p>
<p><a href="offButton"><button class="button">OFF</button></a></p>
</body></html>

将 Web 转换为二进制格式

为了将 HTML 页面转换为 C++ 代码,我使用了一个实用程序,它将 HTML 文件路径作为命令行参数。
用法

BinToCArray.exe index.html

输出如下所示

这个实用程序的源代码非常简单

int main(int argc, char** argv) {
	assert(argc == 2);
	char* fn = argv[1];
	FILE* f = fopen(fn, "r");
	fseek(f, 0, SEEK_END); 
	long size = ftell(f); 
	fseek(f, 0, SEEK_SET); 

	printf("char a[%ld] = {\n", size );
	unsigned long n = 0;
	while (!feof(f)) {
		unsigned char c;
		if (fread(&c, 1, 1, f) == 0) break;
		printf("0x%.2X,", (int)c); //Print in hex
		++n;
		if (n % 10 == 0) printf("\n");
	}
	fclose(f);
	printf("};\n");
}

通过这样做,我们可以将网页存储在 ESP8266 的 RAM 中。作为替代方案,我建议您将其写入头文件中,并在使用它的模块中包含它。

如何构建和刷写

  1. 在 Eclipse 中,选择 File -> Import -> General -> Existing Project into Workspace,在 Select root directory 行,选择项目根目录并导入。
  2. 要构建项目,请在菜单栏中选择 Project -> Build Project

  3. 要刷写 ESP8266,请转到 Make Target 视图中的项目文件夹,然后构建“flash”目标。

  4. 您需要 USB-3.3V UART 转换器,市面上有许多可供选择,价格也很便宜。
  5. 在刷写之前,请确保 GPIO0 连接到地,然后重置芯片(断电再上电),刷写完成后断开与地的连接。

如何使用

如果刷写成功,模块将启动一个名为 iqhub56 的接入点,使用任何手机连接它,然后打开网页地址:http://192.168.4.1。您将看到一个小型网页,您可以在其中控制引脚输出。以下是一个展示该设备工作原理的小视频。

历史

  • 2018 年 8 月 6 日 - 第一个版本
© . All rights reserved.