Mcp23017 and PoKeys and Linuxcnc

371

3

17

Introduction: Mcp23017 and PoKeys and Linuxcnc

Introduction:

The mcp23017 is really a nice piece of electronics especially when you need extra I/O's and you need a mix of outputs and inputs, now there is a lot of papers to discuss how to connect it to a bunch of microcontrollers ATMEL , PIC, Arduino or even raspberry pi.

but i will discuss here how to connect it to pokeys under linux environment and to be specific under Linuxcnc.

Copyright stuff:

This tutorial won’t be here if this thread wasn't there

https://forum.linuxcnc.org/24-hal-components/29816-pokeys?limitstart=0

I would like to thank "fixer" for his work on this topic.

Step 1: ​Communicating With Mcp23017:

Now the mcp23017 is easy to work with but you must know a couple of things beforehand. a look in the datasheet wouldn't hurt at all, but who will read a dozen of pages to know just three lines:

  1. Set port direction.
  2. Set pull up resistors
  3. Read or write to that port.

Easy huh.

Step 2: ​Some Basic Extra Information:

So as you may noticed connecting the mcp23017 to pokeys doesn't reserve any pins

with this configuration the address of mcp is 0x20 hexadecimal when all address pins a[0..2] are connected to ground, reset pin must be always connected to +5V unless you need reset it externally.

The MCP23017 has two 8 bits ports GPA and GPB these two ports as we said before needs to be initialized first to select IO direction, then if you select some of the IO's to be an input it's a good idea to use the internal programmable pull-up resistors, then you have to send a write command or send a read command to the mcp23017.

the table shows all registers that can be accessed in the mcp23017

whats really important is the
first two columns, the last column is just for knowing the default values as the mcp23017 is powered up or reseted.

Step 3: Setting Up the PoKeys Part:

Now pokeys is really designed to work on windows and the support for Linux is really limited but referring to this page i could have the cross platform library:

https://bitbucket.org/mbosnak/pokeyslib/overview

after downloading the library refer to installation section, in short some libraries must be installed first USBlib-1.0

sudo apt-get install libusb-1.0-0 
sudo apt-get install libusb-1.0-0-dev

then install pokeys library :

sudo make -f Makefile.noqmake install

Step 4: Communicate With Mcp23017 Through PoKeys:

this simple example shows you how

to call the mcp23017 when it's connected to PoKeys.

address of the mcp23017 is 0x20 so pay attention if have other addresses .

the build code will be something like this:

gcc -Wall -o "i2c" "i2c.c" -lPoKeys -lusb-1.0 -std=gnu99

i2c.c

