In my last engagement I was facing the problem of updating SCMDM certificate. The preferred way was to do it from the Windows Mobile device. I was blogging about similar topic some time ago, so this will be the extension of the original article.

Find what you want to update

If you do simple query into the MY certificate store on your enrolled device.

<wap-provisioningdoc>
    <characteristic type="CertificateStore"> 
       <characteristic-query type="MY" />
    </characteristic>  
</wap-provisioningdoc>

You will get the something like the following result. There might be more certificates eventually, but we are interested in the SCMDM one. The most important information in here is the certificate hash. In this case it’s 64BB2D1FAD191B04D33D04781CE9ECC8F7D9BAE5. Let’s keep it for the future use.

In case that you want determine this hash automatically from application, it’s good to look for certificate with template name SCMDM2008MobileDevice. However, this template name is valid only for SCMDM 2008 RTM version, if you are running SCMDM 2008 with Service Pack 1, then the name of instance is also included. The template name looks like this SCMDMMobileDevice (mdm-prg), for the mdm-prg instance.

<wap-provisioningdoc>
  <characteristic type="CertificateStore">
    <characteristic type="MY">
      <characteristic type="64BB2D1FAD191B04D33D04781CE9ECC8F7D9BAE5">
        <parm name="EncodedCertificate" value="MIIE..shortened..3msMYf" datatype="binary" />
        <noparm name="Role" />
        <parm name="ValidFrom" value="2009-04-24T20:46:27Z" />
        <parm name="ValidTo" value="2010-04-24T20:46:27Z" />
        <parm name="IssuedBy" value="Contoso Assurance CA" />
        <parm name="IssuedTo" value="WM-pabans-52.amer.contoso.com" />
        <noparm name="TemplateName" />
        <characteristic type="PrivateKeyContainer">
          <parm name="ContainerName" value="MEEBAB1AFF" />
          <noparm name="ProviderName" />
          <parm name="ProviderType" value="1" />
          <parm name="KeySpec" value="2" />
        </characteristic>
        <characteristic type="RenewalInfo">
          <parm name="ServerName" value="rapp57.amer.contoso.com" />
          <parm name="Template" value="SCMDM2008MobileDevice" />
          <parm name="RequestPage" value="/certsrv/certfnsh.asp" />
          <parm name="PickupPage" value="/certsrv/certnew.cer" />
        </characteristic>
      </characteristic>
    </characteristic>
  </characteristic>
</wap-provisioningdoc>

Initiate renewal

Renewal request is initiated with following query to CertificateEnroller Configuration Service Provider. The RenewalCertificateHash parameter contains hash from the previous step.

<wap-provisioningdoc>
  <characteristic type="CertificateEnroller">
    <characteristic type="Operation">
      <characteristic type="RenewOperation">
        <characteristic type="Some-Unique-String-or-GUID">
          <parm name="RenewCertificateHash" value="64BB2D1FAD191B04D33D04781CE9ECC8F7D9BAE5"/>
        </characteristic>
      </characteristic>
    </characteristic>
  </characteristic>
</wap-provisioningdoc>

To determine result of the operation, you can either use the following query.

<wap-provisioningdoc>
    <characteristic type="CertificateEnroller">
        <characteristic type="Operation">
          <characteristic type="RenewOperation">
            <characteristic type="Same-Unique-String-from-the-previous-step">
              <parm-query name="Status" />
            </characteristic>
          </characteristic>
        </characteristic>
    </characteristic>
</wap-provisioningdoc>

Alternativelly you can check the \Windows\logfiles\GetCertificates\DeviceEnrollLog.txt

Windows Mobile Certificate Enrollment Log
Date: 2009-05-08
Time: 18:10:49Z
Device Name: WM-pabans-52
Domain\Username: (null)
Certificate Type Friendly Name: rapp57.amer.contoso.com_SCMDM2008MobileDevice
CA Server: rapp57.amer.contoso.com
Template: SCMDM2008MobileDevice
Request Page path/name: /certsrv/certfnsh.asp
Pickup Page path/name: /certsrv/certnew.cer
RequestID For Enrollment: 136756
Enrollment or Renewal: Renewal
Desktop Initiated: No
Silent Enrollment: No
Status Upon Completion: Successful
Error Code: The operation completed successfully.

I’ve seen many intelligent apartment concepts and technologies, including Microsoft super apartment located here in Prague. In fact all these concepts are nothing but pile of silicon crap collected in one apartment with 15 different remote controllers laying around. I believe that final solution has to be one remote controller for all devices. I also believe that all devices should be powered by .NET Micro Framework. This means there must be a way how to connect Infra Red receiver with .NET Micro Framework.

