├── .github
├── FUNDING.yml
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_ru.md
├── SECURITY.md
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── firmata4j
│ │ ├── Consumer.java
│ │ ├── Example.java
│ │ ├── I2CDevice.java
│ │ ├── I2CEvent.java
│ │ ├── I2CExample.java
│ │ ├── I2CListener.java
│ │ ├── IODevice.java
│ │ ├── IODeviceEventListener.java
│ │ ├── IOEvent.java
│ │ ├── OnMessageReceiveListener.java
│ │ ├── OnPinChangeListener.java
│ │ ├── OnStartListener.java
│ │ ├── OnStopListener.java
│ │ ├── Parser.java
│ │ ├── Pin.java
│ │ ├── PinEventListener.java
│ │ ├── firmata
│ │ ├── DaemonThreadFactory.java
│ │ ├── FirmataDevice.java
│ │ ├── FirmataI2CDevice.java
│ │ ├── FirmataMessageFactory.java
│ │ ├── FirmataPin.java
│ │ ├── FirmataWatchdog.java
│ │ └── parser
│ │ │ ├── FirmataEventType.java
│ │ │ ├── FirmataParser.java
│ │ │ ├── FirmataToken.java
│ │ │ ├── ParsingAnalogMappingState.java
│ │ │ ├── ParsingAnalogMessageState.java
│ │ │ ├── ParsingCapabilityResponseState.java
│ │ │ ├── ParsingCustomSysexMessageState.java
│ │ │ ├── ParsingDigitalMessageState.java
│ │ │ ├── ParsingExtendedAnalogMessageState.java
│ │ │ ├── ParsingFirmwareMessageState.java
│ │ │ ├── ParsingI2CMessageState.java
│ │ │ ├── ParsingStringMessageState.java
│ │ │ ├── ParsingSysexMessageState.java
│ │ │ ├── ParsingVersionMessageState.java
│ │ │ ├── PinStateParsingState.java
│ │ │ └── WaitingForMessageState.java
│ │ ├── fsm
│ │ ├── AbstractState.java
│ │ ├── DirectExecutor.java
│ │ ├── Event.java
│ │ ├── FiniteStateMachine.java
│ │ └── State.java
│ │ ├── ssd1306
│ │ ├── MonochromeCanvas.java
│ │ ├── SSD1306.java
│ │ ├── SSD1306MessageFactory.java
│ │ └── SSD1306Token.java
│ │ ├── transport
│ │ ├── JSSCTransport.java
│ │ ├── JSerialCommTransport.java
│ │ ├── NetworkTransport.java
│ │ ├── SerialTransport.java
│ │ └── TransportInterface.java
│ │ └── ui
│ │ ├── JPin.java
│ │ └── JPinboard.java
└── resources
│ ├── img
│ ├── blue-off.png
│ ├── blue-on.png
│ ├── firmata4j.png
│ ├── gray-off.png
│ ├── gray-on.png
│ ├── green-off.png
│ └── green-on.png
│ └── nondistributable
│ └── indicator.svg
└── test
├── java
└── org
│ └── firmata4j
│ └── firmata
│ ├── FirmataDeviceTest.java
│ ├── fsm
│ └── FiniteStateMachineTest.java
│ └── parser
│ └── FirmataParserTest.java
└── resources
└── simplelogger.properties
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: maven/firmata4j
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://www.paypal.me/OlegKurbatov']
13 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | name: "CodeQL"
7 |
8 | on:
9 | push:
10 | branches: [master]
11 | pull_request:
12 | # The branches below must be a subset of the branches above
13 | branches: [master]
14 | schedule:
15 | - cron: '0 9 * * 6'
16 |
17 | jobs:
18 | analyze:
19 | name: Analyze
20 | runs-on: ubuntu-latest
21 |
22 | strategy:
23 | fail-fast: false
24 | matrix:
25 | # Override automatic language detection by changing the below list
26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
27 | language: ['java']
28 | # Learn more...
29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
30 |
31 | steps:
32 | - name: Checkout repository
33 | uses: actions/checkout@v2
34 | with:
35 | # We must fetch at least the immediate parents so that if this is
36 | # a pull request then we can checkout the head.
37 | fetch-depth: 2
38 |
39 | # If this run was triggered by a pull request event, then checkout
40 | # the head of the pull request instead of the merge commit.
41 | - run: git checkout HEAD^2
42 | if: ${{ github.event_name == 'pull_request' }}
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v1
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v1
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v1
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | .idea/
3 | *.iml
4 | .classpath
5 | .project
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.codementor.io/@olegkurbatov?utm_source=github&utm_medium=button&utm_term=olegkurbatov&utm_campaign=github)
2 |
3 | ## firmata4j
4 | **firmata4j** is a client library of [Firmata](https://github.com/firmata/protocol)
5 | written in Java. The library allows controlling Arduino (or another board) which
6 | runs Firmata protocol from your java program.
7 |
8 | ## Capabilities
9 | - Interaction with a board and its pins in object-oriented style
10 | - Communication over serial port, network or custom transport layer
11 | - Abstraction over details of the protocol
12 | - Provides a UI component that visualize the current state of pins and allows
13 | changing their mode and state
14 | - Allows communicating with I2C devices connected to the board
15 |
16 | ## Installation
17 |
18 | ### Maven
19 | Add the following dependency to `pom.xml` of your project:
20 |
21 | ```xml
22 |
23 | com.github.kurbatov
24 | firmata4j
25 | 2.3.9
26 |
27 | ```
28 |
29 | If you want to connect to the device via serial port (which is the most probable
30 | case), please include one of the following libraries as a dependency into
31 | the `pom.xml`:
32 |
33 | ```xml
34 |
35 |
36 | com.fazecast
37 | jSerialComm
38 | 2.6.2
39 |
40 |
41 |
42 | io.github.java-native
43 | jssc
44 | 2.9.4
45 |
46 | ```
47 |
48 | **jssc** is an older library that worked just fine until recently. Now it reveals
49 | [issues on **GraalVM** and latest updates of **Windows 10**](https://github.com/kurbatov/firmata4j/issues/42).
50 | **firmata4j** was using **jssc** by default in versions prior to **2.3.9**.
51 |
52 | **jSerialComm** has proven itself to be working on **GraalVM** and latest
53 | updates of **Windows 10**.
54 |
55 | ## Usage
56 | General scenario of usage is following:
57 | ```java
58 | // construct a Firmata device instance
59 | IODevice device = new FirmataDevice("/dev/ttyUSB0"); // using the name of a port
60 | // IODevice device = new FirmataDevice(new NetworkTransport("192.168.1.18:4334")); // using a network address
61 | // subscribe to events using device.addEventListener(...);
62 | // and/or device.getPin(n).addEventListener(...);
63 | device.start(); // initiate communication to the device
64 | device.ensureInitializationIsDone(); // wait for initialization is done
65 | // sending commands to the board
66 | device.stop(); // stop communication to the device
67 | ```
68 |
69 | Sending commands to the board may cause the device to emit events.
70 | Registered listeners process the events asynchronously. You can add and remove
71 | listeners along the way.
72 |
73 | You can subscribe to events of the device or its pin.
74 |
75 | ```java
76 | device.addEventListener(new IODeviceEventListener() {
77 | @Override
78 | public void onStart(IOEvent event) {
79 | // since this moment we are sure that the device is initialized
80 | // so we can hide initialization spinners and begin doing cool stuff
81 | System.out.println("Device is ready");
82 | }
83 |
84 | @Override
85 | public void onStop(IOEvent event) {
86 | // since this moment we are sure that the device is properly shut down
87 | System.out.println("Device has been stopped");
88 | }
89 |
90 | @Override
91 | public void onPinChange(IOEvent event) {
92 | // here we react to changes of pins' state
93 | Pin pin = event.getPin();
94 | System.out.println(
95 | String.format(
96 | "Pin %d got a value of %d",
97 | pin.getIndex(),
98 | pin.getValue())
99 | );
100 | }
101 |
102 | @Override
103 | public void onMessageReceive(IOEvent event, String message) {
104 | // here we react to receiving a text message from the device
105 | System.out.println(message);
106 | }
107 | });
108 | ```
109 |
110 | To obtain more fine grained control you can subscribe to events of a particular
111 | pin.
112 |
113 | ```java
114 | Pin pin = device.getPin(2);
115 | pin.addEventListener(new PinEventListener() {
116 | @Override
117 | public void onModeChange(IOEvent event) {
118 | System.out.println("Mode of the pin has been changed");
119 | }
120 |
121 | @Override
122 | public void onValueChange(IOEvent event) {
123 | System.out.println("Value of the pin has been changed");
124 | }
125 | });
126 | ```
127 |
128 | You can change the mode and value of a pin:
129 |
130 | ```java
131 | pin.setMode(Pin.Mode.OUTPUT); // our listeners will get event about this change
132 | pin.setValue(1); // and then about this change
133 | ```
134 |
135 | ## I2C
136 | **firmata4j** supports working with I2C devices. You can obtain a reference to
137 | an I2C device in this way:
138 |
139 | ```java
140 | IODevice device = new FirmataDevice(port);
141 | ...
142 | byte i2cAddress = 0x3C;
143 | I2CDevice i2cDevice = device.getI2CDevice(i2cAddress);
144 | ```
145 |
146 | You may find convenient writing a wrapper for [`I2CDevice` class](https://github.com/kurbatov/firmata4j/blob/master/src/main/java/org/firmata4j/I2CDevice.java)
147 | to facilitate communication with I2C device. Consider [`SSD1306`](https://github.com/kurbatov/firmata4j/blob/master/src/main/java/org/firmata4j/ssd1306/SSD1306.java)
148 | and [`I2CExample`](https://github.com/kurbatov/firmata4j/blob/master/src/main/java/org/firmata4j/I2CExample.java)
149 | classes as an example of that approach.
150 |
151 | ## Low-Level Messages and Events
152 |
153 | **firmata4j** allows sending an arbitrary binary message to the device. For
154 | example, setting sampling intervall using a low-level message:
155 |
156 | ```java
157 | device.sendMessage(FirmataMessageFactory.setSamplingInterval(12));
158 | ```
159 |
160 | Low-level event handlers are supported as well. Those may be useful for
161 | debugging or processing custom messages from a device with modified protocol
162 | implementation.
163 |
164 | ```java
165 | device.addProtocolMessageHandler(FirmataEventType.SYSEX_CUSTOM_MESSAGE, new Consumer() {
166 | @Override
167 | public void accept(Event evt) {
168 | byte[] message = (byte[]) evt.getBodyItem(FirmataEventType.SYSEX_CUSTOM_MESSAGE);
169 | byte messageType = message[0];
170 | // and so on
171 | }
172 | });
173 | ```
174 |
175 | ## Watchdog
176 |
177 | Low-level event handlers allow regestering a watchdog:
178 |
179 | ```java
180 | IODevice device = new FirmataDevice(port);
181 | //...
182 | FirmataWatchdog watchdog = new FirmataWatchdog(3000, new Runnable() {
183 | @Override
184 | public void run() {
185 | // do something when there were no low-level events during 3000 milliseconds
186 | }
187 | });
188 | device.addProtocolMessageHandler(FirmataEventType.ANY, watchdog);
189 | //...
190 | device.start();
191 | ```
192 |
193 | This watchdog implementation gets activated by the first received message since
194 | it subscribed. That's why it should be registered before communication starts.
195 |
196 | ## Visualization
197 |
198 | You can get visual representation of device's pins using `JPinboard` Swing component.
199 |
200 | ```java
201 | JPinboard pinboard = new JPinboard(device);
202 | JFrame frame = new JFrame("Pinboard Example");
203 | frame.add(pinboard);
204 | frame.pack();
205 | frame.setVisible(true);
206 | ```
207 |
208 | `JPinboard` allows setting the pin's mode by choosing one from a context menu of
209 | the pin. State of the output pin can be changed by double clicking on it.
210 |
211 | An example of `JPinboard` usage can be found in
212 | [`org.firmata4j.Example` class](https://github.com/kurbatov/firmata4j/blob/master/src/main/java/org/firmata4j/Example.java).
213 |
214 | ## Versions
215 | **firmata4j** sticks to Firmata protocol versions. The first available version
216 | of **firmata4j** is 2.3.1.
217 |
218 | **firmata4j**-2.3.x will work well with Fimata v. 2.3.x. Actually it should work
219 | with Firmata v. 2.x.x but not necessarily support all of the protocol features.
220 | The first digits of versions must be equal because those stand for incompatible
221 | changes of the protocol.
222 |
223 | ## Uploading Firmata To Arduino
224 | Arduino IDE is shipped with an implementation of Firmata protocol. You can
225 | upload it as follows:
226 |
227 | - Plug your Arduino to the computer
228 | - Launch Arduino IDE
229 | - Select `File -> Examples -> Firmata -> StandardFirmata` in IDE's menu
230 | - Select your board in `Tools -> Board`
231 | - Select the port in `Tools -> Port` (it is already selected if you have uploaded something to your Arduino)
232 | - Click on `Upload` button
233 |
234 | Note that **firmata4j** is focused to be client for the `StandardFirmata` firmware.
235 | Although there are several other firmwares that support Firmata protocol, those
236 | may implement only a featured subset of the protocol. A firmware has to respond
237 | to the following requests in order for **firmata4j** to initialize properly:
238 |
239 | - `REPORT_FIRMWARE`
240 | - `CAPABILITY_QUERY`
241 | - `PIN_STATE_QUERY`
242 | - `ANALOG_MAPPING_QUERY`
243 |
244 | ## Cases
245 |
246 | - [Easy Peripherals for the Internet of Things](https://repositorio-aberto.up.pt/bitstream/10216/84433/2/138208.pdf)
247 | - [Modelovanie a Riadenie Hybridných Systémov s Využitím Petriho Sietí Vyšších Úrovní](http://www.fei.stuba.sk/docs/2016/autoreferaty/autoref_Kucera.pdf)
248 | - [Programmazione di Sistemi Embedded con Linguaggi ad Agenti: un Caso di Studio basato su Jason e Arduino](https://amslaurea.unibo.it/9188/1/cozzolino_francesco_tesi.pdf)
249 | - [Using **firmata4j** in Clojure](https://github.com/cowlike/firmata4j-samples-clojure)
250 |
251 | ## Contributing
252 | Contributions are welcome. If you discover a bug or would like to propose a new
253 | feature, please, [open a new issue](https://github.com/kurbatov/firmata4j/issues/new).
254 |
255 | If you have an improvement to share, please, do the following:
256 |
257 | 1. Fork this repository
258 | 2. Clone your own fork to your machine (`git clone https://github.com//firmata4j.git`)
259 | 3. Create a feature branch (`git checkout -b my-new-feature`)
260 | 4. Change the code
261 | 5. Commit the changes (`git commit -am 'Adds some feature'`)
262 | 6. Push to the branch (`git push origin my-new-feature`)
263 | 7. Create new Pull Request
264 |
265 | ## License
266 | **firmata4j** is distributed under the terms of the MIT License. See the
267 | [LICENSE](https://github.com/kurbatov/firmata4j/blob/master/LICENSE) file.
268 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Security updates are provided for the latest version only.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | Please, create an [issue](https://github.com/kurbatov/firmata4j/issues) to report a vulnerability.
10 |
11 | Follow the status of the issue to track the progress.
12 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.github.kurbatov
5 | firmata4j
6 | 2.3.9-SNAPSHOT
7 | jar
8 | firmata4j
9 | firmata4j is a client library of Firmata.
10 | https://github.com/kurbatov/firmata4j
11 |
12 |
13 | MIT License
14 | https://raw.github.com/kurbatov/firmata4j/master/LICENSE
15 | repo
16 |
17 |
18 |
19 |
20 | Oleg Kurbatov
21 | o.v.kurbatov@gmail.com
22 | https://github.com/kurbatov
23 |
24 |
25 |
26 | https://github.com/kurbatov/firmata4j
27 | scm:git:git@github.com:kurbatov/firmata4j.git
28 | scm:git:git@github.com:kurbatov/firmata4j.git
29 | HEAD
30 |
31 |
32 |
33 | UTF-8
34 | src/main/resources
35 | src/test/resources
36 | 2.9.4
37 |
38 |
39 |
40 |
41 | io.github.java-native
42 | jssc
43 | ${jssc.version}
44 | true
45 |
46 |
47 | com.fazecast
48 | jSerialComm
49 | 2.6.2
50 | true
51 |
52 |
53 |
54 | org.slf4j
55 | slf4j-api
56 | 1.7.25
57 |
58 |
59 |
60 | junit
61 | junit
62 | 4.13.1
63 | test
64 |
65 |
66 | org.slf4j
67 | slf4j-simple
68 | test
69 | 1.7.25
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | ${project.resource.directory}
78 |
79 | nondistributable/**
80 |
81 |
82 |
83 |
84 |
85 | ${test.resource.directory}
86 |
87 |
88 |
89 |
90 | org.apache.maven.plugins
91 | maven-compiler-plugin
92 | 3.6.1
93 |
94 | 1.7
95 | 1.7
96 | -Xlint:unchecked
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-surefire-plugin
102 | 2.21.0
103 |
104 |
105 | org.apache.maven.plugins
106 | maven-release-plugin
107 | 2.5.3
108 |
109 | true
110 | false
111 | release
112 | deploy
113 |
114 |
115 |
116 | org.codehaus.mojo
117 | exec-maven-plugin
118 | 3.0.0
119 |
120 | org.firmata4j.Example
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | release
129 |
130 |
131 |
132 | org.apache.maven.plugins
133 | maven-source-plugin
134 | 3.0.1
135 |
136 |
137 | attach-sources
138 |
139 | jar-no-fork
140 |
141 |
142 |
143 |
144 |
145 | org.apache.maven.plugins
146 | maven-javadoc-plugin
147 | 2.10.4
148 |
149 |
150 | attach-javadocs
151 |
152 | jar
153 |
154 |
155 | -Xdoclint:none
156 |
157 |
158 |
159 |
160 |
161 | org.apache.maven.plugins
162 | maven-gpg-plugin
163 | 1.6
164 |
165 |
166 | sign-artifacts
167 | verify
168 |
169 | sign
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | ossrh
182 | https://oss.sonatype.org/content/repositories/snapshots
183 |
184 |
185 | ossrh
186 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
187 |
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/Consumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j;
25 |
26 | /**
27 | * Functional interface that represents an operation that accepts a single input
28 | * argument and returns no result.
29 | * It is replacement for Java 8's {@link java.util.function.Consumer} in an
30 | * attempt to stay pure Java 7 implementation.
31 | *
32 | * @param the type of the input to the operation
33 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
34 | */
35 | public abstract class Consumer {
36 |
37 | /**
38 | * Performs this operation on the given argument.
39 | *
40 | * @param t the input argument
41 | */
42 | public abstract void accept(T t);
43 |
44 | /**
45 | * Returns a composed {@code Consumer} that performs, in sequence, this
46 | * operation followed by the {@code after} operation. If performing either
47 | * operation throws an exception, it is relayed to the caller of the
48 | * composed operation. If performing this operation throws an exception,
49 | * the {@code after} operation will not be performed.
50 | *
51 | * @param after the operation to perform after this operation
52 | * @return a composed {@code Consumer} that performs in sequence this
53 | * operation followed by the {@code after} operation
54 | * @throws NullPointerException if {@code after} is null
55 | */
56 | public Consumer andThen(final Consumer super T> after) {
57 | if (after == null) {
58 | throw new NullPointerException();
59 | }
60 | final Consumer firstly = this;
61 | return new Consumer() {
62 | @Override
63 | public void accept(T t) {
64 | firstly.accept(t);
65 | after.accept(t);
66 | }
67 | };
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/Example.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | import java.awt.Dimension;
28 | import java.awt.GridBagConstraints;
29 | import java.awt.GridBagLayout;
30 | import java.awt.Toolkit;
31 | import java.awt.event.WindowAdapter;
32 | import java.awt.event.WindowEvent;
33 | import org.firmata4j.firmata.FirmataDevice;
34 | import java.io.IOException;
35 | import java.lang.reflect.InvocationTargetException;
36 | import java.util.logging.Level;
37 | import java.util.logging.Logger;
38 | import java.util.regex.Pattern;
39 | import javax.swing.DefaultComboBoxModel;
40 | import javax.swing.JComboBox;
41 | import javax.swing.JFrame;
42 | import javax.swing.JLabel;
43 | import javax.swing.JOptionPane;
44 | import javax.swing.JPanel;
45 | import javax.swing.SwingUtilities;
46 | import javax.swing.UIManager;
47 | import javax.swing.UnsupportedLookAndFeelException;
48 | import jssc.SerialNativeInterface;
49 | import jssc.SerialPortList;
50 | import org.firmata4j.ui.JPinboard;
51 |
52 | /**
53 | * Example of usage {@link JPinboard}.
54 | *
55 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
56 | */
57 | public class Example {
58 |
59 | private static final JFrame INITIALIZATION_FRAME = new JFrame();
60 |
61 | public static void main(String[] args) throws IOException {
62 | try { // set look and feel
63 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
64 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
65 | Logger.getLogger(Example.class.getName()).log(Level.SEVERE, "Cannot load system look and feel.", ex);
66 | }
67 | // requesting a user to define the port name
68 | String port = requestPort();
69 | final IODevice device = new FirmataDevice(port);
70 | showInitializationMessage();
71 | device.start();
72 | try {
73 | device.ensureInitializationIsDone();
74 | } catch (InterruptedException e) {
75 | JOptionPane.showMessageDialog(INITIALIZATION_FRAME, e.getMessage(), "Connection error", JOptionPane.ERROR_MESSAGE);
76 | System.exit(1);
77 | }
78 | hideInitializationWindow();
79 | SwingUtilities.invokeLater(new Runnable() {
80 | @Override
81 | public void run() {
82 | JFrame mainFrame = new JFrame("Pinboard Example");
83 | GridBagLayout layout = new GridBagLayout();
84 | GridBagConstraints constraints = new GridBagConstraints();
85 | mainFrame.setLayout(layout);
86 | constraints.gridy = 0;
87 | constraints.fill = GridBagConstraints.BOTH;
88 | constraints.weightx = 1;
89 | constraints.weighty = 1;
90 | JPinboard pinboard = new JPinboard(device);
91 | layout.setConstraints(pinboard, constraints);
92 | mainFrame.add(pinboard);
93 | mainFrame.pack();
94 | mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
95 | mainFrame.addWindowListener(new WindowAdapter() {
96 | @Override
97 | public void windowClosing(WindowEvent e) {
98 | try {
99 | device.stop();
100 | } catch (IOException ex) {
101 | throw new RuntimeException(ex);
102 | }
103 | super.windowClosing(e);
104 | }
105 | });
106 | mainFrame.setVisible(true);
107 | }
108 | });
109 | }
110 |
111 | @SuppressWarnings("unchecked")
112 | private static String requestPort() {
113 | JComboBox portNameSelector = new JComboBox<>();
114 | portNameSelector.setModel(new DefaultComboBoxModel());
115 | String[] portNames;
116 | if (SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X) {
117 | // for MAC OS default pattern of jssc library is too restrictive
118 | portNames = SerialPortList.getPortNames("/dev/", Pattern.compile("tty\\..*"));
119 | } else {
120 | portNames = SerialPortList.getPortNames();
121 | }
122 | for (String portName : portNames) {
123 | portNameSelector.addItem(portName);
124 | }
125 | if (portNameSelector.getItemCount() == 0) {
126 | JOptionPane.showMessageDialog(null, "Cannot find any serial port", "Error", JOptionPane.ERROR_MESSAGE);
127 | System.exit(1);
128 | }
129 | JPanel panel = new JPanel();
130 | panel.setLayout(new GridBagLayout());
131 | panel.add(new JLabel("Port "));
132 | panel.add(portNameSelector);
133 | if (JOptionPane.showConfirmDialog(null, panel, "Select the port", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
134 | return portNameSelector.getSelectedItem().toString();
135 | } else {
136 | System.exit(0);
137 | }
138 | return "";
139 | }
140 |
141 | private static void showInitializationMessage() {
142 | try {
143 | SwingUtilities.invokeAndWait(new Runnable() {
144 | @Override
145 | public void run() {
146 | JFrame frame = INITIALIZATION_FRAME;
147 | frame.setUndecorated(true);
148 | JLabel label = new JLabel("Connecting to device");
149 | label.setHorizontalAlignment(JLabel.CENTER);
150 | frame.add(label);
151 | frame.pack();
152 | frame.setSize(frame.getWidth() + 40, frame.getHeight() + 40);
153 | Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
154 | int x = (int) ((dimension.getWidth() - frame.getWidth()) / 2);
155 | int y = (int) ((dimension.getHeight() - frame.getHeight()) / 2);
156 | frame.setLocation(x, y);
157 | frame.setVisible(true);
158 | }
159 | });
160 | } catch (InterruptedException | InvocationTargetException ex) {
161 | throw new RuntimeException(ex);
162 | }
163 | }
164 |
165 | private static void hideInitializationWindow() {
166 | try {
167 | SwingUtilities.invokeAndWait(new Runnable() {
168 | @Override
169 | public void run() {
170 | INITIALIZATION_FRAME.setVisible(false);
171 | INITIALIZATION_FRAME.dispose();
172 | }
173 | });
174 | } catch (InterruptedException | InvocationTargetException ex) {
175 | throw new RuntimeException(ex);
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/I2CDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | import java.io.IOException;
28 |
29 | /**
30 | * Represents an I2C device and encapsulates logic to communicate to it.
31 | *
32 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
33 | */
34 | public interface I2CDevice {
35 |
36 | /**
37 | * Returns address of I2C device.
38 | */
39 | byte getAddress();
40 |
41 | /**
42 | * Sets delay between writing and reading data to/from I2C device.
43 | *
44 | * @param delay delay between the moment the device is written to and the
45 | * moment when the data can be read from it
46 | * @throws IOException when delay cannot be set due to communication fail
47 | */
48 | void setDelay(int delay) throws IOException;
49 |
50 | /**
51 | * Sends data to the I2C device.
52 | *
53 | * @param data data to send
54 | * @throws IOException when the data cannot be sent due to communication
55 | * fail
56 | */
57 | void tell(byte... data) throws IOException;
58 |
59 | /**
60 | * Requests data from I2C device and receives it only once. Specified
61 | * listener is exclusive processor of the received data.
62 | *
63 | * @param responseLength length of expected response
64 | * @param listener processor of the response
65 | * @throws IOException when I2C device cannot be asked for data due to
66 | * communication fail
67 | */
68 | void ask(byte responseLength, I2CListener listener) throws IOException;
69 |
70 | /**
71 | * Requests data from I2C device and receives it only once. Specified
72 | * listener is exclusive processor of the received data.
73 | *
74 | * @param register Device register to read from.
75 | * @param responseLength length of expected response
76 | * @param listener processor of the response
77 | * @throws IOException when I2C device cannot be asked for data due to
78 | * communication fail
79 | */
80 | void ask(int register, byte responseLength, I2CListener listener) throws IOException;
81 |
82 | /**
83 | * Registers a listener as a receiver of regular updates from I2C device.
84 | *
85 | * The listener starts receiving updates after
86 | * {@link #startReceivingUpdates} invocation.
87 | *
88 | * @param listener the object that receives updates
89 | */
90 | void subscribe(I2CListener listener);
91 |
92 | /**
93 | * Unregisters a listener from receiving of regular updates from I2C device.
94 | *
95 | * @param listener the object that used to receive updates
96 | */
97 | void unsubscribe(I2CListener listener);
98 |
99 | /**
100 | * Tells I2C device to send data continuously from specified register as it
101 | * available.
102 | *
103 | * @param register the device register to read from
104 | * @param messageLength length of expected message
105 | * @throws IOException when I2C device cannot be asked for data due to
106 | * communication fail
107 | * @return true if receiving updates just started or false otherwise (if it
108 | * have been already going for example)
109 | */
110 | boolean startReceivingUpdates(int register, byte messageLength) throws IOException;
111 |
112 | /**
113 | * Tells I2C device to send data continuously as it available.
114 | *
115 | * @param messageLength length of expected message
116 | * @throws IOException when I2C device cannot be asked for data due to
117 | * communication fail
118 | * @return true if receiving updates just started or false otherwise (if it
119 | * have been already going for example)
120 | */
121 | boolean startReceivingUpdates(byte messageLength) throws IOException;
122 |
123 | /**
124 | * Tells I2C device to stop sending data continuously.
125 | *
126 | * @throws IOException when I2C device cannot be asked to stop due to
127 | * communication fail
128 | */
129 | void stopReceivingUpdates() throws IOException;
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/I2CEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | import java.util.Arrays;
28 |
29 | /**
30 | * This event occurs when I2C device transmits some data. {@link I2CListener}
31 | * processes this kind of events.
32 | *
33 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
34 | */
35 | public class I2CEvent {
36 |
37 | private final I2CDevice device;
38 | private final int register;
39 | private final byte[] data;
40 |
41 | public I2CEvent(I2CDevice device, int register, byte[] data) {
42 | this.device = device;
43 | this.register = register;
44 | this.data = data;
45 | }
46 |
47 | /**
48 | * Returns data received from {@link I2CDevice}.
49 | */
50 | public byte[] getData() {
51 | return data;
52 | }
53 |
54 | /**
55 | * Returns register address received from {@link I2CDevice}.
56 | */
57 | public int getRegister() {
58 | return register;
59 | }
60 |
61 | /**
62 | * Returns {@link I2CDevice} which sent a piece of data.
63 | */
64 | public I2CDevice getDevice() {
65 | return device;
66 | }
67 |
68 | @Override
69 | public String toString() {
70 | return String.format("I2CEvent [device=%s, register=0x%02X, data=%s]", device, register, Arrays.toString(data));
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/I2CListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * Describes processing of data received from {@link I2CDevice}.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public interface I2CListener {
33 |
34 | void onReceive(I2CEvent event);
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/IODevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | import java.io.IOException;
28 | import java.util.Set;
29 | import org.firmata4j.fsm.Event;
30 |
31 | /**
32 | * This interface describes a device which is able to receive and transmit
33 | * signals. It has a set of pins that represent connections of the device to
34 | * external signal receivers and transmitters.
35 | *
36 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
37 | */
38 | public interface IODevice {
39 |
40 | /**
41 | * Initiates communication with hardware.
42 | * This method initialize the {@link IODevice} instance. Initialization may
43 | * take some time depending on speed of communication to hardware.
44 | * To check whether the device is ready, use {@link #isReady()}.
45 | * If you want to wait until the device is initializing and continue working
46 | * with it, use {@link #ensureInitializationIsDone()}.
47 | * If you develop application in asynchronous style, you may want to
48 | * register an event listener that will receive a message when the device is
49 | * ready.
50 | *
51 | * @throws IOException when communication cannot be established
52 | * @see IODeviceEventListener#onStart(org.firmata4j.IOEvent)
53 | */
54 | void start() throws IOException;
55 |
56 | /**
57 | * Terminates communication with hardware.
58 | * When communication is terminated, an event is published to
59 | * {@link IODeviceEventListener}s.
60 | *
61 | * @throws IOException when communication cannot be properly terminated
62 | * @see IODeviceEventListener#onStop(org.firmata4j.IOEvent)
63 | */
64 | void stop() throws IOException;
65 |
66 | /**
67 | * Waits for initialization is done.
68 | * Use this method if you want to be sure that device is ready to
69 | * communicate.
70 | *
71 | * @throws InterruptedException when the device cannot be started or waiting
72 | * for initialization is interrupted or connection has not been established
73 | * during timeout.
74 | */
75 | void ensureInitializationIsDone() throws InterruptedException;
76 |
77 | /**
78 | * Checks whether the device is fully initialized and ready to use.
79 | *
80 | * @return true if device is ready, false otherwise
81 | */
82 | boolean isReady();
83 |
84 | /**
85 | * Returns a set of pins of the device.
86 | *
87 | * @return set of device's pins
88 | * @see Pin
89 | */
90 | Set getPins();
91 |
92 | /**
93 | * Returns count of pins of the device.
94 | *
95 | * @return count of pins
96 | */
97 | int getPinsCount();
98 |
99 | /**
100 | * Returns a pin by its index on device. The index should be less than
101 | * result returned by {@link #getPinsCount()}.
102 | *
103 | * @param index index of the pin
104 | * @return the pin
105 | */
106 | Pin getPin(int index);
107 |
108 | /**
109 | * Returns I2C device by its address.
110 | *
111 | * @param address
112 | * @return I2C device
113 | * @throws IOException when communication to the IO device failed
114 | */
115 | I2CDevice getI2CDevice(byte address) throws IOException;
116 |
117 | /**
118 | * Adds the specified listener to receive events from this device.
119 | *
120 | * @param listener the listener
121 | */
122 | void addEventListener(IODeviceEventListener listener);
123 |
124 | /**
125 | * Removes the specified listener so that it no longer receives events from
126 | * this device.
127 | *
128 | * @param listener the listener
129 | */
130 | void removeEventListener(IODeviceEventListener listener);
131 |
132 | /**
133 | * Returns the name of a protocol that the device uses.
134 | *
135 | * @return the name of a protocol
136 | */
137 | String getProtocol();
138 |
139 | /**
140 | * Adds handler for low-level events of specified type.
141 | *
142 | * "*" is a special type that matches to any message. The handlers registed
143 | * for that type will receive an even after the hadlers of specific type.
144 | *
145 | * @param messageType type of low-level event
146 | * @param handler handler of the event
147 | */
148 | void addProtocolMessageHandler(String messageType, Consumer handler);
149 |
150 | /**
151 | * Sends text message to device.
152 | *
153 | * @param message the message
154 | * @throws IOException when sending a message fails
155 | */
156 | void sendMessage(String message) throws IOException;
157 |
158 | /**
159 | * Sends binary message to device.
160 | *
161 | * @param msg message
162 | * @throws IOException when sending the message fails
163 | */
164 | void sendMessage(byte... msg) throws IOException;
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/IODeviceEventListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * This interface describes an object that can receive and handle events from an
29 | * {@link IODevice}.
30 | *
31 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
32 | */
33 | public interface IODeviceEventListener {
34 |
35 | /**
36 | * Invoked when an {@link IODevice} has been successfully started
37 | * and initialized.
38 | *
39 | * @param event the event
40 | */
41 | void onStart(IOEvent event);
42 |
43 | /**
44 | * Invoked when communication with {@link IODevice} has been
45 | * successfully terminated.
46 | *
47 | * @param event the event
48 | */
49 | void onStop(IOEvent event);
50 |
51 | /**
52 | * Invoked when the state of one of device's pins has been changed.
53 | * It can be change of mode or a value.
54 | *
55 | * @param event the event
56 | */
57 | void onPinChange(IOEvent event);
58 |
59 | /**
60 | * Invoked when a string message has been received from the device.
61 | *
62 | * @param event the event
63 | * @param message the message
64 | */
65 | void onMessageReceive(IOEvent event, String message);
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/IOEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * An event which indicates that the state of an {@link IODevice} changed.
29 | *
30 | * @author @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public class IOEvent {
33 |
34 | private final IODevice device;
35 | private final Pin pin;
36 | private final long value;
37 | private final long timestamp;
38 |
39 | /**
40 | * Constructs the event is relevant to {@link IODevice} as a whole.
41 | *
42 | * @param device the device that originated the event
43 | */
44 | public IOEvent(IODevice device) {
45 | this(device, System.currentTimeMillis());
46 | }
47 |
48 | /**
49 | * Constructs the event is relevant to {@link IODevice} as a whole.
50 | *
51 | * This constructor allows setting the timestamp of event.
52 | *
53 | * @param device the device that originated the event
54 | * @param timestamp the timestamp of event
55 | */
56 | public IOEvent(IODevice device, long timestamp) {
57 | this.device = device;
58 | this.pin = null;
59 | this.value = 0;
60 | this.timestamp = timestamp;
61 | }
62 |
63 | /**
64 | * Constructs the event is relevant to a particular {@link Pin}.
65 | *
66 | * @param pin the pin that originated the event
67 | */
68 | public IOEvent(Pin pin) {
69 | this(pin, System.currentTimeMillis());
70 | }
71 |
72 | /**
73 | * Constructs the event is relevant to a particular {@link Pin}.
74 | *
75 | * This constructor allows setting the timestamp of event.
76 | *
77 | * @param pin the pin that originated the event
78 | * @param timestamp the timestamp of event
79 | */
80 | public IOEvent(Pin pin, long timestamp) {
81 | this.device = pin.getDevice();
82 | this.pin = pin;
83 | this.value = pin.getValue();
84 | this.timestamp = timestamp;
85 | }
86 |
87 | /**
88 | * Returns the device that relates to the event.
89 | *
90 | * @return the device in which the event has occurred
91 | */
92 | public IODevice getDevice() {
93 | return device;
94 | }
95 |
96 | /**
97 | * Returns the pin that relates to the event. If the event does not involve
98 | * any particular pin, it returns null.
99 | *
100 | * @return the pin that originated the event or null if a pin does not
101 | * involved in the event
102 | */
103 | public Pin getPin() {
104 | return pin;
105 | }
106 |
107 | /**
108 | * Returns the value the pin received.
109 | *
110 | * If the event is not about pin, always returns 0.
111 | *
112 | * @return the value the pin received
113 | */
114 | public long getValue() {
115 | return value;
116 | }
117 |
118 | /**
119 | * Returns the timestamp of the event.
120 | *
121 | * @return timestamp of the event
122 | */
123 | public long getTimestamp() {
124 | return timestamp;
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/OnMessageReceiveListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * Handles events of receiving a message. Ignores the other events.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public abstract class OnMessageReceiveListener implements IODeviceEventListener {
33 |
34 | @Override
35 | public void onStart(IOEvent event) {
36 |
37 | }
38 |
39 | @Override
40 | public void onStop(IOEvent event) {
41 |
42 | }
43 |
44 | @Override
45 | public void onPinChange(IOEvent event) {
46 |
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/OnPinChangeListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * Handles events of a pin change. Ignores the other events.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public abstract class OnPinChangeListener extends Consumer implements IODeviceEventListener {
33 |
34 | @Override
35 | public void onStart(IOEvent event) {
36 |
37 | }
38 |
39 | @Override
40 | public void onStop(IOEvent event) {
41 |
42 | }
43 |
44 | @Override
45 | public void onPinChange(IOEvent event) {
46 | accept(event);
47 | }
48 |
49 | @Override
50 | public void onMessageReceive(IOEvent event, String message) {
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/OnStartListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * Handles events when {@link IODevice} starts. Ignores other events.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public abstract class OnStartListener extends Consumer implements IODeviceEventListener {
33 |
34 | @Override
35 | public void onStart(IOEvent event) {
36 | accept(event);
37 | }
38 |
39 | @Override
40 | public void onStop(IOEvent event) {
41 |
42 | }
43 |
44 | @Override
45 | public void onPinChange(IOEvent event) {
46 |
47 | }
48 |
49 | @Override
50 | public void onMessageReceive(IOEvent event, String message) {
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/OnStopListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * Handles events when {@link IODevice} stops. Ignores other events.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public abstract class OnStopListener extends Consumer implements IODeviceEventListener {
33 |
34 | @Override
35 | public void onStart(IOEvent event) {
36 |
37 | }
38 |
39 | @Override
40 | public void onStop(IOEvent event) {
41 | accept(event);
42 | }
43 |
44 | @Override
45 | public void onPinChange(IOEvent event) {
46 |
47 | }
48 |
49 | @Override
50 | public void onMessageReceive(IOEvent event, String message) {
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/Parser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j;
25 |
26 | /**
27 | * Processes byte-stream by the rules of underlying protocol.
28 | *
29 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
30 | */
31 | public interface Parser {
32 |
33 | /**
34 | * Starts processing of input data.
35 | */
36 | void start();
37 |
38 | /**
39 | * Stops processing of input data.
40 | */
41 | void stop();
42 |
43 | /**
44 | * Processes the input data.
45 | *
46 | * @param bytes the data
47 | */
48 | void parse(byte[] bytes);
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/Pin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | import java.io.IOException;
28 | import java.util.Set;
29 | import org.firmata4j.firmata.parser.FirmataToken;
30 |
31 | /**
32 | * A pin is a connector of an {@link IODevice} to external signal receiver or
33 | * transmitter.
34 | *
35 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
36 | */
37 | public interface Pin {
38 |
39 | /**
40 | * Mode represents particular duty of a pin.
41 | */
42 | enum Mode {
43 |
44 | /**
45 | * Digital pin in input mode
46 | */
47 | INPUT,
48 | /**
49 | * Digital pin in output mode
50 | */
51 | OUTPUT,
52 | /**
53 | * Analog pin in analog input mode
54 | */
55 | ANALOG,
56 | /**
57 | * Digital pin in PWM output mode
58 | */
59 | PWM,
60 | /**
61 | * Digital pin in Servo output mode
62 | */
63 | SERVO,
64 | /**
65 | * shiftIn/shiftOut mode
66 | */
67 | SHIFT,
68 | /**
69 | * Pin included in I2C setup
70 | */
71 | I2C,
72 | /**
73 | * Pin configured for 1-wire
74 | */
75 | ONEWIRE,
76 | /**
77 | * Pin configured for stepper motor
78 | */
79 | STEPPER,
80 | /**
81 | * Pin configured for rotary encoders
82 | */
83 | ENCODER,
84 | /**
85 | * Pin configured for serial communication
86 | */
87 | SERIAL,
88 | /**
89 | * Enable internal pull-up resistor for pin
90 | */
91 | PULLUP,
92 |
93 | // add new modes here
94 |
95 | /**
96 | * Indicates a mode that this client library doesn't support
97 | */
98 | UNSUPPORTED,
99 | /**
100 | * Pin configured to be ignored by digitalWrite and capabilityResponse
101 | */
102 | IGNORED;
103 |
104 | /**
105 | * Resolves a mode-token from firmata message to enum value.
106 | *
107 | * @param modeToken token that stands for mode
108 | * @return mode
109 | */
110 | public static Mode resolve(byte modeToken) {
111 | if (modeToken == FirmataToken.PIN_MODE_IGNORE) {
112 | return IGNORED;
113 | }
114 | if (modeToken > FirmataToken.TOTAL_PIN_MODES) {
115 | return UNSUPPORTED;
116 | }
117 | return values()[modeToken];
118 | }
119 | }
120 |
121 | /**
122 | * Return an {@link IODevice} the pin belongs to.
123 | *
124 | * @return the pin's device
125 | */
126 | IODevice getDevice();
127 |
128 | /**
129 | * Returns the index of the pin on its device.
130 | *
131 | * @return the index of the pin
132 | */
133 | byte getIndex();
134 |
135 | /**
136 | * Returns current mode of the pin.
137 | *
138 | * @return current mode of the pin
139 | */
140 | Mode getMode();
141 |
142 | /**
143 | * Assigns new mode to the pin.
144 | *
145 | * @param mode the mode the pin should get into
146 | * @throws IOException when assigning is failed due a communication issue
147 | * @throws IllegalArgumentException when the pin does not support the mode
148 | */
149 | void setMode(Mode mode) throws IOException, IllegalArgumentException;
150 |
151 | /**
152 | *
153 | * @param minPulse servo moter control pulse with [µs] on setValue(0)
154 | * @param maxPulse servo moter control pulse with [µs] on setValue(180)
155 | * @throws IOException when assigning is failed due a communication issue
156 | * @throws IllegalArgumentException when the pin does not support the mode
157 | */
158 | void setServoMode(int minPulse, int maxPulse) throws IOException, IllegalArgumentException;
159 |
160 | /**
161 | * Checks if the pin supports the mode
162 | *
163 | * @param mode the mode
164 | * @return true if the pin supports the mode, false otherwise
165 | */
166 | boolean supports(Mode mode);
167 |
168 | /**
169 | * Returns a set of modes supported by the pin.
170 | *
171 | * @return set of supported modes
172 | */
173 | Set getSupportedModes();
174 |
175 | /**
176 | * Returns current value of the pin.
177 | *
178 | * @return current value of the pin
179 | */
180 | long getValue();
181 |
182 | /**
183 | * Sets the value to the pin. It is impossible to set a value to a pin in
184 | * input mode such as {@link Mode#INPUT} or {@link Mode#ANALOG}.
185 | *
186 | * @param value the value to be assigned to the pin
187 | * @throws IOException when setting fails due a communication issue
188 | * @throws IllegalStateException when the pin is in input mode such as
189 | * {@link Mode#INPUT} or {@link Mode#ANALOG}.
190 | */
191 | void setValue(long value) throws IOException, IllegalStateException;
192 |
193 | /**
194 | * Adds the specified listener to receive events from this pin.
195 | *
196 | * @param listener the listener
197 | */
198 | void addEventListener(PinEventListener listener);
199 |
200 | /**
201 | * Removes the specified listener so that it no longer receives events from
202 | * this pin.
203 | *
204 | * @param listener the listener
205 | */
206 | void removeEventListener(PinEventListener listener);
207 |
208 |
209 | /**
210 | * Remove all listeners from this pin.
211 | *
212 | */
213 | void removeAllEventListeners();
214 |
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/PinEventListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j;
26 |
27 | /**
28 | * The listener interface for receiving events from {@link Pin}. When pin's mode
29 | * or value changes, the relevant method in the listener object is invoked.
30 | *
31 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
32 | */
33 | public interface PinEventListener {
34 |
35 | /**
36 | * Invoked when pin's mode changes.
37 | * @param event Information about the event that was triggered
38 | */
39 | void onModeChange(IOEvent event);
40 |
41 | /**
42 | * Invoked when pin's value changes.
43 | * @param event Information about the event that was triggered
44 | */
45 | void onValueChange(IOEvent event);
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/DaemonThreadFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata;
26 |
27 | import java.util.concurrent.ThreadFactory;
28 | import java.util.concurrent.atomic.AtomicInteger;
29 |
30 | /**
31 | * Creates daemon threads.
32 | *
33 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
34 | */
35 | public class DaemonThreadFactory implements ThreadFactory {
36 |
37 | private final AtomicInteger counter = new AtomicInteger();
38 |
39 | private final String namePrefix;
40 |
41 | /**
42 | * Creates thread factory that spawns threads with specified name prefix.
43 | *
44 | * @param namePrefix prefix of thread's names
45 | */
46 | public DaemonThreadFactory(String namePrefix) {
47 | this.namePrefix = namePrefix;
48 | }
49 |
50 | @Override
51 | public Thread newThread(Runnable r) {
52 | Thread result = new Thread(r, String.format("%s-%d", namePrefix, counter.incrementAndGet()));
53 | result.setDaemon(true);
54 | return result;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/FirmataI2CDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata;
25 |
26 | import java.io.IOException;
27 | import java.util.Map;
28 | import java.util.Set;
29 | import java.util.concurrent.ConcurrentHashMap;
30 | import java.util.concurrent.ConcurrentSkipListSet;
31 | import java.util.concurrent.atomic.AtomicBoolean;
32 | import org.firmata4j.I2CDevice;
33 | import org.firmata4j.I2CEvent;
34 | import org.firmata4j.I2CListener;
35 |
36 | /**
37 | * Represents an I2C device and encapsulates communication logic using Firmata
38 | * protocol.
39 | *
40 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
41 | */
42 | public class FirmataI2CDevice implements I2CDevice {
43 |
44 | public static final int REGISTER_NOT_SET = -1;
45 |
46 | private final FirmataDevice masterDevice;
47 |
48 | private final byte address;
49 |
50 | private final AtomicBoolean receivingUpdates = new AtomicBoolean(false);
51 |
52 | private final Map callbacks = new ConcurrentHashMap<>();
53 |
54 | private final Set subscribers = new ConcurrentSkipListSet<>();
55 |
56 | FirmataI2CDevice(FirmataDevice masterDevice, byte address) {
57 | this.masterDevice = masterDevice;
58 | this.address = address;
59 | }
60 |
61 | @Override
62 | public byte getAddress() {
63 | return address;
64 | }
65 |
66 | @Override
67 | public void setDelay(int delay) throws IOException {
68 | masterDevice.setI2CDelay(delay);
69 | }
70 |
71 | @Override
72 | public void tell(byte... data) throws IOException {
73 | masterDevice.sendMessage(FirmataMessageFactory.i2cWriteRequest(address, data));
74 | }
75 |
76 | @Override
77 | public void ask(byte responseLength, I2CListener listener) throws IOException {
78 | ask(REGISTER_NOT_SET, responseLength, listener);
79 | }
80 |
81 | @Override
82 | public void ask(int register, byte responseLength, I2CListener listener) throws IOException {
83 | callbacks.put(register, listener);
84 | masterDevice.sendMessage(FirmataMessageFactory.i2cReadRequest(address, register, responseLength, false));
85 | }
86 |
87 | @Override
88 | public void subscribe(I2CListener listener) {
89 | subscribers.add(listener);
90 | }
91 |
92 | @Override
93 | public void unsubscribe(I2CListener listener) {
94 | subscribers.remove(listener);
95 | }
96 |
97 | @Override
98 | public boolean startReceivingUpdates(int register, byte messageLength) throws IOException {
99 | boolean result = receivingUpdates.compareAndSet(false, true);
100 | if (result) {
101 | masterDevice.sendMessage(FirmataMessageFactory.i2cReadRequest(address, register, messageLength, true));
102 | }
103 | return result;
104 | }
105 |
106 | @Override
107 | public boolean startReceivingUpdates(byte messageLength) throws IOException {
108 | boolean result = receivingUpdates.compareAndSet(false, true);
109 | if (result) {
110 | masterDevice.sendMessage(FirmataMessageFactory.i2cReadRequest(address, REGISTER_NOT_SET, messageLength, true));
111 | }
112 | return result;
113 | }
114 |
115 | @Override
116 | public void stopReceivingUpdates() throws IOException {
117 | if (receivingUpdates.compareAndSet(true, false)) {
118 | masterDevice.sendMessage(FirmataMessageFactory.i2cStopContinuousRequest(address));
119 | }
120 | }
121 |
122 | /**
123 | * {@link FirmataDevice} calls this method when receives a message from I2C
124 | * device.
125 | *
126 | * @param register The device register read from.
127 | * @param message actual data from I2C device
128 | */
129 | void onReceive(int register, byte[] message) {
130 | I2CEvent evt = new I2CEvent(this, register, message);
131 | I2CListener listener = callbacks.remove(register);
132 | if (listener == null) {
133 | for (I2CListener subscriber : subscribers) {
134 | subscriber.onReceive(evt);
135 | }
136 | } else {
137 | listener.onReceive(evt);
138 | }
139 | }
140 |
141 | @Override
142 | public String toString() {
143 | return String.format("FirmataI2CDevice [address=0x%02X]", address);
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/FirmataPin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata;
26 |
27 | import java.io.IOException;
28 | import java.util.Collections;
29 | import java.util.EnumSet;
30 | import java.util.HashSet;
31 | import java.util.Set;
32 |
33 | import org.firmata4j.IOEvent;
34 | import org.firmata4j.Pin;
35 | import org.firmata4j.PinEventListener;
36 |
37 | /**
38 | * This class contains implementation of Firmata pin.
39 | *
40 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
41 | */
42 | public class FirmataPin implements Pin {
43 |
44 | private final FirmataDevice device;
45 | private final byte pinId;
46 | private final Set supportedModes = Collections.synchronizedSet(EnumSet.noneOf(Mode.class));
47 | private final Set listeners = Collections.synchronizedSet(new HashSet());
48 | private volatile Mode currentMode;
49 | private volatile long currentValue;
50 |
51 | /**
52 | * Constructs Firmata pin for the specified device.
53 | *
54 | * @param device the device the pin belongs to
55 | * @param index the index of pin on the device
56 | */
57 | public FirmataPin(FirmataDevice device, byte index) {
58 | this.device = device;
59 | this.pinId = index;
60 | }
61 |
62 | @Override
63 | public FirmataDevice getDevice() {
64 | return device;
65 | }
66 |
67 | @Override
68 | public byte getIndex() {
69 | return pinId;
70 | }
71 |
72 | @Override
73 | public Mode getMode() {
74 | return currentMode;
75 | }
76 |
77 | @Override
78 | public void setMode(Mode mode) throws IOException {
79 | setMode(mode, 544, 2400); // Arduino defaults (https://www.arduino.cc/en/Reference/ServoAttach)
80 | }
81 |
82 | @Override
83 | public void setServoMode(int minPulse, int maxPulse) throws IOException {
84 | setMode(Mode.SERVO, minPulse, maxPulse);
85 | }
86 |
87 | private synchronized void setMode(Mode mode, int minPulse, int maxPulse) throws IOException {
88 | if (supports(mode)) {
89 | if (currentMode != mode) {
90 | if (mode == Mode.SERVO) {
91 | getDevice().sendMessage(FirmataMessageFactory.servoConfig(pinId, minPulse, maxPulse));
92 | // The currentValue for a servo is unknown as the motor is
93 | // send to the 1.5ms position when pinStateRequest is invoked
94 | currentValue = -1;
95 | }
96 | getDevice().sendMessage(FirmataMessageFactory.setMode(pinId, mode));
97 | currentMode = mode;
98 | IOEvent evt = new IOEvent(this);
99 | getDevice().pinChanged(evt);
100 | for (PinEventListener listener : listeners) {
101 | listener.onModeChange(evt);
102 | }
103 | getDevice().sendMessage(FirmataMessageFactory.pinStateRequest(pinId));
104 | }
105 | } else {
106 | throw new IllegalArgumentException(String.format("Pin %d does not support mode %s", pinId, mode));
107 | }
108 | }
109 |
110 | @Override
111 | public boolean supports(Mode mode) {
112 | return supportedModes.contains(mode);
113 | }
114 |
115 | @Override
116 | @SuppressWarnings("unchecked")
117 | public Set getSupportedModes() {
118 | if (supportedModes.isEmpty()) {
119 | return Collections.EMPTY_SET;
120 | } else {
121 | return EnumSet.copyOf(supportedModes);
122 | }
123 | }
124 |
125 | @Override
126 | public long getValue() {
127 | return currentValue;
128 | }
129 |
130 | @Override
131 | public synchronized void setValue(long value) throws IOException, IllegalStateException {
132 | byte[] message;
133 | long newValue;
134 | if (currentMode == Mode.OUTPUT) {
135 | //have to calculate the value of whole port (8-pin set) the pin sits in
136 | byte portId = (byte) (pinId / 8);
137 | byte pinInPort = (byte) (pinId % 8);
138 | byte portValue = 0;
139 | for (int i = 0; i < 8; i++) {
140 | Pin p = device.getPin(portId * 8 + i);
141 | if (p.getMode() == Mode.OUTPUT && p.getValue() > 0) {
142 | portValue |= 1 << i;
143 | }
144 | }
145 | byte bitmask = (byte) (1 << pinInPort);
146 | boolean val = value > 0;
147 | if (val) {
148 | portValue |= bitmask;
149 | } else {
150 | portValue &= ((byte) ~bitmask);
151 | }
152 | message = FirmataMessageFactory.setDigitalPinValue(portId, portValue);
153 | newValue = val ? 1 : 0;
154 | } else if (currentMode == Mode.ANALOG || currentMode == Mode.PWM || currentMode == Mode.SERVO) {
155 | message = FirmataMessageFactory.setAnalogPinValue(pinId, value);
156 | newValue = value;
157 | } else {
158 | throw new IllegalStateException(String.format("Port %d is in %s mode and its value cannot be set.", pinId, currentMode));
159 | }
160 | if (currentValue != newValue) {
161 | device.sendMessage(message);
162 | updateValue(value);
163 | }
164 | }
165 |
166 | @Override
167 | public void addEventListener(PinEventListener listener) {
168 | listeners.add(listener);
169 | }
170 |
171 | @Override
172 | public void removeEventListener(PinEventListener listener) {
173 | listeners.remove(listener);
174 | }
175 |
176 |
177 | @Override
178 | public void removeAllEventListeners() {
179 | listeners.clear();
180 | }
181 |
182 |
183 | /**
184 | * Adds supported mode to the pin.
185 | *
186 | * @param mode the mode
187 | */
188 | void addSupportedMode(Mode mode) {
189 | supportedModes.add(mode);
190 | }
191 |
192 | /**
193 | * Sets initial mode of a pin. This method bypasses standard
194 | * {@link #setMode(org.firmata4j.Pin.Mode)} to avoid sending a
195 | * message to hardware.
196 | *
197 | * @param mode initial mode
198 | */
199 | synchronized void initMode(Mode mode) {
200 | currentMode = mode;
201 | }
202 |
203 | /**
204 | * Sets initial value of a pin. This method bypasses standard
205 | * {@link #setValue(long)} to avoid sending a message to hardware.
206 | *
207 | * @param value initial value
208 | */
209 | synchronized void initValue(long value) {
210 | currentValue = value;
211 | }
212 |
213 | /**
214 | * Permits the {@link FirmataDevice} to update input pin value.
215 | *
216 | * @param value the new value
217 | */
218 | synchronized void updateValue(long value) {
219 | if (value != currentValue) {
220 | currentValue = value;
221 | IOEvent evt = new IOEvent(this);
222 | getDevice().pinChanged(evt); // the device listeners receive the event first
223 | for (PinEventListener listener : listeners) { // then pin listeners receive the event
224 | listener.onValueChange(evt);
225 | }
226 | }
227 | }
228 |
229 | }
230 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/FirmataWatchdog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata;
25 |
26 | import java.util.concurrent.Executors;
27 | import java.util.concurrent.ScheduledExecutorService;
28 | import java.util.concurrent.TimeUnit;
29 | import java.util.concurrent.atomic.AtomicBoolean;
30 | import org.firmata4j.Consumer;
31 | import org.firmata4j.fsm.Event;
32 |
33 | /**
34 | * Implementation of low-level event hadler that supposed to be used as a
35 | * watchdog.
36 | *
37 | * It gets activated by the first event received since the watchdog subscribed.
38 | *
39 | *
Example:
40 | *
41 | * IODevice device = new FirmataDevice(port);
42 | * //...
43 | * FirmataWatchdog watchdog = new FirmataWatchdog(3000, new Runnable() {
44 | * public void run() {
45 | * // do something when there were no low-level events during 3000 milliseconds
46 | * }
47 | * });
48 | * device.addProtocolMessageHandler(FirmataEventType.ANY, watchdog);
49 | * //...
50 | * device.start();
51 | *
52 | *
53 | *
54 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
55 | */
56 | public class FirmataWatchdog extends Consumer {
57 |
58 | private final long timeout;
59 |
60 | private final Runnable action;
61 |
62 | private final AtomicBoolean active = new AtomicBoolean(false);
63 |
64 | private volatile long lastTimestamp = 0;
65 |
66 | private final static TimeUnit UNIT = TimeUnit.MILLISECONDS;
67 |
68 | private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("firmata-watchdog"));
69 |
70 | /**
71 | * Creates a watchdog that activates itself on the first received event.
72 | *
73 | * @param timeout timeout after the latest low-level event that triggers the
74 | * action
75 | * @param action the action that is to be triggered when there were no
76 | * low-level events during timeout since the latest event
77 | */
78 | public FirmataWatchdog(long timeout, Runnable action) {
79 | this.timeout = timeout;
80 | this.action = action;
81 | }
82 |
83 | @Override
84 | public void accept(Event evt) {
85 | if (lastTimestamp == 0) { // got fitst event
86 | enable();
87 | }
88 | lastTimestamp = evt.getTimestamp();
89 | }
90 |
91 | public boolean isActive() {
92 | return active.get();
93 | }
94 |
95 | public void setActive(boolean active) {
96 | if (active) {
97 | enable();
98 | } else {
99 | disable();
100 | }
101 | }
102 |
103 | public void enable() {
104 | if (!active.getAndSet(true)) {
105 | EXECUTOR.schedule(watch, timeout, UNIT);
106 | }
107 | }
108 |
109 | public void disable() {
110 | active.set(false);
111 | }
112 |
113 | private final Runnable watch = new Runnable() {
114 | @Override
115 | public void run() {
116 | if (System.currentTimeMillis() - lastTimestamp >= timeout) {
117 | action.run();
118 | }
119 | if (active.get()) {
120 | EXECUTOR.schedule(watch, timeout, UNIT);
121 | }
122 | }
123 | };
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/FirmataEventType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata.parser;
25 |
26 | /**
27 | * Contains constants for protocol message types and payload parts.
28 | *
29 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
30 | */
31 | public interface FirmataEventType {
32 |
33 | String ANY = "*";
34 |
35 | String PROTOCOL_MESSAGE = "protocolMessage";
36 | String PROTOCOL_MAJOR = "major";
37 | String PROTOCOL_MINOR = "minor";
38 |
39 | String FIRMWARE_MESSAGE = "firmwareMessage";
40 | String FIRMWARE_MAJOR = "major";
41 | String FIRMWARE_MINOR = "minor";
42 | String FIRMWARE_NAME = "firmwareName";
43 |
44 | String ANALOG_MAPPING_MESSAGE = "analogMapping";
45 | String ANALOG_MAPPING = "analogMapping";
46 |
47 | String ANALOG_MESSAGE_RESPONSE = "analogMessage";
48 | String DIGITAL_MESSAGE_RESPONSE = "digitalMessage";
49 | String I2C_MESSAGE = "i2cMessage";
50 | String I2C_ADDRESS = "i2cAddress";
51 | String I2C_REGISTER = "i2cRegister";
52 |
53 | String PIN_CAPABILITIES_MESSAGE = "pinCapabilities";
54 | String PIN_STATE = "pinState";
55 | String PIN_ID = "pinId";
56 | String PIN_SUPPORTED_MODES = "supportedModes";
57 | String PIN_MODE = "pinMode";
58 | String PIN_VALUE = "pinValue";
59 | String PIN_CAPABILITIES_FINISHED = "pinCapabilitiesFinished";
60 |
61 | String SYSTEM_RESET_MESSAGE = "systemReset";
62 |
63 | String STRING_MESSAGE = "stringMessage";
64 |
65 | String SYSEX_CUSTOM_MESSAGE = "sysexCustomMessage";
66 |
67 | String ERROR_MESSAGE = "error";
68 | String ERROR_DESCRIPTION = "description";
69 | String ERROR_CAUSE = "cause";
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/FirmataParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata.parser;
25 |
26 | import java.util.concurrent.ArrayBlockingQueue;
27 | import java.util.concurrent.atomic.AtomicBoolean;
28 | import org.firmata4j.fsm.FiniteStateMachine;
29 | import org.firmata4j.Parser;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | /**
34 | * Parses byte-stream of Firmata messages.
35 | *
36 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
37 | * @author Ali Kia
38 | */
39 | public class FirmataParser implements Parser {
40 |
41 | private Thread parserExecutor;
42 | private final FiniteStateMachine fsm;
43 | private final ArrayBlockingQueue byteQueue = new ArrayBlockingQueue<>(128);
44 | private final AtomicBoolean running = new AtomicBoolean();
45 |
46 | private static final long WAIT_FOR_TERMINATION_DELAY = 3000;
47 | private static final Logger LOGGER = LoggerFactory.getLogger(FirmataParser.class);
48 |
49 | public FirmataParser(FiniteStateMachine fsm) {
50 | this.fsm = fsm;
51 | }
52 |
53 | @Override
54 | public void start() {
55 | if (!running.getAndSet(true)) {
56 | parserExecutor = new Thread(new JobRunner(), "firmata-parser-thread");
57 | parserExecutor.setDaemon(true);
58 | parserExecutor.start();
59 | }
60 | }
61 |
62 | @Override
63 | public void stop() {
64 | if (running.getAndSet(false)) {
65 | byteQueue.clear();
66 | // interrupt the thread to ensure it falls out of the loop
67 | // and sees the shutdown request
68 | parserExecutor.interrupt();
69 | try {
70 | parserExecutor.join(WAIT_FOR_TERMINATION_DELAY);
71 | } catch (InterruptedException e) {
72 | Thread.currentThread().interrupt();
73 | throw new RuntimeException("Parser didn't stop gracefully", e);
74 | }
75 | }
76 | }
77 |
78 | @Override
79 | public void parse(byte[] bytes) {
80 | if (bytes != null && !byteQueue.offer(bytes)) {
81 | LOGGER.warn("Parser reached byte queue limit. Some bytes were skipped.");
82 | }
83 | }
84 |
85 | private class JobRunner implements Runnable {
86 |
87 | @Override
88 | public void run() {
89 | while (running.get()) {
90 | try {
91 | fsm.process(byteQueue.take());
92 | } catch (InterruptedException e) {
93 | Thread.currentThread().interrupt();
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/FirmataToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | /**
28 | * This class contains set of constants that represent tokens of Firmata
29 | * protocol.
30 | * In addition it contains headers and keys of events that occur when Firmata
31 | * messages are received.
32 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
33 | */
34 | public interface FirmataToken {
35 |
36 | /**
37 | * Version numbers for the protocol. The protocol is still changing, so these
38 | * version numbers are important. This number can be queried so that host
39 | * software can test whether it will be compatible with the currently
40 | * installed firmware.
41 | */
42 | byte FIRMATA_MAJOR_VERSION = 2; // for non-compatible changes
43 | byte FIRMATA_MINOR_VERSION = 3; // for backwards compatible changes
44 | byte FIRMATA_BUGFIX_VERSION = 1; // for bugfix releases
45 |
46 | // message command bytes (128-255/0x80-0xFF)
47 |
48 | /**
49 | * send data for a digital pin
50 | */
51 | byte DIGITAL_MESSAGE = (byte) 0x90;
52 |
53 | /**
54 | * send data for an analog pin (or PWM)
55 | */
56 | byte ANALOG_MESSAGE = (byte) 0xE0;
57 |
58 | /**
59 | * enable analog input by pin #
60 | */
61 | byte REPORT_ANALOG = (byte) 0xC0;
62 |
63 | /**
64 | * enable digital input by port pair
65 | */
66 | byte REPORT_DIGITAL = (byte) 0xD0;
67 |
68 | /**
69 | * set a pin to INPUT/OUTPUT/PWM/etc
70 | */
71 | byte SET_PIN_MODE = (byte) 0xF4;
72 |
73 | /**
74 | * set value of an individual digital pin
75 | */
76 | byte SET_DIGITAL_PIN_VALUE = (byte) 0xF5;
77 |
78 | /**
79 | * report protocol version
80 | */
81 | byte REPORT_VERSION = (byte) 0xF9;
82 |
83 | /**
84 | * reset from MIDI
85 | */
86 | byte SYSTEM_RESET = (byte) 0xFF;
87 |
88 | /**
89 | * start a MIDI Sysex message
90 | */
91 | byte START_SYSEX = (byte) 0xF0;
92 |
93 | /**
94 | * end a MIDI Sysex message
95 | */
96 | byte END_SYSEX = (byte) 0xF7;
97 |
98 | // extended command set using sysex (0-127/0x00-0x7F)
99 | /* 0x00-0x0F reserved for user-defined commands */
100 | byte RESERVED_COMMAND = 0x00; // 2nd SysEx data byte is a chip-specific command (AVR, PIC, TI, etc).
101 | byte SERIAL_MESSAGE = 0x60; // communicate with serial devices, including other boards
102 | byte ENCODER_DATA = 0x61; // reply with encoders current positions
103 | byte SERVO_CONFIG = 0x70; // set max angle, minPulse, maxPulse, freq
104 | byte STRING_DATA = 0x71; // a string message with 14-bits per byte
105 | byte STEPPER_DATA = 0x72; // control a stepper motor
106 | byte ONEWIRE_DATA = 0x73; // send an OneWire read/write/reset/select/skip/search request
107 | byte SHIFT_DATA = 0x75; // a bitstream to/from a shift register
108 | byte I2C_REQUEST = 0x76; // send an I2C read/write request
109 | byte I2C_REPLY = 0x77; // a reply to an I2C read request
110 | byte I2C_CONFIG = 0x78; // config I2C settings such as delay times and power pins
111 | byte EXTENDED_ANALOG = 0x6F; // analog write (PWM, Servo, etc) to any pin
112 | byte PIN_STATE_QUERY = 0x6D; // ask for a pin's current mode and value
113 | byte PIN_STATE_RESPONSE = 0x6E; // reply with pin's current mode and value
114 | byte CAPABILITY_QUERY = 0x6B; // ask for supported modes and resolution of all pins
115 | byte CAPABILITY_RESPONSE = 0x6C; // reply with supported modes and resolution
116 | byte ANALOG_MAPPING_QUERY = 0x69; // ask for mapping of analog to pin numbers
117 | byte ANALOG_MAPPING_RESPONSE = 0x6A; // reply with mapping info
118 | byte REPORT_FIRMWARE = 0x79; // report name and version of the firmware
119 | byte SAMPLING_INTERVAL = 0x7A; // set the poll rate of the main loop
120 | byte SCHEDULER_DATA = 0x7B; // send a createtask/deletetask/addtotask/schedule/querytasks/querytask request to the scheduler
121 | byte SYSEX_NON_REALTIME = 0x7E; // MIDI Reserved for non-realtime messages
122 | byte SYSEX_REALTIME = 0x7F; // MIDI Reserved for realtime messages
123 |
124 | // pin modes
125 | byte PIN_MODE_INPUT = 0x00; // defined in wiring.h
126 | byte PIN_MODE_OUTPUT = 0x01; // defined in wiring.h
127 | byte PIN_MODE_ANALOG = 0x02; // analog pin in analogInput mode
128 | byte PIN_MODE_PWM = 0x03; // digital pin in PWM output mode
129 | byte PIN_MODE_SERVO = 0x04; // digital pin in Servo output mode
130 | byte PIN_MODE_SHIFT = 0x05; // shiftIn/shiftOut mode
131 | byte PIN_MODE_I2C = 0x06; // pin included in I2C setup
132 | byte PIN_MODE_ONEWIRE = 0x07; // pin configured for 1-wire
133 | byte PIN_MODE_STEPPER = 0x08; // pin configured for stepper motor
134 | byte PIN_MODE_ENCODER = 0x09; // pin configured for rotary encoders
135 | byte PIN_MODE_SERIAL = 0x0A; // pin configured for serial communication
136 | byte PIN_MODE_PULLUP = 0x0B; // enable internal pull-up resistor for pin
137 | byte PIN_MODE_IGNORE = 0x7F; // pin configured to be ignored by digitalWrite and capabilityResponse
138 | byte TOTAL_PIN_MODES = 13;
139 |
140 | byte I2C_WRITE = 0X00;
141 | byte I2C_READ = 0X08;
142 | byte I2C_READ_CONTINUOUS = 0X10;
143 | byte I2C_STOP_READ_CONTINUOUS = 0X18;
144 |
145 | int MIN_SAMPLING_INTERVAL = 10;
146 | int MAX_SAMPLING_INTERVAL = 100;
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingAnalogMappingState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 | import java.util.Map;
31 | import java.util.concurrent.ConcurrentHashMap;
32 |
33 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
34 | import static org.firmata4j.firmata.parser.FirmataToken.*;
35 |
36 | /**
37 | * This state parses analog mapping message and fires an event that contains
38 | * information about how the analog ports match to the pin indexes.
39 | * After receiving the last byte, the state transfers FSM to
40 | * {@link WaitingForMessageState}.
41 | *
42 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
43 | */
44 | public class ParsingAnalogMappingState extends AbstractState {
45 |
46 | private int portId;
47 | private final Map mapping = new ConcurrentHashMap<>();
48 |
49 | public ParsingAnalogMappingState(FiniteStateMachine fsm) {
50 | super(fsm);
51 | }
52 |
53 | @Override
54 | public void process(byte b) {
55 | if (b == END_SYSEX) {
56 | Event evt = new Event(ANALOG_MAPPING_MESSAGE);
57 | evt.setBodyItem(ANALOG_MAPPING, mapping);
58 | publish(evt);
59 | transitTo(WaitingForMessageState.class);
60 | } else if (b != 127) { // if pin does support analog, corresponding analog id is in the byte
61 | mapping.put((int) b, portId);
62 | }
63 | portId++;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingAnalogMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 |
33 | /**
34 | * This state parses the analog message that points an analog input has
35 | * changed.
36 | * The analog message consists of three bytes: first contains four bits of a
37 | * command and four bits with port id; second and third contains current value
38 | * of port.
39 | * After receiving the last byte, the state fires an event to FSM and transfers
40 | * FSM to {@link WaitingForMessageState}.
41 | *
42 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
43 | */
44 | public class ParsingAnalogMessageState extends AbstractState {
45 |
46 | private int portId, counter, value;
47 |
48 | public ParsingAnalogMessageState(FiniteStateMachine fsm, int portId) {
49 | super(fsm);
50 | this.portId = portId;
51 | }
52 |
53 | @Override
54 | public void process(byte b) {
55 | switch (counter) {
56 | case 0:
57 | value = b;
58 | counter++;
59 | break;
60 | case 1:
61 | value |= b << 7;
62 | Event evt = new Event(ANALOG_MESSAGE_RESPONSE);
63 | evt.setBodyItem(PIN_ID, portId);
64 | evt.setBodyItem(PIN_VALUE, value);
65 | publish(evt);
66 | transitTo(WaitingForMessageState.class);
67 | break;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingCapabilityResponseState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 | import static org.firmata4j.firmata.parser.FirmataToken.*;
33 |
34 | /**
35 | * This state parses capability response and fires an event that contains
36 | * information about pins and their supported modes.
37 | * After receiving the last byte, the state transfers FSM to
38 | * {@link WaitingForMessageState}.
39 | *
40 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
41 | */
42 | public class ParsingCapabilityResponseState extends AbstractState {
43 |
44 | private byte pinId;
45 |
46 | public ParsingCapabilityResponseState(FiniteStateMachine fsm) {
47 | super(fsm);
48 | }
49 |
50 | @Override
51 | public void process(byte b) {
52 | if (b == END_SYSEX) {
53 | publish(new Event(PIN_CAPABILITIES_FINISHED));
54 | transitTo(WaitingForMessageState.class);
55 | } else if (b == 127) {
56 | byte[] buffer = getBuffer();
57 | byte[] supportedModes = new byte[buffer.length / 2];
58 | for (int i = 0; i < buffer.length; i += 2) {
59 | //every second byte contains mode's resolution of pin
60 | supportedModes[i / 2] = buffer[i];
61 | }
62 | Event evt = new Event(PIN_CAPABILITIES_MESSAGE);
63 | evt.setBodyItem(PIN_ID, pinId);
64 | evt.setBodyItem(PIN_SUPPORTED_MODES, supportedModes);
65 | publish(evt);
66 | ParsingCapabilityResponseState nextState = new ParsingCapabilityResponseState(getFiniteStateMashine());
67 | nextState.setPinId(++pinId);
68 | transitTo(nextState);
69 | } else {
70 | bufferize(b);
71 | }
72 | }
73 |
74 | public void setPinId(byte pinId) {
75 | this.pinId = pinId;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingCustomSysexMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata.parser;
25 |
26 | import org.firmata4j.fsm.AbstractState;
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.FiniteStateMachine;
29 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
30 | import static org.firmata4j.firmata.parser.FirmataToken.*;
31 |
32 | /**
33 | * This state collects raw binary data until it receives {@code SYSEX_END}
34 | * message and publishes it as a {@code SYSEX_CUSTOM_MESSAGE} event.
35 | *
36 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
37 | */
38 | public class ParsingCustomSysexMessageState extends AbstractState {
39 |
40 | public ParsingCustomSysexMessageState(FiniteStateMachine fsm) {
41 | super(fsm);
42 | }
43 |
44 | @Override
45 | public void process(byte b) {
46 | if (b == END_SYSEX) {
47 | Event event = new Event(SYSEX_CUSTOM_MESSAGE);
48 | event.setBodyItem(SYSEX_CUSTOM_MESSAGE, getBuffer());
49 | transitTo(WaitingForMessageState.class);
50 | publish(event);
51 | } else {
52 | bufferize(b);
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingDigitalMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 |
33 | /**
34 | * This state parses digital message and fires an event that contains
35 | * information about the state of each pin of the port where state of at least
36 | * one pin has changed.
37 | * After receiving the last byte, the state transfers FSM to
38 | * {@link WaitingForMessageState}.
39 | * When digital input of particular pin has been changed, Firmata transmits
40 | * state of whole port the pin is contained in.
41 | * A port is a set of 8 pins. State of every pin is represented inside one byte
42 | * by bit (0 - low, 1 - high).
43 | *
44 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
45 | */
46 | public class ParsingDigitalMessageState extends AbstractState {
47 |
48 | private int portId, counter, value;
49 |
50 | public ParsingDigitalMessageState(FiniteStateMachine fsm, int portId) {
51 | super(fsm);
52 | this.portId = portId;
53 | }
54 |
55 | @Override
56 | public void process(byte b) {
57 | switch (counter) {
58 | case 0:
59 | value = b;
60 | counter++;
61 | break;
62 | case 1:
63 | value |= b << 7;
64 | int pinId = portId * 8;
65 | for (int i = 0; i < 8; i++) {
66 | Event evt = new Event(DIGITAL_MESSAGE_RESPONSE);
67 | evt.setBodyItem(PIN_ID, pinId + i);
68 | evt.setBodyItem(PIN_VALUE, (value >>> i) & 0x01);
69 | publish(evt);
70 | }
71 | transitTo(WaitingForMessageState.class);
72 | break;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingExtendedAnalogMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 | import static org.firmata4j.firmata.parser.FirmataToken.*;
33 |
34 | /**
35 | * This state parses extended analog state message and fires an event that
36 | * contains information about the state of an analog input where the state has
37 | * changed.
38 | * This kind of message is received when pin id of the analog input is greater
39 | * than 15 and/or value on the input is greater than 16383.
40 | * After receiving the last byte of the message, the state transfers FSM to
41 | * {@link WaitingForMessageState}.
42 | *
43 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
44 | */
45 | public class ParsingExtendedAnalogMessageState extends AbstractState {
46 |
47 | public ParsingExtendedAnalogMessageState(FiniteStateMachine fsm) {
48 | super(fsm);
49 | }
50 |
51 | @Override
52 | public void process(byte b) {
53 | if (b == END_SYSEX) {
54 | byte[] buffer = getBuffer();
55 | int pinId = buffer[0];
56 | long value = buffer[1];
57 | for (int i = 2; i < buffer.length; i++) {
58 | value |= buffer[i] << 7 * (i - 1);
59 | }
60 | Event evt = new Event(ANALOG_MESSAGE_RESPONSE);
61 | evt.setBodyItem(PIN_ID, pinId);
62 | evt.setBodyItem(PIN_VALUE, value);
63 | publish(evt);
64 | transitTo(WaitingForMessageState.class);
65 | } else {
66 | bufferize(b);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingFirmwareMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 | import static org.firmata4j.firmata.parser.FirmataToken.*;
33 |
34 | /**
35 | * This state parses firmware message and fires an event that contains the name
36 | * and version of firmware.
37 | * After receiving the last byte of the message, the state transfers FSM to
38 | * {@link WaitingForMessageState}.
39 | *
40 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
41 | */
42 | public class ParsingFirmwareMessageState extends AbstractState {
43 |
44 | public ParsingFirmwareMessageState(FiniteStateMachine fsm) {
45 | super(fsm);
46 | }
47 |
48 | @Override
49 | public void process(byte b) {
50 | if (b == END_SYSEX) {
51 | byte[] buffer = getBuffer();
52 | int major = buffer[0];
53 | int minor = buffer[1];
54 | String name = decode(buffer, 2, buffer.length - 2);
55 | Event event = new Event(FIRMWARE_MESSAGE);
56 | event.setBodyItem(FIRMWARE_MAJOR, major);
57 | event.setBodyItem(FIRMWARE_MINOR, minor);
58 | event.setBodyItem(FIRMWARE_NAME, name);
59 | transitTo(WaitingForMessageState.class);
60 | publish(event);
61 | } else {
62 | bufferize(b);
63 | }
64 | }
65 |
66 | private String decode(byte[] buffer, int offset, int length) {
67 | StringBuilder result = new StringBuilder();
68 | length = length >>> 1; // divide by two
69 | for (int i = 0; i < length; i++) {
70 | result.append((char) (buffer[offset++] + (buffer[offset++] << 7)));
71 | }
72 | return result.toString();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingI2CMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.firmata.parser;
25 |
26 | import org.firmata4j.fsm.AbstractState;
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.FiniteStateMachine;
29 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
30 | import static org.firmata4j.firmata.parser.FirmataToken.*;
31 |
32 | /**
33 | * This class parses inbound I2C messages and publishes them when they are
34 | * complete.
35 | *
36 | * @author William Reichardt
37 | */
38 | public class ParsingI2CMessageState extends AbstractState {
39 |
40 | public ParsingI2CMessageState(FiniteStateMachine fsm) {
41 | super(fsm);
42 | }
43 |
44 | /*
45 | * /* I2C reply
46 | * -------------------------------
47 | * 0 START_SYSEX (0xF0) (MIDI System Exclusive)
48 | * 1 I2C_REPLY (0x77)
49 | * 2 slave address (LSB)
50 | * ... Lotsa Bytes
51 | * n END_SYSEX (0xF7)
52 | */
53 | /**
54 | * Collects I2C message from individual bytes.
55 | *
56 | * @param b the input byte
57 | */
58 | @Override
59 | public void process(byte b) {
60 | if (b == END_SYSEX) {
61 | byte[] buffer = convertI2CBuffer(getBuffer());
62 | byte address = buffer[0];
63 | byte register = buffer[1];
64 | byte[] message = new byte[buffer.length - 2];
65 | System.arraycopy(buffer, 2, message, 0, buffer.length - 2);
66 | Event event = new Event(I2C_MESSAGE);
67 | event.setBodyItem(I2C_ADDRESS, address);
68 | event.setBodyItem(I2C_REGISTER, register);
69 | event.setBodyItem(I2C_MESSAGE, message);
70 | transitTo(WaitingForMessageState.class);
71 | publish(event);
72 | } else {
73 | bufferize(b);
74 | }
75 | }
76 |
77 | private static byte[] convertI2CBuffer(byte[] byteBuffer) {
78 | int outSize = new Double(Math.floor(byteBuffer.length / 2)).intValue();
79 | byte[] outBuffer = new byte[outSize];
80 | int outIndex = 0;
81 | for (int index = 0; index < byteBuffer.length; index = index + 2) {
82 | outBuffer[outIndex] = (byte) (((byteBuffer[index + 1] & 0x01) << 7) | (byteBuffer[index] & 0x7F));
83 | outIndex++;
84 | }
85 | return outBuffer;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingStringMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.AbstractState;
28 | import org.firmata4j.fsm.Event;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
31 | import static org.firmata4j.firmata.parser.FirmataToken.*;
32 |
33 | /**
34 | *
35 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
36 | */
37 | public class ParsingStringMessageState extends AbstractState {
38 |
39 | public ParsingStringMessageState(FiniteStateMachine fsm) {
40 | super(fsm);
41 | }
42 |
43 | @Override
44 | public void process(byte b) {
45 | if (b == END_SYSEX) {
46 | byte[] buffer = getBuffer();
47 | String value = decode(buffer, 0, buffer.length);
48 | Event event = new Event(STRING_MESSAGE);
49 | event.setBodyItem(STRING_MESSAGE, value);
50 | transitTo(WaitingForMessageState.class);
51 | publish(event);
52 | } else {
53 | bufferize(b);
54 | }
55 | }
56 |
57 | private String decode(byte[] buffer, int offset, int length) {
58 | StringBuilder result = new StringBuilder();
59 | length = length >>> 1; // divide by two
60 | for (int i = 0; i < length; i++) {
61 | result.append((char) (buffer[offset++] + (buffer[offset++] << 7)));
62 | }
63 | return result.toString();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingSysexMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.AbstractState;
28 | import org.firmata4j.fsm.FiniteStateMachine;
29 | import org.firmata4j.fsm.State;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import java.util.HashMap;
34 | import java.util.Map;
35 |
36 | import static org.firmata4j.firmata.parser.FirmataToken.*;
37 |
38 | /**
39 | * This state parses type of sysex message and transfers FSM to the state which
40 | * is able to handle the message.
41 | * If the state receives unknown type of sysex message, it transfers FSM to
42 | * {@link WaitingForMessageState}.
43 | *
44 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
45 | */
46 | public class ParsingSysexMessageState extends AbstractState {
47 |
48 | private static final Logger LOGGER = LoggerFactory.getLogger(ParsingSysexMessageState.class);
49 | private static final Map> STATES;
50 |
51 | static {
52 | STATES = new HashMap<>();
53 | STATES.put(REPORT_FIRMWARE, ParsingFirmwareMessageState.class);
54 | STATES.put(EXTENDED_ANALOG, ParsingExtendedAnalogMessageState.class);
55 | STATES.put(CAPABILITY_RESPONSE, ParsingCapabilityResponseState.class);
56 | STATES.put(ANALOG_MAPPING_RESPONSE, ParsingAnalogMappingState.class);
57 | STATES.put(PIN_STATE_RESPONSE, PinStateParsingState.class);
58 | STATES.put(STRING_DATA, ParsingStringMessageState.class);
59 | STATES.put(I2C_REPLY,ParsingI2CMessageState.class);
60 | }
61 |
62 | public ParsingSysexMessageState(FiniteStateMachine fsm) {
63 | super(fsm);
64 | }
65 |
66 | @Override
67 | public void process(byte b) {
68 | Class extends State> nextState = STATES.get(b);
69 | if (nextState == null) {
70 | State newState = new ParsingCustomSysexMessageState(getFiniteStateMashine());
71 | newState.process(b);
72 | transitTo(newState);
73 | } else {
74 | transitTo(nextState);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/ParsingVersionMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 |
33 | /**
34 | * This state parses version report message that contains the version of the
35 | * protocol the hardware supports. The version is handed with the event and the
36 | * FSM is transfered to {@link WaitingForMessageState}.
37 | *
38 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
39 | */
40 | public class ParsingVersionMessageState extends AbstractState {
41 |
42 | private int counter, major;
43 |
44 | public ParsingVersionMessageState(FiniteStateMachine fsm) {
45 | super(fsm);
46 | }
47 |
48 | @Override
49 | public void process(byte b) {
50 | if (counter == 0) {
51 | major = b;
52 | counter++;
53 | } else {
54 | int minor = b;
55 | Event event = new Event(PROTOCOL_MESSAGE);
56 | event.setBodyItem(PROTOCOL_MAJOR, major);
57 | event.setBodyItem(PROTOCOL_MINOR, minor);
58 | transitTo(WaitingForMessageState.class);
59 | publish(event);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/PinStateParsingState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.fsm.Event;
28 | import org.firmata4j.fsm.AbstractState;
29 | import org.firmata4j.fsm.FiniteStateMachine;
30 |
31 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
32 | import static org.firmata4j.firmata.parser.FirmataToken.*;
33 |
34 | /**
35 | * This state parses the pin state message.
36 | * After receiving the last byte of the message, the state transfers FSM to
37 | * {@link WaitingForMessageState}.
38 | *
39 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
40 | */
41 | public class PinStateParsingState extends AbstractState {
42 |
43 | public PinStateParsingState(FiniteStateMachine fsm) {
44 | super(fsm);
45 | }
46 |
47 | @Override
48 | public void process(byte b) {
49 | if (b == END_SYSEX) {
50 | byte[] buffer = getBuffer();
51 | Event evt = new Event(PIN_STATE);
52 | evt.setBodyItem(PIN_ID, buffer[0]);
53 | evt.setBodyItem(PIN_MODE, buffer[1]);
54 | long value = 0;
55 | for (int i = 2; i < buffer.length; i++) {
56 | value |= (buffer[i] << ((i - 2) * 7));
57 | }
58 | evt.setBodyItem(PIN_VALUE, value);
59 | publish(evt);
60 | transitTo(WaitingForMessageState.class);
61 | } else {
62 | bufferize(b);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/firmata/parser/WaitingForMessageState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.firmata.parser;
26 |
27 | import org.firmata4j.firmata.FirmataDevice;
28 | import org.firmata4j.fsm.Event;
29 | import org.firmata4j.fsm.AbstractState;
30 | import org.firmata4j.fsm.FiniteStateMachine;
31 |
32 | import static org.firmata4j.firmata.parser.FirmataEventType.*;
33 | import static org.firmata4j.firmata.parser.FirmataToken.*;
34 |
35 | /**
36 | * This is initial default state of {@link FirmataDevice}.
37 | * The state is waiting for command and determines to which state transfers to
38 | * parse further data. It extracts additional data from a command byte, when the
39 | * command contains that, and hands it to the next state.
40 | * The state skips unknown command bytes throwing events with error messages.
41 | *
42 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
43 | */
44 | public class WaitingForMessageState extends AbstractState {
45 |
46 | public WaitingForMessageState(FiniteStateMachine fsm) {
47 | super(fsm);
48 | }
49 |
50 | @Override
51 | public void process(byte b) {
52 | // first byte may contain not only command but additional information as well
53 | byte command = b < (byte) 0xF0 ? (byte) (b & 0xF0) : b;
54 | FiniteStateMachine fsm = getFiniteStateMashine();
55 | switch (command) {
56 | case DIGITAL_MESSAGE:
57 | transitTo(new ParsingDigitalMessageState(fsm, b & 0x0F));
58 | break;
59 | case ANALOG_MESSAGE:
60 | transitTo(new ParsingAnalogMessageState(fsm, b & 0x0F));
61 | break;
62 | case REPORT_VERSION:
63 | transitTo(ParsingVersionMessageState.class);
64 | break;
65 | case START_SYSEX:
66 | transitTo(ParsingSysexMessageState.class);
67 | break;
68 | case SYSTEM_RESET:
69 | publish(new Event(SYSTEM_RESET_MESSAGE));
70 | break;
71 | default:
72 | //skip non control token
73 | Event evt = new Event(ERROR_MESSAGE);
74 | evt.setBodyItem(ERROR_DESCRIPTION, String.format("Unknown control token has been received. Skipping. 0x%2X", b));
75 | publish(evt);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/fsm/AbstractState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.fsm;
26 |
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | /**
31 | * This state stores FSM it belongs to. It facilitates transitions between
32 | * states and buffering of the input data.
33 | *
34 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
35 | */
36 | public abstract class AbstractState implements State {
37 |
38 | private final FiniteStateMachine fsm;
39 | private byte[] buffer = new byte[128];
40 | private int index;
41 | private static final Logger LOGGER = LoggerFactory.getLogger(AbstractState.class);
42 |
43 | public AbstractState(FiniteStateMachine fsm) {
44 | this.fsm = fsm;
45 | }
46 |
47 | @Override
48 | public FiniteStateMachine getFiniteStateMashine() {
49 | return fsm;
50 | }
51 |
52 | /**
53 | * Transfers the FSM to the state of specified class. This method takes only
54 | * classes that provide a constructor taking a {@link FiniteStateMachine}
55 | * instance as a parameter. The {@link IllegalArgumentException} is thrown
56 | * otherwise.
57 | *
58 | * @param stateClass the state class
59 | */
60 | protected void transitTo(Class extends State> stateClass) {
61 | fsm.transitTo(stateClass);
62 | }
63 |
64 | /**
65 | * Transfers the FSM to the specified state.
66 | *
67 | * @param state the state
68 | */
69 | protected void transitTo(State state) {
70 | fsm.transitTo(state);
71 | }
72 |
73 | /**
74 | * Publishes the event to the FSM.
75 | *
76 | * @param event the event
77 | */
78 | protected void publish(Event event) {
79 | fsm.handle(event);
80 | }
81 |
82 | /**
83 | * Stores the byte to the internal buffer in order to get a chunk of data
84 | * latter.
85 | *
86 | * @param b the byte
87 | */
88 | protected void bufferize(byte b) {
89 | if (index == buffer.length) {
90 | byte[] newBuffer = new byte[buffer.length * 2];
91 | System.arraycopy(buffer, 0, newBuffer, 0, index);
92 | buffer = newBuffer;
93 | }
94 | buffer[index++] = b;
95 | }
96 |
97 | /**
98 | * Returns the data collected so far.
99 | *
100 | * @return the buffered bytes
101 | */
102 | protected byte[] getBuffer() {
103 | byte[] result = new byte[index];
104 | System.arraycopy(buffer, 0, result, 0, index);
105 | return result;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/fsm/DirectExecutor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2018 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.fsm;
26 |
27 | import java.util.concurrent.Executor;
28 |
29 | /**
30 | * Executes tasks in the current thread.
31 | *
32 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
33 | */
34 | public class DirectExecutor implements Executor {
35 |
36 | public static final DirectExecutor INSTANCE = new DirectExecutor();
37 |
38 | @Override
39 | public void execute(Runnable command) {
40 | command.run();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/fsm/Event.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.fsm;
26 |
27 | import java.util.HashMap;
28 | import java.util.Map;
29 |
30 | /**
31 | * The event of very loose structure. It provides possibility to build an event
32 | * of structure that meets the needs of a particular FSM application.
33 | *
34 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
35 | */
36 | public class Event {
37 |
38 | private final long timestamp;
39 | private final String type;
40 | private final Map body;
41 |
42 | /**
43 | * Constructs the event of unspecified type.
44 | */
45 | public Event() {
46 | timestamp = System.currentTimeMillis();
47 | type = "unspecified";
48 | body = new HashMap<>();
49 | }
50 |
51 | /**
52 | * Constructs the event of specified type.
53 | *
54 | * @param type the type of the event
55 | */
56 | public Event(String type) {
57 | timestamp = System.currentTimeMillis();
58 | this.type = type;
59 | body = new HashMap<>();
60 | }
61 |
62 | /**
63 | * Constructs the event of specified type with specified type. This
64 | * constructor allows to set the body of event at once.
65 | *
66 | * @param type the type of the event
67 | * @param body the event's body
68 | */
69 | public Event(String type, Map body) {
70 | timestamp = System.currentTimeMillis();
71 | this.type = type;
72 | this.body = new HashMap<>(body);
73 | }
74 |
75 | /**
76 | * Returns the type of the event.
77 | */
78 | public String getType() {
79 | return type;
80 | }
81 |
82 | /**
83 | * Returns the timestamp of the event.
84 | */
85 | public long getTimestamp() {
86 | return timestamp;
87 | }
88 |
89 | /**
90 | * Returns the body of the event.
91 | */
92 | public Map getBody() {
93 | return new HashMap<>(body);
94 | }
95 |
96 | /**
97 | * Returns the item of the event's body.
98 | * @param key the key of event item
99 | * @return the event item
100 | */
101 | public Object getBodyItem(String key) {
102 | return body.get(key);
103 | }
104 |
105 | /**
106 | * Sets the item of the event's body.
107 | * @param key the key of event item
108 | * @param value the event item
109 | */
110 | public void setBodyItem(String key, Object value) {
111 | body.put(key, value);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/fsm/FiniteStateMachine.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.fsm;
26 |
27 | import java.util.Map;
28 | import java.util.concurrent.ConcurrentHashMap;
29 | import java.util.concurrent.Executor;
30 | import org.firmata4j.Consumer;
31 | import org.slf4j.Logger;
32 | import org.slf4j.LoggerFactory;
33 |
34 | /**
35 | * Finite State Machine base implementation.
36 | * The finite state machine is not thread-safe by its nature. This
37 | * implementation does not cope with simultaneously received bytes. The bytes
38 | * have to be fed to the FSM one by one in a single thread that should define
39 | * the order the bytes go in.
40 | *
41 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
42 | */
43 | public class FiniteStateMachine {
44 |
45 | public static final String FSM_IS_IN_TERMINAL_STATE = "fsm is in terminal state";
46 | private static final Logger LOGGER = LoggerFactory.getLogger(FiniteStateMachine.class);
47 | private final Map> handlers;
48 | private Executor eventHandlingExecutor = DirectExecutor.INSTANCE;
49 | private State currentState;
50 |
51 | /**
52 | * Constructs the finite state machine in the terminal state, i.e. without
53 | * any particular current state. So that it will do noting on
54 | * {@link #process(byte)} method invocation until a state will be set using
55 | * {@link #transitTo(org.firmata4j.fsm.State)}.
56 | */
57 | public FiniteStateMachine() {
58 | handlers = new ConcurrentHashMap<>();
59 | handlers.put("*", new Consumer() {
60 | @Override
61 | public void accept(Event t) {
62 | // default wildcard handler does nothing
63 | }
64 | });
65 | }
66 |
67 | /**
68 | * Constructs the finite state machine in the state of the specified class.
69 | *
70 | * @param stateClass the class of initial state of the FSM
71 | * @throws IllegalArgumentException when creating of the state instance is
72 | * impossible (likely because of lack of the constructor taking the FSM as a
73 | * parameter)
74 | */
75 | @SuppressWarnings("LeakingThisInConstructor")
76 | public FiniteStateMachine(Class extends State> stateClass) {
77 | this();
78 | try {
79 | // LeakingThisInConstructor is suppresed since the state instance doesn't run its own thread
80 | currentState = stateClass.getConstructor(FiniteStateMachine.class).newInstance(this);
81 | } catch (ReflectiveOperationException ex) {
82 | throw new IllegalArgumentException("Cannot instantiate the initial state", ex);
83 | }
84 | }
85 |
86 | /**
87 | * Assigns an executor responsible for performing event hanling.
88 | *
89 | * @param executor the executor that will perform event handling
90 | */
91 | public void setEventHandlingExecutor(Executor executor) {
92 | this.eventHandlingExecutor = executor;
93 | }
94 |
95 | /**
96 | * Transfers the FSM to the new state.
97 | *
98 | * @param state the new state
99 | */
100 | public void transitTo(State state) {
101 | currentState = state;
102 | }
103 |
104 | /**
105 | * Transfers the FSM to the new state of the specified class.
106 | * This method takes only classes that provide a constructor taking a
107 | * {@link FiniteStateMachine} instance as a parameter. The
108 | * {@link IllegalArgumentException} is thrown otherwise.
109 | *
110 | * @param stateClass the state class
111 | * @throws IllegalArgumentException when the state class does not provide a
112 | * constructor taking {@link FiniteStateMachine} instance as a single
113 | * parameter.
114 | */
115 | public void transitTo(Class extends State> stateClass) {
116 | try {
117 | State nextState = stateClass.getConstructor(FiniteStateMachine.class).newInstance(this);
118 | transitTo(nextState);
119 | } catch (ReflectiveOperationException ex) {
120 | throw new IllegalArgumentException("Cannot instantiate the new state", ex);
121 | }
122 | }
123 |
124 | /**
125 | * Returns current state of the FSM.
126 | *
127 | * @return the current state
128 | */
129 | public State getCurrentState() {
130 | return currentState;
131 | }
132 |
133 | /**
134 | * Hands the byte to be processed with the current state.
135 | *
136 | * @param b input byte
137 | */
138 | public void process(byte b) {
139 | if (currentState == null) {
140 | LOGGER.debug("{} is in terminal state", this);
141 | Event evt = new Event(FSM_IS_IN_TERMINAL_STATE);
142 | evt.setBodyItem("fsm", this);
143 | handle(evt);
144 | } else {
145 | LOGGER.trace("processing of byte {} with {}", b, currentState);
146 | currentState.process(b);
147 | }
148 | }
149 |
150 | /**
151 | * Hands bytes from the buffer to be processed by the current state
152 | * sequentially.
153 | *
154 | * @param buffer the bytes to be processed
155 | */
156 | public void process(byte[] buffer) {
157 | process(buffer, 0, buffer.length);
158 | }
159 |
160 | /**
161 | * Hands bytes from the buffer to be processed by the current state
162 | * sequentially.
163 | *
164 | * @param buffer the bytes to be processed
165 | * @param offset the index of the first byte to process
166 | * @param length the number of the bytes to be processed
167 | */
168 | public void process(byte[] buffer, int offset, int length) {
169 | int finalIndex = offset + length;
170 | for (int i = offset; i < finalIndex; i++) {
171 | process(buffer[i]);
172 | }
173 | }
174 |
175 | /**
176 | * Adds a handler for specified event type.
177 | *
178 | * If there already is aanother handler for that event type, this handler
179 | * gets added to the end of handler chain.
180 | *
181 | * @param eventType type of event the handler is supposed to deal with
182 | * @param handler an object that gets an event to process
183 | */
184 | public synchronized void addHandler(String eventType, Consumer handler) {
185 | if (handlers.containsKey(eventType)) {
186 | handlers.put(eventType, handlers.get(eventType).andThen(handler));
187 | } else {
188 | handlers.put(eventType, handler);
189 | }
190 | }
191 |
192 | /**
193 | * Handles an event that occurs during processing of the input.
194 | * The method is invoked by the current state of FSM when an event occurs.
195 | *
196 | * @param event the event
197 | */
198 | public void handle(final Event event) {
199 | final Consumer handler = handlers.get(event.getType());
200 | if (handler == null) {
201 | LOGGER.warn(
202 | "No specific event handler is registered for {}:{}.",
203 | event.getType(),
204 | event.getBody()
205 | );
206 | } else {
207 | eventHandlingExecutor.execute(new Runnable() {
208 | @Override
209 | public void run() {
210 | handler.accept(event);
211 | }
212 | });
213 | }
214 | eventHandlingExecutor.execute(new Runnable() {
215 | @Override
216 | public void run() {
217 | handlers.get("*").accept(event);
218 | }
219 | });
220 | }
221 |
222 | }
223 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/fsm/State.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.fsm;
26 |
27 | /**
28 | * The interface of the state of the {@link FiniteStateMachine}.
29 | *
30 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
31 | */
32 | public interface State {
33 |
34 | /**
35 | * Performs processing of the input byte.
36 | * In particular circumstances the state may fire an event and/or transit
37 | * the finite state machine to the new state.
38 | *
39 | * @param b the input byte
40 | */
41 | void process(byte b);
42 |
43 | /**
44 | * Returns the {@link FiniteStateMachine} instance the state belongs to.
45 | *
46 | * @return the finite state machine
47 | */
48 | FiniteStateMachine getFiniteStateMashine();
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/ssd1306/SSD1306.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.ssd1306;
26 |
27 | import java.io.IOException;
28 | import java.util.Arrays;
29 | import org.firmata4j.I2CDevice;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 | import static org.firmata4j.ssd1306.SSD1306Token.DATA_CONTROL_BYTE;
33 | import static org.firmata4j.ssd1306.SSD1306MessageFactory.*;
34 |
35 | /**
36 | * This class provides facilities to control a monochrome OLED display on
37 | * SSD1306 driver.
38 | *
39 | * Example:
40 | *
41 | * IODevice device = new FirmataDevice(port); // connecting to firmata device
42 | * device.ensureInitializationIsDone(); // waiting for the device is ready
43 | * I2CDevice i2cDevice = device.getI2CDevice((byte) 0x3C); // or 0x3D - standard address for SSD1306
44 | * SSD1306 display = new SSD1306(i2cDevice, SSD1306.Size.SSD1306_128_64); // constructing a display on top of i2c device reference
45 | * display.init(); // initializing display
46 | * display.getCanvas().drawString(3, 3, "firmata4j"); // drawing a string on display's canvas
47 | * // drawing anything else
48 | * display.display(); // displaying current state of display's canvas
49 | * // doing anything else
50 | * display.turnOff(); // switching off the display
51 | * device.stop(); // stopping IODevice
52 | *
53 | *
54 | *
55 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
56 | *
57 | * see https://www.adafruit.com/category/63_98
58 | */
59 | public class SSD1306 {
60 |
61 | private final I2CDevice device;
62 |
63 | private final Size size;
64 |
65 | private final MonochromeCanvas canvas;
66 |
67 | private final VCC vccState;
68 |
69 | private static final Logger LOGGER = LoggerFactory.getLogger(SSD1306.class);
70 |
71 | public SSD1306(I2CDevice device, Size size) {
72 | this(device, size, VCC.INTERNAL);
73 | }
74 |
75 | public SSD1306(I2CDevice device, Size size, VCC vcc) {
76 | this.device = device;
77 | this.size = size;
78 | canvas = new MonochromeCanvas(size.width, size.height);
79 | vccState = vcc;
80 | }
81 |
82 | /**
83 | * Prepares the device to operation.
84 | */
85 | public void init() {
86 | turnOff();
87 | // initialization
88 | command(setDisplayClock((byte) 0, (byte) 8));
89 | command(setMultiplexRatio(size.height));
90 | command(setDisplayOffset(0)); // no offset
91 | command(setDisplayStartLine(0)); // line #0
92 | command(setMemoryAddressingMode(MemoryAddressingMode.HORIZONTAL));
93 | command(setHorizontalFlip(false));
94 | command(setVerticalFlip(false));
95 | if (vccState == VCC.EXTERNAL) {
96 | command(setChargePump(false));
97 | } else {
98 | command(setChargePump(true));
99 | }
100 | int contrast = 0;
101 | boolean sequentialPins = true;
102 | boolean leftRightPinsRemap = false;
103 | if (size == Size.SSD1306_128_32) {
104 | contrast = 0x8F;
105 | } else if (size == Size.SSD1306_128_64) {
106 | if (vccState == VCC.EXTERNAL) {
107 | contrast = 0x9F;
108 | } else {
109 | contrast = 0xCF;
110 | }
111 | sequentialPins = false;
112 | } else if (size == Size.SSD1306_96_16) {
113 | if (vccState == VCC.EXTERNAL) {
114 | contrast = 0x10;
115 | } else {
116 | contrast = 0xAF;
117 | }
118 | }
119 | command(setCOMPinsConfig(sequentialPins, leftRightPinsRemap));
120 | command(setContrast((byte) contrast));
121 | if (vccState == VCC.EXTERNAL) {
122 | command(setPrechargePeriod((byte) 2, (byte) 2));
123 | } else {
124 | command(setPrechargePeriod((byte) 1, (byte) 15));
125 | }
126 | command(setVcomhDeselectLevel((byte) 3));
127 | command(DISPLAY_RESUME);
128 | command(setDisplayInverse(false));
129 | stopScrolling();
130 | display(); // synchronize canvas with display's internal buffer
131 | turnOn();
132 | }
133 |
134 | public void turnOn() {
135 | command(TURN_ON);
136 | }
137 |
138 | public void turnOff() {
139 | command(TURN_OFF);
140 | }
141 |
142 | public void scrollRight() {
143 | command(setHorizontalScroll(ScrollDirection.RIGHT, (byte) 0, (byte) 7, (byte) 0));
144 | command(ENABLE_SCROLLING);
145 | }
146 |
147 | public void scrollLeft() {
148 | command(setHorizontalScroll(ScrollDirection.LEFT, (byte) 0, (byte) 7, (byte) 0));
149 | command(ENABLE_SCROLLING);
150 | }
151 |
152 | public void stopScrolling() {
153 | command(DISABLE_SCROLLING);
154 | }
155 |
156 | public void invertDisplay(boolean inverted) {
157 | command(setDisplayInverse(inverted));
158 | }
159 |
160 | public void clear() {
161 | getCanvas().clear();
162 | display();
163 | }
164 |
165 | /**
166 | * Dims the display
167 | *
168 | * @param dim true - display is dimmed, false - display is normal
169 | */
170 | public void dim(boolean dim) {
171 | byte contrast;
172 |
173 | if (dim) {
174 | contrast = 0; // Dimmed display
175 | } else {
176 | if (vccState == VCC.EXTERNAL) {
177 | contrast = (byte) 0x9F;
178 | } else {
179 | contrast = (byte) 0xCF;
180 | }
181 | }
182 | // the range of contrast to too small to be really useful
183 | // it is useful to dim the display
184 | command(setContrast(contrast));
185 | }
186 |
187 | public void display() {
188 | command(setColumnAddress((byte) 0, (byte) (size.width - 1)));
189 | byte pageEndAddr = 0;
190 | switch (size) {
191 | case SSD1306_128_64:
192 | pageEndAddr = 7; // Page end address
193 | break;
194 | case SSD1306_128_32:
195 | pageEndAddr = 3; // Page end address
196 | break;
197 | case SSD1306_96_16:
198 | pageEndAddr = 1; // Page end address
199 | break;
200 | }
201 | command(setPageAddress((byte) 0, pageEndAddr));
202 | //TODO increase 2C bitrate if possible
203 | byte[] buffer = canvas.getBuffer();
204 | try {
205 | for (int i = 0; i < buffer.length / 16; i++) {
206 | byte[] row = new byte[17];
207 | row[0] = DATA_CONTROL_BYTE;
208 | System.arraycopy(buffer, i * 16, row, 1, 16);
209 | device.tell(row);
210 | }
211 | } catch (IOException e) {
212 | LOGGER.error("Displaying attempt failed", e);
213 | }
214 | }
215 |
216 | public Size getSize() {
217 | return size;
218 | }
219 |
220 | public MonochromeCanvas getCanvas() {
221 | return canvas;
222 | }
223 |
224 | private void command(byte... command) {
225 | try {
226 | for (int i = 0; i < command.length; i += 2) {
227 | device.tell(Arrays.copyOfRange(command, i, i + 2));
228 | }
229 | } catch (IOException e) {
230 | throw new RuntimeException(e);
231 | }
232 | }
233 |
234 | public static enum Size {
235 |
236 | SSD1306_128_64(128, 64),
237 | SSD1306_128_32(128, 32),
238 | SSD1306_96_16(96, 16);
239 |
240 | public final int width, height;
241 |
242 | private Size(int w, int h) {
243 | width = w;
244 | height = h;
245 | }
246 |
247 | }
248 |
249 | public static enum VCC {
250 | EXTERNAL,
251 | INTERNAL
252 | }
253 |
254 | }
255 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/ssd1306/SSD1306Token.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.firmata4j.ssd1306;
26 |
27 | /**
28 | * This class contains set of constants that represent tokens of SSD1306 device
29 | * protocol (commands and control bytes).
30 | *
31 | * @author Oleg Kurbatov <o.v.kurbatov@gmail.com>
32 | *
33 | * see https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
34 | */
35 | public interface SSD1306Token {
36 |
37 | byte COMMAND_CONTROL_BYTE = 0x00; // Co = 0, D/C = 0
38 | byte DATA_CONTROL_BYTE = 0x40; // Co = 0, D/C = 1
39 |
40 | // fundamental commands
41 | byte SET_CONTRAST = (byte) 0x81;
42 | byte DISPLAY_ON_RESUME = (byte) 0xA4; // resume to RAM content display
43 | byte DISPLAY_ON_RESET = (byte) 0xA5; // ignore RAM content
44 | byte NORMAL_DISPLAY = (byte) 0xA6;
45 | byte INVERSE_DISPLAY = (byte) 0xA7;
46 | byte LED_OFF = (byte) 0xAE;
47 | byte LED_ON = (byte) 0xAF;
48 |
49 | // scrolling commands
50 | byte SET_HORIZONTAL_SCROLL = 0x26;
51 | byte VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
52 | byte VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A;
53 | byte SET_VERTICAL_SCROLL_AREA = (byte) 0xA3;
54 | byte DEACTIVATE_SCROLL = 0x2E;
55 | byte ACTIVATE_SCROLL = 0x2F;
56 |
57 | // addressing comands
58 | byte SET_MEMORY_ADDR_MODE = 0x20;
59 | byte SET_COLUMN_ADDR = 0x21;
60 | byte SET_PAGE_ADDR = 0x22;
61 | byte PAGE_ADDR_SET_LOW_COLUMN = 0x00;
62 | byte PAGE_ADDR_SET_HIGH_COLUMN = 0x10;
63 | byte RAM_PAGE_START_ADDRESS = (byte) 0xB0;
64 |
65 | // hardvare configuration commands
66 | byte SET_START_LINE = 0x40;
67 | byte SEG_REMAP = (byte) 0xA0;
68 | byte SET_MULTIPLEX = (byte) 0xA8;
69 | byte COM_SCAN_INC = (byte) 0xC0;
70 | byte COM_SCAN_DEC = (byte) 0xC8;
71 | byte SET_DISPLAY_OFFSET = (byte) 0xD3;
72 | byte SET_COM_PINS = (byte) 0xDA;
73 |
74 | // timing configuration commands
75 | byte SET_DISPLAY_CLOCK = (byte) 0xD5;
76 | byte SET_PRECHARGE_PERIOD = (byte) 0xD9;
77 | byte SET_VCOMH_DESELECT = (byte) 0xDB;
78 | byte NOP = (byte) 0xE3;
79 |
80 | byte SET_CHARGE_PUMP = (byte) 0x8D;
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/transport/JSSCTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2021 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.transport;
25 |
26 | import java.io.IOException;
27 | import jssc.SerialPort;
28 | import jssc.SerialPortEvent;
29 | import jssc.SerialPortEventListener;
30 | import jssc.SerialPortException;
31 | import org.firmata4j.Parser;
32 | import org.slf4j.Logger;
33 | import org.slf4j.LoggerFactory;
34 |
35 | /**
36 | * Transfers data via serial port using jssc library.
37 | *
38 | * @author Ali Kia
39 | */
40 | public class JSSCTransport implements TransportInterface, SerialPortEventListener {
41 | private Parser parser;
42 |
43 | private final SerialPort port;
44 |
45 | private static final Logger LOGGER = LoggerFactory.getLogger(JSSCTransport.class);
46 |
47 | public JSSCTransport(String portName) {
48 | this.port = new SerialPort(portName);
49 | }
50 |
51 | @Override
52 | public void start() throws IOException {
53 | if (!port.isOpened()) {
54 | try {
55 | port.openPort();
56 | port.setParams(
57 | SerialPort.BAUDRATE_57600,
58 | SerialPort.DATABITS_8,
59 | SerialPort.STOPBITS_1,
60 | SerialPort.PARITY_NONE);
61 | port.setEventsMask(SerialPort.MASK_RXCHAR);
62 | port.addEventListener(this);
63 | } catch (SerialPortException ex) {
64 | throw new IOException("Cannot start firmata device", ex);
65 | }
66 | }
67 | }
68 |
69 | @Override
70 | public void stop() throws IOException {
71 | try {
72 | if (port.isOpened()) {
73 | port.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
74 | port.closePort();
75 | }
76 | } catch (SerialPortException ex) {
77 | throw new IOException("Cannot properly stop firmata device", ex);
78 | }
79 | }
80 |
81 | @Override
82 | public void write(byte[] bytes) throws IOException {
83 | try {
84 | port.writeBytes(bytes);
85 | } catch (SerialPortException ex) {
86 | throw new IOException("Cannot send message to device", ex);
87 | }
88 | }
89 |
90 | @Override
91 | public void setParser(Parser parser) {
92 | this.parser = parser;
93 | }
94 |
95 | @Override
96 | public void serialEvent(SerialPortEvent event) {
97 | // queueing data from input buffer to processing by FSM logic
98 | if (event.isRXCHAR() && event.getEventValue() > 0) {
99 | try {
100 | parser.parse(port.readBytes());
101 | } catch (SerialPortException ex) {
102 | LOGGER.error("Cannot read from device", ex);
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/transport/JSerialCommTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2021 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.transport;
25 |
26 | import java.io.IOException;
27 |
28 | import org.firmata4j.Parser;
29 |
30 | import com.fazecast.jSerialComm.SerialPort;
31 | import com.fazecast.jSerialComm.SerialPortDataListener;
32 | import com.fazecast.jSerialComm.SerialPortEvent;
33 |
34 | /**
35 | * Implementation of {@link TransportInterface} based on {@link SerialPort}.
36 | *
37 | * @author Norbert Sándor
38 | */
39 | public class JSerialCommTransport implements TransportInterface {
40 |
41 | private final SerialPort serialPort;
42 |
43 | private Parser parser;
44 |
45 | public JSerialCommTransport(String portDescriptor) {
46 | serialPort = SerialPort.getCommPort(portDescriptor);
47 | }
48 |
49 | @Override
50 | public void start() throws IOException {
51 | if (!serialPort.isOpen()) {
52 | if (serialPort.openPort()) {
53 | serialPort.setComPortParameters(57600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
54 | serialPort.addDataListener(new SerialPortDataListener() {
55 | @Override
56 | public void serialEvent(SerialPortEvent event) {
57 | if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
58 | parser.parse(event.getReceivedData());
59 | }
60 | }
61 |
62 | @Override
63 | public int getListeningEvents() {
64 | return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
65 | }
66 | });
67 | } else {
68 | throw new IOException("Cannot start firmata device: port=" + serialPort);
69 | }
70 | }
71 | }
72 |
73 | @Override
74 | public void stop() throws IOException {
75 | if (serialPort.isOpen() && !serialPort.closePort()) {
76 | throw new IOException("Cannot properly stop firmata device: port=" + serialPort);
77 | }
78 | }
79 |
80 | @Override
81 | public void write(byte[] bytes) throws IOException {
82 | serialPort.writeBytes(bytes, bytes.length);
83 | }
84 |
85 | @Override
86 | public void setParser(Parser parser) {
87 | this.parser = parser;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/transport/NetworkTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2019 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.transport;
25 |
26 | import java.io.DataInputStream;
27 | import java.io.DataOutputStream;
28 | import java.io.IOException;
29 | import java.net.InetAddress;
30 | import java.net.Socket;
31 | import java.net.SocketTimeoutException;
32 | import java.net.UnknownHostException;
33 | import org.firmata4j.Parser;
34 |
35 | /**
36 | * Allows connections over the network.
37 | *
38 | * @author Thomas Welsch <ttww@gmx.de>
39 | */
40 | public class NetworkTransport implements TransportInterface {
41 |
42 | private Parser parser;
43 | private Thread readerThread;
44 | private Socket socket;
45 | private DataOutputStream out;
46 | private DataInputStream in;
47 | private final InetAddress ip;
48 | private final int port;
49 |
50 | /**
51 | * Creates network transport using a sting as address.
52 | *
53 | * Address should specify host and port. Examples:
54 | *
55 | *
"192.168.1.10:36363"
56 | *
"explorer-bot.local:72727"
57 | *
58 | * @param address host and port
59 | */
60 | public NetworkTransport(String address) {
61 | String[] parts = address.split(":");
62 | if (parts.length != 2) {
63 | throw new IllegalArgumentException("Address must specify host and port but received " + address);
64 | }
65 | try {
66 | this.ip = InetAddress.getByName(parts[0]);
67 | } catch (UnknownHostException e) {
68 | throw new IllegalArgumentException(e);
69 | }
70 | this.port = Integer.valueOf(parts[1]);
71 | }
72 |
73 | public NetworkTransport(InetAddress ip, int port) {
74 | this.ip = ip;
75 | this.port = port;
76 | }
77 |
78 | @Override
79 | public void start() throws IOException {
80 | socket = new Socket(ip, port);
81 | socket.setReuseAddress(true);
82 | socket.setSoTimeout(1500);
83 | socket.setSoLinger(true, 1500);
84 | socket.setSoTimeout(1500);
85 | out = new DataOutputStream(socket.getOutputStream());
86 | in = new DataInputStream(socket.getInputStream());
87 | readerThread = new Thread(new Reader(), "firmata-network-transport");
88 | readerThread.start();
89 | }
90 |
91 | @Override
92 | public void stop() throws IOException {
93 | try {
94 | readerThread.interrupt();
95 | readerThread.join();
96 | } catch (InterruptedException e) {
97 | Thread.currentThread().interrupt();
98 | }
99 | try {
100 | if (out != null) {
101 | out.close();
102 | }
103 | } finally {
104 | try {
105 | if (in != null) {
106 | in.close();
107 | }
108 | } finally {
109 | if (socket != null) {
110 | socket.close();
111 | }
112 | }
113 | }
114 | out = null;
115 | in = null;
116 | socket = null;
117 |
118 | }
119 |
120 | @Override
121 | public void write(byte[] bytes) throws IOException {
122 | out.write(bytes);
123 | }
124 |
125 | @Override
126 | public void setParser(Parser parser) {
127 | this.parser = parser;
128 | }
129 |
130 | private class Reader implements Runnable {
131 |
132 | @Override
133 | public void run() {
134 | byte[] buf = new byte[100];
135 | int readIn;
136 | while (!Thread.currentThread().isInterrupted()) {
137 | try {
138 | readIn = in.read(buf);
139 | } catch (SocketTimeoutException e) {
140 | break; // We try to reconnect, hearthbeats (1*second) missing
141 | } catch (IOException e) {
142 | break;
143 | }
144 | if (readIn == -1) {
145 | break; // Connection closed
146 | }
147 | byte[] data = new byte[readIn];
148 | System.arraycopy(buf, 0, data, 0, readIn);
149 | parser.parse(data);
150 | }
151 | }
152 |
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/main/java/org/firmata4j/transport/SerialTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2014-2021 Oleg Kurbatov (o.v.kurbatov@gmail.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.firmata4j.transport;
25 |
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 | import java.io.IOException;
29 | import org.firmata4j.Parser;
30 |
31 | /**
32 | * Transfers data over the serial interface.
33 | *
34 | * Using an instance of this transport requires one of the following libraries
35 | * present in the classpath:
36 | *
37 | *