#include<PoKeysLib.h>
#include<unistd.h>/* UNIX standard function definitions */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
intwriteI2C(sPoKeysDevice *PK,uint8_t Reg,uint8_t Value)
{
//read portB
uint8_t status;
usleep(1000);
unsignedchar cmd1[]= {Reg,Value};
PK_I2CWriteStart(PK, 0x20, cmd1, 2);
usleep(1000);
PK_I2CWriteStatusGet(PK, &status);
return status;
}
intreadI2C(sPoKeysDevice *PK,uint8_t Reg,unsignedchar* buffer,uint8_t length)
{
unsignedchar cmd[] = {Reg};
int i;
uint8_t status;
PK_I2CWriteStart(PK, 0x20, cmd, 1);
// Read bytes
PK_I2CReadStart(PK, 0x20, length);
unsignedchar readBytes;
unsignedchar readBuffer[length];
usleep(100000);
PK_I2CReadStatusGet(PK, &status, &readBytes, readBuffer, length);
if (status == PK_I2C_STAT_COMPLETE)
{
for (i = 0; i < length ; i++)
{
buffer[i] = readBuffer[i];
}
}
return status;
}
intsetupI2c(sPoKeysDevice *PK , unsignedchar* device)
{
PK_I2CBusScanStart(PK);
// Wait until the scan is complete...
intwait = 10;
uint8_t status;
unsignedchar devices[128] = {0};
while(wait--)
{
usleep(100000);
PK_I2CBusScanGetResults(PK, &status, devices, 128);
if (status == PK_I2C_STAT_COMPLETE) break;
}
if (status == PK_I2C_STAT_COMPLETE)
{
for (int i = 0; i < 128; i++)
{
if ((i % 16) == 0)
{
printf("\n%02X", i);
}
if (devices[i])
printf("\%02X", i);
else
printf("--");
}
} else
{
printf("\nScan was not completed in time.");
}
return status;
}
intmain()
{
int devNum = PK_EnumerateUSBDevices();
printf("Found %u USB PoKeys devices", devNum);
if (devNum == 0) return0;
printf("\nConnecting to first PoKeys device...");
sPoKeysDevice *dev = PK_ConnectToDevice(0);
if (dev == NULL)
{
printf(" Error!");
return0;
}
printf(" connected");
printf("\nScanning for present I2C devices...");
PK_I2CBusScanStart(dev);
// Wait until the scan is complete...
intwait = 10;
uint8_t status;
unsignedchar devices[128] = {0};
while(wait--)
{
usleep(100000);
PK_I2CBusScanGetResults(dev, &status, devices, 128);
if (status == PK_I2C_STAT_COMPLETE) break;
}
if (status == PK_I2C_STAT_COMPLETE)
{
for (int i = 0; i < 128; i++)
{
if ((i % 16) == 0)
{
printf("\n%02X", i);
}
if (devices[i])
printf("\%02X", i);
else
printf("--" );
}
} else
{
printf("\nScan was not completed in time.");
}
// Check if MCP23017 is present...
if (devices[0x20])
{
printf("\nMCP23017 Port is present \nReading PORT ...");
status = writeI2C(dev,0x00,255);
status |= writeI2C(dev,0x01,255);
status |= writeI2C(dev,0x0C,255);
status |= writeI2C(dev,0x0D,255);
if (status == PK_I2C_STAT_COMPLETE)
{
unsignedchar readBuffer[1];
status = readI2C(dev,0x12,readBuffer,sizeof readBuffer);
if (status == PK_I2C_STAT_COMPLETE)
{
printf(" A=%X", 255-readBuffer[0]);
} else
{
printf("Error reading");
}
unsignedchar readBufferB[1];
status = readI2C(dev,0x13,readBufferB,sizeof readBufferB);
if (status == PK_I2C_STAT_COMPLETE)
{
printf(" B=%X\n", 255-readBufferB[0]);
} else
{
printf("Error reading");
}
} else
{
printf("Error writing");
}
}
PK_DisconnectDevice(dev);
printf("\n");
return0;
}
view rawi2c.c hosted with ❤ by GitHub

Step 5: Linuxcnc Part:

Compile this file use the following command:

sudo halcompile ./pokeys.comp

Then use this following big chunk of code to make the binary

gcc -Wall -Os -g -I. -I/usr/include/libusb-1.0 -I/usr/include -L/usr/lib/ -L/usr/lib/arm-linux-gnueabihf/ -lPoKeys -lusb-1.0 -I/usr/realtime-3.4-9-rtai-686-pae/include -I. -I/usr/realtime-3.4-9-rtai-686-pae/include -I/usr/include/i386-linux-gnu -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -fno-math-errno -funsafe-math-optimizations -fno-rounding-math -fno-signaling-nans -fcx-limited-range -mhard-float -DRTAI=3 -fno-fast-math -mieee-fp -fno-unsafe-math-optimizations -DRTAPI -D_GNU_SOURCE -Drealtime -D__MODULE__ -I/usr/include/linuxcnc -Wframe-larger-than=2560 -URTAPI -U__MODULE__ -DULAPI -Os -o pokeys ./pokeys.c -Wl,-rpath,/lib -L/lib -llinuxcnchal

Then move the binary file to place that linuxcnc can reach:

sudo mv ./pokeys /usr/bin/pokeys

pokeys.comp

