Morse Code Glove

Overview shot of Teensy and Glove The glove and wrist pieces

A while back I created a glove that “spoke” morse code. I finally finished doing a little write-up on that project /projects/morse-code-glove/

Tmux Cheatsheet

TmuxCheatsheet-0.1.png

A cross-platform command-line loader for WifiMCU

Detail photo of the WifiMCU board

I wrote this article as an introduction to the WifiMCU chip and to explain some of the troubleshooting thought process. If you want to get right to the code, jump to the finished product

A little background

I came across the WifiMCU in my latest binge on inexpensive eBay electronics. Following my recent obsession with Arduino’s I wanted to find something wifi enabled but all of the Arduino “sheilds” I found were just too expensive for just playing around. For example the CC3000. Nothing against Adafruit, I love their store, I’m just really cheap.

Soon enough my searching turned up the “new” EM3165 module. These are cheap. The bare module along retails around $8.00. Granted this is a system on a chip (SoC) with a “self hosted wifi library” - you get a lot built in - but it is still pretty far out of my abilities.

…Then i found the WifiMCU…

The WifiMCU is a $13.00 development board based on the EM3165 chip. What you get with this is essentially a platform with all of the power of the EM3165 chip that you can command program over a usb connector. The other fun bonus is that you get a lot of gpio pins on this so instead of using this as a peripheral to your arduino, it can actually do a lot of th hardware controlling. The guys at WifiMCU have done a lot of hard work to make this happen.

So with little hesitation I ordered the WifiMCU module.

This is not an Arduino

So just to be clear this is “arduino-like” in the fact that it is a chip on a board with pins that you can control. You can read inputs and send outputs but this is not programmed like an Arduino.

The WifiMCU is loaded with Lua, a wholly different and much simpler language. This project is still in the beginning stages but you can see they’ve built up a really impressive list of commands already.

Here is the other difference. With an arduino you upload your compiled executable through the Arduino studio (or other software) to be run exclusively when the arduino reboots. The WifiMCU isn’t like that. You don’t “reprogram” it, you run scripts on it. As a result it’s “main program” is always the Lua script interpreter. The way you get it to “run something on boot” is by naming your file init.lua.

So cool, just write up my script and upload it over the serial (USB) interface…

Nope…

Currently, the only way to “upload’ a file is with the WifiMCU Studio which is a Windows only app. I’ve only got a Mac.

This chip is too cool to just chuck in the bin, so I’m going to work around this.

So how does it work?

After a back and forth with the developers, I found that the way they “upload” files in the WifiMCU Studio is simply to use the native lua commands on the chip to open the file and write it to memory. What this means is that whenever you want to upload or download a file from the chip you are essentially “typing in” the code when you save it and echoing out the text when you want to save it.

You can use a serial tool conneting to wifimcu at 115200,n,8,1 then type the above 3 commands indivaully. The 3 lines comands will create a file named “test.lua”, and the file will be stored in the embeded flash inside wifimcu. This is the uploading procedure. WiFiMCU STUDIO does the same thing. I hope this may help you!

file.open("test.lua","w+")
file.writeline("print('hello world1')");
file.close()

Hacking the code

Day One

I got the initial email from the vendor. I decided I really want to keep this as basic as possible so my initial set of commands will be “get”, “put”, “delete” and “list” - the very basics needed to manipulate files. I’m going to focus on making a very solid and minimal client and then potentially build on that if needed.

While I don’t want to get too elaborate I do want to “engineer” this a little. I’m deciding to use Go for the programming language, the main factor here is that i can make cross platform executables without a very complicated build environment.

I found the tarm/serial library which sounds like it should be pretty portable to Windows, Mac, Linux, etc. so this is where I’ll start.

I will probably use spf13/cobra for the cli since I’ve had really good luck with this in the past

Day Two

Spent a few minutes gathering my thoughts and writing up the previous material. I think I have a good enough mental model built up to start coding soon.

Day Three

Having some difficulties with serial connectivity on my Mac so i setup a dev environment in Linux under Virtual Box

Started working on the code. Got the project folder setup and I’m testing the example code from tamr/serial. I had to make a few changes but the initial serial communications back and forth is working. It’s all downhill from here

Here is the testing code

package main

import "github.com/tarm/serial"
import "log"

