web server, AP->client switcher, config successfully passed to esp

This commit is contained in:
Evan Feenstra
2022-05-23 11:06:58 -07:00
parent c3e05c530e
commit c311b74884
9 changed files with 582 additions and 138 deletions

View File

@@ -40,4 +40,9 @@ These notes were tested for macOS
ls /dev/tty.*
ls /dev/cu.*
espmonitor /dev/tty.usbserial-1420
```
```
### clear NVS
espflash target/riscv32imc-esp-espidf/debug/clear
espmonitor /dev/tty.usbserial-1420

View File

@@ -31,3 +31,7 @@ serde_json = "1.0.81"
[build-dependencies]
embuild = "0.29"
anyhow = "1"
[[bin]]
name = "clear"
path = "src/clear.rs"

191
sphinx-key/config.html Normal file
View File

@@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="description" content="sphinxkey" />
<meta charset="utf-8">
<title>Sphinx Key</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
<style>
html {
font-family: Arial, Helvetica, sans-serif;
color: white;
font-size: 20px;
}
body{
padding:0;
background: #292a2d;
display: flex;
flex-direction: column;
align-items: center;
}
#logo{
margin-top:25px;
}
input {
margin-top:10px;
height:32px;
width:230px;
padding-left:15px;
border-radius: 16px;
}
button {
margin-top:20px;
height:40px;
width:108px;
color:white;
font-weight: bold;
border-radius: 20px;
background: #618AFF;
cursor: pointer;
}
button:disabled {
cursor:default;
background:grey;
}
@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
#button svg {
animation-name: spin;
animation-duration: 600ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
#loading {
display: none;
}
</style>
</head>
<body>
<svg id="logo" width="194px" height="186px" viewBox="0 0 194 186" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="white" transform="translate(-508.000000, -189.000000)" stroke-width="1" d="M667.21,214.51 C658.045169,206.151145 647.39915,199.578356 635.82,195.13 C625.907772,191.21621 615.366017,189.139033 604.71,189 L604.64,189 C593.983983,189.139033 583.442228,191.21621 573.53,195.13 C561.95085,199.578356 551.304831,206.151145 542.14,214.51 C526.56,228.7 508,254.87 508,299.01 C508.002384,301.229806 509.18759,303.280098 511.11,304.39 C511.3,304.5 529.85,315.39 533.38,336.77 C535.8,351.44 545.74,362.71 562.12,369.36 C569.522132,372.317389 577.318819,374.170534 585.26,374.86 C586.972704,374.964831 588.652634,374.357474 589.902326,373.181627 C591.152018,372.00578 591.860447,370.36591 591.86,368.65 L591.86,324.52 C595.931833,325.717067 600.155885,326.316653 604.4,326.3 C608.837411,326.319158 613.252794,325.675389 617.5,324.39 L617.5,368.65 C617.5,370.299647 618.15532,371.881728 619.321796,373.048204 C620.488272,374.21468 622.070353,374.87 623.72,374.87 L624.1,374.87 C632.041181,374.180534 639.837868,372.327389 647.24,369.37 C663.6,362.72 673.54,351.45 676,336.77 C677.588215,327.719728 681.863657,319.357004 688.27,312.77 C691.228816,309.557963 694.589929,306.74135 698.27,304.39 C700.19241,303.280098 701.377616,301.229806 701.38,299.01 C701.34,254.87 682.78,228.7 667.21,214.51 Z M604.64,201.43 L604.71,201.43 C617.38,201.43 635.81,205.99 652.35,218.35 L641.43,239.24 L567.63,239.24 L556.93,218.4 C573.49,206 592,201.43 604.64,201.43 Z M545.65,334.77 C542.12,313.39 527.11,300.48 520.48,295.72 C521.29,261.14 534.75,239.52 547.28,226.83 L557.6,246.93 L557.65,273.15 C557.6448,282.683193 559.914304,292.08004 564.27,300.56 C567.824721,307.643965 573.02275,313.774622 579.43,318.44 L579.43,361.54 C568.77,359.48 548.7,353.22 545.65,334.77 Z M575.3,294.77 C571.858657,288.054866 570.069114,280.615578 570.08,273.07 L570.079951,251.64 L639,251.64 L638.81,272.13 C638.761448,279.909114 636.94573,287.575481 633.5,294.55 C629.09,303.34 620.5,313.83 604.44,313.83 C587.69,313.87 579,301.93 575.3,294.77 Z M663.69,334.77 C662,345.01 654.98,352.77 642.82,357.77 C638.655184,359.452348 634.334139,360.718515 629.92,361.55 L629.92,318.12 C636.158361,313.363256 641.179183,307.194329 644.57,300.12 C648.863933,291.449073 651.131029,281.915644 651.2,272.24 L651.43,246.92 L662,226.77 C674.55,239.45 688.06,261.08 688.87,295.77 C682.23,300.46 667.22,313.37 663.69,334.77 Z" id="Shape"></path>
</svg>
<p style="max-width:260px;text-align:center;margin-top:32px;">
Enter your WiFi credentials and MQTT Broker to connect you Sphinx Key
</p>
<input id="ssid" placeholder="WiFi SSID" />
<input id="pass" placeholder="Password" />
<input id="broker" placeholder="Broker" />
<button id="button" type="submit">
<svg id="loading" height="16" width="16" viewbox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path fill="white" stroke="grey" d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" />
</svg>
<span style="margin:0 8px;">OK</span>
</button>
<p id="msg" style="max-width:260px;text-align:center;margin-top:32px;"></p>
</body>
<script>
function get(id){
return document.getElementById(id)
}
const params = getParams()
const button = get('button')
button.disabled = true
const ssid = get('ssid')
const pass = get('pass')
const broker = get('broker')
const loading = get('loading')
const msg = get('msg')
if(params['ssid']) {
ssid.value = params.ssid
}
if(params['pass']) {
pass.value = params.pass
}
if(params['broker']) {
broker.value = params.broker
}
function checker() {
if(ssid.value && broker.value && button.disabled) {
button.disabled = false
} else if(!ssid.value || !broker.value) {
button.disabled = true
}
}
ssid.onchange = function(){
checker()
}
ssid.oninput = function(){
checker()
}
broker.onchange = function(){
checker()
}
broker.oninput = function(){
checker()
}
button.onclick = function(e) {
fetchWithTimeout('/config?config=' +
encodeURIComponent(JSON.stringify({
ssid: ssid.value,
pass: pass.value || '',
broker: broker.value,
})), {
method:'POST',
timeout:3000,
})
.then(r=> r.json())
.then(finish)
.catch(e=> {
console.log(e)
msg.innerHTML = 'Failed to post configuration to the Sphinx Key'
})
}
async function finish(r) {
console.log("done!", r)
loading.style.display = 'inline-block'
button.disabled = true
await sleep(2000)
loading.style.display = 'none'
button.disabled = false
msg.innerHTML = 'Please unplug your Sphinx Key and plug it back in'
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
function getParams() {
const ps = new URLSearchParams(window.location.search);
const r = {};
for (const [k, v] of ps) {
r[k] = v;
}
return r;
}
async function fetchWithTimeout(resource, options = {}) {
const { timeout = 5000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
</script>
</html>

20
sphinx-key/src/clear.rs Normal file
View File

@@ -0,0 +1,20 @@
#![allow(unused_imports)]
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use esp_idf_svc::nvs::*;
use esp_idf_svc::nvs_storage::EspNvsStorage;
use embedded_svc::httpd::*;
use embedded_svc::wifi::*;
use embedded_svc::storage::Storage;
use std::sync::Arc;
fn main() -> Result<()> {
let default_nvs = Arc::new(EspDefaultNvs::new()?);
let mut store = EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("no storage");
store.remove("config").expect("couldnt remove config 1");
println!("NVS cleared!");
Ok(())
}

View File

@@ -1,8 +1,191 @@
pub const HTML: &str = r#"
<doctype html5>
<html>
<body>
hello!!!
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="description" content="sphinxkey" />
<meta charset="utf-8">
<title>Sphinx Key</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
<style>
html {
font-family: Arial, Helvetica, sans-serif;
color: white;
font-size: 20px;
}
body{
padding:0;
background: #292a2d;
display: flex;
flex-direction: column;
align-items: center;
}
#logo{
margin-top:25px;
}
input {
margin-top:10px;
height:32px;
width:230px;
padding-left:15px;
border-radius: 16px;
}
button {
margin-top:20px;
height:40px;
width:108px;
color:white;
font-weight: bold;
border-radius: 20px;
background: #618AFF;
cursor: pointer;
}
button:disabled {
cursor:default;
background:grey;
}
@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
#button svg {
animation-name: spin;
animation-duration: 600ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
#loading {
display: none;
}
</style>
</head>
<body>
<svg id="logo" width="194px" height="186px" viewBox="0 0 194 186" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="white" transform="translate(-508.000000, -189.000000)" stroke-width="1" d="M667.21,214.51 C658.045169,206.151145 647.39915,199.578356 635.82,195.13 C625.907772,191.21621 615.366017,189.139033 604.71,189 L604.64,189 C593.983983,189.139033 583.442228,191.21621 573.53,195.13 C561.95085,199.578356 551.304831,206.151145 542.14,214.51 C526.56,228.7 508,254.87 508,299.01 C508.002384,301.229806 509.18759,303.280098 511.11,304.39 C511.3,304.5 529.85,315.39 533.38,336.77 C535.8,351.44 545.74,362.71 562.12,369.36 C569.522132,372.317389 577.318819,374.170534 585.26,374.86 C586.972704,374.964831 588.652634,374.357474 589.902326,373.181627 C591.152018,372.00578 591.860447,370.36591 591.86,368.65 L591.86,324.52 C595.931833,325.717067 600.155885,326.316653 604.4,326.3 C608.837411,326.319158 613.252794,325.675389 617.5,324.39 L617.5,368.65 C617.5,370.299647 618.15532,371.881728 619.321796,373.048204 C620.488272,374.21468 622.070353,374.87 623.72,374.87 L624.1,374.87 C632.041181,374.180534 639.837868,372.327389 647.24,369.37 C663.6,362.72 673.54,351.45 676,336.77 C677.588215,327.719728 681.863657,319.357004 688.27,312.77 C691.228816,309.557963 694.589929,306.74135 698.27,304.39 C700.19241,303.280098 701.377616,301.229806 701.38,299.01 C701.34,254.87 682.78,228.7 667.21,214.51 Z M604.64,201.43 L604.71,201.43 C617.38,201.43 635.81,205.99 652.35,218.35 L641.43,239.24 L567.63,239.24 L556.93,218.4 C573.49,206 592,201.43 604.64,201.43 Z M545.65,334.77 C542.12,313.39 527.11,300.48 520.48,295.72 C521.29,261.14 534.75,239.52 547.28,226.83 L557.6,246.93 L557.65,273.15 C557.6448,282.683193 559.914304,292.08004 564.27,300.56 C567.824721,307.643965 573.02275,313.774622 579.43,318.44 L579.43,361.54 C568.77,359.48 548.7,353.22 545.65,334.77 Z M575.3,294.77 C571.858657,288.054866 570.069114,280.615578 570.08,273.07 L570.079951,251.64 L639,251.64 L638.81,272.13 C638.761448,279.909114 636.94573,287.575481 633.5,294.55 C629.09,303.34 620.5,313.83 604.44,313.83 C587.69,313.87 579,301.93 575.3,294.77 Z M663.69,334.77 C662,345.01 654.98,352.77 642.82,357.77 C638.655184,359.452348 634.334139,360.718515 629.92,361.55 L629.92,318.12 C636.158361,313.363256 641.179183,307.194329 644.57,300.12 C648.863933,291.449073 651.131029,281.915644 651.2,272.24 L651.43,246.92 L662,226.77 C674.55,239.45 688.06,261.08 688.87,295.77 C682.23,300.46 667.22,313.37 663.69,334.77 Z" id="Shape"></path>
</svg>
<p style="max-width:260px;text-align:center;margin-top:32px;">
Enter your WiFi credentials and MQTT Broker to connect you Sphinx Key
</p>
<input id="ssid" placeholder="WiFi SSID" />
<input id="pass" placeholder="Password" />
<input id="broker" placeholder="Broker" />
<button id="button" type="submit">
<svg id="loading" height="16" width="16" viewbox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path fill="white" stroke="grey" d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" />
</svg>
<span style="margin:0 8px;">OK</span>
</button>
<p id="msg" style="max-width:260px;text-align:center;margin-top:32px;"></p>
</body>
<script>
function get(id){
return document.getElementById(id)
}
const params = getParams()
const button = get('button')
button.disabled = true
const ssid = get('ssid')
const pass = get('pass')
const broker = get('broker')
const loading = get('loading')
const msg = get('msg')
if(params['ssid']) {
ssid.value = params.ssid
}
if(params['pass']) {
pass.value = params.pass
}
if(params['broker']) {
broker.value = params.broker
}
function checker() {
if(ssid.value && broker.value && button.disabled) {
button.disabled = false
} else if(!ssid.value || !broker.value) {
button.disabled = true
}
}
ssid.onchange = function(){
checker()
}
ssid.oninput = function(){
checker()
}
broker.onchange = function(){
checker()
}
broker.oninput = function(){
checker()
}
button.onclick = function(e) {
fetchWithTimeout('/config?config=' +
encodeURIComponent(JSON.stringify({
ssid: ssid.value,
pass: pass.value || '',
broker: broker.value,
})), {
method:'POST',
timeout:3000,
})
.then(r=> r.json())
.then(finish)
.catch(e=> {
console.log(e)
msg.innerHTML = 'Failed to post configuration to the Sphinx Key'
})
}
async function finish(r) {
console.log("done!", r)
loading.style.display = 'inline-block'
button.disabled = true
await sleep(2000)
loading.style.display = 'none'
button.disabled = false
msg.innerHTML = 'Please unplug your Sphinx Key and plug it back in'
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
function getParams() {
const ps = new URLSearchParams(window.location.search);
const r = {};
for (const [k, v] of ps) {
r[k] = v;
}
return r;
}
async function fetchWithTimeout(resource, options = {}) {
const { timeout = 5000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
</script>
</html>
"#;

View File

@@ -0,0 +1,33 @@
use crate::conn::{Params, Config};
use crate::conn::html;
use url;
use embedded_svc::httpd::*;
use esp_idf_svc::httpd as idf;
use std::sync::{Condvar, Mutex, Arc};
use embedded_svc::httpd::registry::Registry;
use esp_idf_sys::{self};
#[allow(unused_variables)]
pub fn config_server(mutex: Arc<(Mutex<Option<Config>>, Condvar)>) -> Result<idf::Server> {
let server = idf::ServerRegistry::new()
.at("/")
.get(|_| Ok(html::HTML.into()))?
.at("/config")
.post(move |mut request| {
let bod = &request.query_string()
.ok_or(anyhow::anyhow!("failed to parse query string"))?;
println!("bod {:?}", bod);
let params = serde_urlencoded::from_str::<Params>(bod)?;
let conf = serde_json::from_str::<Config>(&params.config)?;
let mut wait = mutex.0.lock().unwrap();
*wait = Some(conf);
mutex.1.notify_one();
Ok("{\"success\":true}".to_owned().into())
})?;
server.start(&Default::default())
}

View File

@@ -1,22 +1,14 @@
mod html;
pub mod wifi;
pub mod http;
mod html;
use url;
use embedded_svc::httpd::*;
use esp_idf_svc::httpd as idf;
use std::sync::{Condvar, Mutex, Arc};
use embedded_svc::httpd::registry::Registry;
use esp_idf_sys::{self};
use esp_idf_svc::nvs::*;
use esp_idf_svc::nvs_storage::EspNvsStorage;
use embedded_svc::storage::Storage;
use serde::Deserialize;
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Deserialize)]
pub struct Params {
pub config: String
}
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config {
pub broker: String,
pub ssid: String,
@@ -24,35 +16,11 @@ pub struct Config {
}
/*
curl -X POST 192.168.71.1/config?broker=52.91.253.115%3A1883&ssid=apples%26acorns&pass=42flutes
curl -X POST 192.168.71.1/config?ssid=apples%26acorns&pass=42flutes&broker=52.91.253.115%3A1883
curl -X POST 192.168.71.1/config?broker=52.91.253.115%3A1883
52.91.253.115:1883
curl -X POST 192.168.71.1/config?config=%7B%22ssid%22%3A%22apples%26acorns%22%2C%22pass%22%3A%2242flutes%22%2C%22broker%22%3A%2252.91.253.115%3A1883%22%7D
*/
#[allow(unused_variables)]
pub fn config_server(mutex: Arc<(Mutex<Option<Config>>, Condvar)>, store: Arc<Mutex<EspNvsStorage>>) -> Result<idf::Server> {
arp -a
let server = idf::ServerRegistry::new()
.at("/")
.get(|_| Ok(html::HTML.into()))?
.at("/config")
.post(move |mut request| {
let bod = &request.query_string()
.ok_or(anyhow::anyhow!("failed to parse query string"))?;
println!("bod {:?}", bod);
let params = serde_urlencoded::from_str::<Params>(bod)?;
let conf = serde_json::from_str::<Config>(&params.config)?;
let mut wait = mutex.0.lock().unwrap();
*wait = Some(conf);
mutex.1.notify_one();
Ok("{\"success\":true}".to_owned().into())
})?;
server.start(&Default::default())
}
http://192.168.71.1/?broker=52.91.253.115%3A1883
*/

View File

@@ -1,3 +1,5 @@
use crate::conn::Config;
use esp_idf_svc::wifi::*;
use esp_idf_svc::sysloop::*;
use esp_idf_svc::netif::*;
@@ -13,102 +15,112 @@ use log::*;
use anyhow::bail;
use std::time::Duration;
use std::sync::Arc;
use std::thread;
const SSID: &str = "apples&acorns";
const PASS: &str = "42flutes";
#[cfg(not(feature = "qemu"))]
#[allow(dead_code)]
pub fn connect(
pub fn start_client(
netif_stack: Arc<EspNetifStack>,
sys_loop_stack: Arc<EspSysLoopStack>,
default_nvs: Arc<EspDefaultNvs>,
config: &Config,
) -> Result<Box<EspWifi>> {
let mut wifi = Box::new(EspWifi::new(netif_stack, sys_loop_stack, default_nvs)?);
let ap_infos = wifi.scan()?;
let ssid = config.ssid.as_str();
let pass = config.pass.as_str();
let ours = ap_infos.into_iter().find(|a| a.ssid == ssid);
let channel = if let Some(ours) = ours {
info!("Found configured access point {} on channel {}", ssid, ours.channel);
Some(ours.channel)
} else {
info!("Configured access point {} not found during scanning, will go with unknown channel", ssid);
None
};
wifi.set_configuration(&Configuration::Client(
ClientConfiguration {
ssid: ssid.into(),
password: pass.into(),
channel,
..Default::default()
}
))?;
// not working
info!("Wifi client configuration set, about to get status");
match wifi.wait_status_with_timeout(Duration::from_secs(20), |status| !status.is_transitional()) {
Ok(_) => (),
Err(e) => warn!("Unexpected Wifi status: {:?}", e),
};
let status = wifi.get_status();
println!("=> wifi STATUS 1 {:?}", status);
info!("...Wifi client configuration set, AGAIN get status");
match wifi.wait_status_with_timeout(Duration::from_secs(20), |status| !status.is_transitional()) {
Ok(_) => (),
Err(e) => warn!("Unexpected Wifi status: {:?}", e),
};
let status = wifi.get_status();
println!("=> wifi STATUS {:?}", status);
println!("=> is transitional? {:?}", status.is_transitional());
if let Status(
ClientStatus::Started(ClientConnectionStatus::Connected(ClientIpStatus::Done(ip_settings))),
ApStatus::Stopped,
) = status {
info!("Wifi started!");
ping(&ip_settings)?;
} else {
thread::sleep(Duration::from_secs(13));
// bail!("Unexpected Client Wifi status: {:?}", status);
return Err(anyhow::anyhow!("Unexpected Client Wifi status: {:?}", status));
}
info!("wifi::start_client Ok(())");
Ok(wifi)
}
#[allow(dead_code)]
pub fn start_server(
netif_stack: Arc<EspNetifStack>,
sys_loop_stack: Arc<EspSysLoopStack>,
default_nvs: Arc<EspDefaultNvs>,
) -> Result<Box<EspWifi>> {
let mut wifi = Box::new(EspWifi::new(netif_stack, sys_loop_stack, default_nvs)?);
info!("Wifi created, about to scan");
let ap_infos = wifi.scan()?;
let ours = ap_infos.into_iter().find(|a| a.ssid == SSID);
let channel = if let Some(ours) = ours {
info!(
"Found configured access point {} on channel {}",
SSID, ours.channel
);
Some(ours.channel)
} else {
info!(
"Configured access point {} not found during scanning, will go with unknown channel",
SSID
);
None
};
// let conf = Configuration::Client(
// ClientConfiguration {
// ssid: SSID.into(),
// password: PASS.into(),
// channel,
// ..Default::default()
// };
// );
// let conf = Configuration::AccessPoint(
// AccessPointConfiguration {
// ssid: "aptest111".into(),
// channel: channel.unwrap_or(1),
// ..Default::default()
// },
// );
let conf = Configuration::Mixed(
ClientConfiguration {
ssid: SSID.into(),
password: PASS.into(),
channel,
..Default::default()
},
wifi.set_configuration(&Configuration::AccessPoint(
AccessPointConfiguration {
ssid: "aptest123".into(),
channel: channel.unwrap_or(1),
ssid: "sphinxkey".into(),
channel: 6,
..Default::default()
},
);
wifi.set_configuration(&conf)?;
))?;
info!("Wifi configuration set, about to get status");
wifi.wait_status_with_timeout(Duration::from_secs(20), |status| !status.is_transitional())
.map_err(|e| anyhow::anyhow!("Unexpected Wifi status: {:?}", e))?;
let status = wifi.get_status();
if let Status(
ClientStatus::Started(ClientConnectionStatus::Connected(ClientIpStatus::Done(ip_settings))),
ClientStatus::Stopped,
ApStatus::Started(ApIpStatus::Done),
) = status
{
info!("Wifi connected");
ping(&ip_settings)?;
) = status {
info!("Wifi started!");
} else {
bail!("Unexpected Wifi status: {:?}", status);
return Err(anyhow::anyhow!("Unexpected AP Wifi status: {:?}", status));
}
Ok(wifi)
}
fn ping(ip_settings: &ipv4::ClientSettings) -> Result<()> {
info!("About to do some pings for {:?}", ip_settings);
let ping_summary =
ping::EspPing::default().ping(ip_settings.subnet.gateway, &Default::default())?;
if ping_summary.transmitted != ping_summary.received {
bail!(
"Pinging gateway {} resulted in timeouts",
ip_settings.subnet.gateway
);
return Err(anyhow::anyhow!("Pinging gateway {} resulted in timeouts", ip_settings.subnet.gateway));
}
info!("Pinging done");

View File

@@ -7,7 +7,7 @@ use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, alway
use std::thread;
use log::*;
use std::sync::{Condvar, Mutex, Arc};
use std::sync::{Condvar, Mutex, Arc, atomic::*};
use std::time::*;
use esp_idf_svc::nvs::*;
@@ -19,7 +19,7 @@ use esp_idf_svc::wifi::*;
use embedded_svc::httpd::*;
use embedded_svc::wifi::*;
use embedded_svc::storage::Storage;
// use log::*;
// use url;
@@ -32,33 +32,52 @@ fn main() -> Result<()> {
sphinx_key_signer::say_hi();
// let init_conf = Some(conn::Config{
// broker: "52.91.253.115:1883".to_string(),
// });
let mutex = Arc::new((Mutex::new(None), Condvar::new()));
thread::sleep(Duration::from_secs(1));
let default_nvs = Arc::new(EspDefaultNvs::new()?);
// let storage = Arc::new(Mutex::new(EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("NVS FAIL")));
let mut store = EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("no storage");
// uncomment to clear:
// store.remove("config").expect("couldnt remove config 1");
let existing: Option<conn::Config> = store.get("config").expect("failed");
if let Some(exist) = existing {
println!("=============> START CLIENT NOW <============== {:?}", exist);
// store.remove("config").expect("couldnt remove config");
if let Err(e) = start_client(default_nvs.clone(), &exist) {
error!("CLIENT ERROR {:?}", e);
}
} else {
println!("=============> START SERVER NOW AND WAIT <==============");
if let Ok((mut wifi, config)) = start_server_and_wait(default_nvs.clone()) {
store.put("config", &config).expect("could not store config");
println!("CONFIG SAVED");
drop(wifi);
thread::sleep(Duration::from_secs(1));
}
}
Ok(())
}
fn start_server_and_wait(default_nvs: Arc<EspDefaultNvs>) -> Result<(Box<EspWifi>, conn::Config)> {
let netif_stack = Arc::new(EspNetifStack::new()?);
let sys_loop_stack = Arc::new(EspSysLoopStack::new()?);
let default_nvs = Arc::new(EspDefaultNvs::new()?);
let storage = Arc::new(Mutex::new(EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("NVS FAIL")));
let mutex = Arc::new((Mutex::new(None), Condvar::new()));
#[allow(clippy::redundant_clone)]
#[allow(unused_mut)]
let mut wifi = conn::wifi::connect(
let mut wifi = conn::wifi::start_server(
netif_stack.clone(),
sys_loop_stack.clone(),
default_nvs.clone(),
)?;
// conn::tcp::tcp_bind().expect("failed TCP bind");
let httpd = conn::config_server(mutex.clone(), storage);
let httpd = conn::http::config_server(mutex.clone());
info!("=====> yo yo");
let mut wait = mutex.0.lock().unwrap();
let config = loop {
let config: &conn::Config = loop {
if let Some(conf) = &*wait {
break conf;
} else {
@@ -67,25 +86,34 @@ fn main() -> Result<()> {
.wait_timeout(wait, Duration::from_secs(1))
.unwrap()
.0;
println!("tick...");
}
};
drop(httpd);
// drop(wifi);
// thread::sleep(Duration::from_secs(1));
println!("===> config! {:?}", config);
Ok((wifi, config.clone()))
}
fn start_client(default_nvs: Arc<EspDefaultNvs>, config: &conn::Config) -> Result<()> {
let netif_stack = Arc::new(EspNetifStack::new()?);
let sys_loop_stack = Arc::new(EspSysLoopStack::new()?);
let wifi = conn::wifi::start_client(
netif_stack.clone(),
sys_loop_stack.clone(),
default_nvs.clone(),
config
)?;
println!("CLIENT CONNECTED!!!!!! {:?}", wifi.get_status());
let mut i = 0;
loop {
thread::sleep(Duration::from_secs(5));
i = i + 1;
println!("wait forever... {}", i);
}
// drop(httpd);
// println!("Httpd stopped");
/* shutdown */
// drop(httpd);
// info!("Httpd stopped");
}
Ok(())
}
}