diff --git a/mongoose.c b/mongoose.c index 699625e9..a7294472 100644 --- a/mongoose.c +++ b/mongoose.c @@ -19300,6 +19300,41 @@ uint64_t mg_millis(void) { } #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/wifi_dummy.c" +#endif + + +#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) + +bool mg_wifi_scan(void) { + MG_ERROR(("No Wi-Fi driver enabled")); + return false; +} + +bool mg_wifi_connect(char *ssid, char *pass) { + (void) ssid; + (void) pass; + return mg_wifi_scan(); +} + +bool mg_wifi_disconnect(void) { + return mg_wifi_scan(); +} + +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) { + (void) ssid; + (void) pass; + (void) channel; + return mg_wifi_scan(); +} + +bool mg_wifi_ap_stop(void) { + return mg_wifi_scan(); +} + +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/ws.c" #endif @@ -20080,17 +20115,18 @@ static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) { s_ifp = ifp; if (cyw43_arch_init() != 0) return false; // initialize async_context and WiFi chip - if (d->apmode && d->apssid != NULL){ + if (d->apmode && d->apssid != NULL) { MG_DEBUG(("Starting AP '%s' (%u)", d->apssid, d->apchannel)); - cyw43_wifi_ap_set_channel(&cyw43_state, d->apchannel); - cyw43_arch_enable_ap_mode(d->apssid, d->appass, CYW43_AUTH_WPA2_AES_PSK); - cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); + if (!mg_wifi_ap_start(d->apssid, d->appass, d->apchannel)) return false; + cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); // same MAC } else { cyw43_arch_enable_sta_mode(); - cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, ifp->mac); - if (d->ssid != NULL && d->pass != NULL) { + cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); + if (d->ssid != NULL) { MG_DEBUG(("Connecting to '%s'", d->ssid)); - return (cyw43_arch_wifi_connect_bssid_async(d->ssid, NULL, d->pass, CYW43_AUTH_WPA2_AES_PSK) == 0); + return mg_wifi_connect(d->ssid, d->pass); + } else { + cyw43_arch_disable_sta_mode(); } } return true; @@ -20098,18 +20134,36 @@ static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) { static size_t mg_tcpip_driver_pico_w_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { - (void) ifp; - return cyw43_send_ethernet(&cyw43_state, CYW43_ITF_STA, len, buf, false) - ? 0 - : len; + struct mg_tcpip_driver_pico_w_data *d = + (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + return cyw43_send_ethernet(&cyw43_state, + d->apmode ? CYW43_ITF_AP : CYW43_ITF_STA, len, buf, + false) == 0 + ? len + : 0; } +static bool s_aplink = false, s_scanning = false; +static bool s_stalink = false, s_connecting = false; + static bool mg_tcpip_driver_pico_w_poll(struct mg_tcpip_if *ifp, bool s1) { - (void) ifp; - cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA) - return s1 ? (cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) == - CYW43_LINK_JOIN) - : false; + cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA) + if (s_scanning && !cyw43_wifi_scan_active(&cyw43_state)) { + MG_VERBOSE(("scan complete")); + s_scanning = 0; + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL); + } + if (!s1) return false; + struct mg_tcpip_driver_pico_w_data *d = + (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + if (d->apmode) return s_aplink; + int sdkstate = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA); + MG_VERBOSE(("conn: %c state: %d", s_connecting ? '1' : '0', sdkstate)); + if (sdkstate < 0 && s_connecting) { + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &sdkstate); + s_connecting = false; + } + return s_stalink; } struct mg_tcpip_driver mg_tcpip_driver_pico_w = { @@ -20122,15 +20176,26 @@ struct mg_tcpip_driver mg_tcpip_driver_pico_w = { // Called once per outstanding frame by async_context void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) { - if (itf != CYW43_ITF_STA) return; mg_tcpip_qwrite((void *) buf, len, s_ifp); (void) cb_data; } // Called by async_context void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) { + if (itf == CYW43_ITF_AP) { + s_aplink = true; + } else { + s_stalink = true; + s_connecting = false; + } } void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) { + if (itf == CYW43_ITF_AP) { + s_aplink = false; + } else { + s_stalink = false; + // SDK calls this before we check status, don't clear s_connecting here + } } // there's life beyond lwIP @@ -20138,6 +20203,59 @@ void pbuf_copy_partial(void) { (void) 0; } +static int result_cb(void *arg, const cyw43_ev_scan_result_t *data) { + struct mg_wifi_scan_bss_data bss; + bss.SSID = mg_str_n(data->ssid, data->ssid_len); + bss.BSSID = (char *) data->bssid; + bss.RSSI = (int8_t) data->rssi; + bss.has_n = 0; // SDK ignores this + bss.channel = (uint8_t) data->channel; + bss.band = MG_WIFI_BAND_2G; + // SDK-internal dependency, 2.1.0 + bss.security = data->auth_mode & MG_BIT(0) ? MG_WIFI_SECURITY_WEP + : MG_WIFI_SECURITY_OPEN; + if (data->auth_mode & MG_BIT(1)) bss.security |= MG_WIFI_SECURITY_WPA; + if (data->auth_mode & MG_BIT(2)) bss.security |= MG_WIFI_SECURITY_WPA2; + MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, + bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, + bss.security)); + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss); + return 0; +} + +bool mg_wifi_scan(void) { + cyw43_wifi_scan_options_t opts; + memset(&opts, 0, sizeof(opts)); + bool res = (cyw43_wifi_scan(&cyw43_state, &opts, NULL, result_cb) == 0); + if (res) s_scanning = true; + return res; +} + +bool mg_wifi_connect(char *ssid, char *pass) { + cyw43_arch_enable_sta_mode(); + int res = cyw43_arch_wifi_connect_async(ssid, pass, CYW43_AUTH_WPA2_AES_PSK); + MG_VERBOSE(("res: %d", res)); + if (res == 0) s_connecting = true; + return (res == 0); +} + +bool mg_wifi_disconnect(void) { + cyw43_arch_disable_sta_mode(); + s_connecting = false; + return true; +} + +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) { + cyw43_wifi_ap_set_channel(&cyw43_state, channel); + cyw43_arch_enable_ap_mode(ssid, pass, CYW43_AUTH_WPA2_AES_PSK); + return true; +} + +bool mg_wifi_ap_stop(void) { + cyw43_arch_disable_ap_mode(); + return true; +} + #endif #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index b9d17f08..53d918ea 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2734,6 +2734,31 @@ bool mg_ota_flash_end(struct mg_flash *flash); #endif +struct mg_wifi_scan_bss_data { + struct mg_str SSID; + char *BSSID; + int16_t RSSI; + uint8_t security; +#define MG_WIFI_SECURITY_OPEN 0 +#define MG_WIFI_SECURITY_WEP MG_BIT(0) +#define MG_WIFI_SECURITY_WPA MG_BIT(1) +#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) +#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) + uint8_t channel; + unsigned band :2; +#define MG_WIFI_BAND_2G 0 +#define MG_WIFI_BAND_5G 1 + unsigned has_n :1; +}; + + +bool mg_wifi_scan(void); +bool mg_wifi_connect(char *ssid, char *pass); +bool mg_wifi_disconnect(void); +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel); +bool mg_wifi_ap_stop(void); + + @@ -2759,27 +2784,10 @@ enum { MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct mg_wifi_scan_bss_data * MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL - MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed chip specific + MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific MG_TCPIP_EV_USER // Starting ID for user events }; -struct mg_wifi_scan_bss_data { - struct mg_str SSID; - char *BSSID; - int16_t RSSI; - uint8_t security; -#define MG_WIFI_SECURITY_OPEN 0 -#define MG_WIFI_SECURITY_WEP MG_BIT(0) -#define MG_WIFI_SECURITY_WPA MG_BIT(1) -#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) -#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) - uint8_t channel; - unsigned band :2; -#define MG_WIFI_BAND_2G 0 -#define MG_WIFI_BAND_5G 1 - unsigned has_n :1; -}; - // Network interface struct mg_tcpip_if { diff --git a/src/drivers/pico-w.c b/src/drivers/pico-w.c index 2a340d21..aa94621a 100644 --- a/src/drivers/pico-w.c +++ b/src/drivers/pico-w.c @@ -13,17 +13,18 @@ static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) { s_ifp = ifp; if (cyw43_arch_init() != 0) return false; // initialize async_context and WiFi chip - if (d->apmode && d->apssid != NULL){ + if (d->apmode && d->apssid != NULL) { MG_DEBUG(("Starting AP '%s' (%u)", d->apssid, d->apchannel)); - cyw43_wifi_ap_set_channel(&cyw43_state, d->apchannel); - cyw43_arch_enable_ap_mode(d->apssid, d->appass, CYW43_AUTH_WPA2_AES_PSK); - cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); + if (!mg_wifi_ap_start(d->apssid, d->appass, d->apchannel)) return false; + cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); // same MAC } else { cyw43_arch_enable_sta_mode(); - cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, ifp->mac); - if (d->ssid != NULL && d->pass != NULL) { + cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); + if (d->ssid != NULL) { MG_DEBUG(("Connecting to '%s'", d->ssid)); - return (cyw43_arch_wifi_connect_bssid_async(d->ssid, NULL, d->pass, CYW43_AUTH_WPA2_AES_PSK) == 0); + return mg_wifi_connect(d->ssid, d->pass); + } else { + cyw43_arch_disable_sta_mode(); } } return true; @@ -31,18 +32,36 @@ static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) { static size_t mg_tcpip_driver_pico_w_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { - (void) ifp; - return cyw43_send_ethernet(&cyw43_state, CYW43_ITF_STA, len, buf, false) - ? 0 - : len; + struct mg_tcpip_driver_pico_w_data *d = + (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + return cyw43_send_ethernet(&cyw43_state, + d->apmode ? CYW43_ITF_AP : CYW43_ITF_STA, len, buf, + false) == 0 + ? len + : 0; } +static bool s_aplink = false, s_scanning = false; +static bool s_stalink = false, s_connecting = false; + static bool mg_tcpip_driver_pico_w_poll(struct mg_tcpip_if *ifp, bool s1) { - (void) ifp; - cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA) - return s1 ? (cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) == - CYW43_LINK_JOIN) - : false; + cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA) + if (s_scanning && !cyw43_wifi_scan_active(&cyw43_state)) { + MG_VERBOSE(("scan complete")); + s_scanning = 0; + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL); + } + if (!s1) return false; + struct mg_tcpip_driver_pico_w_data *d = + (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + if (d->apmode) return s_aplink; + int sdkstate = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA); + MG_VERBOSE(("conn: %c state: %d", s_connecting ? '1' : '0', sdkstate)); + if (sdkstate < 0 && s_connecting) { + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &sdkstate); + s_connecting = false; + } + return s_stalink; } struct mg_tcpip_driver mg_tcpip_driver_pico_w = { @@ -55,15 +74,26 @@ struct mg_tcpip_driver mg_tcpip_driver_pico_w = { // Called once per outstanding frame by async_context void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) { - if (itf != CYW43_ITF_STA) return; mg_tcpip_qwrite((void *) buf, len, s_ifp); (void) cb_data; } // Called by async_context void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) { + if (itf == CYW43_ITF_AP) { + s_aplink = true; + } else { + s_stalink = true; + s_connecting = false; + } } void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) { + if (itf == CYW43_ITF_AP) { + s_aplink = false; + } else { + s_stalink = false; + // SDK calls this before we check status, don't clear s_connecting here + } } // there's life beyond lwIP @@ -71,4 +101,57 @@ void pbuf_copy_partial(void) { (void) 0; } +static int result_cb(void *arg, const cyw43_ev_scan_result_t *data) { + struct mg_wifi_scan_bss_data bss; + bss.SSID = mg_str_n(data->ssid, data->ssid_len); + bss.BSSID = (char *) data->bssid; + bss.RSSI = (int8_t) data->rssi; + bss.has_n = 0; // SDK ignores this + bss.channel = (uint8_t) data->channel; + bss.band = MG_WIFI_BAND_2G; + // SDK-internal dependency, 2.1.0 + bss.security = data->auth_mode & MG_BIT(0) ? MG_WIFI_SECURITY_WEP + : MG_WIFI_SECURITY_OPEN; + if (data->auth_mode & MG_BIT(1)) bss.security |= MG_WIFI_SECURITY_WPA; + if (data->auth_mode & MG_BIT(2)) bss.security |= MG_WIFI_SECURITY_WPA2; + MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, + bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, + bss.security)); + mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss); + return 0; +} + +bool mg_wifi_scan(void) { + cyw43_wifi_scan_options_t opts; + memset(&opts, 0, sizeof(opts)); + bool res = (cyw43_wifi_scan(&cyw43_state, &opts, NULL, result_cb) == 0); + if (res) s_scanning = true; + return res; +} + +bool mg_wifi_connect(char *ssid, char *pass) { + cyw43_arch_enable_sta_mode(); + int res = cyw43_arch_wifi_connect_async(ssid, pass, CYW43_AUTH_WPA2_AES_PSK); + MG_VERBOSE(("res: %d", res)); + if (res == 0) s_connecting = true; + return (res == 0); +} + +bool mg_wifi_disconnect(void) { + cyw43_arch_disable_sta_mode(); + s_connecting = false; + return true; +} + +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) { + cyw43_wifi_ap_set_channel(&cyw43_state, channel); + cyw43_arch_enable_ap_mode(ssid, pass, CYW43_AUTH_WPA2_AES_PSK); + return true; +} + +bool mg_wifi_ap_stop(void) { + cyw43_arch_disable_ap_mode(); + return true; +} + #endif diff --git a/src/net_builtin.h b/src/net_builtin.h index 9c0504a2..b077c342 100644 --- a/src/net_builtin.h +++ b/src/net_builtin.h @@ -25,27 +25,10 @@ enum { MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi scan results struct mg_wifi_scan_bss_data * MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan has finished NULL - MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed chip specific + MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connect has failed driver and chip specific MG_TCPIP_EV_USER // Starting ID for user events }; -struct mg_wifi_scan_bss_data { - struct mg_str SSID; - char *BSSID; - int16_t RSSI; - uint8_t security; -#define MG_WIFI_SECURITY_OPEN 0 -#define MG_WIFI_SECURITY_WEP MG_BIT(0) -#define MG_WIFI_SECURITY_WPA MG_BIT(1) -#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) -#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) - uint8_t channel; - unsigned band :2; -#define MG_WIFI_BAND_2G 0 -#define MG_WIFI_BAND_5G 1 - unsigned has_n :1; -}; - // Network interface struct mg_tcpip_if { diff --git a/src/wifi.h b/src/wifi.h new file mode 100644 index 00000000..c63da152 --- /dev/null +++ b/src/wifi.h @@ -0,0 +1,29 @@ +#pragma once + +#include "arch.h" +#include "log.h" +#include "str.h" + +struct mg_wifi_scan_bss_data { + struct mg_str SSID; + char *BSSID; + int16_t RSSI; + uint8_t security; +#define MG_WIFI_SECURITY_OPEN 0 +#define MG_WIFI_SECURITY_WEP MG_BIT(0) +#define MG_WIFI_SECURITY_WPA MG_BIT(1) +#define MG_WIFI_SECURITY_WPA2 MG_BIT(2) +#define MG_WIFI_SECURITY_WPA3 MG_BIT(3) + uint8_t channel; + unsigned band :2; +#define MG_WIFI_BAND_2G 0 +#define MG_WIFI_BAND_5G 1 + unsigned has_n :1; +}; + + +bool mg_wifi_scan(void); +bool mg_wifi_connect(char *ssid, char *pass); +bool mg_wifi_disconnect(void); +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel); +bool mg_wifi_ap_stop(void); diff --git a/src/wifi_dummy.c b/src/wifi_dummy.c new file mode 100644 index 00000000..8f0bda8f --- /dev/null +++ b/src/wifi_dummy.c @@ -0,0 +1,31 @@ +#include "wifi.h" + +#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) + +bool mg_wifi_scan(void) { + MG_ERROR(("No Wi-Fi driver enabled")); + return false; +} + +bool mg_wifi_connect(char *ssid, char *pass) { + (void) ssid; + (void) pass; + return mg_wifi_scan(); +} + +bool mg_wifi_disconnect(void) { + return mg_wifi_scan(); +} + +bool mg_wifi_ap_start(char *ssid, char *pass, unsigned int channel) { + (void) ssid; + (void) pass; + (void) channel; + return mg_wifi_scan(); +} + +bool mg_wifi_ap_stop(void) { + return mg_wifi_scan(); +} + +#endif diff --git a/test/Makefile b/test/Makefile index d5253585..5047f307 100644 --- a/test/Makefile +++ b/test/Makefile @@ -206,7 +206,7 @@ mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c) cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ mongoose.h: $(HDRS) Makefile - cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/flash.h src/net_builtin.h src/profile.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ + cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/flash.h src/wifi.h src/net_builtin.h src/profile.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ clean: clean_tutorials clean_tutorials_embedded