func main() {
	c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 115200}
	s, err := serial.OpenPort(c)
	if err != nil {
		log.Fatal(err)
	}

	_, err = s.Write([]byte("print(mcu.info())\n"))
	if err != nil {
		log.Fatal(err)
	}

	buf := make([]byte, 128)
	r, err := s.Read(buf)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("%#v", string(buf[:r]))

	buf = make([]byte, 128)
	r, err = s.Read(buf)
	if err != nil {
		log.Fatal(err)
	}	
	log.Printf("%#v", string(buf[:r]))
}

Day Four

Now that the basic concept has been proven, i spent a little time building up some low level functions. First i built read and write functions to avoid future code duplication

func readSerial(p serial.Port) string {
	buf := make([]byte, 1024)
	r, err := p.Read(buf)
	if err != nil {
		 log.Printf("Can't read from serial port\n")
		 log.Fatal(err)
	 }
	
	return strings.TrimSpace(string(buf[:r]))
}

func writeSerial(p serial.Port, s string) (err error){
	_, err = p.Write([]byte(s + "\n"))
	if err != nil {
		log.Printf("Can't send command\n")
		log.Fatal(err)
	}
	return err
}

Next, using these primitives I built up a “command” routine to send a command and retrieve it’s result. Currently, we aren’t trapping for errors but this will be done in the future.

func sendCommand(p serial.Port, cmd string) (out string, err error) {
	err = writeSerial(p, cmd)
	_ = readSerial(p)
	out = readSerial(p)
	return out, err
}

From here, things build up quick. I was able to put together a higher level command to upload a file. This isn’t quite working yet so i need to do some debugging

func uploadFile(p serial.Port, filename string) (err error) {
	log.Printf("Uploading file: %s\n", filename)
	_, destFileName := filepath.Split(filename)
	log.Printf("Destination name: %s\n", destFileName)

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	
	log.Printf("Writing file...\n")
	_, _ = sendCommand(p, fmt.Sprintf("file.open('%s', 'w+')", destFileName))
	
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		log.Print(scanner.Text())
		//_, _ = sendCommand(p, fmt.Sprintf("file.write(\"%s\")", scanner.Text()))
		fmt.Sprintf("file.write(\"%s\")", scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	_, _ = sendCommand(p, "file.close()")

	return err
}

Day Five

So the code is getting to the point where we need a little structure so we’ll pull in spf13/cobra and rough in our UI. This will help to keep our code clean

Here is a quick example of the relevant parts. I built out skeleton’s for the commands that we’ll fill in as we go

func main() {
	var rootCmd = &cobra.Command{}
	rootCmd.AddCommand(cmdVersion)
	rootCmd.Execute()
}
var cmdVersion = &cobra.Command{
	Use: "version",
	Short: "Get the current version",
	Run: func(cmd *cobra.Command, args []string) {
		s := openSerial()
		res, err := sendCommand(*s, "print(mcu.ver())")
		if err != nil {
			log.Printf("Comm error")
			log.Fatal(err)
		}
		log.Printf("Result: %s", res)
	},
}

Day Six

Spent a lot of time working on the serial read/writing code. The way the serial prompt was designed it was really made for interactive use. I’m trying a few different ways to “flush” the serial line so that when I issue a command i get only the results. Right now I’m sometimes getting the command results and sometimes getting an echo of the command itself. I’ll need a little more work on this…

I think i finally got it working so now I’m wrapping up the command “interface”. The last piece of the puzzle is adding some configuration around the serial port, update the readme, cross compile and release an alpha version.

Final command set

  • ls - list files on the device
  • rm [filename] - remove a file
  • ver - get the device version
  • put [filename] - send a file to the device
  • config - show the current app configuration
  • version - the version of this app

Configuring the serial port

For configuration I’m using spf13/viper. It’s a little overkill for just the one configuration option (WMC_SERIAL) but it will allow for easy expansion in the future

Cross-compiling

For the newest versions of Go cross-compiling is a snap. Simply set the GOOS and GOARCH environment variables and Go takes care of the rest. Unfortunately, the underlying tamr/serial code doesn’t appear to support all platforms, so we are a bit limited. It’s opensource, however so we can always contribute changes in the future

The finished product

Okay big disclaimer here, at this point the code has had maybe fifteen minutes of testing. This is not a robust code library at this point, just a quick hack. Use at your own risk. If you do discover a bug please report them to the “issues” page on github. Pull requests are welcome!