[136] | 1 | /*
|
---|
| 2 | * Firmata is a generic protocol for communicating with microcontrollers
|
---|
| 3 | * from software on a host computer. It is intended to work with
|
---|
| 4 | * any host computer software package.
|
---|
| 5 | *
|
---|
| 6 | * To download a host software package, please clink on the following link
|
---|
| 7 | * to open the download page in your default browser.
|
---|
| 8 | *
|
---|
| 9 | * http://firmata.org/wiki/Download
|
---|
| 10 | */
|
---|
| 11 |
|
---|
| 12 | /*
|
---|
| 13 | Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
|
---|
| 14 |
|
---|
| 15 | This library is free software; you can redistribute it and/or
|
---|
| 16 | modify it under the terms of the GNU Lesser General Public
|
---|
| 17 | License as published by the Free Software Foundation; either
|
---|
| 18 | version 2.1 of the License, or (at your option) any later version.
|
---|
| 19 |
|
---|
| 20 | See file LICENSE.txt for further informations on licensing terms.
|
---|
| 21 | */
|
---|
| 22 |
|
---|
| 23 | /*
|
---|
| 24 | * This is an old version of StandardFirmata (v2.0). It is kept here because
|
---|
| 25 | * its the last version that works on an ATMEGA8 chip. Also, it can be used
|
---|
| 26 | * for host software that has not been updated to a newer version of the
|
---|
| 27 | * protocol. It also uses the old baud rate of 115200 rather than 57600.
|
---|
| 28 | */
|
---|
| 29 |
|
---|
| 30 | #include <EEPROM.h>
|
---|
| 31 | #include <Firmata.h>
|
---|
| 32 |
|
---|
| 33 | /*==============================================================================
|
---|
| 34 | * GLOBAL VARIABLES
|
---|
| 35 | *============================================================================*/
|
---|
| 36 |
|
---|
| 37 | /* analog inputs */
|
---|
| 38 | int analogInputsToReport = 0; // bitwise array to store pin reporting
|
---|
| 39 | int analogPin = 0; // counter for reading analog pins
|
---|
| 40 |
|
---|
| 41 | /* digital pins */
|
---|
| 42 | byte reportPINs[TOTAL_PORTS]; // PIN == input port
|
---|
| 43 | byte previousPINs[TOTAL_PORTS]; // PIN == input port
|
---|
| 44 | byte pinStatus[TOTAL_PINS]; // store pin status, default OUTPUT
|
---|
| 45 | byte portStatus[TOTAL_PORTS];
|
---|
| 46 |
|
---|
| 47 | /* timer variables */
|
---|
| 48 | unsigned long currentMillis; // store the current value from millis()
|
---|
| 49 | unsigned long previousMillis; // for comparison with currentMillis
|
---|
| 50 |
|
---|
| 51 |
|
---|
| 52 | /*==============================================================================
|
---|
| 53 | * FUNCTIONS
|
---|
| 54 | *============================================================================*/
|
---|
| 55 |
|
---|
| 56 | void outputPort(byte portNumber, byte portValue)
|
---|
| 57 | {
|
---|
[224] | 58 | portValue = portValue & ~ portStatus[portNumber];
|
---|
[136] | 59 | if (previousPINs[portNumber] != portValue) {
|
---|
| 60 | Firmata.sendDigitalPort(portNumber, portValue);
|
---|
| 61 | previousPINs[portNumber] = portValue;
|
---|
| 62 | Firmata.sendDigitalPort(portNumber, portValue);
|
---|
| 63 | }
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | /* -----------------------------------------------------------------------------
|
---|
| 67 | * check all the active digital inputs for change of state, then add any events
|
---|
| 68 | * to the Serial output queue using Serial.print() */
|
---|
| 69 | void checkDigitalInputs(void)
|
---|
| 70 | {
|
---|
| 71 | byte i, tmp;
|
---|
| 72 | for (i = 0; i < TOTAL_PORTS; i++) {
|
---|
| 73 | if (reportPINs[i]) {
|
---|
| 74 | switch (i) {
|
---|
[224] | 75 | case 0: outputPort(0, PIND & ~ B00000011); break; // ignore Rx/Tx 0/1
|
---|
[136] | 76 | case 1: outputPort(1, PINB); break;
|
---|
| 77 | case 2: outputPort(2, PINC); break;
|
---|
| 78 | }
|
---|
| 79 | }
|
---|
| 80 | }
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | // -----------------------------------------------------------------------------
|
---|
| 84 | /* sets the pin mode to the correct state and sets the relevant bits in the
|
---|
| 85 | * two bit-arrays that track Digital I/O and PWM status
|
---|
| 86 | */
|
---|
| 87 | void setPinModeCallback(byte pin, int mode) {
|
---|
| 88 | byte port = 0;
|
---|
| 89 | byte offset = 0;
|
---|
| 90 |
|
---|
| 91 | if (pin < 8) {
|
---|
| 92 | port = 0;
|
---|
| 93 | offset = 0;
|
---|
| 94 | } else if (pin < 14) {
|
---|
| 95 | port = 1;
|
---|
| 96 | offset = 8;
|
---|
| 97 | } else if (pin < 22) {
|
---|
| 98 | port = 2;
|
---|
| 99 | offset = 14;
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | if (pin > 1) { // ignore RxTx (pins 0 and 1)
|
---|
| 103 | pinStatus[pin] = mode;
|
---|
| 104 | switch (mode) {
|
---|
| 105 | case INPUT:
|
---|
| 106 | pinMode(pin, INPUT);
|
---|
[224] | 107 | portStatus[port] = portStatus[port] & ~ (1 << (pin - offset));
|
---|
[136] | 108 | break;
|
---|
| 109 | case OUTPUT:
|
---|
| 110 | digitalWrite(pin, LOW); // disable PWM
|
---|
| 111 | case PWM:
|
---|
| 112 | pinMode(pin, OUTPUT);
|
---|
| 113 | portStatus[port] = portStatus[port] | (1 << (pin - offset));
|
---|
| 114 | break;
|
---|
[224] | 115 | //case ANALOG: // TODO figure this out
|
---|
[136] | 116 | default:
|
---|
| 117 | Firmata.sendString("");
|
---|
| 118 | }
|
---|
| 119 | // TODO: save status to EEPROM here, if changed
|
---|
| 120 | }
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | void analogWriteCallback(byte pin, int value)
|
---|
| 124 | {
|
---|
[224] | 125 | setPinModeCallback(pin, PIN_MODE_PWM);
|
---|
[136] | 126 | analogWrite(pin, value);
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | void digitalWriteCallback(byte port, int value)
|
---|
| 130 | {
|
---|
| 131 | switch (port) {
|
---|
| 132 | case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1)
|
---|
| 133 | // 0xFF03 == B1111111100000011 0x03 == B00000011
|
---|
[224] | 134 | PORTD = (value & ~ 0xFF03) | (PORTD & 0x03);
|
---|
[136] | 135 | break;
|
---|
| 136 | case 1: // pins 8-13 (14,15 are disabled for the crystal)
|
---|
| 137 | PORTB = (byte)value;
|
---|
| 138 | break;
|
---|
| 139 | case 2: // analog pins used as digital
|
---|
| 140 | PORTC = (byte)value;
|
---|
| 141 | break;
|
---|
| 142 | }
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | // -----------------------------------------------------------------------------
|
---|
| 146 | /* sets bits in a bit array (int) to toggle the reporting of the analogIns
|
---|
| 147 | */
|
---|
| 148 | //void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
|
---|
| 149 | //}
|
---|
| 150 | void reportAnalogCallback(byte pin, int value)
|
---|
| 151 | {
|
---|
| 152 | if (value == 0) {
|
---|
[224] | 153 | analogInputsToReport = analogInputsToReport & ~ (1 << pin);
|
---|
[136] | 154 | }
|
---|
| 155 | else { // everything but 0 enables reporting of that pin
|
---|
| 156 | analogInputsToReport = analogInputsToReport | (1 << pin);
|
---|
| 157 | }
|
---|
| 158 | // TODO: save status to EEPROM here, if changed
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | void reportDigitalCallback(byte port, int value)
|
---|
| 162 | {
|
---|
| 163 | reportPINs[port] = (byte)value;
|
---|
| 164 | if (port == 2) // turn off analog reporting when used as digital
|
---|
| 165 | analogInputsToReport = 0;
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | /*==============================================================================
|
---|
| 169 | * SETUP()
|
---|
| 170 | *============================================================================*/
|
---|
| 171 | void setup()
|
---|
| 172 | {
|
---|
| 173 | byte i;
|
---|
| 174 |
|
---|
| 175 | Firmata.setFirmwareVersion(2, 0);
|
---|
| 176 |
|
---|
| 177 | Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
|
---|
| 178 | Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
|
---|
| 179 | Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
|
---|
| 180 | Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
|
---|
| 181 | Firmata.attach(SET_PIN_MODE, setPinModeCallback);
|
---|
| 182 |
|
---|
| 183 | portStatus[0] = B00000011; // ignore Tx/RX pins
|
---|
| 184 | portStatus[1] = B11000000; // ignore 14/15 pins
|
---|
| 185 | portStatus[2] = B00000000;
|
---|
| 186 |
|
---|
| 187 | // for(i=0; i<TOTAL_PINS; ++i) { // TODO make this work with analogs
|
---|
| 188 | for (i = 0; i < 14; ++i) {
|
---|
| 189 | setPinModeCallback(i, OUTPUT);
|
---|
| 190 | }
|
---|
| 191 | // set all outputs to 0 to make sure internal pull-up resistors are off
|
---|
| 192 | PORTB = 0; // pins 8-15
|
---|
| 193 | PORTC = 0; // analog port
|
---|
| 194 | PORTD = 0; // pins 0-7
|
---|
| 195 |
|
---|
| 196 | // TODO rethink the init, perhaps it should report analog on default
|
---|
| 197 | for (i = 0; i < TOTAL_PORTS; ++i) {
|
---|
| 198 | reportPINs[i] = false;
|
---|
| 199 | }
|
---|
| 200 | // TODO: load state from EEPROM here
|
---|
| 201 |
|
---|
| 202 | /* send digital inputs here, if enabled, to set the initial state on the
|
---|
| 203 | * host computer, since once in the loop(), this firmware will only send
|
---|
| 204 | * digital data on change. */
|
---|
[224] | 205 | if (reportPINs[0]) outputPort(0, PIND & ~ B00000011); // ignore Rx/Tx 0/1
|
---|
[136] | 206 | if (reportPINs[1]) outputPort(1, PINB);
|
---|
| 207 | if (reportPINs[2]) outputPort(2, PINC);
|
---|
| 208 |
|
---|
| 209 | Firmata.begin(115200);
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | /*==============================================================================
|
---|
| 213 | * LOOP()
|
---|
| 214 | *============================================================================*/
|
---|
| 215 | void loop()
|
---|
| 216 | {
|
---|
| 217 | /* DIGITALREAD - as fast as possible, check for changes and output them to the
|
---|
| 218 | * FTDI buffer using Serial.print() */
|
---|
| 219 | checkDigitalInputs();
|
---|
| 220 | currentMillis = millis();
|
---|
| 221 | if (currentMillis - previousMillis > 20) {
|
---|
| 222 | previousMillis += 20; // run this every 20ms
|
---|
| 223 | /* SERIALREAD - Serial.read() uses a 128 byte circular buffer, so handle
|
---|
| 224 | * all serialReads at once, i.e. empty the buffer */
|
---|
| 225 | while (Firmata.available())
|
---|
| 226 | Firmata.processInput();
|
---|
| 227 | /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
|
---|
| 228 | * 60 bytes. use a timer to sending an event character every 4 ms to
|
---|
| 229 | * trigger the buffer to dump. */
|
---|
| 230 |
|
---|
| 231 | /* ANALOGREAD - right after the event character, do all of the
|
---|
| 232 | * analogReads(). These only need to be done every 4ms. */
|
---|
| 233 | for (analogPin = 0; analogPin < TOTAL_ANALOG_PINS; analogPin++) {
|
---|
| 234 | if ( analogInputsToReport & (1 << analogPin) ) {
|
---|
| 235 | Firmata.sendAnalog(analogPin, analogRead(analogPin));
|
---|
| 236 | }
|
---|
| 237 | }
|
---|
| 238 | }
|
---|
| 239 | }
|
---|