britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Fri Jun 08, 2012 1:46 am

Well, if you're 13 and already writing code in 4 languagues, you'll be ahead of us lot pretty soon, if not already! I firmly believe that because the Raspberry Pi was design to get young people into computers, it will be someone like you that does something truly amazing with one, that might change the world!

So in answer to your questions:
1. I dont know much about Geany. If seems like its a code editor? If so, then yes you can use that on the Pi if you end up with .cs source files you can compile.
2. You just need to install the mono package and it should bring in all its dependencies. I dont know how to do this on Debian (shouldnt be too hard), but on ArchLinux, I just did a 'pacman -S mono' I think.
3. It should be the same on Debian, but to compile an app, you simply type: mcs test.cs
to make the .exe file, then: mono text.exe to run it
see here for more info https://wiki.archlinux.org/index.php/Mono
4. Yes there is an IDE for Mono, called MonoDevelop http://monodevelop.com/ but I've heard its a bit slow on the PI. Instead, if you have a PC then I would download Visual Studio Express c# (free) and use that to develop your code and compile it, and you just have to copy the .exe over (e.g. using a usb stick) and type mono myprogram.exe to run it, no need to compile on the PI. This would be much quicker to write and debug your code.

Hope that helps! Good luck in changing the world!

melikescheese wrote:Personally I can only deal with three/four languages:

C# (versatile and works brilliantly to develop games, winforms, apps and OS with COSMOS), PHP (fast server side scripting syntax similar to C#),VB.net (my first language) and HTML (CSS) (useful for use with web scripting)

Heck i'm only 13 but python confuses me and i'd love to use my pi to program in C#. Especially some GUI applications. But I have some really basic questions.

+ Can I use Geany to do C#
+ What extra packages would I need to install on debian to use C#
+ How would I compile and run the applications
+ Is there an IDE for mono?

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Fri Jun 08, 2012 2:16 am

x4m & godFather89, you're doing an awesome job! So glad someone has started the pretty difficult (I think) talk of converting from c to c#. I agree with you that its the way to go rather than using my file gpio code, if possible, it will be faster and more reliable.

I was wondering how much you know about c and specifically malloc. I has a quick look at your converted code so far, and I think the stuff in the referenced files libc.so might be the most difficult to do, but I'm sure you know more about c than me. I was assuming to do malloc you're going have to use IntPtr's and marshalling, like here maybe?
http://stackoverflow.com/questions/7281 ... -an-object

