Taillieu.Info

More Than a Hobby..

Stopping SD Card Corruption on Raspberry Pi’s Raspbian

The following are instructions for minimizing SD card writes for Raspberry Pi’s “Raspbian” Distribution.

If you’re like me, you’ve run into a corrupted SD card too many times to not become hell-bent on making it never happen again. I have the following setup, and it seems to be working well for me.

The biggest offender for Filesystem writes on any linux system is logging. If you are like me, you don’t really look at /var/log after a recycle anyways. This area, and /var/run, a location where lock files, pid files and other “stuff” shows up, are the most common areas for mess-ups. Take a look at your blinking FS light on the board. Our goal is to make that light stay off as long as possible.

Set up tmpfs mounts for worst offenders. Do other tweaks.

Linux has with it the concept of an in-memory filesystem. If you write files to an in-memory filesystem, they will only exist in memory, and never be written to disk. There are two common mount types you can use here: ramfs, which will continue to eat memory until your system locks up (bad), and tmpfs, which sets a hard upper limit on mount size, but will swap things out if memory gets low (bad for raspberry pi, you will probably be hard stopping your device if it is low on memory).

We will first solve the usual corruption culprit and then move on to making sure we are covered when our programs decide to blow up.

The following two lines should be added to /etc/fstab:

none        /var/run        tmpfs   size=1M,noatime         00
none        /var/log        tmpfs   size=1M,noatime         00

UPDATE (unverified): I have been told that /var/run is now a symlink to a tmpfs filesystem, anyways, so you may not need to add /var/run anymore, and adding it may cause issues.

There’s more, however. By default, linux also records when a file was last accessed. That means that every time you read a file, the SD card is written to. That is no good! Luckily, you can specify the “noatime” option to disable this filesystem feature. I use this flag generously.

Also, for good measure, i set /boot to read-only. There’s really no need to regularly update this, and you can come back here and change it to “defaults” and reboot when you need to do something.

After this, /etc/fstab should look as follows:

proc            /proc               proc    defaults                    0   0
/dev/mmcblk0p1  /boot               vfat    ro,noatime                  0   2
/dev/mmcblk0p2  /                   ext4    defaults,noatime            0   1
none            /var/run            tmpfs   size=1M,noatime             0   0
none            /var/log            tmpfs   size=1M,noatime             0   0

UPDATE (unverified): I have been told that /var/run is now a symlink to a tmpfs filesystem, anyways, so you may not need to add /var/run anymore, and adding it may cause issues.

Go ahead and reboot now to see things come up. Check the Filesystem light on your raspberry pi after it’s fully booted. You should see no blinking at all.

Disable swapping

As a note, since i have done the changes above, i have not corrupted an SD card. I’m not saying I’ve tried very hard, but it is much better, even with power plug pulls, which i tried a few of after doing these changes.

One protection against SD card corruption is an optional, but potentially “I’m glad i did that” change to disable swapping.

The raspberry pi uses dphys-swapfile to control swapping. It dynamically creates a swap partition based on the available RAM. This tool needs to be used to turn off swap, and then needs to be removed from startup.

Run the following commands to disable swapping forever on your system:

sudo dphys-swapfile swapoff
sudo dphys-swapfile uninstall
sudo update-rc.d dphys-swapfile remove

After doing this, call free -m in order to see your memory usage:

pi@raspberrypi ~ $ free -m
             total       used       free     shared    buffers     cached
Mem:           438         59        378          0          9         27
-/+ buffers/cache:         22        416
Swap:            0          0          0

If you reboot, and run a free -m again, you should still see swap at 0. Now we don’t have to worry about tmpfs filesystems swapping out to hard disk!

Housekeeping!

As you go on and install other tools and frameworks on your raspberry pi (like ROS), you need to be aware of where caches and logfiles are written to. If, for example, you use ROS, the nodes will, by default, log to ~/.ros/log/. For these sorts of things, and for ROS in particular, point these logs to a folder on /dev/shm. This mount is created by default on linux boxes, and is a tmpfs filesystem that is globally writable by default. Mine has 88 MB.
For example, pointing ROS logs to this file could be done by adding the following to your .bashrc:

export ROS_LOG_DIR=/dev/shm/rosLogs

