分享

实验室

 青樓滿座wu0pn9 2018-08-08

                                 实验室:

最初由Tom Igoe
2015年10月4日撰写。最后由Tom Igoe 于2017年10月11日修改

概观

内容[ 显示 ]

在浏览器中与网页进行串行通信并不是您每天都能看到的网络浏览器通常无法访问计算机的串行端口为了使基于浏览器的应用程序与微控制器串行通信,您需要一个既可以提供HTML / JavaScript的页面又可以与串行端口通信的程序当您使用P5.js制作项目时,您可以通过使用P5.serialport库和P5.serialserver以及肖恩·范·每一个P5.serialcontrol应用程序此实现目的当您使用p5.serialport库时,它与serialserver(一个用的node.js编写的网页套接字服务器)通信 允许您访问连接到计算机的串行设备本实验室向您展示如何做到这一点在本实验中,您将从电位计生成模拟输出值,然后通过异步串行通信将该值发送到P5 .js文件。您将在P5.js中使用该值来绘制图形。

为了充分利用本教程,您应该知道微控制器是什么以及如何对它们进行编程您还应该了解微控制器和个人计算机之间的异步串行通信您还应该了解P5.js的基础知识从Arduino到p5.js的连续视频可能很有用。

你需要的东西

在本实验中,您将需要以下硬件,并且您需要下载P5.js完整库您还需要安装node.jsP5.serialserver(下面的说明)。您可以使用P5.js Web编辑器或您喜欢的文本编辑器(Atom文本编辑器工作正常)。

Arduino的 电位器
Arduino微控制器 10千欧电位器

组装电路

组装以下电路。它与模拟输入实验室的电路相同

将面包板上的电源和接地连接到微控制器的电源和接地。在Arduino模块上,使用5V和任何接地连接:

LabTemplate_bb
Fritzing制成

添加电位器

将电位器连接到模块的引脚0中的模拟:

Related Video: Potentiometer schematic

Potentiometer and LED circuit
Arduino with Potentiometer circuit Same circuit on a breadboard

Program the Microcontroller

Program your Arduino to read the analog input as follows:

void setup() {
 Serial.begin(9600); // initialize serial communications
}

void loop() {
 int potentiometer = analogRead(A0);                  // read the input pin
 int mappedPot = map(potentiometer, 0, 1023, 0, 255); // remap the pot value to fit in 1 byte
 Serial.write(mappedPot);                             // print it out the serial port
 delay(1);                                            // slight delay to stabilize the ADC
}

The P5.js serialport library

To communicate with your microcontroller serially, you’re going to use the P5.js serialport library and the P5.serialserver (command line version) or p5.serialcontrol (GUI version). The P5.js serialport library can’t access your serial ports directly when a sketch is running in a browser because the browser doesn’t have direct access to the serial port. But it can communicate with a server program on your computer that can exchange data with the serialport.P5.serialserver (or p5.serialcontrol) is  the server that connects your sketch, running in a browser, with the serial ports on your computer as shown below:

Once you gain an understanding of serial communication, you can use any program that can connect with your computer’s serial ports to communicate with a microcontroller. Processing, Max/MSP, and OpenFrameworks are three other popular multimedia programming environments that can communicate via the serial ports.

Install the P5.serialcontrol App

Download the latest version of the P5.serialcontrol application and save it in your Applications folder. When you run it, it will check serial ports on your machine. Click “list ports” to refresh the list, and you should see the serial port of your Arduino board show up. You can now move onto using the P5.js editor making sure that the p5.serialport.js file is inside your project folder. You can skip the next section unless you want to run p5.serialserver from the command line instead from the p5.serialcontol app.

Running P5.serialserver in commandline

The P5.serialcontrol app runs P5.serialserver with a GUI wrapped around it. If you don’t want to use the p5.serialcontrol GUI, you can install p5.serialserver on the command line.  P5.serialserver uses the node.js programming environment to create the serial-to-webSocket server that allows you to connect your sketch to the serial port. If you’re interested in learning to write your own serial-to-webSocket server, you can see the Serial Communication with Node.js lab. In that lab, you’ll write your own serial-to-webSocket server.

You will need to install node.js on your computer to use p5.serialserver on the command line. Download the version for your operating system from and install it as you would any other application. Then open a command line interface (Terminal on OSX, Windows Powershell on Windows) and install P5.serialserver by typing the following:

