├── .gitignore ├── .gitmodules ├── ArduinoVendingMachine.h ├── ArduinoVendingMachine.ino ├── EEPROMAnything.h ├── Makefile ├── README.md ├── TODO.txt └── gpl2.txt /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Arduino_Makefile_master"] 2 | path = Arduino_Makefile_master 3 | url = https://github.com/Lauszus/Arduino_Makefile_master.git 4 | [submodule "VendingMachinePython"] 5 | path = VendingMachinePython 6 | url = https://github.com/Jervelund/VendingMachinePython.git 7 | [submodule "VendingMachineRFID"] 8 | path = VendingMachineRFID 9 | url = https://github.com/Jervelund/VendingMachineRFID 10 | [submodule "VendingMachineImp"] 11 | path = VendingMachineImp 12 | url = https://github.com/Jervelund/VendingMachineImp 13 | -------------------------------------------------------------------------------- /ArduinoVendingMachine.h: -------------------------------------------------------------------------------- 1 | #ifndef _ArduinoVendingMachine_h_ 2 | #define _ArduinoVendingMachine_h_ 3 | 4 | #include 5 | #include "EEPROMAnything.h" 6 | 7 | enum Letter { 8 | OFF = 0x00, 9 | SPACE = 0xFF, space = SPACE, 10 | dash = 0xBF, // '-' 11 | mdash = dash, // middle dash 12 | ldash = 0xF7, // high dash 13 | hdash = 0xFE, // low dash 14 | A = 0x88, a = 0xA0, 15 | B = 0x80, b = 0x83, 16 | C = 0xC6, c = 0xA7, 17 | d = 0xA1, 18 | E = 0x86, 19 | F = 0x8E, 20 | g = 0x90, 21 | H = 0x89, h = 0x8B, 22 | I = 0xCF, i = 0xEF, 23 | J = 0xE0, j = 0xF1, 24 | K = 0x89, 25 | L = 0xC7, 26 | m1 = 0xAF, m2 = 0xAB, 27 | n = 0xAB, 28 | O = 0xC0, o = 0xA3, 29 | P = 0x8C, 30 | Q = 0x40, 31 | R = 0x88, r = 0xAF, 32 | S = 0x92, 33 | T1 = 0xF8, T2 = 0xFE, 34 | u = 0xE3, 35 | V = 0xC1, 36 | // w 37 | X = 0x89, 38 | Y = 0x91, 39 | // z 40 | }; 41 | 42 | const uint8_t ERR_EEPROM_BAD[] = {E, r, r, SPACE, E, E, P, R, O, m1, m1, SPACE, b, A, d, OFF}; 43 | const uint8_t ERR_OUT_OF_MEM[] = {E, r, r, SPACE, n, o, SPACE, F, r, E, E, SPACE, m1, m2, E, m1, m2, OFF}; 44 | const uint8_t ERR_NO_CREDIT[] = {n, o, SPACE, C, r, E, d, I, T1, T2, OFF}; 45 | 46 | const uint8_t COLA[] = { C, O, L, A, OFF }; 47 | const uint8_t PEPSI[] = { P, E, P, S, I, OFF }; 48 | const uint8_t FANTA[] = { F, A, n, T1, T2 , A, OFF }; 49 | const uint8_t FAXE[] = { F, A, X, E, OFF }; 50 | const uint8_t BEER[] = { B, E, E, r, OFF }; 51 | const uint8_t NO_REFUND[] = { n, o, SPACE, r, E, F, u, n, d, OFF }; 52 | const uint8_t TRAPPED[] = { H, E, L, P, SPACE, I, SPACE, A, m1, m2, SPACE, T1, T2, r, A, P, P, E, d, SPACE, i, n, SPACE, A, SPACE, V, E, n, d, i, n, g, SPACE , m1, m2, A, c, h, i, n, E, OFF }; 53 | const uint8_t LADDER[] = { ldash, mdash, hdash, mdash, ldash, mdash, hdash, mdash, ldash, OFF }; 54 | const uint8_t CONGRATULATIONS[] = { C, o, n, g, r, a, T1, T2, u, L, a, T1, T2, i, o, n, S, OFF }; 55 | 56 | // Function prototypes 57 | bool checkCoinSlots(); 58 | void coinChecker(); 59 | void coinReturnCheck(); 60 | void sortArray(uint8_t *input, uint8_t size); 61 | void scrollDisplay(const uint8_t *output); 62 | void updateScroll(); 63 | void checkAllSlots(); 64 | void spinMotor(uint8_t motor); 65 | void checkStopMotor(); 66 | void purchaseChecker(); 67 | void randomChecker(); 68 | void showError(); 69 | void showErrorJam(); 70 | void showErrorDry(); 71 | void cointInterrupt(); 72 | void showBoot(); 73 | void errorDisplay(); 74 | void showValue(uint16_t input); 75 | void printDisplay(uint8_t *output); 76 | void resetMotors(); 77 | void motorStuck(uint8_t motor); 78 | bool checkSlot(uint32_t input, uint8_t motor); 79 | bool motorSwitchPressed(uint32_t input, uint8_t motor); 80 | bool buyButtonPressed(uint32_t input, uint8_t button); 81 | void updateMotorsLEDs(); 82 | uint32_t readSwitches(); 83 | void delayNew(unsigned long ms); 84 | void updateDry(); 85 | void tweetBoot(); 86 | void tweetStatus(); 87 | void delayTweet(); 88 | void updateDryNoOutput(); 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /ArduinoVendingMachine.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * The code is released under the GNU General Public License. 3 | * Developed by Kristian Lauszus, TKJ Electronics 2013 4 | * This code is used for an old vending machine where the main board did not work anymore 5 | */ 6 | 7 | #include "ArduinoVendingMachine.h" 8 | 9 | // Change price of the items here: 10 | const uint8_t priceArray[] = { 5, 5, 5, 5, 5, 5 }; 11 | // Change the name of the item here: 12 | const uint8_t *nameArray[] = { LADDER, LADDER, LADDER, LADDER, LADDER, LADDER }; // See in ArduinoVendingMachine.h for the possible names. If the one you need is not present then type NULL instead 13 | // Change value of the coin slots: 14 | const uint8_t coinSlotValue[] = { 5, 0, 10 }; // Coin slots from right to left - note that the middle one is not connected at the moment 15 | const uint8_t coinSlotLeftDefault[] = { 6, 0, 5 }; // Coins there is in the slot when it thinks it is empty - with safety margin of 1 16 | 17 | const uint16_t timeBetweenTweets = 60000; 18 | uint8_t tweetCoins = 0; 19 | uint32_t lastTweet, tweetCoinsTimer; 20 | 21 | // Do not change anything else below this line! 22 | uint8_t coinSlotLeft[] = { coinSlotLeftDefault[0], coinSlotLeftDefault[1], coinSlotLeftDefault[2] }; 23 | 24 | const uint8_t coinPin = 2; // Interrupt pin connected to the coin validator pulse pin 25 | uint8_t lastButtonPressed; 26 | uint32_t purchaseTimer; 27 | bool waitAfterButtonPress; 28 | 29 | const uint8_t clockPinLED = A1, dataPinLED = A2, latchPinLED = A0, resetPinLED = 13; 30 | const uint8_t numbers[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; // Numbers for LED matrix 31 | 32 | uint8_t displayBuffer[5]; 33 | uint8_t *pOutputString; 34 | volatile bool displayScrolling; 35 | uint8_t scrollPosition, trailingSpaces; 36 | uint32_t scrollTimer; 37 | 38 | bool lastCoinInput; 39 | volatile uint8_t coinPulsesRecieved; 40 | uint8_t lastCoinPulsesRecieved; 41 | uint16_t counter, totalUnitsDispensed; // Counter for the credit currently in the machine and the total value of coins that have been put into the machine 42 | uint16_t lastCounter; 43 | uint32_t lastCoinPulseTime; 44 | 45 | const uint8_t clockPinOut = 3, dataPinOut = 5, latchPinOut = 4, resetPinOut = 6; // Pins for driving the motors 46 | const uint8_t clockPinIn = 11, dataPinIn = 9, latchPinIn = 7; // Pins used to check the switches 47 | 48 | const uint8_t motorToOutputMask[] = { 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 49 | const uint8_t motorToInputMask[] = { 0x02, 0x04, 0x08, 0x10, 0x20, 0x40 }; 50 | const uint8_t errorLedMask = 0x02; //, greenLedMask = 0x01; // The green LED does not work at the moment 51 | 52 | uint8_t motorOutput = 0, ledOutput = 0; 53 | uint32_t motorTimer; 54 | bool motorIsStuck[6]; 55 | bool reportedDry[6]; 56 | uint32_t timeToNextTrapped; 57 | uint32_t lastTrapped; 58 | 59 | const uint8_t coinSolenoid[] = { 10, 0, A3 }; // Connected to the solenoids 60 | const uint8_t coinSlot[] = { A6, 0, A7 }; // Analog input used to check if the coin slots are empty 61 | const uint8_t coinReturn = A5; // Return button 62 | const uint8_t COIN_EMPTY = 500; // If the ADC value gets below this value, then the coin slot is empty 63 | uint32_t refundTimer; 64 | 65 | void setup() { 66 | Serial.begin(57600); // Initialize serial communications with master vending Arduino 67 | Serial.setTimeout(200); 68 | 69 | // Pins for LED matrix 70 | digitalWrite(clockPinLED, LOW); 71 | digitalWrite(latchPinLED, HIGH); 72 | digitalWrite(resetPinLED, HIGH); 73 | 74 | pinMode(clockPinLED, OUTPUT); 75 | pinMode(latchPinLED, OUTPUT); 76 | pinMode(resetPinLED, OUTPUT); 77 | pinMode(dataPinLED, OUTPUT); 78 | 79 | // Pins used for shiftOut 80 | digitalWrite(dataPinOut, LOW); 81 | digitalWrite(clockPinOut, LOW); 82 | digitalWrite(resetPinOut, HIGH); // Reset first 83 | digitalWrite(latchPinOut, HIGH); // Then latch 84 | 85 | pinMode(clockPinOut, OUTPUT); 86 | pinMode(latchPinOut, OUTPUT); 87 | pinMode(resetPinOut, OUTPUT); 88 | pinMode(dataPinOut, OUTPUT); 89 | 90 | // Pins for shiftIn 91 | pinMode(clockPinIn, OUTPUT); 92 | pinMode(latchPinIn, OUTPUT); 93 | pinMode(dataPinIn, INPUT); 94 | 95 | digitalWrite(clockPinIn, LOW); 96 | digitalWrite(latchPinIn, HIGH); 97 | 98 | // Setup outputs for solenoids 99 | for (uint8_t i = 0; i < sizeof(coinSolenoid); i++) { 100 | pinMode(coinSolenoid[i], OUTPUT); 101 | digitalWrite(coinSolenoid[i], LOW); // Make sure it is low by default 102 | } 103 | 104 | pinMode(coinReturn, INPUT_PULLUP); 105 | 106 | // Update display and set motors to the default position 107 | showBoot(); 108 | //delay(2000); 109 | //resetMotors(); // Reset all motors to the default position 110 | if (!checkCoinSlots()) { 111 | scrollDisplay(NO_REFUND); // If there is no coins left show "No refund" 112 | refundTimer = millis(); 113 | } 114 | else 115 | showValue(counter); // Update display to show counter value 116 | 117 | pinMode(coinPin, INPUT); // Setup coin input 118 | counter = lastCounter = coinPulsesRecieved = lastCoinPulsesRecieved = lastTweet = 0; 119 | EEPROM_readAnything(0, totalUnitsDispensed); // Read value from EEPROM 120 | delay(300); // Make sure the voltage is stable 121 | updateDryNoOutput(); 122 | lastTrapped = 0; 123 | randomSeed(analogRead(A4)); // Use analog input as random seed 124 | timeToNextTrapped = random(1000000, 3000000); 125 | attachInterrupt(0, cointInterrupt, CHANGE); 126 | } 127 | 128 | #define TRANSMISSION_REPEATS 5 129 | #define TRANSMISSION_SPACE 95 130 | #define TRANSMISSION_ATOM_SIZE 3 131 | uint8_t recieve_error; 132 | 133 | uint16_t rfid_raw_read() { 134 | char parseBuffer[TRANSMISSION_REPEATS * TRANSMISSION_ATOM_SIZE]; 135 | uint16_t number = 0; 136 | recieve_error = 1; // set default to error 137 | if (Serial.readBytes(parseBuffer, sizeof(parseBuffer)) == sizeof(parseBuffer)) { 138 | // Recieved correct ammount of bytes - check message integrity 139 | if (memcmp(parseBuffer, parseBuffer + TRANSMISSION_ATOM_SIZE, sizeof(parseBuffer) - TRANSMISSION_ATOM_SIZE) == 0) { 140 | if (parseBuffer[2] == TRANSMISSION_SPACE) { 141 | // OK 142 | recieve_error = 0; 143 | memcpy(&number, parseBuffer, sizeof(number)); 144 | // Rember to notify other party that we do not need a retransmission 145 | } 146 | } 147 | } 148 | return number; 149 | } 150 | 151 | void rfid_raw_transmit(uint16_t number) { 152 | for (int i = 0; i < TRANSMISSION_REPEATS * TRANSMISSION_ATOM_SIZE; i = i + TRANSMISSION_ATOM_SIZE) { 153 | Serial.write(number & 0xFF); 154 | Serial.write(number >> 8); 155 | Serial.write(TRANSMISSION_SPACE); 156 | } 157 | } 158 | 159 | void loop() { 160 | if (Serial.available()) { 161 | int input = Serial.read(); 162 | // Only used for debugging 163 | //if (input >= '0' && input <= '5') { 164 | //spinMotor(input - '0'); 165 | //} 166 | //RFID functionality 167 | if (input == 'C') { // Fetch current credits 168 | Serial.write('C'); 169 | rfid_raw_transmit(counter); 170 | } 171 | else if (input == 'S') { // Set current credits 172 | uint16_t temp_counter = rfid_raw_read(); 173 | if (recieve_error == 0) { // If credits received correctly, update counter 174 | counter = temp_counter; 175 | // Transmit received message back to notify other party we received correctly 176 | Serial.write('S'); 177 | Serial.write(temp_counter & 0xFF); 178 | Serial.write(temp_counter >> 8); 179 | } else { 180 | Serial.write(255); // Did not receive correctly - notify other party 181 | Serial.write(255); 182 | Serial.write(255); 183 | } 184 | showValue(counter); 185 | } 186 | else if (input == 'Z') { // Zero current credits 187 | Serial.write('Z'); 188 | counter = 0; 189 | showValue(counter); 190 | } 191 | else if (input == 'F') { 192 | Serial.write('F'); // RFID request concluded without error 193 | } 194 | else if (input == 'E') { 195 | Serial.write('E'); 196 | scrollDisplay(ERR_EEPROM_BAD); 197 | } 198 | else if (input == 'O') { 199 | Serial.write('O'); 200 | scrollDisplay(ERR_OUT_OF_MEM); 201 | } 202 | else if (input == 'N') { 203 | Serial.write('N'); 204 | scrollDisplay(ERR_NO_CREDIT); 205 | } 206 | /*else if (input == 'V') { 207 | for (uint8_t motor = 0; motor < sizeof(motorToOutputMask); motor++) 208 | spinMotor(motor); 209 | } 210 | else if (input == 'C') 211 | scrollDisplay(COLA); 212 | else if (input == 'P') 213 | scrollDisplay(PEPSI); 214 | else if (input == 'F') 215 | scrollDisplay(FANTA); 216 | else if (input == 'X') 217 | scrollDisplay(FAXE); 218 | else if (input == 'B') 219 | scrollDisplay(BEER); 220 | else if (input == 'N') 221 | scrollDisplay(NO_REFUND); 222 | else if (input == 'T') 223 | scrollDisplay(TRAPPED); 224 | else if (input == 'E') 225 | Serial.println(totalUnitsDispensed); 226 | else if (input == 'R') { 227 | Serial.print("EEPROM was reset - old value: "); 228 | Serial.println(totalUnitsDispensed); 229 | totalUnitsDispensed = 0; 230 | EEPROM_updateAnything(0, totalUnitsDispensed); 231 | } else if (input == 'S') { 232 | tweetStatus(); 233 | }*/ 234 | 235 | delayTweet(); 236 | } 237 | 238 | checkStopMotor(); // Check if a motor has turned a half revolution 239 | checkAllSlots(); // Check if any slot is empty 240 | updateMotorsLEDs(); // Send out the new values to the shift register 241 | coinChecker(); // Check if any coins have been inserted 242 | updateDry(); // Check for empty slots, and tweet if any slots become empty 243 | 244 | purchaseChecker(); // Check if a button has been pressed 245 | randomChecker(); // Check if random button is pressed 246 | coinReturnCheck(); // Check if the coin return button is pressed 247 | 248 | if (millis() - lastTweet > timeBetweenTweets) { 249 | tweetStatus(); 250 | lastTweet = millis(); 251 | } 252 | 253 | if (millis() - lastTrapped > timeToNextTrapped) { 254 | scrollDisplay(TRAPPED); // Display "Help... I'm stuck in a vendingmachine" 255 | lastTrapped = millis(); 256 | timeToNextTrapped = random(100000, 3000000); 257 | } 258 | 259 | if (displayScrolling) 260 | updateScroll(); 261 | else if (!checkCoinSlots() && (millis() - refundTimer > 12000)) { // Scroll "No refund" every 12s 262 | scrollDisplay(NO_REFUND); // If there is no coins left show "No refund" 263 | refundTimer = millis(); 264 | } 265 | else if ((!waitAfterButtonPress && counter != lastCounter) || (waitAfterButtonPress && (millis() - purchaseTimer > 1000))) { // Only update the LED matrix if a coin has been inserted or 1s after purchaseChecker() has printed something to the LED matrix 266 | showValue(counter); 267 | lastCounter = counter; 268 | waitAfterButtonPress = false; 269 | } 270 | 271 | if (totalUnitsDispensed % 1000 == 0) { 272 | scrollDisplay(CONGRATULATIONS); 273 | for (uint8_t motor = 0; motor < sizeof(motorToOutputMask); motor++) 274 | spinMotor(motor); 275 | } 276 | } 277 | 278 | void delayTweet() { 279 | if (millis() - lastTweet > timeBetweenTweets - 5000) // Assume that we sent a response 280 | lastTweet += 10000; // Postpone tweet 281 | } 282 | 283 | bool checkCoinSlots() { 284 | uint8_t minCoinIndex = 0; 285 | uint8_t minCoinValue = 0xFF; 286 | for (uint8_t i = 0; i < sizeof(coinSlotValue); i++) { 287 | if (coinSlotValue[i] > 0 && coinSlotValue[i] < minCoinValue) { // Find minimum coin value 288 | minCoinIndex = i; 289 | minCoinValue = coinSlotValue[i]; 290 | } 291 | if (analogRead(coinSlot[i]) < COIN_EMPTY) // Check if coin slot is empty 292 | coinSlotLeft[i] = 0; 293 | else 294 | coinSlotLeft[i] = coinSlotLeftDefault[i]; // Restore default value if has been refilled 295 | } 296 | 297 | if (analogRead(coinSlot[minCoinIndex]) < COIN_EMPTY) // Check if coin slot with minimum value is empty 298 | return false; 299 | return true; 300 | } 301 | 302 | void coinChecker() { 303 | if (coinPulsesRecieved != lastCoinPulsesRecieved) { // Only run the check if pulses has changed 304 | if (coinPulsesRecieved > 1) { // Accept coin(s) and reset coin pulses 305 | cli(); // Disable interrupts to make sure we don't disregard any coins 306 | uint8_t coins = coinPulsesRecieved >> 1; // Get count of "whole" coins 307 | coinPulsesRecieved -= coins << 1; // Subtract "whole" coins from pulses received (we could be between pulses) 308 | sei(); // Enable interrupts again 309 | uint8_t creditsAdded = coins * 5; 310 | counter += creditsAdded; 311 | tweetCoins += creditsAdded; 312 | lastCoinPulseTime = 0; 313 | tweetCoinsTimer = millis(); 314 | } 315 | lastCoinPulsesRecieved = coinPulsesRecieved; 316 | delayTweet(); 317 | } 318 | else if (coinPulsesRecieved == 1) { // If pulses is 1, and has not changed for 150ms, reset pulse count 319 | if (lastCoinPulseTime == 0) // If timer is not set, the pulse was just received 320 | lastCoinPulseTime = millis(); 321 | else if (millis() - lastCoinPulseTime > 150) // Faux pulse - reset everything 322 | lastCoinPulseTime = coinPulsesRecieved = lastCoinPulsesRecieved = 0; 323 | } 324 | if (tweetCoins && millis() - tweetCoinsTimer > 500) { 325 | Serial.write('c'); 326 | Serial.write(tweetCoins); 327 | tweetCoins = 0; 328 | } 329 | } 330 | 331 | void coinReturnCheck() { 332 | if (counter && analogRead(coinReturn) < 50) { // The button is normally high 333 | uint8_t sortedArray[sizeof(coinSlotValue)]; 334 | memcpy(sortedArray, coinSlotValue, sizeof(coinSlotValue)); // Copy array 335 | sortArray(sortedArray, sizeof(sortedArray)); // Sort the array in descending order 336 | 337 | for (uint8_t i = 0; i < sizeof(sortedArray); i++) { 338 | for (uint8_t j = 0; j < sizeof(coinSlotValue); j++) { 339 | if (sortedArray[i] > 0 && coinSlotValue[j] == sortedArray[i]) { 340 | while (counter >= coinSlotValue[j]) { // Keep releasing coins until the counter is lower than the value 341 | if (coinSlotLeft[j] == 0) // Check if coin slot is empty 342 | break; 343 | else { 344 | digitalWrite(coinSolenoid[j], HIGH); // Turn on solenoid 345 | delayNew(250); // Wait while polling motors 346 | digitalWrite(coinSolenoid[j], LOW); // Release solenoid 347 | delayNew(250); // Wait while polling motors 348 | 349 | if (analogRead(coinSlot[j]) < COIN_EMPTY) 350 | coinSlotLeft[j]--; 351 | 352 | counter -= coinSlotValue[j]; 353 | showValue(counter); 354 | 355 | // Tweet coin release 356 | Serial.write('r'); 357 | Serial.write(coinSlotValue[j]); 358 | } 359 | } 360 | } 361 | } 362 | } 363 | if (counter != 0) 364 | refundTimer = 0; 365 | } 366 | } 367 | 368 | void sortArray(uint8_t *input, uint8_t size) { // Inspired by: http://www.tenouk.com/cpluscodesnippet/sortarrayelementasc.html 369 | for (uint8_t i = 1; i < size; i++) { 370 | for (uint8_t j = 0; j < size - 1; j++) { 371 | if (input[j] < input[j + 1]) { 372 | uint8_t temp = input[j]; 373 | input[j] = input[j + 1]; 374 | input[j + 1] = temp; 375 | } 376 | } 377 | } 378 | } 379 | 380 | void scrollValue(uint16_t input) { 381 | static uint8_t output[6]; // Reserve one, so the buffer always end with OFF 382 | memset(output, SPACE, sizeof(output)); // Initialize the rest to off 383 | 384 | output[5] = OFF; 385 | output[4] = numbers[input % 10]; 386 | if (input >= 10) { 387 | output[3] = numbers[(input / 10) % 10]; 388 | if (input >= 100) { 389 | output[2] = numbers[(input / 100) % 10]; 390 | if (input >= 1000) { 391 | output[1] = numbers[(input / 1000) % 10]; 392 | if (input >= 10000) 393 | output[0] = numbers[(input / 10000) % 10]; 394 | } 395 | } 396 | } 397 | scrollDisplay(output); 398 | } 399 | 400 | void scrollDisplay(const uint8_t *output) { 401 | if (output == NULL) 402 | return; 403 | pOutputString = (uint8_t*)output; 404 | displayScrolling = true; 405 | scrollPosition = 0; 406 | trailingSpaces = 0; 407 | scrollTimer = 0; 408 | memset(displayBuffer, SPACE, sizeof(displayBuffer)); // Initialize all to off 409 | } 410 | 411 | void updateScroll() { // This should be called regularly after scrollDisplay() is called 412 | uint32_t timer = millis(); 413 | if (timer - scrollTimer < 300) 414 | return; 415 | scrollTimer = timer; 416 | 417 | for (uint8_t i = sizeof(displayBuffer) - 1; i > 0 ; i--) // Shift array one to the left 418 | displayBuffer[i] = displayBuffer[i - 1]; 419 | 420 | if (trailingSpaces == 0) { // Check if it is still reading the array 421 | displayBuffer[0] = *(pOutputString + scrollPosition); // Read new value into array 422 | if (displayBuffer[0] == OFF) { // End char found 423 | displayBuffer[0] = SPACE; // Set LEDs off 424 | trailingSpaces++; 425 | } 426 | else 427 | scrollPosition++; 428 | } 429 | else 430 | trailingSpaces++; // End char is found, so just add trailing spaces until text is fully scrolled out 431 | 432 | if (trailingSpaces == sizeof(displayBuffer)) 433 | showValue(counter); // Show counter value on display again after scrolling the text 434 | else { 435 | printDisplay(displayBuffer); 436 | displayScrolling = true; 437 | } 438 | } 439 | 440 | void checkAllSlots() { // Check if any of the slots are empty 441 | uint32_t input = readSwitches(); 442 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 443 | if (checkSlot(input, i)) 444 | ledOutput |= motorToOutputMask[i]; 445 | else 446 | ledOutput &= ~motorToOutputMask[i]; 447 | } 448 | } 449 | 450 | void spinMotor(uint8_t motor) { // You must call checkStopMotor() to stop the motor again after it has done the half revolution 451 | motorTimer = millis(); 452 | motorOutput |= motorToOutputMask[motor]; 453 | ledOutput |= errorLedMask; 454 | updateMotorsLEDs(); 455 | while (!motorSwitchPressed(readSwitches(), motor)) // Wait until switch is pressed 456 | delay(10); 457 | } 458 | 459 | void checkStopMotor() { // Stops motors after is has done a half revolution 460 | uint32_t input = readSwitches(); 461 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 462 | if (!motorSwitchPressed(input, i) && motorOutput & motorToOutputMask[i]) { // Switch is released and motor is running 463 | motorOutput &= ~motorToOutputMask[i]; 464 | ledOutput &= ~errorLedMask; 465 | totalUnitsDispensed++; 466 | EEPROM_updateAnything(0, totalUnitsDispensed); 467 | } 468 | } 469 | 470 | if (motorOutput && millis() - motorTimer > 10000) { // If the motor has been turning more than 10s, then it must be stuck 471 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 472 | if (motorOutput & motorToOutputMask[i]) { // Motor is running 473 | counter += priceArray[i]; // Give back credit 474 | motorStuck(i); 475 | showErrorJam(); // Show error for 1s 476 | } 477 | } 478 | } 479 | } 480 | 481 | // TODO: If there is not enough money blink price 482 | void purchaseChecker() { 483 | uint8_t price = 0; 484 | uint8_t buttonPressed = 0xFF; // No button is pressed 485 | 486 | uint32_t switchInput = readSwitches(); 487 | for (uint8_t i = 0; i < sizeof(motorToInputMask); i++) { 488 | if (buyButtonPressed(switchInput, i)) { 489 | price = priceArray[i]; 490 | buttonPressed = i; 491 | break; 492 | } 493 | } 494 | 495 | if (buttonPressed != 0xFF && buttonPressed != lastButtonPressed) { 496 | if (ledOutput & motorToOutputMask[buttonPressed]) { // Check if the selected item is available 497 | if (counter >= price) { // Purchase item 498 | if (!motorOutput) { // Check if any motor is spinning 499 | spinMotor(buttonPressed); 500 | counter -= price; 501 | scrollValue(totalUnitsDispensed); 502 | } 503 | } else { // Not enough money to buy item 504 | showValue(price); // Show the price of the item 505 | purchaseTimer = millis(); // Set up timer, so it clears it after a set amount of time 506 | waitAfterButtonPress = true; 507 | } 508 | } 509 | else { 510 | if (motorIsStuck[buttonPressed] == true) 511 | showErrorJam(); // Show error for 1s 512 | else 513 | showErrorDry(); // Show error for 1s 514 | } 515 | } 516 | lastButtonPressed = buttonPressed; 517 | } 518 | 519 | void randomChecker() { 520 | if ((readSwitches() >> 16) & 0x01) // If random button is not set return 521 | return; 522 | 523 | uint8_t purchaseAvailable = 0, selections = 0xFF; 524 | uint8_t ledCounter = sizeof(motorToInputMask) - 1; // Start from the top 525 | uint8_t buttons = 0, oldButtons = 0; 526 | uint32_t timer = 0; 527 | uint8_t nAvailable = 0; 528 | const uint16_t startSpeed = 300; 529 | 530 | // Scroll LEDs 531 | while (1) { // Run until button is released 532 | checkStopMotor(); // Check if a motor has turned a half revolution 533 | coinChecker(); // Check if any coins have been inserted 534 | 535 | uint32_t switchInput = readSwitches(); 536 | if ((switchInput >> 16) & 0x01) 537 | break; 538 | 539 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 540 | // Check if the selected item is available and there is enough money 541 | if (checkSlot(switchInput, i) && counter >= priceArray[i]) 542 | purchaseAvailable |= motorToOutputMask[i]; 543 | else 544 | purchaseAvailable &= ~motorToOutputMask[i]; 545 | } 546 | if (purchaseAvailable == 0) 547 | return; // None are available 548 | 549 | nAvailable = 0; 550 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { // Make sure that there is a even delay between when LEDs are lit 551 | if ((purchaseAvailable & selections) & motorToOutputMask[i]) 552 | nAvailable++; 553 | } 554 | 555 | uint32_t now = millis(); 556 | if (nAvailable > 0 && now - timer > startSpeed / nAvailable) { // Add delay when scrolling LEDs. The delay is adjusted based on the number of items selected 557 | if ((purchaseAvailable & selections) & motorToOutputMask[ledCounter]) { // Make sure the item is both available and selected 558 | timer = now; 559 | ledOutput = motorToOutputMask[ledCounter]; // Set LED 560 | updateMotorsLEDs(); 561 | } 562 | if (ledCounter-- == 0) 563 | ledCounter = sizeof(motorToOutputMask) - 1; 564 | } else if (nAvailable == 0) { // If all slots are deselected turn off all LEDs 565 | ledOutput = 0; 566 | updateMotorsLEDs(); 567 | } 568 | 569 | buttons = 0; 570 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 571 | if (buyButtonPressed(switchInput, i)) 572 | buttons |= motorToOutputMask[i]; // Check if any buttons are pressed 573 | } 574 | selections ^= buttons & ~oldButtons; // Update selections. A bit is toggled every time a button is pressed and then released 575 | oldButtons = buttons; 576 | } 577 | 578 | if (nAvailable == 0) 579 | return; // No item has been selected 580 | 581 | randomSeed(millis()); // Use millis as seed 582 | uint16_t randomSelection = random(20, 40); // Get random number 583 | uint16_t scrollDelay = startSpeed / nAvailable; 584 | uint8_t itemSelected = 0; 585 | 586 | while (randomSelection) { 587 | checkStopMotor(); // Check if a motor has turned a half revolution 588 | 589 | uint32_t now = millis(); 590 | if (now - timer > scrollDelay) { // Add delay when scrolling LEDs 591 | if ((purchaseAvailable & selections) & motorToOutputMask[ledCounter]) { // Make sure the item is both available and selected 592 | if (randomSelection > 10) 593 | scrollDelay++; 594 | else 595 | scrollDelay += (10 - randomSelection) * 15; // Slow down scrolling effect way more when we get close to the final selection 596 | randomSelection--; 597 | timer = now; 598 | ledOutput = motorToOutputMask[ledCounter]; // Set new LED 599 | itemSelected = ledCounter; // Store selected item 600 | updateMotorsLEDs(); 601 | } 602 | if (ledCounter-- == 0) 603 | ledCounter = sizeof(motorToOutputMask) - 1; 604 | } 605 | } 606 | delayNew(600); 607 | 608 | counter -= priceArray[itemSelected]; // Subtract price from counter 609 | spinMotor(itemSelected); 610 | scrollDisplay(CONGRATULATIONS); 611 | 612 | uint8_t tmpLedOutput = ledOutput; 613 | for (uint8_t i = 0; i < 5; i++) { // Toggle LED to indicate selection 614 | ledOutput ^= tmpLedOutput; 615 | updateMotorsLEDs(); 616 | delayNew(250); 617 | } 618 | } 619 | 620 | void showError() { 621 | errorDisplay(); 622 | purchaseTimer = millis(); // Set up timer, so it clears it after a set amount of time 623 | waitAfterButtonPress = true; 624 | } 625 | 626 | void showErrorJam() { 627 | static uint8_t output[5]; 628 | output[4] = SPACE; 629 | output[3] = j; 630 | output[2] = A; 631 | output[1] = r; 632 | output[0] = n; 633 | printDisplay(output); 634 | 635 | purchaseTimer = millis(); // Set up timer, so it clears it after a set amount of time 636 | waitAfterButtonPress = true; 637 | } 638 | 639 | void showErrorDry() { 640 | static uint8_t output[5]; 641 | output[4] = SPACE; 642 | output[3] = d; 643 | output[2] = r; 644 | output[1] = Y; 645 | output[0] = SPACE; 646 | printDisplay(output); 647 | 648 | purchaseTimer = millis(); // Set up timer, so it clears it after a set amount of time 649 | waitAfterButtonPress = true; 650 | } 651 | 652 | void cointInterrupt() { 653 | bool input = PIND & (1 << PIND2); // Read pin 2 directly using the port registers 654 | if (!input && lastCoinInput) 655 | coinPulsesRecieved++; 656 | lastCoinInput = input; 657 | displayScrolling = false; 658 | } 659 | 660 | void showBoot() { 661 | static uint8_t output[5]; 662 | output[4] = B; 663 | output[3] = O; 664 | output[2] = O; 665 | randomSeed(analogRead(A4)); // Use analog input as random seed 666 | if (random(1, 5) == 1) { 667 | output[1] = T1; 668 | output[0] = T2; 669 | } 670 | else { 671 | output[1] = B; 672 | output[0] = S; 673 | } 674 | printDisplay(output); 675 | } 676 | 677 | void errorDisplay() { 678 | static uint8_t output[5]; 679 | output[4] = dash; // '-' 680 | output[3] = E; 681 | output[2] = r; 682 | output[1] = r; 683 | output[0] = dash; // '-' 684 | printDisplay(output); 685 | } 686 | 687 | void showValue(uint16_t input) { 688 | static uint8_t output[5]; 689 | output[0] = numbers[input % 10]; 690 | 691 | memset(output + 1, SPACE, sizeof(output) - 1); // Initialize the rest to off 692 | if (input >= 10) { 693 | output[1] = numbers[(input / 10) % 10]; 694 | if (input >= 100) { 695 | output[2] = numbers[(input / 100) % 10]; 696 | if (input >= 1000) { 697 | output[3] = numbers[(input / 1000) % 10]; 698 | if (input >= 10000) 699 | output[4] = numbers[(input / 10000) % 10]; 700 | } 701 | } 702 | } 703 | printDisplay(output); 704 | } 705 | 706 | void printDisplay(uint8_t *output) { 707 | displayScrolling = false; // Stop scrolling by default 708 | digitalWrite(latchPinLED, LOW); 709 | for (uint8_t i = 0; i < 5; i++) 710 | shiftOut(dataPinLED, clockPinLED, MSBFIRST, output[i]); 711 | digitalWrite(latchPinLED, HIGH); 712 | } 713 | 714 | bool checkMotors() { 715 | uint8_t motorsDone = 0; 716 | uint32_t input = readSwitches(); 717 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 718 | if (!motorSwitchPressed(input, i)) // If switch is released stop motor 719 | motorsDone++; 720 | } 721 | if (motorsDone == sizeof(motorToOutputMask)) 722 | return true; 723 | return false; 724 | } 725 | /* 726 | void resetMotors() { // Set all motors to the default position 727 | if (checkMotors()) { // If all motors are in correct position, write motorOutput to zero and return 728 | updateMotorsLEDs(); 729 | return; 730 | } 731 | 732 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) 733 | motorOutput |= motorToOutputMask[i]; // Set all motors on in the buffer 734 | 735 | uint32_t timer = millis(); 736 | while (motorOutput != 0x00) { 737 | uint32_t input = readSwitches(); 738 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 739 | if (!motorSwitchPressed(input, i)) // If switch is released stop motor 740 | motorOutput &= ~motorToOutputMask[i]; 741 | } 742 | if (millis() - timer > 10000) { // Motors running now must be stuck 743 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 744 | if (motorOutput & motorToOutputMask[i]) // Motor is running 745 | motorStuck(i); 746 | } 747 | } 748 | updateMotorsLEDs(); 749 | delay(2); 750 | } 751 | } 752 | */ 753 | void motorStuck(uint8_t motor) { 754 | motorOutput &= ~motorToOutputMask[motor]; // Turn off motor 755 | motorIsStuck[motor] = true; 756 | } 757 | 758 | bool checkDry(uint32_t input, uint8_t motor) { 759 | return (input & motorToInputMask[motor]); 760 | } 761 | 762 | bool checkSlot(uint32_t input, uint8_t motor) { 763 | return !(input & motorToInputMask[motor]) && !motorIsStuck[motor]; 764 | } 765 | 766 | bool motorSwitchPressed(uint32_t input, uint8_t motor) { 767 | return input & ((uint16_t)motorToInputMask[motor] << 8); 768 | } 769 | 770 | bool buyButtonPressed(uint32_t input, uint8_t button) { 771 | return !(input & ((uint32_t)motorToInputMask[button] << 16)); 772 | } 773 | 774 | void updateMotorsLEDs() { 775 | digitalWrite(latchPinOut, LOW); // Ground latchPin and hold low for as long as you are transmitting 776 | shiftOut(dataPinOut, clockPinOut, LSBFIRST, ledOutput); 777 | shiftOut(dataPinOut, clockPinOut, LSBFIRST, motorOutput); 778 | digitalWrite(latchPinOut, HIGH); // Return the latch pin high to signal chip that it no longer needs to listen for information 779 | } 780 | 781 | uint32_t readSwitches() { 782 | digitalWrite(latchPinIn, LOW); 783 | delayMicroseconds(20); 784 | digitalWrite(latchPinIn, HIGH); 785 | uint32_t input = shiftIn(dataPinIn, clockPinIn, LSBFIRST); 786 | input |= (uint16_t)shiftIn(dataPinIn, clockPinIn, LSBFIRST) << 8; 787 | input |= (uint32_t)shiftIn(dataPinIn, clockPinIn, LSBFIRST) << 16; 788 | return input; 789 | } 790 | 791 | void delayNew(unsigned long ms) { // Just a copy of the normal delay(), but also checks if motor should be stopped 792 | uint16_t start = (uint16_t)micros(); 793 | uint8_t oldMotorOutput; 794 | 795 | while (ms > 0) { 796 | if (((uint16_t)micros() - start) >= 1000) { 797 | ms--; 798 | start += 1000; 799 | 800 | oldMotorOutput = motorOutput; 801 | checkStopMotor(); // Check if motor should be stopped 802 | if (motorOutput != oldMotorOutput) // Check if motor output is updated 803 | updateMotorsLEDs(); // Update motor and LED output 804 | } 805 | } 806 | } 807 | 808 | void updateDry() { // Check if any of the slots are empty, and updates the Dry information 809 | uint32_t input = readSwitches(); 810 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) { 811 | if (checkDry(input, i)) { 812 | if (!reportedDry[i]) 813 | reportedDry[i] = true; 814 | } 815 | else { 816 | if (reportedDry[i]) 817 | reportedDry[i] = false; 818 | } 819 | } 820 | } 821 | 822 | void updateDryNoOutput() { 823 | // updateDry without printing 824 | uint32_t input = readSwitches(); 825 | for (uint8_t i = 0; i < sizeof(motorToOutputMask); i++) 826 | if (checkDry(input, i)) 827 | if (!reportedDry[i]) 828 | reportedDry[i] = true; 829 | } 830 | 831 | void tweetStatus() { 832 | // Show beverages dispensed (sold) 833 | Serial.write('B'); 834 | Serial.print(totalUnitsDispensed); 835 | 836 | // Print all jammed slots 837 | Serial.write(",J"); 838 | for (uint8_t i = 0; i < sizeof(motorIsStuck); i++) 839 | if (motorIsStuck[i]) 840 | Serial.print(i); 841 | 842 | // Print all empty slots 843 | Serial.write(",D"); 844 | for (uint8_t i = 0; i < sizeof(reportedDry); i++) 845 | if (reportedDry[i]) 846 | Serial.print(i); 847 | 848 | // Print all empty coin slots 849 | Serial.write(",R"); 850 | if (coinSlotLeft[0] == 0) 851 | Serial.write('0'); 852 | if (coinSlotLeft[2] == 0) 853 | Serial.write('2'); 854 | 855 | Serial.write(','); 856 | } 857 | -------------------------------------------------------------------------------- /EEPROMAnything.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2013-2014 Kristian Lauszus, TKJ Electronics. All rights reserved. 2 | 3 | This software may be distributed and modified under the terms of the GNU 4 | General Public License version 2 (GPL2) as published by the Free Software 5 | Foundation and appearing in the file GPL2.TXT included in the packaging of 6 | this file. Please note that GPL2 Section 2[b] requires that all works based 7 | on this software must also be made publicly available under the terms of 8 | the GPL2 ("Copyleft"). 9 | 10 | Contact information 11 | ------------------- 12 | 13 | Kristian Lauszus, TKJ Electronics 14 | Web : http://www.tkjelectronics.com 15 | e-mail : kristianl@tkjelectronics.com 16 | */ 17 | 18 | #ifndef _eepromanything_h_ 19 | #define _eepromanything_h_ 20 | 21 | #include 22 | 23 | // Source: http://playground.arduino.cc/Code/EEPROMWriteAnything 24 | // Modified to use the standard EEPROM library and added 'eeprom_busy_wait()' 25 | // Also added EEPROM_updateAnything to limit number of write/erase cycles 26 | // By: Krisitan Lauszus, TKJ Electronics 27 | 28 | template uint16_t EEPROM_writeAnything(uint16_t addr, const T& value) { 29 | eeprom_busy_wait(); // Wait until the EEPROM is ready 30 | const uint8_t *p = (const uint8_t*)(const void*)&value; 31 | uint16_t i; 32 | for (i = 0; i < sizeof(value); i++) 33 | eeprom_write_byte((uint8_t*)addr++, *p++); 34 | return i; 35 | } 36 | 37 | template uint16_t EEPROM_updateAnything(uint16_t addr, const T& value) { 38 | eeprom_busy_wait(); // Wait until the EEPROM is ready 39 | const uint8_t *p = (const uint8_t*)(const void*)&value; 40 | uint16_t i; 41 | for (i = 0; i < sizeof(value); i++) { 42 | if (eeprom_read_byte((uint8_t*)addr) != *p) // Limits number of write/erase cycles 43 | eeprom_write_byte((uint8_t*)addr, *p); 44 | addr++; 45 | p++; 46 | } 47 | return i; 48 | } 49 | 50 | template uint16_t EEPROM_readAnything(uint16_t addr, T& value) { 51 | eeprom_busy_wait(); // Wait until the EEPROM is ready 52 | uint8_t *p = (uint8_t*)(void*)&value; 53 | uint16_t i; 54 | for (i = 0; i < sizeof(value); i++) 55 | *p++ = eeprom_read_byte((uint8_t*)addr++); 56 | return i; 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # You should only have to change these values 2 | PORT = /dev/tty.usbserial-A100P01P 3 | 4 | # You should set the path to your Arduino application if it is not the default one 5 | # The default for Linux and Solaris is: 6 | #ARD_HOME = /opt/Arduino 7 | # The default for Mac is: 8 | #ARD_HOME = /Applications/Arduino.app 9 | 10 | # Leave these alone 11 | BOARD = pro 12 | BOARD_SUB = pro.menu.cpu.16MHzatmega328 13 | MON_SPEED = 115200 14 | 15 | include Arduino_Makefile_master/_Makefile.master -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino Vending Machine 2 | #### Developed by Kristian Lauszus and Sigurd Jervelund Hansen 3 | 4 | The code is released under the GNU General Public License. 5 | _________ 6 | 7 | This is the Arduino code to be used with an old vending machine which had a faulty controller board. 8 | 9 | The code is used with an Arduino Pro Mini. 10 | 11 | The current status of the machine can be seen at its Twitter profile: . 12 | 13 | More information can be found at the blog post: . 14 | 15 | For more information send me an email at . -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | Skal ikke printe "No credit flere gange" 2 | Re-alloker kort ved Bad EEPROM på RFID -------------------------------------------------------------------------------- /gpl2.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | --------------------------------------------------------------------------------