KP16 keypad returns wrong characters

The KP16 keypad column keys 1,4,7,* does not produce a key press, and when 2 is pressed, I receive 1, when 5 is pressed, I receive 4, when 8 is pressed, I receive 7, when 0 is pressed, I receive *. The column 3,6,9,# has no response, and the column A,B,C,D works ok. See the IsKeyPressed(out char key) function.

This problem happened after I ported my code from MF4.2 to MF4.3. When I create a new empty MF 4.3 project, with only a Raptor board and a KP16 keypad, the same code works ok.


using System;
using Microsoft.SPOT;
using System.Threading;
using System.Text;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTME = Gadgeteer.Modules.GHIElectronics;

namespace KeyPadKP16Test
{

    /// <summary>
    /// The keyboard character is stored in the KeyboardEventArgs class
    /// </summary>
    public class KP16EventArgs : EventArgs
    {
        private char cKeyPressed;
        public KP16EventArgs(char _cKey)
        {
            cKeyPressed = _cKey;
        }
        public char KeyChar
        {
            get
            {
                return cKeyPressed;
            }
            set
            {
                cKeyPressed = value;
            }
        }
        public string KeyString
        {
            get
            {
                string rs;
                rs = new string(KeyChar, 1);
                return rs;
            }
        }
        public byte KeyByte
        {
            get
            {
                return (byte)KeyChar;
            }
        }

    }

    /// <summary>
    /// The delegate for the Keyboard event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void KP16EventHandler(object sender, KP16EventArgs e);

    /// <summary>
    /// Constructs a KP16KeyPad class by given parameters
    /// </summary>
    /// <param name="dev">Parent device</param>
    /// <param name="_keypad_KP16">Gadgeteer KP16 keypad object</param>
    /// <param name="Code">Equipment code</param>
    public class KP16KeyPad 
    {
        private const int CHECK_MSEC = 40;  // Read hardware every X msec  
        private const int PRESS_MSEC = 80;  // Stable time before registering pressed  
        private const int RELEASE_MSEC = 80;  // Stable time before registering released  
        private bool DebouncedKeyPress = false;
        private int Count = RELEASE_MSEC / CHECK_MSEC;
        private const int PressCount = PRESS_MSEC / CHECK_MSEC;
        private const int ReleaseCount = RELEASE_MSEC / CHECK_MSEC;
       
        public event KP16EventHandler KeyPressEvent;
        private GTME.KeypadKP16 keypad_KP16;
        private int LoggingLevel;
        private int ScanKeyPadInterval;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="dev"></param>
        /// <param name="_keypad_KP16"></param>
        /// <param name="_systemData"></param>
        /// <param name="Code"></param>
        public KP16KeyPad( GTME.KeypadKP16 _keypad_KP16  )
        {
            Debug.Print("Initializing ");
            keypad_KP16 = _keypad_KP16;
            LoggingLevel = 2;
            ScanKeyPadInterval = CHECK_MSEC;
            ThreadStart KP16ScanStarter = new ThreadStart(KP16ScanLoop);
            Thread KP16ScanThread = new Thread(KP16ScanStarter);
            KP16ScanThread.Start();
            Debug.Print("Done initializing " );
        }

        /// <summary>
        /// Fires the key press event if an event handler method is registered
        /// </summary>
        /// <param name="e"></param>
        private void FireKeyEvent(char key)
        {
            if (KeyPressEvent != null)
            {
                KP16EventArgs e = new KP16EventArgs(key);
                KeyPressEvent(this, e);
            }
            if (LoggingLevel > 1)
            {
                Debug.Print(" KP16 Key = " + key.ToString());
            }

        }

