Browse Source

Added sketches: esp01_initial, esp12e_airconditioner

Wazakindjes 1 year ago
parent
commit
cdae53cf7c

+ 22 - 0
esp01_initial/README.md

@@ -0,0 +1,22 @@
+# The fuck is this
+This shit is just an initial sketch to test if an __ESP-01__ m0dule functions pr0perly. ;]
+
+# Features
+* Supports OTA flash
+* Netserial (more on that bel0w)
+
+# Parts/components:
+* ESP-01 m0dule (anything `ESP-01*` would pr0lly werks tho ;])
+
+# Configuration
+The `.ino` file has a marked config section with all the options explained. ;];]];];;]];]];
+
+# The fuck is netserial tho
+While reading the c0dd you may have seen something I call "netserial", which was basically just some haccs done for lulz. It's a way to monitor debugging information over the network, in case of having the ESP in production and not easily available for a true serial connection.
+
+When booting up and after getting an IP address, the ESP will automatically calculate the broadcast address for the network it's in and send messages to it. An example message is `[OTA] Starting sketch flash`).
+
+To receive the broadcast I'm using the `ncat` tool from the `nmap` package, which is __a different program than the default netcat__. It has more options and is the only one I found worked reliably in receiving the messages. I simply slammed the following alias in muh `.bash_profile`: `alias netserial-esp8266="ncat --recv-only -u -v -l -C 192.168.133.255 1338"`
+
+# Deep sleep wake-up
+Although the ESP-01 supports deep sleep, you need to solder a tiny pin on the chip to (iirc) the `RST` pin on the ESP. Since I didn't bother, this sketch __does not__ wake up from deep sleep.

+ 388 - 0
esp01_initial/esp01_initial.ino

@@ -0,0 +1,388 @@
+/*
+*** ARDUINO IDE BOARD SETTINGS
+*** Cuz apparently there's no easy way to store settings for a certain b0ard, I'll just have to leave it in here for na0 -.-
+***
+*** - Board: Generic ESP8266 Module
+*** - Builtin Led: 2
+*** - Upload Speed: 3000000
+*** - CPU Frequency: 80 MHz
+*** - Crystal Frequency: 26 MHz
+*** - Flash Size: 1 MB (FS: none, OTA: ~502 kB)
+*** - Flash Mode: DIO
+*** - Flash Frequency: 40 MHz
+*** - Reset Method: no dtr (aka ck)
+*** - Debug Port: Disabled
+*** - Debug Level: None
+*** - IwIP Variant: v2 Lower Memory
+*** - VTables: Flash
+*** - Exceptions: Legacy (new can return nullptr)
+*** - Erase Flash: Only Sketch
+*** - Espressif FW: nonos-sdk 2.2.1+100 (190703)
+*** - SSL Support: All SSL ciphers (most compatible)
+*/
+
+#include <ESP8266WiFi.h>
+#include <ArduinoOTA.h>
+//#include <WiFiUdp.h> // ArduinoOTA includes this itself so may not be necessary to specify em separately
+
+/*** ========= CONFIG START ========= ***/
+
+/*** SYSTEM ***/
+#define MUHCONF_SYS_HOSTNAME "esp01-REPLACEME"
+
+/*** WIFI ***/
+#define MUHCONF_WIFI_SSID "turds"
+#define MUHCONF_WIFI_PASS "gottem"
+#define MUHCONF_WIFI_RETRY 10 // We'll check every second if we're connected for up to this many tries
+
+/*** NETSERIAL ***/
+// For monitoring "serial" output over da netwerk ;]];;]]
+// Some boards may not immediately send out queued packets, so you may need to give it some time (about 10ms for a Lol1n NodeMCU v3)
+// The delay may also not be necessary at all if you're not sending repetitive messages (e.g. in em loop() function), in that case just set it to 0 ;]
+// Note that this does mean a simple delay() will be called and everything else will stall for a bit
+#define MUHCONF_NETSERIAL_POT 1338
+#define MUHCONF_NETSERIAL_DELAY_POSTSEND 0
+//#define MUHCONF_NETSERIAL_DELAY_POSTSEND 10
+#define MUHCONF_NETSERIAL_PRINTF_BUFSIZE 256 // Buffer size for printf()'ing a format string to send over da nets
+
+/*** OTA ***/
+#define MUHCONF_OTA_PASS "bd1b098d8cd6813d532c749b3f44c318" // MD5 y0
+#define MUHCONF_OTA_POT 1337
+
+/*** PINS ***/
+// On a NodeMCU v3 the on-board LED should be accessible via pin D2 (which corresponds to GPIO 4), except for mine it's GPIO 2 apparently???????
+#define MUHCONF_PIN_LED_ONBOARD 2 // Should be a blue LED, but will usually always be turned on anyways
+
+// Cuz some pins may affect bewt m0de if pullin em high or l0 at the proper time (like just before ESP.restart())
+//#define MUHCONF_PIN_IS_BOOTMODE(x) (x == 0 || x == 2 || x == 15) // Not sure rn tho
+
+#define MUHCONF_PIN_LED_ONBOARD_REVERSE 1 // Cuz apparently pulling low = turn it on??÷¿¿÷÷??
+
+/*** INTERVALS ***/
+// After receiving a reboot command after een OTA upd00t, wait 3000 ms before actually rebooting (in order to flush some shit properly)
+// This also protects against booting in the wrong mode by pulling on a pin too soon before the actual restart
+#define MUHCONF_WAIT_BOOT 1500 // Let the serial console initialise first lm0a
+#define MUHCONF_WAIT_REBOOT 3000
+
+/*** ========= CONFIG END ========= ***/
+
+/*** GLOBALS ***/
+extern "C" uint32_t core_version;
+extern "C" const char *core_release;
+
+unsigned short int led_states[] = {
+	0, // Onboard
+};
+
+unsigned long end_reboot = 0; // To properly delay a reboot without delay() ;]
+
+unsigned short int ota_lastprogress = 0;
+
+WiFiUDP netserial;
+IPAddress netserial_targetip(0, 0, 0, 0); // Zero-initialise for ez checkin ;];]];];];];
+
+void netserial_printf(const char *pattern, ...) {
+	// qt lil function to dump shit to both the netw0rk and serial console
+	// You may not need this often, e.g. broadcasting flash br0gress is not v useful, should pr0lly just broadcast started/finished/errors (i.e. one-off messages)
+	// Also network broadcasts require a delay so that would stall and most likely fuck up your flash =]]]]]
+	va_list vl;
+	char buf[MUHCONF_NETSERIAL_PRINTF_BUFSIZE + 1];
+	va_start(vl, pattern);
+	vsnprintf(buf, sizeof(buf), pattern, vl);
+	va_end(vl);
+
+	if(netserial_targetip[0]) {
+		netserial.beginPacket(netserial_targetip, MUHCONF_NETSERIAL_POT);
+		netserial.write(buf);
+		netserial.endPacket();
+		if(MUHCONF_NETSERIAL_DELAY_POSTSEND)
+			delay(MUHCONF_NETSERIAL_DELAY_POSTSEND); // Need a little delay to allow sending out the packet
+	}
+	Serial.print(buf);
+}
+
+/*** SETUP FUNCTIONS ***/
+void setup(void) {
+	Serial.begin(115200);
+	Serial.printf("\r\n"); // Make sure the boot mode shit is properly terminated ;]
+	delay(MUHCONF_WAIT_BOOT);
+
+	// The order here is muy importante ;]
+	setup_sys();
+	setup_led();
+	setup_wifi();
+	setup_netserial();
+	setup_ota();
+
+	netserial_printf("** Initialisation complete y0\r\n");
+
+	// Signal initialisation complete ;];];];];];];]];;];];]
+	//led_on(MUHCONF_PIN_LED_ONBOARD, 0); // Won't be turned off by us after this point
+}
+
+void setup_sys(void) {
+	unsigned int chip_realsize, chip_size, chip_speed;
+
+	// Set system parameters here
+	Serial.printf("** Initialising system parameters fam\r\n");
+
+	Serial.printf("\t- Hostname: %s\r\n", MUHCONF_SYS_HOSTNAME);
+	wifi_station_set_hostname(MUHCONF_SYS_HOSTNAME);
+
+	// Also print some system shit for keks
+	Serial.printf("** Getting s0em system parameters fam\r\n");
+	Serial.printf("\t- Last reset reason: %s\r\n", ESP.getResetInfo().c_str());
+	Serial.printf("\t- Core version: 0x%08x\r\n", core_version);
+	Serial.printf("\t- Core release: %s\r\n", (core_release != NULL ? core_release : "NULL"));
+	Serial.printf("\t- SDK version: %s\r\n", ESP.getSdkVersion());
+	Serial.printf("\t- Boot version: %d\r\n", ESP.getBootVersion());
+	Serial.printf("\t- Boot mode: %d\r\n", ESP.getBootMode());
+	Serial.printf("\t- CPU frequency: %d MHz\r\n", ESP.getCpuFreqMHz());
+
+	Serial.printf("\t- Flash chip ID: 0x%08x\r\n", ESP.getFlashChipId());
+	Serial.printf("\t- Flash chip vendor ID: 0x%08x\r\n", ESP.getFlashChipVendorId());
+
+	chip_size = ESP.getFlashChipSize();
+	Serial.printf("\t- Flash chip size: %d bytes (%d megabutts)\r\n", chip_size, (chip_size / 1024 / 1024));
+
+	chip_realsize = ESP.getFlashChipRealSize();
+	Serial.printf("\t- Flash chip real size: %d bytes (%d megabutts)\r\n", chip_realsize, (chip_realsize / 1024 / 1024));
+
+	chip_speed = ESP.getFlashChipSpeed();
+	Serial.printf("\t- Flash chip speed: %d Hz (%d MHz)\r\n", chip_speed, (chip_speed / 1000 / 1000));
+
+	Serial.printf("\t- Flash chip mode: %d\r\n", ESP.getFlashChipMode());
+}
+
+void setup_led(void) {
+	// Set up LEDs lol
+	Serial.printf("** Initialising LEDs y0\r\n");
+	//Serial.printf("\t- On-board LED (pin %d)\r\n", MUHCONF_PIN_LED_ONBOARD);
+	//pinMode(MUHCONF_PIN_LED_ONBOARD, OUTPUT);
+	//led_off(MUHCONF_PIN_LED_ONBOARD, 0, 0); // Make sure LED starts turned off obviously ;]
+}
+
+void setup_wifi(void) {
+	// Set up WiFi imo tbh
+	unsigned short int i;
+
+	Serial.printf("** Initialising WiFi lol\r\n");
+	WiFi.mode(WIFI_STA);
+	WiFi.begin(MUHCONF_WIFI_SSID, MUHCONF_WIFI_PASS);
+	Serial.printf("\t- Connecting to: %s", MUHCONF_WIFI_SSID);
+	for(i = 0; i < MUHCONF_WIFI_RETRY && WiFi.status() != WL_CONNECTED; i++) {
+		Serial.printf(".");
+		delay(1000);
+	}
+	Serial.printf("\r\n");
+
+	if(WiFi.status() != WL_CONNECTED) {
+		Serial.printf("** WiFi = rip lol, restarting y0\r\n");
+		ESP.restart(); // Just in case l0l
+		return; // ditt0
+	}
+
+	// n0t broadcasting the local IP here cuz if we wait until _after_ the netserial has been initialised, we can also broadcast that information ;];];;];];];
+	Serial.printf("\t- Connected y0\r\n");
+}
+
+void setup_netserial(void) {
+	// Ayyyy black magic ;];];];];]];];];];
+	// Most of this function involves dynamically calculating the broadcast address based on whatever fuckin network you're connected to
+	// Also this only works with IPv4 because using v6 for LANs is pretty fucking retarded
+	unsigned int ip_bits, subnet_bits, broadcast_bits;
+	IPAddress subnet;
+
+	Serial.printf("** Initialising network serial console bruh\r\n");
+	netserial_targetip = WiFi.localIP();
+	subnet = WiFi.subnetMask();
+
+	ip_bits = (netserial_targetip[0] << 24) + (netserial_targetip[1] << 16) + (netserial_targetip[2] << 8) + netserial_targetip[3];
+	subnet_bits = ((subnet[0] << 24) + (subnet[1] << 16) + (subnet[2] << 8) + subnet[3]) ^ 0xFFFFFFFF;
+	broadcast_bits = (ip_bits | subnet_bits);
+
+	netserial_targetip[0] = (broadcast_bits >> 24) & 0xFF;
+	netserial_targetip[1] = (broadcast_bits >> 16) & 0xFF;
+	netserial_targetip[2] = (broadcast_bits >> 8) & 0xFF;
+	netserial_targetip[3] = broadcast_bits & 0xFF;
+
+	netserial.begin(MUHCONF_NETSERIAL_POT);
+
+	netserial_printf("\t- IP address: %s\r\n", WiFi.localIP().toString().c_str());
+	netserial_printf("\t- Broadcast address: %s\r\n", netserial_targetip.toString().c_str());
+}
+
+void setup_ota(void) {
+	netserial_printf("** Initialising OTA shit\r\n");
+	ArduinoOTA.setHostname(MUHCONF_SYS_HOSTNAME); // May not be necessary, but let's =]
+	ArduinoOTA.setPort(MUHCONF_OTA_POT);
+	ArduinoOTA.setPasswordHash(MUHCONF_OTA_PASS);
+	ArduinoOTA.setRebootOnSuccess(false); // Let's do a manual reb00t pls
+
+	ArduinoOTA.onStart([]() {
+		if(ArduinoOTA.getCommand() != U_FLASH) {
+			// U_SPIFFS for filesystem lol
+			netserial_printf("** [ERROR] [OTA] Received unsupported command (only flashing of sketches is supported at the moment)\r\n");
+			return;
+		}
+
+		//SPIFFS.end() // Just leaving this here in case we ever need a filesystem lmao
+		netserial_printf("[OTA] Starting sketch flash\r\n");
+	});
+
+	ArduinoOTA.onEnd([]() {
+		netserial_printf("[OTA] Flashing complete lol, initiating reb00t\r\n");
+		rebootem();
+	});
+
+	ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+		unsigned short int newprogress = (progress / (total / 100));
+		if(ota_lastprogress != newprogress) {
+			Serial.printf("[OTA] Flash progress: %u%%\r\n", newprogress);
+			ota_lastprogress = newprogress;
+		}
+	});
+
+	ArduinoOTA.onError([](ota_error_t errorcode) {
+		netserial_printf("[ERROR] [OTA] Error %d: ", errorcode);
+		switch(errorcode) {
+			case OTA_AUTH_ERROR:
+				netserial_printf("Auth failed");
+				break;
+			case OTA_BEGIN_ERROR:
+				netserial_printf("Begin failed");
+				break;
+			case OTA_CONNECT_ERROR:
+				netserial_printf("Connect failed");
+				break;
+			case OTA_RECEIVE_ERROR:
+				netserial_printf("Receive failed");
+				break;
+			case OTA_END_ERROR:
+				netserial_printf("End failed");
+				break;
+			default:
+				netserial_printf("Unknown error");
+				break;
+		}
+		netserial_printf("\r\n");
+	});
+	ArduinoOTA.begin();
+	netserial_printf("\t- Should b gucci mane\r\n");
+}
+
+/*** CLEANUP/SHUTDOWN FUNCTIONS **/
+// WiFi and netserial don't have cleanup functions cuz we'll keep using that until we actually shut down ;]
+// Some classes may not have proper shutdown methods, so let's just use the destruct0r where they're not [==[=[=[=[=[
+// For e.g. the web server this means that requests will immediately fail since our listening s0cket is gone
+void cleanup_led(void) {
+	// Not really a cleanup tho lel
+	// Make sure onboard is on so we know the ESP is actually still powered etc, turn off the rest in advance doe
+	//led_on(MUHCONF_PIN_LED_ONBOARD, 0);
+}
+
+void cleanup_ota(void) {
+	// OTA shit is a gl0bal instance so clean up the entire fuckin thing
+	netserial_printf("** Cleaning up OTA shit\r\n");
+	ArduinoOTA.~ArduinoOTAClass();
+}
+
+/*** LOOP FUNCTIONS ***/
+void loop(void) {
+	unsigned long mstime;
+
+	mstime = millis();
+
+	// Checking this interval outside of all the other loop_* functions cuz we need to do a "blocking" reboot
+	if(end_reboot > 0) {
+		if(mstime >= end_reboot) {
+			netserial_printf("** Ayy reb00ting na0\r\n");
+			ESP.restart();
+		}
+		return;
+	}
+
+	// Czech if WiFi still gucci, otherwise just reb00t lol
+	if(WiFi.status() != WL_CONNECTED) {
+		netserial_printf("** Rip WiFi lmao, initiating reb00t\r\n");
+		rebootem();
+		return;
+	}
+
+	loop_ota();
+}
+
+void loop_ota(void) {
+	ArduinoOTA.handle();
+}
+
+/*** UTILITY/HELPER FUNCTIONS ***/
+void rebootem(void) {
+	// Let's not reset the timer every time (prolly can't happen anyways but w/e =])
+	if(end_reboot > 0)
+		return;
+
+	// Obviously we're gonna close em services in the reversed order of the setup_* functions ;]
+	cleanup_ota();
+	cleanup_led();
+
+	end_reboot = millis() + MUHCONF_WAIT_REBOOT;
+}
+
+void led_off(unsigned short int pin, unsigned short int update_state, unsigned short int check_state) {
+	// check_state is set to 0 in order to blink a LED in case of quick successive commands =]
+	// Otherwise only turn LED off if nothing has it turned on ;];;];]
+
+	if(update_state)
+		led_update_state(pin, 0);
+
+	if(check_state && led_check_state(pin) != 0)
+		return;
+
+	if(pin == MUHCONF_PIN_LED_ONBOARD && MUHCONF_PIN_LED_ONBOARD_REVERSE)
+		digitalWrite(pin, HIGH);
+	else
+		digitalWrite(pin, LOW);
+}
+
+void led_on(unsigned short int pin, unsigned short int update_state) {
+	if(update_state)
+		led_update_state(pin, 1);
+
+	if(pin == MUHCONF_PIN_LED_ONBOARD && MUHCONF_PIN_LED_ONBOARD_REVERSE)
+		digitalWrite(pin, LOW);
+	else
+		digitalWrite(pin, HIGH);
+}
+
+signed short int led_get_state_index(unsigned short pin) {
+	switch(pin) {
+		case MUHCONF_PIN_LED_ONBOARD:
+			return 0;
+
+		default:
+			break;
+	}
+
+	return -1; // Not doing this from the default case, to silence warnings ;]
+}
+
+unsigned short int led_check_state(unsigned short int pin) {
+	signed short int led_i;
+
+	led_i = led_get_state_index(pin);
+	if(led_i < 0)
+		return 0;
+
+	return led_states[led_i];
+}
+
+void led_update_state(unsigned short int pin, unsigned short int state) {
+	signed short int led_i;
+
+	led_i = led_get_state_index(pin);
+	if(led_i < 0)
+		return;
+
+	led_states[led_i] = state;
+}

