/*
 * Copyright 2007-2008 (c) Erik Sjodin, eriksjodin.net
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#pragma once

#include <list>
#include "ofConstants.h"
#include "ofEvents.h"
#include "ofSerial.h"

 /* Version numbers for the protocol.  The protocol is still changing, so these
 * version numbers are important.  This number can be queried so that host
 * software can test whether it will be compatible with the currently
 * installed firmware. */
#define FIRMATA_MAJOR_VERSION	2 // for non-compatible changes
#define FIRMATA_MINOR_VERSION	5 // for backwards compatible changes
#define FIRMATA_BUGFIX_VERSION	0 // for bugfix releases

#define FIRMATA_MAX_DATA_BYTES	64 // max number of data bytes in incoming messages

 // message command bytes (128-255/0x80-0xFF)
#define DIGITAL_MESSAGE			0x90 // send data for a digital port (collection of 8 pins)
#define ANALOG_MESSAGE			0xE0 // send data for an analog pin (or PWM)
#define REPORT_ANALOG			0xC0 // enable analog input by pin #
#define REPORT_DIGITAL			0xD0 // enable digital input by port pair
 //
#define SET_PIN_MODE			0xF4 // set a pin to INPUT/OUTPUT/PWM/etc
#define SET_DIGITAL_PIN_VALUE   0xF5 // set value of an individual digital pin
 //
#define REPORT_VERSION			0xF9 // report protocol version
#define SYSTEM_RESET			0xFF // reset from MIDI
 //
#define START_SYSEX				0xF0 // start a MIDI Sysex message
#define END_SYSEX				0xF7 // end a MIDI Sysex message

 // extended command set using sysex (0-127/0x00-0x7F)
 /* 0x00-0x0F reserved for user-defined commands */
#define SERIAL_MESSAGE          0x60 // communicate with serial devices, including other boards
#define ENCODER_DATA            0x61 // reply with encoders current positions
#define SERVO_CONFIG            0x70 // set max angle, minPulse, maxPulse, freq
#define STRING_DATA             0x71 // a string message with 14-bits per char
#define STEPPER_DATA            0x72 // control a stepper motor
#define ONEWIRE_DATA            0x73 // send an OneWire read/write/reset/select/skip/search request
#define SHIFT_DATA              0x75 // a bitstream to/from a shift register
#define I2C_REQUEST             0x76 // send an I2C read/write request
#define I2C_REPLY               0x77 // a reply to an I2C read request
#define I2C_CONFIG              0x78 // config I2C settings such as delay times and power pins
#define EXTENDED_ANALOG         0x6F // analog write (PWM, Servo, etc) to any pin
#define PIN_STATE_QUERY         0x6D // ask for a pin's current mode and value
#define PIN_STATE_RESPONSE      0x6E // reply with pin's current mode and value
#define CAPABILITY_QUERY        0x6B // ask for supported modes and resolution of all pins
#define CAPABILITY_RESPONSE     0x6C // reply with supported modes and resolution
#define ANALOG_MAPPING_QUERY    0x69 // ask for mapping of analog to pin numbers
#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info
#define REPORT_FIRMWARE         0x79 // report name and version of the firmware
#define SAMPLING_INTERVAL       0x7A // set the poll rate of the main loop
#define SCHEDULER_DATA          0x7B // send a createtask/deletetask/addtotask/schedule/querytasks/querytask request to the scheduler
#define SYSEX_NON_REALTIME      0x7E // MIDI Reserved for non-realtime messages
#define SYSEX_REALTIME          0x7F // MIDI Reserved for realtime messages

 // pin modes
#define ARD_INPUT				0x00 // defined in Arduino.h
#define ARD_OUTPUT				0x01 // defined in Arduino.h
#define ARD_ANALOG				0x02 // analog pin in analogInput mode
#define ARD_PWM					0x03 // digital pin in PWM output mode
#define ARD_SERVO				0x04 // digital pin in Servo output mode
#define ARD_SHIFT				0x05 // shiftIn/shiftOut mode
#define ARD_I2C					0x06 // pin included in I2C setup
#define ARD_ONEWIRE				0x07 // pin configured for 1-wire
#define ARD_STEPPER				0x08 // pin configured for stepper motor
#define ARD_ENCODER				0x09 // pin configured for rotary encoders
#define ARD_SERIAL              0x0A // pin configured for serial communication
#define ARD_INPUT_PULLUP        0x0B // enable internal pull-up resistor for pin
#define ARD_IGNORE				0x7F // pin configured to be ignored by digitalWrite and capabilityResponse
#define TOTAL_PIN_MODES			13

