0% found this document useful (0 votes)
8 views51 pages

Dotnet Iot

.NET IoT Libraries provide tools for developing Internet of Things applications on platforms like Raspberry Pi. The documentation includes quickstarts, how-to guides, and tutorials for using GPIO pins to control devices and read sensor data. It also covers installation, deployment, and available device bindings to streamline development.

Uploaded by

michael.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views51 pages

Dotnet Iot

.NET IoT Libraries provide tools for developing Internet of Things applications on platforms like Raspberry Pi. The documentation includes quickstarts, how-to guides, and tutorials for using GPIO pins to control devices and read sensor data. It also covers installation, deployment, and available device bindings to streamline development.

Uploaded by

michael.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

Tell us about your PDF experience.

.NET IoT Libraries documentation


.NET 💜 Internet of Things

Get started in 5 minutes

f QUICKSTART

Raspberry Pi Sense HAT quickstart

Learn

e OVERVIEW

.NET IoT Libraries overview

` DEPLOY

Deploy .NET apps to Single Board Computers

p CONCEPT

Debug .NET apps on Single Board Computers

c HOW-TO GUIDE

Use the IoT libraries on Windows, Linux, and macOS computers

i REFERENCE

Available device bindings (GitHub)

API Reference

Build devices

d TRAINING

Construct Internet of Things devices using the .NET IoT Libraries


g TUTORIAL

Blink an LED

Read binary input using GPIO

Read environmental conditions from a sensor

Display text on an LCD

Read values from an analog-to-digital converter

Packages and source code

a DOWNLOAD

System.Device.Gpio on NuGet

Iot.Device.Bindings on NuGet

.NET IoT Libraries source on GitHub

Device bindings with samples on GitHub

Video learning

q VIDEO

.NET IoT for Beginners

Let's Learn .NET: IoT

BBQ, Bots, and .NET Core

Resources

b GET STARTED

.NET IoT Discord community

.NET IoT on Q&A

p CONCEPT
Raspberry Pi documentation

c HOW-TO GUIDE

Passwordless SSH access

Setting up a Raspberry Pi headless

Run a .NET app automatically at boot


Develop apps for IoT devices with the
.NET IoT Libraries
Article • 04/08/2023

.NET runs on various platforms and architectures. Common Internet of things (IoT)
boards, such as Raspberry Pi and Hummingboard, are supported. IoT apps typically
interact with specialized hardware, such as sensors, analog-to-digital converters, and
LCD devices. The .NET IoT Libraries enable these scenarios.

Libraries
The .NET IoT Libraries are composed of two NuGet packages:

System.Device.Gpio
Iot.Device.Bindings

System.Device.Gpio
System.Device.Gpio supports various protocols for interacting with low-level hardware

pins to control devices. These include:

General-purpose I/O (GPIO)


Inter-Integrated Circuit (I2C)
Serial Peripheral Interface (SPI)
Pulse Width Modulation (PWM)
Serial port

Iot.Device.Bindings
The Iot.Device.Bindings package:

Contains device bindings to streamline app development by wrapping


System.Device.Gpio.
Is community-supported, and additional bindings are added continually.

The complete list of available device bindings is maintained on GitHub.

Some commonly used device bindings include:

CharacterLcd - LCD character display


SN74HC595 - 8-bit shift register
BrickPi3
Max7219 - LED Matrix driver
RGBLedMatrix - RGB LED Matrix

Supported operating systems


System.Device.Gpio is supported on any operating system that supports .NET, including
most versions of Linux that support ARM/ARM64 and Windows 10 IoT Core.

 Tip

For Raspberry Pi, Raspberry Pi OS (64-bit) is recommended. No desktop is


required, so Raspberry Pi OS Lite can be used to reduce the size of the operating
system.

Supported hardware platforms


System.Device.Gpio is compatible with most single-board platforms. Recommended

platforms are Raspberry Pi (2 and greater) and Hummingboard. Other platforms known
to be compatible are BeagleBoard and ODROID.

PC platforms are supported via the use of a USB to SPI/I2C bridge.


) Important

.NET is not supported on devices prior to ARMv7, including Raspberry Pi Zero and
Raspberry Pi devices prior to Raspberry Pi 2.

Resources
.NET IoT Libraries on GitHub
Quickstart - Use .NET to drive a
Raspberry Pi Sense HAT
Article • 04/08/2023

The Raspberry Pi Sense HAT (Hardware Attached on Top) is an add-on board for
Raspberry Pi. The Sense HAT is equipped with an 8×8 RGB LED matrix, a five-button
joystick, and includes the following sensors:

Gyroscope
Accelerometer
Magnetometer
Temperature
Barometric pressure
Humidity

This quickstart uses .NET to retrieve sensor values from the Sense HAT, respond to
joystick input, and drive the LED matrix.

Prerequisites
Raspberry Pi that supports ARM v7 instructions with Raspberry Pi OS . Raspberry
Pi OS Lite (64-bit) is recommended.
Sense HAT

Prepare the Raspberry Pi


Use the raspi-config command to ensure your SBC is configured to support the
following services:

SSH
I2C

For more information on raspi-config , refer to the Raspberry Pi documentation .

Attach the Sense HAT


With the Raspberry Pi device powered off, attach the Sense HAT. Power on the
Raspberry Pi and launch the Bash shell once it boots. You may use SSH or connect the
Raspberry Pi to a display.
Install Git
From the shell, ensure the latest version of Git is installed on your Raspberry Pi. Run the
following commands:

Bash

sudo apt update


sudo apt install git

The commands use the Advanced Package Tool command to:

Download package information from all configured sources.


Install the Git command line tool.

Run the quickstart


From the shell, run the following command:

Bash

. <(wget -q -O - https://aka.ms/dotnet-iot-sensehat-quickstart)

The command downloads and runs a script. The script:

Installs the .NET SDK.


Clones a GitHub repository that includes the Sense HAT quickstart project.
Builds the project.
Runs the project.

Observe the console output as sensor data is displayed. The LED matrix displays a yellow
pixel on a field of blue. Holding the joystick in any direction moves the yellow pixel in
that direction. Clicking the center joystick button causes the background to switch from
blue to red.

Get the source code


The source for this quickstart is available on GitHub .

Next steps
Learn to use General Purpose Input/Output to blink an LED
Blink an LED
Article • 04/08/2023

General-purpose I/O (GPIO) pins can be controlled individually. This is useful for
controlling LEDs, relays, and other stateful devices. In this topic, you will use .NET and
your Raspberry Pi's GPIO pins to power an LED and blink it repeatedly.

Prerequisites
ARM-based (ARMv7 or greater) single-board computer (SBC)
5 mm LED
330 Ω resistor
Breadboard
Jumper wires
Raspberry Pi GPIO breakout board (optional/recommended)
.NET SDK 7 or later

7 Note

This tutorial is written assuming the target device is Raspberry Pi. However, this
tutorial can be used for any Linux-based SBC that supports .NET, such as Orange Pi,
ODROID, and more.

Ensure SSH is enabled on your device. For Raspberry Pi, refer to Setting up an SSH Server
in the Raspberry Pi documentation .

Prepare the hardware


Use the hardware components to build the circuit as depicted in the following diagram:

The image above depicts the following connections:

GPIO 18 to LED anode (longer, positive lead)


LED cathode (shorter, negative lead) to 330 Ω resistor (either end)
330 Ω resistor (other end) to ground

Refer to the following pinout diagram as needed:

Image courtesy Raspberry Pi Foundation .

 Tip
A GPIO breakout board in conjunction with a breadboard is recommended to
streamline connections to the GPIO header.

Create the app


Complete the following steps in your preferred development environment:

1. Create a new .NET Console App using either the .NET CLI or Visual Studio. Name it
BlinkTutorial.

.NET CLI

dotnet new console -o BlinkTutorial


cd BlinkTutorial

2. Add the System.Device.Gpio package to the project. Use either .NET CLI from the
project directory or Visual Studio.

.NET CLI

dotnet add package System.Device.Gpio --version 2.2.0-*

3. Replace the contents of Program.cs with the following code:

C#

using System;
using System.Device.Gpio;
using System.Threading;

Console.WriteLine("Blinking LED. Press Ctrl+C to end.");


int pin = 18;
using var controller = new GpioController();
controller.OpenPin(pin, PinMode.Output);
bool ledOn = true;
while (true)
{
controller.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low));
Thread.Sleep(1000);
ledOn = !ledOn;
}

In the preceding code:


A using declaration creates an instance of GpioController . The using
declaration ensures the object is disposed and hardware resources are
released properly.
GPIO pin 18 is opened for output
A while loop runs indefinitely. Each iteration:
a. Writes a value to GPIO pin 18. If ledOn is true, it writes PinValue.High (on).
Otherwise, it writes PinValue.Low .
b. Sleeps 1000 ms.
c. Toggles the value of ledOn .

4. Build the app. If using the .NET CLI, run dotnet build . To build in Visual Studio,
press Ctrl + Shift + B .

5. Deploy the app to the SBC as a self-contained app. For instructions, see Deploy
.NET apps to Raspberry Pi. Make sure to give the executable execute permission
using chmod +x .

6. Run the app on the Raspberry Pi by switching to the deployment directory and
running the executable.

Bash

./BlinkTutorial

The LED blinks off and on every second.

7. Terminate the program by pressing Ctrl + C .

Congratulations! You've used GPIO to blink an LED.

Get the source code


The source for this tutorial is available on GitHub .

Next steps
Learn how to read binary input using GPIO
Use GPIO for binary input
Article • 04/08/2023

General-purpose I/O (GPIO) pins can be configured to receive electrical signals as input.
At its most basic level, this is useful for scenarios that detect the opening/closing of a
circuit. Such circuits might include push buttons, toggle switches, reed switches,
pressure switches, and other devices that represent binary (on/off) values by completing
a circuit.

In this tutorial, you'll use .NET and your Raspberry Pi's GPIO pins to detect the opening
and closing of a circuit.

Prerequisites
ARM-based (ARMv7 or greater) single-board computer (SBC)
Jumper wires
Breadboard (optional)
Raspberry Pi GPIO breakout board (optional)
.NET SDK 7 or later

7 Note

This tutorial is written assuming the target device is Raspberry Pi. However, this
tutorial can be used for any Linux-based SBC that supports .NET, such as Orange Pi,
ODROID, and more.

Ensure SSH is enabled on your device. For Raspberry Pi, refer to Setting up an SSH Server
in the Raspberry Pi documentation .

Prepare the hardware


Use the hardware components to build the circuit as depicted in the following diagram:

The image above depicts a direct connection between a ground pin and pin 21.

 Tip

The diagram depicts a breadboard and GPIO breakout for illustrative purposes, but
feel free to just connect a ground pin and pin 21 with a jumper wire on the
Raspberry Pi.

Refer to the following pinout diagram as needed:

Image courtesy Raspberry Pi Foundation .

Create the app


Complete the following steps in your preferred development environment:

1. Create a new .NET Console App using either the .NET CLI or Visual Studio. Name it
InputTutorial.

.NET CLI

dotnet new console -o InputTutorial


cd InputTutorial
2. Add the System.Device.Gpio package to the project. Use either .NET CLI from the
project directory or Visual Studio.

.NET CLI

dotnet add package System.Device.Gpio --version 2.2.0-*

3. Replace the contents of Program.cs with the following code:

C#

using System.Device.Gpio;
using System.Threading.Tasks;

const int Pin = 21;


const string Alert = "ALERT 🚨";
const string Ready = "READY ✅";

using var controller = new GpioController();


controller.OpenPin(Pin, PinMode.InputPullUp);

Console.WriteLine(
$"Initial status ({DateTime.Now}): {(controller.Read(Pin) ==
PinValue.High ? Alert : Ready)}");

controller.RegisterCallbackForPinValueChangedEvent(
Pin,
PinEventTypes.Falling | PinEventTypes.Rising,
OnPinEvent);

await Task.Delay(Timeout.Infinite);

static void OnPinEvent(object sender, PinValueChangedEventArgs args)


{
Console.WriteLine(
$"({DateTime.Now}) {(args.ChangeType is PinEventTypes.Rising ?
Alert : Ready)}");
}

In the preceding code:

A using declaration creates an instance of GpioController . The using


declaration ensures the object is disposed and hardware resources are
released properly.
GpioController is instantiated with no parameters, indicating that it

should detect which hardware platform it's running on and use the logical
pin numbering scheme.
GPIO pin 21 is opened with PinMode.InputPullUp .
This opens the pin with a PullUp resistor engaged. In this mode, when the
pin is connected to ground, it will return PinValue.Low . When the pin is
disconnected from ground and the circuit is open, the pin returns
PinValue.High .

The initial status is written to a console using a ternary expression. The pin's
current state is read with Read() . If it's PinValue.High , it writes the Alert
string to the console. Otherwise, it writes the Ready string.
RegisterCallbackForPinValueChangedEvent() registers a callback function for
both the PinEventTypes.Rising and PinEventTypes.Falling events on the pin.
These events correspond to pin states of PinValue.High and PinValue.Low ,
respectively.
The callback function points to a method called OnPinEvent() . OnPinEvent()
uses another ternary expression that also writes the corresponding Alert or
Ready strings.

The main thread sleeps indefinitely while waiting for pin events.

4. Build the app. If using the .NET CLI, run dotnet build . To build in Visual Studio,
press Ctrl + Shift + B .

5. Deploy the app to the SBC as a self-contained app. For instructions, see Deploy
.NET apps to Raspberry Pi. Make sure to give the executable execute permission
using chmod +x .

6. Run the app on the Raspberry Pi by switching to the deployment directory and
running the executable.

Bash

./InputTutorial

The console displays text similar to the following:

Console

Initial status (05/10/2022 15:59:25): READY ✅

7. Disconnect pin 21 from ground. The console displays text similar to the following:

Console

(05/10/2022 15:59:59) ALERT 🚨


8. Reconnect pin 21 and ground. The console displays text similar to the following:

Console

(05/10/2022 16:00:25) READY ✅

9. Terminate the program by pressing Ctrl + C .

Congratulations! You've used GPIO to detect input using the System.Device.Gpio NuGet
package! There are many uses for this type of input. This example can be used with any
scenario where a switch connects or breaks a circuit. Here's an example using it with a
magnetic reed switch, which is often used to detect open doors or windows.

Laser tripwire
Extending the previous example concept a bit further, let's take a look at how this could
be applied to creating a laser tripwire. Building a laser tripwire requires the following
additional components:

KY-008 laser transmitter module


Laser receiver sensor module (see note below)
2 10K Ω resistors

7 Note
Laser receiver sensor module is the generic name applied to a common module
found at many internet retailers. The device may vary in name or manufacturer, but
should resemble this image.

Connect laser tripwire hardware


Connect the components as detailed in the following diagram.

Pay close attention to the 10K Ω resistors. These implement a voltage divider . This is
because the laser receiver module outputs 5V to indicate the beam is broken. Raspberry
Pi only supports up to 3.3V for GPIO input. Since sending the full 5V to the pin could
damage the Raspberry Pi, the current from the receiver module is passed through a
voltage divider to halve the voltage to 2.5V.

Apply source code updates


You can almost use the same code as earlier, with one exception. In the other examples,
we used PinMode.InputPullUp so that when the pin is disconnected from ground and
the circuit is open, the pin returns PinValue.High .
However, in the case of the laser receiver module, we're not detecting an open circuit.
Instead, we want the pin to act as a sink for current coming from the laser receiver
module. In this case, we'll open the pin with PinMode.InputPullDown . This way, the pin
returns PinValue.Low when it's getting no current, and PinValue.High when it receives
current from the laser receiver module.

C#

controller.OpenPin(pin, PinMode.InputPullDown);

) Important

Make sure the code deployed on your Raspberry Pi includes this change before
testing a laser tripwire. The program does work without it, but using the wrong
input mode risks damage to your Raspberry Pi!

Get the source code


The source for this tutorial is available on GitHub .

Next steps
Learn how to read environmental conditions from a sensor
Read environmental conditions from a
sensor
Article • 04/08/2023

One of the most common scenarios for IoT devices is detection of environmental
conditions. A variety of sensors are available to monitor temperature, humidity,
barometric pressure, and more.

In this topic, you will use .NET to read environmental conditions from a sensor.

Prerequisites
ARM-based (ARMv7 or greater) single-board computer (SBC)
BME280 humidity/barometric pressure/temperature sensor breakout
Jumper wires
Breadboard (optional)
Raspberry Pi GPIO breakout board (optional)
.NET SDK 7 or later

7 Note

This tutorial is written assuming the target device is Raspberry Pi. However, this
tutorial can be used for any Linux-based SBC that supports .NET, such as Orange Pi,
ODROID, and more.

) Important

There are many manufacturers of BME280 breakouts. Most designs are similar, and
the manufacturer shouldn't make any difference to the functionality. This tutorial
attempts to account for variations. Ensure your BME280 breakout includes an Inter-
Integrated Circuit (I2C) interface.

Components like BME280 breakouts are often sold with unsoldered pin headers. If
you're uncomfortable with soldering, look for a BME280 breakout board with a pre-
soldered header or a different connector. If you want, consider learning how to
solder! Here's a good beginner's guide to soldering .
Prepare the SBC
Ensure your SBC is configured to support the following services:

SSH
I2C

For many devices, no additional configuration is required. For Raspberry Pi, use the
raspi-config command. For more information on raspi-config , refer to the Raspberry
Pi documentation .

Prepare the hardware


Use the hardware components to build the circuit as depicted in the following diagram:

The following are the connections from the Raspberry Pi to the BME280 breakout. Note
that pin labels differ on various BME280 breakouts.

Raspberry Pi BME280 Breakout Color

3.3V VIN/3V3 red


Raspberry Pi BME280 Breakout Color

Ground GND black

SDA (GPIO 2) SDI/SDA blue

SCL (GPIO 3) SCK/SCL orange

Refer to the following pinout diagram as needed:

Image courtesy Raspberry Pi Foundation .

 Tip

A GPIO breakout board in conjunction with a breadboard is recommended to


streamline connections to the GPIO header.

Create the app


Complete the following steps in your preferred development environment:

1. Create a new .NET Console App using either the .NET CLI or Visual Studio. Name it
SensorTutorial.

.NET CLI

dotnet new console -o SensorTutorial


cd SensorTutorial

2. Add the Iot.Device.Bindings package to the project. Use either .NET CLI from the
project directory or Visual Studio.

.NET CLI
dotnet add package Iot.Device.Bindings --version 2.2.0-*

3. Replace the contents of Program.cs with the following code:

C#

using System;
using System.Device.I2c;
using System.Threading;
using Iot.Device.Bmxx80;
using Iot.Device.Bmxx80.PowerMode;

var i2cSettings = new I2cConnectionSettings(1,


Bme280.DefaultI2cAddress);
using I2cDevice i2cDevice = I2cDevice.Create(i2cSettings);
using var bme280 = new Bme280(i2cDevice);

int measurementTime = bme280.GetMeasurementDuration();

while (true)
{
Console.Clear();

bme280.SetPowerMode(Bmx280PowerMode.Forced);
Thread.Sleep(measurementTime);

bme280.TryReadTemperature(out var tempValue);


bme280.TryReadPressure(out var preValue);
bme280.TryReadHumidity(out var humValue);
bme280.TryReadAltitude(out var altValue);

Console.WriteLine($"Temperature:
{tempValue.DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Pressure: {preValue.Hectopascals:#.##} hPa");
Console.WriteLine($"Relative humidity: {humValue.Percent:#.##}%");
Console.WriteLine($"Estimated altitude: {altValue.Meters:#} m");

Thread.Sleep(1000);
}

In the preceding code:

i2cSettings is set to a new instance of I2cConnectionSettings . The


constructor sets the busId parameter to 1 and the deviceAddress parameter
to Bme280.DefaultI2cAddress .

) Important
Some BME280 breakout manufacturers use the secondary address value.
For those devices, use Bme280.SecondaryI2cAddress .

A using declaration creates an instance of I2cDevice by calling


I2cDevice.Create and passing in i2cSettings . This I2cDevice represents the
I2C bus. The using declaration ensures the object is disposed and hardware
resources are released properly.

Another using declaration creates an instance of Bme280 to represent the


sensor. The I2cDevice is passed in the constructor.

The time required for the chip to take measurements with the chip's current
(default) settings is retrieved by calling GetMeasurementDuration .

A while loop runs indefinitely. Each iteration:

a. Clears the console.

b. Sets the power mode to Bmx280PowerMode.Forced . This forces the chip to


perform one measurement, store the results, and then sleep.

c. Reads the values for temperature, pressure, humidity, and altitude.

7 Note

Altitude is calculated by the device binding. This overload of


TryReadAltitude uses mean sea level pressure to generate an

estimate.

d. Writes the current environmental conditions to the console.

e. Sleeps 1000 ms.

4. Build the app. If using the .NET CLI, run dotnet build . To build in Visual Studio,
press Ctrl + Shift + B .

5. Deploy the app to the SBC as a self-contained app. For instructions, see Deploy
.NET apps to Raspberry Pi. Make sure to give the executable execute permission
using chmod +x .

6. Run the app on the Raspberry Pi by switching to the deployment directory and
running the executable.
Bash

./SensorTutorial

Observe the sensor output in the console.

7. Terminate the program by pressing Ctrl+C .

Congratulations! You've used I2C to read values from a


temperature/humidity/barometric pressure sensor!

Get the source code


The source for this tutorial is available on GitHub .

Next steps
Learn how to display text on an LCD
Display text on an LCD
Article • 04/08/2023

LCD character displays are useful for displaying information without the need for an
external monitor. Common LCD character displays can be connected directly to the
GPIO pins, but such an approach requires the use of up to 10 GPIO pins. For scenarios
that require connecting to a combination of devices, devoting so much of the GPIO
header to a character display is often impractical.

Many manufacturers sell 20x4 LCD character displays with an integrated GPIO expander.
The character display connects directly to the GPIO expander, which then connects to
the Raspberry Pi via the Inter-Integrated Circuit (I2C) serial protocol.

In this topic, you will use .NET to display text on an LCD character display using an I2C
GPIO expander.

Prerequisites
ARM-based (ARMv7 or greater) single-board computer (SBC)
20x4 LCD Character Display with I2C interface
Jumper wires
Breadboard (optional/recommended)
Raspberry Pi GPIO breakout board (optional/recommended)
.NET SDK 7 or later

7 Note

This tutorial is written assuming the target device is Raspberry Pi. However, this
tutorial can be used for any Linux-based SBC that supports .NET, such as Orange Pi,
ODROID, and more.

7 Note

There are many manufacturers of LCD character displays. Most designs are
identical, and the manufacturer shouldn't make any difference to the functionality.
For reference, this tutorial was developed with the SunFounder LCD2004 .

Prepare the SBC


Ensure your SBC is configured to support the following services:

SSH
I2C

For many devices, no additional configuration is required. For Raspberry Pi, use the
raspi-config command. For more information on raspi-config , refer to the Raspberry

Pi documentation .

Prepare the hardware


Use jumper wires to connect the four pins on the I2C GPIO expander to the Raspberry Pi
as follows:

GND to ground
VCC to 5V
SDA to SDA (GPIO 2)
SCL to SCL (GPIO 3)

Refer to the following figures as needed:

I2C interface (back of display) Raspberry Pi GPIO

 

Image courtesy Raspberry Pi Foundation .

 Tip

A GPIO breakout board in conjunction with a breadboard is recommended to


streamline connections to the GPIO header.

Create the app


Complete the following steps in your preferred development environment:
1. Create a new .NET Console App using either the .NET CLI or Visual Studio. Name it
LcdTutorial.

.NET CLI

dotnet new console -o LcdTutorial


cd LcdTutorial

2. Add the Iot.Device.Bindings package to the project. Use either .NET CLI from the
project directory or Visual Studio.

.NET CLI

dotnet add package Iot.Device.Bindings --version 2.2.0-*

3. Replace the contents of Program.cs with the following code:

C#

using System;
using System.Device.Gpio;
using System.Device.I2c;
using System.Threading;
using Iot.Device.CharacterLcd;
using Iot.Device.Pcx857x;

Console.WriteLine("Displaying current time. Press Ctrl+C to end.");

using I2cDevice i2c = I2cDevice.Create(new I2cConnectionSettings(1,


0x27));
using var driver = new Pcf8574(i2c);
using var lcd = new Lcd2004(registerSelectPin: 0,
enablePin: 2,
dataPins: new int[] { 4, 5, 6, 7 },
backlightPin: 3,
backlightBrightness: 0.1f,
readWritePin: 1,
controller: new
GpioController(PinNumberingScheme.Logical, driver));
int currentLine = 0;

while (true)
{
lcd.Clear();
lcd.SetCursorPosition(0,currentLine);
lcd.Write(DateTime.Now.ToShortTimeString());
currentLine = (currentLine == 3) ? 0 : currentLine + 1;
Thread.Sleep(1000);
}
In the preceding code:

A using declaration creates an instance of I2cDevice by calling


I2cDevice.Create and passing in a new I2cConnectionSettings with the

busId and deviceAddress parameters. This I2cDevice represents the I2C bus.
The using declaration ensures the object is disposed and hardware resources
are released properly.

2 Warning

The device address for the GPIO expander depends on the chip used by
the manufacturer. GPIO expanders equipped with a PCF8574 use the
address 0x27 , while those using PCF8574A chips use 0x3F . Consult your
LCD's documentation.

Another using declaration creates an instance of Pcf8574 and passes the


I2cDevice into the constructor. This instance represents the GPIO expander.

Another using declaration creates an instance of Lcd2004 to represent the


display. Several parameters are passed to the constructor describing the
settings to use to communicate with the GPIO expander. The GPIO expander
is passed as the controller parameter.

A while loop runs indefinitely. Each iteration:


a. Clears the display.
b. Sets the cursor position to the first position on the current line.
c. Writes the current time to the display at the current cursor position.
d. Iterates the current line counter.
e. Sleeps 1000 ms.

4. Build the app. If using the .NET CLI, run dotnet build . To build in Visual Studio,
press Ctrl + Shift + B .

5. Deploy the app to the SBC as a self-contained app. For instructions, see Deploy
.NET apps to Raspberry Pi. Make sure to give the executable execute permission
using chmod +x .

6. Run the app on the Raspberry Pi by switching to the deployment directory and
running the executable.

Bash
./LcdTutorial

Observe the LCD character display as the current time displays on each line.

 Tip

If the display is lit but you don't see any text, try adjusting the contrast dial on
the back of the display.

7. Terminate the program by pressing Ctrl + C .

Congratulations! You've displayed text on an LCD using a I2C and a GPIO expander!

Get the source code


The source for this tutorial is available on GitHub .

Next steps
Learn to use General Purpose Input/Output to blink an LED
Read values from an analog-to-digital
converter
Article • 04/08/2023

An analog-to-digital converter (ADC) is a device that can read an analog input voltage
value and convert it into a digital value. ADCs are used for reading values from
thermistors, potentiometers, and other devices that change resistance based on certain
conditions.

In this topic, you will use .NET to read values from an ADC as you modulate the input
voltage with a potentiometer.

Prerequisites
ARM-based (ARMv7 or greater) single-board computer (SBC)
MCP3008 analog-to-digital converter
Three-pin potentiometer
Breadboard
Jumper wires
Raspberry Pi GPIO breakout board (optional/recommended)
.NET SDK 7 or later

7 Note

This tutorial is written assuming the target device is Raspberry Pi. However, this
tutorial can be used for any Linux-based SBC that supports .NET, such as Orange Pi,
ODROID, and more.

Prepare the SBC


Ensure your SBC is configured to support the following services:

SSH
SPI

For many devices, no additional configuration is required. For Raspberry Pi, use the
raspi-config command. For more information on raspi-config , refer to the Raspberry

Pi documentation .
Prepare the hardware
Use the hardware components to build the circuit as depicted in the following diagram:

The MCP3008 uses Serial Peripheral Interface (SPI) to communicate. The following are
the connections from the MCP3008 to the Raspberry Pi and potentiometer:

VDD to 3.3V (shown in red)


VREF to 3.3V (red)
AGND to ground (black)
CLK to SCLK (orange)
DOUT to MISO (orange)
DIN to MOSI (orange)
CS/SHDN to CE0 (green)
DGND to ground (black)
CH0 to variable (middle) pin on potentiometer (yellow)

Supply 3.3V and ground to the outer pins on the potentiometer. Order is unimportant.

Refer to the following pinout diagrams as needed:

MCP3008 Raspberry Pi GPIO


MCP3008 Raspberry Pi GPIO

 

Image courtesy Raspberry Pi Foundation .

 Tip

A GPIO breakout board in conjunction with a breadboard is recommended to


streamline connections to the GPIO header.

Create the app


Complete the following steps in your preferred development environment:

1. Create a new .NET Console App using either the .NET CLI or Visual Studio. Name it
AdcTutorial.

.NET CLI

dotnet new console -o AdcTutorial


cd AdcTutorial

2. Add the Iot.Device.Bindings package to the project. Use either .NET CLI from the
project directory or Visual Studio.

.NET CLI

dotnet add package Iot.Device.Bindings --version 2.2.0-*

3. Replace the contents of Program.cs with the following code:

C#
using System;
using System.Device.Spi;
using System.Threading;
using Iot.Device.Adc;

var hardwareSpiSettings = new SpiConnectionSettings(0, 0);

using SpiDevice spi = SpiDevice.Create(hardwareSpiSettings);


using var mcp = new Mcp3008(spi);
while (true)
{
Console.Clear();
double value = mcp.Read(0);
Console.WriteLine($"{value}");
Console.WriteLine($"{Math.Round(value/10.23, 1)}%");
Thread.Sleep(500);
}

In the preceding code:

hardwareSpiSettings is set to a new instance of SpiConnectionSettings . The

constructor sets the busId parameter to 0 and the chipSelectLine parameter


to 0.
A using declaration creates an instance of SpiDevice by calling
SpiDevice.Create and passing in hardwareSpiSettings . This SpiDevice
represents the SPI bus. The using declaration ensures the object is disposed
and hardware resources are released properly.
Another using declaration creates an instance of Mcp3008 and passes the
SpiDevice into the constructor.

A while loop runs indefinitely. Each iteration:


a. Clears the console.
b. Reads the value of CH0 on the ADC by calling mcp.Read(0) .
c. Writes the raw value to the console.
d. Writes the value to the console formatted as a percentage.
To calculate the percentage, the value is divided by 10.23. The MCP3008
is a 10-bit ADC, which means it returns 1024 possible values ranging 0-
1023. Dividing the value by 10.23 represents the value as a percentage.
The percentage is rounded to the nearest 0.1.
e. Sleeps 500 ms.

4. Build the app. If using the .NET CLI, run dotnet build . To build in Visual Studio,
press Ctrl + Shift + B .
5. Deploy the app to the SBC as a self-contained app. For instructions, see Deploy
.NET apps to Raspberry Pi. Make sure to give the executable execute permission
using chmod +x .

6. Run the app on the Raspberry Pi by switching to the deployment directory and
running the executable.

Bash

./AdcTutorial

Observe the output as you rotate the potentiometer dial. This is due to the
potentiometer varying the voltage supplied to CH0 on the ADC. The ADC
compares the input voltage on CH0 to the reference voltage supplied to VREF to
generate a value.

7. Terminate the program by pressing Ctrl + C .

Congratulations! You've used SPI to read values from an analog-to-digital converter.

Get the source code


The source for this tutorial is available on GitHub .

Next steps
Learn to use General Purpose Input/Output to blink an LED
Deploy .NET apps on ARM single-board
computers
Article • 04/08/2023

Deployment of .NET apps to single-board computers is identical to that of any other


platform. Your app can run as self-contained or framework-dependent deployment
modes. There are advantages to each strategy. For more information, see .NET
application publishing overview.

Deploying a framework-dependent app

To deploy your app as a framework-dependent app, complete the following steps:

1. Ensure SSH is enabled on your device. For Raspberry Pi, refer to Setting up an SSH
Server in the Raspberry Pi documentation .

2. Install .NET on the device using the dotnet-install scripts. Complete the following
steps from a Bash prompt on the device (local or SSH):

a. Run the following command to install .NET:

Bash

curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --


channel STS
7 Note

This installs the latest version. If you need a specific version, replace the --
channel STS parameter with --version <VERSION> , where <VERSION> is the
specific build version.

b. To simplify path resolution, add a DOTNET_ROOT environment variable and add


the .dotnet directory to $PATH with the following commands:

Bash

echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc


echo 'export PATH=$PATH:$HOME/.dotnet' >> ~/.bashrc
source ~/.bashrc

c. Verify the .NET installation with the following command:

.NET CLI

dotnet --version

Verify the displayed version matches the version you installed.

3. Publish the app on the development computer as follows, depending on


development environment.

If using Visual Studio, deploy the app to a local folder. Before publishing,
select Edit in the publish profile summary and select the Settings tab. Ensure
that Deployment mode is set to Framework-dependent and Target runtime is
set to Portable.
If using the .NET CLI, use the dotnet publish command. No additional
arguments are required.

4. Using an SFTP client like scp , copy the files from the publish location on the
development computer to a new folder on the SBC.

For example, to use the scp command to copy files from the development
computer to your SBC, open a command prompt and execute the following:

Console

scp -r /publish-location/* pi@raspberrypi:/home/pi/deployment-location/


Where:

The -r option instructs scp to copy files recursively.


/publish-location/ is the folder you published to in the previous step.
pi@raspberypi is the user and host names in the format
<username>@<hostname> .

/home/pi/deployment-location/ is the new folder on the SBC.

 Tip

Recent versions of Windows have OpenSSH, which includes scp , pre-installed.

5. From a Bash prompt on the Raspberry Pi (local or SSH), run the app. To do this, set
the deployment folder as the current directory and execute the following
command (where HelloWorld.dll is the entry point of the app):

.NET CLI

dotnet HelloWorld.dll

Deploying a self-contained app

To deploy your app as a self-contained app, complete the following steps:

1. Ensure SSH is enabled on your device. For Raspberry Pi, refer to Setting up an SSH
Server in the Raspberry Pi documentation .
2. Publish the app on the development computer as follows, depending on
development environment.

If using Visual Studio, deploy the app to a local folder. Before publishing,
select Edit in the publish profile summary and select the Settings tab. Ensure
that Deployment mode is set to Self-contained and Target runtime is set to
linux-arm64.

If using the .NET CLI, use the dotnet publish command with the --runtime
linux-arm64 and --self-contained arguments:

.NET CLI

dotnet publish --runtime linux-arm64 --self-contained

) Important

If you're using a 32-bit OS, you need to target the linux-arm runtime.

3. Using an SFTP client like scp , copy the files from the publish location on the
development computer to a new folder on the SBC.

For example, to use the scp command to copy files from the development
computer to your SBC, open a command prompt and execute the following:

Console

scp -r /publish-location/* pi@raspberrypi:/home/pi/deployment-location/

Where:

The -r option instructs scp to copy files recursively.


/publish-location/ is the folder you published to in the previous step.
pi@raspberypi is the user and host names in the format

<username>@<hostname> .
/home/pi/deployment-location/ is the new folder on the SBC.

 Tip

Recent versions of Windows have OpenSSH, which includes scp , pre-installed.


4. From a Bash prompt on the device (local or SSH), run the app. To do this, set the
current directory to the deployment location and complete the following steps:

a. Give the executable execute permission (where HelloWorld is the executable file
name).

Bash

chmod +x HelloWorld

b. Run the executable.

Bash

./HelloWorld
Debug .NET apps on ARM single-board
computers
Article • 04/08/2023

Debugging .NET apps running on ARM-based SBCs like Raspberry Pi presents a unique
challenge. If desired, you can install Visual Studio Code and the .NET SDK on the device
and develop locally. However, the device's performance is such that coding and
debugging locally is not ideal. Additionally, the Visual Studio Code extension for C# is
not compatible with 32-bit ARM operating systems. Consequently, functionality like
IntelliSense and debugging in Visual Studio Code on ARM devices is only supported in
64-bit systems.

For these reasons, it's strongly recommended that you develop your app on a
development computer and then deploy the app to the device for remote debugging. If
you wish to develop and debug locally on the device, the following is required:

A 64-bit OS with a desktop environment, such as Raspberry Pi OS (64-bit).


Visual Studio Code with the C# extension .
Disable the hardware acceleration .
.NET SDK 6.0 or later.
Install using the dotnet-install script as in a framework-dependent deployment.
Be sure to add a DOTNET_ROOT environment variable and add the .dotnet
directory to $PATH .

The rest of this article describes how to debug .NET apps on single-board computers
remotely from a development computer.

) Important

As of this writing, remotely debugging .NET 7 apps in linux-arm environments is


unreliable and may cause the process to exit prematurely. This issue is under
investigation. .NET 6 apps that target linux-arm and .NET 7 apps that target linux-
arm64 are unaffected.

Debug from Visual Studio Code (cross-


platform)
Debugging .NET on single-board computers from Visual Studio Code requires
configuration steps on the SBC and in the project's launch.json file.

Enable SSH on the SBC


SSH is required for remote debugging. To enable SSH on Raspberry Pi, refer to Enable
SSH in the Raspberry Pi documentation . Ensure that you have configured
passwordless SSH .

) Important

This example requires you to configure passwordless SSH on your device, as


OpenSSH doesn't support passing passwords on the command line. If you need to
use a password, consider substituting the plink tool included with PuTTY for ssh.

Install the Visual Studio Remote Debugger on the SBC


Within a Bash console on the SBC (either in a local session or via SSH), run the following
command. This command downloads and installs the Visual Studio Remote Debugger
on the device:

Bash

curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -l


~/vsdbg

Set up launch.json in Visual Studio Code


On the development computer, add a launch configuration to the project's launch.json.
If the project doesn't have a launch.json file, add one by switching to the Run tab,
selecting create a launch.json file, and selecting .NET or .NET Core in the dialog.

The new configuration in launch.json should look similar to one of the following:

Self-contained

JSON

"configurations": [
{
"name": ".NET Remote Launch - Self-contained",
"type": "coreclr",
"request": "launch",
"program": "~/sample/sample",
"args": [],
"cwd": "~/sample",
"stopAtEntry": false,
"console": "internalConsole",
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "ssh",
"pipeArgs": [
"pi@raspberrypi"
],
"debuggerPath": "~/vsdbg/vsdbg"
}
},

Notice the following:

program is the executable file created by dotnet publish .

cwd is the working directory to use when launching the app on the device.

pipeProgram is the path to an SSH client on the local machine.


pipeArgs are the parameters to be passed to the SSH client. Be sure to specify

the password parameter, as well as the pi user in the format


<user>@<hostname> .

Deploy the app


Deploy the app as described in Deploy .NET apps to ARM single-board computers.
Ensure the deployment path is the same path specified in the cwd parameter in the
launch.json configuration.

Launch the debugger


In Visual Studio Code, on the Run and Debug tab, select the configuration you added to
launch.json and select Start Debugging. The app launches on the device. The debugger
may be used to set breakpoints, inspect locals, and more.
Use .NET IoT Libraries on Windows,
Linux, and macOS computers
Article • 01/26/2024

The .NET IoT libraries are commonly used to develop code for Raspberry Pi and other
IoT devices. However, you can also use them to develop code for Windows, Linux, and
macOS PCs using a USB-to-serial adapter such as the FTDI FT232H . This article shows
you how to use the .NET IoT libraries to communicate with devices connected to the
FT232H adapter.

 Tip

This article uses an FTDI FT232H adapter, but you can use any USB-to-serial
adapter that is supported by the .NET IoT libraries, such as the FT2232H, FT4232H,
and FT4222. Check the list of supported device bindings for more information.

Prerequisites
Ensure you have installed the D2XX drivers for your USB-to-serial adapter, which are
found on the FTDI website .

7 Note

Windows devices may automatically install the drivers when you plug in the
adapter. Check Device Manager for a device named USB Serial Converter listed
under Universal Serial Bus controllers. The device's driver provider should be FTDI.

List available devices


Before you can create a GPIO, I2C, or SPI device, you must identify the connected USB-
to-serial adapter. The following code lists the connected FTDI devices:

C#

using Iot.Device.FtCommon;

var devices = FtCommon.GetDevices();


Console.WriteLine($"{devices.Count} available device(s)");
foreach (var device in devices)
{
Console.WriteLine($" {device.Description}");
Console.WriteLine($" Flags: {device.Flags}");
Console.WriteLine($" Id: {device.Id}");
Console.WriteLine($" LocId: {device.LocId}");
Console.WriteLine($" Serial number: {device.SerialNumber}");
Console.WriteLine($" Type: {device.Type}");
}

if (devices.Count == 0)
{
Console.WriteLine("No device connected");
return;
}

In the preceding code, the FtCommon.GetDevices() method returns a list of all connected
FTDI devices.

Use a GPIO device


Here's a hardware implementation of the Blink an LED tutorial that uses the FTDI
FT232H adapter to control an LED:

In the preceding image, the LED circuit is very similar to the original tutorial. The only
difference is that the LED is connected to pin D7 on the FT232H adapter instead of pin
18 on the Raspberry Pi.

The code for the tutorial is also similar to the original tutorial:

C#

using System.Device.Gpio;
using Iot.Device.Ft232H;
using Iot.Device.FtCommon;

Console.WriteLine("Blinking LED. Press Ctrl+C to end.");


Ft232HDevice ft232h = new Ft232HDevice(FtCommon.GetDevices()[0]);
GpioController controller = ft232h.CreateGpioController();

int pin = Ft232HDevice.GetPinNumberFromString("D7");


controller.OpenPin(pin, PinMode.Output);
bool ledOn = true;
while (true)
{
controller.Write(pin, ledOn ? PinValue.High : PinValue.Low);
Thread.Sleep(1000);
ledOn = !ledOn;
}

In the preceding code:

An instance Ft232HDevice is created by passing the first device ID returned by


FtCommon.GetDevices() to the constructor.

An instance of GpioController named controller is created by calling


CreateGpioController() on the Ft232HDevice instance. This GpioController

instance performs the same functions as the GpioController instance in the


original tutorial.
The integer value of the pin is retrieved by calling GetPinNumberFromString() on
the Ft232HDevice instance and passing in the alphanumeric pin name D7.
The rest of the code is identical to the original tutorial.

Use an I2C device


For I2C communication, the D0 and D1 pins on the FT232H adapter are used for the SDL
and SCA lines, respectively. The I2C selector switch on the FT232H adapter must be set
to On.

Here's a hardware implementation of the Read environmental conditions from a sensor


tutorial that uses the FTDI FT232H adapter to read temperature, humidity, and
barometric pressure from a BME280 sensor:

In the preceding image:

The D0 and D1 pins on the FT232H adapter are connected to the SDL and SCA pins
on the BME280 breakout board, respectively.
The I2C selector switch on the BME280 breakout board is set to On.

C#

using System.Device.I2c;
using Iot.Device.Bmxx80;
using Iot.Device.Bmxx80.PowerMode;
using Iot.Device.Ft232H;
using Iot.Device.FtCommon;

Ft232HDevice ft232h = new Ft232HDevice(FtCommon.GetDevices()[0]);


I2cConnectionSettings i2cSettings = new I2cConnectionSettings(0,
Bme280.SecondaryI2cAddress);

using I2cDevice i2cDevice = ft232h.CreateI2cDevice(i2cSettings);


using Bme280 bme280 = new Bme280(i2cDevice);

int measurementTime = bme280.GetMeasurementDuration();

while (true)
{
Console.Clear();

bme280.SetPowerMode(Bmx280PowerMode.Forced);
Thread.Sleep(measurementTime);

bme280.TryReadTemperature(out var tempValue);


bme280.TryReadPressure(out var preValue);
bme280.TryReadHumidity(out var humValue);
bme280.TryReadAltitude(out var altValue);

Console.WriteLine($"Temperature:
{tempValue.DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Pressure: {preValue.Hectopascals:#.##} hPa");
Console.WriteLine($"Relative humidity: {humValue.Percent:#.##}%");
Console.WriteLine($"Estimated altitude: {altValue.Meters:#} m");

Thread.Sleep(1000);
}

In the preceding code:

An instance Ft232HDevice is created by passing the first device ID returned by


FtCommon.GetDevices() to the constructor.

An instance of I2cDevice is created by calling CreateI2cDevice() on the


Ft232HDevice instance. This I2cDevice instance performs the same functions as

the I2cDevice instance in the original tutorial.


The rest of the code is identical to the original tutorial.

Use an SPI device


For SPI communication, the D0, D1, D2, and D3 pins on the FT232H adapter are used for
the SCK, MOSI, MISO, and CS lines, respectively. The I2C selector switch on the FT232H
adapter must be set to Off.

Here's a hardware implementation of the Read values from an analog-to-digital


converter tutorial that uses the FTDI FT232H adapter to read values from an MCP3008
ADC:

In the preceding image:

The D0, D1, D2, and D3 pins on the FT232H adapter are connected to the CLK,
DIN, DOUT,and CS/SHDN pins on the MCP3008, respectively.
The I2C selector switch on the MCP3008 breakout board is set to Off.

C#

using System.Device.Gpio;
using System.Device.Spi;
using Iot.Device.Adc;
using Iot.Device.Ft232H;
using Iot.Device.FtCommon;

var devices = FtCommon.GetDevices();


var ft232h = new Ft232HDevice(devices[0]);
var hardwareSpiSettings = new SpiConnectionSettings(0, 3) { ClockFrequency =
1_000_000, DataBitLength = 8, ChipSelectLineActiveState = PinValue.Low };
using SpiDevice spi = ft232h.CreateSpiDevice(hardwareSpiSettings);
using var mcp = new Mcp3008(spi);
while (true)
{
Console.Clear();
double value = mcp.Read(0);
Console.WriteLine($"{value}");
Console.WriteLine($"{Math.Round(value/10.23, 1)}%");
Thread.Sleep(500);
}

In the preceding code:

An instance Ft232HDevice is created by passing the first device ID returned by


FtCommon.GetDevices() to the constructor.

An instance of SpiDevice is created by calling CreateSpiDevice() on the


Ft232HDevice instance. This SpiDevice instance performs the same functions as

the SpiDevice instance in the original tutorial.


The rest of the code is identical to the original tutorial.

Get the code


The code for this tutorial is available on GitHub .

6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.

You might also like