Motor Control Panel for DIY Milling Machine

Hi all. I have been collecting parts to build a milling machine and I am finally to the point of beginning assembly. I have started the control panel which will include an Arduino (at least an Uno, maybe a Mega), a Matrix keypad connected via a Spartfun I/O expander (so it is 2 wire back to Arduino), 2 x 4 digit, 7 seg displays (also I2C) and switches for power and direction selection. The Arduino will manage the motor driver and PID.
I am in desperate need of some help with my code for the keypad. Despite my efforts to find examples of keypad code I am struggling to get it to work as I would like. I need some one-on-one time with someone to walk me through this.
Is anyone willing to help me? I can come to you or we can meet at Core Electronics.

Hi Adrian,

Does the keypad work when connected via the instructions on the keypad product page, without the i/o expander?
https://core-electronics.com.au/membrane-3x4-matrix-keypad-3x4.html

I’m assuming this is the keypad you are using…

Hi Stephen, thanks for replying. Yes I have the keypad and LED display working. It will run a test sketch and I can enter a single digit at a time. I am having problems figuring out how to capture a 4 digit number. I have not been able to find any good examples.
I am using this keypad: https://core-electronics.com.au/4x4-matrix-keypad.html. With a https://core-electronics.com.au/404search_ps1?q=i/o+expander.
I’ll tidy up the sketch and upload it for you.

Hi Adrian,

Give something like this a try:

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

//define the symbols
char stdKeys[ROWS][COLS] = {
 { '1' , '2' , '3' , 'A' },
 { '4' , '5' , '6' , 'B' },
 { '7', ' 8', ' 9', ' C' },
 { '#' , '0' , '*' , 'D'}
};
byte colPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte rowPins[COLS] = {6, 7, 8, 9}; //connect to the column pinouts of the keypad
int myInt = 0;
byte digitCount = 0;

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(stdKeys), rowPins, colPins, ROWS, COLS); 

void setup(){
 Serial.begin(115200);
 Serial.println("Enter a 4 digit number");
}

void loop(){
 char Key = customKeypad.getKey();

 if (Key >= '0' && Key <= '9'){
   Serial.println(Key);
     myInt = (myInt * 10) + Key -'0';
     digitCount++;
   if (digitCount == 4) {
     Serial.print("You entered: ");
     Serial.println(myInt);
     digitCount = 0;
     myInt = 0;
   }
 }
}

Hi Stephen. Thank you for this sketch, I’ll have a play with it.
I’ve attached the sketch as I have it at the moment. You will notice the I/O expander means the key mapping is slightly different and I need to use a different keypad library. You will also get to see all the libraries I am using. I really want to learn this stuff rather than just get a solution. any chance we could catch up?

See attached Zip: controlPanel.zip (74.7 KB)

Hi Stephen. I have played with the sketch you sent me. I have modified it to work with the I/O expander library and almost have it working correctly (see attached). Only having issues with the keypad debouncing. I am getting multiple inputs from each key press.
Can you explain why this line is needed? “myInt = (myInt * 10) + key - ‘0’;”. I am guessing it has to do with converting the ascii or hex number passed from the keypad.
keypad_Stephen.zip (1.3 KB)

Hi Adrian,

That is what ‘adds’ the keypress number to the number that has been pressed.
If you press 1 as the first number and two as the second.
This will take that 1, multiply by 10 = 10
then add the second key press “2” = 12
So now you have a value that represents the two buttons pressed.
the - ‘0’ is removing a zero, but I’m not how this is relevant. Perhaps a stray 0 is read with the button press from the matrix.

You can debounce buttons the proper way:
https://www.arduino.cc/en/tutorial/debounce

Or you can do the shortcut way where when a button press is detected, you delay 50ms or so then take the reading from that button. It works most of the time :slight_smile:

