├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── device
├── controller.go
└── controller_test.go
├── examples
└── first
│ ├── controller.go
│ └── controller_test.go
├── go.mod
├── go.sum
└── main
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright 2022 Andreas Geiß
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | APPNAME=$(shell basename `pwd`)
2 | ARCH=$(shell uname -m)
3 | LDFLAGS="-s"
4 |
5 | INO_BAUD="921600"
6 | INO_BOARD="esp32"
7 | INO_HARDWARE_PATH="/home/$(USER)/Arduino/hardware"
8 | INO_IDE_PATH="/opt/arduino"
9 | INO_MANUFACTURER=espressif
10 | INO_PORT="/dev/ttyUSB0"
11 | INO_SDK_PATH="$(INO_HARDWARE_PATH)/$(INO_MANUFACTURER)/$(INO_BOARD)"
12 | INO_SKETCH_FILE=build/device.ino
13 | INO_SKETCH_IMAGE=build/device.img
14 | INO_SOURCE_FILE=device/controller.go
15 | INO_TOOLS_PATH="$(INO_HARDWARE_PATH)/espressif/$(INO_BOARD)/tools"
16 | INO_TOOLS_BUILD="$(INO_TOOLS_PATH)/build.py"
17 | INO_TOOLS_FLASH="$(INO_TOOLS_PATH)/esptool.py"
18 |
19 | all: clean test build
20 |
21 | build/$(APPNAME):
22 | @go build -ldflags $(LDFLAGS) -o build/$(APPNAME)-$(ARCH) main/main.go
23 | @esp32-transpiler -source $(INO_SOURCE_FILE) -target $(INO_SKETCH_FILE)
24 | @$(INO_TOOLS_BUILD) --ide_path=$(INO_IDE_PATH) -d $(INO_HARDWARE_PATH) -b $(INO_BOARD) -w all -o $(INO_SKETCH_IMAGE) $(INO_SKETCH_FILE)
25 |
26 | build: build/$(APPNAME)
27 |
28 | clean:
29 | @rm -f build/*
30 |
31 | flash:
32 | @$(INO_TOOLS_FLASH) --chip $(INO_BOARD) --port $(INO_PORT) --baud $(INO_BAUD) write_flash -fm dio -fs 4MB -ff 40m 0x00010000 $(INO_SKETCH_IMAGE)
33 |
34 | packages:
35 | @go get -u github.com/andygeiss/esp32-controller
36 | @go get -u github.com/andygeiss/esp32-transpiler
37 | @go install -u github.com/andygeiss/esp32-transpiler
38 | @rm -rf $(INO_SDK_PATH)
39 | @mkdir -p $(INO_SDK_PATH)
40 | @git clone --recursive https://github.com/espressif/arduino-esp32.git $(INO_SDK_PATH)
41 | @curl -SSL https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-73-ge28a011-5.2.0.tar.gz > $(INO_SDK_PATH)/tools/xtensa.tar.gz
42 | @tar xzf $(INO_SDK_PATH)/tools/xtensa.tar.gz -C $(INO_SDK_PATH)/tools/
43 | @rm -f $(INO_SDK_PATH)/tools/xtensa.tar.gz
44 | @git clone --recursive https://github.com/espressif/esp-idf.git $(INO_SDK_PATH)/framework
45 | @mkdir -p $(INO_SDK_PATH)/tools/esptool
46 | @ln -sf $(INO_SDK_PATH)/tools/esptool.py $(INO_SDK_PATH)/tools/esptool/
47 |
48 | run:
49 | @./build/$(APPNAME)-$(ARCH)
50 |
51 | test:
52 | @go test -v ./...
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESP32
2 |
3 | [](https://github.com/andygeiss/esp32/blob/master/LICENSE)
4 | [](https://github.com/andygeiss/esp32/releases)
5 | [](https://goreportcard.com/report/github.com/andygeiss/esp32)
6 | [](https://codeclimate.com/github/andygeiss/esp32/maintainability)
7 |
8 | Build your own toolchain to develop, test, build and finally deploy a Golang controller to your ESP32 device.
9 |
10 | ## Purpose
11 |
12 | The [Arduino IDE](https://www.arduino.cc/en/Main/Software) is easy to use.
13 | But I faced problems like maintainability and testability at more complicated IoT projects.
14 | I needed to compile and flash the ESP32 before testing my code functionality by doing it 100% manually.
15 |
16 | This solution transpiles Golang into Arduino code, which can be compiled to an image by using the ESP32 toolchain.
17 | Now I am able to use a fully automated testing approach instead of doing it 100% manually.
18 |
19 | ## Process
20 |
21 | +--------+ +---------+ +----------+
22 | | Test +----> Build +----> Deploy |
23 | +--------+ +---------+ +----------+
24 |
25 | make make flash
26 |
27 | **Important**: The Transpiler only supports a small subset of the [Golang Language Specification](https://golang.org/ref/spec).
28 |
29 | ## Installation
30 |
31 | First download and install the latest [Arduino IDE](https://www.arduino.cc/en/Main/Software) into /opt/arduino
or change INO_IDE_PATH
in the Makefile
32 | and necessary packages.
33 |
34 | - **Ubuntu**:
35 |
36 | sudo apt-get install -y bison flex git gperf libncurses-dev make python
37 |
38 | - **Manjaro**:
39 |
40 | sudo pip install pyserial
41 |
42 | Next run the ESP32 SDK-Installation:
43 |
44 | make packages
45 |
46 | Look at the [examples](https://github.com/andygeiss/esp32/tree/master/examples) for more information.
47 |
48 | ## Develop, Test and Build
49 |
50 | Change the Arduino port to your current settings by changing the Makefile
:
51 |
52 | INO_PORT="/dev/ttyUSB0"
53 |
54 | Also set the SSID
and PASS
strings at application/device/controller.go
to your WiFi Access Point and start the build process by using:
55 |
56 | make
57 |
58 | Run the binary at build/device-${ARCH}
to simulate your ESP32 device locally.
59 |
60 | Connecting to WiFi ...... Connected!
61 |
62 | ## Deploy
63 |
64 | Finally use the following command to deploy the image device.img
to your real ESP32 device.
65 |
66 | make flash
67 |
68 | **Important**: Please ensure that the current user is in the dialout
group. Or you will receive a permission denied
.
69 |
70 | This will create the following output:
71 |
72 | 2018/08/05 16:04:22 Flashing ...
73 | esptool.py v2.3.1
74 | Connecting....
75 | Chip is ESP32D0WDQ6 (revision 1)
76 | Features: WiFi, BT, Dual Core
77 | Uploading stub...
78 | Running stub...
79 | Stub running...
80 | Changing baud rate to 921600
81 | Changed.
82 | Configuring flash size...
83 | Compressed 607456 bytes to 366770...
84 | Wrote 607456 bytes (366770 compressed) at 0x00010000 in 5.9 seconds (effective 816.9 kbit/s)...
85 | Hash of data verified.
86 |
87 | Leaving...
88 | Hard resetting via RTS pin...
89 | 2018/08/05 16:04:22 Done.
90 |
--------------------------------------------------------------------------------
/device/controller.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | import (
4 | controller "github.com/andygeiss/esp32-controller"
5 | "github.com/andygeiss/esp32-controller/serial"
6 | "github.com/andygeiss/esp32-controller/timer"
7 | wifi "github.com/andygeiss/esp32-controller/wifi"
8 | )
9 |
10 | // Controller handles the api logic and state of an ESP32.
11 | type Controller struct {
12 | }
13 |
14 | const ssid string = "SSID"
15 | const pass string = "PASSPHRASE"
16 | const host string = "HOSTNAME"
17 | const port int = 3000
18 | const request string = "GET /index.html HTTP/1.0\r\n\r\n"
19 |
20 | var client wifi.Client
21 |
22 | // NewController creates a new controller and returns its address.
23 | func NewController() controller.Controller {
24 | return &Controller{}
25 | }
26 |
27 | // Loop code will be called repeatedly.
28 | func (c *Controller) Loop() error {
29 | serial.Print("Connecting to [")
30 | serial.Print(host)
31 | serial.Print(":")
32 | serial.Print(port)
33 | serial.Print(")] ...")
34 | if client.Connect(host, port) {
35 | serial.Println(" Connected!")
36 | client.Println(request)
37 | } else {
38 | serial.Println(" Failed!")
39 | }
40 | timer.Delay(5000)
41 | return nil
42 | }
43 |
44 | // Setup code will be called once.
45 | func (c *Controller) Setup() error {
46 | serial.Begin(serial.BaudRate115200)
47 | serial.Print("Connecting to WiFi ...")
48 | for wifi.Status() != wifi.StatusConnected {
49 | wifi.BeginEncrypted(ssid, pass)
50 | serial.Print(".")
51 | timer.Delay(1000)
52 | }
53 | serial.Println(" Connected!")
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/device/controller_test.go:
--------------------------------------------------------------------------------
1 | package device_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/andygeiss/esp32/device"
7 | "github.com/andygeiss/utils/assert"
8 | )
9 |
10 | func TestControllerSetupErrorShouldBeNil(t *testing.T) {
11 | ctrl := device.NewController()
12 | err := ctrl.Setup()
13 | assert.That("setup should return without an error", t, err, nil)
14 | }
15 |
16 | func TestControllerLoopErrorShouldBeNil(t *testing.T) {
17 | ctrl := device.NewController()
18 | ctrl.Setup()
19 | err := ctrl.Loop()
20 | assert.That("loop once should return without an error", t, err, nil)
21 | }
22 |
--------------------------------------------------------------------------------
/examples/first/controller.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | import (
4 | controller "github.com/andygeiss/esp32-controller"
5 | "github.com/andygeiss/esp32-controller/digital"
6 | "github.com/andygeiss/esp32-controller/serial"
7 | "github.com/andygeiss/esp32-controller/timer"
8 | )
9 |
10 | // Controller handles the api logic and state of an ESP32.
11 | type Controller struct {
12 | }
13 |
14 | // NewController creates a new controller and returns its address.
15 | func NewController() controller.Controller {
16 | return &Controller{}
17 | }
18 |
19 | // Loop code will be called repeatedly.
20 | func (c *Controller) Loop() error {
21 | timer.Delay(500)
22 | serial.Println(" Write PIN 2 -> HIGH")
23 | digital.Write(2, digital.High)
24 | timer.Delay(500)
25 | digital.Write(2, digital.Low)
26 | serial.Println(" Write PIN 2 -> LOW")
27 | return nil
28 | }
29 |
30 | // Setup code will be called once.
31 | func (c *Controller) Setup() error {
32 | serial.Begin(serial.BaudRate115200)
33 | serial.Println("Setting up PIN 2 -> OUTPUT")
34 | digital.PinMode(2, digital.ModeOutput)
35 | serial.Println("Done.")
36 | return nil
37 | }
38 |
--------------------------------------------------------------------------------
/examples/first/controller_test.go:
--------------------------------------------------------------------------------
1 | package device_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/andygeiss/esp32/device"
7 | "github.com/andygeiss/utils/assert"
8 | )
9 |
10 | func TestControllerSetupErrorShouldBeNil(t *testing.T) {
11 | ctrl := device.NewController()
12 | err := ctrl.Setup()
13 | assert.That("setup should return without an error", t, err, nil)
14 | }
15 |
16 | func TestControllerLoopErrorShouldBeNil(t *testing.T) {
17 | ctrl := device.NewController()
18 | err := ctrl.Setup()
19 | assert.That("setup should return without an error", t, err, nil)
20 | err = ctrl.Loop()
21 | assert.That("loop once should return withoout an error", t, err, nil)
22 | }
23 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/andygeiss/esp32
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/andygeiss/esp32-controller v0.1.0
7 | github.com/andygeiss/utils v0.9.1
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/andygeiss/esp32-controller v0.1.0 h1:o+SaEmOA3Sh3Qhxf7xqUpouE9Y39AneYEYrjdP6twAI=
2 | github.com/andygeiss/esp32-controller v0.1.0/go.mod h1:5bEu/i+U9pklziFmp1rJ8ijpz43Gzyz3jmL8+UrI+ys=
3 | github.com/andygeiss/utils v0.9.1 h1:PFFgmdwNDO2/bHA/4F7/fozifH+lPyuMBAhsBuTLGf8=
4 | github.com/andygeiss/utils v0.9.1/go.mod h1:Jatj6UsFNUMgf+9vPeMZ9il72nARlhMvc54r7AiPPiU=
5 |
--------------------------------------------------------------------------------
/main/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/andygeiss/esp32/device"
6 | "os"
7 | )
8 |
9 | func main() {
10 | ctrl := device.NewController()
11 | if err := ctrl.Setup(); err != nil {
12 | fmt.Fprintf(os.Stderr, "Error on Setup: %s", err.Error())
13 | }
14 | for {
15 | if err := ctrl.Loop(); err != nil {
16 | fmt.Fprintf(os.Stderr, "Error on Loop: %s", err.Error())
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------