sudo npm install -g p5.serialserver

The installer will ask for your password, because it’s installing serialserver globally, for all users. Windows users will not need sudo at the beginning of the command. Once the server is successfully installed, you can run it by typing

p5serial

You’ll see the following message:

p5.serialserver is running

You won’t see much else unless there’s a problem, but the program is running. To quit it, type control-C.

Program P5.js to list the available  serial ports

Now you’re ready to make a P5.js sketch. If you’re using the p5.js web editor, make a new sketch. Download this file: p5.serialport.js. Then add it to your sketch by clicking the file navigation arrow on the left of the screen, then click the down arrow to add files. Choose the downloaded p5.serialport.js file and upload it to your sketch.

p5_editor_file_nav_screenshot p5_editor_add_file_screenshot
p5 editor showing file navigation arrow p5 editor showing add file arrow

Whether you are running the P5.serialserver via command line or using the P5.serialcontrol app, you will need to add one line to the index.html file. After this line:

<script language="javascript" type="text/javascript" src="libraries/p5.js"></script>

Add this line:

 <script language="javascript" type="text/javascript" src="p5.serialport.js"></script>

Save index.html. Now you’re ready to edit the sketch.

To start off, your programming environment needs to know what serial ports are available in the operating system. Open the sketch.js file and change it to the following:

var serial; // variable to hold an instance of the serialport library

function setup() {
 serial = new p5.SerialPort(); // make a new instance of the serialport library
 serial.on('list', printList); // set a callback function for the serialport list event

 serial.list(); // list the serial ports
}

// get the list of ports:
function printList(portList) {
 // portList is an array of serial port names
 for (var i = 0; i < portList.length; i++) {
 // Display the list the console:
 println(i + " " + portList[i]);
 }
}

When you run this P5 sketch in a browser, you’ll get a list of the available serial ports in the console. This list will look just like the list of serial ports you see in the Arduino Tools menu. Find the name of your port in the list. Later, you’ll assign that name to a global variable called portName.

 Serial events

JavaScript, the language on which P5.js is based, relies heavily on events and callback functions. An event is generated by the operating system when something significant happens, like a serial port opening, or new data arriving in the port. In your sketch, you write a callback function to respond to that event. The serialport library uses events and callback functions as well. It can listen for the following serialport events:

  • list – the program asks for a list of ports.
  • connected – when the sketch connects to a webSocket-to-serial server
  • open – a serial port is opened
  • close – a serial port is closed
  • data – new data arrives in a serial port
  • error – something goes wrong.

You’re already using a callback for the ‘list’ event in the code above. You set a callback for the ‘list’ event, then you called it with serial.list(). Generally, you should set your callbacks before you use them like this.

To use the rest of the serialport library’s events, you need to set callback functions for them as well. Add a new global variable called portName and initialize it with the name of your serial port. Then change your setup() function to include callbacks for open, close, and error like so:

var serial;          // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1421';  // fill in your serial port name here

function setup() {
  serial = new p5.SerialPort();       // make a new instance of the serialport library
  serial.on('list', printList);  // set a callback function for the serialport list event
  serial.on('connected', serverConnected); // callback for connecting to the server
  serial.on('open', portOpen);        // callback for the port opening
  serial.on('data', serialEvent);     // callback for when new data arrives
  serial.on('error', serialError);    // callback for errors
  serial.on('close', portClose);      // callback for the port closing

  serial.list();                      // list the serial ports
  serial.open(portName);              // open a serial port
}

Notice the final line of the setup(). It’s going to generate an ‘open’ event, which will be handled by a function called portOpen().

Wait a minute! Don’t I have to set the data rate when I open the port?

In asynchronous serial communications, both computers have to set the same data rate in order to communicate. In Arduino, you set the data rate with Serial.begin(9600);. In P5.js, 9600 bits per second is the default, so you don’t have to set the rate if you want 9600bps. But if you want to set the rate to another value, do it like this:

  var options = { baudrate: 9600}; // change the data rate to whatever you wish
  serial.open(portName, options);

Now add new functions to respond to the callbacks you just declared. These come after your setup() function:

function serverConnected() {
  println('connected to server.');
}

function portOpen() {
  println('the serial port opened.')
}

