tamashii-agent 0.3.1 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e9ec90a332693cf4e81bc2c7425eca46d8dc7d2
4
- data.tar.gz: 970f014149a4cf6f427e8217323414be5f80a744
3
+ metadata.gz: f9d22e0f412ffe228a1d91b28c78da5108f60024
4
+ data.tar.gz: 686f0b637d4a6a941b06fff85df869b568fb4f0d
5
5
  SHA512:
6
- metadata.gz: 4b7035125ae4da75c9bae4cb2cca8b601a1215a57d0da0654195b7611607e11a2577b1e54fcdbe607b914edf29173aa360bb52e5a4e29f140e8cb650de7114a1
7
- data.tar.gz: 99a438cc526180f5cf261f2a7f9582733251f9285cd6ed489e94bf388907efa1e409cbba77c1460ee513b1fed008ddf604c8ace5585e9692f8238af95b15c7c4
6
+ metadata.gz: 59b573c6b0a744a33c19d1424d62abb05173df4e1fa546c48ec5bb8d253c65d4ba3c34fef3217c7113181968e396b9ca9ece0d40ffa576e04034684e36613638
7
+ data.tar.gz: 7cdd4e93f41cd4e66e640c8636917f2c4c93b03a50c5e591a73677c33c1911e23c7702411c0c80334ce1e311f8cfa43562b73fb9dc500f889dfe791bbefa93ab
data/README.md CHANGED
@@ -1,38 +1,251 @@
1
- # Tamashii::Agent
1
+ Tamashii Agent [![Gem Version](https://badge.fury.io/rb/tamashii-agent.svg)](https://badge.fury.io/rb/tamashii-agent)
2
+ ===
2
3
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/tamashii/agent`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+ Tamashii Agent is the client gem opposites to the server gem [Tamashii Manager](https://github.com/tamashii-io/tamashii-manager).
5
+
6
+ The Tamashii Agent runs on Raspberry PI. It manages the IoT components on the device and provides high-level API to let developers control the components easily.
7
+
8
+ It is designed to integrate the devices into internet. With the help of [Tamashii Manager](https://github.com/tamashii-io/tamashii-manager), we can create IoT applications with Rails. The networking is based on [Tamashii Client](https://github.com/tamashii-io/tamashii-client).
9
+
10
+ ## Hardware Requirement
11
+
12
+ Tamashii Agent is designed and tested on Raspberry PI 3 (running Raspbian Jessie). This readme will assume you are using the same version of Raspberry PI (newer versions should also be supported, however the configuration may differ).
13
+
14
+ You need to install this gem on a real device to make it work. Following sections assume you are operating on a correctly configured Raspberry PI.
15
+
16
+ >**Note**: we also provide **dummy** devices. These devices will simulate the behavior of those real devices such as RFID readers and buzzers. With them, the Tamashii Agent doesn't need to be executed on Raspberry PI. You can also run Tamashii Agent on non-Raspberry PI devices such as desktop Linux or Mac. Please refer to [this section](#dummy-devices).
17
+
18
+ Tamashii Agent comes with some common hardware-related modules. They wrap the low-level operation of the devices.
19
+
20
+ You can choose which components to be integrated into your application in [configuration file](#configuration-and-execution). Currently it supports the following hardware that can be attached to a Raspberry PI:
21
+
22
+ - RFID/NFC modules
23
+ - MFRC522 RFID reader (SPI)
24
+ - PN532 NFC module (UART)
25
+ - LCD Displays
26
+ - LCM 1602 (I2C)
27
+ - Buzzers
28
+ - DC buzzer using PWM
29
+
30
+ For pin layout of each component module, please refer to [this page](https://github.com/tamashii-io/tamashii-agent/tree/master/lib/tamashii/agent/device).
4
31
 
5
- TODO: Delete this and the text above, and describe your gem
6
32
 
7
33
  ## Installation
8
34
 
9
- Add this line to your application's Gemfile:
35
+ Add the following code to your `Gemfile`:
10
36
 
11
37
  ```ruby
12
38
  gem 'tamashii-agent'
13
39
  ```
14
40
 
15
41
  And then execute:
42
+ ```
43
+ $ bundle install
44
+ ```
16
45
 
17
- $ bundle
46
+ Or install it yourself with:
47
+ ```
48
+ $ gem install tamashii-agent
49
+ ```
18
50
 
19
- Or install it yourself as:
51
+ After installation, a new command line tool is provided as `tamashii-agent`. Get more information by executing
52
+ ```
53
+ $ tamashii-agent -h
54
+ ```
55
+
56
+ ### Daemonize
57
+
58
+ Although it is okay to run Tamashii Agent as a usual ruby program, Tamashii Agent usually runs as a system daemon on the Raspberry PI.
59
+
60
+ This gem also provides a simple daemon installation for `systemd`.
61
+ If you are using Raspberry PI running `systemd` (such as Raspbian Jessie), you can install daemon scripts by executing:
62
+ ```
63
+ $ tamashii-agent --install-systemd
64
+ ```
65
+
66
+ This will generate a `systemd` script `/etc/systemd/system/tamashii-agent.service` and a default configuration file `/etc/tamashii-agent-config.rb`. Modify them to match your need.
20
67
 
21
- $ gem install tamashii-agent
22
68
 
23
69
  ## Usage
24
70
 
25
- TODO: Write usage instructions here
71
+ ### Configuration and Execution
72
+
73
+ Whether the Tamashii Agent is running as system daemon or simply as a ruby program, we need to write a configuration file for it. The default configuration generated by `tamashii-agent --install-systemd` is:
74
+
75
+ ```ruby
76
+ Tamashii::Agent::config do
77
+ # these connection configuration will be forwarded to Tamashii Client
78
+ # please refer to configuration of Tamashii Client
79
+ use_ssl false
80
+ host "localhost"
81
+ port 3000
82
+ entry_point "/tamashii"
83
+ # where to output logs. Default is STDOUT
84
+ log_file "/var/log/tamashii-agent.log"
85
+ # the log level. Default is :DEBUG
86
+ log_level :INFO
87
+ # the authentication information. Must be the same as those in Tamashii Manager
88
+ auth_type :token
89
+ token "SECRET_TOKEN"
90
+ end
91
+ ```
92
+
93
+ The configuration above does not drive any extra components such as NFC readers or buzzers. It only establishs a connection to the [Tamashii Manager](https://github.com/tamashii-io/tamashii-manager) running on `localhost:3000`.
94
+
95
+ - To run as system daemon:
96
+ - Put the configuration file in `/etc/tamashii-agent-config.rb`.
97
+ - Restart the agent by
98
+
99
+ ```
100
+ $ sudo systemctl restart tamashii-agent
101
+ ```
102
+
103
+ - To execute Tamashii Agent as a usual ruby program
104
+ - Specify the path to the configuration file by `-C` option.
105
+ - For example, to load the configuration file in `/path/to/tamashii-agent-config.rb`:
106
+
107
+ ```
108
+ $ sudo tamashii-agent -C /path/to/tamashii-agent-config.rb
109
+ ```
110
+
111
+ > Note: many hardware operations on Raspberry PI require root permission to access the IO. Don't forget to use `sudo` to run the agent command.
112
+
113
+ ### Simple Example for Testing Tamashii Agent
114
+
115
+ The following program is a simple server based on [Tamashii Manager](https://github.com/tamashii-io/tamashii-manager). In this example, the Agent will play a beep sound when swiping the RFID card on the reader.
116
+
117
+ > **Note**: you need to connect a RFID reader (either MFRC522 or PN532) and a DC buzzer to your Raspberry PI. Please refer to [this page](https://github.com/tamashii-io/tamashii-agent/tree/master/lib/tamashii/agent/device) about the setup details.
118
+
119
+ > **Note**: the code below requires Tamashii Manager correctly installed on your system. You may need to install some dependencies such as `redis`.
120
+
121
+ ```ruby
122
+ class MyPacketHook < Tamashii::Hook
123
+ def call(request_packet)
124
+ # we only cares about RFID NUMBER from agent
125
+ if request_packet.type == Tamashii::Type::RFID_NUMBER
126
+ # fetch client infomation from env
127
+ client = @env[:client]
128
+
129
+ # parse data in the packet (in json format)
130
+ json = JSON.parse(request_packet.body)
131
+ packet_id = json['id']
132
+ card_id = json['ev_body']
133
+
134
+ # build response in json format
135
+ type = Tamashii::Type::RFID_RESPONSE_JSON
136
+ body = {
137
+ id: packet_id,
138
+ ev_body: {
139
+ auth: true # true for 1 beep, false for 3 beeps
140
+ }.to_json
141
+ }.to_json
142
+
143
+ # build packet
144
+ response_packet = Tamashii::Packet.new(
145
+ type, client.tag, body
146
+ )
147
+ # send to client
148
+ client.send(response_packet.dump)
149
+ # return true means we have already handled this packet
150
+ return true
151
+ else
152
+ # return false so that the default handler can be executed
153
+ return false
154
+ end
155
+ end
156
+ end
157
+
158
+ Tamashii::Manager.config do |config|
159
+ config.port = 3000
160
+ # this pre-shared token can exclude the unwanted access
161
+ config.auth_type = :token
162
+ config.token = 'abc123'
163
+ end
164
+
165
+ Tamashii::Resolver.config do
166
+ # forward the packet to our hook
167
+ hook MyPacketHook
168
+ end
169
+ ```
170
+
171
+ Save the code above as `manager_config.rb`, then execute the following command to boot up Tamashii Manager.
172
+ ```
173
+ $ tamashii-manager -C ./manager_config.rb
174
+ ```
175
+
176
+ Now let's write a simple configuration for Tamashii Agent:
177
+ ```ruby
178
+ Tamashii::Agent.config do
179
+ # The token must be same as server's
180
+ auth_type :token
181
+ token "abc123"
182
+ # Remember to change to your Tamashii Manager's hostname or IP!
183
+ host "tamashii.manager.local"
184
+ port 3000
185
+
186
+ # components to use
187
+ # the buzzer to play 'beep' sound. Assume the PWM buzzer is connected at pin 19
188
+ add_component :buzzer, 'Buzzer', device: 'PwmBuzzer', pin: 19
189
+
190
+ # card readers
191
+ # assume the PN532 NFC module is connected via UART interface
192
+ add_component :card_reader_felica, 'CardReader', device: 'Pn532Uart'
193
+ # uncomment following line if you are using MFRC522 via SPI interface
194
+ # add_component :card_reader_mfrc, 'CardReader', device: 'Mfrc522Spi'
195
+ end
196
+ ```
197
+
198
+ Save the code above as `agent_config.rb`, then execute following command to boot up Tamashii Agent.
199
+ ```
200
+ $ sudo tamashii-agent -C ./agent_config.rb
201
+ ```
202
+
203
+ Now you can swipe a card on the reader! You should hear a beep sound if everything works fine.
204
+
205
+ ## Dummy Devices
206
+
207
+ Although the Tamashii Agent is designed to run on Raspberry PI, if you need to develop applications on non-Raspberry PI platforms, you can use the **Dummy** devices. Dummy devices simulate the behavior of RFID readers and buzzers. To use them, you need to specify the `Dummy` devices in the configuration file.
208
+
209
+ For example, in the simple example mentioned above, you can change the configuration so that the example can run on non-Raspberry PI platforms:
210
+
211
+ ```ruby
212
+ Tamashii::Agent.config do
213
+ # The token must be same as server's
214
+ auth_type :token
215
+ token "abc123"
216
+ # Remember to change to your Tamashii Manager's hostname or IP!
217
+ host "tamashii.manager.local"
218
+ port 3000
219
+
220
+ # dummy devices
221
+ add_component :buzzer, 'Buzzer', device: 'Dummy'
222
+ add_component :card_reader, 'CardReader', device: 'Dummy'
223
+ end
224
+ ```
225
+
226
+ The dummy buzzers will print the "beep sound" into the log file, while the dummy card reader generates random card uids periodically.
26
227
 
27
228
  ## Development
28
229
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
230
+ To get the source code
231
+
232
+ $ git clone git@github.com:tamashii-io/tamashii-agent.git
233
+
234
+ Initialize the development environment
235
+
236
+ $ ./bin/setup
237
+
238
+ Run the spec
239
+
240
+ $ rspec
241
+
242
+ Installation the version of development on localhost
30
243
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
244
+ $ bundle exec rake install
32
245
 
33
- ## Contributing
246
+ ## Contribution
34
247
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/lctseng/tamashii-agent.
248
+ Please report to us on [Github](https://github.com/tamashii-io/tamashii-agent) if there is any bug or suggested modified.
36
249
 
37
- Contributed by [5xruby Inc.](https://5xruby.tw/)
250
+ The project was developed by [5xruby Inc.](https://5xruby.tw/)
38
251
 
@@ -10,7 +10,11 @@ module Tamashii
10
10
  end
11
11
 
12
12
  def progname
13
- @progname ||= ("%-10s" % self.class.to_s.split(":")[-1])
13
+ @progname ||= ("%-10s" % display_name)
14
+ end
15
+
16
+ def display_name
17
+ self.class.to_s.split(":")[-1]
14
18
  end
15
19
  end
16
20
  end
@@ -108,6 +108,10 @@ module Tamashii
108
108
  def get_device_class_name(device_name)
109
109
  raise NotImplementedError
110
110
  end
111
+
112
+ def display_name
113
+ @name
114
+ end
111
115
  end
112
116
  end
113
117
  end
@@ -0,0 +1,101 @@
1
+ Device Setup
2
+ ===
3
+
4
+ This page describes the connection of hardware supported by Tamashii Agent.
5
+ Following figure shows a typical Raspberry PI and pins on it.
6
+
7
+ ![Raspberry Pins](https://tamashii.io/images/devices/rpi_pins.png)
8
+
9
+ ## Buzzers
10
+
11
+ ### PWM buzzer
12
+
13
+ ![PWM Buzzer](https://tamashii.io/images/devices/pwm_buzzer.jpg)
14
+
15
+ - Connect positive wire (the red one) to the PWM GPIO pin
16
+ - Example in figure: pin 35 (GPIO 19)
17
+ - The **GPIO number** (not the pin number) should be specify in the configuration
18
+ ```ruby
19
+ add_component :buzzer, 'Buzzer', device: 'PwmBuzzer', pin: 19
20
+ ```
21
+ - Connect the negative wire (the black one) to the GND pin
22
+ - Example in figure: pin 34 (GND)
23
+
24
+ ## Card Readers
25
+
26
+ ### MFRC522 via SPI
27
+
28
+ ![MFRC522 SPI](https://tamashii.io/images/devices/mfrc522_spi.jpg)
29
+ - Connect corresponding pins from reader module to Raspberry PI
30
+ - reader NSS to pin 24 (SPI0 CS0)
31
+ - reader SCK to pin 23 (SPI0 SCLK)
32
+ - reader MOSI to pin 19 (MOSI)
33
+ - reader MISO to pin 21 (MISO)
34
+ - reader GND to pin 20 (GND)
35
+ - reader RST522 to pin 18 (GPIO 24)
36
+ - The RESET pin
37
+ - Could be changed in configuration
38
+ ```ruby
39
+ add_component :card_reader_mfrc, 'CardReader', device: 'Mfrc522Spi', reset_pin: 24
40
+ ```
41
+ - reader VIN to pin 4(5V PWR)
42
+
43
+ ### PN532 via UART
44
+
45
+ ![PN532 UART](https://tamashii.io/images/devices/pn532_uart.jpg)
46
+ - Connect corresponding pins from reader module to Raspberry PI
47
+ - reader GND to pin 6 (GND)
48
+ - reader VIN to pin 1 (3.3V PWR)
49
+ - reader **TX** to pin 10 (UART0 **RX**)
50
+ - reader **RX** to pin 8 (UART0 **TX**)
51
+ - On Raspberry 3 (Jessie), the Bluetooth module occupied the UART interface `/dev/ttyAMA0`. Before you using PN532 via UART, you need to disable Bluetooth first.
52
+ - Disable serial console and enable serial hardware using `rasp-config`
53
+ - `rasp-config` => `Interfacing Options` => `Serial`
54
+ - Disable serial login shell
55
+ - Enable serial port hardware
56
+ - Disable Bluetooth
57
+ - Add following line at the end of `/boot/config.txt`
58
+ - This will also make `/dev/serial0` points to `/dev/ttyAMA0`
59
+ ```
60
+ dtoverlay=pi3-disable-bt
61
+ ```
62
+ - Disable Bluetooth services
63
+ ```
64
+ sudo systemctl stop hciuart
65
+ sudo systemctl disable hciuart
66
+ ```
67
+ - Install `libnfc`, refer to [this page](https://www.raspberrypi.org/forums/viewtopic.php?t=78966)
68
+ - We do not need the extra steps after `sudo make install`
69
+ - Create or modify `libnfc` configuration file. Modify or create one of the following files:
70
+ - `/etc/nfc/libnfc.conf`
71
+ ```
72
+ device.name = "PN532 board via UART"
73
+ device.connstring = "pn532_uart:/dev/ttyAMA0"
74
+ ```
75
+
76
+ - or `/etc/nfc/devices.d/pn53x_uart.conf`
77
+ ```
78
+ name = "PN532 board via UART"
79
+ connstring = pn532_uart:/dev/ttyAMA0
80
+ ```
81
+
82
+
83
+
84
+ - Remove `console=serial0` from `/boot/cmdline.txt`, if any.
85
+ - Reboot
86
+
87
+
88
+ ## LCDs
89
+
90
+ ## LCM 1602 via I2C
91
+
92
+ ![LCM602 I2C](https://tamashii.io/images/devices/lcm1602_i2c.jpg)
93
+
94
+ - Connect corresponding pins from lcd module to Raspberry PI
95
+ - lcd GND to pin 9 (GND)
96
+ - lcd VCC to pin 2 (5V PWR)
97
+ - lcd SDA to pin 3 (SDA)
98
+ - lcd SCL to pin 5 (SCL)
99
+ - Enable I2C interface on your Raspberry PI
100
+ - Using `rasp-config` => `Interfacing Options` => `I2C`
101
+ - If successful, you should able to see `/dev/i2c-1` under `/dev` on Raspberry PI
@@ -5,9 +5,8 @@ module Tamashii
5
5
  module Agent
6
6
  module Device
7
7
  module Buzzer
8
- class GpioBuzzer < Base
8
+ class PwmBuzzer < Base
9
9
 
10
- DEFAULT_PIN = 18
11
10
  SHORT_PLAY_TIME = 0.2
12
11
  LONG_PLAY_TIME = 0.5
13
12
  REPEAT_INTERVAL = 0.1
@@ -34,6 +33,10 @@ module Tamashii
34
33
  play_repeat_long(3)
35
34
  end
36
35
 
36
+ def default_pin
37
+ 19
38
+ end
39
+
37
40
  private
38
41
 
39
42
  def stop
@@ -41,10 +44,7 @@ module Tamashii
41
44
  end
42
45
 
43
46
  def setup_pwm
44
- unless pin = @options[:pin]
45
- logger.warn "No GPIO pin given. Using default: #{DEFAULT_PIN}"
46
- pin = DEFAULT_PIN
47
- end
47
+ pin = fetch_option(:pin, default_pin)
48
48
  @pwm = PiPiper::Pwm.new pin: pin #, mode: :markspace
49
49
  @pwm.off
50
50
  @pwm.value = 1.0
@@ -9,7 +9,11 @@ module Tamashii
9
9
 
10
10
  def initialize(*args)
11
11
  super
12
- @reader = MFRC522.new
12
+ @reader = MFRC522.new(fetch_option(:reset_pin, default_reset_pin))
13
+ end
14
+
15
+ def default_reset_pin
16
+ 24
13
17
  end
14
18
 
15
19
  def poll_uid
@@ -9,7 +9,7 @@ module Tamashii
9
9
  def initialize(*args)
10
10
  super
11
11
  @ctx = NFC::Context.new
12
- @dev = @ctx.open "pn532_uart:#{fetch_path}"
12
+ @dev = @ctx.open "pn532_uart:#{fetch_option(:path, default_path)}"
13
13
  @card_type = @options[:card_type] || :felica
14
14
  logger.info "Card type enabled: #{@card_type}"
15
15
  end
@@ -18,16 +18,6 @@ module Tamashii
18
18
  "/dev/ttyAMA0"
19
19
  end
20
20
 
21
- def fetch_path
22
- if @options.has_key?(:path)
23
- path = @options[:path]
24
- else
25
- path = default_path
26
- logger.warn "No path specified. Use default path: #{path}"
27
- end
28
- path
29
- end
30
-
31
21
  def poll_uid
32
22
  tag = @dev.poll(@card_type)
33
23
  if tag && !tag.is_a?(Integer)
@@ -1,3 +1,4 @@
1
+ require 'pi_piper'
1
2
  require 'tamashii/agent/common'
2
3
 
3
4
  module Tamashii
@@ -6,6 +7,8 @@ module Tamashii
6
7
  class DeviceBase
7
8
  include Common::Loggable
8
9
 
10
+ class OptionNotFoundError < RuntimeError; end
11
+
9
12
  def initialize(component, options = {})
10
13
  @component = component
11
14
  @options = options
@@ -14,6 +17,30 @@ module Tamashii
14
17
  def shutdown
15
18
  logger.warn "Device '#{self.class}' does not implement a shutdown method"
16
19
  end
20
+
21
+ def fetch_option(name, default_value)
22
+ fetch_option!(name)
23
+ rescue OptionNotFoundError => e
24
+ logger.warn "No #{name} specified in options. Use default #{name}: #{default_value}"
25
+ return default_value
26
+ end
27
+
28
+ def fetch_option!(name)
29
+ if @options.has_key?(name)
30
+ return @options[name]
31
+ else
32
+ raise OptionNotFoundError, "#{name} not found in option"
33
+ end
34
+ end
35
+
36
+ def unexport_pin(pin_number)
37
+ raise ArgumentErrorm, "pin number must be a integer" unless pin_number.is_a? Integer
38
+ if PiPiper::Platform.driver == PiPiper::Bcm2835
39
+ PiPiper::Platform.driver.unexport_pin(pin_number)
40
+ else
41
+ logger.warn "Underlying driver #{PiPiper::Platform.driver} does not support unexporting"
42
+ end
43
+ end
17
44
  end
18
45
  end
19
46
  end
@@ -0,0 +1,159 @@
1
+ require 'concurrent'
2
+ require 'tamashii/agent/device/device_base'
3
+
4
+ module Tamashii
5
+ module Agent
6
+ module Device
7
+ module Keyboard
8
+ class Base < DeviceBase
9
+
10
+ def initialize(*args)
11
+ super(*args)
12
+ initialize_hardware
13
+ @number_of_keys = fetch_option(:number_of_keys, default_number_of_keys)
14
+ @watcher_mode = fetch_option(:watch, default_watch)
15
+ @watcher_stopping = Concurrent::AtomicBoolean.new(false)
16
+ if @watcher_mode
17
+ initialize_watcher
18
+ start_watcher_thread
19
+ end
20
+ end
21
+
22
+ def default_number_of_keys
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def default_watch
27
+ true
28
+ end
29
+
30
+ def polling_interval
31
+ 0.1
32
+ end
33
+
34
+ def initialize_hardware
35
+ logger.warn "Device #{@name} does not implement hardware initialize code"
36
+ end
37
+
38
+ def finalize_hardware
39
+ logger.warn "Device #{@name} does not implement hardware finalize code"
40
+ end
41
+
42
+ def initialize_watcher
43
+ @last_keys_state = {}
44
+ @current_keys_state = {}
45
+ @current_key = Concurrent::AtomicFixnum.new(-1)
46
+ @callbacks = {}
47
+ end
48
+
49
+ def add_callback(event, callable)
50
+ if @watcher_mode
51
+ @callbacks[event] = callable
52
+ else
53
+ puts "Callbacks are only available in watcher mode!"
54
+ end
55
+ end
56
+
57
+ def on_key_down(callable = nil, &block)
58
+ add_callback(:down, callable || block)
59
+ end
60
+
61
+ def on_key_up(callable = nil, &block)
62
+ add_callback(:up, callable || block)
63
+ end
64
+
65
+ def on_key_pressed(callable = nil, &block)
66
+ add_callback(:pressed, callable || block)
67
+ end
68
+
69
+ def watcher_stopping?
70
+ @watcher_stopping.true?
71
+ end
72
+
73
+ def start_watcher_thread
74
+ @watcher_thread = Thread.new { watcher_loop }
75
+ end
76
+
77
+ def watcher_loop
78
+ puts "watcher started"
79
+ loop do
80
+ if watcher_stopping?
81
+ break
82
+ end
83
+ sleep polling_interval
84
+ record_key_state
85
+ read_key
86
+ process_callbacks
87
+ end
88
+ puts "watcher thread terminated normally"
89
+ end
90
+
91
+ def process_callbacks
92
+ @number_of_keys.times do |key|
93
+ if @current_keys_state[key]
94
+ if @last_keys_state[key]
95
+ @callbacks[:pressed]&.call(key)
96
+ else
97
+ @callbacks[:down]&.call(key)
98
+ end
99
+ else
100
+ if @last_keys_state[key]
101
+ @callbacks[:up]&.call(key)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def record_key_state
108
+ @number_of_keys.times do |key|
109
+ @last_keys_state[key] = @current_keys_state[key]
110
+ end
111
+ end
112
+
113
+ def stop_watcher_thread
114
+ if !@watcher_thread.join(3)
115
+ @watcher_thread.exit
116
+ puts "watcher thread killed forcefully"
117
+ end
118
+ end
119
+
120
+ def poll_key
121
+ if @watcher_mode
122
+ @current_key.value >= 0 ? @current_key.value : nil
123
+ else
124
+ sleep polling_interval
125
+ read_key
126
+ end
127
+ end
128
+
129
+ def shutdown
130
+ @watcher_stopping.make_true
131
+ if @watcher_mode
132
+ stop_watcher_thread
133
+ end
134
+ finalize_hardware
135
+ end
136
+
137
+ def mark_key_up(key)
138
+ return unless @watcher_mode
139
+ if @current_key.value == key
140
+ # remove current key
141
+ @current_key.value = -1
142
+ end
143
+ @current_keys_state[key] = false
144
+ end
145
+
146
+ def mark_key_down(key)
147
+ return unless @watcher_mode
148
+ @current_key.value = key
149
+ @current_keys_state[key] = true
150
+ end
151
+
152
+ def read_key
153
+ raise NotImplementedError, "read_key"
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,43 @@
1
+ require 'pi_piper'
2
+ require 'tamashii/agent/device/keyboard/base'
3
+
4
+ module Tamashii
5
+ module Agent
6
+ module Device
7
+ module Keyboard
8
+ class Dummy < Base
9
+ def initialize_hardware
10
+ @last_report = Time.now
11
+ logger.debug "Initialized"
12
+ end
13
+
14
+ def finalize_hardware
15
+ logger.debug "Finalized"
16
+ end
17
+
18
+ def default_number_of_keys
19
+ 8
20
+ end
21
+
22
+ def read_key
23
+ if (Time.now - @last_report) > (3 + rand)
24
+ @last_report = Time.now
25
+ key = rand(@number_of_keys)
26
+ logger.debug "Fake key generated: #{key}"
27
+ @number_of_keys.times do |testing_key|
28
+ if testing_key == key
29
+ mark_key_down(testing_key)
30
+ else
31
+ mark_key_up(testing_key)
32
+ end
33
+ end
34
+ return key
35
+ else
36
+ return nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,61 @@
1
+ require 'pi_piper'
2
+ require 'tamashii/agent/device/keyboard/base'
3
+
4
+ module Tamashii
5
+ module Agent
6
+ module Device
7
+ module Keyboard
8
+ class TTP229Serial < Base
9
+
10
+ HALF_BIT_TIME=0.001
11
+
12
+ def initialize_hardware
13
+ @scl_pin = PiPiper::Pin.new(pin: fetch_option(:scl_pin, default_scl_pin), direction: :out)
14
+ @sdo_pin = PiPiper::Pin.new(pin: fetch_option(:sdo_pin, default_sdo_pin), direction: :in)
15
+ @scl_pin.on
16
+ sleep(HALF_BIT_TIME)
17
+ end
18
+
19
+ def polling_interval
20
+ 10*HALF_BIT_TIME
21
+ end
22
+
23
+ def default_number_of_keys
24
+ 8
25
+ end
26
+
27
+ def default_scl_pin
28
+ 17
29
+ end
30
+
31
+ def default_sdo_pin
32
+ 4
33
+ end
34
+
35
+ def finalize_hardware
36
+ unexport_pin(fetch_option(:scl_pin, default_scl_pin))
37
+ unexport_pin(fetch_option(:sdo_pin, default_sdo_pin))
38
+ end
39
+
40
+ def read_key
41
+ current_key = nil
42
+ @number_of_keys.times do |key|
43
+ @scl_pin.off
44
+ sleep(HALF_BIT_TIME)
45
+ @sdo_pin.read
46
+ if @sdo_pin.off?
47
+ current_key = key
48
+ mark_key_down(key)
49
+ else
50
+ mark_key_up(key)
51
+ end
52
+ @scl_pin.on
53
+ sleep(HALF_BIT_TIME)
54
+ end
55
+ current_key
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -58,29 +58,9 @@ module Tamashii
58
58
 
59
59
  private
60
60
 
61
- def fetch_address
62
- if @options.has_key?(:address)
63
- address = @options[:address]
64
- else
65
- address = default_address
66
- logger.warn "No address specified. Use default address: #{address.to_s(16)}"
67
- end
68
- address
69
- end
70
-
71
- def fetch_path
72
- if @options.has_key?(:path)
73
- path = @options[:path]
74
- else
75
- path = default_path
76
- logger.warn "No path specified. Use default path: #{path}"
77
- end
78
- path
79
- end
80
-
81
61
  def initialize_lcd
82
- @lcd = I2C.create(fetch_path)
83
- @address = fetch_address
62
+ @lcd = I2C.create(fetch_option(:path, default_path))
63
+ @address = fetch_option(:address, default_address)
84
64
  @backlight = @options.fetch(:backlight, true)
85
65
 
86
66
  byte(0x33, OP_CMD)
@@ -0,0 +1,37 @@
1
+ require 'tamashii/agent/component'
2
+ require 'tamashii/agent/event'
3
+
4
+ module Tamashii
5
+ module Agent
6
+ class Keyboard < Component
7
+ def initialize(name, master, options = {})
8
+ options[:watch] = true # Force enable watch mode
9
+ super
10
+ @kb = initialize_device
11
+ @kb.on_key_down do |key|
12
+ logger.debug "Key down: #{key+1}"
13
+ @master.send_event(Event.new(Event::LCD_MESSAGE, "Key pressed: #{key+1} "))
14
+ end
15
+ end
16
+
17
+ def default_device_name
18
+ 'Dummy'
19
+ end
20
+
21
+ def get_device_class_name(device_name)
22
+ "Keyboard::#{device_name}"
23
+ end
24
+
25
+ # override
26
+ def process_event(event)
27
+ # silent is gold
28
+ end
29
+
30
+ def clean_up
31
+ super
32
+ @kb.shutdown
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -3,10 +3,9 @@ require 'tamashii/agent/networking'
3
3
  require 'tamashii/agent/lcd'
4
4
  require 'tamashii/agent/buzzer'
5
5
  require 'tamashii/agent/card_reader'
6
+ require 'tamashii/agent/keyboard'
6
7
  require 'tamashii/agent/event'
7
8
 
8
- require 'thread'
9
-
10
9
  module Tamashii
11
10
  module Agent
12
11
  class Master < Component
@@ -1,5 +1,5 @@
1
1
  module Tamashii
2
2
  module Agent
3
- VERSION = "0.3.1"
3
+ VERSION = "0.3.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tamashii-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - 蒼時弦也
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-09-11 00:00:00.000000000 Z
13
+ date: 2017-09-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -292,14 +292,18 @@ files:
292
292
  - lib/tamashii/agent/common/loggable.rb
293
293
  - lib/tamashii/agent/component.rb
294
294
  - lib/tamashii/agent/config.rb
295
+ - lib/tamashii/agent/device/README.md
295
296
  - lib/tamashii/agent/device/buzzer/base.rb
296
297
  - lib/tamashii/agent/device/buzzer/dummy.rb
297
- - lib/tamashii/agent/device/buzzer/gpio_buzzer.rb
298
+ - lib/tamashii/agent/device/buzzer/pwm_buzzer.rb
298
299
  - lib/tamashii/agent/device/card_reader/base.rb
299
300
  - lib/tamashii/agent/device/card_reader/dummy.rb
300
301
  - lib/tamashii/agent/device/card_reader/mfrc522_spi.rb
301
302
  - lib/tamashii/agent/device/card_reader/pn532_uart.rb
302
303
  - lib/tamashii/agent/device/device_base.rb
304
+ - lib/tamashii/agent/device/keyboard/base.rb
305
+ - lib/tamashii/agent/device/keyboard/dummy.rb
306
+ - lib/tamashii/agent/device/keyboard/ttp229_serial.rb
303
307
  - lib/tamashii/agent/device/lcd/base.rb
304
308
  - lib/tamashii/agent/device/lcd/dummy.rb
305
309
  - lib/tamashii/agent/device/lcd/lcm1602_i2c.rb
@@ -310,6 +314,7 @@ files:
310
314
  - lib/tamashii/agent/handler/lcd.rb
311
315
  - lib/tamashii/agent/handler/remote_response.rb
312
316
  - lib/tamashii/agent/handler/system.rb
317
+ - lib/tamashii/agent/keyboard.rb
313
318
  - lib/tamashii/agent/lcd.rb
314
319
  - lib/tamashii/agent/master.rb
315
320
  - lib/tamashii/agent/networking.rb