littlewire 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ # A little blinky test to show how to make stuff happen with a littlewire in ruby!
2
+ #
3
+ # To get started, plug an LED in to the ISP cable between ground and Pin 4 (they're next to each other)
4
+ require '../lib/littlewire.rb'
5
+
6
+ wire = LittleWire.connect
7
+
8
+ # set pin4 to an input, so our LED doesn't burn out without a resistor
9
+ wire.pin_mode :pin4 => :input
10
+
11
+ loop do
12
+ wire.digital_write(:pin4, true) # set the LED on for half a second
13
+ sleep 0.5
14
+ wire.digital_write(:pin4, false) # set the LED off for half a second
15
+ sleep 0.5
16
+ end
data/examples/fadey.rb ADDED
@@ -0,0 +1,22 @@
1
+ # A little blinky test to show how to make stuff happen with a littlewire in ruby!
2
+ #
3
+ # To get started, plug an LED in to the ISP cable between ground and Pin 4 (they're next to each other) via a resistor (resistor not optional)
4
+ require '../lib/littlewire.rb'
5
+
6
+ wire = LittleWire.connect
7
+
8
+ FPS = 60 # update 60 times per second
9
+ fader = 0.0 # our current position
10
+
11
+ loop do
12
+ value = (Math.sin(fader) + 1.0) * 127.0 # calculate an ideal brightness
13
+
14
+ ## Some equivilent ways of expressing this idea:
15
+ # wire.software_pwm_write(:softpwm_a, value)
16
+ # wire[:softpwm_a] = value
17
+ # wire[:softpwm_1] = value
18
+ wire.softpwm_a = value
19
+
20
+ fader += 0.025
21
+ sleep 1.0 / FPS
22
+ end
data/lib/analog.rb ADDED
@@ -0,0 +1,33 @@
1
+ module LittleWire::Analog
2
+ # Read the current value of an analog input
3
+ # Valid inputs are
4
+ AnalogReferences = [:vcc, :internal_reference_1_1, :internal_reference_2_56]
5
+ def analog_read input_name, voltage_reference = :vcc
6
+ voltage_reference = AnalogReferences.index(voltage_reference) if AnalogReferences.include? voltage_reference
7
+ scaling_setting = 0x07
8
+
9
+ if @analog_reference != voltage_reference
10
+ @analog_reference = voltage_reference
11
+ # This reset step is to work around a bug in firmware 1.1 - hopefully it can be disabled in future releases
12
+ control_transfer(function: :analog_init, wValue: scaling_setting) # reset analog reference setting
13
+ # set new reference voltage
14
+ control_transfer(function: :analog_init, wValue: scaling_setting | voltage_reference << 8)
15
+ end
16
+
17
+ control_transfer(function: :read_adc,
18
+ wValue: get_pin(AnalogPinMap, input_name),
19
+ dataIn: 2).unpack('S<').first / 1024.0
20
+ end
21
+
22
+ # Get the current temperature inside the LittleWire device's microcontroller. At a courseness of about 1.1°C increments
23
+ # and quite a lot of noise in the measurements, some averaging is required. Rather neatly, because of the noise in each
24
+ # sample, when averaged you get higher than 1.1°C resolution.
25
+ #
26
+ # This method returns a value around 280.0 - you'll need to calibrate it yourself by simple subtraction. You can calibrate
27
+ # it even more accurately by linearly mapping it to two known temperature points. Each LittleWire is a bit different so some
28
+ # calibration is necessary (unless you just want to take relative measurements).
29
+ def temperature
30
+ (analog_read(:temperature, :internal_reference_1_1) * 1024.0) / 1.12
31
+ end
32
+
33
+ end
data/lib/digital.rb ADDED
@@ -0,0 +1,110 @@
1
+ module LittleWire::Digital
2
+ EnableBulkWrite = false # this thing seems to not work good - not ready for real world use
3
+ # these firmwares have a default state specified, enabling bulk writing
4
+ BulkWriteDefaultStates = { '1.1' => 0b00001000 } # D- usb bit is high, others are low
5
+ BulkWriteBitmask = { '1.1' => 0b00100111 }
6
+
7
+ # Write one or more digital values to device pins
8
+ #
9
+ # A simple invokation is `my_wire.digital_write(:pin1, true)` setting pin1 to a
10
+ # high logic level. :high and :low can be substituted for true and false, as can
11
+ # :on and :off, :vcc and :gnd or :ground
12
+ #
13
+ # `digital_write` can also be called with a hash: `my_wire.digital_write(:pin1 => true)`
14
+ # allowing multiple pins to be set at once. A system for updating all pins simultaniously is being
15
+ # worked on, but is not yet stable, so for now digital_write uses a request for each pin.
16
+ #
17
+ # Automatically disables PWM and Servo features if you try to write to a pin used by those modules
18
+ def digital_write *args
19
+ if args.length > 1
20
+ self.digital_write({args[0] => args[1]})
21
+ else
22
+ raise "Incorrect Arguments" unless args.first.respond_to? :to_hash
23
+ hash = args.first.to_hash
24
+
25
+ #self.hardware_pwm_enabled = false if hash.keys.any? { |pin| LittleWire::HardwarePWMPinMap.has_key? pin }
26
+ #self.software_pwm_enabled = false if hash.keys.any? { |pin| LittleWire::SoftwarePWMPinMap.has_key? pin }
27
+
28
+ if use_experimental_bulk_write?
29
+ # could cause problems - must do tests when deciding if to enable this or not
30
+ @bulk_write_bitmap ||= 0
31
+
32
+ hash.each do |pin, state|
33
+ if get_boolean(state)
34
+ @bulk_write_bitmap |= 1 << get_pin(LittleWire::DigitalPinMap, pin)
35
+ else
36
+ @bulk_write_bitmap &= ~(1 << get_pin(LittleWire::DigitalPinMap, pin))
37
+ end
38
+ end
39
+ value = (@bulk_write_bitmap & BulkWriteBitmask[self.version]) | BulkWriteDefaultStates[self.version]
40
+ control_transfer(function: :write, wValue: value)
41
+ else
42
+ hash.each do |pin, state|
43
+ control_transfer(
44
+ function: get_boolean(state) ? :pin_set_high : :pin_set_low,
45
+ wValue: get_pin(LittleWire::DigitalPinMap, pin)
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # Read one or more digital values from device pins
53
+ #
54
+ # The simplest invocation is `my_wire.digital_read(:pin1)`, returning a true or false
55
+ # where true is a high (positive) voltage and false is a lower (near ground) voltage
56
+ #
57
+ # A more advanced invokation is passing several pins as arguments or an array of pins
58
+ # `sensor_a, sensor_b = my_wire.digital_read(:pin1, :pin2)` which reads both pins at
59
+ # the exact same instant in just one USB request, returning an array of the results
60
+ #
61
+ # digital_read tends to work best when the pin in :input mode. See also #pin_mode
62
+ def digital_read *args
63
+ raise "Incorrect Arguments" if args.length < 1
64
+ pins = args.flatten
65
+ port = control_transfer(function: :read, dataIn: 1).unpack('c').first
66
+ mapped = pins.map do |pin|
67
+ pin = get_pin(LittleWire::DigitalPinMap, pin)
68
+ (port & pin) != 0 # discover if pin is high or low
69
+ end
70
+
71
+ if args.length == 1 and (args.first.is_a?(Symbol) or args.first.is_a?(Integer))
72
+ mapped.first
73
+ else
74
+ mapped
75
+ end
76
+ end
77
+
78
+ # Set the mode of one or more device pins
79
+ #
80
+ # A simple form is `my_wire.pin_mode(:pin1, :input)` setting pin1 to input mode.
81
+ # Multiple pins can be set using `my_wire.pin_mode(:pin1 => :input, :pin2 => :output)`
82
+ #
83
+ # :input mode leaves the pin disconnected if `digital_write`n to false, or connected
84
+ # via a 20kohm resistor to 5 volts if `digital_write`n to true.
85
+ #
86
+ # :output mode connects the pin directly to 5 volts when `digital_write`n to true
87
+ # and connects it to ground when digitally written to false. Be careful not to create
88
+ # short circuits as these may damage your LittleWire.
89
+ def pin_mode *args
90
+ if args.length == 2
91
+ self.pin_mode( args[0] => args[1] )
92
+ else
93
+ raise if args.length != 1 or !args.first.is_a?(Hash)
94
+
95
+ modes = {
96
+ :input => :pin_set_input, :in => :pin_set_input,
97
+ :output => :pin_set_output, :out => :pin_set_output
98
+ }
99
+ args.first.each do |pin, mode|
100
+ raise "Unknown mode #{mode.inspect}" unless modes[mode.to_sym]
101
+ control_transfer(function: modes[mode.to_sym], wValue: get_pin(LittleWire::DigitalPinMap, pin))
102
+ end
103
+ end
104
+ end
105
+
106
+ private
107
+ def use_experimental_bulk_write?
108
+ EnableBulkWrite and BulkWriteDefaultStates.include?(self.version.to_s)
109
+ end
110
+ end
@@ -0,0 +1,53 @@
1
+ module LittleWire::HardwarePWM
2
+ attr_reader :hardware_pwm_enabled # initially :unknown, then a boolean
3
+
4
+ # Set hardware pwm as enabled or disabled - hardware pwm is automatically enabled when you start using it
5
+ # but must be disabled before using these pins again for other purposes
6
+ def hardware_pwm_enabled= value
7
+ return if @hardware_pwm_enabled == value
8
+ @hardware_pwm_enabled = !!value
9
+ control_transfer(function: value ? :start_pwm : :stop_pwm)
10
+ end
11
+
12
+ # Get an array of the current values in the Hardware PWM module
13
+ def hardware_pwm; @hardware_pwm.dup; end
14
+
15
+ # Set Hardware PWM to an array of new values - array must be two items long
16
+ def hardware_pwm= values
17
+ self.hardware_pwm_enabled = true
18
+ @hardware_pwm[0] = values[0].to_i % 256
19
+ @hardware_pwm[1] = values[1].to_i % 256
20
+ control_transfer(function: :update_pwm_compare, wValue: @hardware_pwm[0].to_i, wIndex: @hardware_pwm[1].to_i)
21
+ end
22
+
23
+ # Get the current value of a Hardware PWM channel (stored in littlewire.rb library - not requested from device)
24
+ def hardware_pwm_read channel; hardware_pwm[get_pin(LittleWire::HardwarePWMPinMap, channel)]; end
25
+
26
+ # Set an individual Hardware PWM channel to a new value in the range of 0-255
27
+ def hardware_pwm_write channel, value
28
+ updated = self.hardware_pwm
29
+ updated[get_pin(LittleWire::HardwarePWMPinMap, channel)] = value
30
+ self.hardware_pwm = updated
31
+ end
32
+
33
+
34
+ PWMPrescaleSettings = [1, 8, 64, 256, 1024] # :nodoc:
35
+ # Set division of the Hardware PWM Prescaler - default 1024. This setting controls how quickly LittleWire's PWM channels oscillate
36
+ # between their 'high' and 'low' state. Lower prescaler values are often nicer for lighting, while higher values can be better for
37
+ # motor speed control and 1024 is required for servo position control
38
+ # 1024: roughly 63hz
39
+ # 256: roughly 252hz
40
+ # 64: roughly 1khz
41
+ # 8: roughly 8khz
42
+ # 1: roughly 64khz
43
+ #
44
+ # No other values are accepted
45
+ def hardware_pwm_prescale= division
46
+ raise "Unsupported Hardware PWM Prescale value, must be #{PWMPrescaleSettings.inspect}" unless PWMPrescaleSettings.include? division
47
+ if @hardware_pwm_prescale != division
48
+ @hardware_pwm_prescale = division
49
+ control_transfer(function: :change_pwm_prescale, wValue: PWMPrescaleSettings.index(division))
50
+ end
51
+ end
52
+
53
+ end
data/lib/i2c.rb ADDED
@@ -0,0 +1,43 @@
1
+ class LittleWire::I2C
2
+ def initialize wire
3
+ @wire = wire
4
+ @wire.control_transfer(function: :i2c_init)
5
+ end
6
+
7
+ # start an i2c message
8
+ #
9
+ # Arguments:
10
+ # address: (Integer) 7 bit numeric address
11
+ # direction: (Symbol) :in or :out
12
+ def start address_7bit, direction
13
+ direction = 1 if direction == :out || direction == :output || direction == :write
14
+ direction = 0 if direction != 1
15
+ config = (address_7bit.to_i << 1) | direction
16
+ @wire.control_transfer(function: :i2c_begin, wValue: config)
17
+ @wire.control_transfer(function: :read_buffer, dataIn: 8).bytes.first != 0
18
+ end
19
+
20
+ # read bytes from i2c device, optionally ending with a stop when finished
21
+ def read length, endWithStop = false
22
+ @wire.control_transfer(function: :i2c_read, wValue: (length.to_i & 0xFF) << 8 | (endWithStop ? 1 : 0))
23
+ @wire.control_transfer(function: :read_buffer, dataIn: 8)[0...length.to_i]
24
+ end
25
+
26
+ # write data to i2c device, optionally sending a stop when finished
27
+ def write send_buffer, end_with_stop = false
28
+ send_buffer = send_buffer.pack('c*') if send_buffer.is_a? Array
29
+ raise "Send buffer is too long" if send_buffer.length > 7
30
+
31
+ # TODO: Send multiple requests to handle send buffers longer than 7 bytes
32
+ @wire.control_transfer(
33
+ wRequest: 0xE0 | send_buffer.length | (end_with_stop << 3),
34
+ wValue: (send_buffer.bytes[1] << 8) + send_buffer.bytes[0],
35
+ wIndex: (send_buffer.bytes[3] << 8) + send_buffer.bytes[2]
36
+ )
37
+ end
38
+
39
+ # set the update delay of LittleWire's i2c module
40
+ def delay= update_delay
41
+ @wire.control_transfer(function: :i2c_update_delay, wValue: update_delay.to_i)
42
+ end
43
+ end
data/lib/littlewire.rb ADDED
@@ -0,0 +1,302 @@
1
+ # A little library for a little wire, by Bluebie
2
+ # Provides an arduino or wiring style interface to the LittleWire device's IO features
3
+ # and provides a nicer invented ruby-style interface also.
4
+ require 'libusb'
5
+ class LittleWire; end
6
+ require_relative 'digital'
7
+ require_relative 'analog'
8
+ require_relative 'hardware-pwm'
9
+ require_relative 'software-pwm'
10
+ require_relative 'servo'
11
+ require_relative 'spi'
12
+ require_relative 'i2c'
13
+ require_relative 'one-wire'
14
+
15
+ # LittleWire class represents LittleWire's connected to your computer via USB
16
+ #
17
+ # Most of the time you'll only have one LittleWire - in this case, use LittleWire.connect to get
18
+ # ahold of your wire. If you have more than one, you can use LittleWire.all to fetch an array of them
19
+ class LittleWire
20
+ include Digital
21
+ include Analog
22
+ include HardwarePWM
23
+ include SoftwarePWM
24
+ include Servo
25
+
26
+ # pin name to numeric internal code maps
27
+ DigitalPinMap = { # maps common names to bit positions in PORTB
28
+ pin1: 1, d1: 1, miso: 1, pwm_b: 1, pwm_2: 1,
29
+ pin2: 2, d2: 2, sck: 2,
30
+ pin3: 5, d3: 5, reset: 5,
31
+ pin4: 0, d4: 0, mosi: 0, pwm_a: 0, pwm_1: 0 }
32
+ AnalogPinMap = { # maps common names to switch index in littlewire firmware
33
+ a1: 0, adc_1: 0, reset: 0, pin3: 0, d3: 0,
34
+ a2: 1, adc_2: 1, sck: 1, pin2: 1, d2: 1,
35
+ temperature: 2, temp: 2 }
36
+ HardwarePWMPinMap = { # maps common pin names to @hardware_pwm array index
37
+ pwm_b: 1, pwm_1: 1, d1: 1, pin1: 1, miso: 1,
38
+ pwm_a: 0, pwm_2: 0, d4: 0, pin4: 0, mosi: 0 }
39
+ SoftwarePWMPinMap = { # TODO: figure out which pins these are
40
+ softpwm_1: 0, softpwm_a: 0,
41
+ softpwm_2: 1, softpwm_b: 1,
42
+ softpwm_3: 2, softpwm_c: 2 }
43
+ GenericPinMap = { # generic pinmap used by [] and []= methods to refer to anything
44
+ d1: [:digital, :pin1],
45
+ d2: [:digital, :pin2],
46
+ d3: [:digital, :pin3],
47
+ d4: [:digital, :pin4],
48
+ pin1: [:digital, :pin1],
49
+ pin2: [:digital, :pin2],
50
+ pin3: [:digital, :pin3],
51
+ pin4: [:digital, :pin4],
52
+ a1: [:analog, :a1],
53
+ a2: [:analog, :a2],
54
+ adc_1: [:analog, :adc_1],
55
+ adc_1: [:analog, :adc_2],
56
+ pwm_1: [:hardware_pwm, :pwm_1],
57
+ pwm_2: [:hardware_pwm, :pwm_2],
58
+ pwm_a: [:hardware_pwm, :pwm_a],
59
+ pwm_b: [:hardware_pwm, :pwm_b],
60
+ softpwm_1: [:software_pwm, :softpwm_1],
61
+ softpwm_2: [:software_pwm, :softpwm_2],
62
+ softpwm_3: [:software_pwm, :softpwm_3],
63
+ softpwm_a: [:software_pwm, :softpwm_a],
64
+ softpwm_b: [:software_pwm, :softpwm_b],
65
+ softpwm_c: [:software_pwm, :softpwm_c],
66
+ }
67
+
68
+ SupportedVersions = ['1.1', '1.0'] # in order of newness. # TODO: Add version 1.0?
69
+
70
+
71
+ # An array of all unclaimed littlewires connected to computer via USB
72
+ def self.all
73
+ usb = LIBUSB::Context.new
74
+ usb.devices.select { |device|
75
+ device.idProduct == 0x0c9f && device.idVendor == 0x1781 && device.product == 'USBtinySPI'
76
+ }.map { |device|
77
+ self.new(device)
78
+ }
79
+ end
80
+
81
+ # Frst littlewire connected to this computer via USB - good when you only have one
82
+ def self.connect; all.first; end
83
+
84
+
85
+ # initializes a LittleWire with a libusb device reference and some default values - does not talk to device
86
+ def initialize devref #:nodoc:
87
+ @device = devref
88
+
89
+ @hardware_pwm_enabled = :unknown
90
+ @hardware_pwm_prescale = :unknown
91
+ @hardware_pwm = [0, 0]
92
+ @software_pwm_enabled = :unknown
93
+ @software_pwm = [0, 0, 0]
94
+
95
+ # shut everything down, trying to setup littlewire in consistent initial state in case previous programs
96
+ # messed with it's state
97
+ self.software_pwm_enabled = false
98
+ self.hardware_pwm_enabled = false
99
+ self.pin_mode(pin1: :input, pin2: :input, pin3: :input, pin4: :input)
100
+ self.digital_write(pin1: :gnd, pin2: :gnd, pin3: :gnd, pin4: :gnd)
101
+ end
102
+
103
+ # creates a lambda to close usb device when LittleWire is deallocated, without LittleWire instance closured in to it recursively
104
+ def self.create_destructor io #:nodoc:
105
+ lambda do
106
+ io.close
107
+ end
108
+ end
109
+
110
+ # Call finished when you're done with the LittleWire to release it for other programs to use. You can always claim it again
111
+ # later by using any of the methods on this class which communicate over USB
112
+ def finished
113
+ if @io
114
+ ObjectSpace.undefine_finalizer(self) # remove usb closer finalizer
115
+ @io.close
116
+ @io = nil
117
+ end
118
+ end
119
+
120
+
121
+ # implementations of littlewire functions
122
+ # - generic requests
123
+ def echo; control_transfer(function: :echo, dataIn: 8).unpack('S<*'); end # echo's usb request for testing
124
+ def read; control_transfer(function: :read, wIndex: 0, dataIn: 1); end
125
+ def write byte; control_transfer(function: :write, wIndex: 0, wValue: byte); end
126
+ def clear_bit bit; control_transfer(function: :clear_bit, wIndex: 0, wValue: bit); end
127
+ def set_bit bit; control_transfer(function: :set_bit, wIndex: 0, wValue: bit); end
128
+ # - programming requests
129
+ #def power_up sck_period, reset; control_transfer(function: :power_up, wIndex: sck_period, wValue: reset ? 1 : 0); end
130
+ #def power_down; control_transfer(function: :power_down); end
131
+ # TODO: maybe spi, poll_bytes, flash_read, flash_write, eprom_read, eeprom_write
132
+
133
+
134
+ # returns version code number (treat it as a hex number)
135
+ def version_hex
136
+ @version_hex ||= control_transfer(function: :version, dataIn: 1).unpack('c').first
137
+ end
138
+
139
+ # Returns version number of firmware on LittleWire hardware
140
+ def version
141
+ @version ||= version_hex.to_s(16).chars.entries.join('.')
142
+ end
143
+
144
+ # get the SPI interface
145
+ def spi
146
+ @spi ||= SPI.new(self)
147
+ end
148
+
149
+ # get the I2C interface
150
+ def i2c
151
+ @i2c ||= I2C.new(self)
152
+ end
153
+
154
+ # get the 1wire interface (requires firmware 1.1 or newer
155
+ def one_wire
156
+ raise "You need to update your LittleWire firmware to version 1.1 to use One Wire" unless version_hex >= 0x11
157
+ @one_wire ||= OneWire.new(self)
158
+ end
159
+
160
+
161
+
162
+ # translate calls with arduino-style lowerCamelCase method names in to ruby-style underscored_method_names
163
+ def method_missing name, *args, &proc
164
+ underscorized = name.to_s.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2}" }.downcase # make underscored equivilent
165
+ return send(underscorized, *args, &proc) if respond_to? underscorized # translate casing style if we can find an equivilent
166
+
167
+ read_only = name.to_s.gsub('=', '').to_sym
168
+ if GenericPinMap.has_key? read_only
169
+ if name.to_s.end_with? '='
170
+ return (self[read_only] = args.first)
171
+ else
172
+ return self[read_only]
173
+ end
174
+ end
175
+
176
+ super # default behaviour
177
+ end
178
+
179
+
180
+ # get the value of something
181
+ def [] name
182
+ pin = GenericPinMap[name.to_sym]
183
+ raise "Unknown Pin '#{name}'" unless pin
184
+ self.send "#{pin[0]}_read", pin[1]
185
+ end
186
+
187
+ # set the value of something
188
+ def []= name, value
189
+ pin = GenericPinMap[name.to_sym]
190
+ raise "Unknown Pin '#{name}'" unless pin
191
+ self.send "#{pin[0]}_write", pin[1], value
192
+ end
193
+
194
+ protected
195
+ # raw opened device
196
+ def io #:nodoc:
197
+ unless @io
198
+ @io = @device.open
199
+ ObjectSpace.define_finalizer(self, self.class.create_destructor(@io))
200
+
201
+ # check for compatible firmware on littlewire device and warn if is unknown
202
+ warn "Unknown littlewire.cc firmware version #{version} might cause problems" unless SupportedVersions.include? version
203
+ end
204
+ @io
205
+ end
206
+
207
+ # functions offered by the LittleWire
208
+ Functions = [
209
+ ## Generic requests
210
+ :echo, # echo test 0
211
+ :read, # read byte (wIndex:address) 1
212
+ :write, # write byte (wIndex:address, wValue:value) 2
213
+ :clear_bit, # clear bit (wIndex:address, wValue:bitno) 3
214
+ :set_bit, # set bit (wIndex:address, wValue:bitno) 4
215
+ ## Programming requests
216
+ :power_up, # apply power (wValue:SCK-period, wIndex:RESET) 5
217
+ :power_down, # remove power from chip 6
218
+ :spi, # issue SPI command (wValue:c1c0, wIndex:c3c2) 7
219
+ :poll_bytes, # set poll bytes for write (wValue:p1p2) 8
220
+ :flash_read, # read flash (wIndex:address) 9
221
+ :flash_write, # write flash (wIndex:address, wValue:timeout) 10
222
+ :eeprom_read, # read eeprom (wIndex:address) 11
223
+ :eeprom_write, # write eeprom (wIndex:address, wValue:timeout) 12
224
+ ## Additional requests - ihsanKehribar
225
+ :pin_set_input, # 13
226
+ :pin_set_output, # 14
227
+ :read_adc, # 15
228
+ :start_pwm, # 16
229
+ :update_pwm_compare, # 17
230
+ :pin_set_high, # 18
231
+ :pin_set_low, # 19
232
+ :pin_read, # 20
233
+ :single_spi, # 21
234
+ :change_pwm_prescale, # 22
235
+ :setup_spi, # 23
236
+ :setup_i2c, # 24
237
+ :i2c_begin_tx, # 25
238
+ :i2c_add_buffer, # 26
239
+ :i2c_send_buffer, # 27
240
+ :spi_add_buffer, # 28
241
+ :spi_send_buffer, # 29
242
+ :i2c_request_from, # 30
243
+ :spi_update_delay, # 31
244
+ :stop_pwm, # 32
245
+ :debug_spi, # 33
246
+ :version, # 34
247
+ :analog_init, # 35
248
+ :reserved, :reserved, :reserved, :reserved,
249
+ :read_buffer, # 40
250
+ :onewire_reset_pulse, # 41
251
+ :onewire_send_byte, # 42
252
+ :onewire_read_byte, # 43
253
+ :i2c_init, # 44
254
+ :i2c_begin, # 45
255
+ :i2c_read, # 46
256
+ :init_softpwm, # 47
257
+ :update_softpwm, # 48
258
+ :i2c_update_delay, # 49
259
+ :onewire_read_bit, # 50
260
+ :onewire_write_bit, # 51
261
+ :pic_24f_programming, # 52 - experimental
262
+ :pic_24f_sendsix # 53 - experimental
263
+ # special cases
264
+ # pic 24f send bytes - request = 0xD*
265
+ # i2c send multiple messages - request = 0xE* ### experimental ###
266
+ # spi multiple message send - request = 0xF*
267
+ ]
268
+ # transfer data between usb device and this program
269
+ def control_transfer(opts = {}) #:nodoc:
270
+ opts[:bRequest] = Functions.index(opts.delete(:function)) if opts[:function]
271
+ io.control_transfer({
272
+ wIndex: 0,
273
+ wValue: 0,
274
+ bmRequestType: usb_request_type(opts),
275
+ timeout: 5000
276
+ }.merge opts)
277
+ end
278
+
279
+ # calculate usb request type
280
+ def usb_request_type opts #:nodoc:
281
+ c = LIBUSB::Call
282
+ value = c::RequestTypes[:REQUEST_TYPE_VENDOR] | c::RequestRecipients[:RECIPIENT_DEVICE]
283
+ value |= c::EndpointDirections[:ENDPOINT_OUT] if opts.has_key? :dataOut
284
+ value |= c::EndpointDirections[:ENDPOINT_IN] if opts.has_key? :dataIn
285
+ return value
286
+ end
287
+
288
+
289
+ # lookup a pin name in a map and return it's raw identifier
290
+ def get_pin map, value #:nodoc:
291
+ value = value.to_sym if value.is_a? String
292
+ value = map[value] if map.has_key? value
293
+ value
294
+ end
295
+
296
+ # translate possible literal values in to a boolean true or false (meaning high or low)
297
+ def get_boolean value #:nodoc:
298
+ # some exceptions
299
+ value = false if value == :low or value == 0 or value == nil or value == :off or value == :ground or value == :gnd
300
+ !! value # double invert value in to boolean form
301
+ end
302
+ end
data/lib/one-wire.rb ADDED
@@ -0,0 +1,6 @@
1
+ module LittleWire::OneWire
2
+ def initialize wire
3
+ @wire = wire
4
+ raise "One Wire not yet implemented in ruby library"
5
+ end
6
+ end
data/lib/servo.rb ADDED
@@ -0,0 +1,18 @@
1
+ module LittleWire::Servo
2
+ # Get the current andle of a servo connected to a hardware pwm channel as an angle between roughly -90° and +90°
3
+ def servo_read hardware_pwm_channel
4
+ value = hardware_pwm_read(hardware_pwm_channel)
5
+ 90 - ((value - 13).to_f * (180.0 / 23.0))
6
+ end
7
+
8
+ # Set a servo connected to a hardware pwm channel to an angle between -90° and +90° inclusive
9
+ # Note that setting a servo's position automatically enables Hardware PWM - disable hardware pwm when you're done
10
+ # if you want to use these pins for something else
11
+ def servo_write hardware_pwm_channel, angle
12
+ self.hardware_pwm_prescale = 1024 # make sure our PWM is running at the correct frequency
13
+
14
+ value = ((angle + 90.0) / (180.0 / 23.0)).round + 13
15
+
16
+ hardware_pwm_write(hardware_pwm_channel, value)
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ # Interface to LittleWire 1.1's software pwm feature
2
+ module LittleWire::SoftwarePWM
3
+ # Has the software pwm module been enabled?
4
+ attr_reader :software_pwm_enabled
5
+
6
+ # Set if the software pwm module is enabled or inactive
7
+ def software_pwm_enabled= value
8
+ value = !! value # booleanify it
9
+ if @software_pwm_enabled != value
10
+ require_software_pwm_available
11
+ control_transfer(function: :init_softpwm, wValue: value ? 1 : 0)
12
+ @software_pwm_enabled = value
13
+ end
14
+ end
15
+
16
+ # An array of current software pwm values
17
+ def software_pwm; @software_pwm.dup; end
18
+
19
+ # Set software pwm to the values of an array - values must be a number between 0 and 255 inclusive
20
+ def software_pwm= values
21
+ require_software_pwm_available
22
+ self.software_pwm_enabled = true
23
+
24
+ 3.times do |idx|
25
+ @software_pwm[idx] = values[idx].to_i % 256
26
+ end
27
+
28
+ control_transfer(function: :update_softpwm, wValue: @software_pwm[1] << 8 | @software_pwm[0], wIndex: @software_pwm[2])
29
+ end
30
+
31
+ # Get the value of a single software pwm channel
32
+ def software_pwm_read channel
33
+ @software_pwm[get_pin(LittleWire::SoftwarePWMPinMap, channel)]
34
+ end
35
+
36
+ # Set the value of a single software pwm channel - value must be a number between 0 and 255 inclusive
37
+ def software_pwm_write channel, value
38
+ state = self.software_pwm
39
+ state[get_pin(LittleWire::SoftwarePWMPinMap, channel)] = value
40
+ self.software_pwm = state
41
+ end
42
+
43
+ def is_software_pwm_available?
44
+ version_hex >= 0x11
45
+ end
46
+
47
+ private
48
+ def require_software_pwm_available
49
+ raise "Software PWM not available in version #{self.version} firmware" unless is_software_pwm_available?
50
+ end
51
+ end
data/lib/spi.rb ADDED
@@ -0,0 +1,28 @@
1
+ class LittleWire::SPI
2
+ def initialize wire
3
+ @wire = wire
4
+ @wire.control_transfer(function: :setup_spi)
5
+ raise "SPI requires LittleWire firmware 1.1. Yours = #{@wire.version}" unless @wire.version_hex >= 0.11
6
+ end
7
+
8
+ # send and receive a message of up to four bytes
9
+ def send send, receive, auto_chipselect = false
10
+ mode = auto_chipselect ? 1 : 0
11
+ @wire.control_transfer(
12
+ wRequest: 0xF0 + send.length + (mode << 3),
13
+ wValue: send.bytes[1] << 8 | send.bytes[0],
14
+ wIndex: send.bytes[3] << 8 | send.bytes[0]
15
+ )
16
+ end
17
+
18
+ # change spi delay setting
19
+ def delay= number
20
+ @wire.control_transfer(function: :spi_update_delay, wValue: number)
21
+ end
22
+
23
+ # get debug status
24
+ def debug
25
+ @wire.control_transfer(function: :debug_spi, dataIn: 8)
26
+ @wire.control_transfer(function: :read_buffer, dataIn: 8).unpack('c').first
27
+ end
28
+ end
data/license.txt ADDED
@@ -0,0 +1 @@
1
+ I give this code to you, dear reader. Do what you like with it. It's none of my concern! — Bluebie
data/readme.md ADDED
@@ -0,0 +1,68 @@
1
+ Little Wire is a little multiheaded animal who pokes in a USB port, and uploads
2
+ programs to AVR and PIC chips. But Little Wire does so much more - and that's
3
+ what this library is about. The [littlewire.cc](http://littlewire.cc/) gadget
4
+ exposes four digital wires and a five volt power supply. Those four wires can
5
+ each be individually controlled, with three capable of varying brightness of
6
+ lights, two capable of controlling motors and servos, two 10-bit analog inputs,
7
+ a temperature sensor, a Serial Peripheral Interface, an i2c interface, and a
8
+ 1-wire interface.
9
+
10
+ Eventually littlewire.rb hopes to share fun, simple, principal of least surprise
11
+ ruby interfaces to all of these.
12
+
13
+
14
+ ### a blinky ###
15
+
16
+ require 'littlewire'
17
+
18
+ wire = LittleWire.connect # connects to the first Little Wire on your computer
19
+
20
+ loop do
21
+ wire.digital_write :pin3, :vcc # connect pin3 to 5v
22
+ sleep 0.5
23
+ wire.digital_write :pin3, :gnd # connect pin3 to ground
24
+ sleep 0.5
25
+ end
26
+
27
+ And so it is that the ruby on the computer did remotely control the Little Wire's
28
+ digital port. Don't forget a resistor for that LED of yours! If you don't have a
29
+ resistor handy, add `wire.pin_mode :pin3 => :input` before `loop do` to use
30
+ Little Wire's internal 20kohm resistor and keep that light shining.
31
+
32
+
33
+ ### a philosophy ###
34
+
35
+ Little Wire is such a small creature it's possible perhaps to implement every way
36
+ you might want to use it! Every method name and every symbol. I've tried to do this
37
+ a bit. My hope is that you'll play with littlewire in irb (or better yet
38
+ [pry](http://pryrepl.org)) and everything you try just works. To that end, littlewire
39
+ supports `ruby_style_methods` and `wiringStyleMethods`, and has methods like
40
+ `digitalWrite(:pin1, true)` - familliar to arduinoers but also syntaxes like
41
+ `mywire[:d1] = true`. Initialization sequences automatically run the moment you try
42
+ to use features, and they always try to make sure littlewire is correctly configured
43
+ for the task at hand, while not making any unnecessary requests. The only thing
44
+ littlewire.rb doesn't do (yet) is program other chips - use avrdude for that.
45
+
46
+
47
+ ### what's it good for? ###
48
+
49
+ You could use the three pulse width modulated analog outputs to control an RGB light
50
+ adjusting the mood of your batcave at the click of a button. Hook it up to displays,
51
+ memory, sensors, iButtons, RFID readers, digital radios, motors, switches, fairy
52
+ lights... whatever floats your boat really. The possabilities are not especially
53
+ limited. Most projects you might use an Arduino for can be done with a Little Wire
54
+ if you don't mind leaving a computer turned on connected to it, and with the advent
55
+ of Raspberry Pi, that's not all that bad of an idea. I use my Little Wire to quickly
56
+ test ideas before changing them to C and uploading them to cheaper attiny chips (also
57
+ using the Little Wire to program them)
58
+
59
+
60
+ ### a warning ###
61
+
62
+ littlewire.rb is experimental (till it is released as 1.0 via rubygems) and there's
63
+ a pretty good chance the API will change a bit until then. Not to worry - rubygems
64
+ have a mechanism for you to [require a specific version][1] of littlewire.rb,
65
+ ensuring your programs always work, so you can start building stuff on it today!
66
+
67
+ [1]: http://docs.rubygems.org/read/chapter/4#page71 "RubyGems Documentation"
68
+
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: littlewire
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.9'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bluebie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: libusb
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.2.0
30
+ description: A little library for a little wire. Providing a pure ruby interface (via
31
+ the nonpure libusb gem) to littlewire.cc's wonderful gadget. littlewire.rb provides
32
+ general purpose digital IO, pulse width modulation analog outputs, analog inputs,
33
+ SPI, I2C, One Wire, and rough servo control via a friendly interface which responds
34
+ both to familliar Wiring/Arduino style methods and a more concise ruby alternative.
35
+ email: a@creativepony.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - lib/analog.rb
41
+ - lib/digital.rb
42
+ - lib/hardware-pwm.rb
43
+ - lib/i2c.rb
44
+ - lib/littlewire.rb
45
+ - lib/one-wire.rb
46
+ - lib/servo.rb
47
+ - lib/software-pwm.rb
48
+ - lib/spi.rb
49
+ - readme.md
50
+ - license.txt
51
+ - examples/blinky.rb
52
+ - examples/fadey.rb
53
+ homepage: http://creativepony.com/littlewire/
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --main
58
+ - lib/littlewire.rb
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.21
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A tiny library for littlewire.cc usb devices
79
+ test_files: []