6168

Turn on/off ESP32’s led in Sydney, for example, from ESP32’s button in Paris

ESP32 OVER THE WORLD


More options :
·  Broker MQTT : use a cloud or home.
·  WireGuard : VPN tunnel with two router AR300M. But you need two configure the home box.
·  Plateforme IoT (Blynk, Adafruit IO, Particle) : free but you use a third party.
·  Tailscale : two router AR300M, the ESP32 exchange data like the same network. I choose this one.
 
Warning :
I prefer a human colleague to make small talk and to take a coffee. But, my new colleague is AI Claude from Anthropic. This one doesn’t drink coffee but it’s very speed. All my code for my project was created with this AI. I choose to write this article with my low English’s level even if I use AI to translate some sentences and some worlds. Sorry ;-(
 
 
AR300M choice : 
I had two and AI Claude explained that the router will be nice because cheaper. But I need to use a USB key (a use 16Go but may be 8Go is ok) because AR300M memory is slow. For this project, ilt necessary to install Tailscale. 
 
Installation : 


1)    First connection and changes 
  • Connect with Wifi to GL-AR300M-xx with password « goodlife » 
  • Use your browser : http://192.168.8.1 (Be careful, the link isn’t httpS) 
  • Choose your language and change the password 

2)    Update and Install packages 
  • Connect with SSH : ssh root@192.168.8.1 
 --- Begin code -----------------------------------------------------------------------------------------------
opkg update opkg install kmod-usb-storage kmod-usb-storage-uas usbutils block-mount e2fsprogs kmod-fs-ext4 

--- End code -----------------------------------------------------------------------------------------------
 
3)    If you want keep your configuration (not tested)
 --- Begin code -----------------------------------------------------------------------------------------------
mkdir -p /tmp/introot /tmp/extroot mount --bind /overlay /tmp/introot mount /dev/sda /tmp/extroot tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf umount /tmp/introot /tmp/extroot 

--- End code -----------------------------------------------------------------------------------------------
 
4)    Install the USB Key 
  • On your computer, format the USB key in FAT format. 
  • On the AR300, format : 
--- Begin code -----------------------------------------------------------------------------------------------
mkfs.ext4 -F /dev/sda cat /sys/block/sda/size
--- End code -----------------------------------------------------------------------------------------------
=> 30720000
--- Begin code -----------------------------------------------------------------------------------------------
mount /dev/sda /mnt
 --- End code -----------------------------------------------------------------------------------------------
=> some warnings will be appear
  • Switch to extroot :
--- Begin code -----------------------------------------------------------------------------------------------
block detect | uci import fstab uci show fstab
--- End code -----------------------------------------------------------------------------------------------
=> show the good entry for the @mount[N] and replace it
--- Begin code -----------------------------------------------------------------------------------------------
uci set fstab.@mount[N].target='/overlay' uci set fstab.@mount[N].enabled='1' uci commit fstab reboot

--- End code -----------------------------------------------------------------------------------------------
  • Check after reboot : 
--- Begin code ----------------------------------------------------------------------------------------------
df -h /overlay
--- End code -----------------------------------------------------------------------------------------------
=> Filesystem                Size      Used Available Use% Mounted on
=> /dev/sda                 14.3G    496.0K     13.6G   0% /overlay 

5)    Network connection switch 
  • Change the Time Zone To avoid a conflict, later with Tailscale, change the ARM300 Sydney « RouteurIP Address » to « 192.168.10.1 » 
  • Wait and reconnect to « 192.169.10.1 » 
  • Change the SSID name : AR300M-Sydney (AR300M-Paris for the second’s) and the password (old was « goodlife »). You lost the connection. 
  • WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! => The command : ssh-keygen -R 192.168.10.1 
6)    Install Tailscale 
  • Download the Tailscale version :
 --- Begin code -----------------------------------------------------------------------------------------------
uclient-fetch https://pkgs.tailscale.com/stable/tailscale_1.102.2_mips.tgz
 --- End code -----------------------------------------------------------------------------------------------
 Warning : don’t download the mipsle version
 --- Begin code -----------------------------------------------------------------------------------------------
tar xzvf tailscale_1.102.2_mips.tgz mkdir -p /opt/tailscale/state mv tailscale_1.102.2_mips/tailscale /opt/tailscale/ mv tailscale_1.102.2_mips/tailscaled /opt/tailscale/ ln -s /opt/tailscale/tailscale /usr/sbin/tailscale ln -s /opt/tailscale/tailscaled /usr/sbin/tailscaled tailscale --version

--- End code -----------------------------------------------------------------------------------------------
=> ln: /usr/sbin/tailscale: File exists
=> ln: /usr/sbin/tailscaled: File exists
=> 1.102.2 

7)    Init scipt
  --- Begin code -----------------------------------------------------------------------------------------------
