Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DominikN committed Dec 8, 2021
0 parents commit d8d5a07
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 0 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/ota-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: ESP32 OTA update

on:
push:
branches:
- 'main'

jobs:
build:
runs-on: ubuntu-20.04

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Connecting the GitHub workflow to Husarnet VPN network
uses: husarnet/husarnet-action@v2
with:
join-code: ${{ secrets.HUSARNET_JOINCODE }}

- name: ESP32 software reset
run: curl -X POST 'http://${{ secrets.HUSARNET_HOSTNAME }}:8080/reset'

- name: Installing platformio
run: pip3 install -U platformio

- name: Building a firmware for ESP32
run: |
export SSID=${{ secrets.WIFI_SSID }}
export PASS=${{ secrets.WIFI_PASS }}
export JOINCODE=${{ secrets.HUSARNET_JOINCODE }}
export HOSTNAME=${{ secrets.HUSARNET_HOSTNAME }}
pio run
- name: Uploading a firmware to ESP32
run: >
curl --http0.9 -# -v
-H 'Accept: */*'
-H 'Accept-Encoding: gzip, deflate'
-H 'Connection: keep-alive'
-F "MD5="$(md5sum "${{ github.workspace }}/.pio/build/esp32dev/firmware.bin" | cut -d ' ' -f 1)""
-F 'firmware=@${{ github.workspace }}/.pio/build/esp32dev/firmware.bin'
'http://${{ secrets.HUSARNET_HOSTNAME }}:8080/update'
- name: Stop Husarnet
run: sudo systemctl stop husarnet
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.pio
src/credentials.h
.vscode/
firmware.bin
node_modules/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Husarnet

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# esp32-internet-ota

ESP32 + GitHub Actions + Husarnet.

A boilerplate project for ESP32 allowing in-field firmware update using GitHub Actions workflow.

