barkest_lcd 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ module BarkestLcd
2
+ ##
3
+ # A simple enumeration class based on a hash of integers.
4
+ #
5
+ # enum = HashEnum.new({ :alpha => 0x01, :bravo => 0x02, :charlie => 0x04 })
6
+ # enum.alpha # 1
7
+ # enum.bravo # 2
8
+ # enum.CHARLIE # 4
9
+ # enum.alpha?(11) # true
10
+ # enum.BRAVO?(11) # true
11
+ # enum.charlie?(11) # false
12
+ #
13
+ class HashEnum
14
+
15
+ ##
16
+ # Turns a hash into an enumeration.
17
+ def initialize(hash = {})
18
+ @hash = hash.inject({}){ |memo,(k,v)| memo[k.to_sym] = v.to_i; memo }.freeze
19
+ # values ordered highest to lowest.
20
+ @flags = @hash.to_a.sort{|a,b| b[1] <=> a[1]}
21
+
22
+ freeze
23
+ end
24
+
25
+ # :nodoc:
26
+ def method_missing(meth, *args, &block)
27
+
28
+ if @hash.respond_to?(meth)
29
+ return @hash.send(meth, *args, &block)
30
+ end
31
+
32
+ [ meth.to_s.downcase.to_sym, meth.to_s.upcase.to_sym ].each do |test_meth|
33
+ if @hash.keys.include?(test_meth)
34
+ return @hash[test_meth]
35
+ else
36
+ meth_name = test_meth.to_s
37
+ is_flag = meth_name[-1] == '?'
38
+ if is_flag
39
+ test_meth = meth_name[0..-2].to_sym
40
+ if @hash.keys.include?(test_meth)
41
+ valid = @hash[test_meth]
42
+ test = args && args.count > 0 ? args.first : nil
43
+ raise ArgumentError, 'Missing value to test.' unless test
44
+ raise ArgumentError, 'Test value must be an integer.' unless test.is_a?(Fixnum)
45
+ return (test & valid) == valid
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ super meth, *args, &block
52
+ end
53
+
54
+ ##
55
+ # Determines what keys the value contains.
56
+ #
57
+ # If a remainder exists, then the remainder is added at the end of the returned array.
58
+ def flags(value)
59
+ ret = []
60
+ @flags.each do |(k,v)|
61
+ if (value & v) == v
62
+ value -= v
63
+ ret << k
64
+ end
65
+ end
66
+ ret << value if value != 0
67
+ ret
68
+ end
69
+
70
+ # :nodoc:
71
+ def inspect
72
+ @hash.inspect
73
+ end
74
+
75
+ # :nodoc:
76
+ def to_s
77
+ @hash.to_s
78
+ end
79
+
80
+ # :nodoc:
81
+ def ==(other)
82
+ @hash == other
83
+ end
84
+
85
+ # :nodoc:
86
+ def eql?(other)
87
+ @hash.eql?(other)
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,184 @@
1
+
2
+ require 'models/error_logger'
3
+
4
+ module BarkestLcd
5
+ ##
6
+ # A class to interface with the picoLCD 256x64 (aka picoLCD Graphic) from [www.mini-box.com](http://www.mini-box.com).
7
+ #
8
+ #
9
+ class PicoLcdGraphic < ::HIDAPI::Device
10
+
11
+ include BarkestLcd::ErrorLogger
12
+
13
+ ##
14
+ # Any of the Pico LCD errors.
15
+ PicoLcdError = Class.new(StandardError)
16
+
17
+ ##
18
+ # The device has already been opened.
19
+ AlreadyOpen = Class.new(PicoLcdError)
20
+
21
+ ##
22
+ # The device is not currently open.
23
+ NotOpen = Class.new(PicoLcdError)
24
+
25
+ ##
26
+ # The device failed to open (no address returned).
27
+ OpenFailed = Class.new(PicoLcdError)
28
+
29
+ ##
30
+ # The operation has timed out.
31
+ Timeout = Class.new(PicoLcdError)
32
+
33
+ ##
34
+ # USB Vendor ID
35
+ VENDOR_ID = 0x04d8
36
+
37
+ ##
38
+ # USB Device ID
39
+ DEVICE_ID = 0xc002
40
+
41
+
42
+
43
+ ##
44
+ # Enumerates the picoLCD devices attached to the system.
45
+ def self.devices(refresh = false)
46
+ @devices = nil if refresh
47
+ @devices ||= HIDAPI.enumerate(VENDOR_ID, DEVICE_ID, as: 'BarkestLcd::PicoLcdGraphic')
48
+ end
49
+
50
+ ##
51
+ # Resets the device.
52
+ def reset
53
+ write [ OUT_REPORT.LCD_RESET, 0x01, 0x00 ]
54
+
55
+ (0...4).each do |csi|
56
+ cs = (csi << 2) & 0xFF
57
+ write [ OUT_REPORT.CMD, cs, 0x02, 0x00, 0x64, 0x3F, 0x00, 0x64, 0xC0 ]
58
+ end
59
+
60
+ reset_hook.each { |hook| hook.call(self) }
61
+
62
+ self
63
+ end
64
+
65
+
66
+ ##
67
+ # Processes any waiting input from the device and paints the screen if it is dirty.
68
+ def loop
69
+ data = read
70
+ if data && data.length > 0
71
+
72
+ type = data.getbyte(0)
73
+ data = data[1..-1]
74
+
75
+ input_hook(type).call(self, type, data)
76
+
77
+ end
78
+
79
+ loop_hook.each { |hook| hook.call(self) }
80
+
81
+ self
82
+ end
83
+
84
+
85
+
86
+ # :nodoc:
87
+ def self.method_missing(meth, *args, &block)
88
+ # pass through methods like 'first', 'count', 'each', etc to the 'devices' list.
89
+ list = devices
90
+ if list.respond_to?(meth)
91
+ list.send meth, *args, &block
92
+ else
93
+ super meth, *args, &block
94
+ end
95
+ end
96
+
97
+
98
+
99
+ protected
100
+
101
+
102
+ ##
103
+ # Hooks a block to run for a specific incoming type.
104
+ #
105
+ # Yields the device instance, type code, and the data to the block.
106
+ def input_hook(incoming_type, method_name = nil, &block)
107
+ @input_hook ||= {}
108
+
109
+ if block_given?
110
+ # set the hook.
111
+ @input_hook[incoming_type] = block
112
+ elsif method_name
113
+ @input_hook[incoming_type] = Proc.new { |dev, type, data| dev.send(method_name, dev, type, data) }
114
+ else
115
+ # get the hook
116
+ @input_hook[incoming_type] ||= Proc.new { |dev, type, data| HIDAPI.debug "no input hook for #{type} message type with #{data.length} bytes of data" }
117
+ end
118
+ end
119
+
120
+
121
+ ##
122
+ # Hooks a block to run during the loop method.
123
+ #
124
+ # Yields the device instance.
125
+ def loop_hook(method_name = nil, &block)
126
+ @loop_hook ||= []
127
+ if block_given?
128
+ @loop_hook << block
129
+ elsif method_name
130
+ @loop_hook << Proc.new { |dev| dev.send(method_name, dev) }
131
+ end
132
+ @loop_hook
133
+ end
134
+
135
+
136
+ ##
137
+ # Hooks a block to run during the reset method.
138
+ #
139
+ # Yields the device instance.
140
+ def reset_hook(method_name = nil, &block)
141
+ @reset_hook ||= []
142
+ if block_given?
143
+ @reset_hook << block
144
+ elsif method_name
145
+ @reset_hook << Proc.new { |dev| dev.send(method_name, dev) }
146
+ end
147
+ @reset_hook
148
+ end
149
+
150
+
151
+ ##
152
+ # Loops while the block returns true and the timeout hasn't expired.
153
+ def loop_while(options={}, &block)
154
+ timeout = options.delete(:timeout) || 2500
155
+ timeout = 10 if timeout < 10
156
+
157
+ result = block_given? ? block.call : true
158
+ while result
159
+ sleep 0.01
160
+ result = block_given? ? block.call : true
161
+ unless result
162
+ timeout -= 10
163
+ raise BarkestLcd::PicoLcdGraphic::Timeout if timeout < 0
164
+ end
165
+ end
166
+
167
+ true
168
+ end
169
+
170
+ # Turn off blocking for the PicoLcdGraphic.
171
+ # If the user decides, they can turn blocking back on.
172
+ init_hook do |dev|
173
+ dev.blocking = false
174
+ end
175
+
176
+
177
+
178
+ end
179
+ end
180
+
181
+ # Include the components of the model.
182
+ Dir.glob(File.expand_path('../pico_lcd_graphic/*.rb', __FILE__)).each do |file|
183
+ require file
184
+ end
@@ -0,0 +1,97 @@
1
+ require 'models/simple_graphic'
2
+
3
+ BarkestLcd::PicoLcdGraphic.class_eval do
4
+ include BarkestLcd::SimpleGraphic
5
+
6
+ init_hook :init_display
7
+
8
+
9
+ ##
10
+ # Width of the screen in pixels.
11
+ SCREEN_W = 256
12
+
13
+ ##
14
+ # Height of the screen in pixels.
15
+ SCREEN_H = 64
16
+
17
+ ##
18
+ # Default contrast for the screen.
19
+ DEFAULT_CONTRAST = 0xE5
20
+
21
+ ##
22
+ # Default brightness for the screen.
23
+ DEFAULT_BACKLIGHT = 0x7F
24
+
25
+
26
+ ##
27
+ # Sets the backlight level.
28
+ def backlight(level = 0xFF)
29
+ level &= 0xFF
30
+ write [ OUT_REPORT.LCD_BACKLIGHT, level ]
31
+ self
32
+ end
33
+
34
+
35
+ ##
36
+ # Sets the contrast level.
37
+ def contrast(level = 0xFF)
38
+ level &= 0xFF
39
+ write [ OUT_REPORT.LCD_CONTRAST, level ]
40
+ self
41
+ end
42
+
43
+
44
+ ##
45
+ # Sends the screen contents to the device.
46
+ def paint(force = false)
47
+ if dirty? || force
48
+ # 4 chips each holding 64x64 of data.
49
+ (0..3).each do |csi|
50
+ cs = (csi << 2)
51
+ # each memory line holds 64 bytes, or 8 rows of data.
52
+ (0..7).each do |line|
53
+ # use dirty rectangles to avoid sending unnecessary data to the device.
54
+ if force || dirty_rect?(csi * 64, line * 8, 64, 8)
55
+ # send the data in two packets for each memory line.
56
+ packet_1 = [ OUT_REPORT.CMD_DATA, cs, 0x02, 0x00, 0x00, 0xb8 | line, 0x00, 0x00, 0x40, 0x00, 0x00, 32 ]
57
+ packet_2 = [ OUT_REPORT.DATA, cs | 0x01, 0x00, 0x00, 32 ]
58
+
59
+ (0..63).each do |index|
60
+ # each byte holds the data for 8 rows.
61
+ byte = 0x00
62
+ (0..7).each do |bit|
63
+ x = (csi * 64) + index
64
+ y = ((line * 8) + bit) % height
65
+
66
+ byte |= (1 << bit) if get_bit(x, y)
67
+ end
68
+
69
+ # add the byte to the correct packet.
70
+ (index < 32 ? packet_1 : packet_2) << byte
71
+ end
72
+ # send the packets.
73
+ write packet_1
74
+ write packet_2
75
+ end
76
+ end
77
+ end
78
+ clear_dirty
79
+ end
80
+ self
81
+ end
82
+
83
+
84
+ private
85
+
86
+ def init_display(_)
87
+ init_graphic SCREEN_W, SCREEN_H
88
+ loop_hook { |_| paint }
89
+ reset_hook do |_|
90
+ clear.paint
91
+ contrast DEFAULT_CONTRAST
92
+ backlight DEFAULT_BACKLIGHT
93
+ end
94
+ end
95
+
96
+
97
+ end
@@ -0,0 +1,107 @@
1
+ require 'models/hash_enum'
2
+
3
+ BarkestLcd::PicoLcdGraphic.class_eval do
4
+
5
+ ##
6
+ # Status codes that may be returned from the device.
7
+ STATUS = BarkestLcd::HashEnum.new(
8
+ OK: 0x00,
9
+ ERASE: 0x01,
10
+ WRITE: 0x02,
11
+ READ: 0x03,
12
+ ERROR: 0xFF,
13
+ KEY: 0x10,
14
+ IR: 0x11,
15
+ VER: 0x12,
16
+ DISCONNECTED: 0x13,
17
+ )
18
+
19
+ ##
20
+ # Reports received from the device.
21
+ IN_REPORT = BarkestLcd::HashEnum.new(
22
+ POWER_STATE: 0x01,
23
+ KEY_STATE: 0x11,
24
+ IR_DATA: 0x21,
25
+ EXT_EE_DATA: 0x31,
26
+ INT_EE_DATA: 0x32,
27
+ )
28
+
29
+ ##
30
+ # Reports sent to the device.
31
+ OUT_REPORT = BarkestLcd::HashEnum.new(
32
+ LED_STATE: 0x81,
33
+ LCD_BACKLIGHT: 0x91,
34
+ LCD_CONTRAST: 0x92,
35
+ CMD: 0x94,
36
+ DATA: 0x95,
37
+ CMD_DATA: 0x96,
38
+ LCD_RESET: 0x93,
39
+ RELAY_ONOFF: 0xB1,
40
+ TESTSPLASH: 0xC1,
41
+ EXT_EE_READ: 0xA1,
42
+ EXT_EE_WRITE: 0xA2,
43
+ INT_EE_READ: 0xA3,
44
+ INT_EE_WRITE: 0xA4,
45
+ )
46
+
47
+ ##
48
+ # Splash IDs.
49
+ ID_SPLASH = BarkestLcd::HashEnum.new(
50
+ TIMER: 0x72,
51
+ CYCLE_START: 0x72,
52
+ CYCLE_END: 0x73,
53
+ )
54
+
55
+
56
+ ##
57
+ # Types for flash operations.
58
+ FLASH_TYPE = BarkestLcd::HashEnum.new(
59
+ CODE_MEMORY: 0x00,
60
+ EPROM_EXTERNAL: 0x01,
61
+ EPROM_INTERNAL: 0x02,
62
+ CODE_SPLASH: 0x03,
63
+ )
64
+
65
+ ##
66
+ # HID reports for device.
67
+ HID_REPORT = BarkestLcd::HashEnum.new(
68
+ GET_VERSION_1: 0xF1,
69
+ GET_VERSION_2: 0xF7,
70
+ GET_MAX_STX_SIZE: 0xF6,
71
+ EXIT_FLASHER: 0xFF,
72
+ EXIT_KEYBOARD: 0xEF,
73
+ SET_SNOOZE_TIME: 0xF8,
74
+ ERROR: 0x10,
75
+ )
76
+
77
+ ##
78
+ # Flash reports for the device.
79
+ FLASH_REPORT = BarkestLcd::HashEnum.new(
80
+ ERASE_MEMORY: 0xF2,
81
+ READ_MEMORY: 0xF3,
82
+ WRITE_MEMORY: 0xF4,
83
+ )
84
+
85
+ ##
86
+ # Keyboard reports for the device.
87
+ KEYBD_REPORT = BarkestLcd::HashEnum.new(
88
+ ERASE_MEMORY: 0xB2,
89
+ READ_MEMORY: 0xB3,
90
+ WRITE_MEMORY: 0xB4,
91
+ MEMORY: 0x41,
92
+ )
93
+
94
+ ##
95
+ # Request results.
96
+ RESULT = BarkestLcd::HashEnum.new(
97
+ OK: 0x00,
98
+ PARAM_MISSING: 0x01,
99
+ DATA_MISSING: 0x02,
100
+ BLOCK_READ_ONLY: 0x03,
101
+ BLOCK_NOT_ERASABLE: 0x04,
102
+ BLOCK_TOO_BIG: 0x05,
103
+ SECTION_OVERFLOW: 0x06,
104
+
105
+ )
106
+
107
+ end