/*
FaBoGPIOProfile
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.fabo.profile;
import android.content.Intent;
import org.deviceconnect.android.deviceplugin.fabo.FaBoDeviceService;
import org.deviceconnect.android.deviceplugin.fabo.param.ArduinoUno;
import org.deviceconnect.android.deviceplugin.fabo.param.FirmataV32;
import org.deviceconnect.android.event.EventError;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.GPIOProfile;
import org.deviceconnect.android.profile.api.DeleteApi;
import org.deviceconnect.android.profile.api.GetApi;
import org.deviceconnect.android.profile.api.PostApi;
import org.deviceconnect.android.profile.api.PutApi;
import static org.deviceconnect.message.DConnectMessage.RESULT_OK;
/**
* GPIO Profile.
* @author NTT DOCOMO, INC.
*/
public class FaBoGPIOProfile extends GPIOProfile {
private final static String TAG = "FABO_PLUGIN";
/** Digital Pinに書き込むコマンド. */
private final static byte CMD_DIGITAL_WRITE = FirmataV32.DIGITAL_MESSAGE;
/** Pinモードの設定コマンド. */
private final static byte CMD_PIN_SETTING = FirmataV32.SET_PIN_MODE;
/** GPIOのHigh. */
private final static int HIGH = 1;
/** GPIOのLow. */
private final static int LOW = 0;
private void addGetAnalogApi(final ArduinoUno.Pin pin) {
// GET /gpio/analog/{pinName}
switch (pin) {
case PIN_A0:
case PIN_A1:
case PIN_A2:
case PIN_A3:
case PIN_A4:
case PIN_A5:
for (final String pinName : pin.getPinNames()) {
addApi(new GetApi() {
@Override
public String getInterface() {
return INTERFACE_ANALOG;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
int value = ((FaBoDeviceService) getContext()).getAnalogValue(pin.getPinNumber());
setValue(response, value);
setResult(response, RESULT_OK);
return true;
}
});
}
break;
default:
break;
}
}
private void addGetDigitalApi(final ArduinoUno.Pin pin) {
// GET /gpio/digital/{pinName}
switch (pin) {
case PIN_D0:
case PIN_D1:
case PIN_D2:
case PIN_D3:
case PIN_D4:
case PIN_D5:
case PIN_D6:
case PIN_D7:
case PIN_D8:
case PIN_D9:
case PIN_D10:
case PIN_D11:
case PIN_D12:
case PIN_D13:
for (final String pinName : pin.getPinNames()) {
addApi(new GetApi() {
@Override
public String getInterface() {
return INTERFACE_DIGITAL;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
int value = ((FaBoDeviceService) getContext()).getDigitalValue(pin.getPort());
if ((value & pin.getBit()) == pin.getBit()) {
setValue(response, HIGH);
} else {
setValue(response, LOW);
}
setResult(response, RESULT_OK);
return true;
}
});
}
break;
default:
break;
}
}
private void addPostExportApi(final ArduinoUno.Pin pin) {
// POST /gpio/export/{pinName}
for (final String pinName : pin.getPinNames()) {
addApi(new PostApi() {
@Override
public String getInterface() {
return INTERFACE_EXPORT;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
int modeValue;
String mode = request.getStringExtra("mode");
if(mode != null) {
try {
modeValue = Integer.parseInt(mode);
if (modeValue < 0 || modeValue > 3) {
MessageUtils.setInvalidRequestParameterError(response, "The value of mode must be defined 0-3.");
return true;
}
} catch (Exception e) {
MessageUtils.setInvalidRequestParameterError(response, "The value of mode must be defined 0-3.");
return true;
}
} else {
MessageUtils.setInvalidRequestParameterError(response, "The value of mode is null.");
return true;
}
settingPin(pin.getPinNumber(), modeValue);
setResult(response, RESULT_OK);
return true;
}
});
}
}
private void addPostDigitalApi(final ArduinoUno.Pin pin) {
// POST /gpio/digital/{pinName}
for (final String pinName : pin.getPinNames()) {
addApi(new PostApi() {
@Override
public String getInterface() {
return INTERFACE_DIGITAL;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
int hlValue;
String hl = request.getStringExtra(PARAM_VALUE);
if(hl != null) {
try {
hlValue = Integer.parseInt(hl);
if (hlValue != HIGH && hlValue != LOW) {
// 値が無効
MessageUtils.setInvalidRequestParameterError(response, "Value must be defined 1 or 0.");
return true;
}
} catch (Exception e) {
// 値が無効
MessageUtils.setInvalidRequestParameterError(response, "Value must be defined 1 or 0.");
return true;
}
} else {
MessageUtils.setInvalidRequestParameterError(response, "Value is null.");
return true;
}
digitalWrite(pin.getPort(), pin.getBit(), hlValue);
setResult(response, RESULT_OK);
return true;
}
});
}
}
private void addPostAnalogApi(final ArduinoUno.Pin pin) {
// POST /gpio/analog/{pinName}
switch (pin) {
case PIN_D3:
case PIN_D5:
case PIN_D6:
case PIN_D9:
case PIN_D10:
case PIN_D11:
for (final String pinName : pin.getPinNames()) {
addApi(new PostApi() {
@Override
public String getInterface() {
return INTERFACE_ANALOG;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
int hlValue;
String hl = request.getStringExtra(PARAM_VALUE);
if(hl != null) {
try {
hlValue = Integer.parseInt(hl);
if (hlValue > 255) {
// 値が無効
MessageUtils.setInvalidRequestParameterError(response, "Value must be defined under 255.");
return true;
}
} catch (Exception e) {
// 値が無効
MessageUtils.setInvalidRequestParameterError(response, "Value must be defined 0-255.");
return true;
}
} else {
MessageUtils.setInvalidRequestParameterError(response, "Value is null.");
return true;
}
analogWrite(pin.getPinNumber(), hlValue);
setResult(response, RESULT_OK);
return true;
}
});
}
break;
default:
break;
}
}
private void addPutOnChangeApi() {
// PUT /gpio/onChange
addApi(new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_CHANGE;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
EventError error = EventManager.INSTANCE.addEvent(request);
if (EventError.NONE == error) {
((FaBoDeviceService) getContext()).registerOnChange(getServiceID(request));
setResult(response, RESULT_OK);
return true;
} else {
MessageUtils.setError(response, 100, "Failed add event.");
return true;
}
}
});
}
private void addPutDigitalApi(final ArduinoUno.Pin pin) {
// PUT /gpio/digital/{pinName}
for (final String pinName : pin.getPinNames()) {
addApi(new PutApi() {
@Override
public String getInterface() {
return INTERFACE_DIGITAL;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
digitalWrite(pin.getPort(), pin.getBit(), HIGH);
setResult(response, RESULT_OK);
return true;
}
});
}
}
private void addDeleteOnChangeApi() {
// DELETE /gpio/onChange
addApi(new DeleteApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_CHANGE;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
boolean result = EventManager.INSTANCE.removeEvents(getOrigin(request));
if (result) {
((FaBoDeviceService) getContext()).unregisterOnChange(getServiceID(request));
setResult(response, RESULT_OK);
return true;
} else {
MessageUtils.setError(response, 100, "Failed delete event.");
return true;
}
}
});
}
private void addDeleteDigitalApi(final ArduinoUno.Pin pin) {
// DELETE /gpio/digital/{pinName}
for (final String pinName : pin.getPinNames()) {
addApi(new DeleteApi() {
@Override
public String getInterface() {
return INTERFACE_DIGITAL;
}
@Override
public String getAttribute() {
return pinName;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
digitalWrite(pin.getPort(), pin.getBit(), LOW);
setResult(response, RESULT_OK);
return true;
}
});
}
}
public FaBoGPIOProfile() {
for (ArduinoUno.Pin pin : ArduinoUno.Pin.values()) {
addGetAnalogApi(pin);
addGetDigitalApi(pin);
addPostExportApi(pin);
addPostDigitalApi(pin);
addPostAnalogApi(pin);
addPutDigitalApi(pin);
addDeleteDigitalApi(pin);
}
addPutOnChangeApi();
addDeleteOnChangeApi();
}
/**
* Digitalの書き込み.
*
* @param port PORT番号
* @param pinBit PIN番号
* @param hl HIGHとLOWの値
*/
private void digitalWrite(int port, int pinBit, int hl){
FaBoDeviceService service = (FaBoDeviceService) getContext();
if (hl == HIGH){
int status = service.getPortStatus(port) | pinBit;
byte[] bytes = new byte[3];
bytes[0] = (byte) (CMD_DIGITAL_WRITE | port);
bytes[1] = (byte) (status & 0xff);
bytes[2] = (byte) ((status >> 8) & 0xff);
service.SendMessage(bytes);
service.setPortStatus(port, status);
} else if(hl == LOW){
int status = service.getPortStatus(port) & ~pinBit;
byte[] bytes = new byte[3];
bytes[0] = (byte) (CMD_DIGITAL_WRITE | port);
bytes[1] = (byte) (status & 0xff);
bytes[2] = (byte) ((status >> 8) & 0xff);
service.SendMessage(bytes);
service.setPortStatus(port, status);
}
}
/**
* Analogの書き込み.
*
* @param pinNo PIN番号
* @param value 値
*/
private void analogWrite(int pinNo, int value){
FaBoDeviceService service = (FaBoDeviceService) getContext();
byte[] bytes = new byte[5];
bytes[0] = (byte) FirmataV32.START_SYSEX;
bytes[1] = (byte) (0x6F);
bytes[2] = (byte) pinNo;
bytes[3] = (byte) value;
bytes[4] = (byte) FirmataV32.END_SYSEX;
service.SendMessage(bytes);
}
/**
* 各PINの設定.
*
* @param pinNo PIN番号
* @param mode モード、0:GPIO IN, 1:GPIO OUT, 2: ANALOG, 3: PWM, 4:SERVO
*/
private void settingPin(int pinNo, int mode){
FaBoDeviceService service = (FaBoDeviceService) getContext();
byte[] command = new byte[3];
command[0] = (byte) (CMD_PIN_SETTING);
command[1] = (byte) (pinNo);
command[2] = (byte) (mode);
service.SendMessage(command);
service.setPin(pinNo, mode);
}
}