Skip to contentSkip to author details

raspberry pi

A 3-post collection

Driving the Max 7219 7-Segment Display Module from ASP.NET Core on the Raspberry Pi

Written by Michael Earls
 programming  electronics  raspberry pi  C#  .NET Core

In a previous blog post, I discussed how to create a dotnet core 2.0 WebApi application to run on the Raspberry Pi and trigger an LED. This post will build upon that by outlining the steps I took to connect a Max 7219 7-Segment Display Module to the Pi and extend my WebApi to display numbers on the display.

I recently purchased a Max 7219 7-Segment Display Module on eBay. It came in the mail today and I got right to work interfacing it with my Raspberry Pi. I used the excellent instructions found in this post on Plingboot:

Driving the Max7219 with the Raspberry Pi.

I had to convert the C++ code to C# to get it to work with dotnet core, but that didn't take too long. I also added a hex decoder and the ability to use multiple digits.

I hooked up my Pi using the following pin configuration:

  • Pin 11 (Gpio 17) - Data (DIN)
  • Pin 15 (Gpio 22) - Clock (CLK)
  • Pin 16 (Gpio 23) - Load (CS)
  • Pin 1 (3.3v) - 220 Ohm resistor, then to VCC
  • Pin 6 (GND) - GND

I used the same Cake code and foundation WebApi code and added a new Controller for the Max7219. Here is the code:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using Microsoft.AspNetCore.Mvc;  
using Unosquare.RaspberryIO;  
using Unosquare.RaspberryIO.Gpio;

namespace WebApi.Controllers  
{
    [Route("api/[controller]")]
    public class Max7219Controller : Controller
    {
        private GpioPin DataPin = Pi.Gpio.Pin00; // GPIO 17 (WiringPi pin num 0)  header pin 11
        private GpioPin ClockPin = Pi.Gpio.Pin03; // GPIO 22 (WiringPi pin num 3)   header pin 15
        private GpioPin LoadPin = Pi.Gpio.Pin04; // GPIO 23 (WiringPi pin num 4)   header pin 16

        private Dictionary<char, int> Characters = new Dictionary<char, int> 
        {
            {'-', 0b01000000},
            {'0', 0b01111110},
            {'1', 0b00110000},
            {'2', 0b01101101},
            {'3', 0b01111001},
            {'4', 0b00110011},
            {'5', 0b01011011},
            {'6', 0b01011111},
            {'7', 0b01110000},
            {'8', 0b01111111},
            {'9', 0b01111011},
            {'A', 0b01110111},
            {'B', 0b00011111},
            {'C', 0b01001110},
            {'D', 0b00111101},
            {'E', 0b01001111},
            {'F', 0b01000111},
            {'O', 0b00011101},
            {'R', 0b00000101},
            {' ', 0b00000000}
        };
        private int DECODE_MODE = 0x09;
        private int INTENSITY = 0x0a;
        private int SCAN_LIMIT = 0x0b;
        private int SHUTDOWN = 0x0c;
        private int DISPLAY_TEST = 0x0f;

        public Max7219Controller()
        {
            //We need 3 output pins to control the Max7219: Data, Clock and Load
            DataPin.PinMode = GpioPinDriveMode.Output;
            ClockPin.PinMode = GpioPinDriveMode.Output;
            LoadPin.PinMode = GpioPinDriveMode.Output;
        }

        // GET api/values
        [HttpGet]
        public IEnumerable<string> Get()
        {
            Max7219Send(DISPLAY_TEST, 1);
            Max7219Send(DECODE_MODE, 0);   // Set BCD decode mode off
            Max7219Send(INTENSITY, 1);     // set brightness 0 to 15
            Max7219Send(SHUTDOWN, 1);      // come out of shutdown mode    / turn on the digits
            return new string[] { "Test mode. Call Max7219/{number} to show a number on the display" };
        }

        [HttpGet("{value}")]
        public string Get(int value)
        {
            /* 
            BCD decode mode off : data bits correspond to the segments (A-G and DP) of the seven segment display.
            BCD mode on :  0 to 15 =  0 to 9, -, E, H, L, P, and ' '
            */

            Max7219Send(DECODE_MODE, 1);   // Set BCD decode mode on
            Max7219Send(INTENSITY, 1);     // set brightness 0 to 15
            Max7219Send(SHUTDOWN, 1);      // come out of shutdown mode    / turn on the digits
            Max7219Send(1,value);

            /*
            var pin = Pi.Gpio.Pin00;
            pin.PinMode = GpioPinDriveMode.Output;
            pin.Write(value);
            return $"pin {pin.BcmPinNumber} set to {value}";
            */

            return $"sent {value} to Max7219";
        }


