MCP23017 - Serial Port Read - Configuration

Hi everyone,

i will try to explain my project the best as i can ^^.

It consist on the control of a MCP23017, based on some characters readed from Serial Port. For example i send from an external software source some characters like "23A7#" <--- This being ASCII characters.

The two first digits can be from 1 to 32, the third character can be 'A' or 'B', the fourth digit can be from 1 to 8, and the # is just to tell to my self the transmission ends.

I got 8 MCP23017 connected to 32 (8:1) Multiplexer which the have 4 selectors pins including the enable pin.

So with some characters i need to be able to control these selector pins. The connections are the next:

  • Bank A Pins - Connected to Two multiplexers, (En, A0, A1, A2) in this order.
  • Bank B Pins - Connected to Two multiplexers in the same order.

So each MCP23017 controls 4 Multiplexer, and each pin bank controls 2 mux. On bank 'A', high nible bits control Mux nº2, low nible bits control mux nº1. On bank 'B', high nible bits control mux nº4, low nible bits control mux nº3. This for each MCP23017 and multiplexers.

What i pretend to do with this operations is:

For the example "23A7#":

23 - Determines the number of the multiplexer, from there i should extract the address of the MCP23017 which controls it. And also i have to determine if the multiplexer is controlled by the high nible of a bank or the low nible.

'A' or maybe 'B' - Determines the bank, 0x12 or 0x13.

7 - Determines the bits i want to send to (A0, A1, A2) pins. (7 => 110) so send it displaced one position for low nible, and five for high nible.

--- Also i share my actual code, which is horrible and im trying to figure to do that and progress on that. If someone can share me a hand would be nice.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin();

!!!!! All pins are configured as outputs but don't want to extend the code here more!!
}
void sendconfig(int muxnum, int banknum, int combnum)
{

byte combination[8] = {B000, B001, B010, B011, B100, B101, B110, B111};
byte address[8] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
byte bank[2] = {0x12, 0x13};
byte bankdata[16] = {bank1a, bank1b, bank2a, bank2b, bank3a, bank3b, bank4a, bank4b, bank5a, bank5b, bank6a, bank6b, bank7a, bank7b, bank8a, bank8b};
byte pinsx;

  float addnumf = (muxnum / 4) + 0.75;
  int addnum = round(addnumf) - 1;

  int combnumx = combnum - 1;
  
  float bdataf = (muxnum / 2);
  int bdatax = round(bdataf);
  int bdataevencheck = muxnum % 2;

  if(bdataevencheck == 0){

  pinsx = (bankdata[bdatax] | (combination[combnumx] << 5));
  bankdata[bdatax] = pinsx;   
  } else{
  pinsx = (bankdata[bdatax] | (combination[combnumx] << 1));
  bankdata[bdatax] = pinsx;    
  }

  byte addressx = address[addnum];
  byte bankx = bank[banknum];

  
  Wire.beginTransmission(addressx);
  Wire.write(bankx);
  Wire.write(pinsx);
  Wire.endTransmission();  
}


void loop() {
int mnum, bnum, cnum;
  // put your main code here, to run repeatedly:
    while(Serial.available()>0)
      {
          c = Serial.read();
          SerialData += c;  
                     
          if(c == 'A' || c == 'B'){
           mnum = SerialData.toInt();
           if(c == 'A'){
            bnum = 0;      
           } else {
            bnum = 1;
           }
           c = 0;
           SerialData = "";
          }

             if(c=='#'){
              cnum = SerialData.toInt();
              sendconfig(mnum, bnum, cnum);
              c = 0;
              SerialData = "";
             }

                  
      }

      }

Thanks everyone for reading and i hope i can get some ideas or help ^^.

you may be interested in the following which i use in one form or another in almost all my code to aid with development, testing and operation

it reads single character commands that may be preceded by a numeric value, similar to what you're suggesting

// pcRead - debugging using serial monitor

const char version [] = "PcRead 201114a";

int debug = 0;

// ---------------------------------------------------------
// toggle output bit
void
pinToggle (
    int pin)
{
    static int  bits = 0;
    int     bit = 1 << pin;

    if (debug)  {
        Serial.print ("pinToggle: ");
        Serial.println (pin);
    }

    if (bits & bit)  {
        digitalWrite (pin, LOW);
        bits &= ~bit;
    }
    else {
        digitalWrite (pin, HIGH);
        bits |= bit;
    }
}

// ---------------------------------------------------------
// toggle output bit
int
readString (
    char *s,
    int   maxChar )
{
    int  n = 0;

    Serial.print ("> ");
    do {
        if (Serial.available()) {
            int c    = Serial.read ();

            if ('\n' == c)
                break;

            s [n++] = c;
            if (maxChar == n)
                break;
        }
    } while (true);

    return n;
}

// -----------------------------------------------------------------------------
// process single character commands from the PC
#define MAX_CHAR  10
char s [MAX_CHAR] = {};

int  analogPin = 0;

void
pcRead (void)
{

    static int  val = 0;

    if (Serial.available()) {
        int c = Serial.read ();

        switch (c)  {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            val = c - '0' + (10 * val);
            break;

        case 'A':
            analogPin = val;
            Serial.print   ("analogPin = ");
            Serial.println (val);
            val = 0;
            break;

        case 'D':
            debug ^= 1;
            break;

        case 'I':
            pinMode (val, INPUT);
            Serial.print   ("pinMode ");
            Serial.print   (val);
            Serial.println (" INPUT");
            val = 0;
            break;

        case 'O':
            pinMode (val, OUTPUT);
            Serial.print   ("pinMode ");
            Serial.print   (val);
            Serial.println (" OUTPUT");
            val = 0;
            break;

        case 'P':
            pinMode (val, INPUT_PULLUP);
            Serial.print   ("pinMode ");
            Serial.print   (val);
            Serial.println (" INPUT_PULLUP");
            val = 0;
            break;


        case 'a':
            Serial.print   ("analogRead: ");
            Serial.println (analogRead (val));
            val = 0;
            break;

        case 'c':
            digitalWrite (val, LOW);
            Serial.print   ("digitalWrite: LOW  ");
            Serial.println (val);
            val = 0;
            break;

        case 'p':
#if !defined(ARDUINO_ARCH_ESP32)
            analogWrite (analogPin, val);
            Serial.print   ("analogWrite: pin ");
            Serial.print   (analogPin);
            Serial.print   (", ");
            Serial.println (val);
            val = 0;
#endif
            break;

        case 'r':
            Serial.print   ("digitalRead: pin ");
            Serial.print   (val);
            Serial.print   (", ");
            Serial.println (digitalRead (val));
            val = 0;
            break;

        case 's':
            digitalWrite (val, HIGH);
            Serial.print   ("digitalWrite: HIGH ");
            Serial.println (val);
            val = 0;
            break;

        case 't':
            Serial.print   ("pinToggle ");
            Serial.println (val);
            digitalWrite (val, ! digitalRead (val));
            val = 0;
            break;

        case 'v':
            Serial.print ("\nversion: ");
            Serial.println (version);
            break;

        case '\n':          // ignore
            break;

        case '"':
            while ('\n' != Serial.read ())     // discard linefeed
                ;

            readString (s, MAX_CHAR-1);
            Serial.println (s);
            break;

        case '?':
            Serial.println ("\npcRead:\n");
            Serial.println ("    [0-9] append to #");
            Serial.println ("    A # - set analog pin #");
            Serial.println ("    D # - set debug to #");
            Serial.println ("    I # - set pin # to INPUT");
            Serial.println ("    O # - set pin # to OUTPUT");
            Serial.println ("    P # - set pin # to INPUT_PULLUP");
            Serial.println ("    a # - analogRead (pin #)");
            Serial.println ("    c # - digitalWrite (pin #, LOW)");
            Serial.println ("    p # -- analogWrite (analogPin, #)");
            Serial.println ("    r # - digitalRead (pin #)");
            Serial.println ("    s   - digitalWrite (pin #, HIGH)");
            Serial.println ("    t   -- toggle pin # output");
            Serial.println ("    v   - print version");
            Serial.println ("    \"   - read string");
            Serial.println ("    ?   - list of commands");
            break;

        default:
            Serial.print ("unknown char ");
            Serial.println (c,HEX);
            break;
        }
    }
}

// -----------------------------------------------------------------------------
void
loop (void)
{
    pcRead ();
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin(115200);

    Serial.println (version);
#if defined(ARDUINO_ARCH_ESP32)
    Serial.println ("esp32");
#endif
}

Thanks for the answer ^^, i will investigate about your code and look if it can help me.

i'm not sure what your sendconfig() does.

it's been a while since i played with the 23017 (see interlock.ino), but when dealing with any I/O device, there's the issue of doing the I/O and then the issue of doing what you want.

as my code illustrates, it would be easier to develop what you need by having simple routines for read/write to to a specific 23017 chip and register and verify they work. then use those routines to support your application

i like data driven code, but the values in combination[] are the same as its index and the values in address are the index+0x20. the code you posted must be incomplete because i don't see definitions for bank1a, bank1b, ....

looks like the code infers register values based on the arguments.

that routine seem unnecessarily complicated and likely the cause of your problems.

as my code illustrates, adding Serial.prints to your I/O routines will confirm they do what you expect

I think i solved part of what i want to do, but i have a new problem and its based on my level of programming.

I want to get the updated value of the bank pins, when it changes.

For example if i have a B11110000, and i update it to B11110010, i want to send it back to array defined in the void loop function.

Here is the updated code; also i have defined variables like bank1a etc, in other place but dont want to make it longer.

void sendconfig(float muxnum, int banknum, int combnum, byte bankdata[])
{

byte combination[8] = {B000, B001, B010, B011, B100, B101, B110, B111}; //correcto
byte address[8] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27}; //inc
byte bank[2] = {0x12, 0x13}; //correcto


  float addnumf = (float)(muxnum / 4) - 0.01;
  int addnum = (int)addnumf;

  int combnumx = combnum - 1;
  
  float bdataf = (float)(muxnum / 2) - 0.01;
  int bdatax = (int)bdataf;
  int bdataevencheck = (int)muxnum % 2;

  if(bdataevencheck == 0){

  bankdata[bdatax] = ((bankdata[bdatax] & 0x1F) | (combination[combnumx] << 5));
  } else{
  bankdata[bdatax] = ((bankdata[bdatax] & 0xF1) | (combination[combnumx] << 1));
  }
  
  byte addressx = address[addnum];
  byte bankx = bank[banknum];

  
  Wire.beginTransmission(addressx);
  Wire.write(bankx);
  Wire.write(bankdata[bdatax]);
  Wire.endTransmission();
  
}


void loop() {
byte bankdata[16] = {bank1a, bank1b, bank2a, bank2b, bank3a, bank3b, bank4a, bank4b, bank5a, bank5b, bank6a, bank6b, bank7a, bank7b, bank8a, bank8b};
float mnum;
int bnum, cnum;

  // put your main code here, to run repeatedly:
    while(Serial.available()>0)
      {
          c = Serial.read();
          SerialData += c;  
                     
          if(c == 'A' || c == 'B'){
           mnum = SerialData.toInt();
           if(c == 'A'){
            bnum = 0;      
           } else {
            bnum = 1;
           }
           c = 0;
           SerialData = "";
          }

             if(c=='#'){
              cnum = SerialData.toInt();
              sendconfig(mnum, bnum, cnum, bankdata);
              c = 0;
              SerialData = "";
             }

             if(c=='R'){
              c = 0;
              SerialData = "";
             }

                  
      }

      }

So in the part which is use bankdata[bdatax] = ((bankdata[bdatax] & 0x1F) | (combination[combnumx] << 5)); for example, i want to take out of the function that byte and put it on the loop function array; Like updating the value.

it's not clear what you're trying to say. i think you assume i understand your code. stating your problem clearly will help you more than me

Hahaha my bad :(, but i solved it too i was declaring the array on the loop function and it wasn't storing anything, just renewing the data inside it. So i declared it before void setup, and all works perfectly now.

Thanks ^^-