function serialEvent() {

}

function serialError(err) {
  println('Something went wrong with the serial port. ' + err);
}

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

Most of these functions just provide notification. The error function is a bit more useful because it tells you what went wrong if something went wrong. If you didn’t change the portName variable to the name of your microcontroller’s serial port, for example, you probably got this error in the command line window where P5.serialserver is running:


Something went wrong with the serial port. Couldn't open port: /dev/cu.usbmodem1421

The function that matters the most, though, is serialEvent(), the one that responds to new data. Each time a new byte arrives in the serial port, this function is called. Now it’s time to make serialEvent() do some work. Add a new global variable at the top of your sketch called inData like so:

var serial;          // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1421';  // fill in your serial port name here
var inData;                             // for incoming serial data

Then modify the serialEvent() function like so:

function serialEvent() {
  inData = Number(serial.read());
}

Although you’re reading the incoming data, you’re not displaying it anywhere yet. Add a draw() function and print the sensor value to the screen. Start by adding a createCanvas() to the top of your setup() like so:

function setup() {
  createCanvas(400, 300);

Then here’s your draw() function:

function draw() {
  background(0);
  fill(255);
  text("sensor value: " + inData, 30, 30);
}

When you run your sketch now, you should get something like this:

readSerial-截图

The sensor value onscreen should change as you turn your potentiometer. Congratulations! You’ve got P5.js talking to your microcontroller.

What’s Happening Here

Every time your microcontroller sends a byte serially using Serial.write(), the computer receives it and generates a ‘data’ event. Then your serialEvent() function is called. It reads the byte as a number, and stores it in the global variable inByte. The draw() method just uses the latest value of inByte in the text string it displays on the screen.

You may be wondering why you’re mapping the sensor value or dividing it by 4 in the Arduino sketch above. That’s because in order to send the sensor value as a single byte, it must be between 0 and 255, or no more than 28 bits.

P5.js println() and Arduino delay(): a Tricky Combination

In testing this, you may have put a println() statement in the serialEvent() function in your P5.js sketch. When you did, you would have noticed that it causes a lag in the sketch, and the println() statements continue even after you stop the sketch. This is because the operating system keeps the incoming serial data in a buffer, and P5.js isn’t reading and printing it as fast as Arduino is sending it.

You might think, “Okay, then I’ll just put a delay() in my Arduino sketch to slow it down.” That’s a bad idea. When you put in a delay, it means you’re only reading your sensor when that delay is not running.  You can miss critical sensor events while that delay is in progress. Even a relatively small delay, for example 30ms, can make it difficult to reliably read state changes in a switch or peaks in an analog sensor. Don’t use delays if you can avoid it. For more on how to handle the flow of serial data from Arduino to P5.js and back, see the Duplex Serial Flow in P5.js lab.

Draw a Graph With the Sensor Values

It would be useful to see a graph of the sensor values over time. You can do that by modifying the draw() method to draw the graph. To do this, add a new global variable at the top of your position called xPos. You’ll use this to keep track of the x position of the latest graph line:

var serial;          // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1421';  // fill in your serial port name here
var inData;                             // for incoming serial data
var xPos = 0;                           // x position of the graph

Because of the way the graphing function below works, you can’t reset the background every time through the draw() loop. So take the background() command and put it in the setup() function instead of the draw(), as shown below. That way it runs once, then not again. As long as you’re at it, switch from black & white to a nice blue color:

function setup() {
  createCanvas(400, 300);
  background(0x08, 0x16, 0x40);

Now make a new function called graphData(). It’ll take a number value as a parameter, and it will draw a line on the screen that’s mapped to the number value. Then it will increment xPos so that the next line is drawn further along. It will also check if the xPos is at the right edge of the screen, and reset the screen by calling background() again if it is:

function graphData(newData) {
  // map the range of the input to the window height:
  var yPos = map(newData, 0, 255, 0, height);
  // draw the line in a pretty color:
  stroke(0xA8, 0xD9, 0xA7);
  line(xPos, height, xPos, height - yPos);
  // at the edge of the screen, go back to the beginning:
  if (xPos >= width) {
    xPos = 0;
    // clear the screen by resetting the background:
    background(0x08, 0x16, 0x40);
  } else {
    // increment the horizontal position for the next reading:
    xPos++;
  }
}

Finally, take everything out of the draw() function and just call graphData() from there:

function draw() {
  graphData(inData);

When you run the sketch now, you should get a graph, like so:

图的屏幕截图

Try changing the Arduino sketch by adding delay(100); at the end of the loop. Note how it changes what happens in the graph.

Reading Serial Data as a String

This works well if you want to read your sensor values as a single byte, but what if you want a larger range of numbers?  What if you want the full 0 to 1023 that analogRead() can output instead of just 0 to 255?  To do this, you need to send the data as an ASCII-encoded numeric string from the microcontroller, and you need to read and interpret the incoming data in P5 as an ASCII-encoded numeric string as well.

Change your Arduino program to the following:

void setup() {
 Serial.begin(9600); // initialize serial communications
}

void loop() {
 int potentiometer = analogRead(A0);                  // read the input pin
 int mappedPot = map(potentiometer, 0, 1023, 0, 255); // remap the pot value to fit in 1 byte
 Serial.println(mappedPot);                           // print it out the serial port
 delay(1);                                            // slight delay to stabilize the ADC
}

Now it will print the potentiometer’s value as an ASCII-encoded numeric string, and it will add a carriage return byte and a newline byte at the end, because that’s what println() does.

Once you’ve uploaded this to your Arduino, run your P5 sketch again. Try adding println(inData); at the end of your serialEvent() function. When your P5 sketch reads the data from this Arduino program, you get very low values, and every so often you see the value 10 followed by the value 13. What’s going on?

When a computer ASCII-encodes a number, it converts that number to a string of bytes, each of which is the ASCII value for a numeral in the number. For example, the number 865 gets converted to three bytes, like so:

ASCII的标号

If there’s a carriage return byte and a newline byte after this, the string is five bytes, and the last two bytes’ values are 13 (carriage return, or \r in most programming languages) and 10 (newline or \n in most programming languages), respectively.

Your P5.js sketch is reading every byte’s value and graphing it. That’s why you get a graph of very low values, with a bunch of them being 13 and 10. The Arduino is ASCII-encoding the potentiometer values, but the P5 sketch is interpreting the bytes as if they’re not encoded that way.

Now change the serialEvent() function like so:

function serialEvent() {
  // read a byte from the serial port, convert it to a number:
  inData = serial.readLine();
}

Run it again. What’s changed? Now you’re getting a graph kind of like you were before. The serial.readLine(); command reads the incoming serial data as a string, and when that string happens to be all-numeric, it converts it to a number. So you’re getting the ASCII-encoded string as a number again. But now there are gaps. Why?

Remember, the ‘data’ event occurs every time a new byte comes in the serial port. Now that you’re sending an ASCII-encoded string, every potentiometer reading is several bytes long. So you only get a complete string every three to six bytes (three for “0\r\n” and six for “1023\r\n”). Sometimes, when the serialEvent() function calls serial.readLine(); it gets nothing. That’s when draw() draws the gaps. You need to change your function to check that the resulting string is actually a valid number before you put the string into inData. First, create a local variable to get the string, then check to see if the string’s length is greater than zero. If it is, then put it into inData so that the other functions in the sketch can use the new data. Here’s how you do that:

function serialEvent() {
  // read a string from the serial port:
  var inString = serial.readLine();
  // check to see that there's actually a string there:
  if (inString.length > 0 ) {
  // convert it to a number:
  inData = Number(inString);
  }
}

Now you’re able to send in a number of any value to P5.js. You don’t have to limit your input to a 0-255 value range. See if you can modify the Arduino sketch and the P5.js sketch to exchange a potentiometer value that spans the whole range from 0 to 1023.

note: readLine() is the same as readStringUntil(‘\r\n’);

The full code for all the examples in this lab can be found in this gitHub repository.

Conclusion

在本实验中,您了解了如何使用webSocket-to-serial服务器,P5.serialserver和P5.serialport库将Arduino微控制器连接到P5.js草图。您将Arduino中的数据作为原始二进制值(即0到255之间的单个字节)发送到草图,并将其作为ASCII编码的数字字符串发送,并在末尾添加回车符和换行符。了解ASCII编码字符串和原始二进制数据之间的区别是所有串行通信的核心。有关此操作的更多示例,请参阅P5.js实验室串行输出

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多