If you feel like creating more mounts, feel free, but I have run into ownership issues that required having another script on startup that had to be run as root to chown directories to their proper owners.

Getting to 100%

The only way to fully protect against SD card corruption is to mount your root filesystem as readonly. For me, this was too much of a usability issue. I am editing files and installing new packages too often for this to be feasible. The steps listed above should, however, cover you 98% of the time. Try not to pull power while you’re editing files or installing new packages on your device, and you should be fine. Still make backup images of your SD card every once in a while! This is a best practice no matter what!

Happy Hacking!

Use the Adafruit PCA9685 with a Raspberry PI

Use the Adafruit PCA9685 with a Raspberry PI, in Java Raspberry PI
to drive up to 16 servos

The Servo Driver we are talking about here is the Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685.

The original tutorial from Adafruit is available here. The language it uses is Python. 

This board has an I2C (pronounce I "square" C) interface, that needs to be enabled on the Raspberry PI. 
I2C stands for Inter-Integrated-Circuit. 
Arduino & Adafruit provide python libraries that go along with their components. The code we present here is in big part an adaptation of this code.

Enable I2C on the Raspberry PI

We will show how to enable I2C on a Model B Raspberry PI. There is a lot of documentation on the web on how to do it on whatever version on the RasPI you have. We are not going to duplicate the web in this document... 
We will need to edit several configuration files. Use the editor you prefer (nanovigedit, ...), as root (sudo).

  1. Edit /etc/modules 
    Make sure its content eventually looks like this:
      i2c-bcm2708
      i2c-dev
              
    Save your modifications, if you have done any.
  2. Install some I2C Utilities (in python) 
     Prompt> sudo apt-get install python-smbus
     Prompt> sudo apt-get install i2c-tools
              
  3. Edit /etc/modprobe.d/raspi-blacklist.conf, comment the two lines it contents: 
    Before:
     blacklist spi-bcm2708
     blacklist i2c-bcm2708
              

    After:
     # blacklist spi-bcm2708
     # blacklist i2c-bcm2708
              
    Save your modifications, if you have done any.
  4. By now (the PCA9685 needs to be wired for that), you should be able to run the i2cdetect utility, which will will use to know the address of the servo driver:
     Prompt> sudo i2cdetect -y 1
          0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
     00:          -- -- -- -- -- -- -- -- -- -- -- -- --
     10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     70: 70 -- -- -- -- -- -- --
              
    As seen above, the I2C addresses range from 0x03 to 0x77 (binary 0000011 to 1110111). This way, several boards can be used simultaneously from the RasPI, as long as their I2C addresses are different, even if the GPIO interface pins are connected to several different boards. 
    For example, if you have connected
    • A PCA9685 (servo driver)
    • A BMP180 (Pressure, Altitude, Temperature)
    • A LSM303 (Magnetometer & Accelerometer)
    you would have
     Prompt> sudo i2cdetect -y 1
          0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
     00:          -- -- -- -- -- -- -- -- -- -- -- -- --
     10: -- -- -- -- -- -- -- -- -- 19 -- -- -- -- 1e --
     20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     70: 70 -- -- -- -- -- -- 77
              
    where, in addition to the above,
    • 0x19 would be the magnetometer address (LSM303)
    • 0x1e would be the accelerometer address (LSM303)
    • 0x77 would be the BMP180 address

Wiring

The connection setting is quite simple.

PCA9685Raspberry PI
VCC 1. 3.3 VDC Power
GND 6. 0V (Ground)
SDA 3. SDA0 (I2C)
SCL 5. SCL0 (I2C)
Raspberry PI P1 Connector map Click to enlarge Click to enlarge 
The Raspberry PI, connected to a PCA9685, with 2 servos.

This diagram shows the name of the pins you want to use on the GPIO Connector of the Raspberry PI. As we said before, we need to use the pins 1, 3, 5, and 6.

Programming

You will need to know what address to talk to to reach the PCA9685. Run the i2cdetect utility.

 Prompt> sudo i2cdetect -y 1
      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
 00:          -- -- -- -- -- -- -- -- -- -- -- -- --
 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 70: 70 -- -- -- -- -- -- --
      

