Использование pySerialTransfer через Bluetooth-сокеты - PullRequest
0 голосов
/ 01 мая 2020

У меня есть рабочий код, который отправляет структуру из Raspberry Pi в Arduino, используя pySerialTransfer, когда устройства подключены через USB-кабель. Однако я бы хотел сделать это по Bluetooth, а не по кабелю USB.

Независимо, используя отдельный код python и отдельный эскиз Arduino, я смог получить Raspberry Pi & Arduino общаться по Bluetooth через непрерывный поток текстовых или двоичных данных.

Моя проблема в том, что я не вижу, как объединить два подхода - например: если я отправляю структуру с двоичным кодом по Bluetooth, я не понимаю, как я получаю преимущества библиотек pySerialTransfer / SerialTransfer для анализа его на принимающей стороне. Я вижу, как я могу выполнить синтаксический анализ "вручную", ища специальные завершающие символы и т. Д. c., Но я надеялся избежать этого с помощью pySerialTransfer.

Спасибо за любые указатели / предложения / Примеры. Весь рабочий код, который я смог построить, находится здесь.

pySerialTransfer

Рабочий Arduino C Серийный код

#include "SerialTransfer.h"

SerialTransfer myTransfer;

int const ONBOARD_LED_PIN = 13;

struct POSITION {
  int id;
  float azimuth;
  float altitude;
} position;

void Blink(int n) {
  for (int i=0; i<n; i++) {
    digitalWrite(ONBOARD_LED_PIN, HIGH);
    delay(75);
    digitalWrite(ONBOARD_LED_PIN, LOW);
    delay(75);
  }
  delay(150);
}


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  pinMode(ONBOARD_LED_PIN, OUTPUT);
  digitalWrite(ONBOARD_LED_PIN, LOW);
}

