/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.devices.arduino.phiro; import android.util.Log; import org.catrobat.catroid.bluetooth.base.BluetoothConnection; import org.catrobat.catroid.bluetooth.base.BluetoothDevice; import org.catrobat.catroid.formulaeditor.Sensors; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import name.antonsmirnov.firmata.Firmata; import name.antonsmirnov.firmata.message.AnalogMessage; import name.antonsmirnov.firmata.message.Message; import name.antonsmirnov.firmata.message.ReportAnalogPinMessage; import name.antonsmirnov.firmata.message.ReportFirmwareVersionMessage; import name.antonsmirnov.firmata.message.SetPinModeMessage; import name.antonsmirnov.firmata.serial.ISerial; import name.antonsmirnov.firmata.serial.SerialException; import name.antonsmirnov.firmata.serial.StreamingSerialAdapter; public class PhiroImpl implements Phiro { private static final UUID PHIRO_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private static final String TAG = PhiroImpl.class.getSimpleName(); private static final int MIN_VALUE = 0; private static final int MAX_VALUE = 255; private static final int PIN_SPEAKER_OUT = 3; private static final int PIN_RGB_RED_RIGHT = 4; private static final int PIN_RGB_GREEN_RIGHT = 5; private static final int PIN_RGB_BLUE_RIGHT = 6; private static final int PIN_RGB_RED_LEFT = 7; private static final int PIN_RGB_GREEN_LEFT = 8; private static final int PIN_RGB_BLUE_LEFT = 9; private static final int PIN_LEFT_MOTOR_SPEED = 10; private static final int PIN_LEFT_MOTOR_FORWARD_BACKWARD = 11; private static final int PIN_RIGHT_MOTOR_SPEED = 12; private static final int PIN_RIGHT_MOTOR_FORWARD_BACKWARD = 2; private static final int MIN_PWM_PIN = 2; private static final int MAX_PWM_PIN = 13; public static final int PIN_SENSOR_SIDE_RIGHT = 0; public static final int PIN_SENSOR_FRONT_RIGHT = 1; public static final int PIN_SENSOR_BOTTOM_RIGHT = 2; public static final int PIN_SENSOR_BOTTOM_LEFT = 3; public static final int PIN_SENSOR_FRONT_LEFT = 4; public static final int PIN_SENSOR_SIDE_LEFT = 5; private static final int MIN_SENSOR_PIN = 0; private static final int MAX_SENSOR_PIN = 5; private BluetoothConnection btConnection; private Firmata firmata; private boolean isInitialized = false; private boolean isReportingSensorData = false; private PhiroListener phiroListener; @Override public synchronized void playTone(int selectedTone, int durationInSeconds) { sendAnalogFirmataMessage(PIN_SPEAKER_OUT, selectedTone); if (currentStopPlayToneTask != null) { currentStopPlayToneTask.cancel(); } currentStopPlayToneTask = new StopPlayToneTask(); timer.schedule(currentStopPlayToneTask, durationInSeconds * 1000); } Timer timer = new Timer(); TimerTask currentStopPlayToneTask = null; private class StopPlayToneTask extends TimerTask { @Override public void run() { sendAnalogFirmataMessage(PIN_SPEAKER_OUT, MIN_VALUE); } } @Override public void moveLeftMotorForward(int speedInPercent) { sendAnalogFirmataMessage(PIN_LEFT_MOTOR_SPEED, percentToSpeed(speedInPercent)); sendAnalogFirmataMessage(PIN_LEFT_MOTOR_FORWARD_BACKWARD, MIN_VALUE); } @Override public void moveLeftMotorBackward(int speedInPercent) { sendAnalogFirmataMessage(PIN_LEFT_MOTOR_SPEED, percentToSpeed(speedInPercent)); sendAnalogFirmataMessage(PIN_LEFT_MOTOR_FORWARD_BACKWARD, MAX_VALUE); } @Override public void moveRightMotorForward(int speedInPercent) { sendAnalogFirmataMessage(PIN_RIGHT_MOTOR_SPEED, percentToSpeed(speedInPercent)); sendAnalogFirmataMessage(PIN_RIGHT_MOTOR_FORWARD_BACKWARD, MAX_VALUE); sendAnalogFirmataMessage(MAX_PWM_PIN, MAX_VALUE); } @Override public void moveRightMotorBackward(int speedInPercent) { sendAnalogFirmataMessage(PIN_RIGHT_MOTOR_SPEED, percentToSpeed(speedInPercent)); sendAnalogFirmataMessage(PIN_RIGHT_MOTOR_FORWARD_BACKWARD, MIN_VALUE); sendAnalogFirmataMessage(MAX_PWM_PIN, MIN_VALUE); } @Override public void stopLeftMotor() { moveLeftMotorForward(MIN_VALUE); } @Override public void stopRightMotor() { moveRightMotorForward(MIN_VALUE); } @Override public void stopAllMovements() { stopLeftMotor(); stopRightMotor(); } @Override public void setLeftRGBLightColor(int red, int green, int blue) { red = checkRBGValue(red); green = checkRBGValue(green); blue = checkRBGValue(blue); sendFirmataMessage(new AnalogMessage(PIN_RGB_RED_LEFT, red)); sendFirmataMessage(new AnalogMessage(PIN_RGB_GREEN_LEFT, green)); sendFirmataMessage(new AnalogMessage(PIN_RGB_BLUE_LEFT, blue)); } @Override public void setRightRGBLightColor(int red, int green, int blue) { red = checkRBGValue(red); green = checkRBGValue(green); blue = checkRBGValue(blue); sendFirmataMessage(new AnalogMessage(PIN_RGB_RED_RIGHT, red)); sendFirmataMessage(new AnalogMessage(PIN_RGB_GREEN_RIGHT, green)); sendFirmataMessage(new AnalogMessage(PIN_RGB_BLUE_RIGHT, blue)); } private int percentToSpeed(int percent) { if (percent <= 0) { return MIN_VALUE; } if (percent >= 100) { return MAX_VALUE; } return (int) (percent * 2.55); } private int checkRBGValue(int rgbValue) { if (rgbValue > MAX_VALUE) { return MAX_VALUE; } if (rgbValue < MIN_VALUE) { return MIN_VALUE; } return rgbValue; } @Override public String getName() { return "Phiro"; } @Override public Class<? extends BluetoothDevice> getDeviceType() { return PHIRO; } @Override public void setConnection(BluetoothConnection connection) { this.btConnection = connection; } @Override public void disconnect() { if (firmata == null) { return; } try { resetPins(); this.reportSensorData(false); firmata.clearListeners(); firmata.getSerial().stop(); isInitialized = false; firmata = null; } catch (SerialException e) { Log.d(TAG, "Error stop phiro pro serial"); } } @Override public boolean isAlive() { if (firmata == null) { return false; } try { firmata.send(new ReportFirmwareVersionMessage()); return true; } catch (SerialException e) { return false; } } public void reportFirmwareVersion() { if (firmata == null) { return; } try { firmata.send(new ReportFirmwareVersionMessage()); } catch (SerialException e) { Log.d(TAG, "Firmata Serial error, cannot send message."); } } @Override public int getSensorValue(Sensors sensor) { switch (sensor) { case PHIRO_BOTTOM_LEFT: return phiroListener.getBottomLeftSensor(); case PHIRO_BOTTOM_RIGHT: return phiroListener.getBottomRightSensor(); case PHIRO_FRONT_LEFT: return phiroListener.getFrontLeftSensor(); case PHIRO_FRONT_RIGHT: return phiroListener.getFrontRightSensor(); case PHIRO_SIDE_LEFT: return phiroListener.getSideLeftSensor(); case PHIRO_SIDE_RIGHT: return phiroListener.getSideRightSensor(); } return 0; } @Override public UUID getBluetoothDeviceUUID() { return PHIRO_UUID; } @Override public void initialise() { if (isInitialized) { return; } try { tryInitialize(); isInitialized = true; } catch (SerialException e) { Log.d(TAG, "Error starting firmata serials"); } catch (IOException e) { Log.d(TAG, "Error opening streams"); } } private void tryInitialize() throws IOException, SerialException { ISerial serial = new StreamingSerialAdapter(btConnection.getInputStream(), btConnection.getOutputStream()); firmata = new Firmata(serial); phiroListener = new PhiroListener(); firmata.addListener(phiroListener); firmata.getSerial().start(); for (int pin = MIN_PWM_PIN; pin <= MAX_PWM_PIN; ++pin) { sendFirmataMessage(new SetPinModeMessage(pin, SetPinModeMessage.PIN_MODE.PWM.getMode())); } reportSensorData(true); } private void reportSensorData(boolean report) { if (isReportingSensorData == report) { return; } isReportingSensorData = report; for (int pin = MIN_SENSOR_PIN; pin <= MAX_SENSOR_PIN; ++pin) { // sendFirmataMessage(new SetPinModeMessage(? maybe 54 ?, SetPinModeMessage.PIN_MODE.ANALOG.getMode())); // --> not needed sendFirmataMessage(new ReportAnalogPinMessage(pin, report)); } } private void resetPins() { stopAllMovements(); setLeftRGBLightColor(0, 0, 0); setRightRGBLightColor(0, 0, 0); playTone(0, 0); } @Override public void start() { if (!isInitialized) { initialise(); } reportSensorData(true); } @Override public void pause() { stopAllMovements(); reportSensorData(false); } @Override public void destroy() { resetPins(); } private void sendAnalogFirmataMessage(int pin, int value) { sendFirmataMessage(new AnalogMessage(pin, value)); } private void sendFirmataMessage(Message message) { if (firmata == null) { return; } try { firmata.send(message); } catch (SerialException e) { Log.d(TAG, "Firmata Serial error, cannot send message."); } } }