//Stepper Subcommands
#define MAX_STEPPERS			6 // arbitrary value... may need to adjust
#define STEPPER_CONFIG			0
#define STEPPER_STEP			1

//Onewire Subcommands:
#define ONEWIRE_SEARCH_REQUEST			0x40
#define ONEWIRE_CONFIG_REQUEST			0x41
#define ONEWIRE_SEARCH_REPLY			0x42
#define ONEWIRE_READ_REPLY				0x43
#define ONEWIRE_SEARCH_ALARMS_REQUEST	0x44
#define ONEWIRE_SEARCH_ALARMS_REPLY		0x45

#define ONEWIRE_RESET_REQUEST_BIT		0x01
#define ONEWIRE_SKIP_REQUEST_BIT		0x02
#define ONEWIRE_SELECT_REQUEST_BIT		0x04
#define ONEWIRE_READ_REQUEST_BIT		0x08
#define ONEWIRE_DELAY_REQUEST_BIT		0x10
#define ONEWIRE_WRITE_REQUEST_BIT		0x20

#define ONEWIRE_WITHDATA_REQUEST_BITS	0x3C

//default value for power:
#define ONEWIRE_POWER 1

//I2C Subcommands
#define I2C_WRITE						B00000000
#define I2C_READ						B00001000
#define I2C_READ_CONTINUOUSLY			B00010000
#define I2C_STOP_READING				B00011000
#define I2C_READ_WRITE_MODE_MASK		B00011000
#define I2C_10BIT_ADDRESS_MODE_MASK		B00100000
#define I2C_END_TX_MASK					B01000000
#define I2C_STOP_TX						1
#define I2C_RESTART_TX					0
#define I2C_MAX_QUERIES					8
#define I2C_REGISTER_NOT_SPECIFIED		-1

//Encoder Subcommands
#define MAX_ENCODERS					5 // arbitrary value, may need to adjust
#define ENCODER_ATTACH					0x00
#define ENCODER_REPORT_POSITION			0x01
#define ENCODER_REPORT_POSITIONS		0x02
#define ENCODER_RESET_POSITION			0x03
#define ENCODER_REPORT_AUTO				0x04
#define ENCODER_DETACH					0x05
#define ENCODER_DIRECTION_MASK			0x40 // B01000000
#define ENCODER_CHANNEL_MASK			0x3F // B00111111

//Serial Subcommands
#define SERIAL_CONFIG					0x10
#define SERIAL_WRITE					0x20
#define SERIAL_READ						0x30
#define SERIAL_REPLY					0x40
#define SERIAL_CLOSE					0x50
#define SERIAL_FLUSH					0x60
#define SERIAL_LISTEN					0x70

// OF specific settings
//these are given by the capability query
//#define ARD_TOTAL_DIGITAL_PINS        70 // total number of pins currently supported
//#define ARD_TOTAL_ANALOG_PINS         6
#define ARD_TOTAL_PORTS					15 // total number of ports for the board

#define ARD_HIGH						1
#define ARD_LOW							0
#define ARD_ON							1
#define ARD_OFF							0

#define OF_ARDUINO_DELAY_LENGTH			4.0

struct supportedPinTypes {
	bool	inputSupported = false;
	bool	outputSupported = false;
	bool	analogSupported = false;
	bool	pwmSupported = false;
	bool	servoSupported = false;
	bool	i2cSupported = false;
	bool	serialSupported = false;
	bool	onewireSupported = false;
	bool	stepperSupported = false;
	bool	encoderSupported = false;
};

enum Firmata_Pin_Modes {
	MODE_INPUT,
	MODE_OUTPUT,
	MODE_INPUT_PULLUP,
	MODE_ANALOG,
	MODE_PWM,
	MODE_SERVO,
	MODE_I2C,
	MODE_SERIAL,
	MODE_ONEWIRE,
	MODE_STEPPER,
	MODE_ENCODER
};

enum Firmata_Stepper_Interface {
	FIRMATA_STEPPER_DRIVER = 1,
	FIRMATA_STEPPER_TWO_WIRE = 2,
	FIRMATA_STEPPER_FOUR_WIRE = 3
};

