littlewire 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []