An Introduction to Programming the PinKit Controller

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.

Top Lane Switches

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.

When-Lit Rollovers

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

When-Lit Pop Bumpers

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


* Scoring for switches that change value (i.e. "when lit") is common and may be included in the slave switch handling in future software revisions. This would change the "Top Lane" switch handling to send a message to the switch to set the "when lit" mode. This would remove the need for score handling by the master for the rollover; the slave would handle it.

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

Return to Kerry's PinKit page