У меня есть школьный проект, с которым мне нужна помощь. Я пытаюсь создать эскиз с помощью 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, чтобы два потенциометра рисовали линию по осям X и Y, а кнопка очищала рисунок при нажатии.
«кнопка должна очистить рисунок» — если бы был обзор этой «особенности*» дизайна, я бы наложил вето на эту идею и отправил ее обратно на доработку.
Рассматривали ли вы кнопку «Очистить» на холсте p5?
Благодарим за ваше предложение! Итак, сегодня я добился некоторого прогресса. Я попытался добавить кнопку очистки на холст p5. //функция buttonPressed(){ // if (sensors[2] == 1){ // isCleared = true;
Это хорошо написанный вопрос:
Ниже приведены полезные советы о том, как улучшить ваш код, а также общие советы, которым следует следовать, помимо этого вопроса.
Вы решаете две проблемы одновременно:
Хорошая идея — разделить большую проблему на более мелкие, которые гораздо легче решать по отдельности. (Я рекомендую учебник Кевина Уоркмана для получения дополнительной информации).
Начнем с кода Arduino:
potValX
, potValY
) дважды, но в разных областях видимости (сначала как глобальные переменные, затем как локальные для loop()
). Это может быть упущением, но опасность здесь заключается в затенении переменных, когда вы можете захотеть использовать глобальные переменные где-то еще, кроме loop()
в вашем коде, ожидая, что они будут обновлены в loop()
, однако будут обновлены только локальные переменные, не глобальные.count
объявлено, но никогда не обновляется, поэтому Serial.print (count);
всегда будет печатать 0. Это недосмотр/ошибка, поскольку это не состояние кнопки, и ваш эскиз не будет очищен.Что касается кнопки, я рекомендую сначала обратить внимание на значения, выводимые в Serial Monitor, чтобы дважды проверить, выводится ли ожидаемое значение. Например, некоторые контакты Arduino Uno имеют внутренние подтягивающие резисторы, которые вы можете использовать. Для получения дополнительной информации см. Учебное пособие по подтягиванию цифрового входа Arduino. Проверьте, получаете ли вы 1 или 0 при нажатии кнопки, и соответствующим образом закодируйте остальную часть вашей программы.
Опять же, при разделении задачи вы можете начать с более простых эскизов, которые позже можно объединить в один. Например:
Идея состоит в том, чтобы тщательно протестировать каждую программу и убедиться в отсутствии ошибок, прежде чем переходить к следующей задаче. Таким образом, вы можете меньше беспокоиться о прошлом коде. Кроме того, вы можете столкнуться с неожиданным поведением при объединении кода и добавлении одной новой вещи, которую будет легче изолировать/отладить/исправить, чем добавлять несколько вещей за один раз. Иногда медленные действия принесут результаты быстрее, чем спешка.
Вот измененная версия вашего кода 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
и т. д.)Переходим к коду 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 с использованием приведенных выше примечаний:
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;
}
Обратите внимание, что я разделил синтаксический анализ строк на несколько функций. Хотя это может сделать код немного более подробным, он инкапсулирует каждую функциональность, что упрощает отладку исправлений (поскольку только небольшие фрагменты могут пойти не так, а не одна функция с большой ошибкой), а части кода потенциально могут быть повторно использованы в новые эскизы.
Заключительные замечания:
readBytes()
) или 4 байта, если, например, вы ограничите x,y до Значение 0–254 и используйте 255 в качестве символа терминатора (используя readBytesUntil()
). В настоящее время вы можете иметь сообщение длиной 1,1023,1023\n
(длиной 12 символов/байт). Это снова может быть более сложным, однако, если у вас есть время, получение опыта работы с байтами стоит потраченных времени и усилий на улучшение ваших навыков.
Картинка может стоить тысячи слов, но схема передает гораздо больше информации, чем несколько фотографий макета и Arduino. Сжатый и нечитаемый скриншот практически ничего не стоит. Вы не предоставляете какое-либо описание реализации, то есть схемы, какие данные собираются, как данные преобразуются/форматируются и отправляются из Arduino в загадочную систему. Есть минимальные указания на тестирование/отладку подсистемы. У вас может легко возникнуть не одна, а несколько проблем, требующих решения.