        /*
        GetFormatted() will set the value based on the first argument passed in. It will either display the hex or the decimal/integer value.
        http://servername/api/max7219/hex/10
        will display an "A"
         */
        [HttpGet("{format}/{value}")]
        public string GetFormatted(string format, int value)
        {
            Max7219Send(DISPLAY_TEST, 0);  // Disable test mode
            Max7219Send(INTENSITY, 0x01);     // set brightness 0 to 15
            Max7219Send(SHUTDOWN, 0x01);      // come out of shutdown mode    / turn on the digits
            Max7219Send(SCAN_LIMIT, 0x07);    // use all digits
            /* 
            BCD decode mode off : data bits correspond to the segments (A-G and DP) of the seven segment display.
            BCD mode on :  0 to 15 =  0 to 9, -, E, H, L, P, and ' '
            */

            Max7219Send(DECODE_MODE, 0);   // Set BCD decode mode off

            var outValue = format.ToLower() == "hex" ? value.ToString("X") : value.ToString();

            if (outValue.Length > 8)
            {
                // send error message
                outValue = "ERROR";
            }

            // make sure we have enough characters to work with
            outValue = outValue.PadLeft(8, ' ');
            var values = outValue.Reverse().Take(8).ToArray();

            for (int i = 0; i < 8; i++)
            {
                var outChar = values[i];
                Max7219Send(i + 1, Characters[outChar]);
            }

            return $"sent {outValue.Trim()} to Max7219 in {format} format.";
        }

        private void Max7219Send(int regNumber, int dataOut)
        {
            LoadPin.Write(GpioPinValue.High); // set LOAD High to start
            Send16Bits((regNumber << 8) + dataOut);   // send 16 bits ( reg number + dataout )
            LoadPin.Write(GpioPinValue.Low); // LOAD Low to latch
            LoadPin.Write(GpioPinValue.High); // set LOAD high to finish
        }

        private void Send16Bits(int output)
        {
            for(short i = 16; i > 0; i--)
            {
                int mask = 1 << (i - 1); // calculate bitmask

                ClockPin.Write(GpioPinValue.Low);

                // send one bit on the data pin
                if (Convert.ToBoolean(output & mask))
                {
                    DataPin.Write(GpioPinValue.High);
                }
                else
                {
                    DataPin.Write(GpioPinValue.Low);
                }

                ClockPin.Write(GpioPinValue.High);
            }
        }
    }
}

This code will allow you to use either decimal(integer) or hexadecimal values depending on how you call the method.

http://servername/api/max7219/hex/42

will display "2A" on the display, while

http://servername/api/max7219/decimal/42

will display "42".

Also, if the output data is more than 8 characters in length, it will display "Error" (line 113).
ERROR ERROR on the Max7219 8-character 7-segment display

Update - November 29, 2017 - Here are the hexadecimal digits for versions of C# prior to 7 that do not support the binary literals:

private Dictionary<char, int> Characters = new Dictionary<char, int>  
{
    {'0', 0x7e},
    {'1', 0x30},
    {'2', 0x6d},
    {'3', 0x79},
    {'4', 0x33},
    {'5', 0x5b},
    {'6', 0x5f},
    {'7', 0x70},
    {'8', 0x7f},
    {'9', 0x7b},
    {'A', 0x77},
    {'B', 0x1f},
    {'C', 0x4e},
    {'D', 0x3d},
    {'E', 0x4f},
    {'F', 0x47},
    {'O', 0x1d},
    {'R', 0x05},
    {'-', 0x40},
    {' ', 0x00}
};

Running a .NET Core 2.0 WebApi app on the Raspberry Pi (Raspbian)

Written by Michael Earls
 .NET Core  .NET  WebAPI  raspberry pi  programming

I wanted to create a web api app that answered calls on my Raspberry Pi. The first step I learned was how to install and configure .NET core 2.0 on the Raspberry Pi.

Note - .NET Core only runs on the Raspberry Pi 2 or 3. It will not run on the Raspberry Pi Zero or Zero W.

To get this working, I followed these great instructions from Jeremy Lindsay:

