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 +4 -4
- data/README.md +226 -13
- data/lib/tamashii/agent/common/loggable.rb +5 -1
- data/lib/tamashii/agent/component.rb +4 -0
- data/lib/tamashii/agent/device/README.md +101 -0
- data/lib/tamashii/agent/device/buzzer/{gpio_buzzer.rb → pwm_buzzer.rb} +6 -6
- data/lib/tamashii/agent/device/card_reader/mfrc522_spi.rb +5 -1
- data/lib/tamashii/agent/device/card_reader/pn532_uart.rb +1 -11
- data/lib/tamashii/agent/device/device_base.rb +27 -0
- data/lib/tamashii/agent/device/keyboard/base.rb +159 -0
- data/lib/tamashii/agent/device/keyboard/dummy.rb +43 -0
- data/lib/tamashii/agent/device/keyboard/ttp229_serial.rb +61 -0
- data/lib/tamashii/agent/device/lcd/lcm1602_i2c.rb +2 -22
- data/lib/tamashii/agent/keyboard.rb +37 -0
- data/lib/tamashii/agent/master.rb +1 -2
- data/lib/tamashii/agent/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9d22e0f412ffe228a1d91b28c78da5108f60024
|
4
|
+
data.tar.gz: 686f0b637d4a6a941b06fff85df869b568fb4f0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59b573c6b0a744a33c19d1424d62abb05173df4e1fa546c48ec5bb8d253c65d4ba3c34fef3217c7113181968e396b9ca9ece0d40ffa576e04034684e36613638
|
7
|
+
data.tar.gz: 7cdd4e93f41cd4e66e640c8636917f2c4c93b03a50c5e591a73677c33c1911e23c7702411c0c80334ce1e311f8cfa43562b73fb9dc500f889dfe791bbefa93ab
|
data/README.md
CHANGED
@@ -1,38 +1,251 @@
|
|
1
|
-
|
1
|
+
Tamashii Agent [![Gem Version](https://badge.fury.io/rb/tamashii-agent.svg)](https://badge.fury.io/rb/tamashii-agent)
|
2
|
+
===
|
2
3
|
|
3
|
-
|
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
|
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
|
-
|
46
|
+
Or install it yourself with:
|
47
|
+
```
|
48
|
+
$ gem install tamashii-agent
|
49
|
+
```
|
18
50
|
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
244
|
+
$ bundle exec rake install
|
32
245
|
|
33
|
-
##
|
246
|
+
## Contribution
|
34
247
|
|
35
|
-
|
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
|
-
|
250
|
+
The project was developed by [5xruby Inc.](https://5xruby.tw/)
|
38
251
|
|
@@ -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
|
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
|
-
|
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,7 @@ module Tamashii
|
|
9
9
|
def initialize(*args)
|
10
10
|
super
|
11
11
|
@ctx = NFC::Context.new
|
12
|
-
@dev = @ctx.open "pn532_uart:#{
|
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(
|
83
|
-
@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
|
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.
|
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-
|
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/
|
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
|