Search code examples
arduinoraspberry-piiotarduino-unowindows-iot-core-10

How to turn on and off LEDs by Raspberry Pi IoT connected to Arduino with I2C on an UWP project?


I have an Arduino Uno and a Raspberry Pi 3 with Windows IoT Core. I've tried to use this method to pass some info to my Arduino, like telling it to initiate a pin or getting and parsing a string. This method works perfectly for getting info from the Arduino (like sensor parameters).

I was able to send a byte to the Arduino and make an action inside my Arduino code, based on the byte sent (like init pin 7 when getting the number 2). BUT it only works once. I have to reset the Arduino so it would accept bytes from the Raspberry Pi again (I can turn on a LED connected to Arduino from my Raspberry Pi but can't turn it of. The reverse is also true.

My goal is to create a web site inside the Raspberry Pi for controlling things. But to start I'm using UWP. I'm trying to pass data from IoT Core running on a Raspberry Pi 3 to an Arduino Uno (not the reverse) or manage and control Arduino Uno pins with an I2C connection.

My MainPage.xaml:

using System;
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.I2c;
using System.Threading.Tasks;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace I2CComm {
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page {
        private I2cDevice arduio; // Used to Connect to Arduino
        private DispatcherTimer timer = new DispatcherTimer();
        public MainPage() {
            this.InitializeComponent();
            Initialiasecom();
        }
        public async void Initialiasecom() {
            var settings = new I2cConnectionSettings(0x40);
            // Slave Address of Arduino Uno
            settings.BusSpeed = I2cBusSpeed.FastMode;
            // this bus has 400Khz speed
            string aqs =  I2cDevice.GetDeviceSelector("I2C1");
            // This will return Advanced Query String which is used to select i2c device
            var dis = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs);
            arduio = await I2cDevice.FromIdAsync(dis[0].Id, settings);
            timer.Tick += Timer_Tick;
            // We will create an event handler
            timer.Interval = new TimeSpan(0,0,0,0,500);
            // Timer_Tick is executed every 500 milli second
            timer.Start();
        }

        private async void Timer_Tick(object sender, object e) {
            byte[] response = new byte[2];
            try {
                arduio.Read(response);
                // this function will read data from Arduino
            }
            catch (Exception p) {
                Windows.UI.Popups.MessageDialog msg = new Windows.UI.Popups.MessageDialog(p.Message);
                await msg.ShowAsync();
                // this will show error message(if any)
            }
        }

        private void TurnOn_Click(object sender, RoutedEventArgs e) {
            try {
                byte[] sendpos;
                sendpos = BitConverter.GetBytes(2);
                arduio.Write(sendpos);
            }
            catch (Exception p) {
                Windows.UI.Popups.MessageDialog msg = new Windows.UI.Popups.MessageDialog(p.Message);
            }
        }

        private void TurnOff_Click(object sender, RoutedEventArgs e) {
            try {
                byte[] sendpos;
                sendpos = BitConverter.GetBytes(1);
                arduio.Write(sendpos);
            }
            catch (Exception p) {
                Windows.UI.Popups.MessageDialog msg = new Windows.UI.Popups.MessageDialog(p.Message);
            }
        }
    }
}

My Arduino code is:

#include <Wire.h>
// Library that contains functions to have I2C Communication
#define SLAVE_ADDRESS 0x40
// Define the I2C address to Communicate to Uno

byte response[2]; // this data is sent to PI
volatile short LDR_value; // Global Declaration
const int LDR_pin=A0; //pin to which LDR is connected A0 is analog A0 pin
const int ledPin = 7;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  Wire.begin(SLAVE_ADDRESS);
  // this will begin I2C Connection with 0x40 address
  Wire.onRequest(sendData);
  // sendData is a function called when Pi requests data
  Wire.onReceive(I2CReceived);
  pinMode(LDR_pin,INPUT);
  digitalWrite(ledPin, HIGH);
}

void loop() {
  delay(500);
}

void I2CReceived(int NumberOfBytes) {
  /* WinIoT have sent data byte; read it */
  byte ReceivedData = Wire.read();
  Serial.println(ReceivedData);
  if (ReceivedData == 2) {
    digitalWrite(ledPin, HIGH);
    return;
  } else if (ReceivedData == 1) {
    digitalWrite(ledPin, LOW);
    return;
  }
}

void sendData() {
  LDR_value=analogRead(LDR_pin);
  // Arduino returns 10-bit data but we need to convert it to 8 bits
  LDR_value=map(LDR_value,0,1023,0,255);
  response[0]=(byte)LDR_value;
  Wire.write(response,2); // return data to PI
}

Solution

  • Raspberry Pi send 4 bytes(2 is Int) instead of 1 byte. You need receive all of the bytes in Arduino. You can do it like this:

    void I2CReceived(int NumberOfBytes) {
      /* WinIoT have sent data byte; read it */
      byte ReceivedData = Wire.read();
      Serial.println(ReceivedData);
    
      while (0 < Wire.available()) {
        byte UselessData = Wire.read();
        Serial.println(UselessData);
      }
    
      if (ReceivedData == 2) {
        digitalWrite(ledPin, HIGH);
        return;
      } else if (ReceivedData == 1) {
        digitalWrite(ledPin, LOW);
        return;
      }  
    }