Skip to content

Instantly share code, notes, and snippets.

@Bouni
Last active May 31, 2019 01:53
Show Gist options
  • Save Bouni/10188466 to your computer and use it in GitHub Desktop.
Save Bouni/10188466 to your computer and use it in GitHub Desktop.
Some tests for an MDB project
// global state variable
unsigned int coinstate;
// structure for storing the coin changer info
struct COIN_INFO {
byte feature_level;
unsigned int country_code;
byte scaling_factor;
byte decimal_places;
byte type_routing;
byte type_credit[16];
};
// struct for storing the tube status
struct TUBE_STATUS {
unsigned int full_status;
byte status[16];
};
COIN_INFO coin_info =
{0,0,0,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
TUBE_STATUS tube_status =
{0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
void setup() {
Serial.begin(9600);
Serial1.begin(9600, true);
coinstate = 0;
Serial.println("VMC stated.");
}
void loop() {
// run the statemachine
statemachine();
// everytime we wait we can do funny stuff here in the meantime :-)
// but make sure it takes not longer then 100ms, because we have to poll the
// coin acceptor every 100ms at least!
do_funny_stuff();
}
unsigned int calculate_checksum(unsigned int data[], unsigned int n) {
unsigned int checksum = 0x000;
// sum up all bytes except the last one (thats the checksum!)
for(int i=0; i < n-1; i++) {
checksum += data[i];
}
// cut off the bits higher than 8 using a simple binary &
checksum &= 0x0FF;
// check the calculation against the checksum
return checksum;
}
bool validate_checksum(unsigned int data[], unsigned int n) {
unsigned int checksum = calculate_checksum(data, n);
// check the calculation against the checksum
return (checksum == data[n-1]);
}
void do_funny_stuff() {
}
// A simple statemachine for the coinchanger
void statemachine() {
unsigned int reply[40];
unsigned int data[40];
// This will print the coinstate and then wait 10ms to avoid the buffers to be flooded
Serial.print("State ");
Serial.println(coinstate, DEC);
delay(10);
switch(coinstate) {
// We have just powered up
case 0:
Serial1.write9bit(0x108);
Serial1.write9bit(0x008);
coinstate++;
break;
// we have already sent the reset command
case 1:
// wait for an answer
if(Serial1.available() < 1) {
return;
}
if(Serial1.read() == 0x100) {
// ACK received go ahead in the statemachine
coinstate++;
} else {
// No ACK received, start again from the beginning
coinstate = 0;
}
break;
// Poll the coin changer
case 2:
Serial1.write9bit(0x10B);
Serial1.write9bit(0x00B);
coinstate++;
break;
// Wait for the JUST RESET to be received
case 3:
// Check if the received byte is just an ACK
// read and discard it if so
if(Serial1.peek() == 0x100) {
Serial1.read();
return;
}
// If the first byte is no ACK, wait for 2 bytes to be received
if(Serial1.available() < 2) {
return;
}
// read the 2 bytes and verify it is the JUST RESET
for(int i = 0; i < 2; i++) {
reply[i] = Serial1.read();
}
if(reply[0] == 0x000 && reply[1] == 0x100) {
// JUST RESET received, send ACk and go ahead
Serial1.write9bit(0x100);
coinstate++;
} else {
// something else received send RET
Serial1.write9bit(0x0AA);
}
break;
// send the SETUP command
case 4:
Serial1.write9bit(0x109);
Serial1.write9bit(0x009);
coinstate++;
break;
// wait for the answer
case 5:
// Wait for 24 bytes to be received
if(Serial1.available() < 24) {
return;
}
// read the replied 23 databytes + checksum
for(int i = 0; i < 24; i++){
reply[i] = Serial1.read();
}
// validate the checksum
if(validate_checksum(reply, 24)) {
// checksum oki send ACK, go ahead
Serial1.write9bit(0x100);
coinstate++;
} else {
// checksum incorrect, send RET
Serial1.write9bit(0x0AA);
}
break;
// store device info
case 6:
coin_info.feature_level = reply[0];
coin_info.country_code = (reply[1] << 8 | reply[2]);
coin_info.scaling_factor = reply[3];
coin_info.decimal_places = reply[4];
coin_info.type_routing = (reply[5] << 8 | reply[6]);
for(int i = 0; i < 16; i++) {
coin_info.type_credit[i] = reply[i+7];
}
coinstate++;
break;
// send TUBE STATUS command
case 7:
Serial1.write9bit(0x10A);
Serial1.write9bit(0x00A);
coinstate++;
break;
// wait for the answer
case 8:
// Wait for 19 bytes to be received
if(Serial1.available() < 19) {
return;
}
// read the replied 18 databytes + checksum
for(int i = 0; i < 19; i++){
reply[i] = Serial1.read();
}
// validate the checksum
if(validate_checksum(reply, 19)) {
// checksum ok send ACK, go ahead
Serial1.write9bit(0x100);
coinstate++;
} else {
// checksum incorrect, send RET
Serial1.write9bit(0x0AA);
}
break;
// store tube status info
case 9:
tube_status.full_status = (reply[0] << 8 | reply[1]);
for(int i=0; i < 19; i++) {
tube_status.status[i] = reply[i+2];
}
coinstate++;
break;
// send coin type command
case 10:
// prepare the data for the coin type command
data[0] = 0x10C;
data[1] = 0x00C;
data[2] = 0x0FF;
data[3] = 0x0FF;
data[4] = 0x0FF;
data[5] = 0x0FF;
// calculate the necessary checksum
data[6] = calculate_checksum(data, 6);
for(int i = 0; i < 7; i++) {
Serial1.write9bit(data[i]);
}
coinstate++;
break;
// wait for the ACK of the coin changer
case 11:
reply[0] = Serial1.read();
if(reply[0] == 0x100) {
// ACK received go ahead
coinstate++;
} else {
// No ACK received, send the coin type command again
Serial.print("Error: expected ACK, received instead: ");
Serial.println(reply[0], HEX);
coinstate--;
}
break;
// Now we should be able to dispense a coin
case 12:
Serial.println("boot proccess completed :-)");
coinstate++;
break;
// Lets poll the coin changer no forever
case 13:
Serial1.write9bit(0x10B);
Serial1.write9bit(0x00B);
coinstate++;
break;
case 14:
// Check if the received byte is just an ACK
// read and discard it if so
if(Serial1.peek() == 0x100) {
Serial1.read();
return;
}
// If the first byte is no ACK, wait for 16 bytes to be received
if(Serial1.available() < 16) {
return;
}
for(int i = 0; i < 17; i++) {
reply[i] = Serial1.read();
}
// validate the checksum
if(validate_checksum(reply, 17)) {
// checksum ok send ACK, go ahead
Serial1.write9bit(0x100);
coinstate++;
} else {
// checksum incorrect, send RET
Serial1.write9bit(0x0AA);
}
break;
// Lets write the reply to the console for testing purposes
case 15:
for(int i = 0; i < 17; i++) {
Serial.print("Byte ");
Serial.print(i, DEC);
Serial.print(" = ");
Serial.println(reply[i]);
}
delay(50);
coinstate = 13;
break;
}
}
*Power Up*
VMC: Reset 0x108 0x008
Coin: Ack 0x100
// Now poll as long as you get the Just Reset
// That can happen emdiately after the Reset, but also
// can take a few seconds with ACK answers in between
VMC: Poll 0x10B 0x00B
Coin: Ack 0x100
VMC: Poll 0x10B 0x00B
Coin: Ack 0x100
VMC: Poll 0x10B 0x00B
Coin: Ack 0x100
VMC: Poll 0x10B 0x00B
Coin: Just Reset 0x000 0x100
// Now get the Coin Mech setup information. See MDB Spec page 64 for details
// Store this infomation in a struct
VMC: Setup 0x109 0x009
Coin: Answer [1 Byte] // Feature level
[2 Bytes] // Country / Currency Code
[1 Byte] // Coin Scaling factor
[1 Byte] // Decimal Places
[2 Bytes] // Coin Routing
[16 Bytes] // Coin Type Credit
[1 Byte] // Checksum
VMC: Ack 0x100
VMC: Tube Status 010A 0x00A
Coin: Answer [2 Bytes] // Tube Full Status
[16 Bytes] // Tube Status
[1 Byte] // Checksum
VMC: Ack 0x100
// Whenever you idle around, you need to send polls to the device to make sure they are alive
// But expect every time you poll that you get up to 16 Bytes of Status Data
VMC: Poll 0x10B 0x00B
Coin: Ack 0x100
// Now enable the coin types for dispense (only need to be done once)
VMC: Coin Type 0x10C 0x00C
0x0FF 0x0FF // activate all coin types
0x0FF 0x0FF // activate all coin types for maual dispense
[1 Byte] // Checksum
Coin: Ack
// Now dispense a coin
// From here you can cycle and dispense as much coins as you wnat
VMC: Dispense 0x10D 0x00D
0x011 // 0b00110001 -> Bit 0 means Coin type 1, bit 4 and 5 means dispnse 3 coins
[1 Byte] // Checksum
Coin: Ack 0x100
// If you poll after the Ack of the coin mech, you get the status of the dispense.
// connect Serial1 Rx to Serial2 Tx
// connect Serial2 Rx to Serial1 Tx
void setup()
{
Serial.begin(9600);
Serial1.begin(9600,true);
Serial2.begin(9600,true);
Serial.println("Setup completed");
}
void loop()
{
unsigned int tmp;
Serial.println("sending 0x000 on Serial1");
Serial1.write9bit(0x000);
Serial.println("wait for arrival on Serial2");
while(!Serial2.available()) {
delay(1);
}
tmp = Serial2.read();
Serial.println("received on Serial2:");
Serial.println(tmp, HEX);
Serial.println("sending 0x100 on Serial1");
Serial1.write9bit(0x100);
Serial.println("wait for arrival on Serial2");
while(!Serial2.available()) {
delay(1);
}
tmp = Serial2.read();
Serial.println("received on Serial2:");
Serial.println(tmp, HEX);
Serial.println("sending 0x000 on Serial2");
Serial2.write9bit(0x000);
Serial.println("wait for arrival on Serial1");
while(!Serial1.available()) {
delay(1);
}
tmp = Serial1.read();
Serial.println("received on Serial1:");
Serial.println(tmp, HEX);
Serial.println("sending 0x100 on Serial2");
Serial2.write9bit(0x100);
Serial.println("wait for arrival on Serial1");
while(!Serial1.available()) {
delay(1);
}
tmp = Serial1.read();
Serial.println("received on Serial1:");
Serial.println(tmp, HEX);
}
@ocangash
Copy link

Hi Bouni, just to ask for what kind of device is this code for? is for Arduino?
Thanks

Omar Cangas

@ocangash
Copy link

I'm trying to make a source code for VMC to communicate with the Coin Changers devices and also cashless devices.
So far I'm using the Flowcode Software, this can be used to program Pics and Arduinos with atmel chips.

So if you want some information please let me know.

Thanks

Omar Cangas

@lvlisael
Copy link

lvlisael commented Nov 6, 2018

what is your definition for function Serial1.write9bit? it does not exist when i compile it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment