Arduino Sketch работает с Serial Monitor, но не с pyserial

Я тестирую этот простой код arduino в python, но он работает в arduino serial, а не в python. Пользователь определяет количество миганий на светодиоде. Это работает на серийном мониторе arduino. Но когда я использую его в python, он не работает. может ли кто-нибудь помочь? спасибо

Код Arduino:

int ledPin = 13; // select the pin for the LED int val = 0; // variable to store the data from the serial port void setup() { pinMode(ledPin,OUTPUT); // declare the LED's pin as output Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect (USB) } establishContact(); } void establishContact(){ while (Serial.available() <= 0){ val = Serial.parseInt(); Serial.flush(); // Serial.println("Est"); } } void loop () { if (val>0) { for(int i=0; i<val; i++) { digitalWrite(ledPin,HIGH); delay(150); digitalWrite(ledPin, LOW); delay(150); } val=0; } } 

Код Python:

 import serial import time ser = serial.Serial('/dev/tty.usbmodem1421', baudrate=9600,timeout =None) def blinkLED(x): ser.write(x) return; 

Во-первых, я буду предполагать, что где-то в коде, который вы вызываете

 blinkLED('10') 

иначе вся эта дискуссия бессмысленна.

В связи с этим я изменил бы функцию blinkLED(x) следующим образом:

 def blinkLED(x): ser.write(x) ser.flushOutput() return 

Во-вторых, я не полностью убежден в этом коде:

 1: void establishContact() { 2: while (Serial.available() <= 0){ 3: val = Serial.parseInt(); 4: Serial.flush(); 5: } 6: } 

Прежде всего, я не совсем уверен, что вы думаете, что Serial.flush() должен там делать, поскольку согласно документации он «ожидает завершения передачи последовательных данных» , но вы ничего не отправляете Serial выход.

Во-вторых, когда вы находитесь в строке 2: на первой итерации цикла возможны два случая:

  • A. что-то доступно на Serial входе, поэтому Serial.available() > 0 , вы не вводите цикл, и вы не читаете val
  • B. Serial.available() == 0 ничего доступного на Serial входе, поэтому Serial.available() == 0 , и вы вводите цикл. Теперь есть два подсектора:
    • B.1. если ничего не поступит на Serial вход, вы всегда будете читать 0 и остаетесь в этом цикле
    • БИ 2. если что-то прибывает на Serial вход, есть 3 поддиапазона:
      • B.2.I. входные данные поступают сразу после выполнения Serial.parseInt() , поэтому на следующем итерации цикла Serial.available() <= 0 является ложным, и вы выходите из цикла, не читая свой val (то есть, вы заканчиваете в случае A. )
      • B.2.II. входные данные поступают правильно, когда вы выполняете Serial.parseInt() и вы успешно разбираете все входные байты в val . Тем не менее, в Serial.available() <= 0 входе ничего не остается, поэтому условие Serial.available() <= 0 все еще сохраняется, и вы остаетесь в цикле
      • B.2.III. входные данные поступают правильно, когда вы выполняете Serial.parseInt() и вы успешно разбираете некоторые входные байты в val . Еще несколько байтов, которые не имеют значения Int (например, \r , \n , \t , пробел, буквенные символы, …) , игнорируются Serial.parseInt() и остаются в буфере Serial ввода. Поэтому на следующем цикле итерация Serial.available() <= 0 является ложной, и вы выходите из цикла.

Когда вы вызываете blinkLED('10') вы blinkLED('10') в одном из следующих случаев: A. , B.2.I. или B.2.II. , Это объясняет, почему вы не видите мигания: либо вы все еще застреваете в цикле, либо вы прошли мимо него, ничего не читая, и у вас все еще есть val == 0 .

Случай B.2.III. это единственная ситуация, в которой вы получаете рабочий эскиз . Это происходит, когда вы используете серийный монитор Arduino , поскольку последний отправляет дополнительный \n (или \r\n ? Я не помню ..) по умолчанию, когда вы нажимаете enter на своей клавиатуре.

