Ngrok内网穿透实现远程监控

使用ESP32CAM实现视频流功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "esp_camera.h"
#include <WiFi.h>

const char* ssid = "MERCURY";
const char* password = "11235813";

#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27

#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22

WiFiServer server(80);
bool connected = false;
WiFiClient live_client;


String index_html = "<meta charset=\"utf-8\"/>\n" \
"<style>\n" \
"#content {\n" \
"display: flex;\n" \
"flex-direction: column;\n" \
"justify-content: center;\n" \
"align-items: center;\n" \
"text-align: center;\n" \
"min-height: 100vh;}\n" \
"</style>\n" \
"<body bgcolor=\"#000000\"><div id=\"content\"><h2 style=\"color:#ffffff\">Murphy LIVE</h2><img src=\"video\"></div></body>";

void configCamera(){
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;

config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 9;
config.fb_count = 1;

esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
}

//continue sending camera frame
void liveCam(WiFiClient &client){
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Frame buffer could not be acquired");
return;
}
client.print("--frame\n");
client.print("Content-Type: image/jpeg\n\n");
client.flush();
client.write(fb->buf, fb->len);
client.flush();
client.print("\n");
esp_camera_fb_return(fb);
}

void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
String IP = WiFi.localIP().toString();
Serial.println("IP address: " + IP);
index_html.replace("server_ip", IP);
server.begin();
configCamera();
}

void http_resp(){
WiFiClient client = server.available();
if (client.connected()) {
String req = "";
while(client.available()){
req += (char)client.read();
}
Serial.println("request " + req);
int addr_start = req.indexOf("GET") + strlen("GET");
int addr_end = req.indexOf("HTTP", addr_start);
if (addr_start == -1 || addr_end == -1) {
Serial.println("Invalid request " + req);
return;
}
req = req.substring(addr_start, addr_end);
req.trim();
Serial.println("Request: " + req);
client.flush();

String s;
if (req == "/")
{
s = "HTTP/1.1 200 OK\n";
s += "Content-Type: text/html\n\n";
s += index_html;
s += "\n";
client.print(s);
client.stop();
}
else if (req == "/video")
{
live_client = client;
live_client.print("HTTP/1.1 200 OK\n");
live_client.print("Content-Type: multipart/x-mixed-replace; boundary=frame\n\n");
live_client.flush();
connected = true;
}
else
{
s = "HTTP/1.1 404 Not Found\n\n";
client.print(s);
client.stop();
}
}
}

void loop() {
http_resp();
if(connected == true){
liveCam(live_client);
}
}

使用Ngrok服务将私有IP映射到公网

  1. 注册ngrok,得到个人token
  1. 下载合适的ngrok - download版本到本地计算机,本地计算机需要和ESP32CAM在同一个局域网内。
  2. 进入命令行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Microsoft Windows [版本 10.0.19044.1766]
(c) Microsoft Corporation。保留所有权利。

C:\Users\DELL>d:
D:\>cd D:\Develop_Arduino\ESP32CAM\ngrok
D:\Develop_Arduino\ESP32CAM\ngrok>ngrok.exe
NAME:
ngrok - tunnel local ports to public URLs and inspect traffic USAGE:
ngrok [command] [flags] DESCRIPTION:
ngrok exposes local networked services behinds NATs and firewalls to the
public internet over a secure tunnel. Share local websites, build/test
webhook consumers and self-host personal services.
Detailed help for each command is available with 'ngrok help <command>'.
Open http://localhost:4040 for ngrok's web interface to inspect traffic. Author:
ngrok - <support@ngrok.com> TERMS OF SERVICE: https://ngrok.com/tos EXAMPLES:
ngrok http 80 # secure public URL for port 80 web server
ngrok http --subdomain=baz 8080 # port 8080 available at baz.ngrok.io
ngrok http foo.dev:80 # tunnel to host:port instead of localhost
ngrok http https://localhost # expose a local https server
ngrok tcp 22 # tunnel arbitrary TCP traffic to port 22
ngrok tls --hostname=foo.com 443 # TLS traffic for foo.com to port 443
ngrok start foo bar baz # start tunnels from the configuration file COMMANDS:
api use ngrok agent as an api client
completion generates shell completion code for bash or zsh
config update or migrate ngrok's configuration file
credits prints author and licensing information
diagnose diagnose connection issues
help Help about any command
http start an HTTP tunnel
service run and control an ngrok service on a target operating system
start start tunnels by name from the configuration file
tcp start a TCP tunnel
tls start a TLS tunnel
tunnel start a tunnel for use with a tunnel-group backend
update update ngrok to the latest version
version print the version string

OPTIONS:
-h, --help help for ngrok
-v, --version version for ngrok

D:\Develop_Arduino\ESP32CAM\ngrok>ngrok authtoken <your token>
Authtoken saved to configuration file: C:\Users\DELL\AppData\Local/ngrok/ngrok.yml

D:\Develop_Arduino\ESP32CAM\ngrok>ngrok http 192.168.1.103:80

浏览器访问该域名,便可实现在外网访问私网的ESP32CAM。

需要注意的是,当该命令行关闭,则ngrok服务就结束了,所以这并不能长时间地将ESP32CAM推到公网。有长久使用计划地话,我的想法是改造废弃手机,将安卓手机刷成Linux,让Ngrok服务在手机上一直跑着,则这个问题就可以得到解决。

目前我并没有实现我的这个想法,手头没有合适的手机,有一个安卓5.1的手机,Termux和AidLux在这台老手机上不能正常运行,安卓版本太低了,想买一个二手手机,只跑整个一个服务又觉得不怎么值当的,所以该想法目前是搁置状态,之后有机会的话在尝试着实现一下。

还有一个安全性问题,映射到公网意味者任何人都可以访问, 因此还需要做一个后台,限制非法访问,这个目前还没想好应该怎么做。

项目来源自Arduino社区:How to Access ESP32-CAM Worldwide using ngrok - Arduino Project Hub


Ngrok内网穿透实现远程监控
https://cosmicdusty.cc/post/Electronic/IntranetPenetrationInNgrok/
作者
Murphy
发布于
2022年4月27日
许可协议