Подтвердить что ты не робот

PySerial отлично работает в интерпретаторе Python, но не является автономным

Доброе утро! Недавно я купил совет Arduino, чтобы сделать свой "легкий контроль" в моей комнате. Вот код прошивки, которую я написал:

int control = 0;
int pin = 0;

void setup()
{
  Serial.begin(9600);
  for(pin = 0; pin <= 13; pin++) pinMode(pin, OUTPUT);
}

void loop()
{
  control = Serial.read();
  if (control > 0 && control <= 13) digitalWrite(control, HIGH);
  if (control < 256 && control >= (256-13)) digitalWrite((256-control), LOW);
}

После этого я использовал pySerial из интерпретатора Python для управления контактами, и все работало нормально. Вот фрагмент вывода интерпретатора:

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> ser = serial.Serial('/dev/ttyUSB0', 9600)
>>> ser.write(chr(12))
>>> # The light turned on here
... 
>>> ser.write(chr(256-12))
>>> # The light turned off here
...

Затем я решил написать простой Python script, чтобы сделать то же самое:

#!/usr/bin/env python

import serial
import time

ser = serial.Serial('/dev/ttyUSB0', 9600)

ser.write(chr(12))
time.sleep(1)
ser.write(chr(256-12))

Но это не работает! Arduino показывает, что что-то было получено за время, когда я запустил script, но ничего не происходит. Вот фрагмент вывода strace для script:

open("/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
open("/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 4
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
write(4, "\f", 1)                       = 1
close(4)                                = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f45cf4c88f0}, {0x4d9820, [], SA_RESTORER, 0x7f45cf4c88f0}, 8) = 0
exit_group(0)                           = ?

Похоже, все должно быть хорошо, поэтому я не знаю, в чем проблема. Я был бы признателен за любую помощь, большое спасибо заранее!

PS Когда я запускаю программу под PDB, все работает нормально. Гейзенбуг.

UPDATE: я заставил контроллер отправить мне данные, которые он получает, и похоже, что он ничего не получает, когда я запускаю script, но получает все, когда я отправляю данные из интерпретатора. Код прошивки теперь выглядит следующим образом:

int control = 0;
int pin = 0;

void setup()
{
  Serial.begin(9600);
  for(pin = 0; pin <= 13; pin++) pinMode(pin, OUTPUT);
}

void loop()
{
  if (Serial.available() > 0)
  {
    control = Serial.read();
    if (control <= 13) digitalWrite(control, HIGH);
    if (control < 256 && control >= (256-13)) digitalWrite((256-control), LOW);
    Serial.println(control);
  }
}
4b9b3361

Ответ 1

Я думаю, что это, вероятно, условие гонки между тем, когда открыт последовательный порт и когда данные отправляются. Я бы, наверное, спал между открытыми и звонками.

В качестве альтернативы вместо использования этой библиотеки "serial" вы можете просто открывать и писать непосредственно на устройство, возможно, это делает что-то смешное (см. двойное открывание, упомянутое в других сообщениях)

Ответ 2

Мое предположение - это что-то с окружающей средой.

import os
print os.environ['PS1']

Из script, который не будет установлен. (И, возможно, что-то еще.)

tty будет буферизовать по-разному в зависимости от того, считают ли они, что терминал интерактивен. Это должно быть единственной разницей между тем, как работают ваши два метода. Многие приложения решают это относительно того, установлена ​​ли PS1 (ваша подсказка терминала). Если вы установите это вручную в своей среде, оно может начать вести себя так же, как в интерактивном режиме.

Кроме того, я бы вызвал вызов команды pyserial flush вручную в script. (И это был бы предпочтительный способ сделать это. Вместо маскировки в качестве интерактивного терминала.)

Ответ 3

Ваш вывод strace показывает, что он дважды открывает чтение/запись последовательного порта. Во второй раз он записывает только chr (12), затем закрывает файл. У меня недостаточно информации для решения проблемы, но, возможно, это помогает? или вы уже это выяснили?

Ответ 4

Можно ли дважды проверить, сбрасывается ли Arduino при открытии последовательного соединения? В случае reset первые последовательные байты, которые вы отправляете, будут получены загрузчиком, а не кодом. Тогда загрузчик может предположить, что вы хотите запрограммировать контроллер и ждать дальнейших команд и/или данных.

Точное поведение загрузчика зависит от вашего конкретного Arduino.

Чтобы проверить это, напишите небольшой эскиз, который мигает светодиодом 13 и посмотрит, влияет ли на инициализацию Python script мигание. Если это так, то есть загрузчик.

Чтобы исправить это, существует несколько возможных решений:

1) убедитесь, что не существует reset, вызванного инициализацией последовательного интерфейса. 1a) сделать это на стороне Python 1b) сделать это на стороне Arduino 1b аппаратное решение) отключить оскорбительные следы на плате 1b) избавиться от загрузчика

2) не отправлять данные, когда загрузчик выполняет свою работу.

Самое простое решение - (2) мое предпочтительное решение избавляется от загрузчика. Однако в этом случае вам нужен системный программист (что в любом случае является хорошей идеей).