Поэтому я думаю, что это объясняет, почему ваш эскиз работает, когда вы используете последовательный монитор , но не тогда, когда вы используете код python . Быстрое испытание было бы изменить blinkLED(x) следующим образом:

 def blinkLED(x): ser.write(x) ser.write('\n') ser.flushOutput() return 

Обратите внимание: тот факт, что использование последовательного монитора работает на нескольких тестах или что даже pyserial может работать с этим исправлением, не означает, что ваш эскиз теперь правильный и что он всегда будет работать. Фактически, код может по-прежнему не работать, например, если Serial.available > 0 слишком скоро, вы все равно не входите в тело цикла и parse val .


@ArnoBozo предложил изменить installContact establishContact() следующим образом:

 1: void establishContact() { 2: while (Serial.available() > 0){ 3: val = Serial.parseInt(); 4: Serial.flush(); 5: } 6: } 

Я думаю, что этот дизайн также ошибочен , потому что опять же у вас нет гарантии, что к моменту проверки Serial.available() > 0 копия python уже отправила данные (или что она была получена) . Если это не так, тело цикла просто не выполняется, и вы никогда не разбираете val . Конечно, вы можете попробовать играть с delay() , но это делает весь эскиз довольно хрупким.


Окончательное замечание: если вы посмотрите документацию Serial.parseInt() , вы обнаружите, что:

  • Парсинг останавливается, если никакие символы не были прочитаны для настраиваемого значения тайм-аута, или считывается не цифра;
  • Если действительные цифры не были прочитаны, когда тайм-аут (см. Serial.setTimeout ()), возвращается 0;

Если вы проверите документацию Serial.setTimeout() вы обнаружите, что timeout «по умолчанию составляет 1000 миллисекунд» . Опять же, за счет повторения себя и появления педантизма, не следует полагаться на тайм-ауты и задержки в протоколе связи, если это строго необходимо (например, чтобы эвристически решить, что пришло время освободить ресурсы, выделенные для связи с внешним объектом, который больше не является участвуя в сообщении).

Таким образом, мой совет – либо поцарапать Serial.parseInt() написать собственный синтаксический анализатор, либо использовать его более надежным способом. цель, которую вы имеете в виду:

 Serial.setTimeout(0); // disables timeout while (val == 0) { // discard any 'garbage' input val = Serial.parseInt(); // keeps trying to read an Int } 

Этот подход довольно жестокий (но YOLO) : Arduino не перестанет пытаться анализировать int отличное от 0 пока он не получит его. Опять же, вы должны отправить недопустимую цифру после своего номера (например, \n ), потому что иначе Serial.parseInt() не будет возвращаться, так как timeout теперь равен 0 .

(обратите внимание, что я не тестировал этот код, он также может не работать, если я неверно истолковал некоторые части документации библиотеки.)

Я думаю, что скрипт python вызывает blinkLED() где-то, и сообщение получено в blinkLED() establishContact() на вашем arduino.

Но вы должны были написать Serial.available() > 0 (сколько байтов ожидает чтения в входящем буфере).

Вы можете удалить следующие строки:

 while (!Serial) { } // DOES NOT wait for serial port to connect (USB) 

Действительно, серийный объект инстанцируется во время компиляции и сразу возвращается (за исключением одной платы: Леонардо?).

Serial.flush() пустой исходящий буфер, который вы здесь не используете; он ничего не делает на входящем буфере.

В вашем скрипте python, когда вы открываете последовательный порт для arduino, это, безусловно, сбросит плату arduino; который будет длиться менее 2 секунд. Так:

  • python может подождать, когда arduino отправит вам посылку привет.
  • или python может ждать 2 секунды, прежде чем отправлять сообщение blinkLED() .

Если вы откроете серийный монитор Arduino IDE, он сбрасывает плату arduino, но вы не можете вводить и отправлять сообщения до того, как плата готова, поэтому вы не видите проблему.

Что происходит с этим прослушиваемым кодом:

 while (Serial.available() <= 0){ val = Serial.parseInt(); } 

Он входит в цикл while, если нет данных, ожидающих серийный номер. Затем Serial.parseInt() будет ждать тайм-аута 1 с для входящих данных. Если ничего не произойдет, оно вернет 0.