cat > /etc/init.d/tailscale << 'EOF' #!/bin/sh /etc/rc.common USE_PROCD=1 START=80 STOP=01 start_service() { /usr/sbin/tailscaled --cleanup procd_open_instance procd_set_param command /usr/sbin/tailscaled procd_append_param command --port 41641 procd_append_param command --state /opt/tailscale/state/tailscaled.state procd_append_param command --statedir /opt/tailscale/state procd_set_param respawn procd_set_param stdout 1 procd_set_param stderr 1 procd_close_instance } stop_service() { /usr/sbin/tailscaled --cleanup } EOF
--- End code -----------------------------------------------------------------------------------------------
  --- Begin code -----------------------------------------------------------------------------------------------
chmod +x /etc/init.d/tailscale /etc/init.d/tailscale enable /etc/init.d/tailscale start tailscale up --advertise-routes=192.168.10.0/24 

 --- End code -----------------------------------------------------------------------------------------------
=> https://login.tailscale.com/a/xxxxxxxxxxxxx
  • Copy the link to your browser and connect to your Taiscale
=> Login successful
=> Your device GL-AR300M is logged
 
8)    Enable IP forwarding
--- Begin code -----------------------------------------------------------------------------------------------
sysctl -w net.ipv4.ip_forward=1 echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf

--- End code -----------------------------------------------------------------------------------------------

9)    Configure the tailscale0 interface on the network side
--- Begin code -----------------------------------------------------------------------------------------------
uci set network.tailscale=interface uci set network.tailscale.proto='none' uci set network.tailscale.device='tailscale0' uci commit network 

--- End code -----------------------------------------------------------------------------------------------

10)  Firewall zone and bidirectional forwarding rules
 --- Begin code -----------------------------------------------------------------------------------------------
uci set firewall.tailscale=zone uci set firewall.tailscale.name='tailscale' uci set firewall.tailscale.input='ACCEPT' uci set firewall.tailscale.output='ACCEPT' uci set firewall.tailscale.forward='ACCEPT' uci set firewall.tailscale.network='tailscale uci commit firewall uci set firewall.tailscale_to_lan=forwarding uci set firewall.tailscale_to_lan.src='tailscale' uci set firewall.tailscale_to_lan.dest='lan' uci commit firewall uci set firewall.lan_to_tailscale=forwarding uci set firewall.lan_to_tailscale.src='lan' uci set firewall.lan_to_tailscale.dest='tailscale' uci commit firewall 

--- End code -----------------------------------------------------------------------------------------------

11)  Restart
  --- Begin code -----------------------------------------------------------------------------------------------
/etc/init.d/network restart
--- End code -----------------------------------------------------------------------------------------------
 => You lost the connection
 --- Begin code -----------------------------------------------------------------------------------------------
/etc/init.d/firewall restart
--- End code -----------------------------------------------------------------------------------------------
=> Section block_dns (block_dns) is disabled, ignoring section
=> Section nat6 option 'reload' is not supported by fw4
=> Section gls2s option 'reload' is not supported by fw4
=> Section gls2s specifies unreachable path '/var/etc/gls2s.include', ignoring section
=> Section glblock option 'reload' is not supported by fw4
=> Section vpn_server_policy option 'reload' is not supported by fw4
=> Automatically including '/usr/share/nftables.d/chain-pre/mangle_output/01-process_mark.nft'
=> DROP  all opt -- in * out *  0.0.0.0/0  -> 0.0.0.0/0   match-set GL_MAC_BLOCK src 
 
12)  Subnet route approval 
On Tailscale board, select the AR300 and check that the « Subnets »is approved on « 192.168.10.0/24 » 
 
The ESP32 Code not tested
--- Begin code -----------------------------------------------------------------------------------------------

// ESP32 Paris - interrupteur // Envoie un chiffre au hasard a l'ESP32 de Sydney : // - impair -> commande ON // - pair -> commande OFF // La LED de confirmation clignote pendant l'attente de la reponse. // Reponse recue : LED fixe (allumee = ON confirme, eteinte = OFF confirme). // Pas de reponse (timeout) : clignotement tres rapide.

#include <WiFi.h> #include <HTTPClient.h>

const char* ssid = "AR300M-Paris"; const char* password = "MOT_DE_PASSE_WIFI_PARIS";

const char* sydneyIP = "192.168.9.50"; // IP fixe de l'ESP32 Sydney const int switchPin = 4; // interrupteur (pull-up interne) const int confirmLed = 2; // LED de confirmation (LED integree)

volatile bool waitingForResponse = false; int lastSwitchState = HIGH;

// Tache de fond dediee au clignotement, pour que la LED bouge // pendant que loop() est bloque sur http.GET() void blinkTask(void *parameter) { for (;;) { if (waitingForResponse) { digitalWrite(confirmLed, HIGH); vTaskDelay(120 / portTICK_PERIOD_MS); digitalWrite(confirmLed, LOW); vTaskDelay(120 / portTICK_PERIOD_MS); } else { vTaskDelay(50 / portTICK_PERIOD_MS); } } }

