Change pin based on serial.read()

Hi,
I want to switch LEDs on and off based on a 5V signal sent from a camera during exposure. So far, this is working well. The next step, is to cycle between different LEDs according to what my PC sends on the serial port. This part is not working. For now I’m trying with just 2 LEDs, but in the end there should be a dozen or so.

The way I’m trying to do it, is to store the pin numbers in an array. When needed, the PC sends a byte, for now, b’0’ or b’1’ (I’m using python, PySerial).
Every loop, I check for serial.available(), and do a serial.read(). I try to use the result of the read as the index for my pin number array, then digitalWrite on the chosen element of this array. So far, the LED is not changing, it’s always the same one strobing.

I’ve the impression that serial.read() is not doing what I am expecting (that is, returning a byte, which I can use as an index in an array), or maybe there’s a C++ mistake that I can’t spot (I’m very new to this language). Am I trying to overwrite a constant? Scope issue? Mixing up byte and int?

//---------------- variables declaration

//-Constants

const byte NBR_LED = 2;
const byte PIN_LED[NBR_LED] = {53 , 51};
const byte PIN_PWM_RECOM = 5;
const byte PIN_CAM_GPO1 = A8;

//-globals

byte selected_LED = PIN_LED[0];

//---------------- Initialization
void setup() {
  // pins assignment
  pinMode(PIN_PWM_RECOM, OUTPUT);
  digitalWrite(PIN_PWM_RECOM, HIGH);
  pinMode(PIN_CAM_GPO1, INPUT);
  
  // LED pin assignment
  for (byte n = 0; n < NBR_LED; n++) {
    pinMode(PIN_LED[n], OUTPUT);
    digitalWrite(PIN_LED[n], LOW);
  }
  
  // Opening COM3
  Serial.begin(9600);
  // Signaling to PC resetting is over and arduino ready
  Serial.println('g');
}

//---------------- Loop
void loop() {
  updateSelectedLED();
  useGPO1ActiveDuringExposure();
}

//---------------- main functions

void updateSelectedLED(){
  if (Serial.available() > 0){
    byte index = Serial.read();
    // answer to PC, for checking
    Serial.write(index);
    // Checking index is within the array size
    if(index < NBR_LED){
      // updates selected_LED value
      selected_LED = PIN_LED[index];
    }
  }
}

void useGPO1ActiveDuringExposure(){
  // This part is working well, it enables the LED driver's current, and close one of the LED's relay.
  // (one driver for all LEDs, and one relay for each LED)
  digitalWrite(PIN_PWM_RECOM, !digitalRead(PIN_CAM_GPO1));
  digitalWrite(selected_LED, digitalRead(PIN_CAM_GPO1));
}

On python side, I have this running (I leave out all the camera parts):

def openCOMAndConfirm(port='COM3', bps=9600, timeoutSerial=0.1, timeoutReset = 2):
    ser = serial.Serial(port, bps, timeout=timeoutSerial)  # open serial port
    start = time.time()
    while ser.readline() != b'g\r\n':
        if (time.time()-start)>=timeoutReset:
            print('timeout reset arduino')
            break
    if (time.time()-start)<timeoutReset:
        print('serial ready')
    return(ser)

def sendAndWaitConfirm(ser, index, timeout = 1): #timeout In seconds
    # takes a number (index), convert it to ascii for ser.write()
    index = int(index)
    ToWrite = str(index).encode('ascii')
    ser.write(ToWrite)
    # wait for arduino's echo before proceeding or timeout
    start = time.time()
    while int(ser.readline()) != index:
        if (time.time()-start)>timeout:
            print('timeout confirmation write')
            break
    print('write/read ok')
    return


ser = openCOMAndConfirm()
openCamera()
ring = 0
NBR_LED = 2
for i in range(0 , NbrImage):
    ring %= NBR_LED
    print('ring =', ring)
    sendAndWaitConfirm(ser, ring)
    ring += 1
    takeImage()
closeCamera()
ser.close()

Try changing this line:

    Serial.write(index);

to:

    Serial.print(index);

and see if you are still getting the input you expect.

It's important to understand the difference between the character representation of a number and the decimal integer literal representation of a number. For example, the ASCII value of the character 1 ('1') is 49.

And in the Python you need to send a raw byte, not an ASCII string, so replace:

    ToWrite = str(index).encode('ascii')
    ser.write(ToWrite)

with

    ser.write(index)

You need to be consistent about when you are using binary directly and when an ascii encoding.

Kudos for the bounds-check before indexing the pin array BTW.

Thanks for replying, that was heplful.

MarkT:
And in the Python you need to send a raw byte, not an ASCII string, so replace:

ToWrite = str(index).encode('ascii')

ser.write(ToWrite)



with


ser.write(index)




You need to be consistent about when you are using binary directly and when an ascii encoding.

Kudos for the bounds-check before indexing the pin array BTW.

Hmm, ok, then my problem is that I don't know how to send raw bits... At least not the good ones. I thought my code did it, since I checked and got this in the iPython console:

In [882]: index = int(4);
In [883]: ToWrite = str(index).encode('ascii')
In [884]: type(ToWrite)
Out[884]: bytes

but it was bait; I think I solved my problem using int.to_bytes():

In [885]: index = int(4);
In [886]: ToWrite = index.to_bytes(1, 'big')
In [887]: type(ToWrite)
Out[887]: bytes

Now what is sent is a byte corresponding to an integer, not a byte corresponding to the string of this integer in ASCII.

pert:
Try changing this line:

    Serial.write(index);

to:

    Serial.print(index);

and see if you are still getting the input you expect.

It's important to understand the difference between the character representation of a number and the decimal integer literal representation of a number. For example, the ASCII value of the character 1 ('1') is 49.

You are right that something is fishy there. This part of my code ends up working as I intended (it's just checking the communication) but it's probably two mistakes compensating each other. I'm still a bit confused about things, I think what my iPython consoles displays is not what the arduino receives, for instance the console shows me this:

In [1033]: index = int(0);
In [1034]: ToWrite = str(index).encode('ascii')
In [1035]: ToWrite
Out[1035]: b'0'

In [1036]: ToWrite = index.to_bytes(1, 'big')
In [1037]: ToWrite
Out[1037]: b'\x00'