Implemented web server that uses the official Arduino command line to upload sketches
parent
43b8b03dbc
commit
89d2147ea9
|
@ -36,13 +36,17 @@ The preffered way is to put the BlocklyDuino/web folder into a web server and op
|
|||
### Integrated Arduino upload
|
||||
|
||||
To avoid the tedious step of manually pasting code to the Arduino IDE, you can run a mini webserver that uses
|
||||
[ino](https://github.com/gumbypp/ino) to upload the code to a connected Arduino board on Mac OS X and Linux systems.
|
||||
the [Arduino IDE](https://www.arduino.cc/en/Main/Software) to upload the code to a connected Arduino board on Windows, Mac OS X and Linux systems.
|
||||
Invoke this command from the BlocklyDuino root folder:
|
||||
|
||||
```
|
||||
python ino_web_server.py
|
||||
python arduino_web_server.py
|
||||
```
|
||||
|
||||
You can optionally specify the port with `--port=COM3` (or `--port=/dev/tty.foo` on Linux and Mac); if you don't, it will try and guess which port to use.
|
||||
|
||||
When the webserver is running, you can access BlocklyDuino itself on [http://127.0.0.1:8080/](http://127.0.0.1:8080/).
|
||||
|
||||
### Usage
|
||||
|
||||
1. Open browser to BlocklyDuino, drag and drop blocks to make an Arduino program
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# credit: http://sheep.art.pl/Wiki%20Engine%20in%20Python%20from%20Scratch
|
||||
|
||||
|
||||
import BaseHTTPServer
|
||||
import SimpleHTTPServer
|
||||
import itertools
|
||||
import logging
|
||||
import platform
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import urllib
|
||||
from optparse import OptionParser
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
arduino_cmd = None
|
||||
def get_arduino_command():
|
||||
"""Attempt to find or guess the path to the Arduino binary."""
|
||||
global arduino_cmd
|
||||
if not arduino_cmd:
|
||||
if platform.system() == "Darwin":
|
||||
arduino_cmd_guesses = ["/Applications/Arduino.app/Contents/MacOS/Arduino"]
|
||||
elif platform.system() == "Windows":
|
||||
arduino_cmd_guesses = [
|
||||
"c:\Program Files\Arduino\Arduino.exe",
|
||||
"c:\Program Files (x86)\Arduino\Arduino.exe"
|
||||
]
|
||||
else:
|
||||
arduino_cmd_guesses = []
|
||||
|
||||
for guess in arduino_cmd_guesses:
|
||||
if os.path.exists(guess):
|
||||
logging.info("Found Arduino command at %s", guess)
|
||||
arduino_cmd = guess
|
||||
break
|
||||
else:
|
||||
logging.info("Couldn't find Arduino command; hoping it's on the path!")
|
||||
arduino_cmd = "arduino"
|
||||
return arduino_cmd
|
||||
|
||||
|
||||
def guess_port_name():
|
||||
"""Attempt to guess a port name that we might find an Arduino on."""
|
||||
portname = None
|
||||
if platform.system() == "Windows":
|
||||
import _winreg as winreg
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM")
|
||||
# We'll guess it's the last COM port.
|
||||
for i in itertools.count():
|
||||
try:
|
||||
portname = winreg.EnumValue(key, i)[1]
|
||||
except WindowsError:
|
||||
break
|
||||
else:
|
||||
# We'll guess it's the first non-bluetooth tty. or cu. prefixed device
|
||||
ttys = [filename for filename in os.listdir("/dev")
|
||||
if (filename.startswith("tty.") or filename.startswith("cu."))
|
||||
and not "luetooth" in filename]
|
||||
ttys.sort(key=lambda k:(k.startswith("cu."), k))
|
||||
if ttys:
|
||||
portname = "/dev/" + ttys[0]
|
||||
logging.info("Guessing port name as %s", portname)
|
||||
return portname
|
||||
|
||||
|
||||
parser = OptionParser()
|
||||
parser.add_option("--port", dest="port", help="Upload to serial port named PORT", metavar="PORT")
|
||||
parser.add_option("--board", dest="board", help="Board definition to use", metavar="BOARD")
|
||||
parser.add_option("--command", dest="cmd", help="Arduino command name", metavar="CMD")
|
||||
|
||||
|
||||
class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
def do_HEAD(self):
|
||||
"""Send response headers"""
|
||||
if self.path != "/":
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", "text/html;charset=utf-8")
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
"""Send page text"""
|
||||
if self.path != "/":
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
else:
|
||||
self.send_response(302)
|
||||
self.send_header("Location", "/blockly/apps/blocklyduino/index.html")
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
"""Save new page text and display it"""
|
||||
if self.path != "/":
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self)
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
if length:
|
||||
text = self.rfile.read(length)
|
||||
|
||||
print "sketch to upload: " + text
|
||||
|
||||
dirname = tempfile.mkdtemp()
|
||||
sketchname = os.path.join(dirname, os.path.basename(dirname)) + ".ino"
|
||||
f = open(sketchname, "wb")
|
||||
f.write(text + "\n")
|
||||
f.close()
|
||||
|
||||
print "created sketch at %s" % (sketchname,)
|
||||
|
||||
# invoke arduino to build/upload
|
||||
compile_args = [
|
||||
options.cmd or get_arduino_command(),
|
||||
"--upload",
|
||||
"--port",
|
||||
options.port or guess_port_name(),
|
||||
]
|
||||
if options.board:
|
||||
compile_args.extend([
|
||||
"--board",
|
||||
options.board
|
||||
])
|
||||
compile_args.append(sketchname)
|
||||
|
||||
print "Uploading with %s" % (" ".join(compile_args))
|
||||
rc = subprocess.call(compile_args)
|
||||
|
||||
if not rc == 0:
|
||||
print "arduino --upload returned " + `rc`
|
||||
self.send_response(400)
|
||||
else:
|
||||
self.send_response(200)
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
else:
|
||||
self.send_response(400)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "Blocklyduino can now be accessed at http://127.0.0.1:8080/"
|
||||
server = BaseHTTPServer.HTTPServer(("127.0.0.1", 8080), Handler)
|
||||
server.pages = {}
|
||||
server.serve_forever()
|
|
@ -237,7 +237,7 @@ function uploadCode(code, callback) {
|
|||
function uploadClick() {
|
||||
var code = document.getElementById('content_arduino').value;
|
||||
|
||||
alert("Ready to upload to Arduino.\n\nNote: this only works on Mac OS X and Linux at this time.");
|
||||
alert("Ready to upload to Arduino.");
|
||||
|
||||
uploadCode(code, function(status, errorInfo) {
|
||||
if (status == 200) {
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# credit: http://sheep.art.pl/Wiki%20Engine%20in%20Python%20from%20Scratch
|
||||
|
||||
import BaseHTTPServer, urllib, re, os
|
||||
|
||||
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
template = u"""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd"><html><body><h1>Arduino INO web server</h1>To upload to an Arduino board connected to this computer, POST to /.</body></html>"""
|
||||
|
||||
def escape_html(self, text):
|
||||
"""Replace special HTML characters with HTML entities"""
|
||||
return text.replace(
|
||||
"&", "&").replace(">", ">").replace("<", "<")
|
||||
|
||||
def do_HEAD(self):
|
||||
"""Send response headers"""
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", "text/html;charset=utf-8")
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
"""Send page text"""
|
||||
self.do_HEAD()
|
||||
self.wfile.write(self.template)
|
||||
|
||||
def do_POST(self):
|
||||
"""Save new page text and display it"""
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
if length:
|
||||
text = self.rfile.read(length)
|
||||
|
||||
print "sketch to upload: " + text
|
||||
|
||||
# create ino project (if it doesn't exist already)
|
||||
os.system("mkdir ino_project")
|
||||
os.chdir("ino_project")
|
||||
rc = os.system("ino init")
|
||||
|
||||
# 32512 probably means ino is not installed
|
||||
if rc == 32512:
|
||||
print "ino init returned " + `rc`
|
||||
self.send_response(501)
|
||||
else:
|
||||
# write to file
|
||||
fo = open("src/sketch.ino", "wb")
|
||||
fo.write(text + "\n");
|
||||
fo.close()
|
||||
|
||||
print "created src/sketch.ino"
|
||||
|
||||
# invoke ino to build/upload
|
||||
# skip_lib_includes is used to avoid "line too long" errors with IDE 1.5.8+
|
||||
rc = os.system("ino build --skip_lib_includes")
|
||||
|
||||
# 512 probably means invalid option (skip_lib_includes)
|
||||
if rc == 512:
|
||||
print "ino build returned " + `rc` + " - trying again without skip_lib_includes"
|
||||
rc = os.system("ino build")
|
||||
|
||||
if not rc == 0:
|
||||
print "ino build returned " + `rc`
|
||||
self.send_response(400)
|
||||
else:
|
||||
rc = os.system("ino upload")
|
||||
if not rc == 0:
|
||||
print "ino upload returned " + `rc`
|
||||
self.send_response(500)
|
||||
else:
|
||||
self.send_response(200)
|
||||
os.chdir("..")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "running local web server at 127.0.0.1:8080..."
|
||||
server = BaseHTTPServer.HTTPServer(("127.0.0.1", 8080), Handler)
|
||||
server.pages = {}
|
||||
server.serve_forever()
|
Loading…
Reference in New Issue