void loop()
{
  if(myTransfer.available())
  {
    //////////////////////////////////////////////
    // handle call from Python

    myTransfer.rxObj(position, sizeof(position));

    //////////////////////////////////////////////

    //////////////////////////////////////////////
    // send response
    myTransfer.txObj(position, sizeof(position));
    myTransfer.sendData(sizeof(position));
    //////////////////////////////////////////////
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

Рабочий серийный код Raspberry Pi

import time
import struct
from pySerialTransfer import pySerialTransfer


def StuffObject(txfer_obj, val, format_string, object_byte_size, start_pos=0):
  """Insert an object into pySerialtxfer TX buffer starting at the specified index.

  Args:
    txfer_obj: txfer - Transfer class instance to communicate over serial
    val: value to be inserted into TX buffer
    format_string: string used with struct.pack to pack the val
    object_byte_size: integer number of bytes of the object to pack
    start_pos: index of the last byte of the float in the TX buffer + 1

  Returns:
    start_pos for next object
  """
  val_bytes = struct.pack(format_string, *val)
  for index in range(object_byte_size):
    txfer_obj.txBuff[index + start_pos] = val_bytes[index]
  return object_byte_size + start_pos


if __name__ == '__main__':
  try:
    link = pySerialTransfer.SerialTransfer('/dev/cu.usbmodem14201', baud=115200)

    link.open()
    time.sleep(2) # allow some time for the Arduino to completely reset
    base = time.time()

    while True:

      sent = (4, 1.2, 2.5)
      format_string = '<lff'
      format_size = 4+4+4
      StuffObject(link, sent, format_string, format_size, start_pos=0)
      link.send(format_size)

      start_time = time.time()
      elapsed_time = 0
      while not link.available() and elapsed_time < 2:
        if link.status < 0:
          print('ERROR: {}'.format(link.status))
        else:
          print('.', end='')
        elapsed_time = time.time()-start_time
      print()

      response = bytearray(link.rxBuff[:link.bytesRead])
      response = struct.unpack(format_string, response)

      print('SENT: %s' % str(sent))
      print('RCVD: %s' % str(response))
      print(' ')

  except KeyboardInterrupt:
    link.close()

Связь Bluetooth

Рабочая связь Arduino Bluetooth

#include "SerialTransfer.h"
// Connect the HC-05 TX to Arduino pin 2 RX. 
// Connect the HC-05 RX to Arduino pin 3 TX through a voltage divider.
// 


long n = 0;
struct POSITION {
  float azimuth=5;
  float altitude=10;
};

SerialTransfer myTransfer;

void setup() 
{
    Serial.begin(9600);

    // HC-06 default serial speed for communcation mode is 9600
    Serial1.begin(9600);  
    myTransfer.begin(Serial1);
}

void loop() 
{
    n++;
    POSITION x;
    x.azimuth=n;
    x.altitude=n+1;

    myTransfer.txObj(x, sizeof(x));
    myTransfer.sendData(sizeof(x));

    if(Serial1.available() > 0){ // Checks whether data is comming from the serial port
      Serial.println(Serial1.read());} // Reads the data from the serial port
    delay(1000);
}

Рабочая python Связь Bluetooth

import bluetooth
sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
bd_addr = '98:D3:11:FC:42:16'
port = 1
sock.connect((bd_addr, port))
d = sock.recv(10240000)
print(d)
sock.send("hello")

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

Я не смог установить соединение через сокет дольше, чем на несколько часов (см. Обнаружение и восстановление после зависания bluetooth python connection ); возможно, моя попытка сокета была неправильной или, по крайней мере, ненужной комбинацией технологий, учитывая конечную цель использования pySerialTransfer. Возвращаясь к только последовательному соединению, я в конечном итоге смог заставить этот код работать для двунаправленной последовательной связи RFCOMM между моим Raspberry Pi и Arduino через Bluetooth.

Python Код

#!/usr/bin/python3

import datetime
import sys
import time
import subprocess

from pySerialTransfer import pySerialTransfer

COMMAND_START_CHAR = '<'
COMMAND_END_CHAR = '>'
LOGFILE = 'bt.log'


def Log(s):
  """Appends message s to the logfile."""
  with open(LOGFILE, 'a') as f:
    f.write('%s\n' % s)


def Time(epoch):
  """Converts epoch to easy-to-read string."""
  return datetime.datetime.fromtimestamp(epoch).strftime('%Y-%m-%d %H:%M:%S')


def ConnectBluetoothRetry(attempts=1):
  """Attempts to make connections for a number of attempts, exiting program on fail."""
  attempt = 1
  while attempt <= attempts:
    try:
      ser = pySerialTransfer.SerialTransfer('/dev/rfcomm1', 9600)
    except pySerialTransfer.InvalidSerialPort as e:
      Log('ERROR: Unbound; will attempt to bind: %s' % e)
      cmd = 'sudo rfcomm bind 1 98:D3:11:FC:42:16 1'
      conn = subprocess.Popen(cmd, shell=True)
      if conn.returncode is None:
        Log('ERROR: Unable to bind')
        sys.exit()
      else:
        Log('Connection bound with return code %s' % conn.returncode)
        if attempt == attempts:
          attempts += 1
          Log('Allowing one more attempt to connect')
    if ser:
      Log('Connected after %d attempts' % attempt)
      return ser
    attempt += 1
    if attempt < attempts:
      time.sleep(1)
  Log('ERROR: Failed to connect after %d attempts' % attempt)
  sys.exit()


def ReconnectOnError(ser, error=None):
  """Close and reopen the serial."""
  if error:
    Log('ERROR: %s' % error)
  ser.close()
  ser = ConnectBluetoothRetry(10)
  return ser


def Parse(s):
  """Returns the string encapsulated between COMMAND_START_CHAR and COMMAND_END_CHAR."""
  if COMMAND_START_CHAR in s:
    start_char = s.find(COMMAND_START_CHAR)
  else:
    return None
  if COMMAND_END_CHAR in s:
    end_char = s.rfind(COMMAND_END_CHAR)
  else:
    return None
  return s[start_char + len(COMMAND_START_CHAR): end_char - len(COMMAND_END_CHAR) + 1]


def Read(ser):
  """Non-blocking read on an open Bluetooth pySerialTransfer."""
  recv = ''
  if ser.available():
    if ser.status < 0:
      if ser.status == -1:
        Log('ERROR: CRC_ERROR')
      elif ser.status == -2:
        Log('ERROR: PAYLOAD_ERROR')
      elif ser.status == -3:
        Log('ERROR: STOP_BYTE_ERROR')
    else:
      for index in range(ser.bytesRead):
        recv += chr(ser.rxBuff[index])
  if recv:
    command = Parse(recv)
    if command is not None:
      command = recv[len(COMMAND_START_CHAR):-len(COMMAND_END_CHAR)]
      return command
    Log('ERROR: malformed %s' % recv)

  return None


def Write(ser, command):
  """Sends the encapsulated string command on an open Bluetooth pySerialTransfer."""
  send = COMMAND_START_CHAR+str(command)+COMMAND_END_CHAR
  byte_count = ser.tx_obj(send)
  ser.send(byte_count)
  Log('Sent %s' % send)


def main():
  """Sends sequential numbers over bluetooth, and receives & parses anything sent."""
  sys.stderr = open(LOGFILE, 'a')

  start = time.time()
  last_write = start
  Log('Started at %s' % Time(start))

  ser = ConnectBluetoothRetry(10)

  x = 0
  while True:
    try:
      command = Read(ser)
    except Exception as e:
      ser = ReconnectOnError(ser, 'Failed to receive: %s' % e)
    if command is not None:
      Log('Recd: %s; runtime: %.3f hours' % (command, (time.time() - start) / 60**2))

    if time.time() - last_write > 1:
      last_write = time.time()
      try:
        Write(ser, x)
      except Exception as e:
        ser = ReconnectOnError(ser, 'Failed to send: %s' % e)
      x += 1

    time.sleep(.1)


if __name__ == "__main__":
  main()

Код Arduino

#include "SerialTransfer.h"


// Connect the HC-05 TX to Arduino pin 2 RX. 
// Connect the HC-05 RX to Arduino pin 3 TX through a voltage divider.
// 

SerialTransfer myTransfer;

char number[12];

unsigned long n = 1;
long last_mesg_received = 0;
long last_mesg_sent = 0;

void(* resetFunc) (void) = 0; //declare reset function @ address 0

void setup() 
{
    Serial.begin(9600);

    // HC-05 default serial speed for communcation mode is 9600
    Serial1.begin(9600);
    myTransfer.begin(Serial1);

    last_mesg_received = millis();
}

void Read(){
  if(myTransfer.available())
  {
    Serial.print("Recd: ");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}


void Write(){
  sprintf(number, "<%d>", n);
  int len = strlen(number) + 1;
  for(int x; x<len; x++){
    myTransfer.txBuff[x+1] = number[x];
  }
  Serial.print("Sent: ");
  Serial.println(number);


  myTransfer.sendData(len);

}

void loop() 
{
  Read();
  if (millis() - last_mesg_sent > 1000){
    Write();
    last_mesg_sent = millis();
     n++;    
  }

  delay(100);

}

Это производит вывод, подобный следующему:

Python bt.log

Started at 2020-05-05 09:04:47
Connected after 1 attempts
Sent <0>
Sent <1>
Sent <2>
Recd: <1; runtime: 0.001 hours
Recd: <1; runtime: 0.001 hours
Recd: <2; runtime: 0.001 hours
Recd: <3; runtime: 0.001 hours
Sent <3>
Recd: <4; runtime: 0.001 hours
Sent <4>
Recd: <5; runtime: 0.002 hours
Sent <5>
Recd: <6; runtime: 0.002 hours
Sent <6>
Sent <7>
Recd: <7; runtime: 0.002 hours
Sent <8>
Recd: <8; runtime: 0.003 hours
Sent <9>
Recd: <9; runtime: 0.003 hours
Sent <10>
Recd: <10; runtime: 0.003 hours
Sent <11>
Recd: <11; runtime: 0.003 hours
Sent <12>
Recd: <12; runtime: 0.004 hours
Sent <13>
Recd: <13; runtime: 0.004 hours
Sent <14>
Recd: <14; runtime: 0.004 hours
Sent <15>
Recd: <15; runtime: 0.005 hours
Sent <16>
Recd: <16; runtime: 0.005 hours
Sent <17>

Arduino Serial Monitor

09:04:49.591 -> Sent: <1>
09:04:50.564 -> Sent: <2>
09:04:51.368 -> Recd: <0>
09:04:51.477 -> Recd: <1>
09:04:51.583 -> Recd: <2>
09:04:51.687 -> Sent: <3>
09:04:52.080 -> Recd: <3>
09:04:52.777 -> Sent: <4>
09:04:52.997 -> Recd: <4>
09:04:53.804 -> Sent: <5>
09:04:54.096 -> Recd: <5>
09:04:54.896 -> Sent: <6>
09:04:55.083 -> Recd: <6>
09:04:55.995 -> Sent: <7>
09:04:56.104 -> Recd: <7>
09:04:56.970 -> Sent: <8>
09:04:57.073 -> Recd: <8>
09:04:58.084 -> Recd: <9>
09:04:58.084 -> Sent: <9>
09:04:59.106 -> Recd: <10>
09:04:59.174 -> Sent: <10>
09:05:00.087 -> Recd: <11>
09:05:00.197 -> Sent: <11>
09:05:01.111 -> Recd: <12>
09:05:01.291 -> Sent: <12>
09:05:02.091 -> Recd: <13>
09:05:02.380 -> Sent: <13>
09:05:03.103 -> Recd: <14>
09:05:03.390 -> Sent: <14>
09:05:04.087 -> Recd: <15>
09:05:04.518 -> Sent: <15>
09:05:05.091 -> Recd: <16>
09:05:05.607 -> Sent: <16>
0 голосов
/ 02 мая 2020

pySerialTransfer / SerialTransfer упорядочит данные в формате пакета с CR C, так что это быстрый и безопасный способ связи через последовательный порт.

На самом деле, нет специального символа для завершения пакета, потому что двоичные данные могут быть любой символ от 0x00 до 0xFF покрывает весь код ASCII.

...