Running a .NET Core 2 app on Raspbian Jessie, and deploying to the Pi with Cake

Once I had this template installed, I simply created a new .NET Core web api app:

Note - Make sure you install the .NET core 2.0 SDK before attempting any of this. .NET Downloads

dotnet new webapi -n WebApiApp

I added the following line of code to the Program.cs file. This will ensure that the Pi answers to all host names (so I can access it from other computers other than localhost).

WebHost.CreateDefaultBuilder(args)  
    .UseStartup()
    .UseUrls("http://*:5000") // add this line
    .Build();

I then copied the following files and folders from the directory created in the sample from the article above to my new WebApiApp folder:

  • build.cake
  • tools
  • publish

I opened up build.cake and changed the correct app name to the name of the project. Here are my configuration settings:

///////////////////////////////////////////////////////////////////////  
// ARGUMENTS (WITH DEFAULT PARAMETERS FOR LINUX (Ubuntu 16.04, Raspbian Jessie, etc)
///////////////////////////////////////////////////////////////////////
var runtime = Argument("runtime", "linux-arm");  
var destinationIp = Argument("destinationPi", "192.168.1.113");  
var destinationDirectory = Argument("destinationDirectory", @"/home/pi/DotNetConsoleApps/WebApiApp");  
var username = Argument("username", "pi");  
var sessionname = Argument("sessionname", "Raspberry Pi");  
var executableName = Argument("executableName", "WebApiApp");

I ensured that the /home/pi/DotNetConsoleApps/WebApiApp directory existed on the Raspberry Pi and then ran the build script from the Powershell terminal:

../build

This used cake to clean, build, and deploy my web api app onto my Raspberry Pi.

Once this was complete, I switched to my PuTTY terminal and typed in ./WebApiApp to start the Web Api app. It started listening on port 5000. I was able to access the Web Api from a browser on my PC by using the IP address of the Raspberry Pi:

http://192.168.1.113:5000/api/values

This displayed the default project api controller output of the template app created in the earlier step.

I can't believe how easy this was. .NET core is really improving the development workflow immensely over previous versions of .NET. This was so much better than what came before.

Update: I was able to get an LED wired up to the WebApi by using this great article from Carlos Mendible:

Note - When you use the Gpio library, you will need to start your WbApiApp using sudo ./WebApiApp due to the need to access IO pins

Toggle Raspberry Pi GPIO Pins with ASP.NET Core 2.0

I wired the LED to physical pin 11 on the Pi. Here's my controller (not much different from his sample):

using System.Collections.Generic;  
using Microsoft.AspNetCore.Mvc;  
using Unosquare.RaspberryIO;  
using Unosquare.RaspberryIO.Gpio;

namespace WebApi.Controllers  
{
    [Route("api/[controller]")]
    public class GpioController : Controller
    {
        // GET api/values
        [HttpGet]
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        [HttpGet("{value}")]
        public string Get(bool value)
        {
            var pin = Pi.Gpio.Pin00;
            pin.PinMode = GpioPinDriveMode.Output;
            pin.Write(value);
            return $"pin {pin.BcmPinNumber} set to {value}";
        }
    }
}

Raspberry Pi with LED wired to WebApi (not pictured) The state of the LED after sending a true to the gpio api method.


Controlling a 40106 Oscillator from Raspberry Pi

Written by Michael Earls
 diy  electronics  Lunetta  programming  development  synth  raspberry pi

I have been working with electronics lately moving slowly toward my end goal of creating circuits that can "create" their own "music".