component pokeys "PoKeys IO driver, by Mit Zot";
option userspace yes;
pin out bit in-# [55];
pin out bit inext-# [16];
pin out unsigned ain-# [3];
pin out bit err;
pin in unsigned devSerial;
pin out bit alive;
license "GPL";
;;
#include "PoKeysLib.h"
#include /* UNIX standard function definitions */
sPoKeysDevice * dev=0;
int i=0;
bool notsetup=true;
unsigned char readBuffer[1];
int status=0;
int writeI2C(sPoKeysDevice *PK,uint8_t Reg,uint8_t Value)
{
uint8_t status;
usleep(1000);
unsigned char cmd1[]= {Reg,Value};
PK_I2CWriteStart(PK, 0x20, cmd1, 2);
usleep(1000);
PK_I2CWriteStatusGet(PK, &status);
return status;
}
int readI2C(sPoKeysDevice *PK,uint8_t Reg,unsigned char* buffer,uint8_t length)
{
unsigned char cmd[] = {Reg};
int i;
uint8_t status;
PK_I2CWriteStart(PK, 0x20, cmd, 1);
// Read bytes
PK_I2CReadStart(PK, 0x20, length);
unsigned char readBytes;
unsigned char readBuffer[length];
usleep(1000);
PK_I2CReadStatusGet(PK, &status, &readBytes, readBuffer, length);
if (status == PK_I2C_STAT_COMPLETE)
{
for (i = 0; i < length ; i++)
{
buffer[i] = readBuffer[i];
}
}
return status;
}
void user_mainloop(void)
{
while(0xb){
FOR_ALL_INSTS() {
while(dev == NULL)dev = PK_ConnectToDeviceWSerial(devSerial, 2000); //waits for usb device
alive=1;
if ((PK_DigitalIOGet(dev) == PK_OK) && (PK_AnalogIOGet(dev) == PK_OK)){ //gets IO data and checks return value
err=0;
for(i=0;i<54;i++)in(i)=!dev->Pins[i].DigitalValueGet; //just transfers values
for(i=0;i<3;i++)ain(i)=dev->Pins[i+44].AnalogValue;
//if (notsetup)
//{
status = writeI2C(dev,0x00,255);
status |= writeI2C(dev,0x01,255);
status |= writeI2C(dev,0x0C,255);
status |= writeI2C(dev,0x0D,255);
//if (status == PK_I2C_STAT_COMPLETE)
//{notsetup = false;}}
//if (!notsetup)
//{
status = readI2C(dev,0x12,readBuffer,sizeof readBuffer);
for (i=0;i<8;i++)inext(i) = (readBuffer[0]>>i)&0x01;
status = readI2C(dev,0x13,readBuffer,sizeof readBuffer);
for (i=0;i<8;i++)inext(i+8) = (readBuffer[0]>>i)&0x01;
//}
//}
}
else{ //on connection error
PK_DisconnectDevice(dev);
dev=NULL; //tries to reconnect
err=1;
for(i=0;i<54;i++)in(i)=0;
for(i=0;i<3;i++)ain(i)=0;
}
alive=0;
usleep(40000);
}
}
exit(0);
}
view rawpokeys.comp hosted with ❤ by GitHub

Step 6: Finding Pokeys Serial

you can get it through several ways:

-Windows : just open PoKeys program and when you connect to it will show the ID of your PoKeys

-Linux : use the following command to find it

less /proc/bus/input/devices

scroll down till you find somthing related to PoKeys

Note the ID is mention in "U" line use the last five digits.

Step 7: Test the Comp File

To test our setup we will use halrun command, open a terminal window:

linuxcnc@debian:/usr/share/linuxcnc$ halrun
halcmd: loadusr pokeys

//to see all pins note there will be all pokeys pins and external ios from mcp23017

halcmd: show pin

To start your comp

setp pokeys.0.devSerial 12345

replace 12345 with device serial found from previous step.

the next step to make sure it's running and check signals

Step 8: Testing the Comp File

but how can be sure that it's really started, ok we need some kind of scope to see it, don't worry no need to buy fancy stuff it's all there you know it we can use halscope

