Bluetooth with Python and Arduino (HC-06) - python

I'm trying to send information from my Arduino to Python. The information I'm sending is a string of 16 characters and the problem is I'm not receiving all the data at once.
Arduino code:
void setup(){
BTserial.begin(9600);
}
void loop() {
BTserial.print("0123456789012345");
delay(500);
}
Python code:
import bluetooth
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect(("00:13:04:83:EC:45", 1))
while(True):
print(sock.recv(10))
The output that comes out is:
b'0'
b'123456789'
b'012345'
The output that I want is:
b'0123456789012345'
or
0123456789012345
Thanks for any answer.

I have temporary really massy way to fix it but if this is possible any other way let me know
string = ""
final = ""
num = 0
while(True):
string = sock.recv(4096)
if str(string) == "b'$'":
num=1
elif num == 1:
final+=str(string)
num = 2
elif num == 2:
final += str(string)
final = final.replace(("b"), "")
final = final.replace(("'"), "")
print(final)
final = ""
num == 0

Append a message delimiter character to the arduino output sequence; your choice but ā€˜\nā€™ would be good. Make a python generator function that reads input characters from bluetooth stream, assembling them into a buffer. Yield the entire buffer when the message delimiter character has been received, so the function calling the generator blocks until the entire message is available.

Related

File Transfer to c#

I am trying to send a file from python client to a c# server and present it on screen by saving it first and then showing it on my MainWindow.
I came across a couple of problems I can't figure out why happen (I'm new to C#)
I followed this guide : http://snippetbank.blogspot.com/2014/04/csharp-client-server-file-transfer-example-1.html
The problems were :
1. The file was not being saved to my folder.
2. when I used message box to try and detect if it passes all the info it looks like it gets stuck in the middle .
I've been stuck on this for quite some time now but can't figure out what I'm missing
Python code :
def send_file(conn, name):
try:
full_path = "Output/"+name
file_to_send = open(full_path, "rb")
size = os.path.getsize(full_path)
file_name = name + "\n"
size_to_send = str(size) + "\n"
conn.send(size_to_send.encode())
conn.send(file_name.encode())
while size > 0:
data = file_to_send.read(1024)
conn.send(data)
size -= len(data)
file_to_send.close()
return conn
except IOError:
print("FILE IS ALREADY OPEN")
C# CODE :
public static string ReceiveFile(StreamReader reader, TcpClient tcpClient)
{
string folder = #"C:\Users\Roy\Desktop\GUI243\GUI243\";
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
MessageBox.Show(cmdFileSize);
// The first message from the client is the filename
string cmdFileName = reader.ReadLine();
MessageBox.Show(cmdFileName);
string full_path = folder + cmdFileName;
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
if (read == 0)
{
break;
}
received += read;
}
// Save the file using the filename sent by the client
using (FileStream fStream = new FileStream(Path.GetFileName(cmdFileName), FileMode.Create))
{
fStream.Write(buffer, 0, buffer.Length);
fStream.Flush();
fStream.Close();
}
return full_path;
}
In your C# code looks like a while cycle is missing, unless you call the function ReceiveFile() iterating somewhere else.
The class StreamReader needs to constantly check the tcp client socket to see if new data has been received because this is how a TCP stream works.
You don't know when the client will connect and send the data so you can't just call the ReceiveFile() function once.
In the example there is a perpetual while (true) cycle that makes that reader.Readline() work:
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected to client");
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
...
}
Do you have an equivalent in your code?
Also, is not pratical to use MessageBox for debugging purposes.
Try:
Debug.WriteLine ("Expected size is: " + cmdFileSize);
Note you need System.Diagnostics to use that.

Reading bytes from Arduino using Python