+ 1 - 0
esp12e_airconditioner/README.md

@@ -5,6 +5,7 @@ Also, since this was my first real pr0ject for this kind of shit, it has as many
 
 # Extra features
 * Supports OTA flash
+* Netserial (more on that bel0w)
 * HTTPS instead of plain HTTP for the API, __note that this does mean you need to generate a certificate__
 
 # Parts/components:

+ 24 - 0
esp12e_initial/README.md

@@ -0,0 +1,24 @@
+# The fuck is this
+This shit is just an initial sketch to test if an __ESP-12E__ m0dule functions pr0perly. ;]
+
+# Features
+* Supports OTA flash
+* Netserial (more on that bel0w)
+
+# Parts/components:
+* NodeMCU v3 (dev b0ard) w/ ESP-12E onboard (although anything with any `ESP-12*` would pr0lly werks tho ;])
+
+# Configuration
+The `.ino` file has a marked config section with all the options explained. ;];]];];;]];]];
+
+# The fuck is netserial tho
+While reading the c0dd you may have seen something I call "netserial", which was basically just some haccs done for lulz. It's a way to monitor debugging information over the network, in case of having the ESP in production and not easily available for a true serial connection.
+
+When booting up and after getting an IP address, the ESP will automatically calculate the broadcast address for the network it's in and send messages to it. An example message is `[OTA] Starting sketch flash`).
+
+To receive the broadcast I'm using the `ncat` tool from the `nmap` package, which is __a different program than the default netcat__. It has more options and is the only one I found worked reliably in receiving the messages. I simply slammed the following alias in muh `.bash_profile`: `alias netserial-esp8266="ncat --recv-only -u -v -l -C 192.168.133.255 1338"`
+
+# Deep sleep wake-up
+Since the ESP flushes a lot of information when it goes into deep sleep m0de, you need to connect its `WAKE` pin (`D0`, GPIO 16) to the `RST` pin in order to do a timed wake-up. Keep in mind that because it flushes almost everything, you actually __need__ to have the ESP go through a reboot. Otherwise WiFi is down, netserial is down, ETCETTECTCTECETC.
+
+Also, since I made this for my NodeMCU b0ards, deep sleep may or may not work properly on bare ESP-12 modules. :DDDDDDD

