geppeto 0.1.0 → 0.1.1
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 +4 -4
- data/.gitignore +37 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +13 -0
- data/README.md +143 -0
- data/Rakefile +9 -0
- data/geppeto.gemspec +23 -0
- data/lib/commands/commands.rb +16 -0
- data/lib/commands/events.rb +21 -0
- data/lib/commands/hq.rb +29 -0
- data/lib/commands/led.rb +89 -0
- data/lib/commands/memory.rb +13 -0
- data/lib/commands/mesh.rb +49 -0
- data/lib/commands/pin.rb +57 -0
- data/lib/commands/power.rb +45 -0
- data/lib/commands/temperature.rb +29 -0
- data/lib/commands/uptime.rb +47 -0
- data/lib/commands/wifi.rb +41 -0
- data/lib/connections/connections.rb +9 -0
- data/lib/connections/serial.rb +60 -0
- data/lib/connections/server.rb +73 -0
- data/lib/geppeto.rb +9 -0
- data/lib/scout.rb +127 -0
- data/spec/geppeto_spec.rb +10 -0
- data/spec/spec_helper.rb +2 -0
- metadata +32 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f338448313065fa0b431a489d521f284d2a0cc34
|
4
|
+
data.tar.gz: eb1459535f6430a18769d5db66f08c2f01f0e284
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c36ea309872190f52aac21dd622bcdc83175301b397f31dc05e9d8f52e7d3d964506092e7ce5439c544f8c9a3c38f81330e2a82c70b02f97ea4269b5b0976f90
|
7
|
+
data.tar.gz: 950a9f443a72c1b1118e02a30d208688be90963be74a0d5b5c630ad8b95d9f53de9492a551a6020ca1338b446fd3ae5bf43f6b0b429cb50f02646fd3cf4b611b
|
data/.gitignore
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
.ruby-version
|
31
|
+
.ruby-gemset
|
32
|
+
|
33
|
+
# rbenv
|
34
|
+
.rbenv-gemsets
|
35
|
+
|
36
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
37
|
+
.rvmrc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
celluloid (0.16.0)
|
5
|
+
timers (~> 4.0.0)
|
6
|
+
celluloid-io (0.16.1)
|
7
|
+
celluloid (>= 0.16.0)
|
8
|
+
nio4r (>= 1.0.0)
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
hitimes (1.2.2)
|
11
|
+
nio4r (1.0.1)
|
12
|
+
revolver (1.1.1)
|
13
|
+
rspec (3.1.0)
|
14
|
+
rspec-core (~> 3.1.0)
|
15
|
+
rspec-expectations (~> 3.1.0)
|
16
|
+
rspec-mocks (~> 3.1.0)
|
17
|
+
rspec-core (3.1.7)
|
18
|
+
rspec-support (~> 3.1.0)
|
19
|
+
rspec-expectations (3.1.2)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.1.0)
|
22
|
+
rspec-mocks (3.1.3)
|
23
|
+
rspec-support (~> 3.1.0)
|
24
|
+
rspec-support (3.1.2)
|
25
|
+
serialport (1.2.3)
|
26
|
+
timers (4.0.1)
|
27
|
+
hitimes
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
celluloid-io (~> 0.16)
|
34
|
+
revolver (~> 1.1.1)
|
35
|
+
rspec
|
36
|
+
serialport (~> 1.2.3)
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014 Matt Heitzenroder
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# Geppeto
|
2
|
+
Geppeto provides a Ruby API over two different transports, either a TCPSocket
|
3
|
+
or a serial connection, to the [Pinoccio microcontroller](https://pinocc.io). It
|
4
|
+
was created for Pinoccio applications that do not use Pinoccio's `hq.pinocc.io`
|
5
|
+
or `api.pinocc.io`. In my case, the Pinoccio microcontrollers existed on a LAN
|
6
|
+
that did not have an uplink or gateway to the internet.
|
7
|
+
|
8
|
+
## Install
|
9
|
+
|
10
|
+
`gem install geppeto`
|
11
|
+
|
12
|
+
or add it to your bundler file
|
13
|
+
|
14
|
+
`gem "geppeto", `~>0.1.0`
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
The goal with Geppeto is to provide an API that feels like Ruby. Here's an
|
19
|
+
example:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'geppeto'
|
23
|
+
|
24
|
+
serial = Geppeto::Connection::Serial.new
|
25
|
+
lead = Geppeto::Scout.new serial
|
26
|
+
lead.report
|
27
|
+
=> {"type"=>"scout", "lead"=>true, "version"=>1, "hardware"=>1, "family"=>1000, "serial"=>3001731, "sketch"=>"Custom", "build"=>-1, "revision"=>"unknown", "at"=>12642}
|
28
|
+
```
|
29
|
+
|
30
|
+
Here's an even better example:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
lead.pin.digital
|
34
|
+
=> {"type"=>"digital", "mode"=>[1, -3, -3, -3, -3, -3, -3], "state"=>[1, -1, -1, -1, -1, -1, -1], "at"=>9303}
|
35
|
+
```
|
36
|
+
|
37
|
+
You can even issue commands to all or others
|
38
|
+
```ruby
|
39
|
+
lead.all.led.on
|
40
|
+
=> nil
|
41
|
+
lead.others.led.off
|
42
|
+
=> nil
|
43
|
+
lead.led.report
|
44
|
+
=> {"type"=>"led", "led"=>[0, 255, 0], "torch"=>[0, 255, 0], "at"=>106676}
|
45
|
+
```
|
46
|
+
|
47
|
+
If that wasn't exciting enough, you can also delay commands
|
48
|
+
```ruby
|
49
|
+
lead.delay(5000).led.on
|
50
|
+
=> nil
|
51
|
+
# 5 seconds later... the led turned on.
|
52
|
+
```
|
53
|
+
|
54
|
+
### Creating a Scout
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
id = 2
|
58
|
+
serial = Geppeto::Connection::Serial.new
|
59
|
+
scout = Geppeto::Scout.new serial, id
|
60
|
+
scout.report
|
61
|
+
=> nil
|
62
|
+
```
|
63
|
+
|
64
|
+
That's all well and good, but scout's that are accessible over a `Serial`
|
65
|
+
connection return `nil` all requests by result. One way to obtain data is to
|
66
|
+
use a socket server.
|
67
|
+
|
68
|
+
### Socket Server
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
socket = Geppeto::Connection::Server.new
|
72
|
+
=> #<Celluloid::CellProxy(Geppeto::Connection::Server:0x3fd00c8cb18c) @history=#<Revolver:0x007fa01918fe00 @array=[], @size=10, @unique=false> @logger=#<Logger:0x007fa01918fd88 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0x007fa01918fd60 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x007fa01918fd10 @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDOUT>>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0x007fa01918fce8 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x007fa01918fc98>>>> @server=#<Celluloid::IO::TCPServer:0x007fa01918f950 @server=#<TCPServer:fd 11>>>
|
73
|
+
```
|
74
|
+
|
75
|
+
Now we have a socket server that will capture and parse the JSON responses.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
lead.hq.setaddress("10.0.1.22")
|
79
|
+
=> nil
|
80
|
+
lead.wifi.reassociate
|
81
|
+
=> nil
|
82
|
+
socket.history
|
83
|
+
=> #<Revolver:0x007fee22ac9fa0
|
84
|
+
@array=[{"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>31, "f"=>88, "offset"=>0, "at"=>3540003}}],
|
85
|
+
@size=10,
|
86
|
+
@unique=false>
|
87
|
+
|
88
|
+
```
|
89
|
+
|
90
|
+
Note that the socket server has a `history` attribute. Because of the limitations
|
91
|
+
with socket communication with pinoccio, we're capturing all of the responses in
|
92
|
+
a fixed sized LIFO array.
|
93
|
+
|
94
|
+
Nowe we can have our scout send us a report
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
scout.hq.print("Hello, world!")
|
98
|
+
socket.history
|
99
|
+
=> #<Revolver:0x007fee22ac9fa0
|
100
|
+
@array=
|
101
|
+
[{"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>31, "f"=>88, "offset"=>0, "at"=>3540003}},
|
102
|
+
{"type"=>"report", "from"=>1, "report"=>{"type"=>"temp", "c"=>32, "f"=>90, "offset"=>0, "at"=>197667}},
|
103
|
+
{"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>30, "f"=>88, "offset"=>0, "at"=>3600003}},
|
104
|
+
{"type"=>"announce", "from"=>2, "announce"=>[0, "Hello, world!"]}],
|
105
|
+
@size=10,
|
106
|
+
@unique=false>
|
107
|
+
```
|
108
|
+
|
109
|
+
### Constants
|
110
|
+
|
111
|
+
Most of the time, arguments sent to the scouts are just strings and integers;
|
112
|
+
however occasionally we may need to send a constant. This can be achieved by
|
113
|
+
sending a ruby `Symbol` instead. Such is the case with pins, as seen here:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
scout.pin.save("d2", :OUTPUT, :HIGH)
|
117
|
+
socket.history.pop
|
118
|
+
=> {"type"=>"report",
|
119
|
+
"from"=>2,
|
120
|
+
"report"=>{"type"=>"digital", "mode"=>[1, -3, -3, -3, -3, -3, -3], "state"=>[1, -1, -1, -1, -1, -1, -1], "at"=>3938541}}
|
121
|
+
```
|
122
|
+
|
123
|
+
## What about...
|
124
|
+
|
125
|
+
### Tests
|
126
|
+
Well this is embarrassing... If I knew how to mock out a Pinoccio, Serial, or
|
127
|
+
Socket connection, I'd provide tests. If you're interested in contributing, and
|
128
|
+
you know how to test this library, by all means, please send a pull request.
|
129
|
+
|
130
|
+
### Where did the "t" go?
|
131
|
+
Yes, Geppeto is purposely misspelled. If you're familiar with Pinoccio, you might
|
132
|
+
recognize that the "h" is missing. This is explained on [Pinoccio's website](https://pinocc.io/faq#faq-wheres-the-h).
|
133
|
+
|
134
|
+
### What's missing?
|
135
|
+
Besides tests... a few things:
|
136
|
+
|
137
|
+
* [Events Callback Support](https://docs.pinocc.io/scoutcommands.html#event-callbacks)
|
138
|
+
* [Scout Acknowledgement Callback](https://docs.pinocc.io/scoutcommands.html#command-scout-ack)
|
139
|
+
* [Keys](https://docs.pinocc.io/scoutcommands.html#keys-deprecated)
|
140
|
+
|
141
|
+
### What's next?
|
142
|
+
Should there be a `Thor` backed CLI executable to manage a standalone socket server?
|
143
|
+
Or maybe I'll figure out how to write tests.
|
data/Rakefile
ADDED
data/geppeto.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'geppeto'
|
3
|
+
s.version = '0.1.1'
|
4
|
+
s.date = '2014-10-29'
|
5
|
+
s.summary = "Geppeto makes Pinoccio into Ruby."
|
6
|
+
s.description = "Geppeto provides a Ruby API to Pinoccio microcontrollers by means of a TCPsocket or a Serial connection, forgoing the need to use api.pinocc.io."
|
7
|
+
s.authors = ["Matt Heitzenroder"]
|
8
|
+
s.email = 'mheitzenroder@gmail.com'
|
9
|
+
s.homepage =
|
10
|
+
'http://github.com/roder/geppeto'
|
11
|
+
s.license = 'Apache-2.0'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($/)
|
14
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'serialport', '~> 1.2'
|
19
|
+
s.add_runtime_dependency 'celluloid-io', '~> 0.16'
|
20
|
+
s.add_runtime_dependency 'revolver', '~> 1.1'
|
21
|
+
s.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
s.add_development_dependency 'rake', '~> 10'
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'commands/events'
|
2
|
+
require 'commands/hq'
|
3
|
+
require 'commands/led'
|
4
|
+
require 'commands/memory'
|
5
|
+
require 'commands/mesh'
|
6
|
+
require 'commands/pin'
|
7
|
+
require 'commands/power'
|
8
|
+
require 'commands/uptime'
|
9
|
+
require 'commands/wifi'
|
10
|
+
|
11
|
+
module Geppeto
|
12
|
+
module Commands
|
13
|
+
class NotLeadScout < Exception
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Events
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
@scout.request("events.start")
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop
|
13
|
+
@scout.request("events.stop")
|
14
|
+
end
|
15
|
+
|
16
|
+
def setcycle(digitalMs, analogMs, peripheralMs)
|
17
|
+
@scout.request("events.setcycle", digitalMs, analogMs, peripheralMs )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/commands/hq.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Hq
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def setaddress(host, port = nil)
|
9
|
+
@scout.request("hq.setaddress", host, port)
|
10
|
+
end
|
11
|
+
|
12
|
+
def print(str)
|
13
|
+
@scout.request("hq.print", str)
|
14
|
+
end
|
15
|
+
|
16
|
+
def settoken(token)
|
17
|
+
@scout.request("hq.settoken", token)
|
18
|
+
end
|
19
|
+
|
20
|
+
def gettoken
|
21
|
+
@scout.request("hq.gettoken")
|
22
|
+
end
|
23
|
+
|
24
|
+
def report(report_name, value)
|
25
|
+
@scout.request("hq.report", report_name, value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/commands/led.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Led
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def on?
|
9
|
+
isoff().to_i != 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def off?
|
13
|
+
isoff().to_i == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def on
|
17
|
+
@scout.request("led.on")
|
18
|
+
end
|
19
|
+
|
20
|
+
def off
|
21
|
+
@scout.request("led.off")
|
22
|
+
end
|
23
|
+
|
24
|
+
def red(ms = nil, continuous = nil)
|
25
|
+
@scout.request("led.red", ms, continuous)
|
26
|
+
end
|
27
|
+
|
28
|
+
def green(ms = nil, continuous = nil)
|
29
|
+
@scout.request("led.green", ms, continuous)
|
30
|
+
end
|
31
|
+
|
32
|
+
def blue(ms = nil, continuous = nil)
|
33
|
+
@scout.request("led.blue", ms, continuous)
|
34
|
+
end
|
35
|
+
|
36
|
+
def cyan(ms = nil, continuous = nil)
|
37
|
+
@scout.request("led.cyan", ms, continuous)
|
38
|
+
end
|
39
|
+
|
40
|
+
def purple(ms = nil, continuous = nil)
|
41
|
+
@scout.request("led.purple", ms, continuous)
|
42
|
+
end
|
43
|
+
|
44
|
+
def magenta(ms = nil, continuous = nil)
|
45
|
+
@scout.request("led.magenta", ms, continuous)
|
46
|
+
end
|
47
|
+
|
48
|
+
def yellow(ms = nil, continuous = nil)
|
49
|
+
@scout.request("led.yellow", ms, continuous)
|
50
|
+
end
|
51
|
+
|
52
|
+
def orange(ms = nil, continuous = nil)
|
53
|
+
@scout.request("led.orange", ms, continuous)
|
54
|
+
end
|
55
|
+
|
56
|
+
def white(ms = nil, continuous = nil)
|
57
|
+
@scout.request("led.white", ms, continuous)
|
58
|
+
end
|
59
|
+
|
60
|
+
def torch(ms = nil, continuous = nil)
|
61
|
+
@scout.request("led.torch", ms, continuous )
|
62
|
+
end
|
63
|
+
|
64
|
+
def blink(red, green, blue, millis=500, continuous = nil)
|
65
|
+
@scout.request("led.blink", red, green, blue, millis, continuous)
|
66
|
+
end
|
67
|
+
|
68
|
+
def sethex(hex_value)
|
69
|
+
@scout.request("led.sethex", hex_value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def gethex
|
73
|
+
@scout.request("led.gethex")
|
74
|
+
end
|
75
|
+
|
76
|
+
def setrgb(red, green, blue)
|
77
|
+
@scout.request("led.setrgb", red, green, blue)
|
78
|
+
end
|
79
|
+
|
80
|
+
def isoff
|
81
|
+
@scout.request("led.isoff")
|
82
|
+
end
|
83
|
+
|
84
|
+
def report
|
85
|
+
@scout.request("led.report")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Mesh
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def config(scout_id, troop_id, channel=20)
|
9
|
+
@scout.request("mesh.config", scout_id, troop_id, channel)
|
10
|
+
end
|
11
|
+
|
12
|
+
def setpowerlevel(level)
|
13
|
+
@scout.request("mesh.setpowerlevel", level.to_i)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setdatarate(rate)
|
17
|
+
@scout.request("mesh.setdatarate", rate.to_i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def setkey(key)
|
21
|
+
@scout.request("mesh.setkey", key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def resetkey
|
25
|
+
@scout.request("mesh.resetkey")
|
26
|
+
end
|
27
|
+
|
28
|
+
def joingroup(group)
|
29
|
+
@scout.request("mesh.joingroup", group.to_i)
|
30
|
+
end
|
31
|
+
|
32
|
+
def leavegroup(group)
|
33
|
+
@scout.request("mesh.leavegroup", group.to_i)
|
34
|
+
end
|
35
|
+
|
36
|
+
def ingroup?(group)
|
37
|
+
@scout.request("mesh.ingroup").to_i == 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def report
|
41
|
+
@scout.request("mesh.report")
|
42
|
+
end
|
43
|
+
|
44
|
+
def routing
|
45
|
+
@scout.request("mesh.routing")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/commands/pin.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Pin
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def makeinput(pin_name, input_type=:INPUT_PULLUP)
|
9
|
+
@scout.request("pin.makeinput", pin_name, input_type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def makeoutput(pin_name)
|
13
|
+
@scout.request("pin.makeoutput", pin_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def makepwm(pin_name)
|
17
|
+
@scout.request("pin.makepwm", pin_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def disable(pin_name)
|
21
|
+
@scout.request("pin.disable", pin_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def setmode(pin_name, input_type=:INPUT_PULLUP)
|
25
|
+
@scout.request("pin.setmode", pin_name, input_type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def read(pin_name)
|
29
|
+
@scout.request("pin.read", pin_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write(pin_name, pin_value)
|
33
|
+
@scout.request("pin.write", pin_name, pin_value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def save(pin_name, pin_mode, pin_value = nil)
|
37
|
+
@scout.request("pin.save", pin_name, pin_mode, pin_value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def status
|
41
|
+
@scout.request("pin.status")
|
42
|
+
end
|
43
|
+
|
44
|
+
def report
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def analog
|
49
|
+
@scout.request("pin.report.analog")
|
50
|
+
end
|
51
|
+
|
52
|
+
def digital
|
53
|
+
@scout.request("pin.report.digital")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Power
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def charging?
|
9
|
+
@scout.request("power.ischarging").to_i == 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def battery?
|
13
|
+
@scout.request("power.hasbattery").to_i == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def percent
|
17
|
+
@scout.request("power.percent").to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def voltage
|
21
|
+
@scout.request("power.voltage").to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def enablevcc
|
25
|
+
@scout.request("power.enablevcc")
|
26
|
+
end
|
27
|
+
|
28
|
+
def disablevcc
|
29
|
+
@scout.request("power.disablevcc")
|
30
|
+
end
|
31
|
+
|
32
|
+
def vcc?
|
33
|
+
@scout.request("power.isvccenabled").to_i == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def sleep(ms, command = nil)
|
37
|
+
@scout.request("power.sleep", ms, command)
|
38
|
+
end
|
39
|
+
|
40
|
+
def report
|
41
|
+
@scout.request("power.report")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Temperature
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def c
|
9
|
+
@scout.request("temperature.c")
|
10
|
+
end
|
11
|
+
|
12
|
+
def f
|
13
|
+
@scout.request("temperature.f")
|
14
|
+
end
|
15
|
+
|
16
|
+
def report
|
17
|
+
@scout.request("temperature.report")
|
18
|
+
end
|
19
|
+
|
20
|
+
def setoffset(offset)
|
21
|
+
@scout.request("temperature.offset", offset)
|
22
|
+
end
|
23
|
+
|
24
|
+
def calibrate(current_temp_c)
|
25
|
+
@scout.request("temperature.calibrate", current_temp_c)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Uptime
|
4
|
+
|
5
|
+
def initialize(scout)
|
6
|
+
@context = nil
|
7
|
+
@scout = scout
|
8
|
+
end
|
9
|
+
|
10
|
+
def micros
|
11
|
+
context ||= ".#{@context}" unless @context.nil?
|
12
|
+
command = "uptime#{context}.micros"
|
13
|
+
@scout.request(command)
|
14
|
+
@context = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def seconds
|
18
|
+
context ||= ".#{@context}" unless @context.nil?
|
19
|
+
command = "uptime#{context}.seconds"
|
20
|
+
@scout.request(command)
|
21
|
+
@context = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def report
|
25
|
+
@scout.request("uptime.report")
|
26
|
+
end
|
27
|
+
|
28
|
+
def getlastreset
|
29
|
+
@scout.request("uptime.getlastreset")
|
30
|
+
end
|
31
|
+
|
32
|
+
def status
|
33
|
+
@scout.request("uptime.status")
|
34
|
+
end
|
35
|
+
|
36
|
+
def awake
|
37
|
+
@context = :awake
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def sleeping
|
42
|
+
@context = :sleeping
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Geppeto
|
2
|
+
module Commands
|
3
|
+
class Wifi
|
4
|
+
def initialize(scout)
|
5
|
+
@scout = scout
|
6
|
+
end
|
7
|
+
|
8
|
+
def report
|
9
|
+
@scout.request("wifi.report")
|
10
|
+
end
|
11
|
+
|
12
|
+
def status
|
13
|
+
@scout.request("wifi.status")
|
14
|
+
end
|
15
|
+
|
16
|
+
def list
|
17
|
+
@scout.request("wifi.list", ms: 5)
|
18
|
+
end
|
19
|
+
|
20
|
+
def config(ssid, psk = nil)
|
21
|
+
@scout.request("wifi.config", ssid, psk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def dhcp(hostname)
|
25
|
+
@scout.request("wifi.dhcp", hostname)
|
26
|
+
end
|
27
|
+
|
28
|
+
def static(ip, netmask, gateway, dns)
|
29
|
+
@scout.request("wifi.static", ip, netmask, gateway, dns)
|
30
|
+
end
|
31
|
+
|
32
|
+
def reassociate
|
33
|
+
@scout.request("wifi.reassociate")
|
34
|
+
end
|
35
|
+
|
36
|
+
def command(wifi_cmd)
|
37
|
+
@scout.request("wifi.command", wifi_cmd)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
|
3
|
+
module Geppeto
|
4
|
+
module Connection
|
5
|
+
class Serial
|
6
|
+
attr_accessor :logger
|
7
|
+
|
8
|
+
def initialize(port = false, baud = 115200, logger = nil)
|
9
|
+
@logger = logger.nil? ? Logger.new(STDOUT) : logger
|
10
|
+
port ||= `ls /dev`.split("\n").grep(/tty.usb/i).map{|d| "/dev/#{d}"}.first #TODO: Check that this works on RPi/Begalbone OSs
|
11
|
+
@serial = SerialPort.new(port, baud)
|
12
|
+
@serial.read_timeout = 100
|
13
|
+
sleep 1
|
14
|
+
begin
|
15
|
+
@logger.debug(@serial.read_nonblock(4096))
|
16
|
+
rescue EOFError
|
17
|
+
# no data
|
18
|
+
end
|
19
|
+
@logger.info("Pinoccio Serial Connection started: #{port}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def shutdown
|
23
|
+
@serial.close if @serial
|
24
|
+
rescue IOError
|
25
|
+
@logger.warn("Pinoccio Serial Connection closed.")
|
26
|
+
end
|
27
|
+
|
28
|
+
def write(command, ms = nil)
|
29
|
+
@serial.write command+"\n"
|
30
|
+
ms ||= 0.1
|
31
|
+
sleep ms
|
32
|
+
read command
|
33
|
+
end
|
34
|
+
|
35
|
+
def read(command = nil)
|
36
|
+
begin
|
37
|
+
raw_result = @serial.read_nonblock(4096)
|
38
|
+
if command.nil?
|
39
|
+
raw_result
|
40
|
+
else
|
41
|
+
cmd = Regexp.escape(command)
|
42
|
+
matches = /#{cmd}\r\n(?<response>.*)\r\n>\s.*/m.match(raw_result)
|
43
|
+
if matches[:response]
|
44
|
+
begin
|
45
|
+
JSON.parse(matches[:response])
|
46
|
+
rescue
|
47
|
+
matches[:response]
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raw_result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'celluloid/io'
|
2
|
+
require 'revolver'
|
3
|
+
|
4
|
+
module Geppeto
|
5
|
+
module Connection
|
6
|
+
class Server
|
7
|
+
include Celluloid::IO
|
8
|
+
finalizer :shutdown
|
9
|
+
|
10
|
+
attr_accessor :logger
|
11
|
+
attr_accessor :history
|
12
|
+
|
13
|
+
def initialize(host="0.0.0.0", port = 22756, logger = nil)
|
14
|
+
@history = Revolver.new(10)
|
15
|
+
@logger = logger.nil? ? ::Logger.new(STDOUT) : logger
|
16
|
+
@logger.info("Starting Pinoccio Server on #{host}:#{port}")
|
17
|
+
@server = TCPServer.new(host, port)
|
18
|
+
async.run
|
19
|
+
end
|
20
|
+
|
21
|
+
def shutdown
|
22
|
+
@server.close if @server
|
23
|
+
rescue IOError
|
24
|
+
@logger.warn("Pinoccio Server shutting down")
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_cmd(scout, command)
|
28
|
+
cmd = {
|
29
|
+
:to => scout.to_i,
|
30
|
+
:command => command.to_s,
|
31
|
+
:id => 1,
|
32
|
+
:timeout => 10000, # TODO: Make this configurable.
|
33
|
+
:type => "command",
|
34
|
+
}
|
35
|
+
cmd.to_json + "\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
def send(scout, command)
|
39
|
+
@logger.debug("Scout #{scout}: #{command}")
|
40
|
+
if @socket.nil?
|
41
|
+
@logger.warn("No socket connected")
|
42
|
+
else
|
43
|
+
@socket.write format_cmd(scout, command)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
loop { async.handle_connection @server.accept }
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_connection(socket)
|
52
|
+
_, port, host = socket.peeraddr
|
53
|
+
@logger.info("Connected: #{host}:#{port}")
|
54
|
+
chunk = socket.gets
|
55
|
+
data = JSON.parse(chunk)
|
56
|
+
token = data["token"]
|
57
|
+
@socket = socket
|
58
|
+
@logger.debug("Connected token: #{token}")
|
59
|
+
loop {
|
60
|
+
chunk = @socket.gets.chomp!
|
61
|
+
data = JSON.parse(chunk)
|
62
|
+
@history << data
|
63
|
+
@logger.debug("Received: #{data}")
|
64
|
+
}
|
65
|
+
rescue
|
66
|
+
@logger.info("Disconnected: #{host}:#{port}")
|
67
|
+
@socket.close
|
68
|
+
@logger.debug("Removed token: #{token}")
|
69
|
+
@socket = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/geppeto.rb
ADDED
data/lib/scout.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
module Geppeto
|
2
|
+
class Scout
|
3
|
+
attr_reader :id
|
4
|
+
attr_accessor :last_report
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
attr_reader :events
|
8
|
+
attr_reader :hq
|
9
|
+
attr_reader :led
|
10
|
+
attr_reader :memory
|
11
|
+
attr_reader :mesh
|
12
|
+
attr_reader :pin
|
13
|
+
attr_reader :power
|
14
|
+
attr_reader :temperature
|
15
|
+
attr_reader :uptime
|
16
|
+
attr_reader :wifi
|
17
|
+
|
18
|
+
def initialize(connection, id = nil)
|
19
|
+
@id = id
|
20
|
+
@conn = connection
|
21
|
+
@options = Array.new
|
22
|
+
@logger = @conn.logger ? @conn.logger : Logger.new(STDOUT)
|
23
|
+
|
24
|
+
@events = Geppeto::Commands::Events.new(self)
|
25
|
+
@hq = Geppeto::Commands::Hq.new(self)
|
26
|
+
@led = Geppeto::Commands::Led.new(self)
|
27
|
+
@memory = Geppeto::Commands::Memory.new(self)
|
28
|
+
@mesh = Geppeto::Commands::Mesh.new(self)
|
29
|
+
@pin = Geppeto::Commands::Pin.new(self)
|
30
|
+
@power = Geppeto::Commands::Power.new(self)
|
31
|
+
@uptime = Geppeto::Commands::Uptime.new(self)
|
32
|
+
@wifi = Geppeto::Commands::Wifi.new(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def all
|
36
|
+
@options << :all
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def others
|
41
|
+
@options << :others
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def report
|
46
|
+
request("scout.report")
|
47
|
+
end
|
48
|
+
|
49
|
+
def lead?
|
50
|
+
request("print scout.isleadscout").to_i == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
def randomnumber
|
54
|
+
request("randomnumber")
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
def delay(ms = nil)
|
60
|
+
@options << :delay
|
61
|
+
@_ms = ms
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def boot
|
66
|
+
request("scout.boot")
|
67
|
+
end
|
68
|
+
|
69
|
+
def request(command, *args, ms: nil)
|
70
|
+
args_string = join_args(args)
|
71
|
+
command, args_string = process_options(command, args_string)
|
72
|
+
|
73
|
+
if args_string.empty?
|
74
|
+
unless @id.nil?
|
75
|
+
command = "command.scout(#{@id}, \"#{command}\")"
|
76
|
+
else
|
77
|
+
command = "#{command}()"
|
78
|
+
end
|
79
|
+
else
|
80
|
+
unless @id.nil?
|
81
|
+
command = "command.scout(#{@id}, \"#{command}\", #{args_string})"
|
82
|
+
else
|
83
|
+
command = "#{command}(#{args_string})"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
@logger.debug("Issuing: #{command}")
|
88
|
+
@conn.write(command, ms)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def process_options(command, arg_string)
|
93
|
+
arg_string = "\"#{command}(#{arg_string})\"" unless @options.empty?
|
94
|
+
case @options.shift
|
95
|
+
when :all
|
96
|
+
command = "command.all"
|
97
|
+
process_options(command, arg_string)
|
98
|
+
when :others
|
99
|
+
command = "command.others"
|
100
|
+
process_options(command, arg_string)
|
101
|
+
when :delay
|
102
|
+
command = "scout.delay"
|
103
|
+
arg_string = "#{@_ms}, #{arg_string}"
|
104
|
+
@_ms = false
|
105
|
+
process_options(command, arg_string)
|
106
|
+
else
|
107
|
+
arg_string.gsub(/"/, "\\\"") unless arg_string.empty?
|
108
|
+
return command, arg_string
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def join_args(args)
|
113
|
+
arg_str = String.new
|
114
|
+
n = 0
|
115
|
+
args.each { |arg|
|
116
|
+
if arg.class == String
|
117
|
+
arg_str << "\"#{arg}\""
|
118
|
+
else
|
119
|
+
arg_str << arg.to_s
|
120
|
+
end
|
121
|
+
n += 1
|
122
|
+
arg_str << ", " if n < args.length && args[n].class != NilClass
|
123
|
+
}
|
124
|
+
arg_str
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require './spec/spec_helper.rb'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
RSpec.describe Geppeto do
|
5
|
+
# TODO:
|
6
|
+
# Listen, I know, it's bad. I just don't know how to test this. Maybe since
|
7
|
+
# you're looking at it, you could help - let me know. I just didn't know where
|
8
|
+
# to start.
|
9
|
+
#
|
10
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geppeto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Heitzenroder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: serialport
|
@@ -86,7 +86,32 @@ email: mheitzenroder@gmail.com
|
|
86
86
|
executables: []
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
|
-
files:
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- Gemfile.lock
|
93
|
+
- LICENSE
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- geppeto.gemspec
|
97
|
+
- lib/commands/commands.rb
|
98
|
+
- lib/commands/events.rb
|
99
|
+
- lib/commands/hq.rb
|
100
|
+
- lib/commands/led.rb
|
101
|
+
- lib/commands/memory.rb
|
102
|
+
- lib/commands/mesh.rb
|
103
|
+
- lib/commands/pin.rb
|
104
|
+
- lib/commands/power.rb
|
105
|
+
- lib/commands/temperature.rb
|
106
|
+
- lib/commands/uptime.rb
|
107
|
+
- lib/commands/wifi.rb
|
108
|
+
- lib/connections/connections.rb
|
109
|
+
- lib/connections/serial.rb
|
110
|
+
- lib/connections/server.rb
|
111
|
+
- lib/geppeto.rb
|
112
|
+
- lib/scout.rb
|
113
|
+
- spec/geppeto_spec.rb
|
114
|
+
- spec/spec_helper.rb
|
90
115
|
homepage: http://github.com/roder/geppeto
|
91
116
|
licenses:
|
92
117
|
- Apache-2.0
|
@@ -107,8 +132,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
132
|
version: '0'
|
108
133
|
requirements: []
|
109
134
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.
|
135
|
+
rubygems_version: 2.2.2
|
111
136
|
signing_key:
|
112
137
|
specification_version: 4
|
113
138
|
summary: Geppeto makes Pinoccio into Ruby.
|
114
|
-
test_files:
|
139
|
+
test_files:
|
140
|
+
- spec/geppeto_spec.rb
|
141
|
+
- spec/spec_helper.rb
|