AvBrand Exploring Technology
AvBrand Exploring Technology
All Projects Car PC Thermostat Power Monitor Toilet Dog Dish PopCARD Cheapo Power Dance Floor 

PopCARD: Pop Machine Cash Card - Source Code v2

This code is copyright © 2011 Avatar-X. If you use it, please let me know.
// POPCARD INTERFACE // CREATED BY AvBrand.com, April 2011 // UPDATED OCTOBER 2011 for V2 #include <SPI.h> #include <Ethernet.h> #include <LiquidCrystal.h> #include "pitches.h" byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // the media access control (ethernet hardware) address for the shield: byte ip[] = { 192, 168, 1, 11 }; //the IP address for the shield: byte server[] = { 192, 168, 1, 1 }; // The server to connect to. char data[50]; // Data buffer for TCP comms byte dataPos = 0; // Position in data buffer. char myName[20]; // Current user's name float myCredit = 0; // Current credit char serRead[20]; // Serial input buffer byte serPos = 0; // Position in input buffer. byte CURR_MODE = 0; // Current operation mode. long modeStart = 0; // When did we enter this mode? byte buyPrompt = 0; // Have we shown the "push to buy" prompt yet? long lastPing = 0; // When did we receive the last ping? byte TAKE_BUTTONS = 0; // When 1, we "steal" the buttons. int buySelection = 0; // The selection we want to buy. byte isSoldOut[10]; #define PIN_RFID_RX 0 #define PIN_RFID_RESET 2 #define PIN_LCD_RS 3 #define PIN_LCD_ENABLE 4 #define PIN_LCD_D4 5 #define PIN_LCD_D5 6 #define PIN_LCD_D6 7 #define PIN_LCD_D7 8 #define PIN_VEND_RELAY 9 // pulse twice to add $1 to machine #define PIN_BILL_ENABLE A0 // when HIGH, the vending machine can accept money. #define PIN_BUZZER A2 // the piezo buzzer #define PIN_RESET_NET A3 // network module reset #define PIN_SELECTION_1 23 #define PIN_SELECTION_2 25 #define PIN_SELECTION_3 27 #define PIN_SELECTION_4 29 #define PIN_SELECTION_5 31 #define PIN_SELECTION_6 33 #define PIN_SELECTION_7 35 #define PIN_SELECTION_8 37 #define PIN_SELECTION_9 39 #define PIN_BUTTON_1 22 #define PIN_BUTTON_2 24 #define PIN_BUTTON_3 26 #define PIN_BUTTON_4 28 #define PIN_BUTTON_5 30 #define PIN_BUTTON_6 32 #define PIN_BUTTON_7 34 #define PIN_BUTTON_8 36 #define PIN_BUTTON_9 38 // Sold out switches. Grounded when pressed, so if HIGH, selection is IN STOCK (NOT SOLD OUT) #define PIN_SOLDOUT_1 40 #define PIN_SOLDOUT_2 41 #define PIN_SOLDOUT_3 42 #define PIN_SOLDOUT_4 43 #define PIN_SOLDOUT_5 44 #define PIN_SOLDOUT_6 45 #define PIN_SOLDOUT_7 46 #define PIN_SOLDOUT_8 47 #define PIN_SOLDOUT_9 48 // LCD and TCP clients LiquidCrystal lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7); Client client(server, 6643); #define MODE_STARTUP 0 // Initial mode #define MODE_CONNECT 1 // Connecting #define MODE_UNAVAIL 2 // Unavailable / Not connected #define MODE_IDLE 3 // Idle, Not doing anything #define MODE_WAIT_NAME 4 // Tag was scanned, waiting for the name of the person #define MODE_WAIT_CREDIT 5 // Name received, now waiting for the credit amount to show up. #define MODE_READY 6 // Ready for the user to make a selection #define MODE_ERROR 7 // Something went wrong. #define MODE_WAIT_BUY 8 // Waiting for BUY confirmation. #define MODE_NO_MONEY 9 // Not enough money for this selection #define MODE_NO_MONEY2 10 // No Money message #2 #define MODE_BUYING 11 // Selection purchase process in progress #define MODE_UNKNOWN 12 // Unknown card was scanned #define MODE_SOLDOUT 13 // The selection is sold out. #define MODE_ENJOY 14 // Enjoy your selection. void setup() { // SPI pins on other arduino. pinMode(10, INPUT); pinMode(11, INPUT); pinMode(12, INPUT); pinMode(13, INPUT); pinMode(PIN_SELECTION_1, OUTPUT); pinMode(PIN_SELECTION_2, OUTPUT); pinMode(PIN_SELECTION_3, OUTPUT); pinMode(PIN_SELECTION_4, OUTPUT); pinMode(PIN_SELECTION_5, OUTPUT); pinMode(PIN_SELECTION_6, OUTPUT); pinMode(PIN_SELECTION_7, OUTPUT); pinMode(PIN_SELECTION_8, OUTPUT); pinMode(PIN_SELECTION_9, OUTPUT); // Selection buttons pinMode(PIN_BUTTON_1, INPUT); pinMode(PIN_BUTTON_2, INPUT); pinMode(PIN_BUTTON_3, INPUT); pinMode(PIN_BUTTON_4, INPUT); pinMode(PIN_BUTTON_5, INPUT); pinMode(PIN_BUTTON_6, INPUT); pinMode(PIN_BUTTON_7, INPUT); pinMode(PIN_BUTTON_8, INPUT); pinMode(PIN_BUTTON_9, INPUT); // Internal pullups digitalWrite(PIN_BUTTON_1, HIGH); digitalWrite(PIN_BUTTON_2, HIGH); digitalWrite(PIN_BUTTON_3, HIGH); digitalWrite(PIN_BUTTON_4, HIGH); digitalWrite(PIN_BUTTON_5, HIGH); digitalWrite(PIN_BUTTON_6, HIGH); digitalWrite(PIN_BUTTON_7, HIGH); digitalWrite(PIN_BUTTON_8, HIGH); digitalWrite(PIN_BUTTON_9, HIGH); // Sold out switches pinMode(PIN_SOLDOUT_1, INPUT); pinMode(PIN_SOLDOUT_2, INPUT); pinMode(PIN_SOLDOUT_3, INPUT); pinMode(PIN_SOLDOUT_4, INPUT); pinMode(PIN_SOLDOUT_5, INPUT); pinMode(PIN_SOLDOUT_6, INPUT); pinMode(PIN_SOLDOUT_7, INPUT); pinMode(PIN_SOLDOUT_8, INPUT); pinMode(PIN_SOLDOUT_9, INPUT); for (int i = 0; i < 10; i++) isSoldOut[i] = 0; // Internal pullups digitalWrite(PIN_SOLDOUT_1, HIGH); digitalWrite(PIN_SOLDOUT_2, HIGH); digitalWrite(PIN_SOLDOUT_3, HIGH); digitalWrite(PIN_SOLDOUT_4, HIGH); digitalWrite(PIN_SOLDOUT_5, HIGH); digitalWrite(PIN_SOLDOUT_6, HIGH); digitalWrite(PIN_SOLDOUT_7, HIGH); digitalWrite(PIN_SOLDOUT_8, HIGH); digitalWrite(PIN_SOLDOUT_9, HIGH); buzz(NOTE_C4, 8); // Startup sound delay(500); // Start up the ethernet interface. pinMode(PIN_RESET_NET, OUTPUT); digitalWrite(PIN_RESET_NET, LOW); // Startup sound again buzz(NOTE_D4, 8); // Set up the ethernet and serial. Ethernet.begin(mac, ip); Serial.begin(9600); // Give the ethernet time to start up. delay(500); // Startup sound position 3 buzz(NOTE_E4, 8); // Set up the LCD module lcd.begin(16, 2); lcd.print("Startup"); buzz(NOTE_G4, 8); // Set up my pins. pinMode(PIN_VEND_RELAY, OUTPUT); pinMode(PIN_RFID_RESET, OUTPUT); pinMode(PIN_BILL_ENABLE, INPUT); pinMode(PIN_BUZZER, OUTPUT); // Play the "One Up" sound to start soundOneUp(); // Turn on the RFID module digitalWrite(PIN_RFID_RESET, HIGH); // Set the initial mode and make the connection. setMode(MODE_STARTUP); connectToServer(); } void loop () { // Main program loop. int selButton = 0; long mil = millis(); // Did the timer loop around? Prevent errors or hangs by the timer looping back to 0 if (lastPing > mil) lastPing = mil; if (modeStart > mil) modeStart = mil; // Is my TCP connection still alive? if ((!client.connected() && lastPing == 0) || mil - lastPing > 15000) { // Reconnect. connectToServer(); } // If we are connected, then the rest of the logic can run. switch (CURR_MODE) { case MODE_IDLE: checkRFID(); // See if a tag has been scanned. break; case MODE_READY: // Only allow us to be in this mode for 8 seconds if (mil - modeStart > 8000) { setMode(MODE_IDLE); // Jump out of this mode and go back to being ready to scan cards } if (mil - modeStart > 4000 && buyPrompt == 0) // Display buy prompt after 4 seconds { buyPrompt = 1; // 1234567890123456 lcd.clear(); lcd.print("Make a selection"); lcd.setCursor(0,1); lcd.print(" with buttons "); break; } // Wait for the user to make a selection with one of the buttons selButton = checkButtons(); // Was one of the buttons pressed? if (selButton > 0) { // Is this selection in stock? if (isSelectionInStock(selButton) == 1) { // See if we have enough money to buy this selection. setMode(MODE_WAIT_BUY); buySelection = selButton; char bb[0]; bb[0] = selButton + 48; sendCommand('B', bb); } else { // It's not in stock. Display a message. setMode(MODE_SOLDOUT); // Play a sad sound. buzz(NOTE_G5, 8); buzz(NOTE_G4, 8); buzz(NOTE_G3, 8); } } break; case MODE_WAIT_NAME: // Timeout for these modes case MODE_WAIT_CREDIT: case MODE_WAIT_BUY: if (mil - modeStart > 8000) setMode(MODE_ERROR); // Jump out of this mode and go back to being ready to scan cards break; case MODE_ERROR: case MODE_NO_MONEY2: case MODE_UNKNOWN: case MODE_ENJOY: if (mil - modeStart > 2000) setMode(MODE_IDLE); // Jump out of this mode and go back to being ready to scan cards break; case MODE_SOLDOUT: if (mil - modeStart > 3000) setMode(MODE_READY); // Go back to the credit display. break; case MODE_NO_MONEY: if (mil - modeStart > 2000) setMode(MODE_NO_MONEY2); // Show the 2nd 'No Money' message break; } if (client.connected()) { // See if the client has sent us any data. checkNetData(); } scanButtons(); } void setMode(byte b) // Change to this mode. { CURR_MODE = b; modeStart = millis(); // When did we enter this mode? lcd.clear(); switch(b) { case MODE_STARTUP: // 1234567890123456 lcd.print("Starting Up..."); break; case MODE_CONNECT: lcd.print("Connecting..."); break; case MODE_UNAVAIL: lcd.print("Cards Not Avail."); lcd.setCursor(0,1); lcd.print("Use Coins Below"); TAKE_BUTTONS = 0; // Turn off the RFID module. digitalWrite(PIN_RFID_RESET, LOW); break; case MODE_IDLE: // 1234567890123456 lcd.print(" Scan your "); lcd.setCursor(0,1); lcd.print(" PopCARD now! "); TAKE_BUTTONS = 0; // Turn on the RFID module. digitalWrite(PIN_RFID_RESET, HIGH); break; case MODE_WAIT_NAME: TAKE_BUTTONS = 1; // Don't send the selection buttons to the vending machine. lcd.print("Please wait..."); break; case MODE_WAIT_CREDIT: TAKE_BUTTONS = 1; // Don't send the selection buttons to the vending machine. lcd.setCursor(0,1); lcd.print("Please wait..."); break; case MODE_READY: TAKE_BUTTONS = 1; // Don't send the selection buttons to the vending machine. buyPrompt = 0; lcd.print(myName); showMoney(); break; case MODE_ERROR: TAKE_BUTTONS = 0; lcd.print("An error occured."); break; case MODE_WAIT_BUY: TAKE_BUTTONS = 1; // Don't send the selection buttons to the vending machine. lcd.print("Adding Credit, "); lcd.setCursor(0,1); lcd.print("Please Wait!"); break; case MODE_SOLDOUT: TAKE_BUTTONS = 1; // Don't send the selection buttons to the vending machine. // 1234567890123456 lcd.print(" Sorry, that is "); lcd.setCursor(0,1); lcd.print(" SOLD OUT! "); break; case MODE_NO_MONEY: // 1234567890123456 lcd.print("Not Enough Funds"); lcd.setCursor(0,1); lcd.print("Something else? "); showMoney(); break; case MODE_NO_MONEY2: // 1234567890123456 lcd.print("See Alex, Unit#5"); lcd.setCursor(0,1); lcd.print("to buy credit."); break; case MODE_UNKNOWN: TAKE_BUTTONS = 0; // 1234567890123456 lcd.print("Invalid or"); lcd.setCursor(0,1); lcd.print("Unknown Card"); break; case MODE_ENJOY: // 1234567890123456 lcd.print(" Enjoy your "); lcd.setCursor(0,1); lcd.print(" beverage! "); TAKE_BUTTONS = 1; break; } } void showMoney() { lcd.setCursor(0,1); // 1234567890123456 // Credit: $XX.XX lcd.print("Credit: $"); lcd.print(myCredit, DEC); lcd.setCursor(myCredit < 10 ? 13 : 14, 1); lcd.print(" "); } void connectToServer() { lastPing = millis(); setMode(MODE_CONNECT); // Reset the network module before all connections. buzz(NOTE_C4, 8); digitalWrite(PIN_RESET_NET, LOW); scanDelay(100); digitalWrite(PIN_RESET_NET, HIGH); scanDelay(500); Ethernet.begin(mac, ip); scanDelay(1000); // Reset the sold out status so they get resent to the server. for (int i = 0; i < 10; i++) isSoldOut[i] = 2; client.stop(); // Close the connection if (client.connect()) { setMode(MODE_IDLE); } else { setMode(MODE_UNAVAIL); } } void checkNetData() { // See if there is data available on the network interface. if (client.available() > 0) { byte d = client.read(); switch (d) { case 0x02: // Start of transmission dataPos = 0; break; case 0x03: // End of transmission data[dataPos] = 0; // Blank out the end. readCommand(); break; default: // Data byte. Put it in the buffer. data[dataPos] = d; dataPos++; } } } void readCommand() // Parse an input command from the network interface. { // First byte should be the command. char dd[20]; // Copy the data. for (byte i = 1; i <= dataPos && i < 21; i++) dd[i-1] = data[i]; switch (data[0]) { case 'P': // Ping lastPing = millis(); break; case 'N': // Name has arrived. Copy into MyName if (CURR_MODE == MODE_WAIT_NAME) { for (byte i = 1; i <= dataPos && i < 21; i++) myName[i-1] = data[i]; setMode(MODE_WAIT_CREDIT); } break; case 'C': // Credit amount has arrived if (CURR_MODE == MODE_WAIT_CREDIT) { myCredit = atof(dd); setMode(MODE_READY); } break; case 'Y': // Yes, you may buy. if (CURR_MODE == MODE_WAIT_BUY) { // Buy this selection! if (buySelection > 0) { buyNow(buySelection, atoi(dd)); buySelection = 0; } // After the buy, return to idle mode. setMode(MODE_ENJOY); } break; case 'X': // Not enough money. if (CURR_MODE == MODE_WAIT_BUY) { // Play a sad sound. buzz(NOTE_G5, 8); buzz(NOTE_G4, 8); buzz(NOTE_G3, 8); // Show the no money message. setMode(MODE_NO_MONEY); } break; case 'R': // Unknown card. setMode(MODE_UNKNOWN); break; } } void sendCommand(byte cmd, char cData[]) // Send a command to the host. { // Send a command back to the server. client.write(0x02); client.write(cmd); client.print(cData); client.write(0x03); } void checkRFID() { // Check the serial port for data from the RFID module. if (Serial.available() > 0) { byte d = Serial.read(); switch (d) { case 0x02: // Start of transmission serPos = 0; break; case 0x03: // End of transmission readTag(); break; default: // Add to buffer. serRead[serPos] = d; serPos++; } } } void readTag() // Parse the data from a card. { serRead[10] = 0; Serial.print("Card Read:"); Serial.println(serRead); setMode(MODE_WAIT_NAME); // Make a beep buzz(NOTE_B4, 8); buzz(NOTE_E5, 4); // Ask the server who this is. sendCommand('S', serRead); // Turn off the RFID module. digitalWrite(PIN_RFID_RESET, LOW); } void addCredit() { // Pulsing the 'VEND NO' and 'VEND COM' lines on the vendor // twice tells it that $1 has been inserted. digitalWrite(PIN_VEND_RELAY, HIGH); delay(150); digitalWrite(PIN_VEND_RELAY, LOW); delay(400); digitalWrite(PIN_VEND_RELAY, HIGH); delay(150); digitalWrite(PIN_VEND_RELAY, LOW); } void buzz(long note, long dTime) { // Play a tone. tone(PIN_BUZZER, note, 1000 / dTime); delay(1000/dTime); noTone(PIN_BUZZER); } void soundOneUp() { // Make the one-up sound. tone(PIN_BUZZER, NOTE_E5, 125); delay(125); tone(PIN_BUZZER, NOTE_G5, 125); delay(125); tone(PIN_BUZZER, NOTE_E6, 125); delay(125); tone(PIN_BUZZER, NOTE_C6, 125); delay(125); tone(PIN_BUZZER, NOTE_D6, 125); delay(125); tone(PIN_BUZZER, NOTE_G6, 125); delay(125); noTone(PIN_BUZZER); } void scanDelay(int t) { // Do a delay but also scan the buttons during that time. long ms = millis(); do { // Add other functions here if needed scanButtons(); } while (millis() - ms < t && millis() > ms); } void scanButtons() { // watch the button inputs digitalWrite(PIN_SELECTION_1, digitalRead(PIN_BUTTON_1) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_2, digitalRead(PIN_BUTTON_2) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_3, digitalRead(PIN_BUTTON_3) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_4, digitalRead(PIN_BUTTON_4) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_5, digitalRead(PIN_BUTTON_5) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_6, digitalRead(PIN_BUTTON_6) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_7, digitalRead(PIN_BUTTON_7) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_8, digitalRead(PIN_BUTTON_8) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); digitalWrite(PIN_SELECTION_9, digitalRead(PIN_BUTTON_9) == HIGH || TAKE_BUTTONS == 1 ? LOW : HIGH); checkSoldOut(1, PIN_SOLDOUT_1); checkSoldOut(2, PIN_SOLDOUT_2); checkSoldOut(3, PIN_SOLDOUT_3); checkSoldOut(4, PIN_SOLDOUT_4); checkSoldOut(5, PIN_SOLDOUT_5); checkSoldOut(6, PIN_SOLDOUT_6); checkSoldOut(7, PIN_SOLDOUT_7); checkSoldOut(8, PIN_SOLDOUT_8); checkSoldOut(9, PIN_SOLDOUT_9); } void checkSoldOut(int n, int pin) { int isOut = (digitalRead(pin) == LOW ? 1 : 0); if (isOut != isSoldOut[n-1]) { // Tell the server the sold out status. isSoldOut[n-1] = isOut; char bb[2]; bb[0] = n + 48; bb[1] = isOut == 1 ? 'Y' : 'N'; sendCommand('L', bb); } } int checkButtons() { // Return the first button that was pressed. if (digitalRead(PIN_BUTTON_1) == LOW) return 1; if (digitalRead(PIN_BUTTON_2) == LOW) return 2; if (digitalRead(PIN_BUTTON_3) == LOW) return 3; if (digitalRead(PIN_BUTTON_4) == LOW) return 4; if (digitalRead(PIN_BUTTON_5) == LOW) return 5; if (digitalRead(PIN_BUTTON_6) == LOW) return 6; if (digitalRead(PIN_BUTTON_7) == LOW) return 7; if (digitalRead(PIN_BUTTON_8) == LOW) return 8; if (digitalRead(PIN_BUTTON_9) == LOW) return 9; return 0; } byte isSelectionInStock(int n) { // Check to see if the selection is in stock. // A value of HIGH means the sold-out switch is NOT depressed, meaning the selection is in stock. if (n == 1) return (digitalRead(PIN_SOLDOUT_1) == HIGH ? 1 : 0); if (n == 2) return (digitalRead(PIN_SOLDOUT_2) == HIGH ? 1 : 0); if (n == 3) return (digitalRead(PIN_SOLDOUT_3) == HIGH ? 1 : 0); if (n == 4) return (digitalRead(PIN_SOLDOUT_4) == HIGH ? 1 : 0); if (n == 5) return (digitalRead(PIN_SOLDOUT_5) == HIGH ? 1 : 0); if (n == 6) return (digitalRead(PIN_SOLDOUT_6) == HIGH ? 1 : 0); if (n == 7) return (digitalRead(PIN_SOLDOUT_7) == HIGH ? 1 : 0); if (n == 8) return (digitalRead(PIN_SOLDOUT_8) == HIGH ? 1 : 0); if (n == 9) return (digitalRead(PIN_SOLDOUT_9) == HIGH ? 1 : 0); } void buyNow(int b, int cost) { // Buy this selection!. // We need to add one credit for each cost dollar. int dollLeft = cost; do { dollLeft--; soundOneUp(); // Play the credit noise addCredit(); // Add $1 to the vending machine. delay(900); // Provide enough time for the pop machine to register the credit. // Wait until the vend relay says it's ok. if (dollLeft > 0) // But only if we still have to put money in. { long ms = millis(); do { if (digitalRead(PIN_BILL_ENABLE) == HIGH) break; } while (millis() - ms < 1000 && millis() > ms); } } while (dollLeft > 0); delay(300); // NOW, press the selection button! int selPin = 0; if (b == 1) selPin = PIN_SELECTION_1; if (b == 2) selPin = PIN_SELECTION_2; if (b == 3) selPin = PIN_SELECTION_3; if (b == 4) selPin = PIN_SELECTION_4; if (b == 5) selPin = PIN_SELECTION_5; if (b == 6) selPin = PIN_SELECTION_6; if (b == 7) selPin = PIN_SELECTION_7; if (b == 8) selPin = PIN_SELECTION_8; if (b == 9) selPin = PIN_SELECTION_9; digitalWrite(selPin, HIGH); // Give the vender time to see it delay(1000); digitalWrite(selPin, LOW); // Make sure it doesn't try to reconnect uselessly now. lastPing = millis(); // All done! }
copyright © 2024 AvBrand.com - sitemap