enum Firmata_Stepper_Direction {
	FIRMATA_STEPPER_CCW = 0,
	FIRMATA_STEPPER_CW = 1
};
enum Firmata_I2C_Modes {
	FIRMATA_I2C_WRITE = 0x00,
	FIRMATA_I2C_READ = 1,
	FIRMATA_I2C_CONTINUOUS_READ = 2,
	FIRMATA_I2C_STOP_READING = 3
};

struct Firmata_I2C_Data {
	int		address;
	int		reg;
	std::string	data;
};

struct Firmata_Encoder_Data {
	int		ID;
	bool	direction;
	int		position;
};

struct Firmata_Stepper_Data {
	int		id;
	int		type;
	int		data;
};

enum Firmata_Serial_Modes {
	FIRMATA_SERIAL_READ_CONTINUOUS = 0x00,
	FIRMATA_SERIAL_STOP_READING = 0x01
};

/// ids for hardware and software serial ports on the board
enum Firmata_Serial_Ports {
	HW_SERIAL0 = 0x00,
	HW_SERIAL1 = 0x01,
	HW_SERIAL2 = 0x02,
	HW_SERIAL3 = 0x03,
	SW_SERIAL0 = 0x08,
	SW_SERIAL1 = 0x09,
	SW_SERIAL2 = 0x10,
	SW_SERIAL3 = 0x11
};

struct Firmata_Serial_Data {
	Firmata_Serial_Ports	portID;
	std::string			data;
};

/// \brief This is a way to control an Arduino that has had the firmata library
/// loaded onto it, from OF.
///
/// To load firmata onto your Arduino, run the Arduino IDE, open the Examples >
/// Firmata > StandardFirmata sketch, and upload it to the Arduino board.
///
/// Once the ofArduino instance returns true from isArduinoReady() you can set
/// the mode of the different digital pins using sendDigitalPinMode()
///
/// This sets pin 9 to input so that it can read a button press
/// ~~~~{.cpp}
///     sendDigitalPinMode(9, ARD_INPUT)
/// ~~~~
///
/// This sets pin 9 to be a PWM out pin. Note that this only works on pins
/// that are PWM enabled.
/// ~~~~{.cpp}
///     sendDigitalPinMode(9, ARD_PWM)
/// ~~~~
class ofArduino {

public:
	/// \name Constructor and Destructor
	/// \{

	ofArduino();

	virtual ~ofArduino();

	/// \}
	/// \name Connect
	/// \{

	/// \brief Opens a serial port connection to the arduino
	/// \param device The name of the device.
	/// You can get the name from the Arduino IDE
	/// \param baud The baud rate the connection uses
	bool connect(const std::string & device, int baud = 57600);

	/// \brief Returns true if a succesfull connection has been established
	/// and the Arduino has reported a firmware
	bool isInitialized() const;

	bool isArduinoReady();

	/// \brief Closes the serial port connection.
	/// Does not turn the Arduino off.
	void disconnect();

	/// \}
	/// \name Update
	/// \{

	/// \brief Polls data from the serial port, this has to be called periodically
	void update();

	/// \}
	/// \name Setup
	/// \{

	/// \brief Setting a pins mode to ARD_INPUT turns on reporting for the port the pin is on
	/// \param pin Pin on arduino (2-13)
	/// \param mode `ARD_INPUT`, `ARD_OUTPUT`, `ARD_PWM`
	/// \note Analog pins 0-5 can be used as digitial pins 16-21 but if the
	/// mode of _one_ of these pins is set to `ARD_INPUT` then _all_ analog pin
	/// reporting will be turned off
	void sendDigitalPinMode(int pin, int mode);

	void sendAnalogPinReporting(int pin, int mode);

	void setUseDelay(bool bDelay);

	void setDigitalHistoryLength(int length);
	void setAnalogHistoryLength(int length);
	void setStringHistoryLength(int length);
	void setSysExHistoryLength(int nSysEx);

	/// \}
	/// \name Senders
	/// \{

	void sendDigital(int pin, int value, bool force = false);

	void sendPwm(int pin, int value, bool force = false);

	void sendSysEx(int command, std::vector <unsigned char> data);

	bool isAttached();



	/// \brief Send a string to the Arduino
	/// \note Firmata can not handle strings longer than 12 characters.
	void sendString(std::string str);