How does the TV Remote work?

TV Remotes in these days uses infra-red beam modulated on given frequency. Modulated signal can transfer information that represents command button and address of the target device. So, it’s all about PWM modulation. Unfortunately, every electronics vendor is using different PWM format and communication protocol.

Luckily, most of the TV Remote controllers has something common. Every transfer starts with some starting sequence which is nothing else than “very” long pulse. Then two groups of bit’s are transferred, first group represents command and second group represents address. The length of address field and command field is different for every brand. See SB Projects for more details on communication protocols.

Sony SIRC protocol 430x98 Figure 1. Sony SIRC data transfer

Sony SIRC protocol

Protocol of the Sony devices is very simple to decode and uses reasonably fast pulse length, which makes it easy to implement in .NET Micro Framework. See figure 1. for typical Sony data transmission. Start sequence takes 2.4ms and then the data bits follows. Logical 1 is represented by pulse of 1200us while logical 0 is 600us long. Standard Sony data message consist of 12 bits (7 bits for command, 5 bits for address).

Since commands are more or less same for all devices, address field is important to recognize the target. Most common Sony commands and addresses are in the table. For complete list go to: www.hifi-remote.com/sony

Device Address Command Function
TV 1 0 Key 1
VCR 2 1 Key 2
Cassete 14 2 Key 3
CD 17 3 Key 4
    4 Key 5
    5 Key 6
    6 Key 7
    7 Key 8
    8 Key 9
    9 Key 0
    16 Channel Up
    17 Channel Down
    18 Volume Up
    19 Volume Down

Connecting the IR Receiver with CPU

Key part is the infra red receiver. There are many infra red receivers on the market and it’s price is about one dollar. It’s important to buy IR receiver for the right frequency, most remote controllers operates on 38Khz. Even if Sony standard is 40Khz, surpsinigly 38Khz receiver works very well.

I’ve started with popular IR receiver SFH5110-38 but then drop it, because this receiver is very sensitive to input voltage. Even if it should operate from 4.5 to 5.5 volts, when voltage is not at least 5.2V sensor is very unstable. SFH5110-38 powered from USB operated Tahoe-I was unusable.

Then I went for TSOP4838 IR receiver and everything was fine. All these IR receivers works on the same principle and have same pin-out. The control pin is tight high until the signal of 38Khz frequency arrives. Then the pin is grounded and remains grounded till the signal is present. As a verification of the communication, LED is connected between power and control pin. As the signal is being transferred LED flashes. Complete list of components:

  • 38Khz IR receiver TSOP4838 (or other)
  • Low current LED (2 mA)
  • Resistor before LED (~700 ohms)

Connection IR receiver 325x190 Figure 2. IR Receiver circuit

Using the SonyReceiver class

From the paragraph above is obvious that decoding TV Remote signal is nothing but measuring the pulses. Instead of writing class specifically for Sony TV Remotes, I’ve decided to create general “infrastructure” for future extensibility. All remote controllers are derived from TVRemoteReceiver abstract class, which triggers DataReceived event whenever the valid data are received.

abstract public class TVRemoteReceiver : IDisposable
{
    public Cpu.Pin ReceiverPin;
    public abstract void Dispose();
    public delegate void TVRemoteDataHandler(TVRemote sender, int command, int address);
    public event TVRemoteDataHandler DataReceived;
    protected virtual void OnDataReceived(int command, int address)
}

SonyReceiver class is just the filling for the interface above, which triggers the event when Sony data packet arrived. Usage of SonyReceiver is in the listing bellow.

public static void Main()
{
    TVRemoteReceiver sony = new SonyReceiver(Meridian.Pins.GPIO1);
    sony.DataReceived += new TVRemoteReceiver.TVRemoteDataHandler(sony_OnDataReceived);

    while (true)
    {
        Thread.Sleep(20);
    }
}

static void sony_OnDataReceived(TVRemote sender, int command, int address)
{
    Debug.Print("----");
    Debug.Print(address.ToString());
    Debug.Print(command.ToString());
}

TV Remote as the WPF input

To make things even better I’ve implemented TVRemoteInputProvider, which translates Remote Controller commands into WPF button events. This makes it perfect alternative method to standard GpioButtonInputProvider, because application can use as many InputProviders as developer needs.

Constructor of TVRemoteInputProvider takes four arguments. First agument is the presentation source, second argument is the TVRemoteReceiver driver, third argument is the target address of the device, passing -1 into 3rd argument makes input provider to ignore address field completely. In that case commands from all Sony remotes will be accepted (TV, VCR, Stereo etc.). Last argument is the button map, which binds TV Remote commands into WPF buttons.

