Alex Merchen
Published

PiCan

Raspberry Pi controlled trashcan lets user know when the trash is ready to be taken out.

AdvancedWork in progress1,174
PiCan

Things used in this project

Story

Read more

Schematics

Schematic for circuit

It uses an analog to digital converter and uses a force sensor to detect what weight is being applied.

Code

Force Sensor

C#
This is a modified version of the Potentiometer sample code so that it could read the force sensor and activate the LED.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Gpio;
using Windows.Devices.Spi;
using Windows.Devices.Enumeration;

namespace ForceSensor
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            /* Register for the unloaded event so we can clean up upon exit */
            Unloaded += MainPage_Unloaded;

            /* Initialize GPIO and SPI */
            InitAll();
        }

        /* Initialize GPIO and SPI */
        private async void InitAll()
        {
            if (AdcDevice.MCP3208 == AdcDevice.NONE)
            {
                StatusText.Text = "Please change the ADC_DEVICE variable to either MCP3002 or MCP3208";
                return;
            }

            try
            {
                InitGpio();         /* Initialize GPIO to toggle the LED                          */
                await InitSPI();    /* Initialize the SPI bus for communicating with the ADC      */

            }
            catch (Exception ex)
            {
                StatusText.Text = ex.Message;
                return;
            }

            /* Now that everything is initialized, create a timer so we read data every 500mS */
            periodicTimer = new Timer(this.Timer_Tick, null, 0, 100);

            StatusText.Text = "Status: Running";
        }

        private async Task InitSPI()
        {
            try
            {
                var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
                settings.ClockFrequency = 500000;   /* 0.5MHz clock rate                                        */
                settings.Mode = SpiMode.Mode0;      /* The ADC expects idle-low clock polarity so we use Mode0  */

                string spiAqs = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME);
                var deviceInfo = await DeviceInformation.FindAllAsync(spiAqs);
                SpiADC = await SpiDevice.FromIdAsync(deviceInfo[0].Id, settings);
            }

            /* If initialization fails, display the exception and stop running */
            catch (Exception ex)
            {
                throw new Exception("SPI Initialization Failed", ex);
            }
        }

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

            /* Show an error if there is no GPIO controller */
            if (gpio == null)
            {
                throw new Exception("There is no GPIO controller on this device");
            }

            ledPin = gpio.OpenPin(LED_PIN);

            /* GPIO state is initially undefined, so we assign a default value before enabling as output */
            ledPin.Write(GpioPinValue.High);        
            ledPin.SetDriveMode(GpioPinDriveMode.Output);
        }

        /* Turn on/off the LED depending on the potentiometer position    */
        private void LightLED()
        {
            int adcResolution = 0;

            switch (AdcDevice.MCP3208)
            {
                case AdcDevice.MCP3002:
                    adcResolution = 1024;
                    break;
                case AdcDevice.MCP3208:
                    adcResolution = 4096;
                    break;
            }


            /* 
            So I want to make sure that the value being read from the force sensor is
            greater than a "maximum value" that I can vary using the potentiometer - this
            allows the user to adjust the maximum weight if needed. I decided that a maximum
            value is about 7.8* the maximum resolution of the analog to digital IC. I used the
            3008 analog to digital converter but this would work the same if you used the 3208
            chip instead
            */
            if (adcValue > (adcResolution*7/8))
            {
                // The LED is on the trashcan to show that it is full. The corresponding app would
                // push a notification to the phone to notify the user.
                ledPin.Write(GpioPinValue.High)
            }
            else
            {
                ledPin.Write(GpioPinValue.Low)
            }
        }

        /* Read from the ADC, update the UI, and toggle the LED */
        private void Timer_Tick(object state)
        {
            ReadADC();
            LightLED();
        }

        public void ReadADC()
        {
            byte[] readBuffer = new byte[3]; /* Buffer to hold read data*/
            byte[] writeBuffer = new byte[3] { 0x00, 0x00, 0x00 };

            /* Setup the appropriate ADC configuration byte */
            switch (AdcDevice.MCP3208)
            {
                case AdcDevice.MCP3002:
                    writeBuffer[0] = MCP3002_CONFIG;
                    break;
                case AdcDevice.MCP3208:
                    writeBuffer[0] = MCP3208_CONFIG;
                    break;
            }

            SpiADC.TransferFullDuplex(writeBuffer, readBuffer); /* Read data from the ADC                           */
            adcValue = convertToInt(readBuffer);                /* Convert the returned bytes into an integer value */

            /* UI updates must be invoked on the UI thread */
            var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                textPlaceHolder.Text = adcValue.ToString();         /* Display the value on screen                      */
            });
        }

        /* Convert the raw ADC bytes to an integer */
        public int convertToInt(byte[] data)
        {
            int result = 0;
            switch (AdcDevice.MCP3208)
            {
                case AdcDevice.MCP3002:
                    result = data[0] & 0x03;
                    result <<= 8;
                    result += data[1];
                    break;
                case AdcDevice.MCP3208:
                    result = data[1] & 0x0F;
                    result <<= 8;
                    result += data[2];
                    break;
            }
            return result;
        }

        private void MainPage_Unloaded(object sender, object args)
        {
            /* It's good practice to clean up after we're done */
            if(SpiADC != null)
            {
                SpiADC.Dispose();
            }

            if(ledPin != null)
            {
                ledPin.Dispose();
            }
        }
        enum AdcDevice { NONE, MCP3002, MCP3208 };

        /* Important! Change this to either AdcDevice.MCP3002 or AdcDevice.MCP3208 depending on which ADC you chose     */
        private AdcDevice ADC_DEVICE = AdcDevice.NONE;

        private const int LED_PIN = 4;
        private GpioPin ledPin;

        private const string SPI_CONTROLLER_NAME = "SPI0";  /* Friendly name for Raspberry Pi 2 SPI controller          */
        private const Int32 SPI_CHIP_SELECT_LINE = 0;       /* Line 0 maps to physical pin number 24 on the Rpi2        */
        private SpiDevice SpiADC;

        private const byte MCP3002_CONFIG = 0x68; /* 01101000 channel configuration data for the MCP3002 */
        private const byte MCP3208_CONFIG = 0x06; /* 00000110 channel configuration data for the MCP3208 */

        private Timer periodicTimer;
        private int adcValue;
    }
}

Credits

Alex Merchen

Alex Merchen

22 projects • 37 followers
I'm an EE with a Masters in ECE. I like building things.

Comments