As part of that journey, I have had to establish some baby steps in order to get there. Here are the basic steps I have accomplished so far:

  • Acquire a Raspberry Pi (or other microcontroller) - I chose the Raspberry Pi zero due to its small size (and great price- $5). I recommend the Raspberry Pi starter kit from Adafruit as it has everything you need to get started
  • Acquire basic electronic components - I ordered a lot of passive components (capacitors, resistors, transistors, jumper wires, breadboard, etc.). I've found that the best place to start is Amazon.com, then head over to Mouser Electronics for the little stuff (in big quantities), then All Electronics as a last resort. Adafruit Industries is the best place to buy add-ons, full kits, and great peripherals for your electronic projects.
  • Install an operating system on the Raspberry Pi - I am using Raspbian
  • Configure the WiFi using the serial cable (I don't have a USB hub, so I only had a single USB port on the Pi. This meant that I couldn't install the WiFi adapter and a keyboard & mouse). Once I had the serial adapter, I used PuTTY (I'm on Windows) to connect to the command line and configured WiFi from there
  • Once WiFi was configured, I installed an RDP server so I could use the Remote Desktop client to log into the desktop on the Pi. I rarely use this, but it's nice to have a desktop from time-to-time.
  • Install mono (cross-platform .NET runtime) - I use the C# programming language at my day job, so I'm comfortable with it. The Raspberry Pi supports many different programming languages, so pick the one you are comfortable with.
  • Install monodevelop on the Pi - monodevelop is the IDE that runs on Linux for developing .NET applications. This was not required, but I wanted to see if it would work. It did, without any issues.
  • Once I had the .NET runtime (mono) installed on my Pi, I opened up Visual Studio 2015 Community edition on my Windows 10 machine, added the raspberry-sharp-io NuGet package, and developed a simple application that toggles pin 7 (turning it on) when it starts up, waits for the user to press the ENTER key, then toggles pin 7 again (turning it off).

Here is the source code for my application (I learned the essentials from the raspberry-sharp-io wiki page):

using Raspberry.IO.GeneralPurpose;
using System;

namespace LunettaControl
{
  class Program
  {
    static void Main(string[] args)
    {
      var led1 = ConnectorPin.P1Pin07.Output();

      using (var connection = new GpioConnection(led1))
      {
        connection.Toggle(led1);

        Console.WriteLine("Press [Enter] to quit");
        Console.ReadLine();

        connection.Toggle(led1);
      }
    }
  }
}

Now I had to set up a circuit on my breadboard that controls a 40106 oscillator so that I can turn it on or off using C# and the Raspberry Pi.

Here is my schematic:

Gated Oscillator Schematic Revision 2

The Raspberry Pi is providing +5v that drives the 40106 and the 4093.

The oscillator (pins 1 and 2 of the 40106) is cycling at a fairly low frequency, as controlled by the 1M resistor and the 470nF capacitor (I haven't timed it as I don't have measuring equipment, though I will be able to write some with C# when I get into input pin management on the raspberry-sharp-io library).

I can now use this to control each individual oscillator on the 40106 independent of one another.

The key to making this work is the NAND gate (pins 1, 2, and 3 of the 4093). When combining the signals from the Raspberry Pi (Pin 7 in this case) and the output from the oscillator on pins 1 and 2, we can control the output on pin 3 of the NAND gate.


IC 4093 Quad 2-input Schmitt trigger NAND Gate

4093 Quad 2-input Schmitt trigger NAND Gate IC


40106 Hex Schmitt trigger IC(Logic Gates)

40106 Hex Schmitt trigger IC (Logic Gates)


That means that when Pin 7 of the Pi goes high, it will raise pin 1 of the gate. Then, each time the 40106 oscillates (it is constantly oscillating as long as power is being sent to the IC), it will trigger pin 2 to go high, which causes the gate to go high on pin 3, thereby causing the transistor to pass +3v to the LED and lighting it up (I wanted to isolate the voltage of the LED from the +5v that's driving the oscillator and gate circuit as a proof of concept for managing components with different voltage requirements).

This schematic is actually bad design. I have a few errors (mostly due to using the transistor incorrectly, as well as using an NAND gate rather than an AND gate).

It seems to be working now, but I'm not sure if it's absolutely correct.

The frequency of the blinking is controlled by the oscillator (because pin 7 of the Pi is staying high based depending on whether or not the program is running). When the program stops running, pin 1 of the gate goes low and the gate remains closed, thereby stopping the blinking (oscillator output). Currently, the LED stays lit. I'm guessing this is caused by using a NAND gate rather than an AND gate.

I'd like to thank the following users for their assistance in getting this circuit working (from the electro-music.com Lunetta forum)

Also, the excellent Intro to Lunetta CMOS Synths document helped me get started. Without it, I would not have known what CMOS chips to buy initially.

Intro to Lunetta CMOS Synths

Here is the thread I created on electro-music.com for my question on gating an oscillator:

Clock signals through a Transistor

The following video is similar to my first video about blinking an LED using C#, but this time, the blinking is the result of the oscillation of the 40106, not a timer built into the program. This program simply toggles pin 7 to the "On" position until the user presses the key and then toggles it off again. This video was created before I modified the schematic and removed the indicator LED.