We will use the 0x40 address to reach the PCA9685. 
The Java code we are interested in is in the class named adafruiti2c.AdafruitPCA9685. Check it out in the repository. We are going to comment only on the main method of this class, which is a basic example. It assumes that 2 servos are connected to the PCA9685, on the 15 and 14 slots.
The PCA9685 has 16 such slots, numbered - on the board - from 0 to 15. See them on the picture at the top of this document. The slots we are talking about are the 16 columns of 3 holes drilled at the bottom of the board. Their number is printed on top of each column of 3 holes.

  ...
  public static void main(String[] args)
  {
    AdafruitPCA9685 servoBoard = new AdafruitPCA9685();   // 0x40 is the default address
    servoBoard.setPWMFreq(60); // Set frequency to 60 Hz
    int servoMin = 150;   // Min pulse length out of 4096
    int servoMax = 600;   // Max pulse length out of 4096
    
    final int CONTINUOUS_SERVO_CHANNEL = 14;
    final int STANDARD_SERVO_CHANNEL   = 15;
    
    for (int i=0; i<10; i++)
    {
      System.out.println("i=" + i);
      servoBoard.setPWM(STANDARD_SERVO_CHANNEL,   0, servoMin);
      servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, servoMin);
      waitfor(1000);
      servoBoard.setPWM(STANDARD_SERVO_CHANNEL,   0, servoMax);
      servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, servoMax);
      waitfor(1000);
    } 
    servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, 0); // Stop the continuous one
    System.out.println("Done with the demo.");
  }
  ...
      

This code loops 10 times and makes the servos go from a value of 150 to a value of 600, and vice versa. It waits for 1000 milliseconds between each message to the servos. 
Notice the two int variables, CONTINUOUS_SERVO_CHANNEL and STANDARD_SERVO_CHANNEL
For this demo, the standard servo we use is the Standard servo - TowerPro SG-5010 - 5010
As seen in its documentation, the servo rotates on 180°, 90° on each side. Position "0" (1.5ms pulse) is middle, "90" (~2ms pulse) is all the way to the right, "-90" (~1ms pulse) is all the way to the left. 
We see we use a 60 Hz frequency. This means we will have 60 cycles per second. Each cycle contains 4096 ticks. 
The value minServo is 150. 
The value maxServo is 600. 
A full cycle (of 4096 ticks) at 60 Hz will take 1 / 60 ~ 16 ms. 
150 ticks would take 150 / (60 * 4096) seconds, which is ~ 0.61 ms. 
600 ticks would take 600 / (60 * 4096) seconds, which is ~ 2.44 ms. 

More generically

  • TickTime = 1 / (freq * 4096).
  • PulseTime = TickTime * nbTicks.

It is quite easy - and convenient - to put those formulas in a spreadsheet. 
Based on those formulas, we should have:

Pulse Width
in ms
Nb Ticks
1 246
1.5 369
2 492

The values given in the Adafruit tutorial (150 & 600) happens to be a bit different of what the documentation of the servo says..., but they actually work fine, and this is the ones we use in the code:

Pulse Width
in ms
Nb Ticks
1 150
1.5 375
2 600

Running

We run the code from a script named servo:

 #!/bin/bash
 PI4J_HOME=/home/pi/pi4j/pi4j-distribution/target/distro-contents
 CP=./classes
 CP=$CP:$PI4J_HOME/lib/pi4j-core.jar
 sudo java -cp $CP adafruiti2c.AdafruitPCA9685
      
 Prompt> ./servo
      

And here is the result

 Prompt> ./servo
 Connected to bus. OK.
 Connected to device. OK.
 Setting PWM frequency to 60 Hz
 Estimated pre-scale: 100.72526
 Final pre-scale: 101.0
 i=0
 i=1
 i=2
 i=3
 i=4
 i=5
 i=6
 i=7
 i=8
 i=9
 Done with the demo
 Prompt>
              
The console output

And what next?

Drive the Raspberry PI, on its own cart... 
Fuel the Raspberry PI with a solar panel, oriented by servos to face the sun for greater efficiency.

Something to be aware of...

I followed the PWM instructions, came up with several spreadsheets for the calculations, and... it just did not work correctly. 
Both standard and continuous servos are supposed to be triggered with pulses having width ranging from 1 to 2 ms. As seen above, at 60 Hz, that makes 246 to 492 ticks. 
In reality, after many manual trials, for a frequence of 60 Hz, I used:

