TinyGo on Arduino Uno: An Introduction

TinyGo on Arduino Uno: An Introduction © CC BY-NC-SA

Run Golang on this old but still popular 8-bit AVR microcontroller.

  • 2,492 views
  • 1 comment
  • 4 respects

Components and supplies

Apps and online services

About this project

Note: This is also some kind of a self-learning notes, since I (an amateur maker myself) never have learned Go before. If I made any mistakes or had misunderstandings feel free (politely) to give me feedback. I will also update the content if there are big changes in the future release.

TinyGo - Golang for microcontrollers

TinyGo - "Go for small places", now officially sponsored by Google - is one of the new programming language for microcontrollers. By using a LLVM-based compiler, it can generate binary files small enough to fit into microcontrollers, including 8-bit AVR boards with very limited RAM.

Since TinyGo is mainly designed for 32-bit microcontrollers, these AVR boards have limitations - they are not able to perform some features of TinyGo and may only be able to run smaller scripts.

On the other hand, Arduino Uno and Arduino Nano are still one of the most popular beginner's Maker board, and their clones can be bought cheaply. If you accidentally toast one you won't lose much. And for some people living in certain area around the globe (like me), open-sourced Unos are far more accessible than Cortex M0/M4 or nRF52 products.

Also, Go (Golang) has became rapidly popular in recent years. Learn Go basics on an interactive physical device may be more interesting than learning solely on a computer. It's also exciting to see something else can run on Arduino Uno other than the powerful but intimidating C++. (No more problems caused by forgotten semicolons! yay!)

Uno may not be ideal TinyGo, but it is still capable to do many things.

Setup - Windows

Tested on Windows 10.

