Сопоставление P5.js с потенциометрами и кнопками Arduino

У меня есть школьный проект, с которым мне нужна помощь. Я пытаюсь создать эскиз с помощью p5.js и Arduino. У меня подключено два потенциометра и одна кнопка. Потенциометры должны управлять жизнью по осям X и Y, а кнопка должна очищать рисунок.

Я успешно установил node.js и последовательный порт. Я также написал Arduino IDE, чтобы она могла считывать значения потенциометров при их вращении. Эти значения отображаются как в Arduino IDE, так и в скетче P5.js.

Однако когда я нажимаю кнопку, ничего не происходит. Когда я запускаю скетч p5.js, там, где должно быть начало строки, появляется маленькая точка. Однако через несколько секунд оно слетает со страницы и исчезает.

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

Код эскиза P5.js:

let prevX, prevY;
//let isCleared = false;
let serialPortName = '/dev/tty.usbserial-1130';
let sensors = [200,200,0]

function setup(){
  createCanvas(400,400);
  serial = new p5.SerialPort();
  serial.list();
  serial.on('open', onOpen);
  serial.on('BtnData', BtnData);
  prevX = width/2;
  prevY = height/2;
  
}

function draw(){
  if (isCleared == true);{
  background(250);
  isCleared = false;
  }
  stroke(0);
  strokeWeight(5);
  line(prevX, prevY, sensors[0], sensors[1]);
  //prevX = sensors[0];
  //prevY = sensors[1];
}

function onOpen(){
  print('serial monitor opened.'); 
}

function onData(){
  let data = serial.readLine();
  let sensors = data.split(",");
  prevX = sensors[0];
  prevY = sensors[1];
  prevX = map(prevX, 0, 1023, 0,511);
  prevY = map(prevY, 0, 1023, 0, 511);
  print(data);
}

function BtnData(){
  let currentString = serial.readLine();
  trim(currentString);
  
  if (!currentString)return;
  console.info(currentString);
  latestData = currentString;
  
  
//}

//}
//function mousePressed(){
 // isCleared = true;
}

Ссылка на скетч p5.js https://editor.p5js.org/KelseyV/sketches/asGl0G0MC

Код Arduino Etch-A-Sketch:

const int switchPin = 4;
const int potPinX = A0; // pin the port is connected to
const int potPinY = A1;

//int switchVal = 0;
int potValX = 0; // variable to hold the value from the sensor
int potValY = 0 ;
int count = 0:

void setup(){
    pinMode(LED_BUILTIN, OUTPUT) ;
    pinMode(switchPin, INPUT);
    Serial.begin(9600);
  // pinMode(switchPin, INPUT);

  // establishContact();
}

void loop(){
    int switchVal = digitalRead(switchPin);
    int potValX = analogRead(potPinX) ;
    int potValY = analogRead(potPinY);
    
    Serial.print (count);
    Serial.print(",");
    Serial.print (potValX);
    Serial.print(",");
    Serial.println(potValY);
    //Serial.print(",");

    delay(1000);
 }
//}

Фотографии соединений Arduino

Сопоставление P5.js с потенциометрами и кнопками ArduinoСопоставление P5.js с потенциометрами и кнопками ArduinoСопоставление P5.js с потенциометрами и кнопками Arduino

Я хотел бы иметь возможность управлять эскизом p5.js с помощью Arduino, чтобы два потенциометра рисовали линию по осям X и Y, а кнопка очищала рисунок при нажатии.

Картинка может стоить тысячи слов, но схема передает гораздо больше информации, чем несколько фотографий макета и Arduino. Сжатый и нечитаемый скриншот практически ничего не стоит. Вы не предоставляете какое-либо описание реализации, то есть схемы, какие данные собираются, как данные преобразуются/форматируются и отправляются из Arduino в загадочную систему. Есть минимальные указания на тестирование/отладку подсистемы. У вас может легко возникнуть не одна, а несколько проблем, требующих решения.

sawdust 10.04.2024 07:42

«кнопка должна очистить рисунок» — если бы был обзор этой «особенности*» дизайна, я бы наложил вето на эту идею и отправил ее обратно на доработку.

sawdust 10.04.2024 07:49

Рассматривали ли вы кнопку «Очистить» на холсте p5?

apodidae 10.04.2024 18:13

Благодарим за ваше предложение! Итак, сегодня я добился некоторого прогресса. Я попытался добавить кнопку очистки на холст p5. //функция buttonPressed(){ // if (sensors[2] == 1){ // isCleared = true;

K-PopCorn 10.04.2024 20:14
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
4
98
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это хорошо написанный вопрос:

  • четкое заявление о том, какова цель, что было предпринято / каково ожидаемое поведение и что на самом деле происходит
  • изображения схемы и т. д.

Ниже приведены полезные советы о том, как улучшить ваш код, а также общие советы, которым следует следовать, помимо этого вопроса.

Вы решаете две проблемы одновременно:

  • эскиз/прошивка Arduino
  • код p5.js для чтения/анализа последовательных данных для рисования

Хорошая идея — разделить большую проблему на более мелкие, которые гораздо легче решать по отдельности. (Я рекомендую учебник Кевина Уоркмана для получения дополнительной информации).

Начнем с кода Arduino:

  1. рекомендуется очистить код: удалить закомментированный код и все неиспользуемые переменные. Это облегчит чтение/понимание/исправление. Чем меньше умственная нагрузка, тем лучше, чтобы вы могли использовать свои ресурсы для решения более сложных задач.
  2. Вы объявляете некоторые переменные (например, potValX, potValY) дважды, но в разных областях видимости (сначала как глобальные переменные, затем как локальные для loop()). Это может быть упущением, но опасность здесь заключается в затенении переменных, когда вы можете захотеть использовать глобальные переменные где-то еще, кроме loop() в вашем коде, ожидая, что они будут обновлены в loop(), однако будут обновлены только локальные переменные, не глобальные.
  3. count объявлено, но никогда не обновляется, поэтому Serial.print (count); всегда будет печатать 0. Это недосмотр/ошибка, поскольку это не состояние кнопки, и ваш эскиз не будет очищен.

Что касается кнопки, я рекомендую сначала обратить внимание на значения, выводимые в Serial Monitor, чтобы дважды проверить, выводится ли ожидаемое значение. Например, некоторые контакты Arduino Uno имеют внутренние подтягивающие резисторы, которые вы можете использовать. Для получения дополнительной информации см. Учебное пособие по подтягиванию цифрового входа Arduino. Проверьте, получаете ли вы 1 или 0 при нажатии кнопки, и соответствующим образом закодируйте остальную часть вашей программы.

Опять же, при разделении задачи вы можете начать с более простых эскизов, которые позже можно объединить в один. Например:

  1. эскиз, который просто считывает цифровой вывод кнопки и выводит необходимое значение через последовательный порт. Вы можете протестировать подтягивание (или посмотреть устранение дребезга, если хотите отправлять состояние кнопки только при изменении состояния).
  2. эскиз, который считывает и надежно отправляет данные потенциометра, затем два потенциометра
  3. эскиз, который объединяет данные кнопки и горшка в одно

Идея состоит в том, чтобы тщательно протестировать каждую программу и убедиться в отсутствии ошибок, прежде чем переходить к следующей задаче. Таким образом, вы можете меньше беспокоиться о прошлом коде. Кроме того, вы можете столкнуться с неожиданным поведением при объединении кода и добавлении одной новой вещи, которую будет легче изолировать/отладить/исправить, чем добавлять несколько вещей за один раз. Иногда медленные действия принесут результаты быстрее, чем спешка.

Вот измененная версия вашего кода Arduino с некоторыми примечаниями выше:

const int switchPin = 4;
const int potPinX = A0; // pin the port is connected to
const int potPinY = A1;

int switchVal = 0;
int potValX = 0; // variable to hold the value from the sensor
int potValY = 0 ;

void setup(){
    pinMode(LED_BUILTIN, OUTPUT) ;
    pinMode(switchPin, INPUT);
    Serial.begin(9600);
}

void loop(){
    switchVal = digitalRead(switchPin);
    potValX = analogRead(potPinX) ;
    potValY = analogRead(potPinY);
    
    Serial.print (switchVal);
    Serial.print(",");
    Serial.print (potValX);
    Serial.print(",");
    Serial.println(potValY);

    delay(1000);
 }

(Если вы хотите использовать последовательный монитор, сначала закройте p5.serialserver. Одновременно вы можете иметь только одно соединение с последовательным портом, и если p5.serialserver его использует, вы получите сообщение об ошибке «Порт занят» в последовательном мониторе Arduino (и наоборот). - наоборот)).

Это должно выводить состояние кнопки (1 или 0) и значения банка, разделенные запятыми, один раз в секунду. Это может быть то, что вы хотите, или вы можете сделать что-то вроде:

  • устраните дребезг контакта кнопки и отправляйте только команду кнопки (например, B,0\n или B,1\n при изменении состояния кнопки)
  • быстрее передавать данные банка (например, S1023,512\n и т. д.)
  • идея в том, что вы должны использовать первый символ, чтобы указать B для команды кнопки, а не S для команды ползунка. Это усложняет ваш код, и в зависимости от ваших ограничений (например, сроков и т. д.), возможно, сейчас не время исследовать этот путь (пока).

