This article provides an introduction to programming the PinKit controller. A knowledge of the C programming language is assumed, but you should be able to follow the descriptions without fully understanding the language. The basic handling of incoming and outgoing messages is handled by the PinKit code template that is common to all pinball machines. The unique code as shown here specifies the actions to take when a playfield switch is activated. The following list has a few definitions to help understand the code.
! = logical "NOT" in C. Used to test when a condition is not true. 0x indicates a hexadecimal value in C. Braces {} are used in C to group code statements into a block send_pincan_msg, enqueue, and add_score are functions provided by the PinKit programming library. send_pincan_msg : sends a message onto the PinCAN bus immediately. enqueue : puts a message onto the outgoing queue that will be sent at the next available opportunity. Since the message transmit rate is limited by the microcontroller hardware, it is recommended to queue up messages if there is more than one to send. add_score : updates the local copy of the player's score.
The bulk of the code needed to run the PinKit controller is a template that is reused for each machine programmmed. The basic handling for the machine is in the form of a "machine state sequencer" that handles game start, end, new ball, etc. All that is needed is to customize some sections to handle lamps and switches that are unique to your machine. An example of this customization is discussed here.
The PinKit system uses a set of distributed controllers connected by a high-speed serial CAN bus. The hardware for the controllers is the same, but one controller is designated as the master and has unique software. The other controllers are designated as slaves. All that is needed for slave programming is some minor configuration statements. A slave controller can automatically fire a coil and add a score in response to a switch closure. If additional events (PinCan messages) are required they are handled by the master controller. Scoring that changes based on the game state (i.e. "when lit") are handled by the master controller*. It is this master programming that is described here.
In the machine used in this example, there are four top lanes that light corresponding playfield elements (pop bumpers and rollovers). Code is shown for one lane, one rollover, and one pop bumper. Code for the other similar elements is created by copy/paste and customizing for switch and lamp numbers.
The following three sections show the code implemented to handle three types of playfield switches; top lane switches, rollover switches, and pop bumpers. The section of C code from the controller program is shown, followed by the line-by-line description of the function.
The top lane switches score 10 points and light a set of playfield elements associated with the specific lane. Scoring for the lane is handled by the slave. The master checks to see if this is the first time the switch has been hit as tracked by the seq_flag variable. If it has, no actions are needed. If it has not, the playfield lamps associated with this lane are turned on.
001 /* Playfield switches - Check only during PLAY_BALL */ 002 if (machine_state == MS_PLAY_BALL) { 003 switch(data[1]) { 004 /*****************************************/ 005 /** TOP LANES **/ 006 /*****************************************/ 007 case 0x50: /* Top Lane #1, Red */ 008 if (!seq_flag[1]) { 009 seq_flag[1] = 1; 010 enqueue(MSG_LAMP_ON, 0x71); 011 enqueue(MSG_LAMP_ON, 0x61); 012 enqueue(MSG_LAMP_ON, 0xBF); 013 } 014 break;
Line 001 - This is just a comment, denoted by "/*" and "*/" Line 002 - As the comment says, the switches in this section are only checked when the machine is in the "PLAY_BALL" state, meaning that a ball is active on the playfield. Line 003 - The "switch" statement is a multi-way branch based on the switch number (data[1]) that is received from the PinCAN bus switch messages. The destination of the branches are the upcoming "case" statements. If the received switch number matches a case statement, that section of code will execute. Otherwise, the switch message is ignored. Line 007 - Execution will continue here if the message was for switch 0x50. 0x denotes hexadecimal, a convenient form for bytes of message data. The Windows calculator can convert hexadecimal values if needed. Line 008 - The seq_flag variable tracks events that have occured. In this case, seq_flag[1] indicates that the lane has been scored and playfield elements that score "10 points when lit" should now score 10 points. This line checks to see if this is the first time hit. If so, the code in the "if" section is exectuted. Line 009 - set flag to indicate that lane has been scored at least once. Line 010 - Put a message to turn on lamp 0x71 on the outgoing message queue. This, along with the following two statements, turns on the playfield elements associated with top lane #1. Line 011 - Put a message to turn on lamp 0x61 on the outgoing message queue. Line 012 - Put a message to turn on lamp 0xBF on the outgoing message queue. Line 013 - Closing bracket for the if statement Line 014 - Conclusion of the case section. Each section should be ended with a "break" or the code will continue to execute into the next (unrelated) case section.
This section controls the rollovers that score 1 point, or 10 points when lit. The "when lit" state is indicated by the seq_flag variable that was set in the previous section.
015 /*****************************************/ 016 /** WHEN-LIT ROLLOVERS **/ 017 /*****************************************/ 018 case 0x56: /* Rollover #1, Top Right, Red */ 019 case 0x67: /* Rollover #5, Bottom Left, Red */ 020 if (!seq_flag[1]) { 021 send_pincan_msg(MSG_SCORE_ADD, SCORE_1); 022 add_score(player, SCORE_1); 023 ball_save = 0; 024 } else { 025 send_pincan_msg(MSG_SCORE_ADD, SCORE_10); 026 add_score(player, SCORE_10); 027 } 028 break;
Line 018 - This section of code describes the behavior for switch 0x56 ... Line 019 - ... and switch 0x67. Since both switches have the same behavior they can share a control section. Line 020 - Test the flag to see if this rollover is lit. !seq_flag[1] will be true when the flag is NOT set. Line 021 - Send a PinCAN message to add 1 point to the current player's score Line 022 - Since the master (this controller) needs to know the score, and because it does not receive it's own messages, add 1 point to the local copy of the score Line 023 - Since a point has been scored, turn off ball save. Line 024 - The ELSE leg defines the behavor for when the flag IS lit Line 025 - Send a PinCAN message to add 10 points to the current player's score. Line 026 - Add 10 points to the local copy of the current player's score Line 027 - end of the IF statement Line 028 - end of the handling of these switches
This section controls the scoring and lamp flash for the pop bumpers. Scoring is 1 point or 10 points when lit. The "when lit" state is indicated by the seq_flag variable that was set in the previous section. Note that this code sequence is nearly identical to the roll-over handling in the previous section. It is very typical to have identical or similar sections of code like this.
029 /*****************************************/ 030 /** WHEN-LIT POP BUMPERS **/ 031 /*****************************************/ 032 case 0x71: /* Pop Bumber #1, Top Left, Red */ 033 if (!seq_flag[1]) { 034 send_pincan_msg(MSG_SCORE_ADD, SCORE_1); 035 add_score(player, SCORE_1); 036 ball_save = 0; 037 } else { 038 send_pincan_msg(MSG_SCORE_ADD, SCORE_10); 039 add_score(player, SCORE_10); 040 enqueue(MSG_LAMP_FLASH, 0x71); 041 } 042 break; ... 044 } 045 }
Line 032 - This section is for pop bumber at switch 0x71 Line 033 - Test the flag to see if this rollover is lit. !seq_flag[1] will be true when the flag is NOT set. Line 034 - Send a PinCAN message to add 1 point to the current player's score Line 035 - Since the master (this controller) needs to know the score, and because it does not receive it's own messages, add 1 point to the local copy of the score Line 036 - Since a point has been scored, turn off ball save. Line 037 - The ELSE leg defines the behavor for when the flag IS lit Line 038 - Send a PinCAN message to add 10 points to the current player's score Line 039 - Add 10 points to the local copy of the current player's score Line 040 - Put a message to flash the pop bumber lamp in the outgoing queue. Note that 0x71 is the LAMP number and is not related to switch 0x71. The numbers are determined by the connections to the controller, and for convenience these were assigned so they would be the same number; it is not a requirement. Line 041 - end of the IF statement Line 042 - end of the handling of switch 0x71 switches Line 044 - end of the switch statement for playfield switches. The "..." above indicates that similar handling for other playfield switches is not shown. Line 045 - end of the "(machine_state == MS_PLAY_BALL)" IF statement
If you have any comments or questions, feel free to contact me.
Kerry Imming: kcimming@pobox.com
File: PinKit_programming.html; Last modified: September 10, 2011