	void sendProtocolVersionRequest();

	void sendFirmwareVersionRequest();

	void sendPinCofigurationRequest();

	void sendPinCapabilityRequest();

	void sendAnalogMappingRequest();

	void sendPinStateQuery(int pin);

	/// \brief This will cause your Arduino to reset and boot into the program again.
	void sendReset();

	/// \brief Sends the `FIRMATA_START_SYSEX` command
	void sendSysExBegin();

	/// \brief Sends the `FIRMATA_END_SYSEX` command
	void sendSysExEnd();

	/// \brief Sends a byte without wrapping it in a firmata message.
	///
	/// Data has to be in the 0-127 range. Values > 127 will be interpreted as
	/// commands.
	void sendByte(unsigned char byte);

	/// \brief Send value as two 7 bit bytes.
	///
	/// Sends a value as two 7-bit bytes without wrapping it in a firmata
	/// message.  Values in the range 0 - 16384 will be sent as two bytes
	/// within the 0-127 data range.
	///
	/// \param value The value to send.
	void sendValueAsTwo7bitBytes(int value);

	/// \}
	/// \name Getters
	/// \{

	/// \brief Returns the last set PWM value (0-255) for the given pin
	///
	/// The pins mode has to be ARD_PWM
	///
	/// On the Arduino Uno the following pins are supported: 3, 5, 6, 9, 10 and 11
	/// \note Pin 16-21 can also be used if analog inputs 0-5 are used as digital pins
	int getPwm(int pin) const;

	/// \brief Returns the last received value (if the pin mode is ARD_INPUT)
	/// or the last set value (if the pin mode is ARD_OUTPUT) for the given
	/// pin
	///
	/// Returns whether the pin is reading high or low, 1 or 0. You can test
	/// against this with an if() statement which is handy:
	/// ~~~~{.cpp}
	///     if(arduino.getDigital(pin)){
	///         // do something on high
	///     } else {
	///         // do something on low
	///     }
	/// ~~~~
	/// \note Pin 16-21 can also be used if analog inputs 0-5 are used as digital pins
	int getDigital(int pin) const;

	/// \brief Returns the analog in value that the pin is currently reading.
	/// because the Arduino has a 10 bit ADC you get between 0 and 1023 for
	/// possible values.
	///
	/// \param pin The pin number (0-5)
	int getAnalog(int pin) const;

	/// \returns the last received SysEx message.
	std::vector <unsigned char> getSysEx() const;

	/// \returns the last received string.
	std::string getString() const;

	/// \returns the major firmware version.
	int getMajorFirmwareVersion() const;

	/// \returns the minor firmware version.
	int getMinorFirmwareVersion() const;

	/// \returns the name of the firmware.
	std::string getFirmwareName() const;

	/// \brief Returns a pointer to the digital data history list for the
	/// given pin
	/// \note Pin 16-21 can also be used if analog inputs 0-5 are used as
	/// digital pins
	/// \param pin The pin number (2-13)
	std::list <int> * getDigitalHistory(int pin);

	/// \brief Returns a pointer to the analog data history list for the given pin.
	/// \param pin The Arduino Uno pin: 0-5
	std::list <int> * getAnalogHistory(int pin);

	/// \returns a pointer to the SysEx history.
	std::list <std::vector <unsigned char> > * getSysExHistory();

	/// \returns a pointer to the string history.
	std::list <std::string> * getStringHistory();

	/// \brief Get the pin mode of the given pin
	///
	/// \returns `ARD_INPUT`, `ARD_OUTPUT`, `ARD_PWM`, `ARD_SERVO`, `ARD_ANALOG`
	int getDigitalPinMode(int pin) const;

	/// \returns `ARD_ON` or `ARD_OFF`
	int getAnalogPinReporting(int pin) const;

	/// \brief Useful for parsing SysEx messages
	int getValueFromTwo7bitBytes(unsigned char lsb, unsigned char msb);

	int getInvertedValueFromTwo7bitBytes(unsigned char lsb, unsigned char msb);

	/// \}
	/// \name Events
	/// \{

	/// \brief Triggered when a digital pin changes value, the pin that
	/// changed is passed as an argument.
	ofEvent <const int> EDigitalPinChanged;