I am trying to read serial input from Arduino in Python. I am trying to do a simple task - my Arduino has one button writing to serial its state. This is the code for the Arduino:
const int ledPin = 7;
const int buttonPin = 2;
int buttonState= 0;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
buttonState = digitalRead(buttonPin);
if(buttonState == LOW){ digitalWrite(ledPin, HIGH);} else digitalWrite(ledPin, LOW);
Serial.println(buttonState);
delay(50);
}
Using serial monitor in Arduino 1.8.13 software I can see it prints either 0 or 1.
I'm trying to read it with the following code in Python:
import serial
ser = serial.Serial('COM10', baudrate = 9600, timeout = 1)
t = 0
while t<10:
arduinoData = ser.readline().decode()
print(arduinoData)
t+=1
print(type(arduinoData))
ser.close()
I read the first 10 inputs and print the type of the arduinoDatawhich in this case is string, since I'm using .decode(). However, I can't use boolean operators such as arduinoData=='1' to do anything, even though it is decoded to string. I have tried using .decode('ascii'), .decode('UTF-8') to no avail.
How can I check the values or how can I scrap the .decode() and convert the bytes to simple integers to then use boolean operators?
I am using python 3.9 with an Arduino Uno.
UPDATE 1
I've managed to convert it from str to int this way in Python:
import serial
ser = serial.Serial('COM10', baudrate = 9600, timeout = 1)
t=0
while t<10:
arduinoData = ser.readline().decode()
print(arduinoData)
t+=1
if(int(arduinoData) == 1):
print("Input is 1")
ser.close()
This way I can see that indeed my serial input is equal to 1. However I wish to do something if it is not equal to 1. So I put the checking in the loop like so:
import serial
ser = serial.Serial('COM10', baudrate = 9600, timeout = 1)
t=0
while t<10:
arduinoData = ser.readline().decode()
print(arduinoData)
if(int(arduinoData) == 1):
print("Input is 1")
t+=1
ser.close()
And get the following error:
Traceback (most recent call last):
File "C:\Users\01132892\Desktop\Serial\SerialButtonPurple.py", line 12, in <module>
if(int(arduinoData) == 1):
ValueError: invalid literal for int() with base 10: ''
I am assuming this is a problem with converting it inside the loop, however I have no idea how to solve it.
Okay, I've managed to solve the problem.
Please note that to check whether it is working in the main loop, I am pressing key 'a' if the button is not pressed (signal read is 0) and releasing it if the signal is 1.
import serial
import pynput
ser = serial.Serial('COM10', baudrate = 9600, timeout = 1)
from pynput.keyboard import Key, Controller
keyboard = Controller()
while 1:
arduinoData = ser.readline().rstrip()
print(arduinoData)
if(arduinoData==b'0'):
keyboard.press('a')
else:
keyboard.release('a')
ser.close()
I would like to thank ocrdu for suggesting to use the rstrip() to get rid of EOL.
My Python isn't all that good, but maybe ser.readline() also returns the EOL? If so, arduinoData doesn't contain just the "1".
If I'm right, it should work if you strip off the EOL.
Alternatively: have the Arduino send single bytes with Serial.print(buttonState); and read single bytes in Python with ser.read(). There is no need for a delimiter when you send and read single bytes.

What is the proper way to exchange data between QTcpSocket and python socket?

I am sending .jpg images from Qt client to python server. Qt client sends as follows:
void Sender::sendToServer(QByteArray imageData)
{
QDataStream stream(serverSocket);
int t = imageData.size();
stream << t;
stream << imageData;
serverSocket->waitForBytesWritten(-1);
qDebug() << "Frame size:" << imageData.size();
}
And the server is:
unpacker = struct.Struct('!i')
conn, addr = s.accept()
bool = True
data = b''
while True:
while len(data) < 4:
try:
data += conn.recv(100)
except:
break
img_size = unpacker.unpack(data[:4])[0]
print('size: ', img_size)
print('Data: ',data)
print('Image: ',data[:8]) # deleting preceding 8 bytes
# image processing
The output is:
//Client:
Frame size: 49993
//Server:
size: 49993
Data: b'\x00\x00\xc3\r\x00\x00\xc3\r\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01...
Image: b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00...
In the output, .jpg file starts from '\xff\xd8'. As C++ int is 4 byte, the server expects "{4 byte}\xff\xd8...". However, preceding bytes are always of different size. Sometimes it's 8 bytes, but sometimes even different (such as 6 or 9 bytes). I could not figure out the reason behind it. What is the problem here?
Platform: win7, Qt 5.9.1, Python 3.4.
Edit
Following #Frank's answer, I changed the code as follows and everything works fine:
Qt client send method:
void Sender::sendToServer(QByteArray imageData)
{
QDataStream stream(serverSocket);
QString size = QString::number(data.size());
QString size_8_byte = size.rightJustified(8, ' '); // add spaces to make it exactly 8 bytes
imageData.prepend(size_8_byte.toUtf8());
stream.writeRawData((char*)(imageData.data()), imageData.size());
serverSocket->waitForBytesWritten(-1);
}
Python server:
unpacker = struct.Struct('8s')
conn, addr = s.accept()
data = b''
bool = True
while True:
while len(data) < 8:
try:
data += conn.recv(100)
except:
break
img_size = unpacker.unpack(data[:8])[0]
img_size = int(img_size.decode("utf-8").strip()) #remove spaces and convert to int
data = data[8:] #correct data
#image processing
QDataStream is not a simple binary dumper, it formats the data in some internally defined way. This helps with stuff like endianness correction and the like,
However, there is a very simple fix available to you: QDataStream provides the writeRawData() function, which bypasses all formatting operations.
Simply change your output operations to using it and you should be good to go.