then back to the halrun command:

linuxcnc@debian:/usr/share/linuxcnc$ halrun

.

halcmd: loadusr pokeys

halcms: setp pokeys.o.devserial 32832

halcmd: loadrt threads name1=fast period1=50000

halcmd: start

halcmd: loadusr halscope

Step 9: Choose How Many Channels

Step 10: Select First Channel to Be Pokeys.0.alaive

Step 11: Then in Run Mode Select Roll and You Will See the Alive Pin Toggling

Step 12: Add Some Extra Channels

add some channels an IO like "pokeys.0.inext-0", and try to toggle it by wiring it to ground ( remember we used the the internal pull-up resistor ) .

Step 13: Final Word

please note the scan rate of this setup not more than10 Hz which is good for buttons lamps or none vital inputs or outputs ( don't use it for emergency or limit switches)

Share

    Recommendations

    • Creative Misuse Contest

      Creative Misuse Contest
    • Tiny Home Contest

      Tiny Home Contest
    • Fix It! Contest

      Fix It! Contest

    17 Discussions

    I got it al set up, but after testing at step 8 I gif the comand setp pokeys.o.devserial <myserial>
    I get <stdin>:3: parameter or pin 'pokes.o.devserial' not found

    What could this be?

    15 more answers

    Are you sure that you have compiled / copied the pokeys file to /usr/bin
    What do you see when you issue command show pin in halrun environment?

    Thank you for your quick reply.

    Yes is there on the right place.

    if i run this.

    linuxcnc@debian:/usr/share/linuxcnc$ halrun
    halcmd: loadusr pokeys
    halcms: setp pokeys.o.devserial <myserial>

    I get after the last comand with my pokeys serial number that the parameter or pin not found.

    Do i have to edit a file?

    After the command
    Loadusr pokeys
    Write the command
    show pin
    If no pins are available from pokeys
    Try
    Show comp
    To see if it really loaded
    I think there is a typo
    It should be
    Pokeys.0.devserial
    It’s zero not the letter o

    Even with the Zero '0' getting the same result.
    I did the show comp command.
    at the end of the line is see PID and State.
    Under the PID some numbers and under State both say ready.

    When i do show pin, all value are FALSE. exept for the first 3 and the 5th are 0x00000000

    If i do halshow, i get some errors.
    There is no such file like RTAPI?

    'rtapi' kernel module must be loaded.

    Sorry, but can you post an image for show comp command

    Here is the image of the last one with halshow.

    And a 2nd one with the halscope. Above, 'Realtime function not linked'

    20180714_202845.jpg20180714_204353.jpg

    In regards to halshow command Linuxcnc must be running in the background OR halrun in other terminal to be able to run it.

    and the setp command it's cas sensetive

    setp pokeys.0.devSerial 12345

    pay attention to devSerial not devserial

    Getting there i think. But not done yet.
    Excuse for my lack of linux experience.

    15316108348241971567292027506184.jpg

    Check your typing in command it’s name=fast not nam=fast

    Thank you. Had to much these mis takes.

    So...i was in hal scope...nothing.
    Eventhough i had the right serial number.


    After halscope was started, terminal says:


    halcmd: halscope: Could not find thread: 'THREAD (null)
    halscope: could not set horizontal multiplier: 'HMULT 1'
    halscope: config file 'auosave.halscope' caused 2 warnings

    Hi,
    Did you started the treads, i.e in halrun environment commad “start”
    As for the message in terminal it’s just a warning.
    You should select in hal show the fast thread in the beginning referring to step 9.

    Hi,

    Yes i did everything from the instructions.
    I will search in halscope if i get any signal throug.

    Looks everything fine, just continue per steps

    Still got nothing. In set up via pokeys and poblocks the outputs 1 to 6 for buttons and 45 to 47 for potentiometers.
    All works fine there.

    On hal scope nothing.
    I folowed allot from linuxcnc forum. I read here something about i2c.c ?
    Do i need this to install two?

    Great first instructable. Very well written and well explained.