	/// \brief Triggered when an analog pin changes value, the pin that
	/// changed is passed as an argument.
	ofEvent <const int> EAnalogPinChanged;

	/// \brief Triggered when a SysEx message that isn't in the extended
	/// command set is received, the SysEx message is passed as an argument
	ofEvent <const std::vector <unsigned char> > ESysExReceived;

	/// \brief Triggered when a firmware version is received, the major version
	/// is passed as an argument.
	ofEvent <const int> EFirmwareVersionReceived;

	/// \brief Triggered when the firmware version is received upon connect,
	/// the major firmware version is passed as an argument. From this point
	/// it's safe to send to the Arduino.
	ofEvent <const int> EInitialized;

	/// \brief Triggered when a string is received, the string is passed as an
	/// argument
	ofEvent <const std::string> EStringReceived;

	/// \brief triggered when a stepper has finished rotating. Returns which 
	/// stepper has complted its rotation
	ofEvent<const Firmata_Stepper_Data> EStepperDataReceived;

	/// \brief triggered when the I2C bus returns data after a read request
	ofEvent<const Firmata_I2C_Data> EI2CDataRecieved;

	/// \brief triggered when the encoder returns data after a read request
	ofEvent<const std::vector<Firmata_Encoder_Data> > EEncoderDataReceived;

	/// \brief triggered when a Serial message is received. Returns which 
	/// port and its data
	ofEvent<const Firmata_Serial_Data> ESerialDataReceived;

	ofEvent<const std::pair<int, Firmata_Pin_Modes> > EPinStateResponseReceived;

	/// \}
	/// \name Servos
	/// \{

	/// \brief Send a value to a servo.
	///
	/// A servo has to be atached to the pin prior
	/// \param pin 9 or 10
	/// \param value The value to send
	void sendServo(int pin, int value, bool force = false);

	/// \param angle parameter DEPRECATED as of Firmata 2.2
	void sendServoAttach(int pin, int minPulse = 544, int maxPulse = 2400);

	/// \brief Detaches a servo on a pin
	/// \note sendServoDetach DEPRECATED as of Firmata 2.2
	//void sendServoDetach(int pin);

	/// \returns the last set servo value for a pin if the pin has a servo attached.
	int getServo(int pin) const;

	/// \}
	/// \name Stepper
	/// \{

	/// -- stepper
	void sendStepper2Wire(int dirPin, int stepPin, int stepsPerRev = 200);
	/// the pins has to have a stepper attached

	void sendStepper4Wire(int pin1, int pin2, int pin3, int pin4, int stepsPerRev = 200);
	/// the pins has to have a stepper attached

	void sendStepperMove(int stepperID, int direction, int steps, int speed = 0, float acceleration = 0, float deceleration = 0);
	/// set the steps to move


	/// \}
	/// \name I2C
	/// \{

	/// \brief Sends a I2C config request
	///
	///  Must be called before an I2C Read or Write
	/// \param {number} delay in microseconds to set for I2C Read
	void sendI2CConfig(int delay);
	bool isI2CConfigured();

	/// \brief Asks the arduino to send an I2C request to a device
	///
	/// \param {number} slaveAddress The address of the I2C device
	/// \param {Array} bytes The bytes to send to the device
	void sendI2CWriteRequest(char slaveAddress, unsigned char * bytes, int numOfBytes, int reg = -1);
	void sendI2CWriteRequest(char slaveAddress, const char * bytes, int numOfBytes, int reg = -1);
	void sendI2CWriteRequest(char slaveAddress, char * bytes, int numOfBytes, int reg = -1);
	void sendI2CWriteRequest(char slaveAddress, std::vector<char> bytes, int reg = -1);

	/// \brief Asks the arduino to request bytes from an I2C device
	///
	/// \param {number} slaveAddress The address of the I2C device
	/// \param {number} numBytes The number of bytes to receive.
	/// \param {function} callback A function to call when we have received the bytes.
	void sendI2CReadRequest(char address, int numBytes, int reg = -1);

	/// \brief Initialize a continuous I2C read.
	///
	/// \param {number} address    The address of the I2C device
	/// \param {number} register   Optionally set the register to read from.
	/// \param {number} numBytes   The number of bytes to receive.
	void sendI2ContinuousReadRequest(char address, int numBytes, int reg = -1);

	/// \}
	/// \name OneWire
	/// \{

