EjectABed Version 2 – Now Using the Raspberry Pi (Part 2)

With the connection from Twitter to the PI working well, I decided to hook up the bed top the PI.  The Bed is controlled via a server attached to a bellow that forces air to the screw drive.  You can read about how we figured that one out here.
My initial thought was that it would be easy as the Netduino implementation to control the servo was all of 5 lines of code.  The Netduino has built-in PWM ports and the api has a PWM class:
1 uint period = 20000; 2 uint duration = SERVO_NEUTRAL; 3 _servo = new PWM(PWMChannels.PWM_PIN_D5, period, duration, PWM.ScaleFactor.Microseconds, false); 4 _servo.Start(); 5 _servoReady = true;

However, when I went to look for a PWM port, there wasn’t one!  Ugh!  I want over to Stack Overflow to confirm with this question and sure enough, no PWM.  The only example for servo control that the Windows 10 code samples have are using the GPIO to activate a servo forwards and backwards, but that will not work because I need to hold the bellow in a specific place for the air to push correctly.  The Windows IoT team suggested that I use the AdaFruit PWM shield for the control

image

So I ordered a couple and then my son soldered the pins in

20150904_201350 20150904_201104

I then hooked up the shield to the servo and the PI
20150906_105036
and went to look for some PI code to control the pwms.  Another problem, there isn’t any!  I went over to the Raspberry Pi forums and it turns out, they are waiting for MSFT to finish that piece.  Ugh, I decided to take the path of least resistance and I removed that PWM shield and added back in the Netduino

20150906_105156

Now I have the ability to control the servo from the PI.  I would have rather cut out the Netduino completely, but the limitations of Win10 on Raspberry Pi won’t allow me to do that.  Oh well, it is still a good entry and it was a lot of fun to work on.
Advertisements

EjectABed Version 2 – Now Using the Raspberry Pi (Part 1)

I recently entered a hackster.io competition that centered around using Windows 10 on the Raspberry Pi.  I entered the ejectabed and it was accepted to the semi-final round.  My thought was to take the existing ejectabed controller from a Netduino and move it to a Raspberry Pi.  While doing that, I could open the ejectabed from my local area network to the internet so anyone could eject Sloan.
My 1st step was hook my Raspberry Pi up to my home network and deploy from Visual Studio to it.  Turns out, it was pretty straightforward.
I took a old Asus Portable Wireless Router and plugged it into my home workstation.  I then configured the router to act as an Access Point so that it would pass though all traffic from the router to which my developer workstation is attached.  I then attached the router to the PI and powered it though the PI’s USB port.  I then plugged the PI’s HDMI out to a spare monitor of mine.

20150822_112947

With all of the hardware plugged in, I headed over to Windows On Devices and followed the instructions on how to set up a Raspberry PI.  After installing the correct software on my developer workstation, flashing the SD card with win10, plugging the SD card into the PI, turning the PI on, and then remoting into the PI via powershell, I could see the PI on my local workstation via the Windows IoT Core Watcher and the PI showing its friendly welcome screen via HDMI.

Capture

20150822_101235

I then headed over to Visual Studio and copy/pasted the equisite “Hello IoT World” Blinky project to the Pi and watched the light go on and off.

20150822_104535

With that out of the way, I decided to look at controlling the light via Twitter and Azure.  The thought was to have the PI monitor a message queue on Azure and whenever there was a message, blink on or off (simulating the ejectabed being activated).  To that end, I went into Azure and created a basic storage account.  One of the nice things about Azure is that you get a queue out of the box when you create a storage account:

image

One of the not so nice things about Azure is that there is no way to control said Queue via their UI.  You have to create, push, and pull from the queue in code.  I went back to visual studio and added in the Azure Storage Nuget package

image

I then created a method to monitor the queue
1 internal async Task<Boolean> IsMessageOnQueue() 2 { 3 var storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=ejectabed;AccountKey=xxx"; 4 var storageAccount = CloudStorageAccount.Parse(storageConnectionString); 5 var client = storageAccount.CreateCloudQueueClient(); 6 var queue = client.GetQueueReference("sloan"); 7 var queueExists = await queue.ExistsAsync(); 8 if (!queueExists) 9 { 10 GpioStatus.Text = "Queue does not exist or is unreachable."; 11 return false; 12 } 13 var message = await queue.GetMessageAsync(); 14 if (message != null) 15 { 16 await queue.DeleteMessageAsync(message); 17 return true; 18 } 19 GpioStatus.Text = "No message for the EjectABed."; 20 return false; 21 } 22

Then if there is a message, the PI would run the ejection sequence (in this case blink the light)
1 internal void RunEjectionSequence() 2 { 3 bedCommand.Eject(); 4 bedTimer = new DispatcherTimer(); 5 bedTimer.Interval = TimeSpan.FromSeconds(ejectionLength); 6 bedTimer.Tick += LightTimer_Tick; 7 bedTimer.Start(); 8 }

 

I deployed the code to the PI without a problem.  I then created a Basic console application to push messages to the queue that the PI could drain
1 class Program 2 { 3 static String storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=ejectabed;AccountKey=xxx"; 4 5 static void Main(string[] args) 6 { 7 Console.WriteLine("Start"); 8 Console.WriteLine("Press The 'E' Key To Eject. Press 'Q' to quit..."); 9 10 var keyInfo = ConsoleKey.S; 11 do 12 { 13 keyInfo = Console.ReadKey().Key; 14 if (keyInfo == ConsoleKey.E) 15 { 16 CreateQueue(); 17 WriteToQueue(); 18 //ReadFromQueue(); 19 } 20 21 } while (keyInfo != ConsoleKey.Q); 22 23 Console.WriteLine("End"); 24 Console.ReadKey(); 25 } 26 27 private static void CreateQueue() 28 { 29 var storageAccount = CloudStorageAccount.Parse(storageConnectionString); 30 var client = storageAccount.CreateCloudQueueClient(); 31 var queue = client.GetQueueReference("sloan"); 32 queue.CreateIfNotExists(); 33 Console.WriteLine("Created Queue"); 34 } 35 36 private static void WriteToQueue() 37 { 38 var storageAccount = CloudStorageAccount.Parse(storageConnectionString); 39 var client = storageAccount.CreateCloudQueueClient(); 40 var queue = client.GetQueueReference("sloan"); 41 var message = new CloudQueueMessage("Eject!"); 42 queue.AddMessage(message); 43 Console.WriteLine("Wrote To Queue"); 44 } 45 46 47 private static void ReadFromQueue() 48 { 49 var storageAccount = CloudStorageAccount.Parse(storageConnectionString); 50 var client = storageAccount.CreateCloudQueueClient(); 51 var queue = client.GetQueueReference("sloan"); 52 var queueExists = queue.Exists(); 53 if (!queueExists) 54 Console.WriteLine("Queue does not exist"); 55 var message = queue.GetMessage(); 56 if (message != null) 57 { 58 queue.DeleteMessage(message); 59 Console.WriteLine("Message Found and Deleted"); 60 } 61 else 62 { 63 Console.WriteLine("No messages"); 64 } 65 } 66

I could then Write to the queue and the PI would read and react.  You can see it in action here:

image

With the queue up and running, I was ready to add in the ability for someone to Tweet to the queue.  I created a cloud service project and pointed to a new project that will monitor Twitter and then push to the queue:

image

image

The Twitter project uses the TweetInvi nuget package and is a worker project.  It makes a call to Twitter every 15 seconds and if there is a tweet to “ejectabed” with a person’s name, it will write to the queue (right now, only Sloan’s name is available)
1 type TwitterWorker() = 2 inherit RoleEntryPoint() 3 4 let storageConnectionString = RoleEnvironment.GetConfigurationSettingValue("storageConnectionString") 5 6 let createQueue(queueName) = 7 let storageAccount = CloudStorageAccount.Parse(storageConnectionString) 8 let client = storageAccount.CreateCloudQueueClient() 9 let queue = client.GetQueueReference(queueName); 10 queue.CreateIfNotExists() |> ignore 11 12 let writeToQueue(queueName) = 13 let storageAccount = CloudStorageAccount.Parse(storageConnectionString) 14 let client = storageAccount.CreateCloudQueueClient() 15 let queue = client.GetQueueReference(queueName) 16 let message = new CloudQueueMessage("Eject!") 17 queue.AddMessage(message) |> ignore 18 19 let writeTweetToQueue(queueName) = 20 createQueue(queueName) 21 writeToQueue(queueName) 22 23 let getKeywordFromTweet(tweet: ITweet) = 24 let keyword = "sloan" 25 let hasKeyword = tweet.Text.Contains(keyword) 26 let isFavourited = tweet.FavouriteCount > 0 27 match hasKeyword, isFavourited with 28 | true,false -> Some (keyword,tweet) 29 | _,_ -> None 30 31 32 override this.Run() = 33 while(true) do 34 let consumerKey = RoleEnvironment.GetConfigurationSettingValue("consumerKey") 35 let consumerSecret = RoleEnvironment.GetConfigurationSettingValue("consumerSecret") 36 let accessToken = RoleEnvironment.GetConfigurationSettingValue("accessToken") 37 let accessTokenSecret = RoleEnvironment.GetConfigurationSettingValue("accessTokenSecret") 38 39 let creds = Credentials.TwitterCredentials(consumerKey, consumerSecret, accessToken, accessTokenSecret) 40 Tweetinvi.Auth.SetCredentials(creds) 41 let matchingTweets = Tweetinvi.Search.SearchTweets("@ejectabed") 42 let matchingTweets' = matchingTweets |> Seq.map(fun t -> getKeywordFromTweet(t)) 43 |> Seq.filter(fun t -> t.IsSome) 44 |> Seq.map (fun t -> t.Value) 45 matchingTweets' |> Seq.iter(fun (k,t) -> writeTweetToQueue(k)) 46 matchingTweets' |> Seq.iter(fun (k,t) -> t.Favourite()) 47 48 Thread.Sleep(15000) 49 50 override this.OnStart() = 51 ServicePointManager.DefaultConnectionLimit <- 12 52 base.OnStart()

Deploying to Azure was a snap
image
And now when I Tweet,
image
the PI reacts.  Since Twitter does not allow the same Tweet to be sent again, I deleted it every time I wanted to send a new message to the queue.

Hacking the Dream Cheeky Thunder Missile Launcher, Part 2

One of the things that the Terminator will have is a missile launcher, which I started hacking here.  The missile launcher Api is controlled by time.  Specifically, you tell it to turn in a certain direction for a certain amount of time. 

1 member this.moveMissleLauncher(data, interval:int) = 2 if devicePresent then 3 this.SwitchLed(true) 4 this.sendUSBData(data) 5 Thread.Sleep(interval) 6 this.sendUSBData(this.STOP) 7 this.SwitchLed(false) 8

The challenge is converting that duration into X,Y Cartesian coordinates the way the Kinect and the phidget laser system does.  And before getting Cartesian coordinates, we needed to get the polar coordinates.  This is how we did it.

First, we tackled the pan (X coordinate) of the missile launcher.  The launcher is a on a square base and the full range of the launcher is 45 degrees to 315 degrees.

image

With some experimentation, we determined that the total time it takes the turret to traverse from 45 degrees to 315 degrees (270 total degrees) is 6346 Milliseconds.  Assume that the motor is consistent (which is a big if using cheap electronics), it takes the motor about 23.5 milliseconds to move 1 degree on the X axis. 

The tilt was more of a challenge.  We needed a way of measuring the total range along the Y axis.  To that end, we placed the turret 300 millimeters away from the wall.  We then placed a laser pointer on the turret and put a level on it to ensure that it was a 0 degrees and marked the wall.  We then moved the turret to its highest position and then to its lowest, marking the wall with those points.  We then measured the distance to the highest and lowest point on the wall.

WP_20141010_002WP_20141010_001

 

image

Assuming the wall was vertical, we could then use this site to figure out the angle of the turret.  We first calculated the length of the unknown side using the Pythagorean theorem (3002 + 2352 = X2) =  Sqrt(90000 + 55225) = 381

With all three sides known, we went over to this great site to use some basic trigonometry to help solve the angle problem.  Since we are looking at the angle between adjacent and hypotenuse, we need to determine the  inverse cosine via this formula: cos(θ) = Adjacent / Hypotenuse.  cos(θ) = 300/381 or cos(θ) = .7874 or (θ) = cos-1.7874 or .6642.  This means our rocket launcher can move up about 66 degrees and by doing the same calculation for down, it can move down about -8 degrees.  Since 66+8 equals 74, we decided to round to 75.

We then determined that the total time it takes the rocket launcher to traverse from its max up position to level was 710 milliseconds.  Dividing 66 into 710, each degree takes about 10.7 milliseconds.

So with handy chart in place, we are ready to map the polar coordinates to the Missile Launcher

image

I added the adjustment values to the type

1 let tiltMultiplier = 10.7 2 let panMultiplier = 23.1 3

So now it is a question of keeping track of where the launcher is pointed at and then calling the correct adjustments.  To that end, I created a couple of mutable variables and set them to 90 when the missile launcher initializes

1 let mutable currentPan = 0. 2 let mutable currentTilt = 0. 3

1 member this.Reset() = 2 if devicePresent then 3 this.moveMissleLauncher(this.LEFT,6346) 4 this.moveMissleLauncher(this.RIGHT,3173) 5 this.moveMissleLauncher(this.UP,807) 6 this.moveMissleLauncher(this.DOWN,710) 7 currentPan <- 90. 8 currentTilt <- 90. 9 ()

I then implemented the method for the interface to acquire the target.  Note that I am pretty liberal with with my use of explanatory variables.

1 member this.AquireTarget(X:float, Y:float) = 2 match X = 0.0, Y = 0.0 with 3 | true,true -> false 4 | true,false -> false 5 | false,true -> false 6 | false, false -> 7 let tilt = X 8 let pan = Y 9 10 let tiltChange = currentTilt - tilt 11 let panChange = currentPan - pan 12 13 let tiltChange' = int tiltChange 14 let panChange' = int panChange 15 16 let tiltChange'' = abs(tiltChange) 17 let panChange'' = abs(panChange) 18 19 match tiltChange' with 20 | tiltChange' when tiltChange' > 0 -> this.Down(tiltChange''); currentTilt <- tilt 21 | tiltChange' when tiltChange' < 0 -> this.Up(tiltChange''); currentTilt <- tilt 22 | tiltChange' when tiltChange' = 0 -> () 23 | _ -> () 24 25 match panChange' with 26 | panChange' when panChange' > 0 -> this.Left(panChange''); currentPan <- pan 27 | panChange' when panChange' < 0 -> this.Right(panChange''); currentPan <- pan 28 | panChange' when panChange' = 0 -> () 29 | _ -> () 30 true 31

And with that, we have another weapons system we can add to our kinect Terminiator

Hacking the Dream Cheeky Thunder

A couple of weeks ago, Atmel tweeted about some people that hacked the Dream Cheeky Thunder Missile Launcher by soldering on a Ardunino to the circuit board.

image

A quick Google search shows there are lots of people who done something similar, including this post.  Since I was not interested in messing around wit the circuit board, I decided to go the software hack route.  When the missile launcher arrived, I downloaded software and installed it on my Windows 7 machine.  I then used Telerik’s JustDecompile software to look at the source code.

image

Fortunately, the main executable is a .NET 2.0 Windows Form application.  Unfortunately, the code is a mess and it relies on user controls.  Specifically, the library that the .NET .exe consumes is called USBLib.dll which is a 23-bit Com component that creates a windows control that the main .exe uses.

When I took the code from JustDecompile and stuck it into Visual Studio (no F# option so I went with C#), it took about 2-3 hours to get all of the references set up, the resources set up, and the embedded code put into the right location, but I did manage to get a working Visual Studio solution

image

I then decided to build a brand new solution that controls the missile launcher without the graphical components that are baked into the app.  I added a reference to the USBLib.dll and then tried to make method calls.  No luck, it looks like the application uses Windows Event hooks to call and respond:

protected override void WndProc(ref Message m) { this.USB.ParseMessages(ref m); if (m.Msg == SingleProgramInstance.WakeupMessage) { if (base.WindowState == FormWindowState.Minimized) { base.Visible = true; base.WindowState = FormWindowState.Normal; } base.Activate(); } base.WndProc(ref m); }

Yuck!  I then did a quick search on Google to find a .NETUsbDriver that I could use because I already have the byte array values that the missile launcher is expected:

I found this but the suggestions did not compile and/or did not work.  I then found this site which in the right direction.  I added the code into my project like so:

image

I then wired up the main form like this:

image

With the code behind like this:

MissileLauncher _launcher = new MissileLauncher(); public Form1() { InitializeComponent(); _launcher.command_reset(); _launcher.command_switchLED(true); } private void upButton_Click(object sender, EventArgs e) { _launcher.command_Up(2000); } private void fireButton_Click(object sender, EventArgs e) { _launcher.command_Fire(); } private void downButton_Click(object sender, EventArgs e) { _launcher.command_Down(1000); } private void rightButton_Click(object sender, EventArgs e) { _launcher.command_Right(3000); } private void leftButton_Click(object sender, EventArgs e) { _launcher.command_Left(3000); }

Then, when I run it, it works like a champ.  I now just need to translate the values passed into the Thread.Sleep() and have it correspond to the angles.  The author of the code was on the right track because s/he named the parameter “degree”. 

In the meantime, I ported the code to FSharp.  You can see it here and you can see the missle launcher in action here.  The major difference is that the C# code had 182 lines and the F# code has 83.