├── README.md ├── .gitignore ├── sample ├── TestMasterLibrary.ino └── MdbMaster.ino ├── MdbMaster.h ├── MdbBillValidator.h ├── wiring_private.h ├── MdbMaster.cpp ├── MdbSerial.h ├── MdbBillValidator.cpp ├── Arduino.h └── MdbSerial.cpp /README.md: -------------------------------------------------------------------------------- 1 | MdbBillValidator 2 | ================ 3 | 4 | Libraries for a Vending Machine Controller to communicate with a Bill Validator Peripheral. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /sample/TestMasterLibrary.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MdbBillValidator validator; 5 | SoftwareSerial pcSerial(7, 8); 6 | 7 | void setup() 8 | { 9 | validator = MdbBillValidator(); 10 | 11 | pcSerial.begin(9600); 12 | 13 | validator.SoftReset(); 14 | delay(100); 15 | 16 | //validator.Poll(); 17 | //delay(20); 18 | //validator.SendAck(); 19 | //delay(10); 20 | 21 | } 22 | 23 | void loop() 24 | { 25 | int result = validator.GetSetup(); 26 | delay(30); 27 | 28 | if (result == 0) 29 | { 30 | String x = validator.ToString() + "\n\n"; 31 | pcSerial.print(x); 32 | } 33 | else if (result == -1) 34 | { 35 | pcSerial.print("NAK\n\n"); 36 | } 37 | else if (result == -2) 38 | { 39 | pcSerial.print("Wrong Number of bytes received.\n\n"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /MdbMaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | MdbMaster.h - MDB master library for Wiring 3 | Copyright (c) 2014 Justin T. Conroy. All right reserved. 4 | Created 16 March 2014 by Justin T. Conroy 5 | 6 | This library is an API for accessing the MDB device. It makes use of a 7 | modified version of Arduino's HardwareSerial library to communicate 8 | with devices. This libary provides a convenient interface to common 9 | commands. 10 | */ 11 | 12 | #ifndef MdbMaster_h 13 | #define MdbMaster_h 14 | 15 | #include "MdbSerial.h" 16 | #include 17 | 18 | // Response Codes 19 | #define ACK 0x00 // Acknowledge 20 | #define RET 0xAA // Retransmit (VMC only) 21 | #define NAK 0xFF // Negative Acknowledge 22 | 23 | #define MAX_MSG_SIZE 36 24 | 25 | class MdbMaster 26 | { 27 | public: 28 | MdbMaster(); 29 | 30 | virtual void HardReset(); 31 | 32 | virtual void SendAck(); 33 | virtual void SendRet(); 34 | virtual void SendNak(); 35 | 36 | virtual void SendCommand(unsigned char, unsigned char); 37 | virtual void SendCommand(unsigned char, unsigned char, 38 | unsigned char*, unsigned int); 39 | 40 | virtual int GetResponse(unsigned char*, unsigned int*); 41 | 42 | 43 | }; 44 | 45 | #endif // MdbMaster_h 46 | 47 | -------------------------------------------------------------------------------- /MdbBillValidator.h: -------------------------------------------------------------------------------- 1 | /* 2 | MdbBillValidator.h - MDB bill validator library for Wiring 3 | Copyright (c) 2014 Justin T. Conroy. All right reserved. 4 | Created 24 March 2014 by Justin T. Conroy 5 | 6 | This library is an API for accessing the MDB device. It makes use of a 7 | modified version of Arduino's HardwareSerial library to communicate 8 | with devices. This libary provides a convenient interface to common 9 | commands. 10 | 11 | This library is synchronous, meaning that all of the methods are blocking 12 | and will wait for a response (or a timeout) before returning a result. 13 | */ 14 | 15 | #ifndef MdbBillValidator_h 16 | #define MdbBillValidator_h 17 | 18 | #include "MdbMaster.h" 19 | 20 | // Bill Validator Peripheral Address. 21 | #define BILL_ADDR 0x30 22 | 23 | // Biller commands 24 | #define RESET 0x00 25 | #define SETUP 0x01 26 | #define STATUS 0x01 // Legacy definition of SETUP. 27 | #define SECURITY 0x02 28 | #define POLL 0x03 29 | #define BILLTYPE 0x04 30 | #define ESCROW 0x05 31 | #define STACKER 0x06 32 | #define EXP_CMD 0x07 33 | 34 | 35 | class MdbBillValidator 36 | { 37 | public: 38 | MdbBillValidator(); 39 | 40 | virtual void SendAck(); 41 | virtual void SendRet(); 42 | virtual void SendNak(); 43 | 44 | virtual void SoftReset(); 45 | 46 | virtual int GetSetup(); 47 | virtual int SetSecurity(unsigned int); 48 | 49 | virtual int Poll(); 50 | 51 | //virtual int SetBillTypes(unsigned int); 52 | 53 | //virtual int ReleaseBillInEscrow(); 54 | //virtual int RejectBillInEscrow(); 55 | 56 | //virtual int GetStackerCount(); 57 | 58 | // Expansion Commands 59 | //virtual int SendExpansionCommand(); 60 | 61 | virtual unsigned int FeatureLevel(); 62 | virtual unsigned int CountryCode(); 63 | virtual unsigned int BillScaleFactor(); 64 | virtual unsigned int DecimalPlaces(); 65 | virtual unsigned int StackerCapacity(); 66 | virtual unsigned int BillSecurityLevels(); 67 | virtual bool EscrowEnabled(); 68 | virtual unsigned char* BillTypeCredit(); 69 | 70 | virtual String ToString(); 71 | 72 | }; 73 | 74 | #endif /* MdbBillValidator_h */ 75 | -------------------------------------------------------------------------------- /wiring_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | wiring_private.h - Internal header file. 3 | Part of Arduino - http://www.arduino.cc/ 4 | 5 | Copyright (c) 2005-2006 David A. Mellis 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General 18 | Public License along with this library; if not, write to the 19 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, 20 | Boston, MA 02111-1307 USA 21 | 22 | $Id: wiring.h 239 2007-01-12 17:58:39Z mellis $ 23 | */ 24 | 25 | #ifndef WiringPrivate_h 26 | #define WiringPrivate_h 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "Arduino.h" 34 | 35 | #ifdef __cplusplus 36 | extern "C"{ 37 | #endif 38 | 39 | #ifndef cbi 40 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 41 | #endif 42 | #ifndef sbi 43 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 44 | #endif 45 | 46 | #define EXTERNAL_INT_0 0 47 | #define EXTERNAL_INT_1 1 48 | #define EXTERNAL_INT_2 2 49 | #define EXTERNAL_INT_3 3 50 | #define EXTERNAL_INT_4 4 51 | #define EXTERNAL_INT_5 5 52 | #define EXTERNAL_INT_6 6 53 | #define EXTERNAL_INT_7 7 54 | 55 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 56 | #define EXTERNAL_NUM_INTERRUPTS 8 57 | #elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) 58 | #define EXTERNAL_NUM_INTERRUPTS 3 59 | #elif defined(__AVR_ATmega32U4__) 60 | #define EXTERNAL_NUM_INTERRUPTS 5 61 | #else 62 | #define EXTERNAL_NUM_INTERRUPTS 2 63 | #endif 64 | 65 | typedef void (*voidFuncPtr)(void); 66 | 67 | #ifdef __cplusplus 68 | } // extern "C" 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /MdbMaster.cpp: -------------------------------------------------------------------------------- 1 | #include "MdbMaster.h" 2 | 3 | unsigned long _commandSentTime; 4 | 5 | MdbMaster::MdbMaster() 6 | { 7 | MdbPort.begin(); 8 | while (!MdbPort); 9 | 10 | _commandSentTime = 0; 11 | } 12 | 13 | void MdbMaster::SendCommand(unsigned char address, unsigned char command) 14 | { 15 | unsigned char dataBytes[1]; 16 | 17 | MdbMaster::SendCommand(address, command, dataBytes, 0); 18 | 19 | } 20 | 21 | void MdbMaster::SendCommand(unsigned char address, unsigned char command, 22 | unsigned char *dataBytes, unsigned int dataByteCount) 23 | { 24 | unsigned char sum = 0; 25 | 26 | // Send the command along with the Bill Validator Address. 27 | MdbPort.write(address | command, 1); 28 | sum += address | command; 29 | 30 | // Limit the number of data bytes that can be sent for a command 31 | // to 34, since the total length of a message, including the 32 | // address/command byte and the checksum byte, is limited to 36 bytes. 33 | if (dataByteCount > 34) 34 | { 35 | dataByteCount = 34; 36 | } 37 | 38 | // Send data bytes. 39 | for (unsigned int i = 0; i < dataByteCount; i++) 40 | { 41 | MdbPort.write(dataBytes[i], 0); 42 | sum += dataBytes[i]; 43 | } 44 | 45 | // Send checksum. 46 | MdbPort.write(sum, 0); 47 | 48 | _commandSentTime = millis(); 49 | } 50 | 51 | // Try to read the response after sending a command to the validator. 52 | // Wait for ~5ms after sending a command before returning with error 53 | // code -1 (request retransmit). If there is some other, more fatal error, 54 | // returns -2. If the message is recieved with no problem, 0 is the return 55 | // value and the returned bytes will be put into the array pointed at by 56 | // the response parameter. The number of bytes received will be stored in 57 | // the variable referenced by the numBytes parameter. Other special 58 | // conditions include ACK and NAK, which return 1 and 2 respectively. 59 | // 60 | // Return Codes: 61 | // 0: Message returned normally in response parameter reference 62 | // 1: ACK 63 | // -1: NAK: Request retransmit after response timeout or NAK recieved. 64 | // -2: Unrecoverable error, device should probably be reset after this. 65 | int MdbMaster::GetResponse(unsigned char *response, unsigned int *numBytes) 66 | { 67 | int index = 0; 68 | int lastMode = 0; 69 | 70 | // Wait for some bytes to be available. I should probably add some 71 | // sort of timeout here. 72 | while (!MdbPort.available()) 73 | { 74 | if (millis() - _commandSentTime > 5) 75 | { 76 | return -1; 77 | } 78 | } 79 | 80 | // Loop through bytes received until there are either no bytes 81 | // available, 36 bytes have been received (max message size), 82 | // or a byte with the mode bit set, which signifies the end of 83 | // the message. 84 | while ((MdbPort.available() > 0) 85 | && (index <= MAX_MSG_SIZE) 86 | && (lastMode == 0)) 87 | { 88 | response[index] = MdbPort.read() & 0xFF; 89 | lastMode = (response[index] >> 8) & 0x01; 90 | index++; 91 | } 92 | 93 | *numBytes = index; 94 | 95 | return 0; 96 | } 97 | 98 | // Send an acknowledgement. 99 | void MdbMaster::SendAck() 100 | { 101 | MdbPort.write(ACK, 0); 102 | } 103 | 104 | // Send a retransmit request. 105 | void MdbMaster::SendRet() 106 | { 107 | MdbPort.write(RET, 0); 108 | } 109 | 110 | // Send a negative acknowledgement. 111 | void MdbMaster::SendNak() 112 | { 113 | MdbPort.write(NAK, 0); 114 | } 115 | 116 | // A hard reset will trigger all connected peripherals to reset. 117 | void MdbMaster::HardReset() 118 | { 119 | pinMode(1, OUTPUT); 120 | digitalWrite(1, LOW); 121 | delay(150); 122 | digitalWrite(1, HIGH); 123 | delay(150); 124 | } 125 | 126 | -------------------------------------------------------------------------------- /MdbSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | MdbSerial.h - MDB serial library for Wiring 3 | Copyright (c) 2014 Justin T. Conroy. All right reserved. 4 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | Modified 28 September 2010 by Mark Sproul 21 | Modified 14 August 2012 by Alarus 22 | === Changed to MdbSerial.cpp === 23 | Modified 31 January 2014 by Justin T. Conroy 24 | */ 25 | 26 | /* 27 | This library is designed specifically to support the MDB protocol over the 28 | standard USART pins. MDB is basically a serial protocol that uses a start 29 | bit, stop bit, and 9 data bits (8 bits for actual data, 1 bit is a mode 30 | bit). It always uses 9600 baud. 31 | 32 | For more information on the MDB protocol, refer to the MDB 4.2 specification 33 | document, which can be found at the following URL: 34 | 35 | http://www.vending.org/images/pdfs/technology/mdb_version_4-2.pdf 36 | 37 | This libary was modified from the HardwareSerial libary that comes with 38 | the Arduino software. Some copyright and modification information from 39 | that libary has been preserved above. 40 | */ 41 | 42 | 43 | #ifndef MdbSerial_h 44 | #define MdbSerial_h 45 | 46 | #include 47 | 48 | #include "Stream.h" 49 | 50 | struct ring_buffer; 51 | 52 | class MdbSerial 53 | { 54 | private: 55 | ring_buffer *_rx_buffer; 56 | ring_buffer *_tx_buffer; 57 | volatile uint8_t *_ubrrh; 58 | volatile uint8_t *_ubrrl; 59 | volatile uint8_t *_ucsra; 60 | volatile uint8_t *_ucsrb; 61 | volatile uint8_t *_ucsrc; 62 | volatile uint8_t *_udr; 63 | uint8_t _rxen; 64 | uint8_t _txen; 65 | uint8_t _rxcie; 66 | uint8_t _udrie; 67 | uint8_t _u2x; 68 | uint8_t _ucsz2; 69 | uint8_t _ucsz1; 70 | uint8_t _ucsz0; 71 | uint8_t _upm1; 72 | uint8_t _upm0; 73 | uint8_t _umsel1; 74 | uint8_t _umsel0; 75 | bool transmitting; 76 | public: 77 | MdbSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, 78 | volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, 79 | volatile uint8_t *ucsra, volatile uint8_t *ucsrb, 80 | volatile uint8_t *ucsrc, volatile uint8_t *udr, 81 | uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x, 82 | uint8_t ucsz2, uint8_t ucsz1, uint8_t ucsz0, uint8_t upm1, uint8_t upm0, 83 | uint8_t umsel1, uint8_t umsel0); 84 | void begin(); 85 | void end(); 86 | virtual int available(void); 87 | virtual int peek(void); 88 | virtual int read(void); 89 | virtual void flush(void); 90 | virtual size_t write(uint8_t, uint8_t); 91 | //inline size_t write(unsigned long c, unsigned long m) { return write((uint8_t)c, (uint8_t)m); } 92 | //inline size_t write(long c, long m) { return write((uint8_t)c, (uint8_t)m); } 93 | //inline size_t write(unsigned int c, unsigned int m) { return write((uint8_t)c, (uint8_t)m); } 94 | //inline size_t write(int c, int m) { return write((uint8_t)c, (uint8_t)m); } 95 | //inline size_t write(unsigned char c, unsigned char m) { return write((uint8_t)c, (uint8_t)m); } 96 | //using Print::write; // pull in write(str) and write(buf, size) from Print 97 | 98 | virtual uint8_t readRegister(int); 99 | virtual unsigned int getTxHead(); 100 | virtual unsigned int getTxTail(); 101 | 102 | operator bool(); 103 | }; 104 | 105 | // Define config for Serial.begin(baud, config); 106 | #define SERIAL_5N1 0x00 107 | #define SERIAL_6N1 0x02 108 | #define SERIAL_7N1 0x04 109 | #define SERIAL_8N1 0x06 110 | #define SERIAL_5N2 0x08 111 | #define SERIAL_6N2 0x0A 112 | #define SERIAL_7N2 0x0C 113 | #define SERIAL_8N2 0x0E 114 | #define SERIAL_5E1 0x20 115 | #define SERIAL_6E1 0x22 116 | #define SERIAL_7E1 0x24 117 | #define SERIAL_8E1 0x26 118 | #define SERIAL_5E2 0x28 119 | #define SERIAL_6E2 0x2A 120 | #define SERIAL_7E2 0x2C 121 | #define SERIAL_8E2 0x2E 122 | #define SERIAL_5O1 0x30 123 | #define SERIAL_6O1 0x32 124 | #define SERIAL_7O1 0x34 125 | #define SERIAL_8O1 0x36 126 | #define SERIAL_5O2 0x38 127 | #define SERIAL_6O2 0x3A 128 | #define SERIAL_7O2 0x3C 129 | #define SERIAL_8O2 0x3E 130 | 131 | #if defined(UBRRH) || defined(UBRR0H) 132 | extern MdbSerial MdbPort; 133 | #endif 134 | #if defined(UBRR1H) 135 | extern MdbSerial MdbPort1; 136 | #endif 137 | #if defined(UBRR2H) 138 | extern MdbSerial MdbPort2; 139 | #endif 140 | #if defined(UBRR3H) 141 | extern MdbSerial MdbPort3; 142 | #endif 143 | 144 | extern void serialEventRun(void) __attribute__((weak)); 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /MdbBillValidator.cpp: -------------------------------------------------------------------------------- 1 | #include "MdbBillValidator.h" 2 | 3 | MdbMaster master; 4 | 5 | bool _setupInfoRetrieved; 6 | 7 | unsigned int _featureLevel; 8 | unsigned int _countryCode; 9 | unsigned int _billScaleFactor; 10 | unsigned int _decimalPlaces; 11 | unsigned int _stackerCapacity; 12 | unsigned int _billSecurityLevels; 13 | bool _escrowEnabled; 14 | unsigned char _billTypeCredit[16]; 15 | 16 | MdbBillValidator::MdbBillValidator() 17 | { 18 | master = MdbMaster(); 19 | 20 | _setupInfoRetrieved = false; 21 | } 22 | 23 | // Send an acknowledgement. 24 | void MdbBillValidator::SendAck() 25 | { 26 | master.SendAck(); 27 | } 28 | 29 | // Send a retransmit request. 30 | void MdbBillValidator::SendRet() 31 | { 32 | master.SendRet(); 33 | } 34 | 35 | // Send a negative acknowledgement. 36 | void MdbBillValidator::SendNak() 37 | { 38 | master.SendNak(); 39 | } 40 | 41 | // Send a reset command to the bill validator. There is no response to 42 | // this command. 43 | void MdbBillValidator::SoftReset() 44 | { 45 | master.SendCommand(BILL_ADDR, RESET); 46 | _setupInfoRetrieved = false; 47 | } 48 | 49 | int MdbBillValidator::GetSetup() 50 | { 51 | master.SendCommand(BILL_ADDR, SETUP); 52 | 53 | unsigned char response[MAX_MSG_SIZE]; 54 | unsigned int numBytesReturned; 55 | master.GetResponse(response, &numBytesReturned); 56 | 57 | if (response[0] == NAK || numBytesReturned == 0) 58 | { 59 | // Negative Acknowledgment 60 | return -1; 61 | } 62 | 63 | // Make sure the number of bytes returned is in the correct range. 64 | // There should be between 11 and 27 (inclusive bytes) depending on 65 | // how many bill types are supported. 66 | if (numBytesReturned < 11 || numBytesReturned > 27) 67 | { 68 | return -2; 69 | } 70 | 71 | // Read and save the setup information. 72 | _featureLevel = response[0]; 73 | _countryCode = (((unsigned int) response[1]) << 8) | ((unsigned int) response[2]); 74 | _billScaleFactor = (((unsigned int) response[3]) << 8) | ((unsigned int) response[4]); 75 | _decimalPlaces = response[5]; 76 | _stackerCapacity = (((unsigned int) response[6]) << 8) | ((unsigned int) response[7]); 77 | _billSecurityLevels = (((unsigned int) response[8]) << 8) | ((unsigned int) response[9]); 78 | _escrowEnabled = response[10] == 0xFF; 79 | 80 | // Get the value of each bill type. 81 | for (int i = 0; i < 16; i++) 82 | { 83 | _billTypeCredit[i] = response[11 + i]; 84 | } 85 | 86 | _setupInfoRetrieved = true; 87 | 88 | return 0; 89 | } 90 | 91 | int MdbBillValidator::SetSecurity(unsigned int securitySettings) 92 | { 93 | unsigned char securityBits[2]; 94 | 95 | securityBits[0] = securitySettings && 0xFF; 96 | securityBits[1] = (securitySettings >> 8) && 0xFF; 97 | 98 | master.SendCommand(BILL_ADDR, SECURITY, securityBits, 2); 99 | 100 | unsigned char response[MAX_MSG_SIZE]; 101 | unsigned int numBytesReturned; 102 | master.GetResponse(response, &numBytesReturned); 103 | 104 | if (numBytesReturned == 0) 105 | { 106 | // No data returned. 107 | return -1; 108 | } 109 | 110 | if (numBytesReturned > 1) 111 | { 112 | // The data received doesn't make any sense... 113 | return -2; 114 | } 115 | 116 | if (response[0] == NAK) 117 | { 118 | // Negative acknowledgement received. 119 | return -3; 120 | } 121 | 122 | if (response[0] == ACK) 123 | { 124 | return 0; 125 | } 126 | 127 | // Unidentifiable response. Needs more debugging. 128 | return -4; 129 | } 130 | 131 | int MdbBillValidator::Poll() 132 | { 133 | master.SendCommand(BILL_ADDR, POLL); 134 | 135 | unsigned char response[MAX_MSG_SIZE]; 136 | unsigned int numBytesReturned = 0; 137 | master.GetResponse(response, &numBytesReturned); 138 | 139 | if (response[0] == NAK || numBytesReturned == 0) 140 | { 141 | // Negative Acknowledgment 142 | return -1; 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | // Getter method for retrieving the feature level of the currently 149 | // connected bill acceptor/validator. This method must be called after 150 | // setup has already been called, otherwise the feature level will not 151 | // be known and this method will return -1 to indicate an error. 152 | unsigned int MdbBillValidator::FeatureLevel() 153 | { 154 | if(_setupInfoRetrieved) 155 | { 156 | return _featureLevel; 157 | } 158 | else 159 | { 160 | return -1; 161 | } 162 | } 163 | 164 | unsigned int MdbBillValidator::CountryCode() 165 | { 166 | if(_setupInfoRetrieved) 167 | { 168 | return _countryCode; 169 | } 170 | else 171 | { 172 | return -1; 173 | } 174 | } 175 | 176 | unsigned int MdbBillValidator::BillScaleFactor() 177 | { 178 | if(_setupInfoRetrieved) 179 | { 180 | return _billScaleFactor; 181 | } 182 | else 183 | { 184 | return -1; 185 | } 186 | } 187 | 188 | unsigned int MdbBillValidator::DecimalPlaces() 189 | { 190 | if(_setupInfoRetrieved) 191 | { 192 | return _decimalPlaces; 193 | } 194 | else 195 | { 196 | return -1; 197 | } 198 | } 199 | 200 | unsigned int MdbBillValidator::StackerCapacity() 201 | { 202 | if(_setupInfoRetrieved) 203 | { 204 | return _stackerCapacity; 205 | } 206 | else 207 | { 208 | return -1; 209 | } 210 | } 211 | 212 | unsigned int MdbBillValidator::BillSecurityLevels() 213 | { 214 | if(_setupInfoRetrieved) 215 | { 216 | return _billSecurityLevels; 217 | } 218 | else 219 | { 220 | return -1; 221 | } 222 | } 223 | 224 | bool MdbBillValidator::EscrowEnabled() 225 | { 226 | if(_setupInfoRetrieved) 227 | { 228 | return _escrowEnabled; 229 | } 230 | else 231 | { 232 | return false; 233 | } 234 | } 235 | 236 | unsigned char* MdbBillValidator::BillTypeCredit() 237 | { 238 | if(_setupInfoRetrieved) 239 | { 240 | return _billTypeCredit; 241 | } 242 | else 243 | { 244 | return NULL; 245 | } 246 | } 247 | 248 | String MdbBillValidator::ToString() 249 | { 250 | String result; 251 | 252 | if (_setupInfoRetrieved) 253 | { 254 | result = "Feature Level: " + String(_featureLevel) 255 | + "\nCountryCode: " + String(_countryCode) 256 | + "\nBillScaleFactor: " + String(_billScaleFactor) 257 | + "\nDecimalPlaces: " + String(_decimalPlaces) 258 | + "\nStackerCapacity: " + String(_stackerCapacity) 259 | + "\nBillSecurityLevels: " + String(_billSecurityLevels) 260 | + "\nEscrowEnabled: " + String(_escrowEnabled) 261 | + "\nBillTypes:"; 262 | 263 | // Get each bill type. 264 | //for (int i = 0; i < 16; i++) 265 | //{ 266 | //result += " " + _billTypeCredit[i]; 267 | //} 268 | } 269 | else 270 | { 271 | result = "Configuration of bill validator unknown. Run GetSetup()"; 272 | } 273 | 274 | return result; 275 | } 276 | 277 | -------------------------------------------------------------------------------- /Arduino.h: -------------------------------------------------------------------------------- 1 | #ifndef Arduino_h 2 | #define Arduino_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "binary.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C"{ 16 | #endif 17 | 18 | #define HIGH 0x1 19 | #define LOW 0x0 20 | 21 | #define INPUT 0x0 22 | #define OUTPUT 0x1 23 | #define INPUT_PULLUP 0x2 24 | 25 | #define true 0x1 26 | #define false 0x0 27 | 28 | #define PI 3.1415926535897932384626433832795 29 | #define HALF_PI 1.5707963267948966192313216916398 30 | #define TWO_PI 6.283185307179586476925286766559 31 | #define DEG_TO_RAD 0.017453292519943295769236907684886 32 | #define RAD_TO_DEG 57.295779513082320876798154814105 33 | 34 | #define SERIAL 0x0 35 | #define DISPLAY 0x1 36 | 37 | #define LSBFIRST 0 38 | #define MSBFIRST 1 39 | 40 | #define CHANGE 1 41 | #define FALLING 2 42 | #define RISING 3 43 | 44 | #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) 45 | #define DEFAULT 0 46 | #define EXTERNAL 1 47 | #define INTERNAL 2 48 | #else 49 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) 50 | #define INTERNAL1V1 2 51 | #define INTERNAL2V56 3 52 | #else 53 | #define INTERNAL 3 54 | #endif 55 | #define DEFAULT 1 56 | #define EXTERNAL 0 57 | #endif 58 | 59 | // undefine stdlib's abs if encountered 60 | #ifdef abs 61 | #undef abs 62 | #endif 63 | 64 | #define min(a,b) ((a)<(b)?(a):(b)) 65 | #define max(a,b) ((a)>(b)?(a):(b)) 66 | #define abs(x) ((x)>0?(x):-(x)) 67 | #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) 68 | #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) 69 | #define radians(deg) ((deg)*DEG_TO_RAD) 70 | #define degrees(rad) ((rad)*RAD_TO_DEG) 71 | #define sq(x) ((x)*(x)) 72 | 73 | #define interrupts() sei() 74 | #define noInterrupts() cli() 75 | 76 | #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) 77 | #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) 78 | #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) 79 | 80 | #define lowByte(w) ((uint8_t) ((w) & 0xff)) 81 | #define highByte(w) ((uint8_t) ((w) >> 8)) 82 | 83 | #define bitRead(value, bit) (((value) >> (bit)) & 0x01) 84 | #define bitSet(value, bit) ((value) |= (1UL << (bit))) 85 | #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) 86 | #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) 87 | 88 | 89 | typedef unsigned int word; 90 | 91 | #define bit(b) (1UL << (b)) 92 | 93 | typedef uint8_t boolean; 94 | typedef uint8_t byte; 95 | 96 | void init(void); 97 | 98 | void pinMode(uint8_t, uint8_t); 99 | void digitalWrite(uint8_t, uint8_t); 100 | int digitalRead(uint8_t); 101 | int analogRead(uint8_t); 102 | void analogReference(uint8_t mode); 103 | void analogWrite(uint8_t, int); 104 | 105 | unsigned long millis(void); 106 | unsigned long micros(void); 107 | void delay(unsigned long); 108 | void delayMicroseconds(unsigned int us); 109 | unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); 110 | 111 | void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); 112 | uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); 113 | 114 | void attachInterrupt(uint8_t, void (*)(void), int mode); 115 | void detachInterrupt(uint8_t); 116 | 117 | void setup(void); 118 | void loop(void); 119 | 120 | // Get the bit location within the hardware port of the given virtual pin. 121 | // This comes from the pins_*.c file for the active board configuration. 122 | 123 | #define analogInPinToBit(P) (P) 124 | 125 | // On the ATmega1280, the addresses of some of the port registers are 126 | // greater than 255, so we can't store them in uint8_t's. 127 | extern const uint16_t PROGMEM port_to_mode_PGM[]; 128 | extern const uint16_t PROGMEM port_to_input_PGM[]; 129 | extern const uint16_t PROGMEM port_to_output_PGM[]; 130 | 131 | extern const uint8_t PROGMEM digital_pin_to_port_PGM[]; 132 | // extern const uint8_t PROGMEM digital_pin_to_bit_PGM[]; 133 | extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[]; 134 | extern const uint8_t PROGMEM digital_pin_to_timer_PGM[]; 135 | 136 | // Get the bit location within the hardware port of the given virtual pin. 137 | // This comes from the pins_*.c file for the active board configuration. 138 | // 139 | // These perform slightly better as macros compared to inline functions 140 | // 141 | #define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) ) 142 | #define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) ) 143 | #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) ) 144 | #define analogInPinToBit(P) (P) 145 | #define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) ) 146 | #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) ) 147 | #define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) ) 148 | 149 | #define NOT_A_PIN 0 150 | #define NOT_A_PORT 0 151 | 152 | #ifdef ARDUINO_MAIN 153 | #define PA 1 154 | #define PB 2 155 | #define PC 3 156 | #define PD 4 157 | #define PE 5 158 | #define PF 6 159 | #define PG 7 160 | #define PH 8 161 | #define PJ 10 162 | #define PK 11 163 | #define PL 12 164 | #endif 165 | 166 | #define NOT_ON_TIMER 0 167 | #define TIMER0A 1 168 | #define TIMER0B 2 169 | #define TIMER1A 3 170 | #define TIMER1B 4 171 | #define TIMER2 5 172 | #define TIMER2A 6 173 | #define TIMER2B 7 174 | 175 | #define TIMER3A 8 176 | #define TIMER3B 9 177 | #define TIMER3C 10 178 | #define TIMER4A 11 179 | #define TIMER4B 12 180 | #define TIMER4C 13 181 | #define TIMER4D 14 182 | #define TIMER5A 15 183 | #define TIMER5B 16 184 | #define TIMER5C 17 185 | 186 | #ifdef __cplusplus 187 | } // extern "C" 188 | #endif 189 | 190 | #ifdef __cplusplus 191 | #include "WCharacter.h" 192 | #include "WString.h" 193 | //#include "HardwareSerial.h" 194 | 195 | uint16_t makeWord(uint16_t w); 196 | uint16_t makeWord(byte h, byte l); 197 | 198 | #define word(...) makeWord(__VA_ARGS__) 199 | 200 | unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); 201 | 202 | void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); 203 | void noTone(uint8_t _pin); 204 | 205 | // WMath prototypes 206 | long random(long); 207 | long random(long, long); 208 | void randomSeed(unsigned int); 209 | long map(long, long, long, long, long); 210 | 211 | #endif 212 | 213 | #include "pins_arduino.h" 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /sample/MdbMaster.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define BILL_ADDR 0x30 6 | 7 | #define txLcdPin 4 8 | #define buttonPin 2 9 | 10 | #define NUMSTATUSES 2 11 | 12 | // Biller commands 13 | #define RESET 0x00 14 | #define SETUP 0x01 15 | #define STATUS 0x01 // Legacy definition of SETUP. 16 | #define SECURITY 0x02 17 | #define POLL 0x03 18 | #define BILLTYPE 0x04 19 | #define ESCROW 0x05 20 | #define STACKER 0x06 21 | #define EXP_CMD 0x07 22 | 23 | // Response Codes 24 | #define ACK 0x00 // Acknowledge 25 | #define RET 0xAA // Retransmit (VMC only) 26 | #define NAK 0xFF // Negative Acknowledge 27 | 28 | // MDB state machine states. 29 | enum MdbState 30 | { 31 | hardReset, 32 | waitHardReset, 33 | softReset, 34 | waitSoftReset, 35 | initState, 36 | waitJustReset, 37 | ackJustReset, 38 | getStatus, 39 | waitStatusResponse, 40 | ackStatusResponse, 41 | sendSecurity, 42 | waitSecurityResponse, 43 | getLevel1Id, 44 | waitLevel1Id, 45 | enableBillTypes, 46 | poll, 47 | readPollResponse, 48 | ackPollResponse, 49 | acceptBill, 50 | 51 | displayResponse 52 | }; 53 | 54 | char buttonMode = 0; 55 | 56 | SoftwareSerial LCD = SoftwareSerial(0, txLcdPin); 57 | // since the LCD does not send data back to the Arduino, we should only define the txLcdPin 58 | const int LCDdelay=10; // conservative, 2 actually works 59 | 60 | // wbp: goto with row & column 61 | void lcdPosition(int row, int col) { 62 | LCD.write(0xFE); //command flag 63 | LCD.write((col + row*64 + 128)); //position 64 | delay(LCDdelay); 65 | } 66 | void clearLCD(){ 67 | LCD.write(0xFE); //command flag 68 | LCD.write(0x01); //clear command. 69 | delay(LCDdelay); 70 | } 71 | void backlightOn() { //turns on the backlight 72 | LCD.write(0x7C); //command flag for backlight stuff 73 | LCD.write(157); //light level. 74 | delay(LCDdelay); 75 | } 76 | void backlightOff(){ //turns off the backlight 77 | LCD.write(0x7C); //command flag for backlight stuff 78 | LCD.write(128); //light level for off. 79 | delay(LCDdelay); 80 | } 81 | void serCommand(){ //a general function to call the command flag for issuing all other commands 82 | LCD.write(0xFE); 83 | } 84 | void lcdPrintBits(unsigned char regValue, int x, int y){ 85 | for(int i = 0; i < 8; i++) 86 | { 87 | lcdPosition(x,y+i); 88 | if ((regValue >> (7 - i)) & 0x01) 89 | LCD.print("1"); 90 | else 91 | LCD.print("0"); 92 | } 93 | } 94 | 95 | void MdbSendCommand(unsigned char command, unsigned char *cmdBytes, unsigned int numCmdBytes) 96 | { 97 | unsigned char sum = 0; 98 | 99 | // Write Address 100 | MdbPort.write(BILL_ADDR | command, 1); 101 | sum += BILL_ADDR | command; 102 | 103 | // Avoid buffer overflows on cmdBytes. 104 | if (numCmdBytes > 33) 105 | { 106 | numCmdBytes = 33; 107 | } 108 | 109 | // Send each additional byte for the command. 110 | for (int i = 0; i < numCmdBytes; i++) 111 | { 112 | MdbPort.write(cmdBytes[i], 0); 113 | sum += cmdBytes[i]; 114 | } 115 | 116 | // Send checksum. 117 | MdbPort.write(sum, 0); 118 | } 119 | 120 | void MdbSendAck() 121 | { 122 | MdbPort.write(BILL_ADDR, 0); 123 | } 124 | 125 | // response should be a char array with size 36. 126 | void MdbGetResponse(unsigned char *response, unsigned int numBytes) 127 | { 128 | int index = 0; 129 | int lastMode = 0; 130 | 131 | // Wait for enough bytes to be ready. 132 | //while (MdbPort.available() < numBytes); 133 | 134 | // Largest message size is 36 bytes, so only ever read that much. 135 | // Last byte in a block has mode 1. 136 | while ((MdbPort.available() > 0) 137 | && (index < 37) 138 | && (lastMode == 0)) 139 | { 140 | response[index] = MdbPort.read() & 0xFF; 141 | lastMode = (response[index] >> 8) & 0x01; 142 | 143 | index++; 144 | } 145 | } 146 | 147 | MdbState state; 148 | String debugMessage = ""; 149 | String lastDebugMessage = ""; 150 | 151 | void setup() 152 | { 153 | pinMode(txLcdPin, OUTPUT); 154 | LCD.begin(9600); 155 | clearLCD(); 156 | lcdPosition(0,0); 157 | backlightOn(); 158 | 159 | // Hard Reset 160 | pinMode(1, OUTPUT); 161 | digitalWrite(1, LOW); 162 | delay(200); 163 | digitalWrite(1, HIGH); 164 | delay(200); 165 | 166 | 167 | LCD.print("MDB Test Slave"); 168 | 169 | MdbPort.begin(); 170 | 171 | while (!MdbPort); 172 | 173 | state = waitSoftReset; 174 | } 175 | 176 | 177 | unsigned char extraCmdBytes[33]; 178 | unsigned char responseBytes[36]; 179 | 180 | unsigned long currentTime = 0; 181 | unsigned long debugTime = 0; 182 | 183 | void loop() 184 | { 185 | 186 | switch(state) 187 | { 188 | case hardReset: 189 | digitalWrite(1, LOW); 190 | currentTime = millis(); 191 | 192 | debugMessage = "Hard Reset"; 193 | 194 | state = waitHardReset; 195 | break; 196 | case waitHardReset: 197 | if (millis() - currentTime > 200) 198 | { 199 | digitalWrite(2, HIGH); 200 | state = waitSoftReset; 201 | } 202 | break; 203 | case softReset: 204 | MdbSendCommand(RESET, extraCmdBytes, 0); 205 | currentTime = millis(); 206 | 207 | debugMessage = "Soft Reset"; 208 | 209 | state = waitSoftReset; 210 | break; 211 | 212 | case waitSoftReset: 213 | if (millis() - currentTime > (10*1000)) 214 | { 215 | //state = softReset; 216 | state = initState; 217 | } 218 | 219 | break; 220 | 221 | case initState: // Initial state. Poll device. 222 | MdbSendCommand(POLL, extraCmdBytes, 0); 223 | currentTime = millis(); 224 | 225 | // display current state for debug info 226 | debugMessage = "initState"; 227 | 228 | state = waitJustReset; 229 | break; 230 | 231 | case waitJustReset: 232 | if (MdbPort.available() > 0) 233 | { 234 | MdbGetResponse(responseBytes, 1); 235 | debugMessage += " got response"; 236 | state = ackJustReset; 237 | } 238 | else if ((millis() - currentTime) > 150) 239 | { 240 | // Try again. 241 | state = initState; 242 | } 243 | 244 | // display current state for debug info 245 | debugMessage = "waitJustReset"; 246 | 247 | break; 248 | 249 | case ackJustReset: 250 | MdbSendAck(); 251 | delay(10); 252 | 253 | state = getStatus; 254 | break; 255 | 256 | case getStatus: 257 | MdbSendCommand(STATUS, extraCmdBytes, 0); 258 | currentTime = millis(); 259 | 260 | // display current state for debug info 261 | debugMessage = "getStatus"; 262 | 263 | state = waitStatusResponse; 264 | break; 265 | 266 | case waitStatusResponse: 267 | if (MdbPort.available() > 0) 268 | { 269 | MdbGetResponse(responseBytes, 1); 270 | debugMessage += " got response"; 271 | 272 | delay(10); 273 | state = ackStatusResponse; 274 | } 275 | else if ((millis() - currentTime) > 150) 276 | { 277 | // Try again. 278 | state = getStatus; 279 | } 280 | 281 | // display current state for debug info 282 | debugMessage = "waitStatusResponse"; 283 | break; 284 | 285 | case ackStatusResponse: 286 | MdbSendAck(); 287 | delay(10); 288 | state = sendSecurity; 289 | break; 290 | 291 | case sendSecurity: 292 | // Set all bills to "low" security level. 293 | extraCmdBytes[0] = 0; 294 | extraCmdBytes[1] = 0; 295 | MdbSendCommand(SECURITY, extraCmdBytes, 2); 296 | delay(10); 297 | state = waitSecurityResponse; 298 | break; 299 | 300 | case waitSecurityResponse: 301 | if (MdbPort.available() > 0) 302 | { 303 | MdbGetResponse(responseBytes, 1); 304 | debugMessage += " got response"; 305 | state = getLevel1Id; 306 | } 307 | else if ((millis() - currentTime) > 150) 308 | { 309 | // Try again. 310 | state = sendSecurity; 311 | } 312 | 313 | // display current state for debug info 314 | debugMessage = "waitSecurityResponse"; 315 | break; 316 | 317 | case getLevel1Id: 318 | // Set all bills to "low" security level. 319 | extraCmdBytes[0] = 0; 320 | MdbSendCommand(EXP_CMD, extraCmdBytes, 1); 321 | delay(10); 322 | state = waitLevel1Id; 323 | break; 324 | 325 | case waitLevel1Id: 326 | if (MdbPort.available() > 0) 327 | { 328 | MdbGetResponse(responseBytes, 1); 329 | debugMessage += " got response"; 330 | delay(100); 331 | state = enableBillTypes; 332 | } 333 | else if ((millis() - currentTime) > 150) 334 | { 335 | // Try again. 336 | state = getLevel1Id; 337 | } 338 | 339 | // display current state for debug info 340 | debugMessage = "waitLevel1IdResponse"; 341 | break; 342 | 343 | case enableBillTypes: 344 | // Enable all denominations of bills. 345 | extraCmdBytes[0] = 0xFF; 346 | extraCmdBytes[1] = 0xFF; 347 | 348 | // Enable escrow for all bills. 349 | extraCmdBytes[2] = 0xFF; 350 | extraCmdBytes[3] = 0xFF; 351 | 352 | MdbSendCommand(BILLTYPE, extraCmdBytes, 4); 353 | delay(10); 354 | state = poll; 355 | break; 356 | 357 | case poll: 358 | // Poll repeatedly. 359 | MdbSendCommand(POLL, extraCmdBytes, 0); 360 | state = readPollResponse; 361 | 362 | debugMessage = "Poll"; 363 | delay(10); 364 | break; 365 | 366 | case readPollResponse: 367 | if (MdbPort.available() > 0) 368 | { 369 | MdbGetResponse(responseBytes, 1); 370 | state = ackPollResponse; 371 | } 372 | else if ((millis() - currentTime) > 150) 373 | { 374 | // Try again. 375 | state = poll; 376 | } 377 | 378 | break; 379 | 380 | case ackPollResponse: 381 | MdbSendAck(); 382 | delay(10); 383 | 384 | //state = displayResponse; 385 | if (responseBytes[0] == 0x94) 386 | { 387 | state = acceptBill; 388 | } 389 | else 390 | { 391 | // poll repeatedly 392 | state = poll; 393 | } 394 | break; 395 | 396 | case acceptBill: 397 | extraCmdBytes[0] = 0x01; 398 | MdbSendCommand(ESCROW, extraCmdBytes, 1); 399 | 400 | debugMessage = "Accept Bill"; 401 | 402 | state = poll; 403 | break; 404 | 405 | 406 | case displayResponse: 407 | // Display each hex byte from the last response on the screen. 408 | 409 | debugMessage = ""; 410 | for (int j = 0; j < 11; j++) 411 | { 412 | String statusChar((unsigned int)responseBytes[j], HEX); 413 | debugMessage += statusChar; 414 | 415 | if (j < 10) 416 | { 417 | debugMessage += " "; 418 | } 419 | } 420 | 421 | state = poll; 422 | break; 423 | 424 | default: 425 | // do something here. 426 | debugMessage = "Default State"; 427 | } 428 | 429 | if ((millis() - debugTime) > 1500) 430 | { 431 | clearLCD(); 432 | lcdPosition(0,0); 433 | LCD.print(debugMessage); 434 | lastDebugMessage = debugMessage; 435 | debugTime = millis(); 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /MdbSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MdbSerial.cpp - MDB serial library for Wiring 3 | Copyright (c) 2014 Justin T. Conroy. All right reserved. 4 | Copyright (c) 2006 Nicholas Zambetti. All right reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | Modified 23 November 2006 by David A. Mellis 21 | Modified 28 September 2010 by Mark Sproul 22 | Modified 14 August 2012 by Alarus 23 | === Changed to MdbSerial.cpp === 24 | Modified 31 January 2014 by Justin T. Conroy 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "Arduino.h" 32 | #include "wiring_private.h" 33 | 34 | // this next line disables the entire MdbSerial.cpp, 35 | // this is so I can support Attiny series and any other chip without a uart 36 | #if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H) 37 | 38 | #include "MdbSerial.h" 39 | 40 | /* 41 | * on ATmega8, the uart and its bits are not numbered, so there is no "TXC0" 42 | * definition. 43 | */ 44 | #if !defined(TXC0) 45 | #if defined(TXC) 46 | #define TXC0 TXC 47 | #elif defined(TXC1) 48 | // Some devices have uart1 but no uart0 49 | #define TXC0 TXC1 50 | #else 51 | #error TXC0 not definable in MdbSerial.h 52 | #endif 53 | #endif 54 | 55 | // Define constants and variables for buffering incoming serial data. We're 56 | // using a ring buffer (I think), in which head is the index of the location 57 | // to which to write the next incoming character and tail is the index of the 58 | // location from which to read. 59 | #if (RAMEND < 1000) 60 | #define SERIAL_BUFFER_SIZE 16 61 | #else 62 | #define SERIAL_BUFFER_SIZE 64 63 | #endif 64 | 65 | struct ring_buffer 66 | { 67 | unsigned char buffer[SERIAL_BUFFER_SIZE]; 68 | unsigned char mode_buffer[SERIAL_BUFFER_SIZE]; 69 | volatile unsigned int head; 70 | volatile unsigned int tail; 71 | }; 72 | 73 | #if defined(USBCON) 74 | ring_buffer rx_buffer = { { 0 }, { 0 }, 0, 0}; 75 | ring_buffer tx_buffer = { { 0 }, { 0 }, 0, 0}; 76 | #endif 77 | #if defined(UBRRH) || defined(UBRR0H) 78 | ring_buffer rx_buffer = { { 0 }, { 0 }, 0, 0 }; 79 | ring_buffer tx_buffer = { { 0 }, { 0 }, 0, 0 }; 80 | #endif 81 | #if defined(UBRR1H) 82 | ring_buffer rx_buffer1 = { { 0 }, { 0 }, 0, 0 }; 83 | ring_buffer tx_buffer1 = { { 0 }, { 0 }, 0, 0 }; 84 | #endif 85 | #if defined(UBRR2H) 86 | ring_buffer rx_buffer2 = { { 0 }, { 0 }, 0, 0 }; 87 | ring_buffer tx_buffer2 = { { 0 }, { 0 }, 0, 0 }; 88 | #endif 89 | #if defined(UBRR3H) 90 | ring_buffer rx_buffer3 = { { 0 }, { 0 }, 0, 0 }; 91 | ring_buffer tx_buffer3 = { { 0 }, { 0 }, 0, 0 }; 92 | #endif 93 | 94 | inline void store_char(unsigned char c, unsigned char m, ring_buffer *buffer) 95 | { 96 | unsigned int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE; 97 | 98 | // if we should be storing the received character into the location 99 | // just before the tail (meaning that the head would advance to the 100 | // current location of the tail), we're about to overflow the buffer 101 | // and so we don't write the character or advance the head. 102 | if (i != buffer->tail) { 103 | buffer->buffer[buffer->head] = c; 104 | buffer->mode_buffer[buffer->head] = m; 105 | buffer->head = i; 106 | } 107 | } 108 | 109 | #if !defined(USART0_RX_vect) && defined(USART1_RX_vect) 110 | // do nothing - on the 32u4 the first USART is USART1 111 | #else 112 | #if !defined(USART_RX_vect) && !defined(USART0_RX_vect) && \ 113 | !defined(USART_RXC_vect) 114 | #error "Don't know what the Data Received vector is called for the first UART" 115 | #else 116 | void serialEvent() __attribute__((weak)); 117 | void serialEvent() {} 118 | #define serialEvent_implemented 119 | #if defined(USART_RX_vect) 120 | ISR(USART_RX_vect) 121 | #elif defined(USART0_RX_vect) 122 | ISR(USART0_RX_vect) 123 | #elif defined(USART_RXC_vect) 124 | ISR(USART_RXC_vect) // ATmega8 125 | #endif 126 | { 127 | #if defined(UDR0) 128 | if (bit_is_clear(UCSR0A, UPE0)) { 129 | unsigned char m = (UCSR0B >> 1) & 0x01; 130 | unsigned char c = UDR0; 131 | store_char(c, m, &rx_buffer); 132 | } else { 133 | unsigned char m = (UCSR0B >> 1) & 0x01; 134 | unsigned char c = UDR0; 135 | }; 136 | #elif defined(UDR) 137 | if (bit_is_clear(UCSRA, PE)) { 138 | unsigned char m = (UCSRB >> 1) & 0x01; 139 | unsigned char c = UDR; 140 | store_char(c, m, &rx_buffer); 141 | } else { 142 | unsigned char m = (UCSRB >> 1) & 0x01; 143 | unsigned char c = UDR; 144 | }; 145 | #else 146 | #error UDR not defined 147 | #endif 148 | } 149 | #endif 150 | #endif 151 | 152 | #if defined(USART1_RX_vect) 153 | void serialEvent1() __attribute__((weak)); 154 | void serialEvent1() {} 155 | #define serialEvent1_implemented 156 | ISR(USART1_RX_vect) 157 | { 158 | if (bit_is_clear(UCSR1A, UPE1)) { 159 | unsigned char m = (UCSR1B >> 1) & 0x01; 160 | unsigned char c = UDR1; 161 | store_char(c, m, &rx_buffer1); 162 | } else { 163 | unsigned char m = (UCSR1B >> 1) & 0x01; 164 | unsigned char c = UDR1; 165 | }; 166 | } 167 | #endif 168 | 169 | #if defined(USART2_RX_vect) && defined(UDR2) 170 | void serialEvent2() __attribute__((weak)); 171 | void serialEvent2() {} 172 | #define serialEvent2_implemented 173 | ISR(USART2_RX_vect) 174 | { 175 | if (bit_is_clear(UCSR2A, UPE2)) { 176 | unsigned char m = (UCSR2B >> 1) & 0x01; 177 | unsigned char c = UDR2; 178 | store_char(c, m, &rx_buffer2); 179 | } else { 180 | unsigned char m = (UCSR2B >> 1) & 0x01; 181 | unsigned char c = UDR2; 182 | }; 183 | } 184 | #endif 185 | 186 | #if defined(USART3_RX_vect) && defined(UDR3) 187 | void serialEvent3() __attribute__((weak)); 188 | void serialEvent3() {} 189 | #define serialEvent3_implemented 190 | ISR(USART3_RX_vect) 191 | { 192 | if (bit_is_clear(UCSR3A, UPE3)) { 193 | unsigned char m = (UCSR3B >> 1) & 0x01; 194 | unsigned char c = UDR3; 195 | store_char(c, m, &rx_buffer3); 196 | } else { 197 | unsigned char m = (UCSR3B >> 1) & 0x01; 198 | unsigned char c = UDR3; 199 | }; 200 | } 201 | #endif 202 | 203 | void serialEventRun(void) 204 | { 205 | #ifdef serialEvent_implemented 206 | if (MdbPort.available()) serialEvent(); 207 | #endif 208 | #ifdef serialEvent1_implemented 209 | if (MdbPort1.available()) serialEvent1(); 210 | #endif 211 | #ifdef serialEvent2_implemented 212 | if (MdbPort2.available()) serialEvent2(); 213 | #endif 214 | #ifdef serialEvent3_implemented 215 | if (MdbPort3.available()) serialEvent3(); 216 | #endif 217 | } 218 | 219 | 220 | #if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect) 221 | // do nothing - on the 32u4 the first USART is USART1 222 | #else 223 | #if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect) 224 | #error "Don't know what the Data Register Empty vector is called for the first UART" 225 | #else 226 | #if defined(UART0_UDRE_vect) 227 | ISR(UART0_UDRE_vect) 228 | #elif defined(UART_UDRE_vect) 229 | ISR(UART_UDRE_vect) 230 | #elif defined(USART0_UDRE_vect) 231 | ISR(USART0_UDRE_vect) 232 | #elif defined(USART_UDRE_vect) 233 | ISR(USART_UDRE_vect) 234 | #endif 235 | { 236 | if (tx_buffer.head == tx_buffer.tail) { 237 | // Buffer empty, so disable interrupts 238 | #if defined(UCSR0B) 239 | cbi(UCSR0B, UDRIE0); 240 | #else 241 | cbi(UCSRB, UDRIE); 242 | #endif 243 | } 244 | else { 245 | // There is more data in the output buffer. Send the next byte 246 | unsigned char c = tx_buffer.buffer[tx_buffer.tail]; 247 | unsigned char m = tx_buffer.mode_buffer[tx_buffer.tail]; 248 | tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE; 249 | 250 | #if defined(UDR0) 251 | // Write mode bit. 252 | UCSR0B &= ~(1 << TXB80); 253 | if (m) 254 | UCSR0B |= (1 << TXB80); 255 | 256 | // Write data byte. 257 | UDR0 = c; 258 | #elif defined(UDR) 259 | // Write mode bit. 260 | UCSRB &= ~(1 << TXB8); 261 | if (m) 262 | UCSRB |= (1 << TXB8); 263 | 264 | // Write data byte. 265 | UDR = c; 266 | #else 267 | #error UDR not defined 268 | #endif 269 | } 270 | } 271 | #endif 272 | #endif 273 | 274 | #ifdef USART1_UDRE_vect 275 | ISR(USART1_UDRE_vect) 276 | { 277 | if (tx_buffer1.head == tx_buffer1.tail) { 278 | // Buffer empty, so disable interrupts 279 | cbi(UCSR1B, UDRIE1); 280 | } 281 | else { 282 | // There is more data in the output buffer. Send the next byte 283 | unsigned char c = tx_buffer1.buffer[tx_buffer1.tail]; 284 | unsigned char m = tx_buffer1.mode_buffer[tx_buffer.tail]; 285 | tx_buffer1.tail = (tx_buffer1.tail + 1) % SERIAL_BUFFER_SIZE; 286 | 287 | // Write mode bit. 288 | if (m) 289 | UCSR1B |= (1 << TXB8); 290 | else 291 | UCSR1B &= ~(1 << TXB8); 292 | 293 | // Write data byte. 294 | UDR1 = c; 295 | } 296 | } 297 | #endif 298 | 299 | #ifdef USART2_UDRE_vect 300 | ISR(USART2_UDRE_vect) 301 | { 302 | if (tx_buffer2.head == tx_buffer2.tail) { 303 | // Buffer empty, so disable interrupts 304 | cbi(UCSR2B, UDRIE2); 305 | } 306 | else { 307 | // There is more data in the output buffer. Send the next byte 308 | unsigned char c = tx_buffer2.buffer[tx_buffer2.tail]; 309 | unsigned char m = tx_buffer2.mode_buffer[tx_buffer.tail]; 310 | tx_buffer2.tail = (tx_buffer2.tail + 1) % SERIAL_BUFFER_SIZE; 311 | 312 | // Write mode bit. 313 | if (m) 314 | UCSR2B |= (1 << TXB8); 315 | else 316 | UCSR2B &= ~(1 << TXB8); 317 | 318 | // Write data byte. 319 | UDR2 = c; 320 | } 321 | } 322 | #endif 323 | 324 | #ifdef USART3_UDRE_vect 325 | ISR(USART3_UDRE_vect) 326 | { 327 | if (tx_buffer3.head == tx_buffer3.tail) { 328 | // Buffer empty, so disable interrupts 329 | cbi(UCSR3B, UDRIE3); 330 | } 331 | else { 332 | // There is more data in the output buffer. Send the next byte 333 | unsigned char c = tx_buffer3.buffer[tx_buffer3.tail]; 334 | unsigned char m = tx_buffer3.mode_buffer[tx_buffer.tail]; 335 | tx_buffer3.tail = (tx_buffer3.tail + 1) % SERIAL_BUFFER_SIZE; 336 | 337 | // Write mode bit. 338 | if (m) 339 | UCSR3B |= (1 << TXB8); 340 | else 341 | UCSR3B &= ~(1 << TXB8); 342 | 343 | // Write data byte. 344 | UDR3 = c; 345 | } 346 | } 347 | #endif 348 | 349 | 350 | // Constructors //////////////////////////////////////////////////////////////// 351 | 352 | MdbSerial::MdbSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, 353 | volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, 354 | volatile uint8_t *ucsra, volatile uint8_t *ucsrb, 355 | volatile uint8_t *ucsrc, volatile uint8_t *udr, 356 | uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x, 357 | uint8_t ucsz2, uint8_t ucsz1, uint8_t ucsz0, uint8_t upm1, uint8_t upm0, 358 | uint8_t umsel1, uint8_t umsel0) 359 | { 360 | _rx_buffer = rx_buffer; 361 | _tx_buffer = tx_buffer; 362 | _ubrrh = ubrrh; 363 | _ubrrl = ubrrl; 364 | _ucsra = ucsra; 365 | _ucsrb = ucsrb; 366 | _ucsrc = ucsrc; 367 | _udr = udr; 368 | _rxen = rxen; 369 | _txen = txen; 370 | _rxcie = rxcie; 371 | _udrie = udrie; 372 | _u2x = u2x; 373 | _ucsz2 = ucsz2; 374 | _ucsz1 = ucsz1; 375 | _ucsz0 = ucsz0; 376 | _upm1 = upm1; 377 | _upm0 = upm0; 378 | _umsel1 = umsel1; 379 | _umsel0 = umsel0; 380 | 381 | } 382 | 383 | // Public Methods ////////////////////////////////////////////////////////////// 384 | 385 | // Read back the value of one of the registers listed below. For DEBUG purposes. 386 | uint8_t MdbSerial::readRegister(int regNum) 387 | { 388 | uint8_t value = 0; 389 | 390 | switch(regNum) 391 | { 392 | case 0: 393 | value = UCSR0A; 394 | break; 395 | case 1: 396 | value = UCSR0B; 397 | break; 398 | case 2: 399 | value = UCSR0C; 400 | break; 401 | case 3: 402 | value = UDR0; 403 | default: 404 | value = 0; 405 | break; 406 | } 407 | 408 | return value; 409 | } 410 | 411 | // Get the value of the current TX buffer head. Used for DEBUG purposes. 412 | unsigned int MdbSerial::getTxHead() 413 | { 414 | return _tx_buffer->head; 415 | } 416 | 417 | // Get the value of the current TX buffer tail. Used for DEBUG purposes. 418 | unsigned int MdbSerial::getTxTail() 419 | { 420 | return _tx_buffer->tail; 421 | } 422 | 423 | void MdbSerial::begin() 424 | { 425 | // Force 9600 baud, it's the only speed supported for MDB. 426 | unsigned long baud = 9600; 427 | uint16_t baud_setting; 428 | 429 | *_ucsra = 0; 430 | baud_setting = (F_CPU / 8 / baud - 1) / 2; 431 | 432 | // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) 433 | *_ubrrh = baud_setting >> 8; 434 | *_ubrrl = baud_setting; 435 | 436 | transmitting = false; 437 | 438 | // Receiver Enable. 439 | sbi(*_ucsrb, _rxen); 440 | // Transmitter Enable. 441 | sbi(*_ucsrb, _txen); 442 | // RX Complete Interrupt Enable. 443 | sbi(*_ucsrb, _rxcie); 444 | // Disable Data Register Empty Interrupt. 445 | cbi(*_ucsrb, _udrie); 446 | 447 | // Set Character size to 9-bit. 448 | sbi(*_ucsrb, _ucsz2); 449 | sbi(*_ucsrc, _ucsz1); 450 | sbi(*_ucsrc, _ucsz0); 451 | 452 | // Disable the parity bit. 453 | cbi(*_ucsrc, _upm1); 454 | cbi(*_ucsrc, _upm0); 455 | } 456 | 457 | void MdbSerial::end() 458 | { 459 | // wait for transmission of outgoing data 460 | while (_tx_buffer->head != _tx_buffer->tail) 461 | ; 462 | 463 | cbi(*_ucsrb, _rxen); 464 | cbi(*_ucsrb, _txen); 465 | cbi(*_ucsrb, _rxcie); 466 | cbi(*_ucsrb, _udrie); 467 | 468 | // clear any received data 469 | _rx_buffer->head = _rx_buffer->tail; 470 | } 471 | 472 | int MdbSerial::available(void) 473 | { 474 | return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE; 475 | } 476 | 477 | int MdbSerial::peek(void) 478 | { 479 | if (_rx_buffer->head == _rx_buffer->tail) { 480 | return -1; 481 | } else { 482 | unsigned char m = _rx_buffer->mode_buffer[_rx_buffer->tail]; 483 | unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; 484 | return ((m << 8) | c); 485 | } 486 | } 487 | 488 | int MdbSerial::read(void) 489 | { 490 | // if the head isn't ahead of the tail, we don't have any characters 491 | if (_rx_buffer->head == _rx_buffer->tail) { 492 | return -1; 493 | } else { 494 | // Read the mode bit first. 495 | unsigned char m = _rx_buffer->mode_buffer[_rx_buffer->tail]; 496 | unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; 497 | _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE; 498 | 499 | // Return the mode and the data together in a single integer. 500 | return ((m << 8) | c); 501 | } 502 | } 503 | 504 | void MdbSerial::flush() 505 | { 506 | // UDR is kept full while the buffer is not empty, so TXC triggers when EMPTY && SENT 507 | while (transmitting && ! (*_ucsra & _BV(TXC0))); 508 | transmitting = false; 509 | } 510 | 511 | size_t MdbSerial::write(uint8_t c, uint8_t m) 512 | { 513 | unsigned int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE; 514 | 515 | // If the output buffer is full, there's nothing for it other than to 516 | // wait for the interrupt handler to empty it a bit 517 | // ???: return 0 here instead? 518 | while (i == _tx_buffer->tail) 519 | ; 520 | 521 | // Bitwise invert everything. 522 | //c = ~c; 523 | //m = (~m) & 0x01; 524 | 525 | _tx_buffer->buffer[_tx_buffer->head] = c; 526 | _tx_buffer->mode_buffer[_tx_buffer->head] = m; 527 | _tx_buffer->head = i; 528 | 529 | sbi(*_ucsrb, _udrie); 530 | // clear the TXC bit -- "can be cleared by writing a one to its bit location" 531 | transmitting = true; 532 | sbi(*_ucsra, TXC0); 533 | 534 | return 1; 535 | } 536 | 537 | MdbSerial::operator bool() { 538 | return true; 539 | } 540 | 541 | // Preinstantiate Objects ////////////////////////////////////////////////////// 542 | 543 | #if defined(UBRRH) && defined(UBRRL) 544 | MdbSerial MdbPort(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X, UCSZ2, UCSZ1, UCSZ0, UPM1, UPM0, UMSEL1, UMSEL0); 545 | #elif defined(UBRR0H) && defined(UBRR0L) 546 | MdbSerial MdbPort(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0, UCSZ02, UCSZ01, UCSZ00, UPM01, UPM00, UMSEL01, UMSEL00); 547 | #elif defined(USBCON) 548 | // do nothing - Serial object and buffers are initialized in CDC code 549 | #else 550 | #error no serial port defined (port 0) 551 | #endif 552 | 553 | #if defined(UBRR1H) 554 | MdbSerial MdbPort1(&rx_buffer1, &tx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1, UCSZ12, UCSZ11, UCSZ10, UPM11, UPM10, UMSEL11, UMSEL10); 555 | #endif 556 | #if defined(UBRR2H) 557 | MdbSerial MdbPort2(&rx_buffer2, &tx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2, UCSZ22, UCSZ21, UCSZ20, UPM21, UPM20, UMSEL21, UMSEL20); 558 | #endif 559 | #if defined(UBRR3H) 560 | MdbSerial MdbPort3(&rx_buffer3, &tx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3, UCSZ32, UCSZ31, UCSZ30, UPM31, UPM30, UMSEL31, UMSEL30); 561 | #endif 562 | 563 | #endif // whole file 564 | 565 | --------------------------------------------------------------------------------