Python checking for serial string in If statement

As a newbie to python, I'm trying to use it to read a file and write each line of the file to the RS-232 port. My code bellow seems to work for the most part, except for my listen and react segments. From poking around, it seems that my if statements can't read if I've received a "Start\r", or "End\r" string from my device (RS-232). Can anyone provide feedback on what is missing?
import serial
import time
port = "/dev/ttyS0"
speed = 9600
print("\n\n\n\nScript Starting\n\n\n")
ser = serial.Serial(port, speed, timeout=0)
ser.flushInput() #flush input buffer, discarding all its contents
ser.flushOutput()#flush output buffer, aborting current output and discard all that is in buffer
text_file = open("my.file", "r")
lines = text_file.read().split('\n')
i = 0
counter = 0
while i<len(lines):
response = ser.readline()
if (counter == 0):
print("\n\nProbing With Off Data\n")
ser.write('FFF')
ser.write('\r')
counter+=1
if (response == 'Start'):
ser.write('FFF')
ser.write('\r')
if (response == 'End'):
print("\nString Transmitted:")
print lines
make_list_a_string = ''.join(map(str, lines))
ser.write(make_list_a_string)
ser.write('\r')
print("\n")
i+=1
text_file.close()
exit(0)
Try using strip() to get rid of any trailing or preceding '\r's:
if (response.strip() == 'Start'):

Python Serial: How to use the read or readline function to read more than 1 character at a time

I'm having trouble to read more than one character using my program, I can't seem to figure out what went wrong with my program.
import serial
ser = serial.Serial(
port='COM5',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
count=1
while True:
for line in ser.read():
print(str(count) + str(': ') + chr(line) )
count = count+1
ser.close()
here are the results I get
connected to: COM5
1: 1
2: 2
3: 4
4: 3
5: 1
actually I was expecting this
connected to: COM5
1:12431
2:12431
something like the above mentioned which is able read multiple characters at the same time not one by one.
I see a couple of issues.
First:
ser.read() is only going to return 1 byte at a time.
If you specify a count
ser.read(5)
it will read 5 bytes (less if timeout occurrs before 5 bytes arrive.)
If you know that your input is always properly terminated with EOL characters,
better way is to use
ser.readline()
That will continue to read characters until an EOL is received.
Second:
Even if you get ser.read() or ser.readline() to return multiple bytes,
since you are iterating over the return value, you will
still be handling it one byte at a time.
Get rid of the
for line in ser.read():
and just say:
line = ser.readline()
I use this small method to read Arduino serial monitor with Python
import serial
ser = serial.Serial("COM11", 9600)
while True:
cc=str(ser.readline())
print(cc[2:][:-5])
Serial sends data 8 bits at a time, that translates to 1 byte and 1 byte means 1 character.
You need to implement your own method that can read characters into a buffer until some sentinel is reached. The convention is to send a message like 12431\n indicating one line.
So what you need to do is to implement a buffer that will store X number of characters and as soon as you reach that \n, perform your operation on the line and proceed to read the next line into the buffer.
Note you will have to take care of buffer overflow cases i.e. when a line is received that is longer than your buffer etc...
EDIT
import serial
ser = serial.Serial(
port='COM5',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
#this will store the line
line = []
while True:
for c in ser.read():
line.append(c)
if c == '\n':
print("Line: " + ''.join(line))
line = []
break
ser.close()
I was reciving some date from my arduino uno (0-1023 numbers).
Using code from 1337holiday, jwygralak67 and some tips from other sources:
import serial
import time
ser = serial.Serial(
port='COM4',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
#this will store the line
seq = []
count = 1
while True:
for c in ser.read():
seq.append(chr(c)) #convert from ANSII
joined_seq = ''.join(str(v) for v in seq) #Make a string from array
if chr(c) == '\n':
print("Line " + str(count) + ': ' + joined_seq)
seq = []
count += 1
break
ser.close()

Categories

Resources