Beside described constructor arguments, the TVRemoteInputProvider has one public property ButtonAction represented by the RawButtonActions value, which will be passed in the WPF event. It’s obvious that ButtonUp and ButtonDown can’t be recognized in the infra red data packet. By default ButtonAction is set to RawButtonActions.ButtonUp.

Following code snippet is taken from Tetris games controlled by TV Remote as seen on the video above. By the way, this was the only thing necessary to add into Tetris source at all!

SonyReceiver sonyRemote = new SonyReceiver(Meridian.Pins.GPIO1);

TVRemoteButtonPad[] buttons = new TVRemoteButtonPad[]
{
    new TVRemoteButtonPad(116, Button.VK_UP),
    new TVRemoteButtonPad(117, Button.VK_DOWN),
    new TVRemoteButtonPad(051, Button.VK_RIGHT),
    new TVRemoteButtonPad(052, Button.VK_LEFT),
    new TVRemoteButtonPad(101, Button.VK_RETURN),
};

// Create the object that configures the TV Remote commands to buttons.
TVRemoteInputProvider sonyProvider = new TVRemoteInputProvider(null, 
                                              sonyRemote, 
                                              (int)SonyReceiver.Devices.TV, 
                                              buttons);
                                              
sonyProvider.ButtonAction = RawButtonActions.ButtonDown;

// Create the object that configures the GPIO pins to buttons.
GPIOButtonInputProvider inputProvider = new GPIOButtonInputProvider(null);

Imagine how cool it would be, if IrDA Tetris will be connected with GHI VGA expansion board.

Conclusion

Infra red transfer is very interesting topic but to successfully implement it in .NET MF device several factors are necessary to consider. Most important fact is, that infra red light is part of other light sources as well, not only the IR diode of TV Remote. This means that day light or flickering light tube is possible source of noise for IR transfers. It’s good to place IR receiver in the black box and put filter on the input side. Most TVs and other devices has IR receivers placed behind the dark red plastic filter.

Feel free to download my IrDA library and source codes: Bansky.SPOT.IrDA.zip [162 Kb]

Feel free to download IrDA library source codes from my GitHub repository


What I like most about .NET Micro Framework is the interfacing :) Today it will be blog post about interfacing and controlling of bi-polar stepper motor. I was about to do it some time ago, and when question on stepper was raised in the discussion group I’ve decided to pick the gauntlet. As you will see driving stepper motor is truly piece of cake.

How does stepper motor works

I don’t want to run into much details on stepper motor. There are better place to learn about stepper motors and it’s principles. One can start as usual on Wikipedia and go to more specialized places.

Most important information is that there are two types of stepper motors: Unipolar and Bipolar. The difference is in the construction as well as in the controlling sequence. Bi-polar motors has simpler construction and becoming more common and popular. Unlike the normal motors, steppers has stator with “independently” controlled coils. As those coils are energized in specific sequence the magnetic shaft (rotor) tends to align itself according to pole of the coils. This makes shaft to rotate.

Bi-polar motors are controlled by four steps sequence, where polarity of coils is inverted. Table bellow shows the sequence steps. Applying steps in reverse order will make motor to rotate opposite direction. Every motor has different resolution for one step, most common value is 1.8 degrees per step. This resolution has to be kept in mind while controlling the motor.

# A+ A- B+ B-
step 1 1 0 0 0
step 2 0 0 1 0
step 3 0 1 0 0
step 4 0 0 0 1

Interfacing the motor

Bi-polar motors are connected using four wires, two per each coil. To find out corresponding wires it’s good to measure resistivity between two wires. Two independent wires should return “zero” resistance. Once the corresponding wires for each coil is found, it can be connected to GPIO of the microcontroller. When I say connected I don’t mean directly connected. It’s obvious that GPIO output voltage can’t drive the stator coils and flowing current will destroy the CPU.

It’s necessary to use buffer, transistor or other solution to isolate control voltage from the power voltage. Very common circuit for this purpose is the L293D. I was blogging about it already in my robotics articles, so we can skip the details. L293D is the dual H-Bridge so it can drive four independent “high” current outputs. L293D has ability to drive output current of 600mA for each channel and maximum power voltage of +36V.

L293D and Stepper Motor 450x370 Figure 1. L293D and bipolar stepper motor

Controlling the motor

I believe you’ve already see, that to control stepper motor is not a big challenge for .NET Micro Framework. I’ve implemented simple BipolarStepper class, which takes two arguments. First is the array of pins where motor is connected and second one is resolution. Two public methods MoveAngle(float) and MoveSteps(int) rotates motor for appropriate distance.

