/* * 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; import android.util.Log; import org.catrobat.catroid.bluetooth.base.BluetoothConnection; import org.catrobat.catroid.bluetooth.base.BluetoothDevice; import java.io.IOException; import java.util.UUID; import name.antonsmirnov.firmata.Firmata; import name.antonsmirnov.firmata.message.AnalogMessage; import name.antonsmirnov.firmata.message.DigitalMessage; import name.antonsmirnov.firmata.message.Message; import name.antonsmirnov.firmata.message.ReportAnalogPinMessage; import name.antonsmirnov.firmata.message.ReportDigitalPortMessage; 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 ArduinoImpl implements Arduino { public static final int NUMBER_OF_DIGITAL_PINS = 14; public static final int PINS_IN_A_PORT = 8; public static final int PIN_ANALOG_0 = 0; public static final int PIN_ANALOG_1 = 1; public static final int PIN_ANALOG_2 = 2; public static final int PIN_ANALOG_3 = 3; public static final int PIN_ANALOG_4 = 4; public static final int PIN_ANALOG_5 = 5; public static final int PORT_DIGITAL_0 = 0; public static final int PORT_DIGITAL_1 = 1; private static final int MIN_PWM_PIN_GROUP_1 = 3; private static final int MAX_PWM_PIN_GROUP_1 = 3; private static final int MIN_PWM_PIN_GROUP_2 = 5; private static final int MAX_PWM_PIN_GROUP_2 = 6; private static final int MIN_PWM_PIN_GROUP_3 = 9; private static final int MAX_PWM_PIN_GROUP_3 = 11; private static final int MIN_ANALOG_SENSOR_PIN = 0; private static final int MAX_ANALOG_SENSOR_PIN = 5; private static final UUID ARDUINO_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private static final String TAG = ArduinoImpl.class.getSimpleName(); private Firmata firmata; private boolean isReportingSensorData = false; private boolean isInitialized = false; private ArduinoListener arduinoListener; private BluetoothConnection btConnection; private int castValue(int value) { if (value <= 0) { return 0; } if (value >= 100) { return 255; } return (int) (value * 2.55); } @Override public String getName() { return "ARDUINO"; } @Override public void setConnection(BluetoothConnection connection) { this.btConnection = connection; } @Override public Class<? extends BluetoothDevice> getDeviceType() { return BluetoothDevice.ARDUINO; } @Override public void disconnect() { if (firmata == null) { return; } try { this.reportSensorData(false); firmata.clearListeners(); firmata.getSerial().stop(); isInitialized = false; firmata = null; } catch (SerialException e) { Log.d(TAG, "Error stop Arduino 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 UUID getBluetoothDeviceUUID() { return ARDUINO_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); arduinoListener = new ArduinoListener(); firmata.addListener(arduinoListener); firmata.getSerial().start(); for (int pin = MIN_PWM_PIN_GROUP_1; pin <= MAX_PWM_PIN_GROUP_1; ++pin) { sendFirmataMessage(new SetPinModeMessage(pin, SetPinModeMessage.PIN_MODE.PWM.getMode())); } for (int pin = MIN_PWM_PIN_GROUP_2; pin <= MAX_PWM_PIN_GROUP_2; ++pin) { sendFirmataMessage(new SetPinModeMessage(pin, SetPinModeMessage.PIN_MODE.PWM.getMode())); } for (int pin = MIN_PWM_PIN_GROUP_3; pin <= MAX_PWM_PIN_GROUP_3; ++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 i = MIN_ANALOG_SENSOR_PIN; i <= MAX_ANALOG_SENSOR_PIN; i++) { sendFirmataMessage(new ReportAnalogPinMessage(i, report)); } } @Override public void start() { if (!isInitialized) { initialise(); } reportSensorData(true); } @Override public void pause() { } @Override public void destroy() { reportSensorData(false); } @Override public void setAnalogArduinoPin(int pin, int value) { sendAnalogFirmataMessage(pin, value); } @Override public void setDigitalArduinoPin(int digitalPinNumber, int pinValue) { int digitalPort = getPortFromPin(digitalPinNumber); arduinoListener.setDigitalPinValue(digitalPinNumber, pinValue); sendDigitalFirmataMessage(digitalPort, digitalPinNumber, arduinoListener.getPortValue(digitalPort)); } @Override public double getDigitalArduinoPin(int digitalPinNumber) { sendFirmataMessage(new SetPinModeMessage(digitalPinNumber, SetPinModeMessage.PIN_MODE.INPUT.getMode())); int port = getPortFromPin(digitalPinNumber); sendFirmataMessage(new ReportDigitalPortMessage(port, true)); try { Thread.sleep(100); } catch (InterruptedException e) { Log.d(TAG, "Error Arduino sensor thread sleep()"); } double result = arduinoListener.getDigitalPinValue(digitalPinNumber); sendFirmataMessage(new ReportDigitalPortMessage(port, false)); return result; } @Override public double getAnalogArduinoPin(int analogPinNumber) { switch (analogPinNumber) { case 0: return arduinoListener.getAnalogPin0(); case 1: return arduinoListener.getAnalogPin1(); case 2: return arduinoListener.getAnalogPin2(); case 3: return arduinoListener.getAnalogPin3(); case 4: return arduinoListener.getAnalogPin4(); case 5: return arduinoListener.getAnalogPin5(); } return 0; } public static boolean isValidPin(int pin) { return (pin >= 0) && (pin < NUMBER_OF_DIGITAL_PINS); } public static int getPortFromPin(int pin) { return pin / PINS_IN_A_PORT; } public static int getIndexOfPinOnPort(int pin) { return pin % PINS_IN_A_PORT; } private void sendAnalogFirmataMessage(int pin, int value) { sendFirmataMessage(new AnalogMessage(pin, castValue(value))); } private void sendDigitalFirmataMessage(int port, int pin, int value) { sendFirmataMessage(new SetPinModeMessage(pin, SetPinModeMessage.PIN_MODE.OUTPUT.getMode())); sendFirmataMessage(new DigitalMessage(port, 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."); } } }