├── README.md ├── cashduino_integration_test.ino └── docs ├── CashDuino_Technical_Integration_bill_coin.pdf ├── cashduino-architectures.pdf └── cashduino_ppt_customer.pdf /README.md: -------------------------------------------------------------------------------- 1 | # cashduino 2 | 3 | * MDB bill validator: ICT TAO, CASH CODE, MEI BILL ACCEPTOR, ICT RECYCLER 4 | * MDB coin changers: MEI-CF7000 5 | * MDB cashless: NAYAX 6 | * Hardware compatible: Arduino R3. 7 | * I2C address 0x08. 8 | * Tasks: request from Master(Arduino) to slave(CashDuino). 9 | * Event: event reported from slave. 10 | * CMD: command. 11 | * SUB: Sub command. 12 | * EVT: Event counter. 13 | * DAT: Data 14 | 15 | ### Tasks 16 | * COIN ENABLE 17 | * COIN AUDIT 18 | * COIN OUT 19 | * BILL ENABLE 20 | * BILL ESCROW 21 | * BILL AUDIT 22 | * BILL OUT 23 | 24 | ### Events 25 | * COIN READ 26 | * BILL READ 27 | 28 | **ENQUIRY BOARD** 29 | 30 | MASTER TO SLAVE 31 | 32 | |CMD|DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 33 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----| 34 | |C1 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 35 | 36 | MASTER READ FROM SLAVE 37 | |CMD|SUB |EVT |DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 38 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----| 39 | |D1 |00 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 40 | 41 | **COIN ENABLE** 42 | 43 | MASTER TO SLAVE 44 | 45 | |CMD|DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 46 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----| 47 | |A1 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 48 | 49 | MASTER READ FROM SLAVE 50 | |CMD|SUB |EVT |DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 51 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----| 52 | |F1 |00 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 53 | 54 | **COIN AUDIT** 55 | 56 | MASTER TO SLAVE 57 | 58 | |CMD|DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 59 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----| 60 | |A2 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 61 | 62 | MASTER READ FROM SLAVE 63 | |CMD|SUB |EVT |DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 64 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----| 65 | |F2 |00 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 66 | 67 | * DAT1 = Knows tube Channel 1 68 | * DAT2 = Knows tube Channel 2 69 | * DAT3 = Knows tube Channel 3 70 | * DAT4 = Knows tube Channel 4 71 | * DAT5 = Knows tube Channel 5 72 | * DAT6 = Knows tube Channel 6 73 | * DAT7 = Knows tube Channel 7 74 | * DAT8 = Knows tube Channel 8 75 | 76 | **COIN OUT** 77 | 78 | MASTER TO SLAVE 79 | 80 | |CMD|DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 81 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----| 82 | |A2 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 83 | 84 | * DAT1 = VALUE 85 | * DAT2 = CHANNEL 1 86 | * DAT3 = VALUE 87 | * DAT4 = CHANNEL 2 88 | * DAT5 = VALUE 89 | * DAT6 = CHANNEL 3 90 | * DAT7 = VALUE 91 | * DAT8 = CHANNEL 4 92 | 93 | MASTER READ FROM SLAVE 94 | |CMD|SUB |EVT |DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5| 95 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----| 96 | |F2 |00 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 | 97 | 98 | **COIN READ EVENT** 99 | 100 | |CMD|SUB |EVT |DAT1|DAT2|DAT3|DAT4|DAT5|DAT6|DAT7|DAT8|KEY1|KEY2|KEY3|KEY4|KEY5|Event| 101 | |---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|-----| 102 | |F4 |40 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 |CASHBOX| 103 | |F4 |50 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 |TUBE| 104 | |F4 |01 |00 |00 |00 |00 |00 |00 |00 |00 |00 |33 |CC |33 |CC |33 |REJECT BUTTON| 105 | -------------------------------------------------------------------------------- /cashduino_integration_test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Description: Integration code to use cashduino shield 3 | * Author: Jesus Palomo (jgualberto.palomo@gmail.com) 4 | * Copyrigth: 2021 palomothings 5 | * 6 | * This file is part of palomothings site 7 | * 8 | * CashDuino integration is a free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License GPLV2 as published by 10 | * the Free Software Fundation. 11 | * 12 | * CashDuino Integracion is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without event the implied warranty of 14 | * MERCHANTABILITY of FITNEDD FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. http://www.gnu.org/license/ 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #define MAX_BUFF_CASHDUINO 16 23 | #define MAX_BUFF_ARDUINO 14 24 | #define CASH_DUINO_ADDR 8 25 | #define KEY_1 0x33 26 | #define KEY_2 0xCC 27 | #define NONE 0xFF 28 | #define DISABLE_COIN_DEVICE 2 29 | #define DISABLE_BILL_DEVICE 6 30 | #define CASHDUINO_POLL_TIME 250 31 | #define MAX_BUFFER_MSG 40 32 | 33 | /* user select the option (1-9) and display the message in the Serial Monitor */ 34 | const char MATRIX_MESSAGE[][MAX_BUFFER_MSG] PROGMEM = {"ARDUINO SEND ENQUIRY BOARD > ", /* option 1 */ 35 | "ARDUINO SEND COIN ENABLE TASK > ", /* option 2 */ 36 | "ARDUINO SEND COIN DISABLE TASK > ", /* option 3 */ 37 | "ARDUINO SEND COIN AUDIT TASK > ", /* option 4 */ 38 | "ARDUINO SEND COIN OUT TASK > ", /* option 5 */ 39 | "ARDUINO SEND BILL ENABLE TASK > ", /* option 6 */ 40 | "ARDUINO SEND BILL DISABLE TASK > ", /* option 7 */ 41 | "ARDUINO SEND BILL TO STACKER TASK > ", /* option 8 */ 42 | "ARDUINO SEND BILL REJECT TASK > ", /* option 9 */ 43 | "ARDUINO SEND BILL AUDIT > ", /* option : */ 44 | "ARDUINO SEND BILL OUT > " /* option ; */ 45 | }; 46 | 47 | /* options to the user, the user need to type in the Serial Monitor 1 to 9 */ 48 | typedef enum{ 49 | SEND_ENQUIRY_BOARD = 0, 50 | SEND_COIN_ENABLE, 51 | COIN_DISABLE, 52 | SEND_COIN_AUDIT, 53 | SEND_COIN_OUT, 54 | SEND_BILL_ENABLE, 55 | SEND_BILL_DISABLE, 56 | SEND_BILL_TO_STACKER, 57 | SEND_BILL_REJECT, 58 | SEND_BILL_AUDIT, 59 | SEND_BILL_OUT, 60 | INVALID_OPTION 61 | }pc_options; 62 | 63 | /* arduino tasks options */ 64 | typedef enum{ 65 | ARDUINO_CMD_ENQUIRY = 0xC1, 66 | ARDUINO_CMD_COIN_ENABLE = 0xA1, 67 | ARDUINO_CMD_COIN_AUDIT = 0xA2, 68 | ARDUINO_CMD_COIN_OUT = 0xA3, 69 | ARDUINO_CMD_BILL_ENABLE = 0xB1, 70 | ARDUINO_CMD_BILL_ESCROW = 0xB2, 71 | ARDUINO_CMD_BILL_AUDIT = 0xB3, 72 | ARDUINO_CMD_BILL_OUT = 0xB4, 73 | }cmd_arduino; 74 | 75 | /* cashduino cmd answers */ 76 | typedef enum{ 77 | CASHDUINO_CMD_ENQUIRY = 0xD1, 78 | CASHDUINO_CMD_COIN_ENABLE = 0xF1, 79 | CASHDUINO_CMD_COIN_AUDIT = 0xF2, 80 | CASHDUINO_CMD_COIN_OUT = 0xF3, 81 | CASHDUINO_CMD_COIN_EVENT = 0xF4, 82 | CASHDUINO_CMD_BILL_ENABLE = 0xE1, 83 | CASHDUINO_CMD_BILL_ESCROW = 0xE2, 84 | CASHDUINO_CMD_BILL_AUDIT = 0xE3, 85 | CASHDUINO_CMD_BILL_OUT = 0xE4, 86 | CASHDUINO_CMD_BILL_EVENT = 0xE5, 87 | }cmd_cashduino; 88 | 89 | 90 | /* global variables */ 91 | uint8_t new_event = NONE; 92 | uint8_t command = 0x00; 93 | double amount = 0; 94 | 95 | void setup() { 96 | /* join i2c bus (address optional for master) */ 97 | Wire.begin(); 98 | /* put your setup code here, to run once: */ 99 | Serial.begin(9600); 100 | } 101 | 102 | void loop() { 103 | uint8_t rx_cashduino[MAX_BUFF_CASHDUINO] = {0}; 104 | uint8_t selection = INVALID_OPTION; 105 | uint8_t bill_cmd = NONE; 106 | uint8_t coin_cmd = NONE; 107 | 108 | /* read serial port and send task to cashduino */ 109 | if ( ( selection = read_buffer_from_serial() ) != INVALID_OPTION ) 110 | { 111 | if( (selection == DISABLE_COIN_DEVICE) || (selection == DISABLE_BILL_DEVICE) ) 112 | { 113 | amount = 0; 114 | } 115 | /* it is a valid data, push the buffer to cashduino */ 116 | send_task_to_cashduino(selection); 117 | } 118 | 119 | /* read cashduino buffer */ 120 | read_buffer_from_cashduino(rx_cashduino); 121 | 122 | /* is a new event? */ 123 | if ( looking_for_new_event(rx_cashduino) == true ) 124 | { 125 | /* is a valid package? */ 126 | if( is_a_valid_package(rx_cashduino) == true ) 127 | { 128 | /* parsing buffer to find data */ 129 | parsing_buffer_cashduino(rx_cashduino); 130 | coin_cmd = parsing_buffer_coin_acceptor(rx_cashduino, &amount); 131 | bill_cmd = parsing_buffer_bill_acceptor(rx_cashduino, &amount); 132 | 133 | if ( ( coin_cmd == CASHDUINO_CMD_COIN_EVENT) || ( bill_cmd == CASHDUINO_CMD_BILL_EVENT) ) 134 | { 135 | Serial.print("Cash: $ " ); 136 | Serial.println(amount, 2); /* pint amount */ 137 | } 138 | else if(coin_cmd == CASHDUINO_CMD_COIN_AUDIT) 139 | { 140 | /* print channel values, information available from 3 to 9 */ 141 | Serial.print(rx_cashduino[3]);Serial.print("\n"); 142 | Serial.print(rx_cashduino[4]);Serial.print("($0.50 MXN)\n"); 143 | Serial.print(rx_cashduino[5]);Serial.print("\n"); 144 | Serial.print(rx_cashduino[6]);Serial.print("($1 MXN)\n"); 145 | Serial.print(rx_cashduino[7]);Serial.print("($2 MXN)\n"); 146 | Serial.print(rx_cashduino[8]);Serial.print("($5 MXN)\n"); 147 | Serial.print(rx_cashduino[9]);Serial.print("($10 MXN)\n"); 148 | Serial.print(rx_cashduino[10]);Serial.print("\n"); 149 | } 150 | } 151 | } 152 | 153 | /* time to enable cashduino data process */ 154 | delay(CASHDUINO_POLL_TIME); 155 | 156 | } 157 | 158 | /* function to validate the data structure */ 159 | bool is_a_valid_package(uint8_t *rx_cashduino) 160 | { 161 | if( (rx_cashduino[11] == KEY_1) && (rx_cashduino[12] == KEY_2) && 162 | (rx_cashduino[13] == KEY_1) && (rx_cashduino[14] == KEY_2) && 163 | (rx_cashduino[15] == KEY_1) ) 164 | { 165 | return true; 166 | } 167 | return false; 168 | } 169 | 170 | /* function to know if the board is working */ 171 | void parsing_buffer_cashduino(uint8_t *rx_cashduino) 172 | { 173 | /* type of command */ 174 | switch(rx_cashduino[0]) 175 | { 176 | case CASHDUINO_CMD_ENQUIRY: 177 | /* Serial.print("CashDuino is a live :) \n" ); //debug */ 178 | break; 179 | } 180 | } 181 | 182 | /* function to parsing the cashduino buffer and get information */ 183 | uint8_t parsing_buffer_coin_acceptor(uint8_t *rx_cashduino, double *amount) 184 | { 185 | uint8_t ret_code = NONE; 186 | /* type of command */ 187 | switch(rx_cashduino[0]) 188 | { 189 | case CASHDUINO_CMD_COIN_ENABLE: 190 | /*Serial.print("Coin charger configured \n" ); //debug*/ 191 | break; 192 | case CASHDUINO_CMD_COIN_AUDIT: 193 | ret_code = CASHDUINO_CMD_COIN_AUDIT; 194 | break; 195 | case CASHDUINO_CMD_COIN_OUT: 196 | /*Serial.print("Coin changer dispense coin \n" );*/ 197 | break; 198 | case CASHDUINO_CMD_COIN_EVENT: 199 | /*Serial.print("Event Counter: ");Serial.print(rx_cashduino[2]);Serial.print("\n"); //debug*/ 200 | if( (rx_cashduino[1] >= 0x40) && (rx_cashduino[1] <= 0x4F)) 201 | { 202 | ret_code = CASHDUINO_CMD_COIN_EVENT; 203 | /*Serial.print("Coin in CashBox =) -> " ); //debug*/ 204 | switch (rx_cashduino[1]){ 205 | case 0x41: 206 | /*Serial.print("($0.50 MXN) Inserted\n"); //debug*/ 207 | *(amount) += 0.5; 208 | break; 209 | case 0x42: 210 | /*Serial.print("($1 MXN) Inserted\n"); //debug*/ 211 | *(amount) += 1; 212 | break; 213 | case 0x43: 214 | /*Serial.print("($2 MXN) Inserted\n"); //debug*/ 215 | *(amount) += 2; 216 | break; 217 | case 0x44: 218 | /*Serial.print("($5 MXN) Inserted\n"); //debug*/ 219 | *(amount) += 5; 220 | break; 221 | case 0x45: 222 | /*Serial.print("($10 MXN) Inserted\n"); //debug*/ 223 | *(amount) += 10; 224 | break; 225 | } 226 | } 227 | if( (rx_cashduino[1] >= 0x50) && (rx_cashduino[1] <= 0x5F) ) 228 | { 229 | ret_code = CASHDUINO_CMD_COIN_EVENT; 230 | /*Serial.print("Coin in Tube =) -> " ); //debug*/ 231 | switch (rx_cashduino[1]){ 232 | case 0x51: 233 | /*Serial.print("$0.50 MXN\n"); //debug*/ 234 | *(amount) += 0.5; 235 | break; 236 | case 0x52: 237 | /*Serial.print("$1 MXN\n"); //debug*/ 238 | *(amount) += 1; 239 | break; 240 | case 0x53: 241 | /*Serial.print("$2 MXN\n"); //debug*/ 242 | *(amount) += 2; 243 | break; 244 | case 0x54: 245 | /*Serial.print("$5 MXN\n"); //debug*/ 246 | *(amount) += 5; 247 | break; 248 | case 0x55: 249 | /*Serial.print("$10 MXN\n"); //debug*/ 250 | *(amount) += 10; 251 | break; 252 | break; 253 | } 254 | } 255 | if(rx_cashduino[1] == 0x01) 256 | { 257 | /*Serial.print("Push Reject =(\n" );//debug*/ 258 | } 259 | break; 260 | } 261 | return ret_code; 262 | } 263 | 264 | /* function to parsing the cashduino buffer and get information */ 265 | uint8_t parsing_buffer_bill_acceptor(uint8_t *rx_cashduino, double *amount) 266 | { 267 | uint8_t ret_code = NONE; 268 | /* type of command */ 269 | switch(rx_cashduino[0]) 270 | { 271 | case CASHDUINO_CMD_BILL_ENABLE: 272 | /*Serial.print("Bill acceptor configured :)\n" );//debug*/ 273 | break; 274 | case CASHDUINO_CMD_BILL_ESCROW: 275 | /*Serial.print("Bill Escrow\n" );//debug*/ 276 | break; 277 | case 0xE3: 278 | /*Serial.print("Bill Audit\n" );//debug*/ 279 | break; 280 | case 0xE4: 281 | /*Serial.print("Bill Out\n" );//debug*/ 282 | break; 283 | case CASHDUINO_CMD_BILL_EVENT: 284 | /*Serial.print("Event Counter: ");Serial.print(rx_cashduino[2]);Serial.print("\n");*/ 285 | if( (rx_cashduino[1] >= 0x90) && (rx_cashduino[1] <= 0x9F)) 286 | { 287 | /*Serial.print("Bill in pre stacker =) -> " );*/ 288 | switch (rx_cashduino[1]){ 289 | case 0x90: 290 | /*Serial.print("$20 MXN\n");//debug*/ 291 | break; 292 | case 0x91: 293 | /*Serial.print("$50 MXN\n");//debug*/ 294 | break; 295 | case 0x92: 296 | /*Serial.print("$100 MXN\n");//debug*/ 297 | break; 298 | case 0x93: 299 | /*Serial.print("$200 MXN\n");//debug*/ 300 | break; 301 | case 0x94: 302 | /*Serial.print("$500 MXN\n");//debug*/ 303 | break; 304 | break; 305 | } 306 | } 307 | if( (rx_cashduino[1] >= 0x80) && (rx_cashduino[1] <= 0x8F)) 308 | { 309 | ret_code = CASHDUINO_CMD_BILL_EVENT; 310 | /*Serial.print("Bill in cashbox =) -> " );//debug*/ 311 | switch (rx_cashduino[1]){ 312 | case 0x80: 313 | /*Serial.print("$20 MXN\n");//debug*/ 314 | *(amount) += 20; 315 | break; 316 | case 0x81: 317 | /*Serial.print("$50 MXN\n");//debug*/ 318 | *(amount) += 50; 319 | break; 320 | case 0x82: 321 | /*Serial.print("$100 MXN\n");//debug*/ 322 | *(amount) += 100; 323 | break; 324 | case 0x83: 325 | /*Serial.print("$200 MXN\n");//debug*/ 326 | *(amount) += 200; 327 | break; 328 | case 0x84: 329 | /*Serial.print("$500 MXN\n");//debug*/ 330 | *(amount) += 500; 331 | break; 332 | break; 333 | } 334 | } 335 | if( (rx_cashduino[1] >= 0xA0) && (rx_cashduino[1] <= 0xAF)) 336 | { 337 | /*Serial.print("Bill rejected =) -> " );*/ 338 | switch (rx_cashduino[1]){ 339 | case 0xA0: 340 | /*Serial.print("$20 MXN\n");//debug*/ 341 | break; 342 | case 0xA1: 343 | /*Serial.print("$50 MXN\n");//debug*/ 344 | break; 345 | case 0xA2: 346 | /*Serial.print("$100 MXN\n");//debug*/ 347 | break; 348 | case 0xA3: 349 | /*Serial.print("$200 MXN\n");//debug*/ 350 | break; 351 | case 0xA4: 352 | /*Serial.print("$500 MXN\n");//debug*/ 353 | break; 354 | break; 355 | } 356 | } 357 | break; 358 | } 359 | return ret_code; 360 | } 361 | 362 | /* function to update the event counter */ 363 | bool looking_for_new_event(uint8_t *rx_cashduino) 364 | { 365 | uint8_t i = 0; 366 | if( (command != rx_cashduino[0]) || ( new_event != rx_cashduino[2] ) ) 367 | { 368 | command = rx_cashduino[0]; 369 | new_event = rx_cashduino[2]; 370 | Serial.print("CASHDUINO ANSWER < " ); /* Debug */ 371 | for(i = 0; i= SEND_ENQUIRY_BOARD) && (selection < INVALID_OPTION) ) 446 | { 447 | arduino_push_buffer(MATRIX_MESSAGE[selection], ARDUINO_TASK); 448 | } 449 | 450 | } 451 | 452 | void arduino_push_buffer(const char *message, uint8_t *cmd) 453 | { 454 | char myChar = 0; 455 | uint8_t i = 0; 456 | for (i = 0; i < strlen_P(message); i++) { 457 | myChar = pgm_read_word_near(message + i); /* Debug */ 458 | Serial.print(myChar); 459 | } 460 | 461 | Wire.beginTransmission(CASH_DUINO_ADDR); /* transmit to CashDuino */ 462 | for(i = 0; i 0) 476 | { 477 | serial_option = Serial.read(); 478 | /* normal tasks */ 479 | if( ( serial_option >= '1' ) && (serial_option <= ';') ) 480 | { 481 | serial_option -= '0'; 482 | return serial_option - 1; 483 | } 484 | } 485 | return INVALID_OPTION; 486 | } 487 | -------------------------------------------------------------------------------- /docs/CashDuino_Technical_Integration_bill_coin.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalomoProjects/cashduino/f719b6267b1890d91c98e53ed7bbe0fa1b4de737/docs/CashDuino_Technical_Integration_bill_coin.pdf -------------------------------------------------------------------------------- /docs/cashduino-architectures.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalomoProjects/cashduino/f719b6267b1890d91c98e53ed7bbe0fa1b4de737/docs/cashduino-architectures.pdf -------------------------------------------------------------------------------- /docs/cashduino_ppt_customer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PalomoProjects/cashduino/f719b6267b1890d91c98e53ed7bbe0fa1b4de737/docs/cashduino_ppt_customer.pdf --------------------------------------------------------------------------------