Skip to content

Instantly share code, notes, and snippets.

@csexton
Last active August 29, 2015 14:17
Show Gist options
  • Save csexton/de32899a0b640984c6b6 to your computer and use it in GitHub Desktop.
Save csexton/de32899a0b640984c6b6 to your computer and use it in GitHub Desktop.
Flasher Script for RadBeacon USB
#!/usr/bin/env ruby
#
# radbeacon-flasher
#
# USAGE
#
# On a mac you should not need to pass any options in to flash the default
# firmware to the beacon. The firmware will be automatically downloaded. Use
# the `-w` option to "watch" for the beacon to be plugged in and continue
# flashing beacon.
#
# For full usage run with `--help` option:
#
# radbeacon-flasher --help
#
# PREREQUISITES
#
# This script requires both rbcomser and dfu-util.
#
# rbcomser can be installed from source:
# https://github.com/RadiusNetworks/ble112-usb-comm
#
# dfu-util can be found in most package managers:
# brew install dfu-util ; # On Mac
# sudo apt-get install dfu-util ; # On Ubuntu
#
require "ostruct"
require "net/http"
require "uri"
#
# Default Settings
#
options = OpenStruct.new
options.version = "2.2.0"
options.watch = false
options.port = "/dev/tty.usbmodem1"
################################################################################
require 'fileutils'
require 'net/http'
class FirmwareFile
def initialize(version)
version.gsub! ".", "-"
@filename = "radbeacon-usb-#{version}.bin"
@filepath = File.expand_path "~/.radbeacon/firmware/#{@filename}"
end
def path
fetch unless File.exist? @filepath
@filepath
end
def fetch
puts "Downloading Firmware #{@filename}"
url = "https://s3.amazonaws.com/downloads.radiusnetworks.com/43D9DC92-4AB7-4C9E-ABF2-EEB3A496EBDF/#{@filename}"
FileUtils.mkdir_p File.dirname(@filepath)
uri = URI.parse(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request request # Net::HTTPResponse object
if(response.code != "200")
puts "ERROR: Unable to fetch #{url}"
exit 1
end
File.write(@filepath, response.body)
end
end
end
class DFUer
V21_DFU = "aa"
V20_DFU = "30"
BLUGIGA_DFU = "0001090001"
NICE_DFU = "0530303030"
attr_reader :port
def initialize(port)
@port = port
end
def apply
if File.exist? port
[V21_DFU, V20_DFU, BLUGIGA_DFU, NICE_DFU].each do |cmd|
dfu_cmd(cmd, port)
sleep 0.5
return true if dfu_mode?
end
end
return true if dfu_mode?
sleep 2 # WTF
dfu_mode?
end
private
def dfu_mode?
`dfu-util -l`.include? "Found DFU: [2458:fffe]"
end
def dfu_cmd(cmd, port)
`rbcomser #{port} #{cmd} 2>&1 > /dev/null`
end
end
class Flasher
attr_reader :dir, :name
def initialize(path)
@dir = File.dirname(path)
@name = File.basename(path)
end
def flash
# looks like dfu-util has to execute in the same directory as the firmware image
# try without sudo first, if that fails use sudo.
Dir.chdir(dir) do |path|
precmd = RUBY_PLATFORM.include?("darwin") ? "" : "sudo "
cmd = "#{precmd}dfu-util -d 2458:fffe -D #{name}"
# using system so we get the output of dfu-util
print "\033[34m"
system(cmd)
print "\033[0m"
end
$?.success?
end
end
class ProgressSpinner
def self.tick
@chars ||= %w{| / - \\}
print "\b#{(@chars.rotate!).last}"
end
end
################################################################################
require 'optparse'
OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename $0} [options]"
opts.on("-w", "--watch", "Run forever and watch for new beacons") do
options.watch = true
end
opts.on("-pPORT", "--port=port", "Logical Port (#{options.port})") do |port|
options.port = port
end
opts.on("-VVERSION", "--version=VERSION", "Firmware Version (#{options.version})") do |ver|
options.version = ver
end
end.parse!
# Check for system commands needed by this script
%w{dfu-util rbcomser}.each do |cmd|
unless system("which #{cmd} > /dev/null 2>&1")
STDERR.puts "ERROR: Missing #{cmd} command"
exit 1
end
end
def wait_for_beacon(port)
loop do
break if File.exist?(port)
sleep 0.1
ProgressSpinner.tick
end
puts
end
def notify_start
if File.exist? "/System/Library/Sounds/Purr.aiff"
`afplay /System/Library/Sounds/Purr.aiff`
end
end
def notify_success
if File.exist? "/System/Library/Sounds/Frog.aiff"
`afplay /System/Library/Sounds/Frog.aiff`
end
puts
puts "Success!"
end
def run(options)
puts "Using port #{options.port}"
puts "WARNING: #{options.port} does not exist" unless File.exist? options.port
file = FirmwareFile.new(options.version)
puts "Using fimware #{file.path}"
puts "Going into DFU mode..."
dfu = DFUer.new options.port
notify_start
if (dfu.apply)
puts "DFU Success, flashing..."
flasher = Flasher.new(file.path)
notify_success if flasher.flash
else
STDERR.puts "ERROR: DFU failed!"
end
end
begin
if options.watch
print "Please insert beacon (ctrl-c to cancel) "
wait_for_beacon(options.port)
end
run(options)
rescue Interrupt
puts "\n\n kthxbye"
exit 0
end while options.watch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment