Go Down

Topic: Sending a value to Arduino UNO(pyserial), then transfer that to a slave(SPI) (Read 386 times) previous topic - next topic

Renoveth

Hello Arduino Community, I am trying to control data processing between Arduino UNO(Master) to the shift register-controlled switches(Slave).

The whole schematic is simple, I send 1-byte value from PC(Master) to the Arduino(slave) through pyserial, which is read serially by the same Arduino which now becomes a Master to control the shift register(new slave). The idea is I have 8 by 8 switch matrix at the new slave, so 8 bit(1 byte) information will control the on-off state of the switch. The switch IC is daisy chained, so ultimately I will be sending 8 bytes from PC to Arduino.

Unfortunately, it is not working as I intended and I'm yet to identify the core issue. Following are my codes for

Python:
Code: [Select]

import serial
#make dsrdtr true to not lose the program compiled on the arduino
ser = serial.Serial(port='COM3', baudrate=9600, bytesize=8, parity='N',dsrdtr=True)

#value = 0 should correspond to closing all the switches. List of all zeros should close everything
Value = [0, 0, 0, 0, 0, 0, 0, 0]

#encode the value into an array of bytes
Sending = bytearray(Value)

#send
ser.write(Sending)


simply explained, I generate list of values between 0 to 255, convert it to a bytearray, and then send it to the arduino serially. I have checked that the bytearray can be used for serial.write().

And for the Arduino:

Code: [Select]

#include <SPI.h>

int SwitchArray;
const int ChipSelectPin = 10;
int i=0; 
int flag;

void setup() {
  // init serial port baud rate
  Serial.begin(9600);
  pinMode(ChipSelectPin, OUTPUT);
  Serial.setTimeout(5);
  //initialize SPI to set pin 10,11,13 for SS, DATA, SCK
  SPI.begin();
}

void loop() {

  // check serial
  if ( Serial.available()>=0){
    // cast the string read in an integer
    SwitchArray = Serial.read();
    flag= 1;
    }
  if (flag == 1){
    //turn on the chip select
    digitalWrite(CS, LOW);
    //Transfer the received value,
    SPI.transfer(SwitchArray);
    i++;
    //Do this 8 times, at every transfer,the previous value
    //will be sent to the next line in the daisy chain
   
    //Once all 8 bytes have been transferred
    if(i==8){
      //Put Chip select to high
      digitalWrite(CS, HIGH);
      i=0;
      }
    //reset the flag everytime a value is transferred via SPI
    flag = 0;
    }
 }
}


And it does not work, please bear in mind the python code is extensively longer but I have shortened it for the sake of simplicity when asking in this forum.

Now, the reason I've chosen to ask in the interfacing category is that I suspect the issue to be at the PC-Arduino interface.
When I try to just send a value from Arduino by writing
Code: [Select]

Spi.transfer(0)

and loop it eight times, it successfully closes the switches(more accurately, I externally receive a signal that indicates that the switches are off). When doing this, TX LED blinks.
However, if I try to send 0 through python from PC to Arduino, then it does not seem to perform like SPI.transfer(0) case. Likewise, only blink I see is RX but not TX.

So my question is, Can you receive a value from PC to UNO through serial, convert it to an integer, and then transfer it through SPI (Within the context of what I'm trying to achieve above) If yes, what seems to be the error I'm facing but not detecting?

I have not seen a question revolving similar set up as mine so I've decided to make a new topic. Apology in advance if an identical question was asked before. Moreover, I will be happy to receive any general coding feedback (don't write this don't write that) since I'm a total newbie in any programming language.

If it matters, I'm using windows system.

Robin2

So my question is, Can you receive a value from PC to UNO through serial, convert it to an integer, and then transfer it through SPI (Within the context of what I'm trying to achieve above) If yes, what seems to be the error I'm facing but not detecting?
Yes.

Have a look at this Python - Arduino demo and at Serial Input Basics - simple reliable ways to receive data.

Unless you need a very high data throughput send the data in human readable form as t makes debugging much easier.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Renoveth

Yes.

Have a look at this Python - Arduino demo and at Serial Input Basics - simple reliable ways to receive data.

Unless you need a very high data throughput send the data in human readable form as t makes debugging much easier.

...R

Hello and thanks for the reply! I modified my code with the start-end marker as suggested and I can finally communicate from PC to the ultimate slave. Thanks!

I do however have another question. It seems like my understanding of serial.read() is weak. With the arduino code as :

Code: [Select]

#include <SPI.h>

const byte numBytes = 8;
byte receivedBytes[numBytes];
byte numReceived = 0;
const int CSPin = 10;
static byte i = 0;
boolean flag = false;
boolean newData = false;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(CSPin, OUTPUT);
  digitalWrite(CSPin, HIGH);
  Serial.setTimeout(200);
  SPI.beginTransaction(SPISettings(4000000, LSBFIRST, SPI_MODE0));
  SPI.begin();

}

void loop() {
  // put your main code here, to run repeatedly:
  recvBytesWithMarkers();
  SendSignals();
}

void recvBytesWithMarkers(){
  static boolean recvProgress = false;
  static byte ndx = 0;
  byte StartMark = 0x3C;
  byte EndMark = 0x3E;
  byte switcharray;
  int counter = 0;

  while(Serial.available()>=0 && newData == false){
    switcharray = Serial.read();

    if (recvProgress ==true){
      if(switcharray !=EndMark){
        receivedBytes[ndx] = switcharray;
        ndx++;
        if(ndx >=numBytes){
          ndx = numBytes- 1;
          }
        }
      else{
        receivedBytes[ndx] = '\0'; // terminate the string
        recvProgress = false;
        numReceived = ndx;  // save the number for use when printing
        ndx = 0;
        newData = true;
        }
    }else if(switcharray ==StartMark){
      recvProgress =true;
      }
   
  }
  }

void SendSignals(){
  if (newData ==true){
    digitalWrite(CSPin, LOW);
    for ( int k=0; k < 8; k++){
      SPI.transfer(receivedBytes[i]);
      i++;
      }
      Serial.println();
    if(i == 8){
      digitalWrite(CSPin, HIGH);
      i = 0;
      newData = false;
      }
    }
}



I can send a value 8 times as:

Code: [Select]

ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())
ser.write("0".encode())


in between the markers. This is ok because I'm just turning off all the switches, however, when I change one of the values to let's say 1, to turn on the particular switch, it does not perform as expected.
I am expecting that individual ser.write() is picked up by serial.read() sequentially. But I feel like the this is not what's happening and it's not storing those values I sent to receivedBytes[ndx] in a corresponding manner. How can I modify the serial.read() line in such way that
Code: [Select]

ser.write("0".encode())
ser.write("1".encode())
ser.write("2".encode())
ser.write("3".encode())
ser.write("4".encode())
ser.write("5".encode())
ser.write("6".encode())
ser.write("7".encode())


will actually store 0 to 7 in the receivedBytes
  • where x is 0-7??


Thanks in advance.

And unfortunately I'd rather not use parseData() option suggested from python-Arduino demo due to my method of handling data at the python site. Arduino side solution appreciated!!

Robin2

Referring to Reply #2 ...

I don't understand the problem. And I don't recall what the Python ".encode" does.

You seem to be sending the byte values 0 - 7 but you have not told us what the Arduino actually receives.



Is there any reason not to send the characters 0 to 7 (ASCII values 48 to 55)? Sending data in human readable form makes debugging very much easier.

The Arduino can change a numeric character into the byte value simply by subtracting 48.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

ron_sutherland

With Python3 I have been doing:

Code: [Select]
import serial
sio = serial.Serial("/dev/ttyUSB0",38400, timeout=3)
sio.write(("0").encode('ascii'))


I don't think you want to use the default encoding for serial.  

It is a good idea to echo back a reply with a new line e.g. something like Serial.println(switcharray, DEC)

Code: [Select]
echo = sio.readline().strip()

The echo is in an ascii encoding, which Python's print knows how to use. Unfortunately, if I want to add something to the string I need to force both encodings to be the same.

Code: [Select]
print("cmd echo: ".encode('ascii') + echo)

Renoveth

Referring to Reply #2 ...

I don't understand the problem. And I don't recall what the Python ".encode" does.

You seem to be sending the byte values 0 - 7 but you have not told us what the Arduino actually receives.



Is there any reason not to send the characters 0 to 7 (ASCII values 48 to 55)? Sending data in human readable form makes debugging very much easier.

The Arduino can change a numeric character into the byte value simply by subtracting 48.

