netNF Electric Longboard Part 1

Wiichuck connectivity

Roughly four years ago I build myself an electric longboard as summer transport. It initially had a controller built with a devDuino V2.2 which after a while I “upgraded” to a GHI Electronics .NET Microframework device.

Configuring the original netMF based longboard

Now that GHI Electronics no longer supports the FEZ Panda III I figured upgrading to a device that runs the nanoFramework would be a good compromise.

I control the speed of the longboard with a generic wireless wii nunchuk. So my first project is porting the .NET Micro Framework Toolbox code to the nanoFramework.

wireless controller test rig

My test rig uses (prices as at Aug 2020) the following parts

  • Netduino 3 Wifi
  • Grove-Base Shield V2.0 for Arduino USD4.45
  • Grove-Universal 4 Pin Bucked 5cm cable(5 PCs Pack) USD1.90
  • Grove-Nunchuck USD2.90
  • Generic wireless WII nunchuk

My changes were mainly related to the Inter Integrated Circuit(I2C) configuration and the reading+writing of registers.

/// <summary>
/// Initialises a new Wii Nunchuk
/// </summary>
/// <param name="busId">The unique identifier of the I²C to use.</param>
/// <param name="slaveAddress">The I²C address</param>
/// <param name="busSpeed">The bus speed, an enumeration that defaults to StandardMode</param>
/// <param name="sharingMode">The sharing mode, an enumeration that defaults to Shared.</param>
public WiiNunchuk(string busId, ushort slaveAddress = 0x52, I2cBusSpeed busSpeed = I2cBusSpeed.StandardMode, I2cSharingMode sharingMode = I2cSharingMode.Shared)
   {
      I2cTransferResult result;

      // This initialisation routine seems to work. I got it at http://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way
      Device = I2cDevice.FromId(busId, new I2cConnectionSettings(slaveAddress)
      {
         BusSpeed = busSpeed,
         SharingMode = sharingMode,
      });

      result = Device.WritePartial(new byte[] { 0xf0, 0x55 });
      if (result.Status != I2cTransferStatus.FullTransfer)
      {
         throw new ApplicationException("Something went wrong reading the Nunchuk. Did you use proper pull-up resistors?");
      }

      result = Device.WritePartial(new byte[] { 0xfb, 0x00 });
      if (result.Status != I2cTransferStatus.FullTransfer)
      {
         throw new ApplicationException("Something went wrong reading the Nunchuk. Did you use proper pull-up resistors?");
      }

      this.Device.Write(new byte[] { 0xf0, 0x55 });
      this.Device.Write(new byte[] { 0xfb, 0x00 });
   }

   /// <summary>
   /// Reads all data from the nunchuk
   /// </summary>
   public void Read()
   {
      byte[] WaitWriteBuffer = { 0 };
      I2cTransferResult result;

      result = Device.WritePartial(WaitWriteBuffer);
      if (result.Status != I2cTransferStatus.FullTransfer)
      {
         throw new ApplicationException("Something went wrong reading the Nunchuk. Did you use proper pull-up resistors?");
      }

      byte[] ReadBuffer = new byte[6];
      result = Device.ReadPartial(ReadBuffer);
      if (result.Status != I2cTransferStatus.FullTransfer)
      {
         throw new ApplicationException("Something went wrong reading the Nunchuk. Did you use proper pull-up resistors?");
      }

      // Parses data according to http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck#Data_Format

      // Analog stick
      this.AnalogStickX = ReadBuffer[0];
      this.AnalogStickY = ReadBuffer[1];

      // Accelerometer
      ushort AX = (ushort)(ReadBuffer[2] << 2);
      ushort AY = (ushort)(ReadBuffer[3] << 2);
      ushort AZ = (ushort)(ReadBuffer[4] << 2);
      AZ += (ushort)((ReadBuffer[5] & 0xc0) >> 6); // 0xc0 = 11000000
      AY += (ushort)((ReadBuffer[5] & 0x30) >> 4); // 0x30 = 00110000
      AX += (ushort)((ReadBuffer[5] & 0x0c) >> 2); // 0x0c = 00001100
      this.AcceleroMeterX = AX;
      this.AcceleroMeterY = AY;
      this.AcceleroMeterZ = AZ;

      // Buttons
      ButtonC = (ReadBuffer[5] & 0x02) != 0x02;    // 0x02 = 00000010
      ButtonZ = (ReadBuffer[5] & 0x01) != 0x01;    // 0x01 = 00000001
}

The nanoFramework code polls for the joystick position and accelerometer values every 100mSec

public class Program
{
   public static void Main()
   {
      Debug.WriteLine("devMobile.Longboard.WiiNunchuckTest starting");
      Debug.WriteLine(I2cDevice.GetDeviceSelector());

      try
      {
         WiiNunchuk nunchuk = new WiiNunchuk("I2C1");

         while (true)
         {
            nunchuk.Read();

            Debug.WriteLine($"JoyX: {nunchuk.AnalogStickX} JoyY:{nunchuk.AnalogStickY} AX:{nunchuk.AcceleroMeterX} AY:{nunchuk.AcceleroMeterY} AZ:{nunchuk.AcceleroMeterZ} BtnC:{nunchuk.ButtonC} BtnZ:{nunchuk.ButtonZ}");

            Thread.Sleep(100);
         }
      }
      catch (Exception ex)
      {
         Debug.WriteLine(ex.Message);
      }
   }
}

The setup to use for the I2C port was determined by looking at the board.h and target_windows_devices_I2C_config.cpp file

//
// Copyright (c) 2018 The nanoFramework project contributors
// See LICENSE file in the project root for full license information.
//

#include <win_dev_i2c_native_target.h>

//////////
// I2C1 //
//////////

// pin configuration for I2C1
// port for SCL pin is: GPIOB
// port for SDA pin is: GPIOB
// SCL pin: is GPIOB_6
// SDA pin: is GPIOB_7
// GPIO alternate pin function is 4 (see alternate function mapping table in device datasheet)
I2C_CONFIG_PINS(1, GPIOB, GPIOB, 6, 7, 4)

Then checking this against the Netduino 3 Wifi schematic.

This image has an empty alt attribute; its file name is netduinoschematic-1.jpg

After some experimentation with how to detect if an I2C read or write had failed the debugging console output began displaying reasonable value

The thread '<No Name>' (0x2) has exited with code 0 (0x0).
devMobile.Longboard.WiiNunchuckTest starting
I2C1
JoyX: 128 JoyY:128 AX:520 AY:508 AZ:708 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:520 AY:504 AZ:716 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:524 AY:508 AZ:716 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:524 AY:536 AZ:708 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:516 AY:528 AZ:724 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:492 AY:524 AZ:720 BtnC:True BtnZ:False
JoyX: 128 JoyY:128 AX:508 AY:528 AZ:700 BtnC:True BtnZ:False
JoyX: 128 JoyY:128 AX:504 AY:532 AZ:716 BtnC:True BtnZ:False
JoyX: 128 JoyY:128 AX:512 AY:532 AZ:724 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:516 AY:532 AZ:712 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:520 AY:532 AZ:708 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:524 AY:532 AZ:708 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:480 AY:504 AZ:688 BtnC:True BtnZ:True
JoyX: 128 JoyY:128 AX:480 AY:520 AZ:728 BtnC:False BtnZ:True
JoyX: 128 JoyY:128 AX:512 AY:520 AZ:704 BtnC:False BtnZ:True
JoyX: 128 JoyY:128 AX:512 AY:548 AZ:708 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:504 AY:516 AZ:728 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:548 AY:536 AZ:704 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:500 AY:528 AZ:728 BtnC:True BtnZ:False
JoyX: 128 JoyY:128 AX:496 AY:524 AZ:716 BtnC:True BtnZ:False
JoyX: 128 JoyY:128 AX:528 AY:536 AZ:696 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:540 AY:540 AZ:720 BtnC:False BtnZ:False
JoyX: 128 JoyY:128 AX:500 AY:520 AZ:684 BtnC:False BtnZ:False
JoyX: 128 JoyY:0 AX:520 AY:508 AZ:696 BtnC:False BtnZ:False
JoyX: 29 JoyY:0 AX:488 AY:576 AZ:716 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:532 AY:540 AZ:700 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:492 AY:512 AZ:708 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:492 AY:516 AZ:708 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:504 AY:512 AZ:708 BtnC:False BtnZ:False
JoyX: 27 JoyY:128 AX:508 AY:520 AZ:700 BtnC:False BtnZ:False
JoyX: 106 JoyY:128 AX:504 AY:516 AZ:700 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:496 AY:520 AZ:700 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:512 AY:532 AZ:716 BtnC:False BtnZ:False
JoyX: 0 JoyY:128 AX:500 AY:516 AZ:708 BtnC:False BtnZ:False
JoyX: 85 JoyY:113 AX:500 AY:536 AZ:720 BtnC:False BtnZ:False
JoyX: 128 JoyY:110 AX:512 AY:532 AZ:712 BtnC:False BtnZ:False
JoyX: 128 JoyY:90 AX:516 AY:528 AZ:716 BtnC:False BtnZ:False
JoyX: 128 JoyY:43 AX:508 AY:468 AZ:660 BtnC:False BtnZ:False
JoyX: 128 JoyY:0 AX:508 AY:532 AZ:712 BtnC:False BtnZ:False
JoyX: 128 JoyY:0 AX:496 AY:524 AZ:716 BtnC:False BtnZ:False

The next test rig will be getting Pulse Width Modulation(PWM) working.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.