	/// \brief Configure the passed pin as the controller in a 1-wire bus.
	///
	/// Pass as enableParasiticPower true if you want the data pin to power the bus.
	/// \param pin
	/// \param enableParasiticPower
	///
	void sendOneWireConfig(int pin, bool enableParasiticPower);

	/// \brief Searches for 1-wire devices on the bus.  
	///
	/// \param pin
	void sendOneWireSearch(int pin);

	/// \brief Searches for 1-wire devices on the bus in an alarmed state.  
	///
	/// \param pin
	void sendOneWireAlarmsSearch(int pin);
	void sendOneWireSearch(char type, int pin);

	/// \brief Reads data from a device on the bus.
	///
	/// N.b. ConfigurableFirmata will issue the 1-wire select command internally.
	/// \param pin
	/// \param device
	/// \param numBytesToRead
	/// \param callback
	void sendOneWireRead(int pin, std::vector<unsigned char> devices, int numBytesToRead);
	
	/// \brief Resets all devices on the bus.
	/// \param pin
	void sendOneWireReset(int pin);

	/// \brief Writes data to the bus to be received by the passed device.  
	///
	/// The device should be obtained from a previous call to sendOneWireSearch. ConfigurableFirmata will issue the 1-wire select command internally.
	/// \param pin
	/// \param device
	/// \param data
	void sendOneWireWrite(int pin, std::vector<unsigned char> devices, std::vector<unsigned char> data);

	/// \brief Tells firmata to not do anything for the passed amount of ms.
	/// 
	/// For when you need to give a device attached to the bus time to do a calculation.
	/// \param pin
	void sendOneWireDelay(int pin, unsigned int delay);

	/// \brief Sends the passed data to the passed device on the bus, reads the specified number of bytes.
	///
	/// ConfigurableFirmata will issue the 1-wire select command internally.
	/// \param pin
	/// \param device
	/// \param data
	/// \param numBytesToRead
	/// \param callback
	void sendOneWireWriteAndRead(int pin, std::vector<unsigned char> devices, std::vector<unsigned char> data, int numBytesToRead);
	void sendOneWireRequest(int pin, unsigned char subcommand, std::vector<unsigned char> devices, int numBytesToRead, unsigned char correlationId, unsigned int delay, std::vector<unsigned char> dataToWrite);

	/// \}
	/// \name Encoder
	/// \{

	void attachEncoder(int pinA, int pinB);
	void getEncoderPosition(int encoderNum);
	void getAllEncoderPositions();
	void resetEncoderPosition(int encoderNum);
	void enableEncoderReporting();
	void disableEncoderReporting();
	void detachEncoder(int encoderNum);

	/// \}
	/// \name Serial
	/// \{

	/// \brief Asks the Arduino to configure a hardware or serial port.
	///
	/// \param portID The serial port to use (HW_SERIAL1, HW_SERIAL2, HW_SERIAL3, SW_SERIAL0, SW_SERIAL1, SW_SERIAL2, SW_SERIAL3)
	/// \param baud  The baud rate of the serial port
	/// \param rxPin [SW Serial only] The RX pin of the SoftwareSerial instance
	/// \param txPin [SW Serial only] The TX pin of the SoftwareSerial instance
	void sendSerialConfig(Firmata_Serial_Ports portID, int baud, int rxPin, int txPin);

	/// \brief Write an array of bytes to the specified serial port.
	///
	/// \param portId The serial port to write to.
	/// \param bytes An array of bytes to write to the serial port.
	/// \param numOfBytes length of the array of bytes.
	void serialWrite(Firmata_Serial_Ports port, unsigned char * bytes, int numOfBytes);

	/// \brief  Start continuous reading of the specified serial port.
	///
	/// The port is checked for data each iteration of the main Arduino loop.
	/// \param portId The serial port to start reading continuously.
	/// \param  maxBytesToRead [Optional] The maximum number of bytes to read per iteration.
	/// \note If there are less bytes in the buffer, the lesser number of bytes will be returned. A value of 0 indicates that all available bytes in the buffer should be read.
	void serialRead(Firmata_Serial_Ports port, int maxBytesToRead);

	/// \brief Stop continuous reading of the specified serial port.
	///
	/// This does not close the port, it stops reading it but keeps the port open.
	/// \param portId The serial port to stop reading.
	void serialStop(Firmata_Serial_Ports portID);