...R
Hey and sorry I did not make it clear from my post. My goal here is to send 8 bytes info to Arduino, let them process with SPI (thus SPI.transfer(x) for 8 times). And upon setting the chip select to high, all the ICs controlling the LED switches will turn on or off depending on the byte info I sent; sending 64 bits info to control the switches in the daisy chain.

My issue here is that what I am currently implementing is I take the first element of the array, send it by encoding it in the arduino readable format (encode(), as far as I understand, transforms the string into UTF-8 format by default), and then transferring it eight times through serial.

The arduino side has serial.read() prepared and everytime something is detected(which I regulate through the "<" startmarker and the counter everytime non -1 value is read at the serial), it gets stored in the array. So ultimately, this array should contain 8 byte values that I sent from the python side.

The problem I am facing is that the bytes received are clearly not reflecting the switching configuration that is intended, I can further emphasize this by sending 0 for 8 times in the row (which corresponds to all boolean = false) does not lead to the switching. This raises my concern that serial.read(), is perhaps not reading what I'm hoping it reads, the encoded bytes from the python side.

so the

Code: [Select]

ser.write("194".encode())
ser.write("240".encode())
ser.write("5".encode())
ser.write("8".encode())
ser.write("95".encode())
ser.write("45".encode())
ser.write("67".encode())
ser.write("81".encode())


As an example, is the 8 byte (64 bits) I want to send, which I expected it to be received, read, stored sequentially by

Code: [Select]

  while(Serial.available()>=0 && newData == false){
    switcharray = Serial.read();

    if (recvProgress ==true){
      if(switcharray !=EndMark){
        receivedBytes[ndx] = switcharray;
        ndx++;


to store these values in the receivedBytes[ndx]. Yet it doesn't seem like that's the case.

Unfortunately, I don't have the tools to detect what exactly the Arduino is storing as these 8 values. I'm even sceptical that the serial.available()-->Serial.read() is in sync with the serial.write() from the python side. I expect the input buffer to continue reading -1 unless the value is sent from the python side. But is it possible that this form of serially sending the data can cause a discrepancy in the receiver side?

ron_sutherland

Quote
encode(), as far as I understand, transforms the string into UTF-8 format by default
That sounds correct, but I have no clue what Arduino will do with the bytes encoded as UTF-8. Perhaps it would be better to use the Python chr() function and then encode as ascii for serial.

Code: [Select]
ser.write(chr(194).encode('ascii'))
ser.write(chr(240).encode('ascii'))
ser.write(chr(5).encode('ascii'))
ser.write(chr(8).encode('ascii'))
ser.write(chr(95).encode('ascii'))
ser.write(chr(45).encode('ascii'))
ser.write(chr(67).encode('ascii'))
ser.write(chr(81).encode('ascii'))



Update:

Code: [Select]
>>> chr(194).encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xc2' in position 0: ordinal not in range(128)
>>>


does not work, so I am not sure what encoding will work, I don't think UTF-8 turns into single bytes like ascii.


Update2:

Code: [Select]
>>> bytes(chr(194))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string argument without an encoding
>>> bytes(chr(194).encode())
b'\xc3\x82'


So it is clear that UTF-8 is not going to work.

ron_sutherland

Some tinkering and I think this is the magic

Code: [Select]
ser.write(bytes([194]))
ser.write(bytes([240]))
ser.write(bytes(chr(5).encode('ascii')))
ser.write(chr(8).encode('ascii'))
ser.write(chr(95).encode('ascii'))
ser.write(chr(45).encode('ascii'))
ser.write(chr(67).encode('ascii'))
ser.write(chr(81).encode('ascii'))


ascii values are less than 128 so they do turn into bytes, but the higher values don't, and that is not what I recall, I may be thinking Python 2.

update: opps, I had shown ser.write(bytes(194)) but that is very wrong.

Robin2

Unfortunately, I don't have the tools to detect what exactly the Arduino is storing as these 8 values.
Start by simplifying things.

Just write a short Python program that sends data to the Arduino and displays on the PC screen the data it receives from the Arduino. And get the Arduino program to send back whatever message it has received. Don't move on until that works properly.

And don't make things too complicated. The .encocde() method just converts a unicode character to a simple ASCII character. When I asked the question in Reply #3 I had been wondering if it converted (for example) '3' to the binary value 0b00000011, and it does not.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up