Hi Stephen. You have been very helpful and definitely filled in the gaps. In the future I am going to use the [https://www.sparkfun.com/products/15290](QWIIC Keypad) by Sparkfun.
I’ll let you know how I go with this.
Cheers Adrian

1 Like

Hi Stephen, sorry its been a while and I haven’t given you any feedback. The number entry works well and I also have it showing on the display correctly. Also sorted the debouncing. Now this part is working I’ll tidy up the sketch by make creating some functions.
I can move on to the next part of this sketch and add some interrupts. I also want to be able to store numbers using the alpha keys. Is it possible to do this by holding a key (eg ‘A’) for a set period and have the current display be stored as a preset to that key? I am not even sure what to call this action to search. Any direction?
Looking forward to catching up with you soon.
Cheers
Adrian

Working code to-date:
/*Control Panel for DC Motor Control
Date:14 May 2019
Adrian Rawlings
Version: Current
Project notes:

  • Keypad and 4 dig, 7 seg display (Status: working)
  • targetRPM display test function (Status: working)
    • and # functions identified (Status: in progress)
  • switch case structure setup (Status: in progress)
    */

#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library
#include <Arduino.h>
#include <TM1637Display.h>

//----Keypad----
// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):
const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io; // Create an SX1509 object to be used throughout

#define KEY_ROWS 4 // Number of rows in the keypad matrix
#define KEY_COLS 4 // Number of columns in the keypad matrix

// keyMap maps row/column combinations to characters:
char keyMap[KEY_ROWS][KEY_COLS] = {
{ ‘1’, ‘2’, ‘3’, ‘A’},
{ ‘4’, ‘5’, ‘6’, ‘B’},
{ ‘7’, ‘8’, ‘9’, ‘C’},
{ ‘*’, ‘0’, ‘#’, ‘D’}
};
//----Keypad End----

//----4,7 Display----
// Module connection pins (Digital Pins)
#define CLK 4
#define DIO 3
TM1637Display display(CLK, DIO);
uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 };
//4,7 Display End

void setup()
{
Serial.begin(9600);
Serial.println(“Display Test Commencing, Please Wait!”);
if (!io.begin(SX1509_ADDRESS))
{
Serial.println(“Failed to communicate.”);
while (1) ; // If we fail to communicate, loop forever.
}

//----Keypad----
unsigned int sleepTime = 0;// Sleep time range: 128 ms - 8192 ms (powers of 2) 0=OFF
byte scanTime = 32; // Scan time range: 1-128 ms, powers of 2
byte debounceTime = 16; // Debounce time range: 0.5 - 64 ms (powers of 2)
// Note: Scan time must be greater than debounce time!
io.keypad(KEY_ROWS, KEY_COLS, sleepTime, scanTime, debounceTime);

//----TargetRPM display test----
for (int test = 0; test < 1; test++) {

define TEST_DELAY 1000

display.setBrightness(0x0f);
uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 };
delay(TEST_DELAY);

display.setSegments(data);
display.showNumberDec(88, true, 4, 0);
delay(TEST_DELAY);

display.setSegments(data);
display.showNumberDec(888, true, 4, 0);
delay(TEST_DELAY);

display.setSegments(data);
display.showNumberDec(8888, true, 4, 0);
delay(TEST_DELAY);

display.setSegments(data);
for (int i = 0; i <= 99; i++)
{
  display.showNumberDec(i, true);
}

}
delay(10);
//----TargetRPM display test END----

//----Confirm targetRPM display readiness----
for (int confirm_Test = 0; confirm_Test < 3; confirm_Test++) {
display.clear();
display.setSegments(data);
display.showNumberDec(0, true);
delay(500);
}
//----Confirm targetRPM display readiness END----

//----Set targetRPM display to zero----
display.clear();
display.setSegments(data);
display.showNumberDec(0, true);

Serial.println(“Test Complete \n”);
Serial.println(“Enter a 4 digit number”);
}

void loop() {
//----Variables----
int myInt = 0;
byte digitCount = 0;
int target RPM;
//----Variables END----

for (byte digitCount = 0; digitCount <= 4;) {
// display.clear();

unsigned int keyData = io.readKeypad();
if (keyData != 0) // If a key was pressed:
{
  byte row = io.getRow(keyData);
  byte col = io.getCol(keyData);
  char key = keyMap[row][col];


  if (key >= '0' && key <= '9') {
    Serial.println(key);
    myInt = (myInt * 10) + key - '0';
    display.setSegments(data);
    display.showNumberDec(myInt, true, 4, 0);
    delay(100);
    digitCount++;
    
    if (digitCount == 4) {

      Serial.print("You entered: ");
      Serial.println(myInt);

      display.setSegments(data);
      display.showNumberDec(myInt, true, 4, 0);
      
      targetRPM = myInt;
      return targetRPM;
      
      digitCount = 0;
      myInt = 0;
    }
  }
}

}
}

// if (myInt == ‘*’) {
// display.clear();
// display.setSegments(data);
// display.showNumberDec(0, true);
//clear display - display 0000
//Input target RPM displaying digits as they are entered

// }
/*
if (key == ‘#’) {
//Target RPM entered, Press # to accept;
//Flash display to confirm input
//Pass value to PID variable targetRPM
}

Switch(set_Preset) {
case presetA:
//If A is held for 3 sec store current targetRPm as preset#A
break;
case presetB:
//If A is held for 3 sec store current targetRPm as preset#A
break;
case presetC:
//If A is held for 3 sec store current targetRPm as preset#A
break;
case presetD:
//If A is held for 3 sec store current targetRPm as preset#A
break;
}

Switch(use_Preset) {
case A:
//If A pressed use stored preset targetRPm
//display preset value
break;
case B:
//If B pressed use stored preset targetRPm
//display preset value
break;
case C:
//If C pressed use stored preset targetRPm
//display preset value
break;
case D:
//If D pressed use stored preset targetRPm
//display preset value
break;
}

*/
//}
//----End sketch----

You should be able to accomplish that by using some code similar to the debounce code. but rather than ignoring the first 20 milliseconds you ignore the first two seconds to read the press.

Give that a shot!

Adrian,
To answer the question you asked earlier

Can you explain why this line is needed? “myInt = (myInt * 10) + key - ‘0’;”. I am guessing it has to do with converting the ascii or hex number passed from the keypad.

Correct. Look at the array definition for the keypad, it’s char, not int. Needs to be like this to accommodate ABCD etc. The character ‘0’ is hex 30 decimal 48. Not the integer 0. Fortunately, all the digits are consecutive, from hex 30 to hex 39. So if we take the char, and subtract ‘0’, we are in fact subtracting hex 30 decimal 48, which gives the correct integer value.

2 Likes