For the Standard Servo Between 122 & 615 ticks
For the Continuous Servo Between 340 & 410 ticks, stop at 375

Those values are suprisingly different from the theorical ones..., but they work. 
See in the code the classes named adafruiti2c.samples.DemoContinuous and adafruiti2c.samples.DemoStandard
Those are the ones I will use as bases for robotic projects.

Serial Port Sample

Serial Port Sample

This sample has been verified to work with the following configuration:

Windows 10 IoT Core: version 10.0.10586.0Visual Studio: Visual Studio 2015 update 1Windows SDK: version 10586 (Included with Visual Studio Update 1. Check Visual Studio install options)

We’ll create a simple app that allows communication between a desktop and an IoT device over a serial interface.

This is a headed sample. To better understand what headed mode is and how to configure your device to be headed, follow the instructions here.

Load the project in Visual Studio

You can find the C# and C++ versions of this sample here. Make a copy of the folder on your disk and open the project from Visual Studio.

This app is a Universal Windows app and will run on both the PC and your IoT device.

Wiring the serial connection

You have two options for wiring up your board:

  1. using the On-board UART controller
  2. using a USB-to-TTL adapter cable such as this one

On-board UART (MinnowBoard Max)

The MinnowBoard Max has two on-board UARTs. See the MBM pin mapping page for more details on the MBM GPIO pins.

  • UART1 uses GPIO pins 6, 8, 10, and 12.
  • UART2 uses GPIO pins 17 and 19.

In this sample we will use UART2.

Make the following connections:

  • Insert the USB end of the USB-to-TTL cable into a USB port on the PC
  • Connect the GND wire of the USB-to-TTL cable to Pin 1 (GND) on the MBM board
  • Connect the RX wire (white) of the USB-to-TTL cable to Pin 17 (TX) on the MBM board
  • Connect the TX wire (green) of the USB-to-TTL cable to Pin 19 (RX) on the MBM board

Note: Leave the power wire of the USB-to-TTL cable unconnected.

On-board UART (Rasperry Pi2)

The Rasperry Pi2 has one on-board UART. See the Raspberry Pi 2 Pin Mappings page for more details on the MBM GPIO pins.

  • UART0 uses GPIO pins 6 (GND), 8 (TX) and 10 (RX).

Make the following connections:

  • Insert the USB end of the USB-to-TTL cable into a USB port on the PC
  • Connect the GND wire of the USB-to-TTL cable to Pin 6 (GND) on the RPi2 board
  • Connect the RX wire (white) of the USB-to-TTL cable to Pin 8 (TX) on the RPi2 board
  • Connect the TX wire (green) of the USB-to-TTL cable to Pin 10 (RX) on the RPi2 board

Note: Leave the power wire of the USB-to-TTL cable unconnected.

On-Board UART (DragonBoard 410c)

The DragonBoard has two on-board UARTs.

  • UART0 uses GPIO pins 3, 5, 7, and 9.
  • UART1 uses GPIO pins 11 and 13.

In this sample, UART1 will be used. Make the following connections:

  • Insert the USB end of the USB-to-TTL cable into a USB port on the PC
  • Connect the GND wire of the USB-to-TTL cable to pin 1 (GND)
  • Connect the RX wire (white) of the USB-to-TTL cable to pin 11 (UART1 TX)
  • Connect the TX wire (green) of the USB-to-TTL cable to pin 13 (UART1 RX)

NOTE: Leave the power wire of the USB-to-TTL cable unconnected.

Using USB-to-TTL Adapter

Note: Only USB-to-TTL cables and modules with Silicon Labs chipsets are natively supported on MinnowBoard Max and Raspberry Pi2.

You will need:

Make the following connections:

  • Insert the USB end of the USB-to-TTL cable into a USB port on the PC

  • Insert the USB end of the USB-to-TTL module into a USB port on the RPI2 or MBM device

  • Connect the GND pin of the USB-to-TTL module to the GND wire of the USB-to-TTL cable

  • Connect the RX pin of the USB-to-TTL module to the TX wire (green) of the USB-to-TTL cable

  • Connect the TX pin of the USB-to-TTL module to the RX wire (white) of the USB-to-TTL cable

Leave the power pin of the USB-to-TTL cable unconnected. It is not needed.

