barkest_lcd 0.4.0
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +12 -0
- data/barkest_lcd.gemspec +29 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fonts/label.png +0 -0
- data/fonts/slkscr.ttf +0 -0
- data/fonts/slkscrb.ttf +0 -0
- data/lib/barkest_lcd.rb +10 -0
- data/lib/barkest_lcd/version.rb +3 -0
- data/lib/models/error_logger.rb +21 -0
- data/lib/models/font.rb +404 -0
- data/lib/models/font/silkscr.rb +1628 -0
- data/lib/models/font/silkscrb.rb +1627 -0
- data/lib/models/hash_enum.rb +91 -0
- data/lib/models/pico_lcd_graphic.rb +184 -0
- data/lib/models/pico_lcd_graphic/display.rb +97 -0
- data/lib/models/pico_lcd_graphic/enums.rb +107 -0
- data/lib/models/pico_lcd_graphic/flasher.rb +126 -0
- data/lib/models/pico_lcd_graphic/ir.rb +31 -0
- data/lib/models/pico_lcd_graphic/key.rb +92 -0
- data/lib/models/pico_lcd_graphic/splash.rb +40 -0
- data/lib/models/pico_lcd_graphic/version.rb +40 -0
- data/lib/models/simple_graphic.rb +467 -0
- metadata +128 -0
@@ -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
|