        private bool IsKeyPressed(out char key)
        {
            key = (char)0;
            //This is for testing KP16 keypad
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.A))
            {
                key = 'A';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.B))
            {
                key = 'B';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.C))
            {
                key = 'C';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.D))
            {
                key = 'D';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Pound))
            {
                key = '#';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Star))
            {
                key = '*';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Zero))
            {
                key = '0';
                return true;
            } 
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.One))
            {
                key = '1';
                return true;  
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Two))
            {
                key = '2';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Three))
            {
                key = '3';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Four))
            {
                key = '4';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Five))
            {
                key = '5';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Six))
            {
                key = '6';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Seven))
            {
                key = '7';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Eight))
            {
                key  = '8';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Nine))
            {
                key = '9';
                return true;
            }
            return false;
        }

        /// <summary>
        /// Scan keyboard for key press every CHECK_MSEC time
        /// </summary>
        void KP16ScanLoop()
        {
            while (true)
            {
                try
                {
                    Thread.Sleep(ScanKeyPadInterval);
                    PollKeboard();
                }
                catch (Exception ex1)
                {
                }
            }
        }

        /// <summary>
        /// Poll the keyboard to detect a key press
        /// </summary>
        /// <param name="timer"></param>
        //void KeboardPollTimer_Tick(GT.Timer timer) - if using a GT Timer
        void PollKeboard()
        {
            // Service routine called every CHECK_MSEC to
            // debounce both edges of key switches
            bool RawState;
            char KeyChar;
            RawState = IsKeyPressed(out KeyChar);
            if (RawState == DebouncedKeyPress)
            {
                // Set the timer which will allow a change from the current state.
                if (DebouncedKeyPress)
                    Count = ReleaseCount;
                else
                    Count = PressCount;
            }
            else
            {
                // Key has changed - wait for new state to become stable.
                if (--Count <= 0)
                {
                    // Timer expired - accept the change.
                    DebouncedKeyPress = RawState;
                    // And reset the timer.
                    if (DebouncedKeyPress)
                    {
                        Count = ReleaseCount;
                        FireKeyEvent(KeyChar);
                    }
                    else
                    {
                        Count = PressCount;
                    }
                }
            }
        }
    }
}

I have been using the keypad_KP16 object provided by Gadgeteer Designer. I would like to know why does it not work anymore? The keypad_KP16 object used to work ok. keypad_KP16.IsPressed() function returns the wrong key or false when it should return a key pressed.


 private bool IsKeyPressed(out char key)
        {
            key = (char)0;
            //This is for testing KP16 keypad
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.A))
            {
                key = 'A';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.B))
            {
                key = 'B';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.C))
            {
                key = 'C';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.D))
            {
                key = 'D';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Pound))
            {
                key = '#';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Star))
            {
                key = '*';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Zero))
            {
                key = '0';
                return true;
            } 
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.One))
            {
                key = '1';
                return true;  
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Two))
            {
                key = '2';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Three))
            {
                key = '3';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Four))
            {
                key = '4';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Five))
            {
                key = '5';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Six))
            {
                key = '6';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Seven))
            {
                key = '7';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Eight))
            {
                key  = '8';
                return true;
            }
            if (keypad_KP16.IsPressed(GTM.GHIElectronics.KeypadKP16.Key.Nine))
            {
                key = '9';
                return true;
            }
            return false;
        }


What would you suggest I do?

@ andre.m - I ran your code and it ran fine, every button behaved as it should. Since you said the same code works fine in an empty 4.3 project with on a keypad and a raptor connected, it is possible something else in your code is throwing it off. As before, I would remove parts of your program until it functions again to narrow down the cause.

Do you have a web site where I can get the schematic, the keyboard is discontinued and off your web site

@ dspacek - http://www.ghielectronics.com/downloads/schematic/Keypad_KP16_Module_SCH.pdf, when you search for a product you will also see a discontinued ink, click that link to get to the product.

In the source code for KeypadKP16_43.cs, the problem is, when the bool out1 is false, which is driving Pin3 or A0 on the socket, the socket Pin3 stays at logic 1. A0 is always logic 1 no matter what state out1 is in. But the simple test program works ok, with only the T43 display and the KP16 keypad hooked up in the Designer. Do I have to tear down my project and start all over again?


/// <summary>
		/// Determines whether or not a given key is pressed.
		/// </summary>
		/// <param name="key">The key whose state we want to check.</param>
		/// <returns>Whether or not the key is pressed.</returns>
		public bool IsPressed(Key key)
		{
			bool out1 = false;
			bool out2 = false;


			if (key == Key.One || key == Key.Four || key == Key.Seven || key == Key.Star)
                { out1 = false; out2 = false; }
			else if (key == Key.Two || key == Key.Five || key == Key.Eight || key == Key.Zero)
                { out1 = true; out2 = false; }
			else if (key == Key.Three || key == Key.Six || key == Key.Nine || key == Key.Pound)
                { out1 = false; out2 = true; }
			else if (key == Key.A || key == Key.B || key == Key.C || key == Key.D) 
                { out1 = true; out2 = true; }

			this.out1.Write(out1);
			this.out2.Write(out2);
	
			if (key == Key.One || key == Key.Two || key == Key.Three || key == Key.A) return !this.in1.Read();
			else if (key == Key.Four || key == Key.Five || key == Key.Six || key == Key.B) return !this.in2.Read();
			else if (key == Key.Seven || key == Key.Eight || key == Key.Nine || key == Key.C) return !this.in3.Read();
			else if (key == Key.Star || key == Key.Zero || key == Key.Pound || key == Key.D) return !this.in4.Read();

			return false;
		}

@ dspacek - If the pin is always at logic 1 in your program but is not in a simple test program, something in your code must be causing it to go or stay high. As I suggested, you should remove parts of your code until the problem goes away to narrow down the source.