Below is an image of our USB-to-TTL module connected to a USB port in our RPi2. The GND, TX, and RX pins of the module are connected to the GND, RX, TX wires of the USB-to-TTL cable that is connected to our PC.

Deploy and Launch the SerialSample App

Now that our PC and RPi2 or MBM are connected, let’s setup and deploy the app. If you are not familiar with how to set the target device and target architecture in Visual Studio see this section for details.

  1. Download the Visual Studio 2015 SerialSample source project.

  2. Make two separate copies of the app. We’ll refer to them as the ‘Device copy’ and ‘PC copy’.

  3. Open two instances of Visual Studio 2015 on your PC. We’ll refer to these as ‘Instance A’ and ‘Instance B’.

  4. Open the Device copy of the SerialSample app in VS Instance A.

  5. Open the PC copy of the SerialSample app in VS Instance B.

  6. In VS Instance A, configure the app for deployment to your RPi2 or MBM device)

    *For RPi2, set the target device to ‘Remote Machine’ and target architecture to ‘ARM’

    *For MBM, set the target device to ‘Remote Machine’ and target architecture to ‘x86’

  7. In VS Instance B, set the target architecture to ‘x86’. This will be the instance of the sample we run on the PC.

  8. In VS Instance A, press F5 to deploy and launch the app on your RPi2 or MBM.

  9. In VS Instance B, press F5 to deploy and launch the app on your PC.

Using the SerialSample App

When the SerialSample app is launched on the PC, a window will open with the user interface similar to the screenshot shown below. When launched on the RPi2 and MBM, the SerialSample will display the user interface shown below on the entire screen.

Selecting a Serial Device

When the SerialSample app launches, it looks for all the serial devices that are connected to the device. The device ids of all the serial devices found connected to the device will be listed in the top ListBox of the SerialSample app window.

Select and connect to a serial device on the PC and RPi2 or MBM by doing the following:

  1. Select the desired serial device by clicking on the device ID string in the top ListBox next to “Select Device:”.

    • On the PC, the device ID for the USB-to-TTL cable connected in this example begins with ‘\?\USB#VID_067B’.

    • On the MBM, if using the GPIO for serial communication, select the device ID with UART2 in it. UART1 may require using CTS/RTS signals.

    • On the DragonBoard, select the device with QCOM24D4 and UART1 in it. This will likely be the last device in the listbox (you may need to scroll down).

    • On the MBM and RPi2, if using the USB-to-TTL adapter module, select the device ID that begins with \?\USB#. For the USB-to-TTL module used in this example, the device ID should begin with ‘\?\USB#VID_10C4’.

  2. Click ‘Connect’.

The app will attempt to connect and configure the selected serial device. When the app has successfully connected to the attached serial device it will display the configuration of the serial device. By default, the app configures the serial device for 9600 Baud, eight data bits, no parity bits and one stop bit (no handshaking).

Sending and Receiving Data

After connecting the desired serial device in the SerialSample apps running on both the PC and the RPi2 or MBM we can begin sending and receiving data over the serial connection between the two devices.

To send data from one device to the other connected device do the following:

  1. Choose a device to transmit from. On the transmit device, type the message to be sent in the “Write Data” text box. For our example, we typed “Hello World!” in the “Write Data” text box of the SerialSample app running on our PC.

  2. Click the ‘Write’ button.

The app on the transmitting device will display the sent message and “bytes written successfully!” in the status text box in the bottom of the app display.

The device that is receiving the message will automatically display the text in the ‘Read Data:’ window.

KNOWN ISSUES:

  • When connecting to the USB-to-TTL device for the first time from the IoT Device, you may see the error “Object was not instantiated” when you click on Connect. If you see this, un-plug the device, plug it back in and refresh the connection or redeploy the app.
  • If you have more than one Silicon Labs USB-to-TTL devices connected to your IoT device, only the device that was first connected will be recognized. In order to run this sample, connect only one device
  • When connecting USB-to-TTL device to MinnowBoard Max, use a powered USB hub or the bottom USB port

Let’s look at the code

The code for this sample uses the Windows.Devices.SerialCommunication namespace.

The SerialDevice class will be used to enumerate, connect, read, and write to the serial devices connected to the device.

NOTE: The SerialDevice class can be used only for supported USB-to-TTL devices (on PC, Raspberry Pi 2, and MinnowBoard Max) and the on-board UART (on MinnowBoard Max).