void setup() { Serial.begin(115200); pinMode(switchPin, INPUT_PULLUP); pinMode(confirmLed, OUTPUT); digitalWrite(confirmLed, LOW);

WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("Connexion Wi-Fi Paris"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("IP locale Paris : "); Serial.println(WiFi.localIP());

randomSeed(esp_random());

xTaskCreatePinnedToCore(blinkTask, "blinkTask", 2048, NULL, 1, NULL, 0); }

void loop() { int currentState = digitalRead(switchPin);

if (lastSwitchState == HIGH && currentState == LOW) { // front descendant : interrupteur actionne sendCommandToSydney(); } lastSwitchState = currentState; delay(30); // anti-rebond simple }

void sendCommandToSydney() { if (WiFi.status() != WL_CONNECTED) { Serial.println("Wi-Fi non connecte, abandon."); fastFailBlink(); return; }

// Chiffre au hasard : impair = ON, pair = OFF int sentNumber = random(0, 100); bool wantOn = (sentNumber % 2 == 1); Serial.print("Envoi du chiffre "); Serial.print(sentNumber); Serial.println(wantOn ? " (impair -> ON)" : " (pair -> OFF)");

HTTPClient http; String url = String("http://") + sydneyIP + "/cmd?n=" + String(sentNumber); http.begin(url); http.setTimeout(5000); // marge pour la latence Paris-Sydney

waitingForResponse = true; // la tache de fond fait clignoter la LED int httpCode = http.GET(); waitingForResponse = false;

if (httpCode == 200) { String response = http.getString(); int returnedNumber = response.toInt(); bool confirmedOn = (returnedNumber % 2 == 1);

Serial.print("Reponse recue : "); Serial.print(returnedNumber); Serial.println(confirmedOn ? " (impair -> ON confirme)" : " (pair -> OFF confirme)");

digitalWrite(confirmLed, confirmedOn ? HIGH : LOW); } else { Serial.print("Pas de reponse valide, code HTTP : "); Serial.println(httpCode); fastFailBlink(); }

http.end(); }

void fastFailBlink() { for (int i = 0; i < 12; i++) { digitalWrite(confirmLed, HIGH); delay(50); digitalWrite(confirmLed, LOW); delay(50); } }

--- End code -----------------------------------------------------------------------------------------------
--- Begin code -----------------------------------------------------------------------------------------------

// ESP32 Sydney - LED // Recoit un chiffre au hasard depuis l'ESP32 de Paris via /cmd?n=... // - impair -> allume la LED // - pair -> eteint la LED // Renvoie un autre chiffre au hasard, dont la parite confirme l'etat // reellement applique (impair = confirmee ON, pair = confirmee OFF). // Exemple donne : recoit 1 (impair -> ON), renvoie 9 (impair -> confirme ON).

#include <WiFi.h> #include <WebServer.h>

const char* ssid = "AR300M-Sydney"; const char* password = "MOT_DE_PASSE_WIFI_SYDNEY";

WebServer server(80); const int ledPin = 2; // LED integree sur la plupart des cartes ESP32 bool ledState = false;

void handleCmd() { if (!server.hasArg("n")) { server.send(400, "text/plain", "Parametre n manquant"); return; }

int receivedNumber = server.arg("n").toInt(); bool wantOn = (receivedNumber % 2 == 1);

ledState = wantOn; digitalWrite(ledPin, ledState ? HIGH : LOW);

Serial.print("Recu "); Serial.print(receivedNumber); Serial.println(wantOn ? " (impair -> LED ON)" : " (pair -> LED OFF)");

// Chiffre de retour au hasard, dont on force la parite pour qu'elle // corresponde a l'etat reellement applique int returnNumber = random(0, 100); if (ledState && returnNumber % 2 == 0) returnNumber++; // forcer impair si ON if (!ledState && returnNumber % 2 == 1) returnNumber++; // forcer pair si OFF

Serial.print("Renvoi "); Serial.println(returnNumber);

server.send(200, "text/plain", String(returnNumber)); }

void handleRoot() { String html = "<h1>ESP32 Sydney</h1>"; html += "<p>IP locale : " + WiFi.localIP().toString() + "</p>"; html += "<p>Etat LED : " + String(ledState ? "ON" : "OFF") + "</p>"; server.send(200, "text/html", html); }

void setup() { Serial.begin(115200); pinMode(ledPin, OUTPUT);

WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("Connexion Wi-Fi Sydney"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("IP locale Sydney : "); Serial.println(WiFi.localIP());

randomSeed(esp_random());

server.on("/", handleRoot); server.on("/cmd", handleCmd); server.begin(); }

void loop() { server.handleClient(); }

--- End code -----------------------------------------------------------------------------------------------


Coming soon

Jennifer