1. Download Go and install it under C:\. (The installer should automatically added "C:\Go\bin" to your system's PATH variable.)

2. Download TinyGo and unzip it to C:\. Look for tinygo0.12.0.windows-amd64.zip or later release.

3. Open Command Prompt and add TinyGo path to your system PATH variable:

set PATH=%PATH%;"C:\tinygo\bin"

You may need to restart the Command Prompt for the new PATH to take effect.

Personally, I would recommend to set PATH manually via Control Panel -> System -> Advanced System Settings -> Environment Variables.

4. Check if both paths are added correctly:

tinygo version

You should see something like

tinygo version 0.12.0 windows/amd64 (using go version go1.13.7)

5. Download avr-gcc/avrdude and unzip it to C:\. These tools are needed to upload compiled binary files to Arduino Uno.

6. Add path to PATH as well:

set PATH=%PATH%;"C:\avr-gcc-9.2.0-x64-mingw\bin";

You can also use avr-gcc.exe and avrdude.exe provided by Arduino IDE if you had it installed (search it yourself). You'll need to add path for both respectively.

Setup - Linux

Tested on a Utuntu 18.04 virtual machine and my Raspberry Pi 3B+ (Raspbian Buster).

1. Open Terminal, update and upgrade system:

sudo apt-get update
sudo apt-get dist-upgrade -y

2. Install Go:

sudo apt-get install golang-go

3. Download and unpack TinyGo (Ubuntu/Debian):

wget https://github.com/tinygo-org/tinygo/releases/download/v0.12.0/tinygo_0.12.0_amd64.deb
sudo dpkg -i tinygo_0.12.0_amd64.deb

If you are using a Raspberry Pi:

wget https://github.com/tinygo-org/tinygo/releases/download/v0.12.0/tinygo_0.12.0_armhf.deb
sudo dpkg -i tinygo_0.12.0_armhf.deb

4. Add path to bashrc:

nano ~/.bashrc

Scroll down to the end, add this line, press Ctrl+X then Y to save:

export PATH=$PATH:/usr/local/tinygo/bin

5. Restart system or execute the previous line in terminal as well (so it will take effect immediately).

6. Check installation/path:

tinygo version
tinygo version 0.12.0 linux/amd64 (using go version go1.13.6)

7. Install other required tools for Arduino Uno:

sudo apt-get install gcc-avr
sudo apt-get install avr-libc
sudo apt-get install avrdude

Upgrade TinyGo in the future

For Windows, simply delete the tinygo directory and download/unzip the new release file.

For Linux, simply repeat the wget/dpkg commands with the new release version.

Compile and upload TinyGo code

In Command Prompt (Windows) or Terminal (Linux), this is how you compile and upload the official example blinky1.go to your Uno:

tinygo flash -target arduino -port COMX examples/blinky1

On Windows, open Device Manager and see which COM port your Arduino Uno uses.

On Linux systems the port should be /dev/ttyACM0 or may be /dev/ttyUSB0.

Or, you can install Arduino IDE and look at which port is available in Tools -> Port (works on both Windows and Linux).

Target arduino is for Arduino Uno. For Arduino Nano, use target arduino-nano. Nano is basically a smaller Uno with same pins and also .ATmega328P-based.

To write your own code, for example, I added a folder on Windows

C:\Users\username\go\src\main

Or on Linux

/home/src/main

and save a text file main.go in it. Then I can compile and upload this file by the following command: (same on Windows and Linux)

tinygo flash -target arduino -port COMX main

You will see something like this if it's successful:

avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.01s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "C:\Users\username\AppData\Local\Temp\tinygo231126475\main.hex"
avrdude: writing flash (552 bytes):
Writing | ################################################## | 100% 0.14s
avrdude: 552 bytes of flash written
avrdude: verifying flash memory against C:\Users\username\AppData\Local\Temp\tinygo231126475\main.hex:
avrdude: load data flash data from input file C:\Users\username\AppData\Local\Temp\tinygo231126475\main.hex:
avrdude: input file C:\Users\username\AppData\Local\Temp\tinygo231126475\main.hex contains 552 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.11s
avrdude: verifying ...
avrdude: 552 bytes of flash verified
avrdude: safemode: Fuses OK (E:00, H:00, L:00)
avrdude done. Thank you.

You can also try out the TinyGo Playground.

Blinky: digital output, loop and delay

A pin or GPIO (general purpose input/output) can be used to control external devices, for example a LED. It can be switched on (output High voltage) or off (output Low voltage) hence making the LED light up or not.

Below is the most basic code to "blink" the built-in LED on pin 13 (very slightly modified from the official example):

package main

import (
"machine"
"time"
)

func main() {

// use pin 13, which is connected to the built-in LED
var led machine.Pin = machine.Pin(13)

// set the pin to output mode
led.Configure(machine.PinConfig{Mode: machine.PinOutput})

for {
led.High() // pull high (output power, light up led)
time.Sleep(time.Millisecond * 500) // wait 500 milliseconds
led.Low() // pull low (cut off power)
time.Sleep(time.Millisecond * 500) // wait 500 milliseconds
}
}

Blinky (alternate version)

Here we use an external LED connected to pin 13.

Be noted that pin 13 does not have a built-in resistor. A 220 ohms resistor is added between the pin and LED as protection, otherwise the LED might be damaged by higher voltage.

package main

import (
"machine"
"time"
)

func main() {

led := machine.Pin(13)
// same as var led = machine.LED

led.Configure(machine.PinConfig{1})
// 1 = output mode, 0 = input mode

led_switch := true // led switch

for {
led.Set(led_switch)
led_switch = !led_switch // invert switch
delay(500)
}
}

func delay(t int64) { // time delay function
time.Sleep(time.Duration(1000000 * t))
}

machine.LED is pre-defined in the machine module, which is equal to machine.Pin(13).

Here I also demonstrated that you can use 1 to substitute Mode: machine.PinOutput. The .Configure() method accepts a PinConfig parameter, which is a struct with a single field Mode:

type PinConfig struct {
Mode PinMode
}

Mode is in fact a uint8 number:

type PinMode uint8

const (
PinInput PinMode = iota // 0
PinOutput // 1
)

However, you might still need to use these const for readability.

You can declare a variable without specify the type if you give it a value at declaration. Here the variable led would automatically set as machine.Pin type.

:= (short variable declaration operator) is as same as var but can be only used in a function (for example, main()).

Also, I wrote a reusable delay function which make things slightly easier. The smallest unit of time.Duration (int16) is nanosecond; times 1, 000, 000 and it's a millisecond.

(In case you wondered, time.Since() doesn't work on Uno. I tried.)

Knight Rider LED array (simpler version)

Now we will make a row of 9 LEDs blink one by one, toward one and then back. Using pin 2-10.

package main

import (
"machine"
"time"
)

const max_led_num = 9 // number of leds

func main() {

var leds = [max_led_num]machine.Pin{
machine.Pin(2),
machine.Pin(3),
machine.Pin(4),
machine.Pin(5),
machine.Pin(6),
machine.Pin(7),
machine.Pin(8),
machine.Pin(9),
machine.Pin(10),
} // array of pins

for i := 0; i < len(leds); i++ { // setup leds
leds[i].Configure(machine.PinConfig{1})
}

for {

// blink one by one forward
for i := 0; i < len(leds); i++ {
leds[i].High()
delay(75)
leds[i].Low()
}

// blink one by one backward
for i := len(leds) - 1; i >= 0; i-- {
leds[i].High()
delay(75)
leds[i].Low()
}
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

Knight Rider LED array (better version)

package main

import (
"machine"
"time"
)

const (
max_led_num = 9
start_led_pin = 2
)

func main() {

delay := func (t int64) { // implicit time delay function
time.Sleep(time.Duration(1000000 * t))
}

var leds [max_led_num]machine.Pin // array of leds
index, delta := 0, 1 // blink index and direction

for i, _ := range leds { // setup leds
leds[i] = machine.Pin(i + start_led_pin)
leds[i].Configure(machine.PinConfig{1})
}

for {

// turn one the led on index and turn off the rest
for i, led := range leds {
led.Set(index == i)
}

index += delta // move index

// invert index's moving direction at each end
if index == 0 || index == len(leds) - 1 {
delta *= -1
}

delay(75)
}
}

This time delay is an implicit function declared inside main().

Set() method of LED structs accept a bool parameter (true/false). In Go you cannot convert int to bool; you'll have to use logical operators.

Actually, you don't need to use array or variables to store pins at all:

package main

import (
"machine"
"time"
)

const (
max_led_num = 9
start_led_pin = 2
)

func main() {

index, delta := 0, 1

for i := 0; i < max_led_num ; i++ {
machine.Pin(i + start_led_pin).Configure(machine.PinConfig{1})
}

for {

for i := 0; i < max_led_num ; i++ {
machine.Pin(i + start_led_pin).Set(index == i)
}

index += delta

if index == 0 || index == max_led_num - 1 {
delta *= -1
}

delay(75)
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

Button: digital input

Pins can be used to read digital signals (High/Low voltage) as well. Here we will use a push button (or switch) on pin 8 to turn on/off the LED on pin 13.

In order to switch the High/Low states, the button needs a 10K Ohms pull-up resistor.

package main

import (
"machine"
"time"
)

func main() {

// set pin 8 to read button status (input mode)
button := machine.Pin(8)
button.Configure(machine.PinConfig{0})
// same as machine.PinConfig{Mode: machine.PinInput}

led := machine.Pin(13)
led.Configure(machine.PinConfig{1})

for {
// light up led when the button is pressed (read Low)
led.Set(!button.Get())
delay(50)
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

Get() method returns a bool value (true/false).

TinyGo does not support Uno's internal pull-up resistor yet (which allows you to use only 2 wires and no external resistors to read status of a button).

PWM: analog output

PWM (Pulse Width Modulation) is to turn on/off the pin at certain percentage of the time to generate a specific voltage between Low and High (5V on Uno), which can be used to change brightness level of a LED or change the speed of a DC motor (via a motor board).

On Uno (and Nano) only pin 3, 5, 6, 9, 10 and 11 (with the ~ mark beside it) can be used to output PWM.

This example makes the LED on pin 9 "breathes" - light up and fade gradually, The circuit is as same is the blinky code.

package main

import (
"machine"
"time"
)

func main() {

machine.InitPWM() // initialize PWM
led := machine.PWM{9} // set PWM pin
led.Configure() // start the pin's PWM

duty, delta := 0, 1024

for {

led.Set(uint16(duty)) // set PWM duty cycle
duty += delta

if duty < 0 || duty > 65535 {
delta *= -1
duty += delta
}

delay(25)
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

PWM's Set() method accepts a uint16 parameter (0-65535), which is the PWM duty cycle. However, Unos only have 8-bit PWM resolution, so smaller changes may not be visible.

Right now it's not supported to change the board's PWM frequency, which is useful to control passive piezo buzzers and hobby servo motors.

Sensor: analog input

Some of the pins on Arduino Uno have ADC (Analog-to-Digital Converter), which allows them to read voltage as numbers.

Here we use a LDR (photoresistor) to output analog readings to pin A0 based on light levels and turn on/off the LED on pin 13. In order to read the changes of voltage, we'll need a voltage divider circuit with a 10K Ohms resistor.

package main

import (
"machine"
"time"
)

func main() {

machine.InitADC() // initialize ADC
ldr := machine.ADC{0} // set ADC pin
// same as machine.ADC{machine.ADC0}
ldr.Configure() // start the pin's ADC function

led := machine.Pin(13)
led.Configure(machine.PinConfig{1})

for {
// output analog reading to serial port
print(ldr.Get())
// light up led if analog reading is over the threshold
led.Set(ldr.Get() > 40000)
delay(100)
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

There are pre-defined analog pins in the machine module as machine.ADC0 to machine.ADC5.

As PWM, ADC's Get() method returns a uint16 value (0-65535).

Here I set the threshold as 40000. Actual threshold may be different depending on your lighting condition. You can use a terminal software like Tera Term or Arduino IDE's serial monitor to read the output value from print().

Using analog pins as digital output

Analog pins can actually be used as digital output pins as well. A0 to A5 are in fact pin 14 to 19:

UART: serial port communication

UART (universal asynchronous receiver-transmitter) is the serial port, which allows you to transmit and receive data with external devices, like other microcontrollers or computers.

The following code is also very-slightly modified from the official example:

package main

import (
"machine"
"time"
)

var (
uart = machine.UART0 // hardware serial port
tx = machine.UART_TX_PIN // 1, transmit line
rx = machine.UART_RX_PIN // 0, receive line
)

func main() {

uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// same as machine.UARTConfig{9600, 1, 0}
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))

input := make([]byte, 64) // serial port buffer
i := 0

for {

if uart.Buffered() > 0 {

data, _ := uart.ReadByte() // read a character

switch data {
case 13: // pressed return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default: // pressed any other key
uart.WriteByte(data)
input[i] = data
i++
}
}

time.Sleep(10 * time.Millisecond)
}
}

The code read one character at a time from the serial port, store them in a byte array, and when the user pressed Enter key (ASCII code 13) it would print the whole text back to serial port and reset the array index.

Software serial port is not supported yet.

UART with Go: synchronized time display

Here I decided to try something a bit more difficult for beginners. To use TinyGo, you have to install Go well. Why not use them together?

The goal is to read my computer's system time via serial port and let Arduino Uno show then on a 4-digit 7-segment display.

I used a common anode version. The display has 12 pins, 4 or them (D1 to D4) give power to a specific digit (by pulling High), and the rest of them (A to G) controls which led of the digit would light up (by pulling Low). To save power and add protection 220 ohms resisters are added between A-G pins and the display. For common cathode version the pull high/low direction are reversed.

  • D1: pin 2
  • D2: pin 3
  • D3: pin 4
  • D4: pin 5
  • A: pin 6
  • B: pin 7
  • C: pin 8
  • D: pin 9
  • E: pin 10
  • F: pin 11
  • G: pin 12
  • decimal: pin 13

The display shows number by cycling through digits very quickly (multiplexing), so due to persistence of vision you won't see it flicker.

-Code for Arduino Uno (displaytime.go)

// TinyGo code on Arduino Uno

package main

import (
"machine"
"time"
)

var (
input_time = make([]byte, 4)
leds [12]machine.Pin
)

func main() {

uart := machine.UART0
uart.Configure(machine.UARTConfig{9600, 1, 0})
input := make([]byte, 4)
index := 0

for i := 0; i < 12; i++ {
leds[i] = machine.Pin(i + 2)
leds[i].Configure(machine.PinConfig{1})
leds[i].Low()
}

for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
input[index] = data // read 1 characters at a time
if index == 3 { // if 4 characters read
input_time = input // update the display data
index = 0
} else {
index++
}
}
showNumber() // display time
}
}

func delay(t int64) {
time.Sleep(time.Duration(1000000 * t))
}

func showNumber() {

for n := 0; n < 4; n++ { // cycle through 4 digits

for i := 4; i < 12; i++ { // reset leds
leds[i].High()
}

for d := 0; d < 4; d++ { // power up the digit
leds[d].Set(d == n)
}

// light up specific number
// not really elegant, but easier to understand
switch rune(input_time[n]) {
case '0':
leds[4].Set(false) // false is light up
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(false)
leds[8].Set(false)
leds[9].Set(false)
leds[10].Set(true)
case '1':
leds[4].Set(true)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(true)
leds[8].Set(true)
leds[9].Set(true)
leds[10].Set(true)
case '2':
leds[4].Set(false)
leds[5].Set(false)
leds[6].Set(true)
leds[7].Set(false)
leds[8].Set(false)
leds[9].Set(true)
leds[10].Set(false)
case '3':
leds[4].Set(false)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(false)
leds[8].Set(true)
leds[9].Set(true)
leds[10].Set(false)
case '4':
leds[4].Set(true)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(true)
leds[8].Set(true)
leds[9].Set(false)
leds[10].Set(false)
case '5':
leds[4].Set(false)
leds[5].Set(true)
leds[6].Set(false)
leds[7].Set(false)
leds[8].Set(true)
leds[9].Set(false)
leds[10].Set(false)
case '6':
leds[4].Set(true)
leds[5].Set(true)
leds[6].Set(false)
leds[7].Set(false)
leds[8].Set(false)
leds[9].Set(false)
leds[10].Set(false)
case '7':
leds[4].Set(false)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(true)
leds[8].Set(true)
leds[9].Set(true)
leds[10].Set(true)
case '8':
leds[4].Set(false)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(false)
leds[8].Set(false)
leds[9].Set(false)
leds[10].Set(false)
case '9':
leds[4].Set(false)
leds[5].Set(false)
leds[6].Set(false)
leds[7].Set(true)
leds[8].Set(true)
leds[9].Set(false)
leds[10].Set(false)
default:
leds[4].Set(true)
leds[5].Set(true)
leds[6].Set(true)
leds[7].Set(true)
leds[8].Set(true)
leds[9].Set(true)
leds[10].Set(true)
}

// light up second decimal to separate hour and minute
leds[11].Set(n != 1)

delay(5)
}
}

Upload the code (C:\Users\username\go\src\displaytime\displaytime.go) onto the Arduino Uno (in my case it's on COM15):

tinygo flash -target arduino -port COM15 displaytime

-Code for Go on computer (gettime.go; only tested on Windows 10)

First you'll need to download the go-serial package (https://github.com/jacobsa/go-serial) in the Command Prompt or Terminal. You'll need to install Git first and set PATH in order to use "go get":

go get github.com/jacobsa/go-serial/serial

Now the code:

// Go code on computer

package main

import (
"fmt"
"time"
"log"
"github.com/jacobsa/go-serial/serial"
)

func main() {

options := serial.OpenOptions{ // setup serial port
PortName: "COM15",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
}

port, err := serial.Open(options)

if err != nil {
log.Fatalf("serial.Open: %v", err)
}

defer port.Close()

var (
currentTime time.Time
output []byte
)

for {

currentTime = time.Now()
// get formatted time (hour + minute)
output = []byte(currentTime.Format("1504"))

fmt.Println("Time: ", string(output))

_, err = port.Write(output) // send data to serial port

if err != nil {
log.Fatal(err)
}

time.Sleep(time.Second) // wait for 1 second

}
}

In Go you can format time by using reference time Mon Jan 2 15:04:05 -0700 MST 2006. 15 represents hour and 04 is minute. Together the four characters would be sent to the serial port and pick up by the Uno.

Finally run the code (C:\Users\username\go\src\gettime\gettime.go):

go run gettime

Now we have a physical time display powered by Go + TinyGo! You can stop the Go program by pressing Ctrl+C.

I2C: Inter-Integrated Circuit bus

I2C is supported on Arduino Uno. However, due to the RAM limit, you may only be able to run the most basic drivers.

Here I'll demonstrate the only working device I have on hand - BH1750 illuminance sensor.

  • VCC: 3.3V or 5V
  • GND: ground
  • SCL: pin A5
  • SDA: pin A4
package main

import (
"machine"
"time"
)

const ( // device address and some of the registers
ADDRESS = 0x23
POWER_DOWN = 0x00
POWER_ON = 0x01
ONE_TIME_HIGH_RES_MODE = 0x20 // requires 120 ms delay
ONE_TIME_HIGH_RES_MODE_2 = 0x21 // requires 120 ms delay
ONE_TIME_LOW_RES_MODE = 0x23 // requires 24 ms delay
)

func main() {

i2c := machine.I2C0 // hardware I2C
i2c.Configure(machine.I2CConfig{}) // start hardware I2C bus
i2c.WriteRegister(ADDRESS, POWER_ON, nil) // BH1750 power on

for {

// buffer to receive raw data
buf := []byte{1, 0}
// read raw data from BH1750
i2c.ReadRegister(ADDRESS, ONE_TIME_HIGH_RES_MODE_2, buf)
// calculate illuminance
lux := ((uint32(buf[0]) << 24) | uint32(buf[1]) << 16) / 78642
// output result
println("Illuminance:", lux, "lx")
time.Sleep(time.Millisecond * 250)

}
}

You can read the data output in a terminal software:

TinyGo drivers library

There is a official Github repo TinyGo Drivers which provides more than two dozen drivers for various devices. Again, many of them are designed for ARM boards (and their built-in sensors, wireless modules or displays) and may not work on Uno's tiny brain.

At least, I can confirm that the SSD1306 OLED display and WS2812 (NeoPixel) driver causes out of memory runtime error on Uno, whereas they can run smoothly on my BBC micro:bit.

However, there are countless Arduino drivers that work for Uno written by people all around the world. We can surely hope that Unos will get a similar TinyGo support community in the near future.

If you have Git installed, you can download current TinyGo drivers in the Command Prompt or Terminal:

go get tinygo.org/x/drivers

In my case it would be downloaded to (on Windows)

C:\Users\username\go\src\tinygo.org\x\drivers

Or on Linux

/home/scr/tinygo.org/x/drivers

Below is the official example code for BH1750, which uses the TinyGo BH1750 driver:

package main

import (
"time"
"machine"
"tinygo.org/x/drivers/bh1750"
)

func main() {

machine.I2C0.Configure(machine.I2CConfig{})
sensor := bh1750.New(machine.I2C0)
sensor.Configure()

for {

lux := sensor.Illuminance()
println("Illuminance:", lux, "lx")
time.Sleep(500 * time.Millisecond)

}

}

How about SPI?

Right now SPI (Serial Peripheral Interface Bus) is not yet supported on Arduino Uno.

So what projects can we do with Arduino Uno and TinyGo (for now)?

Still a lot. There are many cheap sensors and devices only require simple pin input/output. Let me give you some examples.

Example 1: use a laser pen, a LDR light sensor module (which can be connected directly without a voltage divider circuit) and an active piezo buzzer to make a simple laser target game.

Example 2: use a HC-SR501 human body IR sensor and a relay module to automatically turn on/off lights when you pass by.

Example 3: use a soil moisture sensor and a relay module with a water pump motor to monitor and take care of your plant.

Example 4: use a L9110s motor board and 2-3 TCRT5000 IR reflective modules on a two-wheeled chassis to make a automatic line-following car.

Example 5: use a ADXL335 accelerometer module (which output analog readings to indicate ±3g) and LEDs to indicate if the device is sitting on a leveled surface.

Example 6: use Go to query some web service API, get your local probability of precipitation (chance of raining) or air pollution level, send it over the serial port and display on a 4-digit 7-segment display.

Example 7: use a smoke sensor which will send data from Arduino Uno to your computer. The Go program running on your computer will send you email via IFTTT if the reading gets way too high.

Now, go make something!

Bonus: N-Queen puzzle

The code below can solve the N-queen puzzle on Arduino Uno by using a one-dimensional array and recursive algorithm. Output results via serial port.

Const maxQueens is the maximum number of queens on the chess board (in my test it can go as high as 11 or 12, which take quite some time) and printOut is the option to print out every result (it would run a lot faster if you set it to false).

package main

const maxQueens int8 = 8
const printOut = true

var (
queens = make([]int8, maxQueens)
counter int = 0
)

func main() {

println("Calculating", maxQueens, "queens puzzle...")
placeQueen(0)
println(counter, " result(s)")
}

func placeQueen(columnPos int8) {
if columnPos >= maxQueens {
counter++
if printOut {
print(counter, " [")
for i := 0; i < int(maxQueens); i++ {
print(queens[i] + 1)
if i < int(maxQueens) - 1 {
print(", ")
}
}
print("]\n")
}
} else {
for i := 0; i < int(maxQueens); i++ {
if verifyPos(columnPos, int8(i)) {
queens[columnPos] = int8(i)
placeQueen(columnPos + 1)
}
}
}
}

func verifyPos(checkPos int8, newPos int8) bool {
for i := 0; i < int(checkPos); i++ {
if (queens[i] == newPos) || (abs(checkPos - int8(i)) == abs(queens[i] - newPos)) {
return false
}
}
return true
}

func abs(x int8) int8 {
if x < 0 {
return -x
} else {
return x
}
}
Calculating 8 queens puzzle...
1 [1, 5, 8, 6, 3, 7, 2, 4]
2 [1, 6, 8, 3, 7, 4, 2, 5]
3 [1, 7, 4, 6, 8, 2, 5, 3]
4 [1, 7, 5, 8, 2, 4, 6, 3]
...
90 [8, 2, 5, 3, 1, 7, 4, 6]
91 [8, 3, 1, 6, 2, 5, 7, 4]
92 [8, 4, 1, 3, 6, 2, 7, 5]
92 result(s)

Code

TinyGo
Go compiler for small places. Microcontrollers, WebAssembly, and command-line tools. Based on LLVM. https://tinygo.org

Comments

Similar projects you might like

Stepper - A First Introduction to Nema 17

by Ingo Lohs

  • 16,544 views
  • 7 comments
  • 66 respects

Multifunctional Watch With Arduino Uno

Project showcase by shaqibmusa94

  • 4,830 views
  • 2 comments
  • 9 respects

Control Arduino Uno Using ESP8266 WiFi Module and Blynk App

Project tutorial by Adithya TG

  • 49,488 views
  • 14 comments
  • 47 respects

3-Bit Binary Calculator Using Arduino Uno

Project showcase by 22warehamD

  • 6,014 views
  • 9 comments
  • 13 respects
Add projectSign up / Login