For accessing the serial port, you must add the DeviceCapability to the Package.appxmanifest file in your project.

You can add this by opening the Package.appxmanifest file in an XML editor (Right Click on the file -> Open with -> XML (Text) Editor) and adding the capabilities as shown below:

Visual Studio 2015 has a known bug in the Manifest Designer (the visual editor for appxmanifest files) that affects the serialcommunication capability.  If 
your appxmanifest adds the serialcommunication capability, modifying your appxmanifest with the designer will corrupt your appxmanifest (the Device xml child 
will be lost).  You can workaround this problem by hand editting the appxmanifest by right-clicking your appxmanifest and selecting View Code from the 
context menu.
  <Capabilities>
    <DeviceCapability Name="serialcommunication">
      <Device Id="any">
        <Function Type="name:serialPort" />
      </Device>
    </DeviceCapability>
  </Capabilities>

Connect to selected serial device

This sample app enumerates all serial devices connected to the device and displays the list in the ListBox ConnectDevices. The following code connects and configure the selected device ID and creates a SerialDevice object.

private async void comPortInput_Click(object sender, RoutedEventArgs e)
{
    var selection = ConnectDevices.SelectedItems; // Get selected items from ListBox

    // ...

    DeviceInformation entry = (DeviceInformation)selection[0];         

    try
    {                
        serialPort = await SerialDevice.FromIdAsync(entry.Id);

        // ...

        // Configure serial settings
        serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
        serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);                
        serialPort.BaudRate = 9600;
        serialPort.Parity = SerialParity.None;
        serialPort.StopBits = SerialStopBitCount.One;
        serialPort.DataBits = 8;

        // ...
    }
    catch (Exception ex)
    {
        // ...
    }
}

Perform a read on the serial port

Reading input from serial port is done by Listen() invoked right after initialization of the serial port. We do this in the sample code by creating an async read task using the DataReader object that waits on the InputStream of the SerialDevice object.

Due to differences in handling concurrent tasks, the implementations of Listen() in C# and C++ differ:

  • C# allows awaiting ReadAsync(). All we do is keep reading the serial port in an infinite loop interrupted only when an exception is thrown (triggered by the cancellation token).
private async void Listen()
{
    try
    {
        if (serialPort != null)
        {
            dataReaderObject = new DataReader(serialPort.InputStream);

            // keep reading the serial input
            while (true)
            {
                await ReadAsync(ReadCancellationTokenSource.Token);
            }
        }
    }
    catch (Exception ex)
    {
        ...
    }
    finally
    {
        ...
    }
}

private async Task ReadAsync(CancellationToken cancellationToken)
{
    Task<UInt32> loadAsyncTask;

    uint ReadBufferLength = 1024;

    // If task cancellation was requested, comply
    cancellationToken.ThrowIfCancellationRequested();

    // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
    dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;

    // Create a task object to wait for data on the serialPort.InputStream
    loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);

    // Launch the task and wait
    UInt32 bytesRead = await loadAsyncTask;
    if (bytesRead > 0)
    {
        rcvdText.Text = dataReaderObject.ReadString(bytesRead);
        status.Text = "bytes read successfully!";
    }            
}
  • C++ does not allow awaiting ReadAsync() in Windows Runtime STA (Single Threaded Apartment) threads due to blocking the UI. In order to chain continuation reads from the serial port, we dynamically generate repeating tasks via “recursive” task creation - “recursively” call Listen() at the end of the continuation chain. The “recursive” call is not a true recursion. It will not accumulate stack since every recursive is made in a new task.
void MainPage::Listen()
{
    try
    {
        if (_serialPort != nullptr)
        {
            // calling task.wait() is not allowed in Windows Runtime STA (Single Threaded Apartment) threads due to blocking the UI.
            concurrency::create_task(ReadAsync(cancellationTokenSource->get_token()));
        }
    }
    catch (Platform::Exception ^ex)
    {
        ...
    }
}

