├── Firmata ├── pom.xml └── src │ └── main │ └── java │ └── name │ └── antonsmirnov │ └── firmata │ ├── BytesHelper.java │ ├── Firmata.java │ ├── FormatHelper.java │ ├── IFirmata.java │ ├── InitListener.java │ ├── message │ ├── AnalogMessage.java │ ├── DigitalMessage.java │ ├── FirmwareVersionMessage.java │ ├── I2cConfigMessage.java │ ├── I2cReadRequestMessage.java │ ├── I2cReplyMessage.java │ ├── I2cRequestMessage.java │ ├── Message.java │ ├── ProtocolVersionMessage.java │ ├── ReportAnalogPinMessage.java │ ├── ReportDigitalPortMessage.java │ ├── ReportFirmwareVersionMessage.java │ ├── ReportProtocolVersionMessage.java │ ├── SamplingIntervalMessage.java │ ├── ServoConfigMessage.java │ ├── SetPinModeMessage.java │ ├── StringSysexMessage.java │ ├── SysexMessage.java │ ├── SystemResetMessage.java │ └── factory │ │ ├── BoardMessageFactory.java │ │ ├── MessageFactory.java │ │ ├── MessageValidationException.java │ │ └── arduino │ │ ├── Diecimila.java │ │ ├── Duemilanove.java │ │ ├── Fio.java │ │ ├── Mega.java │ │ ├── Mega2560.java │ │ ├── Mini.java │ │ ├── Nano.java │ │ └── Uno.java │ ├── reader │ ├── AnalogMessageReader.java │ ├── BaseSysexMessageReader.java │ ├── DigitalMessageReader.java │ ├── FirmwareVersionMessageReader.java │ ├── I2cReplyMessageReader.java │ ├── IMessageReader.java │ ├── ProtocolVersionMessageReader.java │ ├── StringSysexMessageReader.java │ └── SysexMessageReader.java │ ├── wrapper │ ├── AndFilter.java │ ├── DateMessagePropertyManager.java │ ├── DigitalPortWrapper.java │ ├── DirectionMessageFilter.java │ ├── DirectionMessagePropertyManager.java │ ├── IMessageFilter.java │ ├── IMessagePropertyManager.java │ ├── MessagePropertyManager.java │ ├── MessageWithProperties.java │ ├── MessagesHistoryWrapper.java │ ├── OrFilter.java │ ├── PinModeWrapper.java │ ├── ReportAutostopWrapper.java │ └── StubMessageFilter.java │ └── writer │ ├── AnalogMessageWriter.java │ ├── DigitalMessageWriter.java │ ├── I2cConfigMessageWriter.java │ ├── I2cRequestMessageWriter.java │ ├── IMessageWriter.java │ ├── ReportAnalogPinMessageWriter.java │ ├── ReportDigitalPortMessageWriter.java │ ├── ReportProtocolVersionMessageWriter.java │ ├── SamplingIntervalMessageWriter.java │ ├── ServoConfigMessageWriter.java │ ├── SetPinModeMessageWriter.java │ ├── SysexMessageWriter.java │ └── SystemResetMessageWriter.java ├── FirmataTests ├── pom.xml └── src │ ├── main │ └── java │ │ └── name │ │ └── antonsmirnov │ │ └── firmata │ │ ├── FirmataWaiter.java │ │ ├── OriginalFirmata.java │ │ ├── TestSerial.java │ │ └── WaitException.java │ └── test │ └── java │ └── name │ └── antonsmirnov │ └── firmata │ └── tests │ ├── AnalogMessageTest.java │ ├── BaseFirmataTest.java │ ├── BytesHelperTest.java │ ├── DigitalMessageTest.java │ ├── FirmataWrappersTest.java │ ├── FirmwareVersionMessageTest.java │ ├── I2cConfigMessageTest.java │ ├── I2cReadRequestMessageTest.java │ ├── I2cReplyMessageTest.java │ ├── I2cRequestMessageTest.java │ ├── MessageValidationTest.java │ ├── ProtocolVersionMessageTest.java │ ├── ReportAnalogPinMessageTest.java │ ├── ReportDigitalPortMessageTest.java │ ├── ReportFirmwareVersionMessageTest.java │ ├── ReportProtocolVersionMessageTest.java │ ├── SamplingIntervalMessageTest.java │ ├── ServoConfigMessageTest.java │ ├── SetPinModeMessageTest.java │ ├── StringSysexMessageTest.java │ ├── SysexMessageTest.java │ ├── SystemResetMessageTest.java │ └── hardware │ ├── BaseHardwareTest.java │ ├── DigitalWriteHardwareTest.java │ ├── EchoStringHardwareTest.java │ ├── HMC5883LHardwareTest.java │ ├── ReadDigitalPortHardwareTest.java │ └── ReportProtocolHardwareTest.java ├── IndepProcessingSerial ├── pom.xml └── src │ └── main │ └── java │ ├── name │ └── antonsmirnov │ │ └── firmata │ │ └── serial │ │ └── IndepProcessingSerialAdapter.java │ └── processing │ └── serial │ └── IndepProcessingSerial.java ├── ProcessingSerial ├── pom.xml ├── res │ ├── install_processing_core.bat │ └── install_processing_serial.bat └── src │ └── main │ └── java │ └── name │ └── antonsmirnov │ └── firmata │ └── serial │ └── ProcessingSerialAdapter.java ├── README ├── Serial ├── pom.xml └── src │ ├── main │ └── java │ │ └── name │ │ └── antonsmirnov │ │ └── firmata │ │ └── serial │ │ ├── BufferingSerialWrapper.java │ │ ├── ByteArrayByteBufferAdapter.java │ │ ├── IByteBuffer.java │ │ ├── ISerial.java │ │ ├── ISerialListener.java │ │ ├── QueueByteBufferAdapter.java │ │ ├── SerialException.java │ │ ├── SocketSerialAdapter.java │ │ └── StreamingSerialAdapter.java │ └── test │ └── java │ └── name │ └── antonsmirnov │ └── firmata │ └── serial │ ├── WritebackSerial.java │ └── tests │ ├── BufferingSerialWrapperTest.java │ ├── SocketSerialAdapterTest.java │ └── StreamingSerialAdapterTest.java └── pom.xml /Firmata/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | name.antonsmirnov.firmata 9 | parent 10 | 1.0 11 | 12 | 13 | name.antonsmirnov.firmata 14 | Firmata 15 | 2.6 16 | Firmata 17 | 18 | 19 | 20 | Anton Smirnov 21 | dev@antonsmirnov.name 22 | 23 | 24 | 25 | Firmata implementation (with Serial API dependency and with no impl dependency) 26 | 27 | 28 | 29 | name.antonsmirnov.firmata.serial 30 | Serial 31 | 1.1 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-api 37 | 1.6.4 38 | 39 | 40 | 41 | junit 42 | junit 43 | 4.8.1 44 | test 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/BytesHelper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Helps to prepare bytes data 7 | */ 8 | public class BytesHelper { 9 | 10 | /** 11 | * @param channel command channel 12 | * @return command channel mask 13 | */ 14 | public static int ENCODE_CHANNEL(int channel) { 15 | return channel & 0x0F; 16 | } 17 | 18 | /** 19 | * Decode command from byte 20 | * 21 | * @param incomingByte 22 | * @return 23 | */ 24 | public static int DECODE_COMMAND(int incomingByte) { 25 | return incomingByte < 0xF0 26 | ? incomingByte & 0xF0 27 | : incomingByte; 28 | } 29 | 30 | /** 31 | * Decode channel from byte 32 | * 33 | * @param incomingByte 34 | * @return 35 | */ 36 | public static int DECODE_CHANNEL(int incomingByte) { 37 | return incomingByte & 0x0F; 38 | } 39 | 40 | /** 41 | * Return less significant byte 42 | * 43 | * @param value value 44 | * @return less significant byte 45 | */ 46 | public static int LSB(int value) { 47 | return value & 0x7F; 48 | } 49 | 50 | /** 51 | * Return most significant byte 52 | * 53 | * @param value value 54 | * @return most significant byte 55 | */ 56 | public static int MSB(int value) { 57 | return (value >> 7) & 0x7F; 58 | } 59 | 60 | /** 61 | * Return byte from LSB and MSB 62 | * 63 | * @param lsb less significant byte 64 | * @param msb most significant byte 65 | * @return byte 66 | */ 67 | public static int DECODE_BYTE(int lsb, int msb) { 68 | return (msb << 7) + lsb; 69 | } 70 | 71 | /** 72 | * Decode string that was encoded using LSB(byte), MSB(byte) 73 | * 74 | * @param buffer buffer 75 | * @param startIndex start index 76 | * @param endIndex end index 77 | * @return decoded string 78 | */ 79 | public static String DECODE_STRING(byte[] buffer, int startIndex, int endIndex) { 80 | StringBuilder sb = new StringBuilder(); 81 | int offset = startIndex; 82 | int length = (endIndex - startIndex + 1) / 2; 83 | for (int i=0; i= 0; eachBit--) { 191 | mask |= (eachBit == bit ? 0 : 1); 192 | if (eachBit > 0) 193 | mask <<= 1; 194 | } 195 | return mask; 196 | } 197 | 198 | /** 199 | * Get mask for port to set pin in LOW 200 | * 201 | * @param pinInPort = [0,7] 202 | * @return 203 | */ 204 | private static int pinMaskLow(int pinInPort) { 205 | return bitMaskLow(pinInPort); 206 | } 207 | 208 | /** 209 | * Get pin in port index using absolute pin index 210 | * 211 | * @param pin any 212 | * @return pin = [0,7] 213 | */ 214 | public static int pinInPort(int pin) { 215 | return pin % BITS_IN_BYTE; 216 | } 217 | 218 | /** 219 | * Set HIGH or LOW pin value for port values 220 | * 221 | * @param portValues 222 | * @param pinInPort = [0, 7] 223 | * @param highLevel pin level is High level 224 | * @return 225 | */ 226 | public static int setPin(int portValues, int pinInPort, boolean highLevel) { 227 | if (highLevel) { 228 | return portValues | pinMaskHigh(pinInPort); 229 | } else { 230 | return portValues & pinMaskLow(pinInPort); 231 | } 232 | } 233 | 234 | public static int setBit(int byteValue, int bit, boolean highLevel) { 235 | if (highLevel) { 236 | return byteValue | pinMaskHigh(bit); 237 | } else { 238 | return byteValue & pinMaskLow(bit); 239 | } 240 | } 241 | 242 | /** 243 | * Check if pin level is High 244 | * 245 | * @param portValues portValues 246 | * @param pinInPort = [0,7] 247 | * @return 248 | */ 249 | public static boolean getPin(int portValues, int pinInPort) { 250 | return (portValues & BytesHelper.pinMaskHigh(pinInPort)) > 0; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/FormatHelper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | /** 4 | * Helps to format values 5 | */ 6 | public class FormatHelper { 7 | 8 | // default delimiter 9 | private static final String HEX_PREFIX = "0x"; 10 | 11 | public static final String SPACE = " "; 12 | public static final String QUOTE = "'"; 13 | 14 | public String formatBinary(int value) { 15 | StringBuilder sb = new StringBuilder(); 16 | formatBinary(value, sb); 17 | return sb.toString(); 18 | } 19 | 20 | protected void formatBinary(int value, StringBuilder sb) { 21 | sb.append(HEX_PREFIX); 22 | sb.append(Integer.toHexString(value).toUpperCase()); 23 | } 24 | 25 | public String formatBinaryData(int[] binaryData, String delimiter, String prefix, String postfix) { 26 | StringBuilder sb = new StringBuilder(prefix); 27 | for (int i = 0; i < binaryData.length; i++) { 28 | formatBinary(binaryData[i], sb); 29 | if (i < binaryData.length-1) 30 | sb.append(delimiter); 31 | } 32 | sb.append(postfix); 33 | return sb.toString(); 34 | } 35 | 36 | public String formatBinaryData(int[] binaryData) { 37 | return formatBinaryData(binaryData, SPACE, QUOTE, QUOTE); 38 | } 39 | 40 | public String formatBinaryData(byte[] binaryData, String delimiter, String prefix, String postfix) { 41 | int[] intArray = new int[binaryData.length]; 42 | for (int i=0; i modes = new HashMap(); 13 | 14 | /** 15 | * Pin mode enumeration 16 | * (can be used as 'mode' parameter value for type-safety) 17 | */ 18 | public enum PIN_MODE { 19 | 20 | /** 21 | * Pin works as input 22 | */ 23 | INPUT(0), 24 | 25 | /** 26 | * Pin works as output 27 | */ 28 | OUTPUT(1), 29 | 30 | /** 31 | * Pin works as analog input 32 | */ 33 | ANALOG(2), 34 | 35 | /** 36 | * Pin works as analog PWM output 37 | */ 38 | PWM(3), 39 | 40 | /** 41 | * Ping workds as servo 42 | */ 43 | SERVO(4); 44 | 45 | private int mode; 46 | 47 | public int getMode() { 48 | return mode; 49 | } 50 | 51 | private PIN_MODE(int mode) { 52 | this.mode = mode; 53 | modes.put(mode, this); 54 | } 55 | 56 | public static PIN_MODE find(int mode) { 57 | return modes.get(mode); 58 | } 59 | } 60 | 61 | protected SetPinModeMessage() { 62 | super(); 63 | } 64 | 65 | /** 66 | * 67 | * @param pin 68 | * @param mode (use PIN_MODE enum for type-safety) 69 | * @see name.antonsmirnov.firmata.message.SetPinModeMessage.PIN_MODE 70 | */ 71 | public SetPinModeMessage(int pin, int mode) { 72 | this(); 73 | setPin(pin); 74 | setMode(mode); 75 | } 76 | 77 | private int pin; 78 | 79 | public int getPin() { 80 | return pin; 81 | } 82 | 83 | public void setPin(int pin) { 84 | this.pin = pin; 85 | } 86 | 87 | private int mode; 88 | 89 | public int getMode() { 90 | return mode; 91 | } 92 | 93 | public void setMode(int mode) { 94 | this.mode = mode; 95 | } 96 | 97 | @Override 98 | public boolean equals(Object obj) { 99 | if (!super.equals(obj)) 100 | return false; 101 | 102 | SetPinModeMessage message = (SetPinModeMessage)obj; 103 | return message != null && 104 | message.getPin() == getPin() && 105 | message.getMode() == getMode(); 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | return MessageFormat.format("SetPinModeMessage[pin={0}, mode={1}]", pin, mode); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/StringSysexMessage.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message; 2 | 3 | import java.text.MessageFormat; 4 | 5 | /** 6 | * String sysex message 7 | */ 8 | public class StringSysexMessage extends SysexMessage { 9 | 10 | // Sysex command byte 11 | public static final int COMMAND = 0x71; 12 | 13 | public StringSysexMessage() { 14 | super(COMMAND, null); 15 | } 16 | 17 | public StringSysexMessage(String data) { 18 | this(); 19 | setData(data); 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return MessageFormat.format("StringSysexMessage[data=\"{0}\"]", getData()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/SysexMessage.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message; 2 | 3 | import java.text.MessageFormat; 4 | 5 | /** 6 | * Sysex message 7 | * 8 | */ 9 | public class SysexMessage extends Message { 10 | 11 | public SysexMessage() { 12 | super(); 13 | } 14 | 15 | /** 16 | * Constructor 17 | * 18 | * @param command sysex command (NOT firmata command) 19 | * @param data sysex command data 20 | */ 21 | public SysexMessage(int command, String data) { 22 | this(); 23 | setCommand(command); 24 | setData(data); 25 | } 26 | 27 | private int command; 28 | 29 | public int getCommand() { 30 | return command; 31 | } 32 | 33 | public void setCommand(int command) { 34 | this.command = command; 35 | } 36 | 37 | private String data; 38 | 39 | public String getData() { 40 | return data; 41 | } 42 | 43 | public void setData(String data) { 44 | this.data = data; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object obj) { 49 | if (!super.equals(obj)) 50 | return false; 51 | 52 | SysexMessage message = (SysexMessage)obj; 53 | return message != null && 54 | message.getCommand() == getCommand() && 55 | ( 56 | (message.getData() == null && getData() == null) 57 | || 58 | (message.getData() != null && message.getData().equals(getData())) 59 | ); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return MessageFormat.format("SysexMessage[command={0}, data=\"{1}\"]", command, data); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/SystemResetMessage.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message; 2 | 3 | /** 4 | * System reset message 5 | */ 6 | public class SystemResetMessage extends Message { 7 | 8 | public SystemResetMessage() { 9 | super(); 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | return "SystemResetMessage[]"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/BoardMessageFactory.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory; 2 | 3 | import name.antonsmirnov.firmata.message.*; 4 | 5 | import java.text.MessageFormat; 6 | import java.util.Arrays; 7 | 8 | /** 9 | * MessageFactory implementation with pins config 10 | */ 11 | public abstract class BoardMessageFactory implements MessageFactory { 12 | 13 | public static final int MIN_PIN = 0; 14 | 15 | protected int minPin; 16 | protected int maxPin; 17 | protected int[] analogOutPins; 18 | protected int[] analogInPins; 19 | 20 | public int getMinPin() { 21 | return minPin; 22 | } 23 | 24 | public int getMaxPin() { 25 | return maxPin; 26 | } 27 | 28 | public int[] getAnalogInPins() { 29 | return analogInPins; 30 | } 31 | 32 | public int[] getAnalogOutPins() { 33 | return analogOutPins; 34 | } 35 | 36 | protected static int[] union(int[] array1, int[] array2) { 37 | int[] array = new int[array1.length + array2.length]; 38 | System.arraycopy(array1, 0, array, 0, array1.length); 39 | System.arraycopy(array2, 0, array, array1.length, array2.length); 40 | return array; 41 | } 42 | 43 | protected static int[] arrayFromTo(int from, int to) { 44 | int[] array = new int[to - from + 1]; 45 | for (int i=0; i maxPin) 64 | throw new MessageValidationException( 65 | MessageFormat.format("Allowed pin values are [{0}-{1}]", minPin, maxPin)); 66 | } 67 | 68 | protected void validatePort(int port) throws MessageValidationException { 69 | int ports = (int)Math.ceil((maxPin + 1) / 8.0); 70 | if (port < 0 || port > ports) 71 | throw new MessageValidationException( 72 | MessageFormat.format("Allowed port values are [{0}-{1}]", 0, ports)); 73 | } 74 | 75 | protected void validateAnalogIn(int pin) throws MessageValidationException { 76 | int[] array = analogInPins; 77 | if (Arrays.binarySearch(array, pin) < 0) 78 | throw new MessageValidationException( 79 | MessageFormat.format("Allowed analog in pins are [{0}]", arrayToString(array))); 80 | } 81 | 82 | public boolean isAnalogIn(int pin) { 83 | try { 84 | validateAnalogIn(pin); 85 | return true; 86 | } catch (MessageValidationException e) { 87 | return false; 88 | } 89 | } 90 | 91 | protected void validateAnalogOut(int pin) throws MessageValidationException { 92 | int[] array = analogOutPins; 93 | if (Arrays.binarySearch(array, pin) < 0) 94 | throw new MessageValidationException( 95 | MessageFormat.format("Allowed analog out (PWM) pins are [{0}]", arrayToString(array))); 96 | } 97 | 98 | public boolean isAnalogOut(int pin) { 99 | try { 100 | validateAnalogOut(pin); 101 | return true; 102 | } catch (MessageValidationException e) { 103 | return false; 104 | } 105 | } 106 | 107 | protected String arrayToString(int[] array) { 108 | StringBuilder sb = new StringBuilder(); 109 | for (int i=0; i0) 111 | sb.append(", "); 112 | sb.append(array[i]); 113 | } 114 | return sb.toString(); 115 | } 116 | 117 | protected void validateMode(int mode) throws MessageValidationException { 118 | SetPinModeMessage.PIN_MODE enumValue = SetPinModeMessage.PIN_MODE.find(mode); 119 | if (enumValue == null) 120 | throw new MessageValidationException( 121 | MessageFormat.format("Allowed modes are [{0}]", SetPinModeMessage.PIN_MODE.values())); 122 | } 123 | 124 | protected void validateDigitalValue(int value) throws MessageValidationException { 125 | if (value != 0 && value != 1) 126 | throw new MessageValidationException("Allowed digital values are [0; 1]"); 127 | } 128 | 129 | protected void validateDigitalMask(int value) throws MessageValidationException { 130 | if (value < 0 || value > 255) 131 | throw new MessageValidationException("Allowed digital mask values are [0-255]"); 132 | } 133 | 134 | private void validateAnalogValue(int value) throws MessageValidationException { 135 | if (value < 0 || value > 255) 136 | throw new MessageValidationException("Allowed analog values are [0-255]"); 137 | } 138 | 139 | public ReportDigitalPortMessage digitalRead(int port) throws MessageValidationException { 140 | validatePort(port); 141 | 142 | return new ReportDigitalPortMessage(port, true); 143 | } 144 | 145 | public ReportAnalogPinMessage analogRead(int pin) throws MessageValidationException { 146 | validatePin(pin); 147 | validateAnalogIn(pin); 148 | 149 | return new ReportAnalogPinMessage(pin, true); 150 | } 151 | 152 | public SetPinModeMessage pinMode(int pin, int mode) throws MessageValidationException { 153 | validatePin(pin); 154 | validateMode(mode); 155 | 156 | // analog in 157 | if (mode == SetPinModeMessage.PIN_MODE.ANALOG.getMode()) 158 | validateAnalogIn(pin); 159 | 160 | // analog out 161 | if (mode == SetPinModeMessage.PIN_MODE.PWM.getMode()) 162 | validateAnalogOut(pin); 163 | 164 | return new SetPinModeMessage(pin, mode); 165 | } 166 | 167 | public DigitalMessage digitalWrite(int port, int value) throws MessageValidationException { 168 | validatePort(port); 169 | validateDigitalMask(value); 170 | 171 | return new DigitalMessage(port, value); 172 | } 173 | 174 | public AnalogMessage analogWrite(int pin, int value) throws MessageValidationException { 175 | validatePin(pin); 176 | validateAnalogOut(pin); 177 | validateAnalogValue(value); 178 | 179 | return new AnalogMessage(pin, value); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/MessageFactory.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory; 2 | 3 | import name.antonsmirnov.firmata.message.*; 4 | 5 | /** 6 | * Builds Messages 7 | * (build SAFE messages with pins, modes validation according to hardware features) 8 | */ 9 | public interface MessageFactory { 10 | 11 | /** 12 | * Read digital value fom the pin 13 | * 14 | * @param port port 15 | * @return firmata message 16 | */ 17 | ReportDigitalPortMessage digitalRead(int port) throws MessageValidationException; 18 | 19 | /** 20 | * Read analog value from the pin 21 | * 22 | * @param pin pin 23 | * @return firmata message 24 | */ 25 | ReportAnalogPinMessage analogRead(int pin) throws MessageValidationException; 26 | 27 | /** 28 | * Set a digital pin to input or output mode 29 | * 30 | * @param pin pin 31 | * @param mode message 32 | * @see SetPinModeMessage.PIN_MODE 33 | * @return firmata message 34 | */ 35 | SetPinModeMessage pinMode(int pin, int mode) throws MessageValidationException; 36 | 37 | /** 38 | * Write to a digital pin 39 | * 40 | * @param port port 41 | * @param value pins mask 42 | * @return firmata message 43 | */ 44 | DigitalMessage digitalWrite(int port, int value) throws MessageValidationException; 45 | 46 | /** 47 | * Write an analog value (PWM-wave) to a pin. 48 | * 49 | * @param pin pin 50 | * @return firmata message 51 | */ 52 | AnalogMessage analogWrite(int pin, int value) throws MessageValidationException; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/MessageValidationException.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory; 2 | 3 | /** 4 | * Message parameters are invalid 5 | * (most likely to hardware features) 6 | */ 7 | public class MessageValidationException extends Exception { 8 | 9 | public MessageValidationException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Diecimila.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Diecimila board 7 | * http://arduino.cc/en/Main/ArduinoBoardDiecimila 8 | */ 9 | public class Diecimila extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Diecimila() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 5), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Duemilanove.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Duemilanove board 7 | * http://arduino.cc/en/Main/ArduinoBoardDuemilanove 8 | */ 9 | public class Duemilanove extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Duemilanove() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 5), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Fio.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Fio board 7 | * http://arduino.cc/en/Main/ArduinoBoardFio 8 | */ 9 | public class Fio extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Fio() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 7), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Mega.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Mega board 7 | * http://arduino.cc/en/Main/ArduinoBoardMega 8 | */ 9 | public class Mega extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 54; 12 | 13 | public Mega() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 15), union(arrayFromTo(2, 13), arrayFromTo(44, 46))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Mega2560.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Mega 2560 board 7 | * http://arduino.cc/en/Main/ArduinoBoardMega2560 8 | */ 9 | public class Mega2560 extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 54; 12 | 13 | public Mega2560() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 15), union(arrayFromTo(2, 13), arrayFromTo(44, 46))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Mini.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Mini board 7 | * http://arduino.cc/en/Main/ArduinoBoardMini 8 | */ 9 | public class Mini extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Mini() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 7), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Nano.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Nano board 7 | * http://arduino.cc/en/Main/ArduinoBoardNano 8 | */ 9 | public class Nano extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Nano() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 7), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/message/factory/arduino/Uno.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.message.factory.arduino; 2 | 3 | import name.antonsmirnov.firmata.message.factory.BoardMessageFactory; 4 | 5 | /** 6 | * Arduino Uno board 7 | * http://arduino.cc/en/Main/ArduinoBoardUno 8 | */ 9 | public class Uno extends BoardMessageFactory { 10 | 11 | public final static int MAX_PIN = 13; 12 | 13 | public Uno() { 14 | super(MIN_PIN, MAX_PIN, arrayFromTo(0, 5), new int[] { 3,5,6,9,10,11 }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/AnalogMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.AnalogMessage; 5 | import name.antonsmirnov.firmata.writer.AnalogMessageWriter; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.DECODE_BYTE; 8 | import static name.antonsmirnov.firmata.BytesHelper.DECODE_CHANNEL; 9 | 10 | /** 11 | * MessageReader for AnalogMessage 12 | */ 13 | public class AnalogMessageReader implements IMessageReader { 14 | 15 | public boolean canRead(byte[] buffer, int bufferLength, int command) { 16 | return command == AnalogMessageWriter.COMMAND; 17 | } 18 | 19 | private boolean isHandling; 20 | 21 | public void startReading() { 22 | isHandling = true; 23 | message = new AnalogMessage(); 24 | } 25 | 26 | public void read(byte[] buffer, int length) { 27 | if (length == 2) { 28 | message.setPin(DECODE_CHANNEL(buffer[0])); 29 | } else { 30 | message.setValue(DECODE_BYTE(buffer[1], buffer[2])); 31 | isHandling = false; 32 | } 33 | } 34 | 35 | public boolean finishedReading() { 36 | return !isHandling; 37 | } 38 | 39 | private AnalogMessage message; 40 | 41 | public AnalogMessage getMessage() { 42 | return message; 43 | } 44 | 45 | public void fireEvent(IFirmata.Listener listener) { 46 | listener.onAnalogMessageReceived(getMessage()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/BaseSysexMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.message.SysexMessage; 4 | import name.antonsmirnov.firmata.writer.SysexMessageWriter; 5 | 6 | import static name.antonsmirnov.firmata.BytesHelper.*; 7 | 8 | /** 9 | * Base MessageReader for SysexMessage 10 | */ 11 | public abstract class BaseSysexMessageReader 12 | implements IMessageReader { 13 | 14 | private Byte sysexCommand; 15 | 16 | public BaseSysexMessageReader(Byte sysexCommand) { 17 | this.sysexCommand = sysexCommand; 18 | } 19 | 20 | public boolean canRead(byte[] buffer, int bufferLength, int command) { 21 | return (bufferLength == 1 && buffer[0] == (byte) SysexMessageWriter.COMMAND_START) // is sysex message? 22 | || 23 | (bufferLength == 2 && (sysexCommand == null || sysexCommand.equals(buffer[1]))) // is needed sysex command 24 | || 25 | (bufferLength == 3 && sysexCommand != null); 26 | } 27 | 28 | protected boolean isReading; 29 | 30 | public void startReading() { 31 | isReading = true; 32 | } 33 | 34 | protected ConcreteSysexMessage message; 35 | 36 | public void read(byte[] buffer, int length) { 37 | byte incomingByte = buffer[length-1]; 38 | 39 | if (incomingByte == (byte) SysexMessageWriter.COMMAND_END) { 40 | isReading = false; 41 | 42 | message = buildSysexMessage(buffer, length); 43 | } 44 | } 45 | 46 | /** 47 | * Build SysexMessage from incoming buffer 48 | * 49 | * @param buffer buffer (start from COMMAND_START byte, ends with COMMAND_END byte) 50 | * @param bufferLength buffer length 51 | * @return SysexMessage command or inherited message 52 | */ 53 | protected abstract ConcreteSysexMessage buildSysexMessage(byte[] buffer, int bufferLength); 54 | 55 | public ConcreteSysexMessage getMessage() { 56 | return message; 57 | } 58 | 59 | public boolean finishedReading() { 60 | return !isReading; 61 | } 62 | 63 | protected void validateSysexDataLength(int startIndex, int endIndex) { 64 | if ((endIndex - startIndex + 1) % 2 != 0) 65 | throw new RuntimeException("Sysex command data length should be even"); 66 | } 67 | 68 | // extract string from buffer 69 | protected String extractStringFromBuffer(byte[] buffer, int startIndex, int endIndex) { 70 | validateSysexDataLength(startIndex, endIndex); 71 | return DECODE_STRING(buffer, startIndex, endIndex); 72 | } 73 | 74 | // extract integer array from buffer 75 | protected int[] extractIntArrayFromBuffer(byte[] buffer, int startIndex, int endIndex) { 76 | validateSysexDataLength(startIndex, endIndex); 77 | return DECODE_INT_ARRAY(buffer, startIndex, endIndex); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/DigitalMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.DigitalMessage; 5 | import name.antonsmirnov.firmata.writer.DigitalMessageWriter; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.DECODE_BYTE; 8 | import static name.antonsmirnov.firmata.BytesHelper.DECODE_CHANNEL; 9 | 10 | /** 11 | * MessageReader for DigitalMessage 12 | */ 13 | public class DigitalMessageReader implements IMessageReader { 14 | 15 | public boolean canRead(byte[] buffer, int bufferLength, int command) { 16 | return command == DigitalMessageWriter.COMMAND; 17 | } 18 | 19 | private boolean isHandling; 20 | 21 | public void startReading() { 22 | isHandling = true; 23 | message = new DigitalMessage(); 24 | } 25 | 26 | public void read(byte[] buffer, int length) { 27 | if (length == 2) { 28 | message.setPort(DECODE_CHANNEL(buffer[0])); 29 | } else { 30 | message.setValue(DECODE_BYTE(buffer[1], buffer[2])); 31 | isHandling = false; 32 | } 33 | } 34 | 35 | public boolean finishedReading() { 36 | return !isHandling; 37 | } 38 | 39 | private DigitalMessage message; 40 | 41 | public DigitalMessage getMessage() { 42 | return message; 43 | } 44 | 45 | public void fireEvent(IFirmata.Listener listener) { 46 | listener.onDigitalMessageReceived(getMessage()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/FirmwareVersionMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.FirmwareVersionMessage; 5 | import name.antonsmirnov.firmata.message.ReportFirmwareVersionMessage; 6 | 7 | /** 8 | * MessageReader for FirmwareVersionMessage 9 | */ 10 | public class FirmwareVersionMessageReader extends BaseSysexMessageReader { 11 | 12 | public FirmwareVersionMessageReader() { 13 | super((byte)ReportFirmwareVersionMessage.COMMAND); 14 | } 15 | 16 | @Override 17 | protected FirmwareVersionMessage buildSysexMessage(byte[] buffer, int bufferLength) { 18 | FirmwareVersionMessage message = new FirmwareVersionMessage(); 19 | message.setMajor(buffer[2]); 20 | message.setMinor(buffer[3]); 21 | // skip 4 first bytes - COMMAND_START, sysex command byte, major, minor 22 | message.setName(extractStringFromBuffer(buffer, 4, bufferLength - 2)); 23 | return message; 24 | } 25 | 26 | public void fireEvent(IFirmata.Listener listener) { 27 | listener.onFirmwareVersionMessageReceived(message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/I2cReplyMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.I2cReplyMessage; 5 | 6 | import static name.antonsmirnov.firmata.BytesHelper.*; 7 | 8 | /** 9 | * MessageReader for I2cReplyMessage 10 | */ 11 | public class I2cReplyMessageReader extends BaseSysexMessageReader { 12 | 13 | public I2cReplyMessageReader() { 14 | super((byte)I2cReplyMessage.COMMAND); 15 | } 16 | 17 | @Override 18 | protected I2cReplyMessage buildSysexMessage(byte[] buffer, int bufferLength) { 19 | I2cReplyMessage message = new I2cReplyMessage(); 20 | // skip 2 first bytes - COMMAND_START and sysex command byte 21 | message.setSlaveAddress(DECODE_BYTE(buffer[2], buffer[3])); 22 | message.setRegister(DECODE_BYTE(buffer[4], buffer[5])); 23 | message.setBinaryData(extractIntArrayFromBuffer(buffer, 6, bufferLength - 2)); 24 | // message.getData() is not used 25 | return message; 26 | } 27 | 28 | public void fireEvent(IFirmata.Listener listener) { 29 | listener.onI2cMessageReceived(getMessage()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/IMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.Message; 5 | 6 | /** 7 | * Message reader 8 | */ 9 | public interface IMessageReader { 10 | 11 | /** 12 | * Can read command 13 | * 14 | * @param buffer incoming buffer 15 | * @param bufferLength current buffer length 16 | * @return true if it's his command message type 17 | */ 18 | boolean canRead(byte[] buffer, int bufferLength, int command); 19 | 20 | /** 21 | * Start handling message 22 | */ 23 | void startReading(); 24 | 25 | /** 26 | * Read next message byte 27 | * 28 | * @param buffer incoming buffer 29 | * @param length current buffer length 30 | */ 31 | public void read(byte[] buffer, int length); 32 | 33 | /** 34 | * Has it finished message handling 35 | * 36 | * @return is it has received all the message bytes 37 | */ 38 | boolean finishedReading(); 39 | 40 | /** 41 | * Message if it finished handling 42 | * (check finishedReading before) 43 | * @return 44 | */ 45 | ConcreteMessage getMessage(); 46 | 47 | /** 48 | * Invoke Firmata listener 49 | * @param listener 50 | */ 51 | void fireEvent(IFirmata.Listener listener); 52 | } 53 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/ProtocolVersionMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.ProtocolVersionMessage; 5 | import name.antonsmirnov.firmata.writer.ReportProtocolVersionMessageWriter; 6 | 7 | /** 8 | * MessageReader for ProtocolVersionMessage 9 | */ 10 | public class ProtocolVersionMessageReader implements IMessageReader { 11 | 12 | public boolean canRead(byte[] buffer, int bufferLength, int command) { 13 | return bufferLength == 1 14 | && 15 | buffer[0] == (byte) ReportProtocolVersionMessageWriter.COMMAND; 16 | } 17 | 18 | private boolean isHandling; 19 | 20 | public void startReading() { 21 | isHandling = true; 22 | message = new ProtocolVersionMessage(); 23 | } 24 | 25 | public void read(byte[] buffer, int length) { 26 | byte incomingByte = buffer[length-1]; 27 | if (length == 2) { 28 | message.setMajor(incomingByte); 29 | } else { 30 | message.setMinor(incomingByte); 31 | isHandling = false; 32 | } 33 | } 34 | 35 | public boolean finishedReading() { 36 | return !isHandling; 37 | } 38 | 39 | private ProtocolVersionMessage message; 40 | 41 | public ProtocolVersionMessage getMessage() { 42 | return message; 43 | } 44 | 45 | public void fireEvent(IFirmata.Listener listener) { 46 | listener.onProtocolVersionMessageReceived(getMessage()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/StringSysexMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.StringSysexMessage; 5 | 6 | /** 7 | * MessageReader for StringSysexMessage 8 | */ 9 | public class StringSysexMessageReader extends BaseSysexMessageReader { 10 | 11 | public StringSysexMessageReader() { 12 | super((byte)StringSysexMessage.COMMAND); 13 | } 14 | 15 | @Override 16 | protected StringSysexMessage buildSysexMessage(byte[] buffer, int bufferLength) { 17 | StringSysexMessage message = new StringSysexMessage(); 18 | // skip 2 first bytes - COMMAND_START and sysex command byte 19 | message.setData(extractStringFromBuffer(buffer, 2, bufferLength - 2)); 20 | return message; 21 | } 22 | 23 | public void fireEvent(IFirmata.Listener listener) { 24 | listener.onStringSysexMessageReceived(getMessage()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/reader/SysexMessageReader.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.reader; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.SysexMessage; 5 | 6 | /** 7 | * MessageReader for SysexMessage 8 | */ 9 | public class SysexMessageReader extends BaseSysexMessageReader { 10 | 11 | public SysexMessageReader() { 12 | super(null); 13 | // null means that 'no command byte specified' 14 | } 15 | 16 | @Override 17 | protected SysexMessage buildSysexMessage(byte[] buffer, int bufferLength) { 18 | SysexMessage message = new SysexMessage(); 19 | message.setCommand(buffer[1]); 20 | // skip 2 first bytes - COMMAND_START and sysex command byte 21 | message.setData(extractStringFromBuffer(buffer, 2, bufferLength - 2)); 22 | return message; 23 | } 24 | 25 | public void fireEvent(IFirmata.Listener listener) { 26 | listener.onSysexMessageReceived(message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/AndFilter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * All filters should allow data 5 | */ 6 | public class AndFilter implements IMessageFilter{ 7 | 8 | private IMessageFilter[] filters; 9 | 10 | public AndFilter(IMessageFilter... filters) { 11 | this.filters = filters; 12 | } 13 | 14 | public boolean isAllowed(MessageWithProperties data) { 15 | for (IMessageFilter eachFilter : filters) 16 | if (!eachFilter.isAllowed(data)) 17 | return false; 18 | 19 | return true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/DateMessagePropertyManager.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import java.util.Date; 4 | import java.util.GregorianCalendar; 5 | 6 | /** 7 | * Date for the message 8 | */ 9 | public class DateMessagePropertyManager extends MessagePropertyManager { 10 | 11 | private static final String KEY = "date"; 12 | 13 | public DateMessagePropertyManager() { 14 | super(KEY); 15 | } 16 | 17 | @Override 18 | protected Date createProperty() { 19 | return GregorianCalendar.getInstance().getTime(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/DigitalPortWrapper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.DigitalMessage; 5 | import name.antonsmirnov.firmata.message.Message; 6 | import name.antonsmirnov.firmata.serial.SerialException; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class DigitalPortWrapper implements IFirmata { 12 | 13 | private IFirmata firmata; 14 | 15 | public DigitalPortWrapper(IFirmata firmata) { 16 | this.firmata = firmata; 17 | } 18 | 19 | public void addListener(Listener listener) { 20 | firmata.addListener(listener); 21 | } 22 | 23 | public void removeListener(Listener listener) { 24 | firmata.removeListener(listener); 25 | } 26 | 27 | public boolean containsListener(Listener listener) { 28 | return firmata.containsListener(listener); 29 | } 30 | 31 | public void clearListeners() { 32 | firmata.clearListeners(); 33 | } 34 | 35 | private Map portValues = new HashMap(); 36 | 37 | public Map getPortValues() { 38 | return portValues; 39 | } 40 | 41 | public void clear() { 42 | portValues.clear(); 43 | } 44 | 45 | public void send(Message message) throws SerialException { 46 | firmata.send(message); 47 | 48 | if (message instanceof DigitalMessage) { 49 | DigitalMessage digitalMessage = (DigitalMessage) message; 50 | portValues.put(digitalMessage.getPort(), digitalMessage.getValue()); 51 | } 52 | } 53 | 54 | public void onDataReceived(int incomingByte) { 55 | firmata.onDataReceived(incomingByte); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/DirectionMessageFilter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * Filter to filter only messages which have according direction property 5 | */ 6 | public class DirectionMessageFilter implements IMessageFilter { 7 | 8 | private DirectionMessagePropertyManager propertyManager; 9 | 10 | public DirectionMessageFilter(DirectionMessagePropertyManager propertyManager) { 11 | this.propertyManager = propertyManager; 12 | } 13 | 14 | public boolean isAllowed(MessageWithProperties data) { 15 | return (propertyManager.get(data).equals(propertyManager.isIncoming())); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/DirectionMessagePropertyManager.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * Direction for the message: incoming/outcoming 5 | */ 6 | public class DirectionMessagePropertyManager extends MessagePropertyManager { 7 | 8 | private static final String KEY = "isIncoming"; 9 | 10 | private boolean isIncoming; 11 | 12 | public boolean isIncoming() { 13 | return isIncoming; 14 | } 15 | 16 | public DirectionMessagePropertyManager(boolean isIncoming) { 17 | super(KEY); 18 | this.isIncoming = isIncoming; 19 | } 20 | 21 | @Override 22 | protected Boolean createProperty() { 23 | return isIncoming; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/IMessageFilter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * Filters messages 5 | */ 6 | public interface IMessageFilter { 7 | 8 | /** 9 | * Return true if message is allowed and should not be filtered 10 | */ 11 | boolean isAllowed(MessageWithProperties data); 12 | } 13 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/IMessagePropertyManager.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Property for the message 7 | */ 8 | public interface IMessagePropertyManager { 9 | ConcretePropertyClass get(MessageWithProperties data); 10 | void set(MessageWithProperties data); 11 | } 12 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/MessagePropertyManager.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * Abstract message property 5 | */ 6 | public abstract class MessagePropertyManager 7 | implements IMessagePropertyManager { 8 | 9 | private String key; 10 | 11 | public MessagePropertyManager(String key) { 12 | this.key = key; 13 | } 14 | 15 | public ConcretePropertyClass get(MessageWithProperties data) { 16 | return (ConcretePropertyClass)data.getProperty(key); 17 | } 18 | 19 | public void set(MessageWithProperties data) { 20 | ConcretePropertyClass property = createProperty(); 21 | data.setProperty(key, property); 22 | } 23 | 24 | /** 25 | * set concrete property 26 | */ 27 | protected abstract ConcretePropertyClass createProperty(); 28 | } 29 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/MessageWithProperties.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import name.antonsmirnov.firmata.message.Message; 4 | 5 | import java.io.Serializable; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Message with associated properties 11 | */ 12 | public class MessageWithProperties implements Serializable { 13 | 14 | public MessageWithProperties(Message message) { 15 | setMessage(message); 16 | } 17 | 18 | private Message message; 19 | 20 | public Message getMessage() { 21 | return message; 22 | } 23 | 24 | public void setMessage(Message message) { 25 | this.message = message; 26 | } 27 | 28 | private Map properties = new HashMap(); 29 | 30 | public Object getProperty(String key) { 31 | return properties.get(key); 32 | } 33 | 34 | public void setProperty(String key, Object property) { 35 | properties.put(key, property); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return message.toString() + " -> " + properties.toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/MessagesHistoryWrapper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.*; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import java.util.*; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | 10 | /** 11 | * Wrapper which remembers sent and received commands 12 | * 13 | * Every message stores map of properties. 14 | * Concrete IMessageProperty inheritor sets and gets concrete property 15 | */ 16 | public class MessagesHistoryWrapper implements IFirmata, IFirmata.Listener { 17 | 18 | private IMessagePropertyManager[] propertyManagers; 19 | 20 | public MessagesHistoryWrapper(IFirmata firmata, IMessagePropertyManager... propertyManagers) { 21 | this.firmata = firmata; 22 | this.propertyManagers = propertyManagers; 23 | firmata.addListener(this); 24 | 25 | clear(); 26 | } 27 | 28 | private IFirmata firmata; 29 | 30 | public void addListener(Listener listener) { 31 | firmata.addListener(listener); 32 | } 33 | 34 | public void removeListener(Listener listener) { 35 | firmata.removeListener(listener); 36 | } 37 | 38 | public boolean containsListener(Listener listener) { 39 | return firmata.containsListener(listener); 40 | } 41 | 42 | public void clearListeners() { 43 | firmata.clearListeners(); 44 | } 45 | 46 | private List messages = new ArrayList(); 47 | 48 | private DirectionMessagePropertyManager receivedPropertyManager = new DirectionMessagePropertyManager(true); 49 | private IMessageFilter receivedFilter = new DirectionMessageFilter(receivedPropertyManager); 50 | 51 | private DirectionMessagePropertyManager sentPropertyManager = new DirectionMessagePropertyManager(false); 52 | private IMessageFilter sentFilter = new DirectionMessageFilter(sentPropertyManager); 53 | 54 | protected void addCommonProperties(MessageWithProperties data) { 55 | for (IMessagePropertyManager eachPropertyManager : propertyManagers) 56 | eachPropertyManager.set(data); 57 | } 58 | 59 | protected void rememberReceivedMessage(Message message) { 60 | MessageWithProperties newData = new MessageWithProperties(message); 61 | 62 | // common properties 63 | addCommonProperties(newData); 64 | 65 | // 'incoming' property 66 | receivedPropertyManager.set(newData); 67 | 68 | messages.add(newData); 69 | } 70 | 71 | private void rememberSentMessage(Message message) { 72 | MessageWithProperties newData = new MessageWithProperties(message); 73 | 74 | // common properties 75 | addCommonProperties(newData); 76 | 77 | // 'incoming' property 78 | sentPropertyManager.set(newData); 79 | 80 | messages.add(newData); 81 | } 82 | 83 | private IMessageFilter stubMessageFilter = new StubMessageFilter(); 84 | 85 | /** 86 | * Get all messages 87 | * @return messages 88 | */ 89 | public List getMessages() { 90 | return getMessages(stubMessageFilter); 91 | } 92 | 93 | /** 94 | * Get filtered messages 95 | * @return messages 96 | */ 97 | public List getMessages(IMessageFilter filter) { 98 | List filteredMessages = new CopyOnWriteArrayList(); 99 | 100 | for (MessageWithProperties eachMessage : messages) 101 | if (filter.isAllowed(eachMessage)) 102 | filteredMessages.add(eachMessage); 103 | 104 | return filteredMessages; 105 | } 106 | 107 | /** 108 | * Get received messages 109 | * @return received messages 110 | */ 111 | public List getReceivedMessages() { 112 | return getMessages(receivedFilter); 113 | } 114 | 115 | /** 116 | * Get sent messages 117 | * @return sent messages 118 | */ 119 | public List getSentMessages() { 120 | return getMessages(sentFilter); 121 | } 122 | 123 | /** 124 | * Get last received message 125 | * @return received message or NULL 126 | */ 127 | public MessageWithProperties getLastReceivedMessageWithProperties() { 128 | List receivedMessages = getReceivedMessages(); 129 | return receivedMessages.size() > 0 130 | ? receivedMessages.get(receivedMessages.size() - 1) 131 | : null; 132 | } 133 | 134 | /** 135 | * Clear history 136 | * (should be invoked on serial.stop()) 137 | */ 138 | public void clear() { 139 | messages.clear(); 140 | } 141 | 142 | public void onAnalogMessageReceived(AnalogMessage message) { 143 | rememberReceivedMessage(message); 144 | } 145 | 146 | public void onDigitalMessageReceived(DigitalMessage message) { 147 | rememberReceivedMessage(message); 148 | } 149 | 150 | public void onFirmwareVersionMessageReceived(FirmwareVersionMessage message) { 151 | rememberReceivedMessage(message); 152 | } 153 | 154 | public void onProtocolVersionMessageReceived(ProtocolVersionMessage message) { 155 | rememberReceivedMessage(message); 156 | } 157 | 158 | public void onSysexMessageReceived(SysexMessage message) { 159 | rememberReceivedMessage(message); 160 | } 161 | 162 | public void onStringSysexMessageReceived(StringSysexMessage message) { 163 | rememberReceivedMessage(message); 164 | } 165 | 166 | public void onI2cMessageReceived(I2cReplyMessage message) { 167 | rememberReceivedMessage(message); 168 | } 169 | 170 | public void onUnknownByteReceived(int byteValue) { 171 | // nothing 172 | } 173 | 174 | public void send(Message message) throws SerialException { 175 | firmata.send(message); 176 | 177 | rememberSentMessage(message); 178 | } 179 | 180 | public void onDataReceived(int incomingByte) { 181 | firmata.onDataReceived(incomingByte); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/OrFilter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * At least one filter should allow data 5 | */ 6 | public class OrFilter implements IMessageFilter { 7 | 8 | private IMessageFilter[] filters; 9 | 10 | public OrFilter(IMessageFilter... filters) { 11 | this.filters = filters; 12 | } 13 | 14 | public boolean isAllowed(MessageWithProperties data) { 15 | for (IMessageFilter eachFilter : filters) 16 | if (eachFilter.isAllowed(data)) 17 | return true; 18 | 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/PinModeWrapper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.Message; 5 | import name.antonsmirnov.firmata.message.SetPinModeMessage; 6 | import name.antonsmirnov.firmata.serial.SerialException; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * Wrapper which remembers pin modes 13 | */ 14 | public class PinModeWrapper implements IFirmata { 15 | 16 | /** 17 | * Set pin mode event listener 18 | */ 19 | public static interface Listener { 20 | void onSetPinMode(int pin, int mode); 21 | } 22 | 23 | private IFirmata firmata; 24 | private Listener listener; 25 | 26 | public void addListener(IFirmata.Listener listener) { 27 | firmata.addListener(listener); 28 | } 29 | 30 | public void removeListener(IFirmata.Listener listener) { 31 | firmata.removeListener(listener); 32 | } 33 | 34 | public boolean containsListener(IFirmata.Listener listener) { 35 | return firmata.containsListener(listener); 36 | } 37 | 38 | public void clearListeners() { 39 | firmata.clearListeners(); 40 | } 41 | 42 | // pins configuration 43 | private Map pinsConfig = new HashMap(); 44 | 45 | /** 46 | * Get remembered pin modes 47 | * @return pin modes 48 | */ 49 | public Map getPinsConfig() { 50 | return pinsConfig; 51 | } 52 | 53 | public PinModeWrapper(IFirmata firmata) { 54 | this(firmata, null); 55 | } 56 | 57 | /** 58 | * Constructor 59 | * @param firmata wrapped firmata 60 | * @param listener set pin mode event listener 61 | */ 62 | public PinModeWrapper(IFirmata firmata, Listener listener) { 63 | this.firmata = firmata; 64 | this.listener = listener; 65 | 66 | clear(); 67 | } 68 | 69 | /** 70 | * Clear pins config 71 | * (should be invoked on serial.stop()) 72 | */ 73 | public void clear() { 74 | pinsConfig.clear(); 75 | } 76 | 77 | public void send(Message message) throws SerialException { 78 | firmata.send(message); 79 | 80 | if (message instanceof SetPinModeMessage) { 81 | SetPinModeMessage setPinModeMessage = (SetPinModeMessage) message; 82 | 83 | // remember 84 | pinsConfig.put(setPinModeMessage.getPin(), setPinModeMessage.getMode()); 85 | 86 | // fire event 87 | if (listener != null) 88 | listener.onSetPinMode(setPinModeMessage.getPin(), setPinModeMessage.getMode()); 89 | } 90 | } 91 | 92 | public void onDataReceived(int incomingByte) { 93 | firmata.onDataReceived(incomingByte); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/ReportAutostopWrapper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | import name.antonsmirnov.firmata.IFirmata; 4 | import name.antonsmirnov.firmata.message.*; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | /** 11 | * Disables pin state reporting after first state message received 12 | */ 13 | public class ReportAutostopWrapper extends IFirmata.StubListener implements IFirmata { 14 | 15 | private IFirmata firmata; 16 | 17 | public ReportAutostopWrapper(IFirmata firmata) { 18 | this.firmata = firmata; 19 | } 20 | 21 | public void addListener(Listener listener) { 22 | firmata.addListener(listener); 23 | } 24 | 25 | public void removeListener(Listener listener) { 26 | firmata.removeListener(listener); 27 | } 28 | 29 | public boolean containsListener(Listener listener) { 30 | return firmata.containsListener(listener); 31 | } 32 | 33 | public void clearListeners() { 34 | firmata.clearListeners(); 35 | } 36 | 37 | // active reporting pins 38 | private Set digitalReporting = new HashSet(); // integer - port 39 | private Set analogReporting = new HashSet(); // integer - pin 40 | 41 | public void send(Message message) throws SerialException { 42 | firmata.send(message); 43 | 44 | // digital 45 | if (message instanceof ReportDigitalPortMessage) { 46 | ReportDigitalPortMessage digitalMessage = (ReportDigitalPortMessage) message; 47 | if (digitalMessage.isEnable()) 48 | digitalReporting.add(digitalMessage.getPort()); 49 | } 50 | 51 | // analog 52 | if (message instanceof ReportAnalogPinMessage) { 53 | ReportAnalogPinMessage analogMessage = (ReportAnalogPinMessage) message; 54 | if (analogMessage.isEnable()) 55 | analogReporting.add(analogMessage.getPin()); 56 | } 57 | } 58 | 59 | @Override 60 | public void onAnalogMessageReceived(AnalogMessage message) { 61 | if (analogReporting.contains(message.getPin())) { 62 | analogReporting.remove(message.getPin()); 63 | 64 | disableAnalogReporting(message); 65 | } 66 | } 67 | 68 | private void disableAnalogReporting(AnalogMessage message) { 69 | try { 70 | firmata.send(new ReportAnalogPinMessage(message.getPin(), false)); 71 | } catch (SerialException e) { 72 | // TODO: fix (bad) 73 | } 74 | } 75 | 76 | @Override 77 | public void onDigitalMessageReceived(DigitalMessage message) { 78 | if (digitalReporting.contains(message.getPort())) { 79 | digitalReporting.remove(message.getPort()); 80 | 81 | disableDigitalReporting(message); 82 | } 83 | } 84 | 85 | private void disableDigitalReporting(DigitalMessage message) { 86 | try { 87 | firmata.send(new ReportDigitalPortMessage(message.getPort(), false)); 88 | } catch (SerialException e) { 89 | // TODO: fix (bad) 90 | } 91 | } 92 | 93 | public void clear() { 94 | digitalReporting.clear(); 95 | analogReporting.clear(); 96 | } 97 | 98 | public void onDataReceived(int incomingByte) { 99 | firmata.onDataReceived(incomingByte); 100 | } 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/wrapper/StubMessageFilter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.wrapper; 2 | 3 | /** 4 | * Filter stub : all messages are allowed 5 | */ 6 | public class StubMessageFilter implements IMessageFilter { 7 | 8 | public boolean isAllowed(MessageWithProperties data) { 9 | return true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/AnalogMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.AnalogMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.*; 8 | 9 | /** 10 | * MessageWriter for AnalogMessage 11 | */ 12 | public class AnalogMessageWriter implements IMessageWriter { 13 | 14 | public static final int COMMAND = 0xE0; 15 | 16 | public void write(AnalogMessage message, ISerial serial) throws SerialException { 17 | serial.write(COMMAND | ENCODE_CHANNEL(message.getPin())); 18 | serial.write(LSB(message.getValue())); 19 | serial.write(MSB(message.getValue())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/DigitalMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.DigitalMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.*; 8 | 9 | /** 10 | * MessageWriter for DigitalMessage 11 | */ 12 | public class DigitalMessageWriter implements IMessageWriter { 13 | 14 | public static final int COMMAND = 0x90; 15 | 16 | public void write(DigitalMessage message, ISerial serial) throws SerialException { 17 | serial.write(COMMAND | ENCODE_CHANNEL(message.getPort())); 18 | serial.write(LSB(message.getValue())); 19 | serial.write(MSB(message.getValue())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/I2cConfigMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.I2cConfigMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.LSB; 8 | import static name.antonsmirnov.firmata.BytesHelper.MSB; 9 | 10 | /** 11 | * MessageWriter for I2cConfigMessage 12 | */ 13 | public class I2cConfigMessageWriter extends SysexMessageWriter { 14 | 15 | @Override 16 | protected void writeData(I2cConfigMessage message, ISerial serial) throws SerialException { 17 | // can not use super.writeData() because it works with String 18 | writeI2cConfigData(message, serial); 19 | } 20 | 21 | private void writeI2cConfigData(I2cConfigMessage message, ISerial serial) throws SerialException { 22 | byte[] buffer = new byte[3]; 23 | 24 | buffer[0] = (byte)(message.isOn() ? 1 : 0); 25 | buffer[1] = (byte)LSB(message.getDelay()); 26 | buffer[2] = (byte)MSB(message.getDelay()); 27 | 28 | serial.write(buffer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/I2cRequestMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.I2cRequestMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.*; 8 | 9 | /** 10 | * MessageWriter for I2cRequestMessage 11 | */ 12 | public class I2cRequestMessageWriter extends SysexMessageWriter { 13 | 14 | @Override 15 | protected void writeData(ConcreteRequestMessage message, ISerial serial) throws SerialException { 16 | // can not use super.writeData() because it works with String 17 | writeI2cRequestData(message, serial); 18 | } 19 | 20 | private void writeI2cRequestData(ConcreteRequestMessage message, ISerial serial) throws SerialException { 21 | byte[] buffer = new byte[2]; 22 | 23 | buffer[0] = (byte)LSB(message.getSlaveAddress()); 24 | int modeByte = 0; 25 | 26 | modeByte = setBit(modeByte, 7, false); // {7: always 0} 27 | modeByte = setBit(modeByte, 6, false); // {6: reserved} 28 | modeByte = setBit(modeByte, 5, message.isTenBitsMode()); // {5: address mode, 1 means 10-bit mode} 29 | 30 | // 4-3 bits are modes 31 | modeByte |= (byte)(message.getMode().getValue() << 3); // {4-3: read/write, 00 => write, 01 => read once, 10 => read continuously, 11 => stop reading} 32 | 33 | // 0-2 bits 34 | if (message.isTenBitsMode()) 35 | modeByte |= MSB(message.getSlaveAddress() & 7); // {2-0: slave address MSB in 10-bit mode, not used in 7-bit mode} 36 | 37 | buffer[1] = (byte)modeByte; 38 | serial.write(buffer); 39 | 40 | int[] binaryData = message.getBinaryData(); 41 | if (binaryData != null) { 42 | byte[] dataBuffer = ENCODE_INT_ARRAY(binaryData); 43 | serial.write(dataBuffer); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/IMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.Message; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | /** 8 | * Message writer 9 | */ 10 | public interface IMessageWriter { 11 | 12 | /** 13 | * Write command to Serial 14 | */ 15 | void write(ConcreteMessage message, ISerial serial) throws SerialException; 16 | } 17 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/ReportAnalogPinMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.ReportAnalogPinMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.ENCODE_CHANNEL; 8 | 9 | /** 10 | * MessageWriter for ReportAnalogPinMessage 11 | */ 12 | public class ReportAnalogPinMessageWriter implements IMessageWriter { 13 | 14 | public static final int COMMAND = 0xC0; 15 | 16 | public void write(ReportAnalogPinMessage message, ISerial serial) throws SerialException { 17 | serial.write(COMMAND | ENCODE_CHANNEL(message.getPin())); 18 | serial.write(message.isEnable() ? 1 : 0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/ReportDigitalPortMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.ReportDigitalPortMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.ENCODE_CHANNEL; 8 | 9 | /** 10 | * MessageWriter for ReportDigitalPortMessage 11 | */ 12 | public class ReportDigitalPortMessageWriter implements IMessageWriter { 13 | 14 | public static final int COMMAND = 0xD0; 15 | 16 | public void write(ReportDigitalPortMessage message, ISerial serial) throws SerialException { 17 | serial.write(COMMAND | ENCODE_CHANNEL(message.getPort())); 18 | serial.write(message.isEnable() ? 1 : 0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/ReportProtocolVersionMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.ReportProtocolVersionMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | /** 8 | * MessageWriter for ReportProtocolVersionMessage 9 | */ 10 | public class ReportProtocolVersionMessageWriter implements IMessageWriter { 11 | 12 | public static final int COMMAND = 0xF9; 13 | 14 | public void write(ReportProtocolVersionMessage message, ISerial serial) throws SerialException { 15 | serial.write(COMMAND); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/SamplingIntervalMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.SamplingIntervalMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.*; 8 | 9 | /** 10 | * MessageWriter for SamplingIntervalMessage 11 | */ 12 | public class SamplingIntervalMessageWriter extends SysexMessageWriter { 13 | 14 | @Override 15 | protected void writeData(SamplingIntervalMessage message, ISerial serial) throws SerialException { 16 | // can not use super.writeData() because it works with String 17 | writeIntervalData(message, serial); 18 | } 19 | 20 | private void writeIntervalData(SamplingIntervalMessage message, ISerial serial) throws SerialException { 21 | byte[] buffer = new byte[2]; 22 | 23 | buffer[0] = (byte)LSB(message.getInterval()); 24 | buffer[1] = (byte)MSB(message.getInterval()); 25 | 26 | serial.write(buffer); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/ServoConfigMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.ServoConfigMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.*; 8 | 9 | /** 10 | * MessageWriter for ServoConfigMessage 11 | */ 12 | public class ServoConfigMessageWriter extends SysexMessageWriter { 13 | 14 | @Override 15 | protected void writeData(ServoConfigMessage message, ISerial serial) throws SerialException { 16 | // can not use super.writeData() because it works with String 17 | writeServoData(message, serial); 18 | } 19 | 20 | private void writeServoData(ServoConfigMessage message, ISerial serial) throws SerialException { 21 | byte[] buffer = new byte[7]; 22 | buffer[0] = (byte)message.getPin(); 23 | 24 | buffer[1] = (byte)LSB(message.getMinPulse()); 25 | buffer[2] = (byte)MSB(message.getMinPulse()); 26 | 27 | buffer[3] = (byte)LSB(message.getMaxPulse()); 28 | buffer[4] = (byte)MSB(message.getMaxPulse()); 29 | 30 | buffer[5] = (byte)LSB(message.getAngle()); 31 | buffer[6] = (byte)MSB(message.getAngle()); 32 | 33 | serial.write(buffer); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/SetPinModeMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.SetPinModeMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | /** 8 | * MessageWriter for SetPinModeMessage 9 | */ 10 | public class SetPinModeMessageWriter implements IMessageWriter { 11 | 12 | public static final int COMMAND = 0xF4; 13 | 14 | public void write(SetPinModeMessage message, ISerial serial) throws SerialException { 15 | serial.write(COMMAND); 16 | serial.write(message.getPin()); 17 | serial.write(message.getMode()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/SysexMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.SysexMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | import static name.antonsmirnov.firmata.BytesHelper.ENCODE_STRING; 8 | 9 | /** 10 | * MessageWriter for SysexMessage and inheritors 11 | */ 12 | public class SysexMessageWriter implements IMessageWriter { 13 | 14 | public static final int COMMAND_START = 0xF0; 15 | public static final int COMMAND_END = 0xF7; 16 | 17 | public void write(ConcreteSysexMessage message, ISerial serial) throws SerialException { 18 | serial.write(COMMAND_START); 19 | writeCommand(message, serial); 20 | writeData(message, serial); 21 | serial.write(COMMAND_END); 22 | } 23 | 24 | protected void writeCommand(ConcreteSysexMessage message, ISerial serial) throws SerialException { 25 | serial.write(message.getCommand()); 26 | } 27 | 28 | protected void writeData(ConcreteSysexMessage message, ISerial serial) throws SerialException { 29 | if (message.getData() != null) { 30 | byte[] dataBytes = ENCODE_STRING(message.getData()); 31 | serial.write(dataBytes); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Firmata/src/main/java/name/antonsmirnov/firmata/writer/SystemResetMessageWriter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.writer; 2 | 3 | import name.antonsmirnov.firmata.message.SystemResetMessage; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | 7 | /** 8 | * MessageWriter for SystemResetMessage 9 | */ 10 | public class SystemResetMessageWriter implements IMessageWriter { 11 | 12 | public static final int COMMAND = 0xFF; 13 | 14 | public void write(SystemResetMessage message, ISerial serial) throws SerialException { 15 | serial.write(COMMAND); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /FirmataTests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | name.antonsmirnov.firmata 9 | parent 10 | 1.0 11 | 12 | 13 | name.antonsmirnov.firmata 14 | FirmataTests 15 | 2.4 16 | FirmataTests 17 | 18 | 19 | 20 | Anton Smirnov 21 | dev@antonsmirnov.name 22 | 23 | 24 | 25 | Firmata real hardware interaction Tests 26 | 27 | 28 | 29 | name.antonsmirnov.firmata 30 | Firmata 31 | 2.6 32 | 33 | 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 1.6.4 39 | test 40 | 41 | 42 | 43 | name.antonsmirnov.firmata.serial 44 | IndepProcessingSerial 45 | 1.2 46 | test 47 | 48 | 49 | 50 | junit 51 | junit 52 | 4.8.1 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-surefire-plugin 62 | 2.10 63 | 64 | 65 | 66 | **/*HardwareTest.java 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /FirmataTests/src/main/java/name/antonsmirnov/firmata/FirmataWaiter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | import name.antonsmirnov.firmata.message.Message; 4 | import name.antonsmirnov.firmata.wrapper.MessagesHistoryWrapper; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * Wait for incoming message from firmata 10 | */ 11 | public class FirmataWaiter { 12 | 13 | private Logger log = LoggerFactory.getLogger(getClass()); 14 | 15 | private final int WAIT_INCREMENT = 10; // ms 16 | 17 | private MessagesHistoryWrapper firmata; 18 | 19 | public FirmataWaiter(IFirmata firmata) { 20 | this.firmata = new MessagesHistoryWrapper(firmata); 21 | } 22 | 23 | private int waited; 24 | 25 | public void waitSeconds(int seconds, Class messageClass) throws WaitException { 26 | waited = 0; 27 | int wait = seconds * 1000; // sec -> ms 28 | 29 | log.info("Started waiting {} ...", messageClass != null 30 | ? "for " + messageClass.getSimpleName() 31 | : ""); 32 | 33 | while (true) { 34 | try { 35 | Thread.sleep(WAIT_INCREMENT); 36 | } catch (InterruptedException e) { 37 | throw new RuntimeException(e); 38 | } 39 | 40 | if (firmata.getLastReceivedMessageWithProperties() == null 41 | || 42 | ( 43 | firmata.getLastReceivedMessageWithProperties() != null 44 | && 45 | messageClass != null 46 | && 47 | !firmata.getLastReceivedMessageWithProperties().getMessage().getClass().equals(messageClass) 48 | )) { 49 | waited += WAIT_INCREMENT; 50 | if (waited > wait) 51 | throw new WaitException(messageClass); 52 | } else { 53 | break; 54 | } 55 | } 56 | } 57 | 58 | public void waitSeconds(int seconds) throws WaitException { 59 | waitSeconds(seconds, null); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /FirmataTests/src/main/java/name/antonsmirnov/firmata/OriginalFirmata.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | import name.antonsmirnov.firmata.serial.ISerial; 4 | import name.antonsmirnov.firmata.serial.SerialException; 5 | 6 | /** 7 | * Almost original Firmata (Arduino.java) 1.5.1 8 | */ 9 | public class OriginalFirmata { 10 | 11 | private ISerial serial; 12 | 13 | public ISerial getSerial() { 14 | return serial; 15 | } 16 | 17 | public void setSerial(ISerial serial) { 18 | this.serial = serial; 19 | } 20 | 21 | public OriginalFirmata(ISerial serial) { 22 | setSerial(serial); 23 | } 24 | 25 | /** 26 | * Constant to set a pin to input mode (in a call to pinMode()). 27 | */ 28 | public static final int INPUT = 0; 29 | /** 30 | * Constant to set a pin to output mode (in a call to pinMode()). 31 | */ 32 | public static final int OUTPUT = 1; 33 | /** 34 | * Constant to set a pin to analog mode (in a call to pinMode()). 35 | */ 36 | public static final int ANALOG = 2; 37 | /** 38 | * Constant to set a pin to PWM mode (in a call to pinMode()). 39 | */ 40 | public static final int PWM = 3; 41 | /** 42 | * Constant to set a pin to servo mode (in a call to pinMode()). 43 | */ 44 | public static final int SERVO = 4; 45 | /** 46 | * Constant to set a pin to shiftIn/shiftOut mode (in a call to pinMode()). 47 | */ 48 | public static final int SHIFT = 5; 49 | /** 50 | * Constant to set a pin to I2C mode (in a call to pinMode()). 51 | */ 52 | public static final int I2C = 6; 53 | 54 | /** 55 | * Constant to send a high value (+5 volts) to a pin (in a call to 56 | * digitalWrite()). 57 | */ 58 | public static final int LOW = 0; 59 | /** 60 | * Constant to send a low value (0 volts) to a pin (in a call to 61 | * digitalWrite()). 62 | */ 63 | public static final int HIGH = 1; 64 | 65 | private final int MAX_DATA_BYTES = 32; 66 | 67 | private final int DIGITAL_MESSAGE = 0x90; // send data for a digital port 68 | private final int ANALOG_MESSAGE = 0xE0; // send data for an analog pin (or PWM) 69 | private final int REPORT_ANALOG = 0xC0; // enable analog input by pin # 70 | private final int REPORT_DIGITAL = 0xD0; // enable digital input by port 71 | private final int SET_PIN_MODE = 0xF4; // set a pin to INPUT/OUTPUT/PWM/etc 72 | private final int REPORT_VERSION = 0xF9; // report firmware version 73 | private final int SYSTEM_RESET = 0xFF; // reset from MIDI 74 | private final int START_SYSEX = 0xF0; // start a MIDI SysEx message 75 | private final int END_SYSEX = 0xF7; // end a MIDI SysEx message 76 | 77 | int waitForData = 0; 78 | int executeMultiByteCommand = 0; 79 | int multiByteChannel = 0; 80 | int[] storedInputData = new int[MAX_DATA_BYTES]; 81 | boolean parsingSysex; 82 | int sysexBytesRead; 83 | 84 | int[] digitalOutputData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 85 | int[] digitalInputData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 86 | int[] analogInputData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 87 | 88 | int majorVersion = 0; 89 | int minorVersion = 0; 90 | 91 | /** 92 | * Returns the last known value read from the digital pin: HIGH or LOW. 93 | * 94 | * @param pin the digital pin whose value should be returned (from 2 to 13, 95 | * since pins 0 and 1 are used for serial communication) 96 | */ 97 | public int digitalRead(int pin) { 98 | return (digitalInputData[pin >> 3] >> (pin & 0x07)) & 0x01; 99 | } 100 | 101 | /** 102 | * Returns the last known value read from the analog pin: 0 (0 volts) to 103 | * 1023 (5 volts). 104 | * 105 | * @param pin the analog pin whose value should be returned (from 0 to 5) 106 | */ 107 | public int analogRead(int pin) { 108 | return analogInputData[pin]; 109 | } 110 | 111 | /** 112 | * Set a digital pin to input or output mode. 113 | * 114 | * @param pin the pin whose mode to set (from 2 to 13) 115 | * @param mode either Arduino.INPUT or Arduino.OUTPUT 116 | */ 117 | public void pinMode(int pin, int mode) throws SerialException { 118 | serial.write(SET_PIN_MODE); 119 | serial.write(pin); 120 | serial.write(mode); 121 | } 122 | 123 | /** 124 | * Write to a digital pin (the pin must have been put into output mode with 125 | * pinMode()). 126 | * 127 | * @param pin the pin to send to (from 2 to 13) 128 | * @param value the value to send: Arduino.LOW (0 volts) or Arduino.HIGH 129 | * (5 volts) 130 | */ 131 | public void digitalWrite(int pin, int value) throws SerialException { 132 | int portNumber = (pin >> 3) & 0x0F; 133 | 134 | if (value == 0) 135 | digitalOutputData[portNumber] &= ~(1 << (pin & 0x07)); 136 | else 137 | digitalOutputData[portNumber] |= (1 << (pin & 0x07)); 138 | 139 | serial.write(DIGITAL_MESSAGE | portNumber); 140 | serial.write(digitalOutputData[portNumber] & 0x7F); 141 | serial.write(digitalOutputData[portNumber] >> 7); 142 | } 143 | 144 | /** 145 | * Write an analog value (PWM-wave) to a digital pin. 146 | * 147 | * @param pin the pin to send to (must be 9, 10, or 11, as those are they 148 | * only ones which support hardware pwm) 149 | * @param value: 0 being the lowest (always off), and 255 the highest 150 | * (always on) 151 | */ 152 | public void analogWrite(int pin, int value) throws SerialException { 153 | pinMode(pin, PWM); 154 | serial.write(ANALOG_MESSAGE | (pin & 0x0F)); 155 | serial.write(value & 0x7F); 156 | serial.write(value >> 7); 157 | } 158 | 159 | private void setDigitalInputs(int portNumber, int portData) { 160 | digitalInputData[portNumber] = portData; 161 | } 162 | 163 | private void setAnalogInput(int pin, int value) { 164 | analogInputData[pin] = value; 165 | } 166 | 167 | private void setVersion(int majorVersion, int minorVersion) { 168 | this.majorVersion = majorVersion; 169 | this.minorVersion = minorVersion; 170 | } 171 | 172 | public void processByte(int inputData) { 173 | int command; 174 | 175 | if (parsingSysex) { 176 | if (inputData == END_SYSEX) { 177 | parsingSysex = false; 178 | //processSysexMessage(); 179 | } else { 180 | storedInputData[sysexBytesRead] = inputData; 181 | sysexBytesRead++; 182 | } 183 | } else if (waitForData > 0 && inputData < 128) { 184 | waitForData--; 185 | storedInputData[waitForData] = inputData; 186 | 187 | if (executeMultiByteCommand != 0 && waitForData == 0) { 188 | //we got everything 189 | switch (executeMultiByteCommand) { 190 | case DIGITAL_MESSAGE: 191 | setDigitalInputs(multiByteChannel, (storedInputData[0] << 7) + storedInputData[1]); 192 | break; 193 | case ANALOG_MESSAGE: 194 | setAnalogInput(multiByteChannel, (storedInputData[0] << 7) + storedInputData[1]); 195 | break; 196 | case REPORT_VERSION: 197 | setVersion(storedInputData[1], storedInputData[0]); 198 | break; 199 | } 200 | } 201 | } else { 202 | if (inputData < 0xF0) { 203 | command = inputData & 0xF0; 204 | multiByteChannel = inputData & 0x0F; 205 | } else { 206 | command = inputData; 207 | // commands in the 0xF* range don't use channel data 208 | } 209 | switch (command) { 210 | case DIGITAL_MESSAGE: 211 | case ANALOG_MESSAGE: 212 | case REPORT_VERSION: 213 | waitForData = 2; 214 | executeMultiByteCommand = command; 215 | break; 216 | } 217 | } 218 | } 219 | 220 | private void processInput() throws SerialException { 221 | processByte(serial.read()); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /FirmataTests/src/main/java/name/antonsmirnov/firmata/TestSerial.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | import name.antonsmirnov.firmata.serial.ISerial; 4 | import name.antonsmirnov.firmata.serial.ISerialListener; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.ConcurrentLinkedQueue; 11 | 12 | /** 13 | * Test ISerial implementation 14 | * (for usage in tests) 15 | */ 16 | public class TestSerial implements ISerial { 17 | 18 | public TestSerial() { 19 | initInputStream(); 20 | initOutputStream(); 21 | } 22 | 23 | private List listeners = new ArrayList(); 24 | 25 | public void addListener(ISerialListener listener) { 26 | listeners.add(listener); 27 | } 28 | 29 | public void removeListener(ISerialListener listener) { 30 | listeners.remove(listener); 31 | } 32 | 33 | private void initOutputStream() { 34 | outputStream = new ByteArrayOutputStream(); 35 | } 36 | 37 | private void initInputStream() { 38 | inputStream = new ConcurrentLinkedQueue(); 39 | } 40 | 41 | private ConcurrentLinkedQueue inputStream = new ConcurrentLinkedQueue(); 42 | 43 | public ByteArrayOutputStream getOutputStream() { 44 | return outputStream; 45 | } 46 | 47 | private ByteArrayOutputStream outputStream; 48 | 49 | public void start() { 50 | } 51 | 52 | public void stop() { 53 | } 54 | 55 | public boolean isStopping() { 56 | return false; 57 | } 58 | 59 | public int available() { 60 | return inputStream.size(); 61 | } 62 | 63 | public void clear() { 64 | initInputStream(); 65 | initOutputStream(); 66 | } 67 | 68 | public int read() { 69 | return inputStream.poll(); 70 | } 71 | 72 | public void write(int what) { 73 | outputStream.write(what); 74 | } 75 | 76 | public void write(byte[] bytes) { 77 | try { 78 | outputStream.write(bytes); 79 | } catch (IOException e) { 80 | throw new RuntimeException(e); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /FirmataTests/src/main/java/name/antonsmirnov/firmata/WaitException.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata; 2 | 3 | import name.antonsmirnov.firmata.message.Message; 4 | 5 | /** 6 | * Wait exception for FirmataWaiter 7 | */ 8 | public class WaitException extends Exception { 9 | 10 | private Class messageClass; 11 | 12 | public Class getMessageClass() { 13 | return messageClass; 14 | } 15 | 16 | public WaitException(Class messageClass) { 17 | super("No expected incoming message"); 18 | this.messageClass = messageClass; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FirmataTests/src/test/java/name/antonsmirnov/firmata/tests/AnalogMessageTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.tests; 2 | 3 | import name.antonsmirnov.firmata.message.AnalogMessage; 4 | import name.antonsmirnov.firmata.message.SetPinModeMessage; 5 | import name.antonsmirnov.firmata.serial.SerialException; 6 | import org.junit.Test; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * Test for AnalogMessage 12 | */ 13 | public class AnalogMessageTest extends BaseFirmataTest { 14 | 15 | @Test 16 | // compare original impl output and new impl output 17 | public void testWrite() throws SerialException { 18 | serial.clear(); 19 | 20 | for (int pin = 0; pin < PIN_MAX; pin++) 21 | for (int value = 0; value < BYTE_MAX; value++) { 22 | 23 | // old Firmata impl 24 | serial.clear(); 25 | originalFirmata.analogWrite(pin, value); 26 | final byte[] oldOutput = serial.getOutputStream().toByteArray(); 27 | 28 | // new Firmata impl 29 | serial.clear(); 30 | firmata.send(new SetPinModeMessage(pin, SetPinModeMessage.PIN_MODE.PWM.getMode())); 31 | firmata.send(new AnalogMessage(pin, value)); 32 | final byte[] newOutput = serial.getOutputStream().toByteArray(); 33 | 34 | assertTrue(Arrays.equals(oldOutput, newOutput)); 35 | } 36 | } 37 | 38 | @Test 39 | public void testRead() throws SerialException { 40 | for (int pin = 0; pin < PIN_MAX; pin++) 41 | for (int value = 0; value < BYTE_MAX; value++) { 42 | // create output 43 | serial.clear(); 44 | AnalogMessage outcomingMessage = new AnalogMessage(pin, value); 45 | firmata.send(outcomingMessage); 46 | final byte[] newOutput = serial.getOutputStream().toByteArray(); 47 | 48 | // feed output to input 49 | for (byte eachByte : newOutput) 50 | firmata.onDataReceived(eachByte); 51 | 52 | // compare original command and received command 53 | assertNotNull(historyFirmataWrapper.getLastReceivedMessageWithProperties()); 54 | assertEquals(outcomingMessage, historyFirmataWrapper.getLastReceivedMessageWithProperties().getMessage()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /FirmataTests/src/test/java/name/antonsmirnov/firmata/tests/BaseFirmataTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.tests; 2 | 3 | import junit.framework.TestCase; 4 | import name.antonsmirnov.firmata.*; 5 | import name.antonsmirnov.firmata.IFirmata; 6 | import name.antonsmirnov.firmata.wrapper.MessagesHistoryWrapper; 7 | 8 | /** 9 | * Base Firmata Test 10 | */ 11 | public abstract class BaseFirmataTest extends TestCase { 12 | 13 | protected static final int PIN_MAX = 16; 14 | protected static final int PORT_MAX = 3; 15 | protected static final int BYTE_MAX = 255; 16 | 17 | protected TestSerial serial; 18 | 19 | // original impl 20 | protected OriginalFirmata originalFirmata; 21 | 22 | // new impl 23 | protected Firmata impl; 24 | protected IFirmata firmata; 25 | protected MessagesHistoryWrapper historyFirmataWrapper; 26 | 27 | @Override 28 | protected void setUp() throws Exception { 29 | serial = new TestSerial(); 30 | originalFirmata = new OriginalFirmata(serial); 31 | 32 | impl = new Firmata(serial); 33 | historyFirmataWrapper = new MessagesHistoryWrapper(impl); 34 | 35 | firmata = historyFirmataWrapper; 36 | } 37 | 38 | protected void feedToFirmata(byte[] input) { 39 | for (byte eachByte : input) 40 | firmata.onDataReceived(eachByte); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /FirmataTests/src/test/java/name/antonsmirnov/firmata/tests/BytesHelperTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.tests; 2 | 3 | import junit.framework.TestCase; 4 | import name.antonsmirnov.firmata.BytesHelper; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Tests for BytesHelper 9 | */ 10 | public class BytesHelperTest extends TestCase { 11 | //0 //1 //2 12 | private int[] portByPin = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}; 13 | private int[] pinInPort = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}; 14 | 15 | @Test 16 | public void testPortByPin() { 17 | for (int pin = 0; pin < portByPin.length; pin++) 18 | assertEquals(portByPin[pin], BytesHelper.portByPin(pin)); 19 | } 20 | 21 | @Test 22 | public void testPinInPort() { 23 | for (int pin = 0; pin < pinInPort.length; pin++) 24 | assertEquals(pinInPort[pin], BytesHelper.pinInPort(pin)); 25 | } 26 | 27 | @Test 28 | public void testSetPin_High_One() { 29 | for (int pin = 0; pin < BytesHelper.BITS_IN_BYTE; pin++) { 30 | assertEquals((int)Math.pow(2, pin), BytesHelper.setPin(0, pin, true)); 31 | } 32 | } 33 | 34 | @Test 35 | public void testSetPin_High_All() { 36 | int portValues = 0; 37 | for (int pin = 0; pin < BytesHelper.BITS_IN_BYTE; pin++) { 38 | // accumulating all bits values 39 | int currentBitValue = (int)Math.pow(2, pin); 40 | portValues += currentBitValue; 41 | 42 | assertEquals(portValues, BytesHelper.setPin(portValues, pin, true)); 43 | } 44 | } 45 | 46 | @Test 47 | public void testSetPin_Low_One() { 48 | for (int pin = 0; pin < BytesHelper.BITS_IN_BYTE; pin++) { 49 | assertEquals( 50 | BytesHelper.BYTE_MAX_VALUE - (int)Math.pow(2, pin), 51 | BytesHelper.setPin(BytesHelper.BYTE_MAX_VALUE, pin, false)); 52 | } 53 | } 54 | 55 | @Test 56 | public void testSetPin_Low_All() { 57 | int portValues = BytesHelper.BYTE_MAX_VALUE; 58 | for (int pin = 0; pin < BytesHelper.BITS_IN_BYTE; pin++) { 59 | // accumulating all bits values 60 | int currentBitValue = (int)Math.pow(2, pin); 61 | portValues -= currentBitValue; 62 | 63 | assertEquals(portValues, BytesHelper.setPin(portValues, pin, false)); 64 | } 65 | } 66 | 67 | @Test 68 | public void testPinGet_High_One() { 69 | for (int setPin=0; setPin messages, Message message) { 80 | for (MessageWithProperties eachMessage : messages) 81 | if (eachMessage.getMessage() == message) 82 | return; 83 | 84 | fail("Message not found in history"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /FirmataTests/src/test/java/name/antonsmirnov/firmata/tests/FirmwareVersionMessageTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.tests; 2 | 3 | import name.antonsmirnov.firmata.message.FirmwareVersionMessage; 4 | import name.antonsmirnov.firmata.message.ReportFirmwareVersionMessage; 5 | import name.antonsmirnov.firmata.reader.FirmwareVersionMessageReader; 6 | import name.antonsmirnov.firmata.wrapper.MessageWithProperties; 7 | import name.antonsmirnov.firmata.writer.SysexMessageWriter; 8 | import org.junit.Test; 9 | 10 | import java.nio.ByteBuffer; 11 | 12 | import static name.antonsmirnov.firmata.BytesHelper.ENCODE_STRING; 13 | 14 | /** 15 | * Test for FirmwareVersionMessage 16 | */ 17 | public class FirmwareVersionMessageTest extends BaseFirmataTest { 18 | 19 | private final FirmwareVersionMessage expectedMessage = new FirmwareVersionMessage(1, 2, "abc"); 20 | 21 | private byte[] getInput() { 22 | final byte[] NAME = expectedMessage.getName().getBytes(); 23 | 24 | // prepare input 25 | byte[] input = new byte[5 + NAME.length * 2]; 26 | // 5: start byte + sysex command byte + major byte + minor byte + end byte 27 | // 2: each name byte is encoded into 2 bytes 28 | 29 | int offset = 0; 30 | input[offset++] = (byte) SysexMessageWriter.COMMAND_START; 31 | input[offset++] = (byte)ReportFirmwareVersionMessage.COMMAND; 32 | input[offset++] = (byte)expectedMessage.getMajor(); 33 | input[offset++] = (byte)expectedMessage.getMinor(); 34 | ENCODE_STRING(NAME, ByteBuffer.wrap(input), offset); 35 | offset += 2 * NAME.length; 36 | input[offset] = (byte) SysexMessageWriter.COMMAND_END; 37 | 38 | return input; 39 | } 40 | 41 | @Test 42 | public void testDeserialize() { 43 | byte[] input = getInput(); 44 | 45 | // feed input 46 | FirmwareVersionMessageReader reader = new FirmwareVersionMessageReader(); 47 | reader.startReading(); 48 | // from 2 (not 0), because COMMAND_START and sysex command bytes were walked during canRead() 49 | for (int i=2; i receivedMessages = historyFirmataWrapper.getReceivedMessages(); 123 | assertTrue(receivedMessages.size() > 0); 124 | 125 | for (int i=0; i 2 | 5 | 4.0.0 6 | 7 | 8 | name.antonsmirnov.firmata 9 | parent 10 | 1.0 11 | 12 | 13 | name.antonsmirnov.firmata.serial 14 | IndepProcessingSerial 15 | 1.2 16 | IndepProcessingSerial 17 | 18 | Processing Serial adapter as Serial API impl (with no processing.* dependencies) 19 | 20 | 21 | 22 | name.antonsmirnov.firmata.serial 23 | Serial 24 | 1.1 25 | 26 | 27 | 28 | com.neuronrobotics 29 | nrjavaserial 30 | 3.7.5.1 31 | 32 | 33 | 34 | junit 35 | junit 36 | 4.8.1 37 | test 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /IndepProcessingSerial/src/main/java/name/antonsmirnov/firmata/serial/IndepProcessingSerialAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import processing.serial.IndepProcessingSerial; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * "ISerial" adapter for RXTX "Serial" class 10 | */ 11 | public class IndepProcessingSerialAdapter implements ISerial, IndepProcessingSerial.Listener { 12 | 13 | private IndepProcessingSerial indepProcessingSerial; 14 | 15 | public IndepProcessingSerial getIndepProcessingSerial() { 16 | return indepProcessingSerial; 17 | } 18 | 19 | public void onDataReceived() { 20 | for (ISerialListener eachListener : listeners) 21 | eachListener.onDataReceived(this); 22 | } 23 | 24 | public IndepProcessingSerialAdapter(IndepProcessingSerial indepProcessingSerial) { 25 | this.indepProcessingSerial = indepProcessingSerial; 26 | indepProcessingSerial.setListener(this); 27 | } 28 | 29 | private List listeners = new ArrayList(); 30 | 31 | public void addListener(ISerialListener listener) { 32 | listeners.add(listener); 33 | } 34 | 35 | public void removeListener(ISerialListener listener) { 36 | listeners.remove(listener); 37 | } 38 | 39 | private boolean isStopping; 40 | 41 | public void start() { 42 | isStopping = false; 43 | indepProcessingSerial.start(); 44 | } 45 | 46 | public void stop() { 47 | isStopping = true; 48 | indepProcessingSerial.stop(); 49 | } 50 | 51 | public boolean isStopping() { 52 | return isStopping; 53 | } 54 | 55 | public int available() { 56 | return indepProcessingSerial.available(); 57 | } 58 | 59 | public void clear() { 60 | indepProcessingSerial.clear(); 61 | } 62 | 63 | public int read() { 64 | return indepProcessingSerial.read(); 65 | } 66 | 67 | public void write(int outcomingByte) { 68 | indepProcessingSerial.write((byte)outcomingByte); 69 | } 70 | 71 | public void write(byte[] outcomingBytes) { 72 | indepProcessingSerial.write(outcomingBytes); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ProcessingSerial/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | name.antonsmirnov.firmata 9 | parent 10 | 1.0 11 | 12 | 13 | name.antonsmirnov.firmata.serial 14 | ProcessingSerial 15 | 1.1 16 | ProcessingSerial 17 | 18 | Processing Serial adapter as Serial API impl 19 | 20 | 25 | 26 | 27 | 28 | name.antonsmirnov.firmata.serial 29 | Serial 30 | 1.1 31 | 32 | 33 | 34 | org.processing 35 | serial 36 | 1.5.1 37 | 38 | 39 | -------------------------------------------------------------------------------- /ProcessingSerial/res/install_processing_core.bat: -------------------------------------------------------------------------------- 1 | mvn install:install-file -Dfile=./lib/core.jar -DgroupId=org.processing -DartifactId=core -Dversion=1.5.1 -Dpackaging=jar -DgeneratePom=true -------------------------------------------------------------------------------- /ProcessingSerial/res/install_processing_serial.bat: -------------------------------------------------------------------------------- 1 | mvn install:install-file -Dfile=./modes/java/libraries/serial/library/serial.jar -DgroupId=org.processing -DartifactId=serial -Dversion=1.5.1 -Dpackaging=jar -DgeneratePom=true -------------------------------------------------------------------------------- /ProcessingSerial/src/main/java/name/antonsmirnov/firmata/serial/ProcessingSerialAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import processing.serial.Serial; 4 | 5 | /** 6 | * "ISerial" adapter for Processing "Serial" class 7 | */ 8 | public class ProcessingSerialAdapter implements ISerial { 9 | 10 | private Serial processingSerial; 11 | 12 | public Serial getProcessingSerial() { 13 | return processingSerial; 14 | } 15 | 16 | public void addListener(ISerialListener listener) { 17 | throw new RuntimeException("ProcessingSerial does not have listener property. Instead it invokes parent.serialEvent(Serial) directly;"); 18 | } 19 | 20 | public void removeListener(ISerialListener listener) { 21 | throw new RuntimeException("ProcessingSerial does not have listener property. Instead it invokes parent.serialEvent(Serial) directly;"); 22 | } 23 | 24 | public ProcessingSerialAdapter(Serial processingSerial) { 25 | this.processingSerial = processingSerial; 26 | } 27 | 28 | public void start() { 29 | // nothing (ProcessingSerial is opened in constructor) 30 | isStopping = false; 31 | } 32 | 33 | private boolean isStopping; 34 | 35 | public void stop() { 36 | isStopping = true; 37 | processingSerial.stop(); 38 | } 39 | 40 | public boolean isStopping() { 41 | return isStopping; 42 | } 43 | 44 | public int available() { 45 | return processingSerial.available(); 46 | } 47 | 48 | public void clear() { 49 | processingSerial.clear(); 50 | } 51 | 52 | public int read() { 53 | return processingSerial.read(); 54 | } 55 | 56 | public void write(int outcomingByte) { 57 | processingSerial.write(outcomingByte); 58 | } 59 | 60 | public void write(byte[] outcomingBytes) { 61 | processingSerial.write(outcomingBytes); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 4ntoine/Firmata 2 | https://github.com/4ntoine/Firmata 3 | 4 | Firmata 2.2 pure Java implementation 5 | 6 | Licensed under Apache License 2.0 7 | -------------------------------------------------------------------------------- /Serial/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | name.antonsmirnov.firmata 9 | parent 10 | 1.0 11 | 12 | 13 | name.antonsmirnov.firmata.serial 14 | Serial 15 | 1.1 16 | Serial API 17 | 18 | Serial API 19 | 20 | 21 | 22 | 23 | org.slf4j 24 | slf4j-api 25 | 1.6.4 26 | 27 | 28 | 29 | 30 | org.slf4j 31 | slf4j-simple 32 | 1.6.4 33 | test 34 | 35 | 36 | 37 | junit 38 | junit 39 | 4.8.1 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-surefire-plugin 50 | 2.10 51 | 52 | 53 | 54 | **/SocketSerialAdapterTest.java 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/BufferingSerialWrapper.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | /** 8 | * Perform buffered reading from the port 9 | * (thread-safe, not bounded) 10 | */ 11 | public class BufferingSerialWrapper implements ISerial, ISerialListener { 12 | 13 | private IByteBuffer buffer; 14 | 15 | private int threadPriority = Thread.NORM_PRIORITY; 16 | 17 | public int getThreadPriority() { 18 | return threadPriority; 19 | } 20 | 21 | /** 22 | * Buffer reading thread priority 23 | * 24 | * @param threadPriority buffer reading thread priority 25 | */ 26 | public void setThreadPriority(int threadPriority) { 27 | this.threadPriority = threadPriority; 28 | } 29 | 30 | private ISerial serial; 31 | 32 | public BufferingSerialWrapper(ISerial serial, IByteBuffer buffer) { 33 | this.serial = serial; 34 | this.serial.addListener(this); 35 | 36 | this.buffer = buffer; 37 | } 38 | 39 | public int available() { 40 | return buffer.size(); 41 | } 42 | 43 | private List listeners = new ArrayList(); 44 | 45 | public void addListener(ISerialListener listener) { 46 | listeners.add(listener); 47 | } 48 | 49 | public void removeListener(ISerialListener listener) { 50 | listeners.remove(listener); 51 | } 52 | 53 | public void start() throws SerialException { 54 | startReadingThread(); 55 | serial.start(); 56 | } 57 | 58 | // flag for the thread to exit 59 | private AtomicBoolean shouldStop = new AtomicBoolean(); 60 | 61 | public AtomicBoolean getShouldStop() { 62 | return shouldStop; 63 | } 64 | 65 | /** 66 | * Buffer reading thread 67 | */ 68 | private class BufferReadingThread extends Thread { 69 | 70 | public void run() { 71 | while (!shouldStop.get()) { 72 | if (available() > 0) { 73 | for (ISerialListener eachListener : listeners) 74 | eachListener.onDataReceived(this); 75 | } 76 | } 77 | } 78 | } 79 | 80 | private BufferReadingThread readingThread; 81 | 82 | private void startReadingThread() { 83 | readingThread = new BufferReadingThread(); 84 | shouldStop.set(false); 85 | readingThread.start(); 86 | } 87 | 88 | public void stop() throws SerialException { 89 | stopReadingThread(); 90 | serial.stop(); 91 | 92 | clear(); 93 | } 94 | 95 | public boolean isStopping() { 96 | return shouldStop.get(); 97 | } 98 | 99 | private void stopReadingThread() { 100 | if (readingThread == null) 101 | return; 102 | 103 | // set exit flag 104 | shouldStop.set(true); 105 | readingThread = null; 106 | } 107 | 108 | public void clear() { 109 | buffer.clear(); 110 | } 111 | 112 | public int read() { 113 | return buffer.get(); 114 | } 115 | 116 | public void write(int outcomingByte) throws SerialException { 117 | serial.write(outcomingByte); 118 | } 119 | 120 | public void write(byte[] outcomingBytes) throws SerialException { 121 | serial.write(outcomingBytes); 122 | } 123 | 124 | public void onDataReceived(ConcreteSerialImpl serialImpl) { 125 | // add incoming byte into buffer 126 | try { 127 | buffer.add((byte)serial.read()); 128 | } catch (SerialException e) { 129 | onException(e); 130 | } 131 | } 132 | 133 | public void onException(Throwable e) { 134 | for (ISerialListener eachListener : listeners) 135 | eachListener.onException(e); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/ByteArrayByteBufferAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | /** 6 | * Adapts byte[] to be used as IByteBuffer 7 | */ 8 | public class ByteArrayByteBufferAdapter implements IByteBuffer { 9 | 10 | private byte[] array; 11 | 12 | public ByteArrayByteBufferAdapter(byte[] array) { 13 | this.array = array; 14 | reset(); 15 | } 16 | 17 | private void reset() { 18 | readIndex.set(0); 19 | writeIndex.set(0); 20 | } 21 | 22 | private AtomicInteger readIndex = new AtomicInteger(); 23 | private AtomicInteger writeIndex = new AtomicInteger(); 24 | 25 | public void add(byte value) { 26 | array[writeIndex.getAndIncrement()] = value; 27 | } 28 | 29 | public byte get() { 30 | if (size() <= 0) 31 | return -1; 32 | 33 | byte outcomingByte = array[readIndex.getAndIncrement()]; 34 | 35 | // if reached written count 36 | if (readIndex == writeIndex) { 37 | reset(); 38 | } 39 | 40 | return outcomingByte; 41 | } 42 | 43 | public void clear() { 44 | reset(); 45 | } 46 | 47 | public int size() { 48 | return writeIndex.get() - readIndex.get(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/IByteBuffer.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | /** 4 | * Byte buffer interface 5 | * (can not use java.util.Queue interface because it works with Objects but not with Primitives) 6 | */ 7 | public interface IByteBuffer { 8 | 9 | /** 10 | * Add data 11 | * @param value byte data 12 | */ 13 | void add(byte value); 14 | 15 | /** 16 | * Get data 17 | * @return byte data 18 | */ 19 | byte get(); 20 | 21 | /** 22 | * Clear data 23 | */ 24 | void clear(); 25 | 26 | /** 27 | * Buffered data size 28 | * (not buffer size) 29 | * @return buffered data size 30 | */ 31 | int size(); 32 | } 33 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/ISerial.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | /** 4 | * Serial interface 5 | * (methods required for Firmata only) 6 | */ 7 | public interface ISerial { 8 | 9 | /** 10 | * Set serial events listener 11 | * @param listener serial events listener 12 | */ 13 | void addListener(ISerialListener listener); 14 | 15 | /** 16 | * Remove serial events listener 17 | * @param listener 18 | */ 19 | void removeListener(ISerialListener listener); 20 | 21 | /** 22 | * Start talking to serial 23 | */ 24 | void start() throws SerialException; 25 | 26 | /** 27 | * Stop talking to serial 28 | */ 29 | void stop() throws SerialException; 30 | 31 | /** 32 | * Serial is stopping 33 | * @return 34 | */ 35 | boolean isStopping(); 36 | 37 | /** 38 | * Returns the number of bytes that have been read from serial 39 | * and are waiting to be dealt with by the user. 40 | */ 41 | int available() throws SerialException; 42 | 43 | /** 44 | * Clear buffers 45 | */ 46 | void clear() throws SerialException; 47 | 48 | /** 49 | * Read byte from serial 50 | * (check available() before) 51 | */ 52 | int read() throws SerialException; 53 | 54 | /** 55 | * Write byte to serial 56 | * @param outcomingByte outcoming byte 57 | */ 58 | void write(int outcomingByte) throws SerialException; 59 | 60 | /** 61 | * Write outcoming bytes to serial 62 | * @param outcomingBytes bytes to write 63 | */ 64 | void write(byte[] outcomingBytes) throws SerialException; 65 | } 66 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/ISerialListener.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | /** 4 | * Serial events listener 5 | */ 6 | public interface ISerialListener { 7 | 8 | /** 9 | * Data received from serial event 10 | * 11 | * @param serialImpl serial implementation (ucan be used to specify serial) 12 | */ 13 | void onDataReceived(ConcreteSerialImpl serialImpl); 14 | 15 | /** 16 | * Exception in serial 17 | * 18 | * @param e 19 | */ 20 | void onException(Throwable e); 21 | } 22 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/QueueByteBufferAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.util.Queue; 4 | 5 | /** 6 | * Adapts java.util.Queue to be used as IByteBuffer 7 | */ 8 | public class QueueByteBufferAdapter implements IByteBuffer { 9 | 10 | private Queue queue; 11 | 12 | public QueueByteBufferAdapter(Queue queue) { 13 | this.queue = queue; 14 | } 15 | 16 | public void add(byte value) { 17 | queue.add(value); 18 | } 19 | 20 | public byte get() { 21 | return queue.poll(); 22 | } 23 | 24 | public void clear() { 25 | queue.clear(); 26 | } 27 | 28 | public int size() { 29 | return queue.size(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/SerialException.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | /** 4 | * Serial exception 5 | */ 6 | public class SerialException extends Exception { 7 | 8 | public SerialException(Exception e) { 9 | super(e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/SocketSerialAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.io.IOException; 4 | import java.io.Serializable; 5 | import java.net.Socket; 6 | 7 | /** 8 | * Serial adapter for regular TCP socket 9 | */ 10 | public class SocketSerialAdapter extends StreamingSerialAdapter implements Serializable { 11 | 12 | private transient Socket socket; 13 | 14 | private String address; 15 | private int port; 16 | 17 | public SocketSerialAdapter(String address, int port) { 18 | this.address = address; 19 | this.port = port; 20 | } 21 | 22 | @Override 23 | public void start() throws SerialException { 24 | try { 25 | socket = new Socket(address, port); 26 | setInStream(socket.getInputStream()); 27 | setOutStream(socket.getOutputStream()); 28 | } catch (IOException e) { 29 | throw new SerialException(e); 30 | } 31 | super.start(); 32 | } 33 | 34 | @Override 35 | public void stop() throws SerialException { 36 | setStopReading(); 37 | 38 | try { 39 | if (socket != null) 40 | socket.close(); 41 | } catch (IOException e) { 42 | throw new SerialException(e); 43 | } 44 | super.stop(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Serial/src/main/java/name/antonsmirnov/firmata/serial/StreamingSerialAdapter.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | /** 11 | * Adapts InputStream and OutputStream to be used as ISerial 12 | */ 13 | public class StreamingSerialAdapter implements ISerial { 14 | 15 | private transient InputStream inStream; 16 | private transient OutputStream outStream; 17 | 18 | public InputStream getInStream() { 19 | return inStream; 20 | } 21 | 22 | public void setInStream(InputStream inStream) { 23 | this.inStream = inStream; 24 | } 25 | 26 | public OutputStream getOutStream() { 27 | return outStream; 28 | } 29 | 30 | public void setOutStream(OutputStream outStream) { 31 | this.outStream = outStream; 32 | } 33 | 34 | public StreamingSerialAdapter() { 35 | // InputStream and OutputStream should be set before start() 36 | } 37 | 38 | public StreamingSerialAdapter(InputStream inStream, OutputStream outStream) { 39 | this(); 40 | setInStream(inStream); 41 | setOutStream(outStream); 42 | } 43 | 44 | private List listeners = new ArrayList(); 45 | 46 | public void addListener(ISerialListener listener) { 47 | listeners.add(listener); 48 | } 49 | 50 | public void removeListener(ISerialListener listener) { 51 | listeners.remove(listener); 52 | } 53 | 54 | private ReadingThread thread; 55 | 56 | private AtomicBoolean shouldStop = new AtomicBoolean(); 57 | 58 | /** 59 | * Threads, that reads InputStream 60 | */ 61 | private class ReadingThread extends Thread implements Thread.UncaughtExceptionHandler{ 62 | 63 | public ReadingThread() { 64 | setUncaughtExceptionHandler(this); 65 | } 66 | 67 | public void uncaughtException(Thread t, Throwable e) { 68 | handleException(e); 69 | } 70 | 71 | private void handleException(Throwable e) { 72 | if (!shouldStop.get()) 73 | for (ISerialListener eachListener : listeners) 74 | eachListener.onException(e); 75 | } 76 | 77 | @Override 78 | public void run() { 79 | while (!shouldStop.get()) { 80 | try { 81 | if (inStream.available() > 0) 82 | for (ISerialListener eachListener : listeners) 83 | eachListener.onDataReceived(StreamingSerialAdapter.this); 84 | } catch (IOException e) { 85 | handleException(e); 86 | break; 87 | } 88 | } 89 | 90 | try { 91 | inStream.close(); 92 | } catch (IOException e) {} 93 | } 94 | } 95 | 96 | public void start() throws SerialException { 97 | if (thread != null) 98 | return; 99 | 100 | thread = new ReadingThread(); 101 | shouldStop.set(false); 102 | thread.start(); 103 | } 104 | 105 | public void stop() throws SerialException { 106 | if (thread == null) 107 | return; 108 | 109 | setStopReading(); 110 | thread = null; 111 | try { 112 | outStream.close(); 113 | } catch (IOException e) {} 114 | } 115 | 116 | protected void setStopReading() { 117 | shouldStop.set(true); 118 | } 119 | 120 | public boolean isStopping() { 121 | return shouldStop.get(); 122 | } 123 | 124 | public int available() throws SerialException { 125 | try { 126 | return inStream.available(); 127 | } catch (IOException e) { 128 | return checkIsStoppingOrThrow(e, 0); 129 | } 130 | } 131 | 132 | public void clear() throws SerialException { 133 | try { 134 | inStream.reset(); 135 | } catch (IOException e) { 136 | checkIsStoppingOrThrow(e); 137 | } 138 | } 139 | 140 | private void checkIsStoppingOrThrow(IOException e) throws SerialException { 141 | checkIsStoppingOrThrow(e, 0); 142 | } 143 | 144 | private int checkIsStoppingOrThrow(IOException e, int value) throws SerialException { 145 | if (shouldStop.get()) { 146 | return value; 147 | } else { 148 | throw new SerialException(e); 149 | } 150 | } 151 | 152 | public int read() throws SerialException { 153 | try { 154 | return inStream.read(); 155 | } catch (IOException e) { 156 | return checkIsStoppingOrThrow(e, -1); 157 | } 158 | } 159 | 160 | public void write(int outcomingByte) throws SerialException { 161 | try { 162 | outStream.write(outcomingByte); 163 | } catch (IOException e) { 164 | checkIsStoppingOrThrow(e); 165 | } 166 | } 167 | 168 | public void write(byte[] outcomingBytes) throws SerialException { 169 | try { 170 | outStream.write(outcomingBytes); 171 | } catch (IOException e) { 172 | checkIsStoppingOrThrow(e); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Serial/src/test/java/name/antonsmirnov/firmata/serial/WritebackSerial.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial; 2 | 3 | import java.io.IOException; 4 | import java.io.PipedInputStream; 5 | import java.io.PipedOutputStream; 6 | 7 | /** 8 | * WriteBack Serial 9 | * (written bytes are returned back as input bytes) 10 | */ 11 | public class WritebackSerial extends StreamingSerialAdapter { 12 | 13 | private PipedInputStream pipedInStream; 14 | private PipedOutputStream pipedOutStream; 15 | 16 | public WritebackSerial() { 17 | super(); 18 | 19 | pipedOutStream = new PipedOutputStream(); 20 | try { 21 | pipedInStream = new PipedInputStream(pipedOutStream); 22 | } catch (IOException e) { 23 | throw new RuntimeException(e); 24 | } 25 | 26 | setInStream(pipedInStream); 27 | setOutStream(pipedOutStream); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Serial/src/test/java/name/antonsmirnov/firmata/serial/tests/BufferingSerialWrapperTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial.tests; 2 | 3 | import junit.framework.TestCase; 4 | import name.antonsmirnov.firmata.serial.*; 5 | import org.junit.Test; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.concurrent.ConcurrentLinkedQueue; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * Test for BufferingSerialWrapper 14 | */ 15 | public class BufferingSerialWrapperTest extends TestCase { 16 | 17 | private final Logger log = LoggerFactory.getLogger(getClass()); 18 | 19 | private static final byte MIN_VALUE = Byte.MIN_VALUE; 20 | private static final byte MAX_VALUE = Byte.MAX_VALUE; 21 | 22 | private static final int SLEEP_DELAY = 100; 23 | private static final int SLEEP_MAX = 20 * 1000; // 20 sec 24 | 25 | /** 26 | * Thread that simulates incoming bytes in TestSerial 27 | */ 28 | public class WriterThread extends Thread { 29 | 30 | private ISerial serial; 31 | private byte minValue; 32 | private byte maxValue; 33 | 34 | public WriterThread(ISerial serial, byte minValue, byte maxValue) { 35 | super(); 36 | setPriority(Thread.MAX_PRIORITY); 37 | 38 | this.serial = serial; 39 | this.minValue = minValue; 40 | this.maxValue = maxValue; 41 | } 42 | 43 | @Override 44 | public void run() { 45 | super.run(); 46 | 47 | for (int i=minValue; i<=maxValue; i++) { 48 | byte outcomingByte = (byte)i; 49 | log.info("Put '{}' to buffer", outcomingByte); 50 | try { 51 | serial.write(outcomingByte); 52 | } catch (SerialException e) { 53 | log.error("serial exception", e); 54 | } 55 | } 56 | } 57 | } 58 | 59 | private WriterThread writingThread; 60 | 61 | private AtomicInteger maxRead = new AtomicInteger(); 62 | final ISerial serial = new WritebackSerial(); 63 | 64 | @Test 65 | // ConcurrentLinkedQueue as buffer 66 | public void testQueue() throws InterruptedException, SerialException { 67 | testWriteRead(new QueueByteBufferAdapter(new ConcurrentLinkedQueue())); 68 | } 69 | 70 | @Test 71 | // byte[] as buffer 72 | public void testByteArray() throws InterruptedException, SerialException { 73 | testWriteRead(new ByteArrayByteBufferAdapter(new byte[256])); 74 | } 75 | 76 | private void testWriteRead(IByteBuffer buffer) throws InterruptedException, SerialException { 77 | final BufferingSerialWrapper bufferingWrapper = new BufferingSerialWrapper(serial, buffer); 78 | 79 | // reading thread is slower than writing to check buffer filling 80 | bufferingWrapper.setThreadPriority(Thread.MIN_PRIORITY); 81 | 82 | bufferingWrapper.start(); 83 | 84 | writingThread = new WriterThread(serial, MIN_VALUE, MAX_VALUE); 85 | bufferingWrapper.addListener(new ISerialListener() { 86 | public void onDataReceived(Object serialImpl) { 87 | byte incomingByte = (byte) bufferingWrapper.read(); 88 | maxRead.set(incomingByte); 89 | final int available = bufferingWrapper.available(); 90 | log.info("Read '{}' from buffer (size={})", maxRead.intValue(), available); 91 | } 92 | 93 | public void onException(Throwable e) { 94 | log.error("serial exception", e); 95 | } 96 | }); 97 | 98 | // start simulating incoming bytes 99 | writingThread.start(); 100 | 101 | int slept = 0; 102 | while (maxRead.get() < (MAX_VALUE-1)) { 103 | Thread.sleep(SLEEP_DELAY); 104 | slept += SLEEP_DELAY; 105 | if (slept > SLEEP_MAX) 106 | throw new RuntimeException("did not receive the last value"); 107 | } 108 | 109 | bufferingWrapper.stop(); 110 | 111 | assertEquals(0, buffer.size()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Serial/src/test/java/name/antonsmirnov/firmata/serial/tests/SocketSerialAdapterTest.java: -------------------------------------------------------------------------------- 1 | package name.antonsmirnov.firmata.serial.tests; 2 | 3 | import junit.framework.TestCase; 4 | import name.antonsmirnov.firmata.serial.ISerial; 5 | import name.antonsmirnov.firmata.serial.ISerialListener; 6 | import name.antonsmirnov.firmata.serial.SerialException; 7 | import name.antonsmirnov.firmata.serial.SocketSerialAdapter; 8 | import org.junit.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Test for SocketSerialAdapter 17 | */ 18 | public class SocketSerialAdapterTest extends TestCase implements ISerialListener { 19 | 20 | private final Logger log = LoggerFactory.getLogger(getClass()); 21 | 22 | private ISerial serial; 23 | 24 | private List readBytes = new ArrayList(); 25 | 26 | @Override 27 | protected void setUp() throws Exception { 28 | super.setUp(); 29 | 30 | serial = new SocketSerialAdapter(IP, PORT); 31 | serial.addListener(this); 32 | 33 | serial.start(); 34 | } 35 | 36 | public void onDataReceived(Object serialImpl) { 37 | try { 38 | byte incomingByte = (byte) serial.read(); 39 | readBytes.add(incomingByte); 40 | 41 | log.info("Received byte: " + incomingByte + " " + new Character((char)incomingByte)); 42 | } catch (SerialException e) { 43 | onException(e); 44 | } 45 | } 46 | 47 | public void onException(Throwable e) { 48 | log.error("Serial error", e); 49 | } 50 | 51 | private static final String IP = "192.168.168.169"; 52 | private static final int PORT = 1000; 53 | 54 | private final byte[] MESSAGE = "ABC1234567890".getBytes(); 55 | 56 | @Test 57 | // incoming bytes array should be exactly the same as sent bytes array 58 | public void testEcho() throws SerialException, InterruptedException { 59 | serial.write(MESSAGE); 60 | 61 | Thread.sleep(5 * 1000); // waiting 5 seconds 62 | 63 | assertEquals(MESSAGE.length, readBytes.size()); 64 | for (int i=0; i 2 | 5 | 4.0.0 6 | 7 | name.antonsmirnov.firmata 8 | parent 9 | 1.0 10 | pom 11 | 12 | parent 13 | 14 | 15 | 16 | Anton Smirnov 17 | dev@antonsmirnov.name 18 | 19 | 20 | 21 | 22 | 23 | 24 | true 25 | org.apache.maven.plugins 26 | maven-compiler-plugin 27 | 2.3.2 28 | 29 | 1.5 30 | 1.5 31 | 32 | 33 | 34 | 35 | 36 | 37 | ./Serial 38 | ./Firmata 39 | ./FirmataTests 40 | ./ProcessingSerial 41 | ./IndepProcessingSerial 42 | 43 | 44 | --------------------------------------------------------------------------------