generated from ben/template
initial commit
This commit is contained in:
BIN
aitrip-inmp441-amazon.jpg
Normal file
BIN
aitrip-inmp441-amazon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
esp32 pinout.png
Normal file
BIN
esp32 pinout.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 366 KiB |
BIN
esp32-pinout-devkitv1.png
Normal file
BIN
esp32-pinout-devkitv1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 346 KiB |
BIN
esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf
Normal file
BIN
esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf
Normal file
Binary file not shown.
BIN
inmp441-datasheet.pdf
Normal file
BIN
inmp441-datasheet.pdf
Normal file
Binary file not shown.
183
mic/mic.ino
Normal file
183
mic/mic.ino
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <math.h>
|
||||
#include <driver/i2s.h>
|
||||
#include <driver/touch_pad.h>
|
||||
|
||||
const char* ssid = "Fios-8EKF5";
|
||||
const char* password = "hero816cars7050jon";
|
||||
const char* DEST_IP = "192.168.1.190";
|
||||
const int UDP_PORT = 5004;
|
||||
|
||||
const int OUTPUT_RATE = 8000; // 8kHz rtp payload rate
|
||||
const int MIC_RATE = 8000; // capture rate inmp441
|
||||
const int FRAME_SIZE = 160; // 20ms frames @ 8kHz
|
||||
const float TONE_HZ = 440.0;
|
||||
const float MIC_GAIN = 4.0f;
|
||||
|
||||
const touch_pad_t TOUCH_PIN = TOUCH_PAD_NUM4; // gpio13
|
||||
const int TOUCH_THRESHOLD = 500;
|
||||
|
||||
const int I2S_WS = 25;
|
||||
const int I2S_SCK = 22;
|
||||
const int I2S_SD = 21;
|
||||
|
||||
WiFiUDP udp;
|
||||
uint16_t seq = 0;
|
||||
uint32_t ts = 0;
|
||||
bool tone_mode = true; // start with tone
|
||||
|
||||
struct rtp_hdr {
|
||||
uint8_t vpxcc;
|
||||
uint8_t mpt;
|
||||
uint16_t seq;
|
||||
uint32_t ts;
|
||||
uint32_t ssrc;
|
||||
};
|
||||
|
||||
static const int16_t exp_lut[256] = {
|
||||
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
|
||||
};
|
||||
|
||||
uint8_t linear2ulaw(int16_t pcm_val) {
|
||||
int sign = (pcm_val >> 8) & 0x80;
|
||||
if (sign) pcm_val = (short)-pcm_val;
|
||||
if (pcm_val > 32635) pcm_val = 32635;
|
||||
pcm_val = pcm_val + 0x84;
|
||||
int exponent = exp_lut[(pcm_val >> 7) & 0xFF];
|
||||
int mantissa = (pcm_val >> (exponent + 3)) & 0x0F;
|
||||
return ~(sign | (exponent << 4) | mantissa);
|
||||
}
|
||||
|
||||
void setup_i2s_mic() {
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
.sample_rate = MIC_RATE,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 256,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0
|
||||
};
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = I2S_SCK,
|
||||
.ws_io_num = I2S_WS,
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
.data_in_num = I2S_SD
|
||||
};
|
||||
|
||||
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
|
||||
i2s_set_pin(I2S_NUM_0, &pin_config);
|
||||
i2s_zero_dma_buffer(I2S_NUM_0);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
touch_pad_init();
|
||||
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
|
||||
touch_pad_config(TOUCH_PIN, 0);
|
||||
|
||||
setup_i2s_mic();
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
|
||||
Serial.printf("\nconnected, ip=%s\n", WiFi.localIP().toString().c_str());
|
||||
udp.begin(UDP_PORT);
|
||||
|
||||
Serial.println("tap pin 13 to switch modes");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned long last_send = 0; // timer
|
||||
static unsigned long last_touch_check = 0; // timer
|
||||
static bool last_touch_state = false;
|
||||
static const unsigned long FRAME_INTERVAL = 20; // 20ms
|
||||
static const unsigned long TOUCH_CHECK_INTERVAL = 50; // check every 50ms
|
||||
|
||||
// check touch state periodically
|
||||
unsigned long now = millis();
|
||||
if (now - last_touch_check > TOUCH_CHECK_INTERVAL) {
|
||||
uint16_t touch_val;
|
||||
touch_pad_read(TOUCH_PIN, &touch_val);
|
||||
bool touched = touch_val < TOUCH_THRESHOLD;
|
||||
|
||||
// detect touch press (not hold)
|
||||
if (touched && !last_touch_state) {
|
||||
tone_mode = !tone_mode;
|
||||
Serial.printf("%s mode active\n", tone_mode ? "tone" : "mic");
|
||||
delay(300); // debounce
|
||||
}
|
||||
|
||||
last_touch_state = touched;
|
||||
last_touch_check = now;
|
||||
}
|
||||
|
||||
// frame buffer
|
||||
if (now - last_send < FRAME_INTERVAL) {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t packet[12 + FRAME_SIZE];
|
||||
int16_t samples[FRAME_SIZE];
|
||||
|
||||
if (tone_mode) {
|
||||
// fixed tone generation with integer phase accumulator
|
||||
static uint32_t phase_acc = 0;
|
||||
const uint32_t phase_inc_int = (uint32_t)(TONE_HZ * (1ULL << 32) / OUTPUT_RATE);
|
||||
|
||||
for (int i = 0; i < FRAME_SIZE; i++) {
|
||||
float phase = (phase_acc >> 16) * (2.0 * M_PI / 65536.0);
|
||||
samples[i] = (int16_t)(16000.0 * sinf(phase));
|
||||
phase_acc += phase_inc_int;
|
||||
}
|
||||
|
||||
} else {
|
||||
int32_t mic_buf[FRAME_SIZE];
|
||||
size_t bytes_read = 0;
|
||||
i2s_read(I2S_NUM_0, mic_buf, sizeof(mic_buf), &bytes_read, portMAX_DELAY);
|
||||
|
||||
if (bytes_read != sizeof(mic_buf)) {
|
||||
memset(samples, 0, sizeof(samples));
|
||||
} else {
|
||||
for (int i = 0; i < FRAME_SIZE; i++) {
|
||||
float y = (mic_buf[i] >> 16) * MIC_GAIN; //gain
|
||||
if (y > 32767.0f) y = 32767.0f;
|
||||
if (y < -32768.0f) y = -32768.0f;
|
||||
samples[i] = (int16_t)y;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < FRAME_SIZE; i++) {
|
||||
packet[12 + i] = linear2ulaw(samples[i]);
|
||||
}
|
||||
|
||||
rtp_hdr* hdr = (rtp_hdr*)packet;
|
||||
hdr->vpxcc = 0x80;
|
||||
hdr->mpt = 0x00;
|
||||
hdr->seq = htons(seq++);
|
||||
hdr->ts = htonl(ts);
|
||||
hdr->ssrc = htonl(0x12345678);
|
||||
|
||||
udp.beginPacket(DEST_IP, UDP_PORT);
|
||||
udp.write(packet, sizeof(packet));
|
||||
udp.endPacket();
|
||||
|
||||
ts += FRAME_SIZE;
|
||||
last_send = now;
|
||||
|
||||
yield();
|
||||
}
|
||||
183
mic/mic.ino~
Normal file
183
mic/mic.ino~
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <math.h>
|
||||
#include <driver/i2s.h>
|
||||
#include <driver/touch_pad.h>
|
||||
|
||||
const char* ssid = "Fios-8EKF5";
|
||||
const char* password = "hero816cars7050jon";
|
||||
const char* DEST_IP = "192.168.1.190";
|
||||
const int UDP_PORT = 5004;
|
||||
|
||||
const int OUTPUT_RATE = 8000; // 8kHz rtp payload rate
|
||||
const int MIC_RATE = 16000; // capture rate inmp441
|
||||
const int FRAME_SIZE = 160; // 20ms frames @ 8kHz
|
||||
const float TONE_HZ = 440.0;
|
||||
|
||||
const touch_pad_t TOUCH_PIN = TOUCH_PAD_NUM4; // gpio13
|
||||
const int TOUCH_THRESHOLD = 800; // adjust as needed
|
||||
|
||||
// inmp441 pins - change as needed
|
||||
const int I2S_WS = 22; // lrcl / ws
|
||||
const int I2S_SCK = 26; // bclk
|
||||
const int I2S_SD = 21; // data from mic to esp32
|
||||
|
||||
WiFiUDP udp;
|
||||
uint16_t seq = 0;
|
||||
uint32_t ts = 0;
|
||||
bool tone_mode = true; // start with tone
|
||||
|
||||
struct rtp_hdr {
|
||||
uint8_t vpxcc;
|
||||
uint8_t mpt;
|
||||
uint16_t seq;
|
||||
uint32_t ts;
|
||||
uint32_t ssrc;
|
||||
};
|
||||
|
||||
static const int16_t exp_lut[256] = {
|
||||
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
|
||||
};
|
||||
|
||||
uint8_t linear2ulaw(int16_t pcm_val) {
|
||||
int sign = (pcm_val >> 8) & 0x80;
|
||||
if (sign) pcm_val = (short)-pcm_val;
|
||||
if (pcm_val > 32635) pcm_val = 32635;
|
||||
pcm_val = pcm_val + 0x84;
|
||||
int exponent = exp_lut[(pcm_val >> 7) & 0xFF];
|
||||
int mantissa = (pcm_val >> (exponent + 3)) & 0x0F;
|
||||
return ~(sign | (exponent << 4) | mantissa);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// init touch
|
||||
touch_pad_init();
|
||||
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
|
||||
touch_pad_config(TOUCH_PIN, 0);
|
||||
|
||||
// init i2s for adc sampling
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
.sample_rate = OUTPUT_RATE,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 128,
|
||||
.use_apll = false
|
||||
};
|
||||
|
||||
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
|
||||
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0); // gpio36
|
||||
i2s_adc_enable(I2S_NUM_0);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
|
||||
Serial.printf("\nconnected, ip=%s\n", WiFi.localIP().toString().c_str());
|
||||
udp.begin(UDP_PORT);
|
||||
|
||||
Serial.println("tap pin 13 to switch modes");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned long last_send = 0;
|
||||
static unsigned long last_touch_check = 0;
|
||||
static bool last_touch_state = false;
|
||||
static const unsigned long FRAME_INTERVAL = 20; // 20ms
|
||||
static const unsigned long TOUCH_CHECK_INTERVAL = 50; // check every 50ms
|
||||
|
||||
// check touch state periodically
|
||||
unsigned long now = millis();
|
||||
if (now - last_touch_check > TOUCH_CHECK_INTERVAL) {
|
||||
uint16_t touch_val;
|
||||
touch_pad_read(TOUCH_PIN, &touch_val);
|
||||
bool touched = touch_val < TOUCH_THRESHOLD;
|
||||
|
||||
// detect touch press (not hold)
|
||||
if (touched && !last_touch_state) {
|
||||
tone_mode = !tone_mode;
|
||||
Serial.printf("switched to %s mode\n", tone_mode ? "tone" : "mic");
|
||||
delay(200); // debounce
|
||||
}
|
||||
|
||||
last_touch_state = touched;
|
||||
last_touch_check = now;
|
||||
}
|
||||
|
||||
// only send if it's time
|
||||
if (now - last_send < FRAME_INTERVAL) {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t packet[12 + FRAME_SIZE];
|
||||
int16_t samples[FRAME_SIZE];
|
||||
|
||||
if (tone_mode) {
|
||||
// fixed tone generation with integer phase accumulator
|
||||
static uint32_t phase_acc = 0;
|
||||
const uint32_t phase_inc_int = (uint32_t)(TONE_HZ * (1ULL << 32) / OUTPUT_RATE);
|
||||
|
||||
for (int i = 0; i < FRAME_SIZE; i++) {
|
||||
float phase = (phase_acc >> 16) * (2.0 * M_PI / 65536.0);
|
||||
samples[i] = (int16_t)(16000.0 * sinf(phase));
|
||||
phase_acc += phase_inc_int;
|
||||
}
|
||||
|
||||
} else {
|
||||
// read from i2s adc buffer - need multiple reads to fill frame
|
||||
static float dc_filter = 2048.0; // dc offset removal filter
|
||||
int samples_filled = 0;
|
||||
|
||||
while (samples_filled < FRAME_SIZE) {
|
||||
size_t bytes_read;
|
||||
uint16_t i2s_buffer[64]; // bigger buffer
|
||||
i2s_read(I2S_NUM_0, i2s_buffer, 128, &bytes_read, 10);
|
||||
|
||||
int samples_read = bytes_read / 2; // 2 bytes per sample
|
||||
for (int i = 0; i < samples_read && samples_filled < FRAME_SIZE; i++) {
|
||||
// i2s adc gives 12-bit data in upper bits of 16-bit word
|
||||
int adc_val = (i2s_buffer[i] >> 4) & 0x0FFF;
|
||||
|
||||
// dc offset removal with simple iir filter
|
||||
dc_filter = dc_filter * 0.999 + adc_val * 0.001;
|
||||
|
||||
// scale and store
|
||||
samples[samples_filled] = (int16_t)((adc_val - dc_filter) * 16);
|
||||
samples_filled++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// µ-law encode
|
||||
for (int i = 0; i < FRAME_SIZE; i++) {
|
||||
packet[12 + i] = linear2ulaw(samples[i]);
|
||||
}
|
||||
|
||||
// build rtp header
|
||||
rtp_hdr* hdr = (rtp_hdr*)packet;
|
||||
hdr->vpxcc = 0x80;
|
||||
hdr->mpt = 0x00;
|
||||
hdr->seq = htons(seq++);
|
||||
hdr->ts = htonl(ts);
|
||||
hdr->ssrc = htonl(0x12345678);
|
||||
|
||||
// send packet
|
||||
udp.beginPacket(DEST_IP, UDP_PORT);
|
||||
udp.write(packet, sizeof(packet));
|
||||
|
||||
ts += FRAME_SIZE;
|
||||
last_send = now;
|
||||
|
||||
yield();
|
||||
}
|
||||
17597
vtech.org~
Normal file
17597
vtech.org~
Normal file
File diff suppressed because it is too large
Load Diff
255
web/web.ino
Normal file
255
web/web.ino
Normal file
@@ -0,0 +1,255 @@
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <Preferences.h>
|
||||
#include <driver/touch_pad.h>
|
||||
|
||||
preferences prefs;
|
||||
webserver server(80);
|
||||
|
||||
// touch pad used as boot override / setup trigger
|
||||
const touch_pad_t touch_pin = TOUCH_PAD_NUM4; // gpio13
|
||||
const int touch_threshold = 500;
|
||||
|
||||
// softap fallback
|
||||
const char* ap_ssid = "baby-monitor-setup";
|
||||
const char* ap_password = "setup1234";
|
||||
|
||||
// saved / editable config
|
||||
String wifi_ssid = "";
|
||||
String wifi_password = "";
|
||||
String dest_ip_str = "192.168.1.190";
|
||||
uint16_t udp_port = 5004;
|
||||
|
||||
bool config_mode = false;
|
||||
const unsigned long wifi_connect_timeout_ms = 15000;
|
||||
|
||||
String html_escape(const String& s) {
|
||||
String out = s;
|
||||
out.replace("&", "&");
|
||||
out.replace("<", "<");
|
||||
out.replace(">", ">");
|
||||
out.replace("\"", """);
|
||||
return out;
|
||||
}
|
||||
|
||||
void load_config() {
|
||||
prefs.begin("bmcfg", true);
|
||||
wifi_ssid = prefs.getString("ssid", "");
|
||||
wifi_password = prefs.getString("pwd", "");
|
||||
dest_ip_str = prefs.getString("destip", "192.168.1.190");
|
||||
udp_port = prefs.getUShort("port", 5004);
|
||||
prefs.end();
|
||||
}
|
||||
|
||||
void save_config(const String& ssid, const String& pwd, const String& dest_ip, uint16_t port) {
|
||||
prefs.begin("bmcfg", false);
|
||||
prefs.putString("ssid", ssid);
|
||||
prefs.putString("pwd", pwd);
|
||||
prefs.putString("destip", dest_ip);
|
||||
prefs.putUShort("port", port);
|
||||
prefs.end();
|
||||
}
|
||||
|
||||
void clear_config() {
|
||||
prefs.begin("bmcfg", false);
|
||||
prefs.clear();
|
||||
prefs.end();
|
||||
}
|
||||
|
||||
bool force_setup_requested() {
|
||||
// give touch pad a moment to settle
|
||||
delay(100);
|
||||
|
||||
uint16_t touch_val = 0;
|
||||
touch_pad_read(touch_pin, &touch_val);
|
||||
|
||||
Serial.printf("touch boot check: %u\n", touch_val);
|
||||
return touch_val < touch_threshold;
|
||||
}
|
||||
|
||||
void handle_root() {
|
||||
String page =
|
||||
"<!doctype html>"
|
||||
"<html><head>"
|
||||
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
|
||||
"<title>baby monitor setup</title>"
|
||||
"<style>"
|
||||
"body{font-family:sans-serif;max-width:520px;margin:2rem auto;padding:0 1rem;}"
|
||||
"input{width:100%;padding:.6rem;margin:.2rem 0 1rem 0;box-sizing:border-box;}"
|
||||
"button{padding:.7rem 1rem;margin-right:.5rem;}"
|
||||
"code{background:#eee;padding:.1rem .3rem;}"
|
||||
"</style>"
|
||||
"</head><body>"
|
||||
"<h2>baby monitor setup</h2>"
|
||||
"<p>connect to ap <code>" + String(ap_ssid) + "</code> then browse to <code>192.168.4.1</code>.</p>"
|
||||
"<form method='post' action='/save'>"
|
||||
"<label>wifi ssid</label>"
|
||||
"<input name='ssid' value='" + html_escape(wifi_ssid) + "'>"
|
||||
"<label>wifi password</label>"
|
||||
"<input name='pwd' type='password' value='" + html_escape(wifi_password) + "'>"
|
||||
"<label>destination ip</label>"
|
||||
"<input name='destip' value='" + html_escape(dest_ip_str) + "'>"
|
||||
"<label>udp port</label>"
|
||||
"<input name='port' value='" + String(udp_port) + "'>"
|
||||
"<button type='submit'>save and reboot</button>"
|
||||
"</form>"
|
||||
"<form method='post' action='/clear' onsubmit='return confirm(\"clear saved config?\");'>"
|
||||
"<button type='submit'>clear saved config</button>"
|
||||
"</form>"
|
||||
"<p><a href='/status'>status json</a></p>"
|
||||
"</body></html>";
|
||||
|
||||
server.send(200, "text/html", page);
|
||||
}
|
||||
|
||||
void handle_status() {
|
||||
String json = "{";
|
||||
json += "\"config_mode\":" + String(config_mode ? "true" : "false") + ",";
|
||||
json += "\"ap_ssid\":\"" + String(ap_ssid) + "\",";
|
||||
json += "\"saved_ssid\":\"" + html_escape(wifi_ssid) + "\",";
|
||||
json += "\"dest_ip\":\"" + html_escape(dest_ip_str) + "\",";
|
||||
json += "\"udp_port\":" + String(udp_port);
|
||||
json += "}";
|
||||
|
||||
server.send(200, "application/json", json);
|
||||
}
|
||||
|
||||
bool valid_ipv4(const String& s) {
|
||||
ipaddress ip;
|
||||
return ip.fromString(s);
|
||||
}
|
||||
|
||||
void handle_save() {
|
||||
String new_ssid = server.arg("ssid");
|
||||
String new_pwd = server.arg("pwd");
|
||||
String new_dest = server.arg("destip");
|
||||
uint16_t new_port = (uint16_t)server.arg("port").toInt();
|
||||
|
||||
if (new_ssid.length() == 0) {
|
||||
server.send(400, "text/plain", "ssid required");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valid_ipv4(new_dest)) {
|
||||
server.send(400, "text/plain", "invalid destination ip");
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_port == 0) {
|
||||
server.send(400, "text/plain", "invalid udp port");
|
||||
return;
|
||||
}
|
||||
|
||||
save_config(new_ssid, new_pwd, new_dest, new_port);
|
||||
|
||||
server.send(200, "text/html",
|
||||
"<html><body><h3>saved. rebooting...</h3></body></html>");
|
||||
delay(1000);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void handle_clear() {
|
||||
clear_config();
|
||||
server.send(200, "text/html",
|
||||
"<html><body><h3>config cleared. rebooting...</h3></body></html>");
|
||||
delay(1000);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void start_config_ap() {
|
||||
config_mode = true;
|
||||
|
||||
WiFi.disconnect(true, true);
|
||||
delay(250);
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(ap_ssid, ap_password);
|
||||
|
||||
IPAddress ip = WiFi.softAPIP();
|
||||
Serial.printf("config ap active\n");
|
||||
Serial.printf(" ssid: %s\n", ap_ssid);
|
||||
Serial.printf(" pass: %s\n", ap_password);
|
||||
Serial.printf(" ip: %s\n", ip.toString().c_str());
|
||||
|
||||
server.on("/", HTTP_GET, handle_root);
|
||||
server.on("/save", HTTP_POST, handle_save);
|
||||
server.on("/clear", HTTP_POST, handle_clear);
|
||||
server.on("/status", HTTP_GET, handle_status);
|
||||
server.begin();
|
||||
}
|
||||
|
||||
bool try_connect_wifi() {
|
||||
if (wifi_ssid.length() == 0) {
|
||||
Serial.println("no saved wifi ssid");
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFi.disconnect(true, true);
|
||||
delay(250);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());
|
||||
|
||||
Serial.printf("connecting to wifi: %s\n", wifi_ssid.c_str());
|
||||
|
||||
unsigned long start = millis();
|
||||
while (WiFi.status() != WL_CONNECTED &&
|
||||
millis() - start < wifi_connect_timeout_ms) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.printf("connected\n");
|
||||
Serial.printf(" ip: %s\n", WiFi.localIP().toString().c_str());
|
||||
Serial.printf(" dest ip: %s\n", dest_ip_str.c_str());
|
||||
Serial.printf(" udp port: %u\n", udp_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
Serial.println("wifi connect failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(200);
|
||||
|
||||
touch_pad_init();
|
||||
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
|
||||
touch_pad_config(touch_pin, 0);
|
||||
|
||||
load_config();
|
||||
|
||||
if (force_setup_requested()) {
|
||||
Serial.println("force setup mode requested");
|
||||
start_config_ap();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!try_connect_wifi()) {
|
||||
start_config_ap();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (config_mode) {
|
||||
server.handleClient();
|
||||
delay(2);
|
||||
return;
|
||||
}
|
||||
|
||||
// placeholder for later normal-mode behavior
|
||||
static unsigned long last_print = 0;
|
||||
if (millis() - last_print > 5000) {
|
||||
Serial.printf("normal mode alive. wifi=%s ip=%s dest=%s:%u\n",
|
||||
wifi_ssid.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
dest_ip_str.c_str(),
|
||||
udp_port);
|
||||
last_print = millis();
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
Reference in New Issue
Block a user