Concurrency::task<void> MainPage::ReadAsync(Concurrency::cancellation_token cancellationToken)
{
    unsigned int _readBufferLength = 1024;
    
    return concurrency::create_task(_dataReaderObject->LoadAsync(_readBufferLength), cancellationToken).then([this](unsigned int bytesRead)
    {
        if (bytesRead > 0)
        {
            rcvdText->Text = _dataReaderObject->ReadString(bytesRead);
            status->Text = "bytes read successfully!";

            /*
            Dynamically generate repeating tasks via "recursive" task creation - "recursively" call Listen() at the end of the continuation chain.
            The "recursive" call is not true recursion. It will not accumulate stack since every recursive is made in a new task.
            */

            // start listening again after done with this chunk of incoming data
            Listen();
        }
    });
}

Perform a write to the serial port

When the bytes are ready to be sent, we write asynchronously to the OutputStream of the SerialDevice object using the DataWriter object.

private async void sendTextButton_Click(object sender, RoutedEventArgs e)
{	
    // ...
	
    // Create the DataWriter object and attach to OutputStream   
    dataWriteObject = new DataWriter(serialPort.OutputStream);

    //Launch the WriteAsync task to perform the write
    await WriteAsync();   
	
    // ..

    dataWriteObject.DetachStream();
    dataWriteObject = null;	
}

private async Task WriteAsync()
{
    Task<UInt32> storeAsyncTask;

    // ...
	
    // Load the text from the sendText input text box to the dataWriter object
    dataWriteObject.WriteString(sendText.Text);                

    // Launch an async task to complete the write operation
    storeAsyncTask = dataWriteObject.StoreAsync().AsTask();

    // ...    
}

Cancelling Read

You can cancel the read operation by using CancellationToken on the Task. Initialize the CancellationToken object and pass that as an argument to the read task.

private async void comPortInput_Click(object sender, RoutedEventArgs e)
{
    // ...

    // Create cancellation token object to close I/O operations when closing the device
    ReadCancellationTokenSource = new CancellationTokenSource();
	
    // ...	
}

private async void rcvdText_TextChanged(object sender, TextChangedEventArgs e)
{
    // ...

    await ReadAsync(ReadCancellationTokenSource.Token);

    // ...	
}

private async Task ReadAsync(CancellationToken cancellationToken)
{
    Task<UInt32> loadAsyncTask;

    uint ReadBufferLength = 1024;

    cancellationToken.ThrowIfCancellationRequested();
    
    // ...
	
}
 
private void CancelReadTask()
{         
    if (ReadCancellationTokenSource != null)
    {
        if (!ReadCancellationTokenSource.IsCancellationRequested)
        {
            ReadCancellationTokenSource.Cancel();
        }
    }         
}

Closing the device

When closing the connection with the device, we cancel all pending I/O operations and safely dispose of all the objects.

In this sample, we proceed to also refresh the list of devices connected.

private void closeDevice_Click(object sender, RoutedEventArgs e)
{
    try
    {
        CancelReadTask();
        CloseDevice();
        ListAvailablePorts(); //Refresh the list of available devices
    }
    catch (Exception ex)
    {
       // ...
    }          
}    

private void CloseDevice()
{            
    if (serialPort != null)
    {
        serialPort.Dispose();
    }    

    // ...
}    

To summarize:

  • First, we enumerate all the serial devices connected and allow the user to connect to the desired one using device ID

  • We create an asynchronous task for reading the InputStream of the SerialDevice object.

  • When the user provides input, we write the bytes to the OutputStream of the SerialDevice object.

  • We add the ability to cancel the read task using the CancellationToken.

  • Finally, we close the device connection and clean up when done.

Set up a Raspberry Pi 2

Set up a Raspberry Pi 2

Get your computer ready for the Windows Developer Program for IoT, set up your Raspberry Pi 2 and build your first Windows IoT Core app.

1. Set up your Raspberry Pi 2


2. Write your first app: Hello, Blinky!


In this tutorial, we'll create a simple LED blinking app and connect a LED to your Windows 10 IoT Core device.

This is a headed sample. To better understand what headed mode is and how to configure your device to be headed, follow the instructions here.

Also, be aware that the GPIO APIs are only available on Windows 10 IoT Core, so this sample cannot run on your desktop.

Load the project in Visual Studio

You can find the source code for this sample by downloading a zip of all of our samples here and navigating to the samples-develop\Blinky. The sample code is available in either C++ or C#, however the documentation here only details the C# variant. Make a copy of the folder on your disk and open the project from Visual Studio.

Connect the LED to your Windows IoT device