Also as for libc.so I am assuming its part of the GNU C library (http://www.gnu.org/software/libc/) . I have followed the download links to v2.15 and downloaded the 21Mb archive file, and there is a malloc.c file in there, I will do more research and see what I can dig up?

x4m wrote:Once more thx to godFather89.
Installed mono runtime, tried to run monodevelop...well RPi is not enough fast (:

Also tried to run gpio ported program and encountereg with absance of "libc.so"... I do not even know where to look for libc.so ..

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Fri Jun 08, 2012 2:21 am

Good point! I think static is the way to go. My code is a little rough round the edges! Thanks for the input!
godFather89 wrote:Just a note: FileGPIO should be implemented as static class or a singleton because there is no need to make multiple instances to read/write from/to a I/O pin.

x4m
Posts: 10
Joined: Wed Jun 06, 2012 4:57 pm
Location: Yekaterinburg, Russia
Contact: ICQ

Re: Mono (C#) anyone?

Fri Jun 08, 2012 5:24 pm

published next version at
https://dl.dropbox.com/u/7610280/GpioProgram.cs

Well, it compiles, runs on mono, finds dll (libc.so.6), outputs like everything is ok.
But I've no multimiter or something like that to measure voltage on output pins. Can anyone say, does that program actually outputs anything to pins?

win-compiled version
https://dl.dropbox.com/u/7610280/Consol ... ation1.exe
Won't work on windows, cannot find "libc.so.6"

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Sat Jun 09, 2012 4:13 am

That's great! I will test it. I have a multimeter and some leds . Do I need those Dlls installed, or are they part of archlinux?

x4m
Posts: 10
Joined: Wed Jun 06, 2012 4:57 pm
Location: Yekaterinburg, Russia
Contact: ICQ

Re: Mono (C#) anyone?

Sat Jun 09, 2012 5:30 am

Probably libc.so.6 is part of any Linux.

Accourding to documents
[DllImport("c")]
[DllImport("libc")]
[DllImport("libc.so")]
[DllImport("libc.so.6")]
Should be equal, but on my debian it works only with last... and I do not know why.

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Sat Jun 09, 2012 4:27 pm

x4m you're a genius! I can confirm the code works perfectly!! Great job! :D

Took me a while to work out why my LED was not flashing (your code uses pins 7-11). I changed which pins it flashes, and all works great...

pin on pi = pin in code (variable g) = pin on slice of pi board
P1-07 = 4 = GP7
P1-11 = 17 = GP0
P1-12 = 18 = GP1
P1-13 = 21 = GP2
P1-15 = 22 = GP3
P1-16 = 23 = GP4
P1-18 = 24 = GP5
P1-22 = 25 = GP6 (I think I read somewhere you can use a total of 17 GPIO pins)

I have not tested inputs yet, but will do in due course, I am sure they work too.

The code seems really fast. I flashed an LED for 10ms ON, 10ms OFF for a couple of seconds and it seemed absolutely fine. I think when i tried this with my FILE based GPIO code it looked like occasionally it couldnt keep up.

You were right about libc.so.6 on ArchLinux, it was just there, no need to install anything.

So, thanks again, will report back on the input stuff once I manage to wire up a switch and resistors accordingly
x4m wrote:published next version at
https://dl.dropbox.com/u/7610280/GpioProgram.cs

Well, it compiles, runs on mono, finds dll (libc.so.6), outputs like everything is ok.
But I've no multimiter or something like that to measure voltage on output pins. Can anyone say, does that program actually outputs anything to pins?

win-compiled version
https://dl.dropbox.com/u/7610280/Consol ... ation1.exe
Won't work on windows, cannot find "libc.so.6"

x4m
Posts: 10
Joined: Wed Jun 06, 2012 4:57 pm
Location: Yekaterinburg, Russia
Contact: ICQ

Re: Mono (C#) anyone?

Sun Jun 10, 2012 8:06 am

Hello, britguy!
Thank you for testing.

About input. I've updated code un dropbox link above to support input.

All updates are:

Code: Select all

public static uint GPIO_IN0 { get { return *(gpio + 13); } }
in class IO.

I think we really should start repository on C# IO for RPi, and place there both, FileGPIO and memory mapped GPIO. I'm planning to add functionality of Gertboard demo software (there are simple programs like on-board LED blinking).
//Update. Tests from Gertboard sw assume you have some external leds. But this code makes builtin ok led blink (: First visible HW response from my board.

Code: Select all

for(int i=0;i<1000;i++)
            {
                IO.GPIO_SET = 1 << 16;
                Thread.Sleep(500);
                IO.GPIO_CLR = 1 << 16;
                Thread.Sleep(500);
            }
I just do not know where to create repo. Github, sourceforge, codeplex?

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Mon Jun 11, 2012 12:00 am

Hi x4m.

Thanks for the adding the input code. I am a little confused about how you read a particular pin...

Would I modify your code to do something like this? (where g is an int representing the pin number), and it returns a bool.

Code: Select all

public static bool GPIO_IN0 { get { return   (  *(gpio + 13) & (1 << g)  ) != 0 ; } }
As regards where to store the code, I agree, I think this will be so useful to so many people. I would try gitub (https://github.com/plans) its free, and its less restrictive about licensing and ensuring the project is kept alive as compared with codeplex, from what I can gather, but I've never setup my own projects on either of them.


aanderson
Posts: 12
Joined: Thu Jun 28, 2012 5:11 am

Re: Mono (C#) anyone?

Fri Jun 29, 2012 5:16 pm

I thought I would share my experiences with Mono on the RaspPi. I'm also a huge fan of C#.

I compiled the latest version of Mono (2.10.8.1) on the Pi itself. I didn't use the packaged version that comes with Debian Lenny because I wanted .NET 4.0 support. This link gives the relevant commands:


Next I had a HD44780 16x2 display that I wanted to integrate with. I used the μLiquidCrystal library for the Netduino but wrote by own GPIO provider for it. All I needed from there was Lcd.cs, ILcdTransferProvider.cs and GPIOLcdTransferProvider.cs. I renamed GPIOLcdTransferProvider.cs to NetopiaGPIOLcdTransferProvider.cs and removed references to the .NET Micro Framework and changed the pins to use my GPIO library.

Here is my NetopiaGPIOLcdTransferProvider.cs provider code.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NetopiaUtils.RaspPi;

// Code modified from original - NetopiaGPIOLcdTransferProvider.cs
// Original code uses license:
// Micro Liquid Crystal Library
// http://microliquidcrystal.codeplex.com
// Appache License Version 2.0

namespace NetopiaUtils.MicroLiquidCrystal
{
    public class NetopiaGPIOLcdTransferProvider : ILcdTransferProvider, IDisposable
    {
        private readonly GPIO _rsPort;
        private readonly GPIO _rwPort;
        private readonly GPIO _enablePort;
        private readonly GPIO[] _dataPorts;
        private readonly bool _fourBitMode;
        private bool _disposed;

        public NetopiaGPIOLcdTransferProvider(GPIO.GPIOPins rs, GPIO.GPIOPins enable, GPIO.GPIOPins d4, GPIO.GPIOPins d5, GPIO.GPIOPins d6, GPIO.GPIOPins d7)
            : this(true, rs, GPIO.GPIOPins.GPIO_NONE, enable, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, d4, d5, d6, d7)
        { }

        public NetopiaGPIOLcdTransferProvider(GPIO.GPIOPins rs, GPIO.GPIOPins rw, GPIO.GPIOPins enable, GPIO.GPIOPins d4, GPIO.GPIOPins d5, GPIO.GPIOPins d6, GPIO.GPIOPins d7)
            : this(true, rs, rw, enable, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, GPIO.GPIOPins.GPIO_NONE, d4, d5, d6, d7)
        { }

        public NetopiaGPIOLcdTransferProvider(GPIO.GPIOPins rs, GPIO.GPIOPins enable, GPIO.GPIOPins d0, GPIO.GPIOPins d1, GPIO.GPIOPins d2, GPIO.GPIOPins d3, GPIO.GPIOPins d4, GPIO.GPIOPins d5, GPIO.GPIOPins d6, GPIO.GPIOPins d7)
            : this(false, rs, GPIO.GPIOPins.GPIO_NONE, enable, d0, d1, d2, d3, d4, d5, d6, d7)
        { }

        public NetopiaGPIOLcdTransferProvider(GPIO.GPIOPins rs, GPIO.GPIOPins rw, GPIO.GPIOPins enable, GPIO.GPIOPins d0, GPIO.GPIOPins d1, GPIO.GPIOPins d2, GPIO.GPIOPins d3, GPIO.GPIOPins d4, GPIO.GPIOPins d5, GPIO.GPIOPins d6, GPIO.GPIOPins d7)
            : this(false, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7)
        { }

        /// <summary>
        /// Creates a variable of type LiquidCrystal. The display can be controlled using 4 or 8 data lines. If the former, omit the pin numbers for d0 to d3 and leave those lines unconnected. The RW pin can be tied to ground instead of connected to a pin on the Arduino; if so, omit it from this function's parameters. 
        /// </summary>
        /// <param name="fourBitMode"></param>
        /// <param name="rs">The number of the CPU pin that is connected to the RS (register select) pin on the LCD.</param>
        /// <param name="rw">The number of the CPU pin that is connected to the RW (Read/Write) pin on the LCD (optional).</param>
        /// <param name="enable">the number of the CPU pin that is connected to the enable pin on the LCD.</param>
        /// <param name="d0"></param>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <param name="d3"></param>
        /// <param name="d4"></param>
        /// <param name="d5"></param>
        /// <param name="d6"></param>
        /// <param name="d7"></param>
        public NetopiaGPIOLcdTransferProvider(bool fourBitMode, GPIO.GPIOPins rs, GPIO.GPIOPins rw, GPIO.GPIOPins enable, 
                                                 GPIO.GPIOPins d0, GPIO.GPIOPins d1, GPIO.GPIOPins d2, GPIO.GPIOPins d3, 
                                                 GPIO.GPIOPins d4, GPIO.GPIOPins d5, GPIO.GPIOPins d6, GPIO.GPIOPins d7)
        {
            _fourBitMode = fourBitMode;

            if (rs == GPIO.GPIOPins.GPIO_NONE) throw new ArgumentException("rs");
            _rsPort = new GPIO(rs);

            // we can save 1 pin by not using RW. Indicate by passing GPIO.GPIOPins.GPIO_NONE instead of pin#
            if (rw != GPIO.GPIOPins.GPIO_NONE) // (RW is optional)
                _rwPort = new GPIO(rw);

            if (enable == GPIO.GPIOPins.GPIO_NONE) throw new ArgumentException("enable");
            _enablePort = new GPIO(enable);

            var dataPins = new[] { d0, d1, d2, d3, d4, d5, d6, d7};
            _dataPorts = new GPIO[8];
            for (int i = 0; i < 8; i++)
            {
                if (dataPins[i] != GPIO.GPIOPins.GPIO_NONE)
                    _dataPorts[i] = new GPIO(dataPins[i]);
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~NetopiaGPIOLcdTransferProvider()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _rsPort.Dispose();
                _rwPort.Dispose();
                _enablePort.Dispose();

                for (int i = 0; i < 8; i++)
                {
                    if (_dataPorts[i] != null)
                        _dataPorts[i].Dispose();
                }
                _disposed = true;
            }
            
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }
        }

        public bool FourBitMode
        {
            get { return _fourBitMode; }
        }

        /// <summary>
        /// Write either command or data, with automatic 4/8-bit selection
        /// </summary>
        /// <param name="value">value to write</param>
        /// <param name="mode">Mode for RS (register select) pin.</param>
        /// <param name="backlight">Backlight state.</param>
        public void Send(byte value, bool mode, bool backlight)
        {
            if (_disposed)
                throw new ObjectDisposedException("NetopiaGPIOLcdTransferProvider");

            //TODO: set backlight

            _rsPort.Write(mode);

            // if there is a RW pin indicated, set it low to Write
            if (_rwPort != null)
            {
                _rwPort.Write(false);
            }

            if (!_fourBitMode)
            {
                Write8Bits(value);
            }
            else
            {
                Write4Bits((byte) (value >> 4));
                Write4Bits(value);
            }
        }

        private void Write8Bits(byte value)
        {
            for (int i = 0; i < 8; i++)
            {
                _dataPorts[i].Write(((value >> i) & 0x01) == 0x01);
            }

            PulseEnable();
        }

        private void Write4Bits(byte value)
        {
            for (int i = 0; i < 4; i++)
            {
                _dataPorts[4+i].Write(((value >> i) & 0x01) == 0x01);
            }

            PulseEnable();
        }

        private void PulseEnable()
        {
            _enablePort.Write(false);
            _enablePort.Write(true);  // enable pulse must be >450ns
            _enablePort.Write(false); // commands need > 37us to settle
        }
    }
}
Here is my GPIO library.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

// Based on work done by x4m and britguy (http://www.raspberrypi.org/phpBB3/viewtopic.php?f=34&t=6720)
// DL: https://dl.dropbox.com/u/7610280/GPIO.cs
namespace NetopiaUtils.RaspPi
{
    public class GPIO : IDisposable
    {
        /// <summary>
        /// GPIO connector on the Pi (P1) (as found next to the yellow RCA video socket on the Rpi circuit board)
        /// </summary>
        /// <remarks>
        /// Refer to http://elinux.org/Rpi_Low-level_peripherals for diagram.
        /// P1-01 = bottom left, P1-02 = top left
        /// pi connector P1 pin    = GPIOnum
        ///                  P1-03 = GPIO0
        ///                  P1-05 = GPIO1
        ///                  P1-07 = GPIO4
        ///                  P1-08 = GPIO14
        ///                  P1-10 = GPIO15
        ///                  P1-11 = GPIO17
        ///                  P1-12 = GPIO18
        ///                  P1-13 = GPIO21
        ///                  P1-15 = GPIO22
        ///                  P1-16 = GPIO23
        ///                  P1-18 = GPIO24
        ///                  P1-19 = GPIO10
        ///                  P1-21 = GPIO9
        ///                  P1-22 = GPIO25
        ///                  P1-23 = GPIO11
        ///                  P1-24 = GPIO8
        ///                  P1-26 = GPIO7
        /// So to turn on Pin7 on the GPIO connector, pass in enumGPIOPIN.gpio4 as the pin parameter
        /// </remarks>
        public enum GPIOPins
        {
            GPIO_NONE = -1,
            GPIO00 = 0,
            GPIO01 = 1,
            GPIO04 = 4,
            GPIO07 = 7,
            GPIO08 = 8,
            GPIO09 = 9,
            GPIO10 = 10,
            GPIO11 = 11,
            GPIO14 = 14,
            GPIO15 = 15,
            GPIO17 = 17,
            GPIO18 = 18,
            GPIO21 = 21,
            GPIO22 = 22,
            GPIO23 = 23,
            GPIO24 = 24,
            GPIO25 = 25,
            Pin03 = 0,
            Pin05 = 1,
            Pin07 = 4,
            Pin08 = 14,
            Pin10 = 15,
            Pin11 = 17,
            Pin12 = 18,
            Pin13 = 21,
            Pin15 = 22,
            Pin16 = 23,
            Pin18 = 24,
            Pin19 = 10,
            Pin21 = 9,
            Pin22 = 25,
            Pin23 = 11,
            Pin24 = 8,
            Pin26 = 7
        };

        public enum DirectionEnum { IN, OUT };

        /// <summary>
        /// The path on the Raspberry Pi for the GPIO interface
        /// </summary>
        private const string GPIO_PATH = "/sys/class/gpio/";

        /// <summary>
        /// The currently assigned GPIO pin. Used for class methods and not static methods.
        /// </summary>
        private GPIOPins _pin;

        public GPIO(GPIOPins pin)
        {
            this._pin = pin;
        }

        #region Static Methods
        private static string GetGPIONumber(GPIOPins pin)
        {
            return ((int)pin).ToString(); //e.g. returns 17 for enum value of gpio17
        }

        /// <summary>
        /// this gets called automatically when we try and output to, or input from, a pin
        /// </summary>
        /// <param name="pin"></param>
        /// <param name="direction"></param>
        private static void ExportPin(GPIOPins pin, DirectionEnum direction)
        {
            if (!Directory.Exists(GPIO_PATH + "gpio" + GetGPIONumber(pin)))
            {
                Debug.WriteLine("Exporting " + GetGPIONumber(pin));
                //export
                File.WriteAllText(GPIO_PATH + "export", GetGPIONumber(pin));
            }

            // set i/o direction
            Debug.WriteLine("Setting direction on pin " + pin + "/gpio " + (int)pin + " as " + direction);
            File.WriteAllText(GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/direction", direction.ToString().ToLower());
        }

        /// <summary>
        /// if for any reason you want to unexport a particular pin use this, otherwise just call CleanUpAllPins when you're done
        /// </summary>
        /// <param name="pin">The pin to unexport</param>
        private static void UnexportPin(GPIOPins pin)
        {
            File.WriteAllText(GPIO_PATH + "unexport", GetGPIONumber(pin));
            Debug.WriteLine("unexporting  pin " + pin);
        }

        /// <summary>
        /// Static method to write a value to the specified pin
        /// </summary>
        /// <param name="pin">The pin to write to</param>
        /// <param name="value">value to write</param>
        public static void Write(GPIOPins pin, bool value)
        {
            if (pin == GPIOPins.GPIO_NONE)
                return;

            ExportPin(pin, DirectionEnum.OUT);

            string writeValue = value ? "1" : "0";
            File.WriteAllText(GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/value", writeValue);
            Debug.WriteLine("output to pin " + pin + "/gpio " + (int)pin + ", value was " + value);

            UnexportPin(pin);
        }

        /// <summary>
        /// Static method to read a value to the specified pin
        /// </summary>
        /// <param name="pin">The pin to write to</param>
        /// <returns>The value for the pin</returns>
        public static bool Read(GPIOPins pin)
        {
            bool returnValue = false;

            ExportPin(pin, DirectionEnum.IN);

            string filename = GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/value";
            if (File.Exists(filename))
            {
                string readValue = File.ReadAllText(filename);
                if (readValue.Length > 0 && readValue[0] == '1')
                    returnValue = true;
            }
            else
                throw new Exception(string.Format("Cannot read from {0}. File does not exist", pin));

            Debug.WriteLine("input from pin " + pin + "/gpio " + (int)pin + ", value was " + returnValue);

            UnexportPin(pin);

            return returnValue;
        }
        #endregion

        /// <summary>
        /// Write a value to the pin
        /// </summary>
        /// <param name="value">value to write</param>
        public void Write(bool value)
        {
            // Call the static method
            Write(_pin, value);
        }

        /// <summary>
        /// Static method to read a value to the specified pin
        /// </summary>
        /// <returns>The value for the pin</returns>
        public bool Read()
        {
            // Call the static method
            return Read(_pin);
        }

        public void Dispose()
        {
            return;
        }
    }
}
Here is my main program.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NetopiaUtils.MicroLiquidCrystal;
using NetopiaUtils.RaspPi;
using System.Threading;

namespace RaspPi
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
            GPIO.Write(GPIO.GPIOPins.Pin21, true);
            GPIO.Write(GPIO.GPIOPins.Pin23, true);
            GPIO.Write(GPIO.GPIOPins.Pin11, true);
            GPIO.Write(GPIO.GPIOPins.Pin13, true);
            GPIO.Write(GPIO.GPIOPins.Pin15, true);
            GPIO.Write(GPIO.GPIOPins.Pin19, true);
            Console.ReadLine();
            */

            NetopiaGPIOLcdTransferProvider lcdProvider = new NetopiaGPIOLcdTransferProvider(
                GPIO.GPIOPins.Pin21,
                GPIO.GPIOPins.Pin23,
                GPIO.GPIOPins.Pin11,
                GPIO.GPIOPins.Pin13,
                GPIO.GPIOPins.Pin15,
                GPIO.GPIOPins.Pin19);

            Lcd lcd = new Lcd(lcdProvider);
            lcd.Begin(16, 2);

            lcd.Clear();
            lcd.SetCursorPosition(0, 0);
            lcd.Write("Hello world");
            lcd.SetCursorPosition(0, 1);
            lcd.Write("This is a test");

            
        }
    }
}
My GPIO library is slow. As I have in the comments it's based on the work done by x4m and britguy but I modified it so it opened and closed the file each time. I wanted to see the performance impact of that. There definitely is one. I compared it against the Python script in this post viewtopic.php?f=32&t=8021 that it takes longer for mine to display by 2-3 times. I'll be re-writing it to be more efficient.

My GPIO library not only allows for static methods but for object instantiation, the reason being for compatibility with the .NET Micro Framework.

aanderson
Posts: 12
Joined: Thu Jun 28, 2012 5:11 am

Re: Mono (C#) anyone?

Sat Jun 30, 2012 3:51 am

Abstract base class for GPIO

Code: Select all


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// Author: Aaron Anderson <[email protected]>
namespace NetopiaUtils.RaspPi
{
    public abstract class GPIO : IDisposable
    {
        /// <summary>
        /// Abstract class for the GPIO connector on the Pi (P1) (as found next to the yellow RCA video socket on the Rpi circuit board)
        /// </summary>
        /// <remarks>
        /// Refer to http://elinux.org/Rpi_Low-level_peripherals for diagram.
        /// P1-01 = bottom left, P1-02 = top left
        /// pi connector P1 pin    = GPIOnum
        ///                  P1-03 = GPIO0
        ///                  P1-05 = GPIO1
        ///                  P1-07 = GPIO4
        ///                  P1-08 = GPIO14
        ///                  P1-10 = GPIO15
        ///                  P1-11 = GPIO17
        ///                  P1-12 = GPIO18
        ///                  P1-13 = GPIO21
        ///                  P1-15 = GPIO22
        ///                  P1-16 = GPIO23
        ///                  P1-18 = GPIO24
        ///                  P1-19 = GPIO10
        ///                  P1-21 = GPIO9
        ///                  P1-22 = GPIO25
        ///                  P1-23 = GPIO11
        ///                  P1-24 = GPIO8
        ///                  P1-26 = GPIO7
        /// So to turn on Pin7 on the GPIO connector, pass in enumGPIOPIN.gpio4 as the pin parameter
        /// </remarks>
        public enum GPIOPins
        {
            GPIO_NONE = -1,
            GPIO00 = 0,
            GPIO01 = 1,
            GPIO04 = 4,
            GPIO07 = 7,
            GPIO08 = 8,
            GPIO09 = 9,
            GPIO10 = 10,
            GPIO11 = 11,
            GPIO14 = 14,
            GPIO15 = 15,
            GPIO17 = 17,
            GPIO18 = 18,
            GPIO21 = 21,
            GPIO22 = 22,
            GPIO23 = 23,
            GPIO24 = 24,
            GPIO25 = 25,
            Pin03 = 0,
            Pin05 = 1,
            Pin07 = 4,
            Pin08 = 14,
            Pin10 = 15,
            Pin11 = 17,
            Pin12 = 18,
            Pin13 = 21,
            Pin15 = 22,
            Pin16 = 23,
            Pin18 = 24,
            Pin19 = 10,
            Pin21 = 9,
            Pin22 = 25,
            Pin23 = 11,
            Pin24 = 8,
            Pin26 = 7
        };

        /// <summary>
        /// Specifies the direction of the GPIO port
        /// </summary>
        public enum DirectionEnum { IN, OUT };

        /// <summary>
        /// The currently assigned GPIO pin. Used for class methods and not static methods.
        /// </summary>
        protected GPIOPins _pin;

        /// <summary>
        /// Creates a GPIOFile class with 
        /// </summary>
        /// <param name="pin"></param>
        public GPIO(GPIOPins pin)
            : this(pin,DirectionEnum.OUT,false)
        {
        }

        public GPIO(GPIOPins pin, DirectionEnum direction)
            : this(pin, direction, false)
        {
        }

        public GPIO(GPIOPins pin, DirectionEnum direction, bool initialValue)
        {
            this._pin = pin;
        }

        protected static string GetGPIONumber(GPIOPins pin)
        {
            return ((int)pin).ToString(); //e.g. returns 17 for enum value of gpio17
        }

        /// <summary>
        /// Write a value to the pin
        /// </summary>
        /// <param name="value">The value to write to the pin</param>
        public abstract void Write(bool value);

        /// <summary>
        /// Read a value from the pin
        /// </summary>
        /// <returns>The value read from the pin</returns>
        public abstract bool Read();

        /// <summary>
        /// Dispose of the GPIO pin
        /// </summary>
        public abstract void Dispose();
    }
}
GPIOFile implementation that now keeps track of the exported pins. This is much faster than my previous post.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

// Author: Aaron Anderson <[email protected]>
// Based on work done by x4m and britguy (http://www.raspberrypi.org/phpBB3/viewtopic.php?f=34&t=6720)
// DL: https://dl.dropbox.com/u/7610280/GPIO.cs
namespace NetopiaUtils.RaspPi
{
    public class GPIOFile : GPIO, IDisposable
    {
        /// <summary>
        /// The path on the Raspberry Pi for the GPIO interface
        /// </summary>
        private const string GPIO_PATH = "/sys/class/gpio/";

        private static Dictionary<int, DirectionEnum> _exported = new Dictionary<int, DirectionEnum>();

        /// <summary>
        /// Access to the specified GPIO setup as an output port with an initial value of false (0)
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        public GPIOFile(GPIOPins pin)
            : this(pin,DirectionEnum.OUT,false)
        {
        }

        /// <summary>
        /// Access to the specified GPIO setup with the specified direction with an initial value of false (0)
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        /// <param name="direction">Direction</param>
        public GPIOFile(GPIOPins pin, DirectionEnum direction)
            : this(pin, direction, false)
        {
        }

        /// <summary>
        /// Access to the specified GPIO setup with the specified direction with the specified initial value
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        /// <param name="direction">Direction</param>
        /// <param name="initialValue">Initial Value</param>
        public GPIOFile(GPIOPins pin, DirectionEnum direction, bool initialValue)
            : base(pin, direction, initialValue)
        {
        }

        ~GPIOFile()
        {
            UnexportPin(_pin);
        }

        #region Static Methods
        /// <summary>
        /// Export the GPIO setting the direction. This creates the /sys/class/gpio/gpioXX directory.
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        /// <param name="direction"></param>
        private static void ExportPin(GPIOPins pin, DirectionEnum direction)
        {
            // If the pin is already exported, check it's in the proper direction
            if (_exported.Keys.Contains((int)pin))
                // If the direction matches, return out of the function. If not, change the direction
                if (_exported[(int)pin] == direction)
                    return;
                else
                {
                    File.WriteAllText(GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/direction", direction.ToString().ToLower());
                    _exported[(int)pin] = direction;
                    return;
                }

            if (!Directory.Exists(GPIO_PATH + "gpio" + GetGPIONumber(pin)))
            {
                Debug.WriteLine("Exporting " + GetGPIONumber(pin));
                //export
                File.WriteAllText(GPIO_PATH + "export", GetGPIONumber(pin));
            }

            // set i/o direction
            Debug.WriteLine("Setting direction on pin " + pin + "/gpio " + (int)pin + " as " + direction);
            File.WriteAllText(GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/direction", direction.ToString().ToLower());

            // Update the list of exported pins
            _exported[(int)pin] = direction;
        }

        /// <summary>
        /// Unexport the GPIO. This removes the /sys/class/gpio/gpioXX directory.
        /// </summary>
        /// <param name="pin">The pin to unexport</param>
        private static void UnexportPin(GPIOPins pin)
        {
            Debug.WriteLine("unexporting pin " + pin);
            File.WriteAllText(GPIO_PATH + "unexport", GetGPIONumber(pin));

            // Remove the pin from the list of exported pins
            _exported.Remove((int)pin);
        }

        /// <summary>
        /// Static method to write a value to the specified pin. When using the static methods ensure you use the CleanUp() method when done.
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        /// <param name="value">The value to write to the pin</param>
        public static void Write(GPIOPins pin, bool value)
        {
            if (pin == GPIOPins.GPIO_NONE)
                return;

            ExportPin(pin, DirectionEnum.OUT);

            string writeValue = value ? "1" : "0";
            File.WriteAllText(GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/value", writeValue);
            Debug.WriteLine("output to pin " + pin + "/gpio " + (int)pin + ", value was " + value);
        }

        /// <summary>
        /// Static method to read a value to the specified pin. When using the static methods ensure you use the CleanUp() method when done.
        /// </summary>
        /// <param name="pin">The GPIO pin</param>
        /// <returns>The value read from the pin</returns>
        public static bool Read(GPIOPins pin)
        {
            bool returnValue = false;

            ExportPin(pin, DirectionEnum.IN);

            string filename = GPIO_PATH + "gpio" + GetGPIONumber(pin) + "/value";
            if (File.Exists(filename))
            {
                string readValue = File.ReadAllText(filename);
                if (readValue.Length > 0 && readValue[0] == '1')
                    returnValue = true;
            }
            else
                throw new Exception(string.Format("Cannot read from {0}. File does not exist", pin));

            Debug.WriteLine("input from pin " + pin + "/gpio " + (int)pin + ", value was " + returnValue);

            return returnValue;
        }

        public static void CleanUp()
        {
            // Loop over all exported pins and unexported them
            foreach (GPIOPins pin in _exported.Keys)
            {
                UnexportPin(pin);
            }
        }
        #endregion

        #region Class Methods
        /// <summary>
        /// Write a value to the pin
        /// </summary>
        /// <param name="value">The value to write to the pin</param>
        public override void Write(bool value)
        {
            // Call the static method
            Write(_pin, value);
        }

        /// <summary>
        /// Read a value from the pin
        /// </summary>
        /// <returns>The value read from the pin</returns>
        public override bool Read()
        {
            // Call the static method
            return Read(_pin);
        }

        /// <summary>
        /// Dispose of the GPIO pin
        /// </summary>
        public override void Dispose()
        {
            UnexportPin(_pin);
        }
        #endregion
    }
}


aanderson
Posts: 12
Joined: Thu Jun 28, 2012 5:11 am

Re: Mono (C#) anyone?

Tue Jul 10, 2012 5:49 am

I recently added basic support for the TM1638 display by porting Ricardo Batista's Arduino library.

percramer
Posts: 5
Joined: Thu Aug 09, 2012 12:01 pm

Re: Mono (C#) anyone?

Thu Aug 09, 2012 12:02 pm

Hi all,

well that gpio code works nicely. But did anybody also got i2c reading /writing working from c#?

Regards,

Per

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Fri Aug 10, 2012 1:06 am

No, sorry, not i2c yet, have you found any examples in python? That would be a start . I am working on c# code for SPI (to use an analogue to digital converter such as mcp3008 chip like on gertboard, without needing an kernel drivers like some people are talking about using). So this would give 8 analog inputs with a $3 chip and only take 4 GPIO pins.

aanderson
Posts: 12
Joined: Thu Jun 28, 2012 5:11 am

Re: Mono (C#) anyone?

Tue Aug 14, 2012 5:06 am

The GPIOMem class which is based on Mike McCauley's BCM2835 library has SPI functions in it. I'll also likely expose those in my library. I also have a couple I2C GPIO expanders that I intent to create functions for when they arrive.

EdwinJ85
Posts: 270
Joined: Wed Feb 01, 2012 4:44 pm
Contact: Website

Re: Mono (C#) anyone?

Tue Aug 14, 2012 10:15 pm

This works like a treat, better than the other options I have found online. Thank you very much!

I've changed it a little myself to make it a static class with static methods, I think this will help people use it a little more easily.

Code: Select all

using System;
using System.Collections.Generic; //required for List<>
using System.IO;

//Originally written by Britguy, extended by EdwinJ85 on the Raspberry Pi Forums. 
namespace Pi.GPIO
{
    //GPIO connector on the Pi (P1) (as found next to the yellow RCA video socket on the Rpi circuit board)
    //P1-01 = top left,    P1-02 = top right
    //P1-25 = bottom left, P1-26 = bottom right
    //pi connector P1 pin     = GPIOnum = slice of pi v1.0 board label
    //                  P1-07 = GPIO4   = GP7
    //                  P1-11 = GPIO17  = GP0
    //                  P1-12 = GPIO18  = GP1
    //                  P1-13 = GPIO21  = GP2
    //                  P1-15 = GPIO22  = GP3
    //                  P1-16 = GPIO23  = GP4
    //                  P1-18 = GPIO24  = GP5
    //                  P1-22 = GPIO25  = GP6
    //So to turn on Pin7 on the GPIO connector, pass in enumGPIOPIN.gpio4 as the pin parameter

    /// <summary>
    /// A enumeration to designate a GPIO pin for input or output. Pin 16 is the OK light on the rPi board itself.
    /// </summary>
    public enum PIN
    {
        gpio0 = 0, gpio1 = 1, gpio4 = 4, gpio7 = 7, gpio8 = 8, gpio9 = 9, gpio10 = 10, gpio11 = 11,
        gpio14 = 14, gpio15 = 15, gpio16 = 16, gpio17 = 17, gpio18 = 18, gpio21 = 21, gpio22 = 22, gpio23 = 23, gpio24 = 24, gpio25 = 25
    };

    public static class GPIOHandler
    {

        private enum Direction { IN, OUT };

        private const string GPIO_PATH = "/sys/class/gpio/";

        //contains list of pins exported with an OUT direction
        static List<PIN> _OutExported = new List<PIN>();

        //contains list of pins exported with an IN direction
        static List<PIN> _InExported = new List<PIN>();

        //set to true to write whats happening to the screen
        private const bool DEBUG = true;

        //this gets called automatically when we try and output to, or input from, a pin
        private static void SetupPin(PIN pin, Direction direction)
        {
            //unexport if it we're using it already
            if (_OutExported.Contains(pin) || _InExported.Contains(pin)) UnexportPin(pin);

            //export
            File.WriteAllText(GPIO_PATH + "export", GetPinNumber(pin));

            if (DEBUG) Console.WriteLine("exporting pin " + pin + " as " + direction);

            // set i/o direction
            File.WriteAllText(GPIO_PATH + pin.ToString() + "/direction", direction.ToString().ToLower());

            //record the fact that we've setup that pin
            if (direction == Direction.OUT)
                _OutExported.Add(pin);
            else
                _InExported.Add(pin);
        }

        //no need to setup pin this is done for you
        /// <summary>
        /// This function allows us to control a GPIO pin as output.
        /// </summary>
        /// <param name="pin"></param>
        /// <param name="value"></param>
        public static void OutputPin(PIN pin, bool value)
        {
            //if we havent used the pin before,  or if we used it as an input before, set it up
            if (!_OutExported.Contains(pin) || _InExported.Contains(pin)) SetupPin(pin, Direction.OUT);

            string writeValue = "0";
            if (value) writeValue = "1";
            File.WriteAllText(GPIO_PATH + pin.ToString() + "/value", writeValue);

            if (DEBUG) Console.WriteLine("output to pin " + pin + ", value was " + value);
        }

        //no need to setup pin this is done for you
        /// <summary>
        /// This function allows us to control a GPIO pin as input.
        /// </summary>
        /// <param name="pin"></param>
        /// <returns></returns>
        public static bool InputPin(PIN pin)
        {
            bool returnValue = false;

            //if we havent used the pin before, or if we used it as an output before, set it up
            if (!_InExported.Contains(pin) || _OutExported.Contains(pin)) SetupPin(pin, Direction.IN);

            string filename = GPIO_PATH + pin.ToString() + "/value";
            if (File.Exists(filename))
            {
                string readValue = File.ReadAllText(filename);
                if (readValue != null && readValue.Length > 0 && readValue[0] == '1') returnValue = true;
            }
            else
                throw new Exception(string.Format("Cannot read from {0}. File does not exist", pin));

            if (DEBUG) Console.WriteLine("input from pin " + pin + ", value was " + returnValue);

            return returnValue;
        }

        /// <summary>
        /// if for any reason you want to unexport a particular pin use this, otherwise just call CleanUpAllPins when you're done
        /// </summary>
        /// <param name="pin"></param>
        public static void UnexportPin(PIN pin)
        {
            bool found = false;
            if (_OutExported.Contains(pin))
            {
                found = true;
                _OutExported.Remove(pin);
            }
            if (_InExported.Contains(pin))
            {
                found = true;
                _InExported.Remove(pin);
            }

            if (found)
            {
                File.WriteAllText(GPIO_PATH + "unexport", GetPinNumber(pin));
                if (DEBUG) Console.WriteLine("unexporting  pin " + pin);
            }
        }

        /// <summary>
        /// This function closes the handles to the GPIO pins after use and should ALWAYS be called as the last part of any program using this GPIO library.
        /// </summary>
        public static void CleanUpAllPins()
        {
            for (int p = _OutExported.Count - 1; p >= 0; p--) UnexportPin(_OutExported[p]); //unexport in reverse order
            for (int p = _InExported.Count - 1; p >= 0; p--) UnexportPin(_InExported[p]);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pin"></param>
        /// <returns>returns n for enum value of gpio n</returns>
        private static string GetPinNumber(PIN pin)
        {
            return ((int)pin).ToString(); //e.g. returns 17 for enum value of gpio17
        }

    }

}
Here's a little video of it flashing the OK light on my pi! http://www.youtube.com/watch?v=3_B3ZHMV1n4&feature=plcp
Hello!

britguy
Posts: 16
Joined: Mon May 28, 2012 1:34 am

Re: Mono (C#) anyone?

Wed Aug 15, 2012 11:02 pm

Speed - So How Fast is GPIO?

If you've wondered how fast you can switch things on and off using GPIO, then wonder no longer. Have a look at this article...

http://codeandlife.com/2012/07/03/bench ... pio-speed/

So, the way I read it is that if your using the File GPIO (with a shell script) the most you're going to get is 3,400 times per second. With that running in Mono in the CLR perhaps half that?? (Personally I think about a 10th of that using files).

However, if you use the code posted by X4M (https://dl.dropbox.com/u/7610280/GpioProgram.cs) that uses memory access rather than files, I suspect you'll be in the same order of magnitude as Python wiringPi, maybe 20,000 times per second?

Anyone else have any thoughts on speed of GPIO from C#?

andyclap2
Posts: 2
Joined: Thu Aug 16, 2012 11:00 pm

Re: Mono (C#) anyone?

Thu Aug 16, 2012 11:51 pm

> I've changed it a little myself to make it a static class with static methods, I think this will help people use it a little more easily.

Careful with static classes, while they're easy to use, they make testing difficult.

When developing it's v. useful to be able to run your code against a "fake" version that acts differently - e.g. it might display the output on the screen, write to a log, check for some expected output, whatever you need.
That way when you come to plug in your circuitry you know the code works - so if there are any problems it makes them easier to find.

To make this easy, in the c# object oriented world you generally describe an interface to the thing you're going to depend on, and "code to an interface not an implementation".
The interface (or abstract base class, as per aanderson's code) describes what you can do, but not what/how it gets done, so you can implement it in several different ways. Your code asks for an implementation to be provided at runtime, which your Main() sets up and passes in. Something like:

Code: Select all

public static void Main(string[] args)
{ 
  var gpio=args.Length>0 && args[0]=="test"
    ?new GPIOFake()
    :new GPIOFile(); 
  var amazingIOFlashingThing=new AmazingIOFlashingThing(gpio); 
  amazingIOFlashingThing.DoYourStuff(); 
}
(note - you would generally have a separate test harness, or use a general purpose test framework like nunit rather than do it with an arg like this; and of course I've not provided the interface/base or either implementation here...)

This may of course be overkill for a lot of projects, but when you start to build big complex systems you really need to be able to test each part in isolation. And here endeth chapter 0 of "So, you want to be a software engineer!"

x4m
Posts: 10
Joined: Wed Jun 06, 2012 4:57 pm
Location: Yekaterinburg, Russia
Contact: ICQ

Re: Mono (C#) anyone?

Fri Aug 17, 2012 9:16 am

britguy wrote:Speed - So How Fast is GPIO?
Hi! I've been away for some time. And probably will be away little more :-) I'm still on vacation.

I tested a bit memmap C# gpio output, it worked quite good - about 7.5 MHz. I do not remember actual timings, maybe I'll post test code later, it's on my home laptop.
Actually, It can be faster than C, mono jit-optimize lots of calculations during raw output.

But. GC breaks the performance down. Sometimes I expiriced 10-20 ms delays. That means If I output at 50 KHz control signal to stepping motor, than stand for 1 ms - motor will go out of sync.
Possibly there is a way to mitigate this, may be disable GC or something...

x4m
Posts: 10
Joined: Wed Jun 06, 2012 4:57 pm
Location: Yekaterinburg, Russia
Contact: ICQ

Re: Mono (C#) anyone?

Fri Aug 17, 2012 9:33 am

andyclap2 wrote:static methods
Actually, static methods have a lots of reasons. I do not suppose that controlling GPIO library should follow business-software design pattrens. Engineering dentist database and with all it's rules aimed to bill cleint dry but not deeper and creating io library to save some CPU cycles are a little bit different.

There cannot be two instances of GIOP. And sigleton gives you nothing, but complexity. If you need tests, you can add compile-time switch.

amigarulez
Posts: 42
Joined: Wed Jul 18, 2012 10:16 am

Re: Mono (C#) anyone?

Fri Aug 17, 2012 9:42 am

x4m wrote:
andyclap2 wrote:static methods
Actually, static methods have a lots of reasons. I do not suppose that controlling GPIO library should follow business-software design pattrens. Engineering dentist database and with all it's rules aimed to bill cleint dry but not deeper and creating io library to save some CPU cycles are a little bit different.

There cannot be two instances of GIOP. And sigleton gives you nothing, but complexity. If you need tests, you can add compile-time switch.
You can also use instance with "Delegate-Properties(Func/Action)" to referer to the static methods by default but you can set them to something. Much "lighther" than i.e. forcing interface or inheritance.

Using this method simple lambdas can replace or inject functionality.

andyclap2
Posts: 2
Joined: Thu Aug 16, 2012 11:00 pm

Re: Mono (C#) anyone?

Mon Aug 20, 2012 10:14 pm

static methods; delegates and lamdas etc...
Going a bit off topic here, sorry if I sounded too preachy about decoupling earlier, but I have to defend my position - especially against accusations of being a wasteful "dental database software developer"! ;) (where does that dentist anecdote come from, I vaguely remember it in the the Joel Spolsky/Bob Martin Duct Tape/Clean code debate, but can't find the reference...):

My basic motivation is actually quite simple: I want to develop and test out some stuff in VS on my PC, which naturally can't access any GPIO pins. I want to run against a simulated GPIO. Now the library is static I can't use it directly and I'll have to make a wrapper. I know it's only a few dozen lines of code, but simply using objects like the earlier version is clean in C#, which after all has OO principles at its heart.

aanderson
Posts: 12
Joined: Thu Jun 28, 2012 5:11 am

Re: Mono (C#) anyone?

Mon Aug 20, 2012 10:20 pm

I agree. My GPIO library has a GPIOFile, GPIOMem and GPIODebug class for that reason.

Return to “Other programming languages”