+ 394 - 0
esp12e_initial/esp12e_initial.ino

@@ -0,0 +1,394 @@
+/*
+*** ARDUINO IDE BOARD SETTINGS
+*** Cuz apparently there's no easy way to store settings for a certain b0ard, I'll just have to leave it in here for na0 -.-
+***
+*** - Board: Generic ESP8266 Module
+*** - Builtin Led: 2
+*** - Upload Speed: 3000000
+*** - CPU Frequency: 160 MHz
+*** - Crystal Frequency: 26 MHz
+*** - Flash Size: 4 MB (FS: none, OTA: ~1019 kB)
+*** - Flash Mode: DIO
+*** - Flash Frequency: 40 MHz
+*** - Reset Method: dtr (aka nodemcu)
+*** - Debug Port: Disabled
+*** - Debug Level: None
+*** - IwIP Variant: v2 Lower Memory
+*** - VTables: Flash
+*** - Exceptions: Legacy (new can return nullptr)
+*** - Erase Flash: Only Sketch
+*** - Espressif FW: nonos-sdk 2.2.1+100 (190703)
+*** - SSL Support: All SSL ciphers (most compatible)
+*/
+
+#include <ESP8266WiFi.h>
+#include <ArduinoOTA.h>
+//#include <WiFiUdp.h> // ArduinoOTA includes this itself so may not be necessary to specify em separately
+
+/*** ========= CONFIG START ========= ***/
+
+/*** SYSTEM ***/
+#define MUHCONF_SYS_HOSTNAME "esp12e-REPLACEME"
+#define MUHCONF_SYS_DEEPSLEEP 10 // Wake from deep sleep after 10 seconds (in case of non-connecting WiFi, low timeout due to this being an initial test sketch ;])
+
+/*** WIFI ***/
+#define MUHCONF_WIFI_SSID "turds"
+#define MUHCONF_WIFI_PASS "gottem"
+#define MUHCONF_WIFI_RETRY 10 // We'll check every second if we're connected for up to this many tries
+
+/*** NETSERIAL ***/
+// For monitoring "serial" output over da netwerk ;]];;]]
+// Some boards may not immediately send out queued packets, so you may need to give it some time (about 10ms for a Lol1n NodeMCU v3)
+// The delay may also not be necessary at all if you're not sending repetitive messages (e.g. in em loop() function), in that case just set it to 0 ;]
+// Note that this does mean a simple delay() will be called and everything else will stall for a bit
+#define MUHCONF_NETSERIAL_POT 1338
+#define MUHCONF_NETSERIAL_DELAY_POSTSEND 0
+//#define MUHCONF_NETSERIAL_DELAY_POSTSEND 10
+#define MUHCONF_NETSERIAL_PRINTF_BUFSIZE 256 // Buffer size for printf()'ing a format string to send over da nets
+
+/*** OTA ***/
+#define MUHCONF_OTA_PASS "bd1b098d8cd6813d532c749b3f44c318" // MD5 y0
+#define MUHCONF_OTA_POT 1337
+
+/*** PINS ***/
+// In order to properly waek from deep sleep, we need to pull on some pins =]
+#define MUHCONF_PIN_WAKEUP_PULLUP 16 // Should be D0 (connect that with the RST pin to do timed wakeups y0)
+
+// On a NodeMCU v3 the on-board LED should be accessible via pin D2 (which corresponds to GPIO 4), except for mine it's GPIO 2 apparently???????
+#define MUHCONF_PIN_LED_ONBOARD 2 // Should be a blue LED, but will usually always be turned on anyways
+
+// Cuz some pins may affect bewt m0de if pullin em high or l0 at the proper time (like just before ESP.restart())
+//#define MUHCONF_PIN_IS_BOOTMODE(x) (x == 0 || x == 2 || x == 15) // Not sure rn tho
+
+#define MUHCONF_PIN_LED_ONBOARD_REVERSE 1 // Cuz apparently pulling low = turn it on??÷¿¿÷÷??
+
+/*** INTERVALS ***/
+// After receiving a reboot command after een OTA upd00t, wait 3000 ms before actually rebooting (in order to flush some shit properly)
+// This also protects against booting in the wrong mode by pulling on a pin too soon before the actual restart
+#define MUHCONF_WAIT_REBOOT 3000
+
+/*** ========= CONFIG END ========= ***/
+
+/*** GLOBALS ***/
+extern "C" uint32_t core_version;
+extern "C" const char *core_release;
+
+unsigned short int led_states[] = {
+	0, // Onboard
+};
+
+unsigned long end_reboot = 0; // To properly delay a reboot without delay() ;]
+
+unsigned short int ota_lastprogress = 0;
+
+WiFiUDP netserial;
+IPAddress netserial_targetip(0, 0, 0, 0); // Zero-initialise for ez checkin ;];]];];];];
+
+void netserial_printf(const char *pattern, ...) {
+	// qt lil function to dump shit to both the netw0rk and serial console
+	// You may not need this often, e.g. broadcasting flash br0gress is not v useful, should pr0lly just broadcast started/finished/errors (i.e. one-off messages)
+	// Also network broadcasts require a delay so that would stall and most likely fuck up your flash =]]]]]
+	va_list vl;
+	char buf[MUHCONF_NETSERIAL_PRINTF_BUFSIZE + 1];
+	va_start(vl, pattern);
+	vsnprintf(buf, sizeof(buf), pattern, vl);
+	va_end(vl);
+
+	if(netserial_targetip[0]) {
+		netserial.beginPacket(netserial_targetip, MUHCONF_NETSERIAL_POT);
+		netserial.write(buf);
+		netserial.endPacket();
+		if(MUHCONF_NETSERIAL_DELAY_POSTSEND)
+			delay(MUHCONF_NETSERIAL_DELAY_POSTSEND); // Need a little delay to allow sending out the packet
+	}
+	Serial.print(buf);
+}
+
+/*** SETUP FUNCTIONS ***/
+void setup(void) {
+	Serial.begin(115200);
+	Serial.printf("\r\n"); // Make sure the boot mode shit is properly terminated ;]
+
+	// The order here is muy importante ;]
+	setup_sys();
+	setup_led();
+	setup_wifi();
+	setup_netserial();
+	setup_ota();
+
+	netserial_printf("** Initialisation complete y0\r\n");
+
+	// Signal initialisation complete ;];];];];];];]];;];];]
+	led_on(MUHCONF_PIN_LED_ONBOARD, 0); // Won't be turned off by us after this point
+}
+
+void setup_sys(void) {
+	unsigned int chip_realsize, chip_size, chip_speed;
+
+	// Set system parameters here
+	Serial.printf("** Initialising system parameters fam\r\n");
+
+	Serial.printf("\t- Setting GPIO pin %d as WAKEUP_PULLUP\r\n", MUHCONF_PIN_WAKEUP_PULLUP);
+	pinMode(MUHCONF_PIN_WAKEUP_PULLUP, WAKEUP_PULLUP);
+
+	Serial.printf("\t- Hostname: %s\r\n", MUHCONF_SYS_HOSTNAME);
+	wifi_station_set_hostname(MUHCONF_SYS_HOSTNAME);
+
+	// Also print some system shit for keks
+	Serial.printf("** Getting s0em system parameters fam\r\n");
+	Serial.printf("\t- Last reset reason: %s\r\n", ESP.getResetInfo().c_str());
+	Serial.printf("\t- Core version: 0x%08x\r\n", core_version);
+	Serial.printf("\t- Core release: %s\r\n", (core_release != NULL ? core_release : "NULL"));
+	Serial.printf("\t- SDK version: %s\r\n", ESP.getSdkVersion());
+	Serial.printf("\t- Boot version: %d\r\n", ESP.getBootVersion());
+	Serial.printf("\t- Boot mode: %d\r\n", ESP.getBootMode());
+	Serial.printf("\t- CPU frequency: %d MHz\r\n", ESP.getCpuFreqMHz());
+
+	Serial.printf("\t- Flash chip ID: 0x%08x\r\n", ESP.getFlashChipId());
+	Serial.printf("\t- Flash chip vendor ID: 0x%08x\r\n", ESP.getFlashChipVendorId());
+
+	chip_size = ESP.getFlashChipSize();
+	Serial.printf("\t- Flash chip size: %d bytes (%d megabutts)\r\n", chip_size, (chip_size / 1024 / 1024));
+
+	chip_realsize = ESP.getFlashChipRealSize();
+	Serial.printf("\t- Flash chip real size: %d bytes (%d megabutts)\r\n", chip_realsize, (chip_realsize / 1024 / 1024));
+
+	chip_speed = ESP.getFlashChipSpeed();
+	Serial.printf("\t- Flash chip speed: %d Hz (%d MHz)\r\n", chip_speed, (chip_speed / 1000 / 1000));
+
+	Serial.printf("\t- Flash chip mode: %d\r\n", ESP.getFlashChipMode());
+}
+
+void setup_led(void) {
+	// Set up LEDs lol
+	Serial.printf("** Initialising LEDs y0\r\n");
+	Serial.printf("\t- On-board LED (pin %d)\r\n", MUHCONF_PIN_LED_ONBOARD);
+	pinMode(MUHCONF_PIN_LED_ONBOARD, OUTPUT);
+	led_off(MUHCONF_PIN_LED_ONBOARD, 0, 0); // Make sure LED starts turned off obviously ;]
+}
+
+void setup_wifi(void) {
+	// Set up WiFi imo tbh
+	unsigned short int i;
+
+	Serial.printf("** Initialising WiFi lol\r\n");
+	WiFi.mode(WIFI_STA);
+	WiFi.begin(MUHCONF_WIFI_SSID, MUHCONF_WIFI_PASS);
+	Serial.printf("\t- Connecting to: %s", MUHCONF_WIFI_SSID);
+	for(i = 0; i < MUHCONF_WIFI_RETRY && WiFi.status() != WL_CONNECTED; i++) {
+		Serial.printf(".");
+		delay(1000);
+	}
+	Serial.printf("\r\n");
+
+	if(WiFi.status() != WL_CONNECTED) {
+		Serial.printf("** WiFi = rip lol, going into deep sleep for %d second%s before restarting y0\r\n", MUHCONF_SYS_DEEPSLEEP, (MUHCONF_SYS_DEEPSLEEP > 1 ? "s" : ""));
+		ESP.deepSleep(MUHCONF_SYS_DEEPSLEEP * 1000 * 1000); // Convert seconds to microseconds ;]
+		ESP.restart(); // Just in case l0l
+		return; // ditt0
+	}
+
+	// n0t broadcasting the local IP here cuz if we wait until _after_ the netserial has been initialised, we can also broadcast that information ;];];;];];];
+	Serial.printf("\t- Connected y0\r\n");
+}
+
+void setup_netserial(void) {
+	// Ayyyy black magic ;];];];];]];];];];
+	// Most of this function involves dynamically calculating the broadcast address based on whatever fuckin network you're connected to
+	// Also this only works with IPv4 because using v6 for LANs is pretty fucking retarded
+	unsigned int ip_bits, subnet_bits, broadcast_bits;
+	IPAddress subnet;
+
+	Serial.printf("** Initialising network serial console bruh\r\n");
+	netserial_targetip = WiFi.localIP();
+	subnet = WiFi.subnetMask();
+
+	ip_bits = (netserial_targetip[0] << 24) + (netserial_targetip[1] << 16) + (netserial_targetip[2] << 8) + netserial_targetip[3];
+	subnet_bits = ((subnet[0] << 24) + (subnet[1] << 16) + (subnet[2] << 8) + subnet[3]) ^ 0xFFFFFFFF;
+	broadcast_bits = (ip_bits | subnet_bits);
+
+	netserial_targetip[0] = (broadcast_bits >> 24) & 0xFF;
+	netserial_targetip[1] = (broadcast_bits >> 16) & 0xFF;
+	netserial_targetip[2] = (broadcast_bits >> 8) & 0xFF;
+	netserial_targetip[3] = broadcast_bits & 0xFF;
+
+	netserial.begin(MUHCONF_NETSERIAL_POT);
+
+	netserial_printf("\t- IP address: %s\r\n", WiFi.localIP().toString().c_str());
+	netserial_printf("\t- Broadcast address: %s\r\n", netserial_targetip.toString().c_str());
+}
+
+void setup_ota(void) {
+	netserial_printf("** Initialising OTA shit\r\n");
+	ArduinoOTA.setHostname(MUHCONF_SYS_HOSTNAME); // May not be necessary, but let's =]
+	ArduinoOTA.setPort(MUHCONF_OTA_POT);
+	ArduinoOTA.setPasswordHash(MUHCONF_OTA_PASS);
+	ArduinoOTA.setRebootOnSuccess(false); // Let's do a manual reb00t pls
+
+	ArduinoOTA.onStart([]() {
+		if(ArduinoOTA.getCommand() != U_FLASH) {
+			// U_SPIFFS for filesystem lol
+			netserial_printf("** [ERROR] [OTA] Received unsupported command (only flashing of sketches is supported at the moment)\r\n");
+			return;
+		}
+
+		//SPIFFS.end() // Just leaving this here in case we ever need a filesystem lmao
+		netserial_printf("[OTA] Starting sketch flash\r\n");
+	});
+
+	ArduinoOTA.onEnd([]() {
+		netserial_printf("[OTA] Flashing complete lol, initiating reb00t\r\n");
+		rebootem();
+	});
+
+	ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+		unsigned short int newprogress = (progress / (total / 100));
+		if(ota_lastprogress != newprogress) {
+			Serial.printf("[OTA] Flash progress: %u%%\r\n", newprogress);
+			ota_lastprogress = newprogress;
+		}
+	});
+
+	ArduinoOTA.onError([](ota_error_t errorcode) {
+		netserial_printf("[ERROR] [OTA] Error %d: ", errorcode);
+		switch(errorcode) {
+			case OTA_AUTH_ERROR:
+				netserial_printf("Auth failed");
+				break;
+			case OTA_BEGIN_ERROR:
+				netserial_printf("Begin failed");
+				break;
+			case OTA_CONNECT_ERROR:
+				netserial_printf("Connect failed");
+				break;
+			case OTA_RECEIVE_ERROR:
+				netserial_printf("Receive failed");
+				break;
+			case OTA_END_ERROR:
+				netserial_printf("End failed");
+				break;
+			default:
+				netserial_printf("Unknown error");
+				break;
+		}
+		netserial_printf("\r\n");
+	});
+	ArduinoOTA.begin();
+	netserial_printf("\t- Should b gucci mane\r\n");
+}
+
+/*** CLEANUP/SHUTDOWN FUNCTIONS **/
+// WiFi and netserial don't have cleanup functions cuz we'll keep using that until we actually shut down ;]
+// Some classes may not have proper shutdown methods, so let's just use the destruct0r where they're not [==[=[=[=[=[
+// For e.g. the web server this means that requests will immediately fail since our listening s0cket is gone
+void cleanup_led(void) {
+	// Not really a cleanup tho lel
+	// Make sure onboard is on so we know the ESP is actually still powered etc, turn off the rest in advance doe
+	led_on(MUHCONF_PIN_LED_ONBOARD, 0);
+}
+
+void cleanup_ota(void) {
+	// OTA shit is a gl0bal instance so clean up the entire fuckin thing
+	netserial_printf("** Cleaning up OTA shit\r\n");
+	ArduinoOTA.~ArduinoOTAClass();
+}
+
+/*** LOOP FUNCTIONS ***/
+void loop(void) {
+	unsigned long mstime;
+
+	mstime = millis();
+
+	// Checking this interval outside of all the other loop_* functions cuz we need to do a "blocking" reboot
+	if(end_reboot > 0) {
+		if(mstime >= end_reboot) {
+			netserial_printf("** Ayy reb00ting na0\r\n");
+			ESP.restart();
+		}
+		return;
+	}
+
+	// Czech if WiFi still gucci, otherwise just reb00t lol
+	if(WiFi.status() != WL_CONNECTED) {
+		netserial_printf("** Rip WiFi lmao, initiating reb00t\r\n");
+		rebootem();
+		return;
+	}
+
+	loop_ota();
+}
+
+void loop_ota(void) {
+	ArduinoOTA.handle();
+}
+
+/*** UTILITY/HELPER FUNCTIONS ***/
+void rebootem(void) {
+	// Let's not reset the timer every time (prolly can't happen anyways but w/e =])
+	if(end_reboot > 0)
+		return;
+
+	// Obviously we're gonna close em services in the reversed order of the setup_* functions ;]
+	cleanup_ota();
+	cleanup_led();
+
+	end_reboot = millis() + MUHCONF_WAIT_REBOOT;
+}
+
+void led_off(unsigned short int pin, unsigned short int update_state, unsigned short int check_state) {
+	// check_state is set to 0 in order to blink a LED in case of quick successive commands =]
+	// Otherwise only turn LED off if nothing has it turned on ;];;];]
+
+	if(update_state)
+		led_update_state(pin, 0);
+
+	if(check_state && led_check_state(pin) != 0)
+		return;
+
+	if(pin == MUHCONF_PIN_LED_ONBOARD && MUHCONF_PIN_LED_ONBOARD_REVERSE)
+		digitalWrite(pin, HIGH);
+	else
+		digitalWrite(pin, LOW);
+}
+
+void led_on(unsigned short int pin, unsigned short int update_state) {
+	if(update_state)
+		led_update_state(pin, 1);
+
+	if(pin == MUHCONF_PIN_LED_ONBOARD && MUHCONF_PIN_LED_ONBOARD_REVERSE)
+		digitalWrite(pin, LOW);
+	else
+		digitalWrite(pin, HIGH);
+}
+
+signed short int led_get_state_index(unsigned short pin) {
+	switch(pin) {
+		case MUHCONF_PIN_LED_ONBOARD:
+			return 0;
+
+		default:
+			break;
+	}
+
+	return -1; // Not doing this from the default case, to silence warnings ;]
+}
+
+unsigned short int led_check_state(unsigned short int pin) {
+	signed short int led_i;
+
+	led_i = led_get_state_index(pin);
+	if(led_i < 0)
+		return 0;
+
+	return led_states[led_i];
+}
+
+void led_update_state(unsigned short int pin, unsigned short int state) {
+	signed short int led_i;
+
+	led_i = led_get_state_index(pin);
+	if(led_i < 0)
+		return;
+
+	led_states[led_i] = state;
+}