Переходим к коду p5.js:

  • уборка — отличное начало
  • isCleared закомментирован, а затем используется позже. Это создаст неожиданное поведение
  • if (isCleared == true);{
  • ; может быть случайным: его следует удалить
  • поскольку let isCleared прокомментировано, isCleared будет undefined (которое может иметь значение false при использовании ==), поэтому код внутри этого условия может никогда не быть достигнут. Похоже, вы хотите использовать isCleared, чтобы отключить кнопку в p5, чтобы очистить ее только один раз.
  • serial.on('BtnData', BtnData);BtnData не существует в p5.serialport. Возможно вы имели в виду serial.on('data', onData); ? (у вас есть только одно событие, которое возвращает новые последовательные данные, поэтому вам может потребоваться один раз проанализировать строку, а затем разделить данные на 3 части вместо 2 (например, кнопка, потенциометр1, потенциометр2)
  • serialPortName объявлен, но никогда не используется. возможно, вы хотели сначала открыть порт: serial.openPort(serialPortName); (иначе вы не получите последовательных данных). Хорошая идея — заключить это в try/catch и обработать типичные ошибки. (например, кто-то забывает подключить Arduino через USB, поэтому обрабатывает порт не найден, кто-то другой забывает закрыть последовательный монитор, поэтому обрабатывает порт занят и т. д.)
  • let sensors = [200,200,0] предлагает данные в порядке потенциометра 1, потенциометра 2 и кнопок, когда ваш Arduino сначала записывает кнопку, а затем два потенциометра
  • если вы очищаете данные только при нажатии кнопки, вы можете рисовать линию только при наличии новых данных (чтобы избежать перерисовки одной и той же линии снова и снова). (не такая уж большая проблема, но со временем могут возникнуть незначительные визуальные артефакты (из-за перекрывающихся линий))
  • Функция int() в p5.js также может принимать массив строк (с числовыми значениями), конвертировать каждую из них в int и возвращать массив. Это может быть полезно при разборе.
  • обрезка входной строки и досрочный выход при отсутствии данных — это хорошая практика: продолжайте делать это.

Вот обновленная версия вашего эскиза p5.js с использованием приведенных выше примечаний:

let serialPortName = '/dev/tty.usbserial-1130';

// line drawing vars driven by pot. data
let prevX, prevY;
// button data to clear the drawing
let isButtonPressed = false;
let wasButtonPressed = false;


function setup(){
  createCanvas(400,400);
  // setup serial
  serial = new p5.SerialPort();
  serial.list();
  serial.on('open', onOpen);
  serial.on('data', onData);
  // also handle other events
  serial.on('connected', serverConnected); // callback for connecting to the server
  serial.on('open', portOpen); // callback for the port opening
  serial.on('data', onData); // callback for when new data arrives
  serial.on('error', serialError); // callback for errors
  serial.on('close', portClose); // callback for the port closing

  serial.open(portName); // open a serial port
  
  prevX = width/2;
  prevY = height/2;
  currX = width/2;
  prevY = height/2;

  // if drawing styles don't change run once in setup()
  stroke(0);
  strokeWeight(5);
}

function draw(){
 // not much here since we're only drawing/clearing when there's new serial data 
}

// serial events
function serverConnected() {
  console.info('connected to server.');
  // if this doesn't fire probably p5.serialserver isn't running this could handled
}

function portOpen() {
  console.info('the serial port opened.')
  // good news: can expect data now
}

function serialError(err) {
  console.info('Something went wrong with the serial port. ' + err);
  // can provide mode helpful error data (e.g. depending on `err` value add suggestions for port busy vs port not found, etc.)
}

function portClose() {
  console.info('The serial port closed.');
}

function onOpen(){
  print('serial monitor opened.'); 
}

function onData(){
  let currentString = serial.readLine();
  currentString = trim(currentString);
  
  if (!currentString)return;

  let data = int(currentString);
  
  updateDrawingFromSerial(data);
}

function updateDrawingFromSerial(data){
  //basic input validation
  if (data.length < 3) return; // we expect at least 3 values
  // this can be futher improved by checking if each value is a Number
  // ...and if so, if each value is in the expected range (otherwise can use constrain())

  // handle button
  clearOnButton(data[0]);

  // handle potentiometers
  drawLineOnPots(data[1], data[2]);
}

function clearOnButton(buttonNewState){
  isButtonPressed = buttonNewState;
  // debounce
  if (isButtonPressed && !wasButtonPressed){
    background(255);
    wasButtonPressed = true;
  }
  if (!isButtonPressed && wasButtonPressed){
    wasButtonPressed = false;
  }
}

function drawLineOnPots(potX, potY){
  // remap to sketch size
  let currentX = map(potX, 0, 1023, 0, width);
  let currentY = map(potY, 0, 1023, 0, height);
  
  // draw line
  line(prevX, prevY, currentX, currentY);

  // update previous values (after drawing line)
  prevX = currentX;
  prevY = currentY;
}

Обратите внимание, что я разделил синтаксический анализ строк на несколько функций. Хотя это может сделать код немного более подробным, он инкапсулирует каждую функциональность, что упрощает отладку исправлений (поскольку только небольшие фрагменты могут пойти не так, а не одна функция с большой ошибкой), а части кода потенциально могут быть повторно использованы в новые эскизы.

Заключительные замечания:

  • дважды проверьте, что p5.serialserver использует ту же скорость передачи данных, что и ваш эскиз Arduino (в данном случае 9600).
  • если вам нужно отправлять данные быстрее, это также увеличит вероятность ошибок при последовательной связи, особенно при анализе более длинных строк. Вместо отправки 1023, когда он переназначается, например, на 512, вы можете переназначить его из Arduino, и максимальное значение («512») будет использовать максимум 3 символа вместо 4. Если вам не нужна полная 10-битная точность (0- 1024) и может обойтись с 8-битной точностью (0-255). Вероятно, вам удастся отправить всего 3 байта (кнопка, x, y) (используя readBytes()) или 4 байта, если, например, вы ограничите x,y до Значение 0–254 и используйте 255 в качестве символа терминатора (используя readBytesUntil()). В настоящее время вы можете иметь сообщение длиной 1,1023,1023\n (длиной 12 символов/байт). Это снова может быть более сложным, однако, если у вас есть время, получение опыта работы с байтами стоит потраченных времени и усилий на улучшение ваших навыков.
  • Предостережение по поводу вышеизложенного: не оптимизируйте заранее/если вам это действительно не нужно. Если чтение 12 символов один раз в секунду достаточно для эскиза (возможно, вы творчески хотите создать эскиз, который имеет ощущение старой школы, имитирующее ретро-компьютер), то не жертвуйте простотой/читабельностью кода (и, следовательно, удобством обслуживания) .

Другие вопросы по теме