You'll need a few components:

A 220 Ω resistor

A breadboard and a couple of connector wires

An LED (any color you like)

Wiring your Raspberry Pi 2

  • Connect the shorter leg of the LED to GPIO 5 (pin 29 on the expansion header) on the RPi2.
  • Connect the longer leg of the LED to the resistor.
  • Connect the other end of the resistor to one of the 3.3V pins on the RPi2.
  • Note that the polarity of the LED is important. (This configuration is commonly known as Active Low)

Here is the pinout of the RPi2:

Here is an example of what your breadboard might look like with the circuit assembled:

*Image made with Fritzing*

Deploy Your App

  • With the application open in Visual Studio, set the architecture in the toolbar dropdown. If you're building for MinnowBoard Max, select x86. If you're building for Raspberry Pi 2 or the DragonBoard, select ARM.
 
  • Next, in the Visual Studio toolbar, click on the Local Machine dropdown and selectRemote Machine
  • At this point, Visual Studio will present the Remote Connections dialog. If you previously used Powershell to set a unique name for your device, you can enter it here (in this example, we're using my-device). Otherwise, use the IP address of your Windows IoT Core device. After entering the device name/IP select Universal (Unencrypted Protocol)Authentication Mode, then click Select.
  • You can verify or modify these values by navigating to the project properties (selectProperties in the Solution Explorer) and choosing the Debug tab on the left:

When everything is set up, you should be able to press F5 from Visual Studio. If there are any missing packages that you did not install during setup, Visual Studio may prompt you to acquire those now. The Blinky app will deploy and start on the Windows IoT device, and you should see the LED blink in sync with the simulation on the screen.

Congratulations! You controlled one of the GPIO pins on your Windows IoT device.

Let's look at the code

The code for this sample is pretty simple. We use a timer, and each time the 'Tick' event is called, we flip the state of the LED.

Timer Code

Here is how you set up the timer in C#:

            public MainPage()
            {
                // ...

                timer = new DispatcherTimer();
                timer.Interval = TimeSpan.FromMilliseconds(500);
                timer.Tick += Timer_Tick;
                InitGPIO();
                if (pin != null)
                {
                    timer.Start();
                }

                // ...
            }

            private void Timer_Tick(object sender, object e)
            {
                if (pinValue == GpioPinValue.High)
                {
                    pinValue = GpioPinValue.Low;
                    pin.Write(pinValue);
                    LED.Fill = redBrush;
                }
                else
                {
                    pinValue = GpioPinValue.High;
                    pin.Write(pinValue);
                    LED.Fill = grayBrush;
                }
            }
        

Initialize the GPIO Pin

To drive the GPIO pin, first we need to initialize it. Here is the C# code (notice how we leverage the new WinRT classes in the Windows.Devices.Gpio namespace):

Let's break this down a little

  • First, we use GpioController.GetDefault() to get the GPIO controller.
  • If the device does not have a GPIO controller, this function will return null.
  • Then we attempt to open the pin by calling GpioController.OpenPin() with the LED_PINvalue.
  • Once we have the pin, we set it to be off (High) by default using the GpioPin.Write()function.
  • We also set the pin to run in output mode using the GpioPin.SetDriveMode() function.
            using Windows.Devices.Gpio;

            private void InitGPIO()
            {
                var gpio = GpioController.GetDefault();

                // Show an error if there is no GPIO controller
                if (gpio == null)
                {
                    pin = null;
                    GpioStatus.Text = "There is no GPIO controller on this device.";
                    return;
                }

                pin = gpio.OpenPin(LED_PIN);
                pinValue = GpioPinValue.High;
                pin.Write(pinValue);
                pin.SetDriveMode(GpioPinDriveMode.Output);

                GpioStatus.Text = "GPIO pin initialized correctly.";

            }
        

Modify the state of the GPIO pin

Once we have access to the GpioOutputPin instance, it's trivial to change the state of the pin to turn the LED on or off.

To turn the LED on, simply write the value GpioPinValue.Low to the pin:

            pin.Write(GpioPinValue.Low);
        

and of course, write GpioPinValue.High to turn the LED off:

            pin.Write(GpioPinValue.High);
        
Remember that we connected the other end of the LED to the 3.3 Volts power supply, so we need to drive the pin to low to have current flow into the LED.