Bite Size C# – Generics

Generics refer to the method of creating classes and methods in a way that defers the specification of the type or types associated with the class or method until it is declared and instantiated.

What this means in plain english is that you can define a single class or method that can be utilised with multiple types, thus resulting in less and tidier code.

So let us have a look at an example. Firstly let us look at some code that is not generic. Here we have a class which consist of a list of integers, it has a default constructor as well as two methods, one to add an integer to the list and the other to return the sum of all the integers in the list.

public class KillerRoboticsIntList
{
   public List<int> IntList { get; set; }

    public KillerRoboticsIntList()
    {
        IntList = new List<int>();
    }

    public void Add(int item)
    {
        IntList.Add(item);
    }

    public int Sum()
   {
	int sum;
	foreach(int i in IntList)
	{
	 sum += i;
	}
	return sum;
   }

}

This is a very basic example and can be utilised as follows:

class Program
        {
            public static void Main()
            {
                KillerRoboticsIntList iList = new KillerRoboticsIntList();
		        iList.Add(1);
		        iList.Add(2);
		        iList.Add(3);
		        iList.Add(4);
		        int sum = iList.Sum();
                Console.WriteLine(“Sum = {0}.", sum);

            }
        }

This is very straight forward, however if I would like to use this class with a data type other than int (for example a float or a double) I would need to define a new class for each data type. This can be overcome by utilising Generics. Below is an example of how this can be implemented:

public class KillerRoboticsGenericList<T>
{
   public List GenList<T> { get; set; }

    public KillerRoboticsGenericList()
    {
        GenList = new List<T>();
    }

    public void Add(T item)
    {
        GenList.Add(item);
    }

    public T Sum()
   {
	T sum;
	foreach(T i in GenList)
	{
	 sum += i;
	}
	return sum;
   }

}

The above class can now be defined for various types as seen below:

class Program
        {
            public static void Main()
            {
              KillerRoboticsIntList<int> intList = new KillerRoboticsIntList<int>();
		        intList.Add(1);
		        intList.Add(2);
		        intList.Add(3);
		        intList.Add(4);
		        int sum = intList.Sum();
                Console.WriteLine(“Int Sum = {0}.", sum);

		        KillerRoboticsIntList<double> = new KillerRoboticsIntList<double>();
		        doubleList.Add(0.1);
		        doubleList.Add(2.2);
		        doubleList.Add(3.0);
		        doubleList.Add(1.4);
		        double dSum = doubleList.Sum();
                Console.WriteLine(“Double Sum = {0}.", dSum);

            }
        }

In some cases you might want to put a restriction on the types that can be utilised to implement a generic class of method, this can be achieved by utilising constraints. For example the class we defined above can be restricted to types that implement the IComparable interface by simple changing the first line as follows:

public class KillerRoboticsGenericList<T> where T : IComparable

Some additional examples of constraints are:

where T : Product

This restricts the type of T to an implementation of the class Product or any of its child classes.

where T : struct

This restricts the type of T to the value type struct.

where T : new()

This restricts the type of T to an object with a default constructor.

Multiple Constraints can also be appled at the same time, for example:

 
public class KillerRoboticsGenericList<T> where T : IComparable, new() 

A Generic class can be defined to utilise multiple types, for example:

 
public class KillerRoboticsGenericList<T,U,V>  

It is also worth mentioning that the default c# List class used in the above examples also utilises generics, along with all the other predefined c# collections contained in System.Collections.Generic.

I hope this post has been useful and I will be posting on some additional C# topics, such as Delegates, Lamda Expressions, LINQ, Extension Methods, etc. over the next few months.

Bite Size C# – Generics

SunFounder DIY 4-DOF Sloth Robot Kit

Today we will have a look at a robot kit created by SunFounder. The Sloth is a bi-pedal robot based on the Arduino Nano. It utilises an ultrasonic module for obstacle detection and four servos for movement.

I found the kit to be an easy and fun build that took approximately an hour to complete. SunFounder have uploaded a YouTube video that serves as detailed introductions and assembly tutorial for the robot. It covers the entire assembly process, including the process to test the ultrasonic module as well as all the servos (before assembly), which is a good thing as one of the servos included with my kit turned out to be broken, but luckily I had a replacement servo on hand.

One thing to note is that the robot utilises two 18650 batteries, which after some research turned out to be commonly used in high-end flashlights and e-cigarettes, and are relatively pricey.

The robot can also be powered through the mini USB port on the Arduino (which I did while I waited for the batteries I had ordered to be delivered).

The code for the robot can be downloaded from here on the SunFounder website. Just note that the default code did not work for me as the ultrasound module did not detect obstacles. I rectified this by replacing the following code:

void loop()
{
 	int <span class="mceItemHidden" data-mce-bogus="1"><span class="hiddenSpellError" pre="int " data-mce-bogus="1">tooclose</span></span> = 0; // Define the test times, the initial value is 0
 	for(int a=0; a&lt;5; a++)  //for is a cyclical function and its cyclic times are determined by the second condition in the bracket. Here a&lt;5, a’s initial value is 0. When a&lt;5, run the program in the bracket. After running, a becomes a++ and it means adding 1 to a’s value.
	{
		delay(50); // Delay 50ms
		int din = sonar.ping_in(); // Call the ultrasonic transducer to read the distance that ultrasonic detected.
		if (din &lt; 7 &amp;&amp; din &gt; 0) tooclose++; // The smoothing. The times add 1 when the detect distance less than 7cm and greater than 0cm.
	}

With this:

void loop()
{
	int <span class="mceItemHidden" data-mce-bogus="1"><span class="hiddenSpellError" pre="int " data-mce-bogus="1">tooclose</span></span> = 0; // Define the test times, the initial value is 0
	for(int a=0; a&lt;5; a++)  //for is a cyclical function and its cyclic times are determined by the second condition in the bracket. Here a&lt;5, a’s initial value is 0. When a&lt;5, run the program in the bracket. After running, a becomes a++ and it means adding 1 to a’s value.
	{
		delay(50); // Delay 50ms
		int din = sonar.ping(); // Call the ultrasonic transducer to read the distance that ultrasonic detected.
		if (din &lt; 750 &amp;&amp; din &gt; 0) tooclose++;
	}

I really enjoyed assembling and playing with this little robot and would definitely recommend it to anyone interested in getting started in basic robotics.

SunFounder DIY 4-DOF Sloth Robot Kit

Book Review – Robot Builder’s Bonanza

Many books have been written on the topic of robot building, ranging from very basic to extremely complex, but I have seldom come across a book that gets the balance right. Robot Builder’s Bonanza by Gordon McComb hits the sweet spot.

robotbook

The book provides a vast amount of detail on the electronics, mechanics and programming required to build a robot. Concepts like movement (for both wheeled and legged robots), sensors (to make your robot perceive its environment and conditions), the pros and cons of different Micro-controllers, as well as many other actuators (that allow the robot to change its environment in some way) are all covered. 

At the start of each section, a brief introduction to the field is given that includes explanations of key concepts (such as resistors and capacitors in the case of the electronics section) to the tools used (such as drills and screws in the case of the mechanical section). The book includes over 100 projects as examples to illustrate the concepts covered.

The book, in both print and content, is of very high quality. It is very clearly written and provides many diagrams, pictures and schematics, making it easy to understand even the more complex topics covered, for example robotic vision, robotic interpretation of sounds or the choice of which micro-controllers to use as your robots’ brain.

I would very highly recommend Robot Builder’s Bonanza to anyone interested in getting started in robot building or even someone who is currently building robots. It is not just a great book for learning the basics and getting started, but it is also a great reference guide to complex topics as well as a source of inspiration.

As far as Robotics books go this is one of the best, do yourself a favour and pick up a copy today.

Book Review – Robot Builder’s Bonanza

Bite Size C# – DataTable to RecordSet Converter

When working on legacy systems you may sometimes find that a wide variety of technologies have been used, and short of rewriting a huge amount of code the only thing that can be done is to create some functionality to map between the different technologies utilised.

I had a scenario where I had to integrate 2 legacy applications utilising different technologies for database access, one utilised ADODB and the other ADO.NET. Due to time constraints I could not change the underlying code of the systems and rather decided to create a helper class to map an ADO.NET DataTable to an ADODB RecordSet in order to facilitate the integration.

My DTtoRSconvert is a static class that resides in the Core.Data.Help namespace. It contains a static method ConvertToRecordSet that takes a DataTable as a parameter and returns a RecordSet. It also has one static private method TranslateType which is used to map between the DataTable and RecordSet Data types.

Here is the code:

using System.Data;
using System.Reflection;
using ADODB;

namespace Core.Data.Helper
{
    //Helper class that convert ADO.Net DataTables to ADODB RecordSets
    public static class DTtoRSconvert
    {
        public static Recordset ConvertToRecordSet(DataTable inTable)
        {
            var recordSet = new Recordset { CursorLocation = CursorLocationEnum.adUseClient };

            var recordSetFields = recordSet.Fields;
            var inColumns = inTable.Columns;

            foreach (DataColumn column in inColumns)
            {
                recordSetFields.Append(column.ColumnName
                                    , TranslateType(column.DataType)
                                    , column.MaxLength
                                    , column.AllowDBNull
                                          ? FieldAttributeEnum.adFldIsNullable
                                          : FieldAttributeEnum.adFldUnspecified
                                    , null);
            }

            recordSet.Open(Missing.Value
                        , Missing.Value
                        , CursorTypeEnum.adOpenStatic
                        , LockTypeEnum.adLockOptimistic, 0);

            foreach (DataRow row in inTable.Rows)
            {
                recordSet.AddNew(Missing.Value,
                              Missing.Value);

                for (var columnIndex = 0; columnIndex &lt; inColumns.Count; columnIndex++)
                {
                    recordSetFields[columnIndex].Value = row[columnIndex];
                }
            }

            return recordSet;
        }

        private static DataTypeEnum TranslateType(IReflect columnDataType)
        {
            switch (columnDataType.UnderlyingSystemType.ToString())
            {
                case "System.Boolean":
                    return DataTypeEnum.adBoolean;

                case "System.Byte":
                    return DataTypeEnum.adUnsignedTinyInt;

                case "System.Char":
                    return DataTypeEnum.adChar;

                case "System.DateTime":
                    return DataTypeEnum.adDate;

                case "System.Decimal":
                    return DataTypeEnum.adCurrency;

                case "System.Double":
                    return DataTypeEnum.adDouble;

                case "System.Int16":
                    return DataTypeEnum.adSmallInt;

                case "System.Int32":
                    return DataTypeEnum.adInteger;

                case "System.Int64":
                    return DataTypeEnum.adBigInt;

                case "System.SByte":
                    return DataTypeEnum.adTinyInt;

                case "System.Single":
                    return DataTypeEnum.adSingle;

                case "System.UInt16":
                    return DataTypeEnum.adUnsignedSmallInt;

                case "System.UInt32":
                    return DataTypeEnum.adUnsignedInt;

                case "System.UInt64":
                    return DataTypeEnum.adUnsignedBigInt;

                //System.String will also be handled by default
                default: 
                    return DataTypeEnum.adVarChar;
            }
        }
    }
}
Bite Size C# – DataTable to RecordSet Converter

Bite Size C# – XLS File Merger

This will be the last Bite Size C# post revolving around CSV and XLS files (for a while at least). The next one will be on a different and probably a bit more exciting topic.

Today we will look at my XLS File Merger, which resides in the Core.XLS namespace of my Core.dll library. It is a static class with one static method MergeFiles. The purpose of this method is to merge 2 or more XLS files into a single file, i.e. taking the worksheets of 2 or more files and combing them into a single file. The MergeFiles method takes 2 arguments: a list of locations of files to be merged (filePathList) and the destination file that will be created (outputFile).

Here is the code:

using System;
using Microsoft.Office.Interop.Excel;

namespace Core.XLS
{
    public static class XlsFileMerge
    {
        public static void MergeFiles(string[] filePathList, string outputFile)
        {
            var app = new Application {Visible = false};

            app.Workbooks.Add("");
            foreach (var file in filePathList)
            {
                app.Workbooks.Add(file);
            }


            for (var i = 2; i &lt;= app.Workbooks.Count; i++)
            {
                var count = app.Workbooks[i].Worksheets.Count;

                app.Workbooks[i].Activate();
                for (var j = 1; j &lt;= count; j++)
                {
                    var ws = (_Worksheet) app.Workbooks[i].Worksheets[j];
                    if (ws.UsedRange.Rows.Count &lt;= 1) continue;
                    ws.Select(Type.Missing);
                    ws.Cells.Select();


                    var sel = (Range) app.Selection;
                    sel.Copy(Type.Missing);

                    var sheet = (_Worksheet) app.Workbooks[1].Worksheets.Add(
                        Type.Missing, Type.Missing, Type.Missing, Type.Missing
                                                 );
                    sheet.Name = ws.Name;
                    sheet.Paste(Type.Missing, Type.Missing);
                }
            }
            app.Workbooks[1].SaveAs(@outputFile);
            app.Quit();
        }
    }
}
Bite Size C# – XLS File Merger

Roaming Robot Update

robot1

My Roaming robot has recently been giving me some problems, specifically regarding the power distribution between the 2 motors (with one wheel sporadicly turning faster than the other). I initially thought that one of the motors might have been damaged so I replaced both, only to have the problem remain. After some testing and replacing both the Pololu Dual MC33926 Motor Driver Shield and the Arduino Uno R3, I determined that the problem lay with the Pololu Motor shield. I am not sure if one of the other sensors or actuators was causing some interference on the shield or if it is simply a design flaw in the shield, but I found it impossible to balance the power of the 2 motors. 

I thus decided to replace the Pololu Dual MC33926 Motor Driver Shield with a Pololu DRV8833 Dual Motor Driver Carrier, and this resolved the problem I was experiencing. 

Here the Pololu Dual MC33926 Motor Driver Shield and the Pololu DRV8833 Dual Motor Driver Carrier can be seen side by side (the  Pololu DRV8833 Dual Motor Driver Carrier being the much smaller of the 2): 

motordrivers

I mounted the Motor driver on a bread board, attached on top of an Adafruit Proto-shield. This has the added benefit of having screw terminals for the Arduino pins, so that the jumper cables can no longer accidentally become dislodged.

Proto      protodriver

To install the new DRV8833 Motor Driver I had to switch some of the pin usage on the Arduino in order to free up some PWM digital pins needed by the motor driver. The new Layout is as follows:

Analog Pins:

  • A0 
  • A1 
  • A2 – Servo 
  • A3
  • A4 – IR Sensor
  • A5 – Photo-resistor

Digital Pins:

  • 0
  • 1
  • 2 – LED
  • 3 – Motor Driver B IN 1
  • 5 – Motor Driver B IN 2
  • 6 – Motor Driver A IN 1
  • 7 – Ultrasonic Sensor
  • 8 – Right Trigger Switch
  • 9 – Motor Driver A IN 2
  • 10 
  • 11 – Left Trigger Switch
  • 12 
  • 13

Here are the updated drawings illustrating the wiring of the different components:

robot 1_bb

robot part 2_bb

robot part 3_bb2


I also replaced some of the wiring to the Ultrasonic sensor as they had become worn due to the “neck movement” of the servo pointing the sensor in different directions.

I also ran the power for the Ultrasonic sensor straight from the Arduino and no longer via the Proto-board, this was simply to give the cables a bit more play for the “neck movement” performed by the servo.

After these changes the little guy is working perfectly again.

I will be posting a video of this robot in action in the near future.

Here is the updated code for the robot:


#include "Servo.h" 

#define IR_PIN A4
#define SERVO_PIN A2
#define PING_PIN 7
#define BUMPER_LEFT_PIN 11
#define BUMPER_RIGHT_PIN 8
#define LDR_PIN A5
#define LED_PIN 2
#define IR_DROP 500 //Distance considered a potential drop
#define MIN_LIGHT 300 //Level of light to turn on LED for light
#define BIN_1  3
#define BIN_2  5
#define AIN_1  6
#define AIN_2  9
#define MAX_PWM_VOLTAGE  150

int IRDist = 0; 
int bpLeft = 0; 
int bpRight = 0; 
int LDRValue = 0;
const int dangerThresh = 16;// (in cm) used for obstacle avoidance
int leftDistance, rightDistance; //distances on either side
Servo panMotor; //'neck' servo 
long duration; //time it takes to recieve PING signal

void setup()
{
  Serial.begin(9600); //used for serial communication for debugging 
  pinMode(BUMPER_LEFT_PIN,INPUT);
  pinMode(BUMPER_RIGHT_PIN,INPUT);
  pinMode(LED_PIN, OUTPUT);
  pinMode(BIN_1, OUTPUT);
  pinMode(BIN_2, OUTPUT);
  pinMode(AIN_1, OUTPUT);
  pinMode(AIN_2, OUTPUT);
}

void moveStop()
{
  digitalWrite(BIN_1, LOW);
  digitalWrite(BIN_2, LOW);
  digitalWrite(AIN_1, LOW);
  digitalWrite(AIN_2, LOW);
}

void moveForward()
{
  digitalWrite(BIN_1, LOW);
  analogWrite(BIN_2, MAX_PWM_VOLTAGE);
  analogWrite(AIN_1, MAX_PWM_VOLTAGE);
  digitalWrite(AIN_2, LOW);
  delay(300);
}

void turnLeft()
{
  digitalWrite(BIN_1, LOW);
  analogWrite(BIN_2, MAX_PWM_VOLTAGE);
  digitalWrite(AIN_1, LOW);
  analogWrite(AIN_2, MAX_PWM_VOLTAGE);
  delay(300); 
}

void turnRight()
{
  analogWrite(BIN_1, MAX_PWM_VOLTAGE);
  digitalWrite(BIN_2, LOW);
  analogWrite(AIN_1, MAX_PWM_VOLTAGE);
  digitalWrite(AIN_2, LOW);
  delay(300);
}

void moveBack()
{
   analogWrite(BIN_1, MAX_PWM_VOLTAGE);
   digitalWrite(BIN_2, LOW);
   digitalWrite(AIN_1, LOW);
   analogWrite(AIN_2, MAX_PWM_VOLTAGE);
   delay(500); 
}

int checkDrop()
{
  pinMode(IR_PIN,INPUT);
  IRDist = analogRead(IR_PIN);    
  //Serial.println(IRDist);
  if(IRDist &lt; IR_DROP) 
  { 
    return 1; //Drop present (determined with IR distance sensor)
  }
  else
  {
    return 0;
  }
}

void lightDarkness()
{
 LDRValue = analogRead(LDR_PIN);
 if(LDRValue &lt; MIN_LIGHT)
  {
      digitalWrite(LED_PIN,HIGH); //It is dark, turn on the light
  }
  else
  {
    digitalWrite(LED_PIN,LOW);
  }
}

int checkLeftBumper()
{
  bpLeft = digitalRead(BUMPER_LEFT_PIN);
  if(bpLeft == HIGH)
  {  
    //Serial.println("Left Bumper");
    //HIT!
    return 1;
  }
  else
  {
    return 0;
  }
}

int checkRightBumper()
{
   bpRight = digitalRead(BUMPER_RIGHT_PIN);
  if(bpRight == HIGH)
  {
    //Serial.println("Right Bumper");
    //HIT!
    return 1;
  }
  else
  {
    return 0;
  }
}

void compareDistance()
{
  if (leftDistance&gt;rightDistance) //if left is less obstructed 
  {
    turnLeft();
  }
  else if (rightDistance&gt;leftDistance) //if right is less obstructed
  {
    turnRight();
  }
   else //if they are equally obstructed
  {
    moveBack();
  }
}

long ping()
{
  // Send Ping pulse
  pinMode(PING_PIN, OUTPUT);
  digitalWrite(PING_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(PING_PIN, HIGH);
  delayMicroseconds(5);
  digitalWrite(PING_PIN, LOW);
  
  //Get duration it takes to receive echo of Ping sent
  pinMode(PING_PIN, INPUT);
  duration = pulseIn(PING_PIN, HIGH);
  
  //Convert duration into distance (cm)
  return duration / 29 / 2;
}

void loop()
{
  
  lightDarkness();
  if(checkDrop() == 1)
  {
    moveBack();
    moveBack();
    moveBack();
    turnRight();
  }
  else
  if(checkLeftBumper() == 1)
  {
    moveBack();
    turnRight();
  } 
  else if(checkRightBumper() == 1)
  {
    moveBack();
    turnLeft();
  } 
  else
  {
  moveStop();
  int distanceFwd = ping();
  //Serial.println(distanceFwd);
  if (distanceFwd&gt;dangerThresh) //if path is clear
  {
   moveForward();
  }
  else //if path is blocked
  {
    moveStop();
    
    panMotor.attach(SERVO_PIN);
    panMotor.write(20); //look right

    delay(400);
    
    rightDistance = ping(); //scan to the right
    panMotor.write(160); //look left

    delay(550);
    
    leftDistance = ping(); //scan to the left
    panMotor.write(90); //look to center

    delay(400);
    
    panMotor.detach();
    compareDistance();
  }
  }
}
Roaming Robot Update