This is the code that is causing the problem with the KP16 keypad:


bool sdExists = GHI.IO.Storage.SDCard.IsCardPresent;

Put this line of code into your KeypadKP16_Tester project in your source code here:

https://bitbucket.org/ghi_elect/gadgeteer/src/66ea5fbcb1c18bb81e4a77ee4565c844af440844/Modules/GHIElectronicsDiscontinued/KeypadKP16/?at=master

Then keypad scanning will not work correctly. The key column 1,4,7,* and 3,6,8,# are not scanned. When you press keys in the column 2,5,8,0,you see two keys pressed. Likewise when you press a key in column A,B,C,D - two keys are reported pressed.

I am calling the function IsCardPresent because of the instructions for using the SD card reader here:
https://www.ghielectronics.com/docs/51/accessing-folders-and-files

The Gadgeteer Designer SDCard module cannot be used because calling the constructor in program_generated.cs throws an exception.

Now there is no way to use both the SD Card reader module and the KP16 keypad.

@ dspacek - Are you using the same socket for the keypad as the one in the tester? Socket 1?

No, I am using socket 18. I used the Gadgeteer designer to move the KP16 to socket 18. Here is program.generated.cs:


//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.18444
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace KeypadKP16_Tester {
    using Gadgeteer;
    using GTM = Gadgeteer.Modules;
    
    
    public partial class Program : Gadgeteer.Program {
        
        /// <summary>The Display T43 module using sockets 15, 16, 17 and 14 of the mainboard.</summary>
        private Gadgeteer.Modules.GHIElectronics.DisplayT43 displayT43;
        
        /// <summary>The Keypad KP16 module using socket 18 of the mainboard.</summary>
        private Gadgeteer.Modules.GHIElectronics.KeypadKP16 keypadKP16;
        
        /// <summary>This property provides access to the Mainboard API. This is normally not necessary for an end user program.</summary>
        protected new static GHIElectronics.Gadgeteer.FEZRaptor Mainboard {
            get {
                return ((GHIElectronics.Gadgeteer.FEZRaptor)(Gadgeteer.Program.Mainboard));
            }
            set {
                Gadgeteer.Program.Mainboard = value;
            }
        }
        
        /// <summary>This method runs automatically when the device is powered, and calls ProgramStarted.</summary>
        public static void Main() {
            // Important to initialize the Mainboard first
            Program.Mainboard = new GHIElectronics.Gadgeteer.FEZRaptor();
            Program p = new Program();
            p.InitializeModules();
            p.ProgramStarted();
            // Starts Dispatcher
            p.Run();
        }
        
        private void InitializeModules() {
            this.displayT43 = new GTM.GHIElectronics.DisplayT43(15, 16, 17, 14);
            this.keypadKP16 = new GTM.GHIElectronics.KeypadKP16(18);
        }
    }
}


I try again…
I must use socket 18 because of requirements. See the attached .jpg file.

@ dspacek - We were able to find something that looks like it will cause your issue on socket 18. We will let you know more when we find out for sure.

@ dspacek - When using the Gadgeteer SDCard module, what exception is getting thrown?

I tried the test again, and the exception is not created anymore.I was getting an exception when using NETMF and Gadgeteer Package 2014 R2. After I put in NETMF and Gadgeteer Package 2014 R3, there is no exception thrown.

see:
https://www.ghielectronics.com/community/forum/topic?id=16680&page=1#msg165385

This code was creating an exception, but not anymore with R3.


private void InitializeModules() {
             this.sdCard = new GTM.GHIElectronics.SDCard(9);
         }

@ dspacek - If the SDCard module works now, I would use that. The property GHI.IO.Storage.SDCard.IsCardInserted is being removed in the next SDK. If you do not want to use the Gadgeteer driver, you will have to create an input port and poll the SD Detect pin yourself.

Yes I want to use the Gadgeteer.Modules.GHIElectronics.sdCard, but it is non-functional. When I insert a SD card, nothing happens. The IsCardInserted property is always false, the IsCardMounted property is always false, the MountedEventHandler and UnmountedEventHandler never get called. Is this problem going to be fixed?

@ John - Why have you decided to remove GHI.IO.Storage.SDCard.IsCardInserted? :snooty:
It is very handy. Why would anyone prefer to check manual in order to find out which SD Detect pin is over IsCardInserted property?
I would even like to get an event triggered when the state of SD Detect pin is being changed (iirc, G120 does not allow this, but maybe other mainboards do).

@ iamin - that function did nothing but check the state of the pin. You can easily do that without the library.

The purpose of the library is to allow me to be more productive and not spend time figuring out pin numbers from the schematics.