MicroPython Iceboat

My daughter’s and I have built a number of air boat projects, but this time I thought that I’d try it using MicroPython.

For this project my goal was to create a MicroPython application that was a standalone WiFi access point, and then run a small web server for controlling the fans.

Hardware

The hardware that I used on this project included:

  • 3 – L9110 Fans ($5 each)
  • 1 – ESP-8266  module. (I used a Wemos D1 $10)
  • 1 – Uno proto shield (optional) or use a small breadboard
  • 9V battery or portable phone charger
  • some K-Nex blocks

Fans

The L9110 fans are designed for Arduino projects and they cost about $5. These fans have four pins: VCC, GND, INA (direction), and INB (on/off). The INA direction pin and INB on/off  pin can be a little confusing because if the direction=0 (forward spinning) then the running state is INB=0 and off is INB=1. This is reversed if the direction=1 or backward spinning.

l9110_fan

MicroPython Hardware

MicroPython is supported on a number of Wifi enabled ESP-8266 type modules. These modules are well priced with the NodeMCU modules starting around $5. It’s important to note that most of the ESP-8266 modules also support Lua programming and Arduino C.

nodemcu

For this project I used an ESP-8266 module that came in an Arduino Uno form factor. The reason for this is that the typical MicroPython modules only support 3.3V where as the  Uno compatible modules support 5V. The fans will work with 3.3V but they generate a lot more wind power at 5V.

wemo_uno

Boat Construction

For the boat frame I used K’Nex pieces because they offer a light weight sturdy construction. Some of the important points in the construction were:

  • tightly securing the fans. For this I wire wrapped the fans to the frame
  • use tape to secure the battery and electronics to the frame
  • thin runners

iceboat_details

MicroPython Setup

The uPyCraft IDE offers a nice development environment.

upycraft

There are some excellent tutorials for get you started, I would recommend : https://randomnerdtutorials.com/getting-started-micropython-esp32-esp8266/.

For MicroPython projects you typically create 2 Python applications, a boot.py and a main.py . I like to think of this like in Arduino where you have a setup() function and a loop() function. For this project I kept things simple and I put everything in the boot.py file.

Creating an Access Point

There is lots of documentation on how to have MicroPython connect to a Wifi LAN, but not a lot on how to create a standalone WiFi access point. The code that I used for this is:

# MicroPython boot.py - Access Point Only Example
try:
  import usocket as socket
except:
  import socket
import network

station = network.WLAN(network.AP_IF)
station.active(True)
station.config(essid='ESP32')
station.config(authmode=3,password='12345678')

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

When the code is run you will be able to see when you connect and disconnect to the access point. For this example the access point is call ESP32 with a password of 12345678.

access_point

MicroPython Web Server

After getting the Access Point working the next step is to create a Web server. This is a simple Web server so I embedded the HTML content into my Python code, however for a more complex application I would definitely have my web pages as files that are independent from the code.

# MicroPython boot.py - Access Point Web Server

try:
  import usocket as socket
except:
  import socket

import network

station = network.WLAN(network.AP_IF)
station.active(True)
station.config(essid='ESP32')
station.config(authmode=3,password='12345678')

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

def web_page(request):
  
  fans_state = ""
  if request.find('/?forward') > 0:
    fans_state="Going Forward"
  if request.find('/?Stopped') > 0:
    fans_state="Stopped" 
  
  html = """<html><head> <title>Ice Boat Web Server</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>
  html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}
  .button{display: inline-block; background-color: #e7bd3b; border: none; 
  border-radius: 4px; color: white; text-decoration: none; font-size: 30px; width:100%}
  </style></head>
  <body> <h1>Ice Boat Web Server</h1> 
  <p>ICE Boat : <strong>""" + fans_state + """</strong></p>
  <p><a href='/?forward'><button class="button">Forward</button></a></p>
  <p><a href='/?stop'><button class="button button">STOP</button></a></p>
  
  </body></html>"""
  
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  request = str(request)
  print('The Content = %s' % request)
  response = web_page(request)
  conn.send(response)
  conn.close()

For the Web Server example application, we start with the access point connection code, then we setup a socket on port 80. The HTTP request/response sequence is passed through a function called web_page(request).  In web_page(request) we look for keywords in the HREF request. 

The embedded HTML code passes keywords using anchor tags, for example: <a href=’/?forward’>. Often we use mobile frameworks like Bootstrap to help with formatting, however because we’re running a standalone access point we need to manually define all our style codes.

To access our MicroPython web server, use the address: 192.168.4.1. This is the default address (in the access point setup you can change this). When our web server is running we should be able to toggle the stop/forward states.

web_server_screen0

Writing Outputs

The MicroPython command line interface is good way to test outputs. To access the command line interface, use the connect button and then enter “Control-C”.

To manage pins on the hardware, machine library is used :

from machine import Pin

Then pin objects can be defined as either inputs or outputs:

Pin14 = Pin(14, Pin.IN)
Pin5 = Pin(5, Pin.OUT)

The value of a pin is read using: pinobject.value()

motor_test

Final Application

Now that we have all the pieces working we can pull it all together. The fan control code was a little messy because 2 fans were front facing and spinning forward, and 1 fan was facing backwards spinning in reverse. With the back fan I needed to reverse the direction and its on/off settings.

# MicroPython boot.py - Access Point Web Server
import time
try:
  import usocket as socket
except:
  import socket

from machine import Pin
import network

# Define the left, right and back fans pin
motorR = Pin(5,Pin.OUT)
motorL = Pin(12,Pin.OUT)
motorB = Pin(4,Pin.OUT)
motorBdir = Pin(16,Pin.OUT) # sets the back motor direction
# start with fans stopped, Note: my FANs are 0=run
motorR.value(1)
motorL.value(1)
motorB.value(0)
motorBdir.value(0)

station = network.WLAN(network.AP_IF)
station.active(True)
station.config(essid='ESP32')
station.config(authmode=3,password='12345678')

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

def fancontrol(left,right,back):
  motorL.value(left)
  motorR.value(right)
  motorB.value(back)

def web_page(request):
  
  fans_state = "Stopped"
  if request.find('/?forward') > 0:
    fans_state="Going Forward"
    fancontrol(0,0,1)
  if request.find('/?left') > 0:
    fans_state="Going Left"
    fancontrol(0,1,1)
  if request.find('/?right') > 0:
    fans_state="Going Right" 
    fancontrol(1,0,1)
  if request.find('/?stop') > 0:
    fans_state="Stopped"
    fancontrol(1,1,0)
  
  html = """<html><head> <title>Ice Boat Web Server</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>
  html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}
  .button{display: inline-block; background-color: #e7bd3b; border: none; 
  border-radius: 4px; color: white; text-decoration: none; font-size: 30px; width:100%}
  .button2{background-color: #4286f4; width:49%}
  </style></head>
  <body> <h1>Ice Boat Web Server</h1> 
  <p>ICE Boat : <strong>""" + fans_state + """</strong></p>
  <p><a href='/?forward'><button class="button">Forward</button></a></p>
  <p><a href='/?left'><button class="button button2">LEFT</button></a>
  <a href='/?right'><button class="button button2" >RIGHT</button></a></p>
  <p><a href='/?stop'><button class="button button">STOP</button></a></p>
  
  </body></html>"""
  
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  request = str(request)
  print('The Content = %s' % request)
  response = web_page(request)
  conn.send(response)
  conn.close()

ice_boat_page

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s