	/// \brief  Close the specified serial port.
	///
	/// \param portId The serial port to close.
	void serialClose(Firmata_Serial_Ports portID);

	/// \brief Flush the specified serial port.
	///
	/// For hardware serial, this waits for the transmission of outgoing serial data to complete.For software serial, this removed any buffered incoming serial data.
	/// \param portId The serial port to listen on.
	void serialFlush(Firmata_Serial_Ports portID);

	/// \brief For SoftwareSerial only. Only a single SoftwareSerial instance can read data at a time.
	///
	/// Call this method to set this port to be the reading port in the case there are multiple SoftwareSerial instances.
	/// \param portId The serial port to flush.
	void serialListen(Firmata_Serial_Ports portID);


	std::map<int, supportedPinTypes> getPinCapabilities() { return pinCapabilities; }

	int getTotalPins() { return _totalDigitalPins; }

	int getNumAnalogPins() { return _totalAnalogPins; }

private:
	mutable bool _initialized; ///\< \brief Indicate that pins are initialized.

	void initPins();
	mutable int _totalDigitalPins; ///\< \brief Indicate the total number of digital pins of the board in use.
	mutable int _totalAnalogPins;

	void sendDigitalPinReporting(int pin, int mode);

	void sendDigitalPortReporting(int port, int mode);

	void purge();

	void processData(unsigned char inputData);
	void processDigitalPort(int port, unsigned char value);
	virtual void processSysExData(std::vector <unsigned char> data);

	ofSerial _port;
	int _portStatus;

	// --- history variables
	int _analogHistoryLength;
	int _digitalHistoryLength;
	int _stringHistoryLength;
	int _sysExHistoryLength;

	// --- data processing variables
	int _waitForData;
	int _executeMultiByteCommand; ///< \brief Indicate Firmata command to execute.
	int _multiByteChannel; ///< \brief Indicates which pin the data came from.

	// --- data holders
	unsigned char _storedInputData[FIRMATA_MAX_DATA_BYTES];
	std::vector <unsigned char> _sysExData;
	int _majorFirmwareVersion;
	int _minorFirmwareVersion;
	std::string _firmwareName;

	std::list <std::vector <unsigned char> > _sysExHistory;
	// maintains a history of received sysEx messages (excluding SysEx messages in the extended command set)

	std::list <std::string> _stringHistory;
	// maintains a history of received strings

	//we dont know the number of pintypes until we do a configuration request so just a placeholder for now
	mutable std::vector<std::list <int> > _analogHistory;
	// a history of received data for each analog pin

	mutable std::vector<std::list <int> > _digitalHistory;
	// a history of received data for each digital pin

	mutable std::vector<int> _digitalPinMode;
	// the modes for all digital pins

	mutable std::vector<int> _digitalPinValue;
	// the last set values (DIGITAL/PWM) on all digital pins

	mutable int _digitalPortValue[ARD_TOTAL_PORTS];
	// the last set values on all ports

	mutable int _digitalPortReporting[ARD_TOTAL_PORTS];
	// whether pin reporting is enabled / disabled

	mutable std::vector<int> _digitalPinReporting;
	// whether pin reporting is enabled / disabled

	mutable std::vector<int> _analogPinReporting;
	// whether pin reporting is enabled / disabled

	bool bUseDelay;

	mutable bool connected; ///< \brief This yields true if a serial connection to Arduino exists.

	float connectTime; ///< \brief This represents the (running) time of establishing a serial connection.

	mutable std::vector<int> _servoValue;
	// the last set servo values

	bool _i2cConfigured;

	int _numSteppers;

	int _numEncoders;

	int _encoderID;

	int _firstAnalogPin;

	bool firmataInputSupported,
		firmataOutputSupported,
		firmataAnalogSupported,
		firmataPwmSupported,
		firmataServoSupported,
		firmataI2cSupported,
		firmataOnewireSupported,
		firmataStepperSupported,
		firmataEncoderSupported,
		firmataSerialSupported;

	mutable std::map<int, supportedPinTypes> pinCapabilities;
	mutable std::map<int, int> analogPinMap;


	bool isAnalogPin(int pin) const;
	bool isPin(int pin) const;

	int convertAnalogPinToDigital(size_t pin) const;
	int convertDigitalPinToAnalog(size_t pin) const;
};

typedef ofArduino ofStandardFirmata;