> **Prerequisites**
>
> Install [Visual Studio Code](https://code.visualstudio.com/) with [PlatformIO extension](https://platformio.org/install/ide?install=vscode).
## Quick start

### First setup

1. Click **[Use this template](https://github.com/husarnet/esp32-internet-ota/generate)** button to create your own copy of this repo.

2. Clone the repo you have just created and open it in Visual Studio Code. Platformio should automatically install all project dependencies.

3. Rename `credentials-template.h` to `credentials.h` and type your WiFi an Husarnet credentials there (you will find you Husarnet Join Code at https://app.husarnet.com).

4. Click "PlatformIO: upload" button to flash your ESP32 board connected to your laptop. You will find the following log in the serial monitor:

```bash
**************************************
GitHub Actions OTA example
**************************************

📻 1. Connecting to: FreeWifi Wi-Fi network .. done

⌛ 2. Waiting for Husarnet to be ready ... done

🚀 HTTP server started

Visit:
http://my-esp32:8080/

Known hosts:
my-laptop (fc94:a4c1:1f22:ab3b:b04a:1a3b:ba15:84bc)
my-esp32 (fc94:f632:c8d9:d2a6:ad18:ed16:ed7e:9f3f)
```

### Internet OTA with GitHub Actions

1. Create the folowing GitHub repository secrets (`Settings` > `Secrets` > `New repository secret`):

| Secret | Sample Value | Desription |
| - | - | - |
| `WIFI_SSID` | FreeWifi | just your WiFi network name |
| `WIFI_PASS` | hardtoguess | ... and password |
| `HUSARNET_HOSTNAME` | my-esp32 | hostname under which you want your ESP32 to be available by other peers |
| `HUSARNET_JOINCODE` | fc94:...:932a/xhfqwPxxxetyCExsSPRPn9 | find your own **secret** Join Code at your user account at https://app/husarnet.com > `choosen network` > `add element` button. Anyone with this Join Code can connect to your Husarnet network |

2. Push changes to your repo:

```bash
git add *
git commit -m "triggering the workflow"
git push
```

3. In ~3 minutes the GitHub workflow should finish its job. Visit: `http://my-esp32:8080` URL with a sample "Hello world" website hosted by your ESP32.


Of course your laptop need to be connected to the same Husarnet network - you will find quick start guide showing how to do it here: https://husarnet.com/docs/


## Tips

### Erasing flash memory of ESP32

1. Connect ESP32 to your laptop

2. Install platformio CLI

```bash
pip install -U platformio
```

3. Make flash erase:

```bash
pio run --target erase
```

### Monitoring network traffic on `hnet0` interface

```bash
sudo tcpflow -p -c -i hnet0
```

### Accesing a webserver hosted by ESP32 using a public domain

Here is a blog post showing how to configure Nginx Proxy Manager to **provide a public access to web servers hosted by Husarnet connected devices**: https://husarnet.com/blog/reverse-proxy-gui

It can be also used o provide the access to a web server hosted by ESP32 using a nice looking link like: `https://my-awesome-esp32.mydomain.com`.
29 changes: 29 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[env]
platform = espressif32
framework = arduino
platform_packages =
framework-arduinoespressif32 @ https://github.com/husarnet/arduino-esp32/releases/download/1.0.4-1/arduino-husarnet-esp32.zip
lib_deps =
; Until our pull requests are merged you need to use AsyncTCP with our fixes for IPv6
https://github.com/husarnet/AsyncTCP.git
Husarnet ESP32
ESP Async WebServer
ayushsharma82/AsyncElegantOTA @ ^2.2.6

[env:esp32dev]
board = esp32dev
monitor_speed = 115200
upload_speed = 921600

monitor_filters = esp32_exception_decoder, default

board_build.partitions = min_spiffs.csv
board_build.embed_txtfiles =
src/index.html

build_flags =
'-DWIFI_SSID="${sysenv.SSID}"'
'-DWIFI_PASS="${sysenv.PASS}"'
'-DHUSARNET_HOSTNAME="${sysenv.HOSTNAME}"'
'-DHUSARNET_JOINCODE="${sysenv.JOINCODE}"'

8 changes: 8 additions & 0 deletions src/credentials-template.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// WiFi credentials
const char *ssid = "FreeWifi";
const char *password = "hardtoguess";

// Husarnet credentials
const char *hostName = "my-esp32";
const char *husarnetJoinCode = "fc94:b01d:1803:8dd8:b293:5c7d:7639:932a/xhfqwPxxxetyCExsSPRPn9"; // find at app.husarnet.com
const char *dashboardURL = "default"; // in you use Husarnet self-hosted Dashboard you can specify URL here. Otherwise keep "default"
9 changes: 9 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP32 boilerplate</title>
</head>
<body>
<p>Hello world!</p>
</body>
</html>
121 changes: 121 additions & 0 deletions src/simple-webserver.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include <AsyncElegantOTA.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Husarnet.h>
#include <WiFi.h>

#define HTTP_PORT 8080

#if __has_include("credentials.h")

// For local development (rename credenials-template.h and type your WiFi and
// Husarnet credentials there)
#include "credentials.h"

#else

// For GitHub Actions OTA deploment

// WiFi credentials
const char *ssid = WIFI_SSID;
const char *password = WIFI_PASS;

// Husarnet credentials
const char *hostName = HUSARNET_HOSTNAME;
const char *husarnetJoinCode = HUSARNET_JOINCODE; // find at app.husarnet.com
const char *dashboardURL = "default";

#endif

AsyncWebServer server(HTTP_PORT);

// index.html available in "index_html" const String
extern const char index_html_start[] asm("_binary_src_index_html_start");
const String index_html = String((const char*)index_html_start);

void setup(void) {
// ===============================================
// Wi-Fi, OTA and Husarnet VPN configuration
// ===============================================

// remap default Serial (used by Husarnet logs)
Serial.begin(115200, SERIAL_8N1, 16, 17); // from P3 & P1 to P16 & P17
Serial1.begin(115200, SERIAL_8N1, 3, 1); // remap Serial1 from P9 & P10 to P3 & P1

Serial1.println("\r\n**************************************");
Serial1.println("GitHub Actions OTA example");
Serial1.println("**************************************\r\n");

// Init Wi-Fi
Serial1.printf("📻 1. Connecting to: %s Wi-Fi network ", ssid);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
static int cnt = 0;
delay(500);
Serial1.print(".");
cnt++;
if (cnt > 10) {
ESP.restart();
}
}

Serial1.println(" done\r\n");

// Init Husarnet P2P VPN service
Serial1.printf("⌛ 2. Waiting for Husarnet to be ready ");

Husarnet.selfHostedSetup(dashboardURL);
Husarnet.join(husarnetJoinCode, hostName);
Husarnet.start();

// Before Husarnet is ready peer list contains:
// master (0000:0000:0000:0000:0000:0000:0000:0001)
const uint8_t addr_comp[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
bool husarnetReady = 0;
while (husarnetReady == 0) {
Serial1.print(".");
for (auto const &host : Husarnet.listPeers()) {
if (host.first == addr_comp) {
;
} else {
husarnetReady = 1;
}
}
delay(1000);
}

Serial1.println(" done\r\n");

// define HTTP API for remote reset
server.on("/reset", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Reseting ESP32 after 1s ...");
Serial1.println("Software reset on POST request");
delay(1000);
ESP.restart();
});

// Init OTA webserver (available under /update path)
AsyncElegantOTA.begin(&server);
server.begin();

// ===============================================
// PLACE YOUR APPLICATION CODE BELOW
// ===============================================

// Example webserver hosting table with known Husarnet Hosts
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/html", index_html);
});

Serial1.println("🚀 HTTP server started\r\n");
Serial1.printf("Visit:\r\nhttp://%s:%d/\r\n\r\n", hostName, HTTP_PORT);

Serial1.printf("Known hosts:\r\n");
for (auto const &host : Husarnet.listPeers()) {
Serial1.printf("%s (%s)\r\n", host.second.c_str(), host.first.toString().c_str());
}
}

void loop(void) { ; }

0 comments on commit d8d5a07

Please sign in to comment.