MoveSteps method is “a bit more” complicated because it has to take care of direction change. To control the speed of the movement the Delay property is used. It’s nothing else than the delay between each steps. Usage of the class is pretty straight forward.

BipolarStepper motor = new BipolarStepper(new Cpu.Pin[] { 
                            Meridian.Pins.GPIO1,        // coil A+
                            Meridian.Pins.GPIO2,        // coil A-
                            Meridian.Pins.GPIO3,        // coil B+
                            Meridian.Pins.GPIO4},       // coil B-
                            1.8F);                      // 1.8 deg per step

motor.MoveAngle(-45);
motor.MoveAngle(45);
public class BipolarStepper : IDisposable
{
    /// <summary>
    /// Instance of BipolarStepper class
    /// </summary>
    /// <param name="portList">Array of Cpu.Pin ports: A+ A- B+ B-</param>
    /// <param name="resolution">Degrees per one step</param>
    public BipolarStepper(Cpu.Pin[] portList, float resolution)
    {
        // Argument validation
        if (portList == null || portList.Length < OUTPUT_COUNT || resolution <= 0)
            throw new ArgumentOutOfRangeException();

        // Create array of OutputPorts 
        this._portList = new OutputPort[OUTPUT_COUNT];
        for (int i = 0; i < OUTPUT_COUNT; i++)
        {
            this._portList[i] = new OutputPort(portList[i], false);
        }

        this.Resolution = resolution;            
        this.Delay = 3;
    }

    /// <summary>
    /// Move motor for given degrees
    /// </summary>
    /// <param name="degrees">Angle in degrees</param>
    public void MoveAngle(float degrees)
    {
        MoveSteps((int)(degrees / Resolution));
    }

    /// <summary>
    /// Move motor for given number of steps
    /// </summary>
    /// <param name="count">Number of steps</param>
    public void MoveSteps(int count)
    {
        int direction = (count < 0) ? -1 : 1;
        int stepCount = System.Math.Abs(count);

        if (direction != _lastDirection)
            AddStep(-2 * direction);

        for (int i = 0; i < stepCount; i++)
        {
            Output(_steps[_lastStep]);
            AddStep(direction);

            Thread.Sleep(this.Delay);
        }

        _lastDirection = direction;
    }

    /// <summary>
    /// Change lastStep value
    /// </summary>
    private void AddStep(int count)
    {
        _lastStep = _lastStep + count;

        if (_lastStep >= OUTPUT_COUNT)
            _lastStep = 0;
        else if (_lastStep < 0)
            _lastStep = OUTPUT_COUNT - 1;
    }

    /// <summary>
    /// Writes a value to the port output
    /// </summary>
    /// <param name="outputValue">Number to write out</param>
    private void Output(byte outputValue)
    {
        int portListLength = OUTPUT_COUNT;
        for (int i = 0; i < portListLength; i++)
        {
            this._portList[i].Write(((outputValue & 1) == 1));
            outputValue >>= 1;
        }
    }

    public void Dispose()
    {
        // Dispose array members        
        int portListLength = this._portList.Length;

        for (int i = 0; i < portListLength; i++)
        {
            this._portList[i].Dispose();
        }
    }

    /// <summary>
    /// Degrees per one step
    /// </summary>
    public float Resolution;

    /// <summary>
    /// Delay between steps
    /// </summary>
    public int Delay;

    private OutputPort[] _portList;
    private byte[] _steps = new byte[] { 1, 4, 2, 8 };
    int _lastStep, _lastDirection;
    const int OUTPUT_COUNT = 4;
}

This year was a first one where broader Micro Framework community meet. Meet point was Embedded World in Nurnmebrg. It was good event and location, not so far for most Europeans and New Zealenders :-) No matter how internet is cool, meeting people face to face is always better.

Here comes few pictures from our MicroCon. More pictures at Michael’s Blog.

Looking forward to see more people there next year!

Micro Framework Community 640x480

Micro Framework Community 640x480

Micro Framework Community 640x480

Micro Framework Community 640x480

Micro Framework Community 640x480

Micro Framework Community 640x480

Micro Framework Community 640x480


Updated and improved documentation for .NET Micro Framework 3.0 was released in February. Now you can have full reference for Micro Framework classes always available on your hard drive.

I believe this will finally solve problems with finding right reference for some classes. Don’t wait and start downloading from MSDN: .NET Micro Framework SDK 3.0 February 2009 Documentation Update.

Update is a simple zip file with documentation and readme.txt describing the installation process. It’s nothing more than copying PSDK.hxs from zip archive into the C:\Program Files\Microsoft .NET Micro Framework\v3.0\Documentation\NET MicroFramework Docs

Enjoy!

Micro Framework Documentation 510x348 Documentation