Being a hacker, I naturally gravitate to junkyards, yard sales, and surplus warehouses. In addition, pursuing our local newspaper’s classified ads, Craig’s List, and Ebay often uncovers really neat items that can be turned into IoT projects. Case in point, a couple of years ago I was on Craig’s List when I ran across this ad:
So of course I picked it up and brought it home. Since we have a hard time getting our youngest out of bed in the morning, I made sense to try and hook it up to his bed.
The first order of business was to understand how the bed’s controller works. We first thought that the controller used PWM so we hooked up our Oscilloscope to the two metal wires that travel from the controller to the bed’s screw drive. We moved the controller up and down but no signal was being recorded. Sonoma then noticed that the “wire” was a hollow tube. Taking a wild guess, we blew down the pipe. Sure enough, that made the bed move.
So now we had to figure out how the controller moved air up and down the pipe. We opened the controller and there is a small bellow that attaches to the pipe. Press the controller on 1 side and air is forced down the pipe, press the controller on the other side and air is sucked up the pipe.
So we batted around ideas about how to push air up and down the pipe – ideas included using a small electric air compressor, some kind of mechanical pressure plate, etc… We then decided to try and gluing a servo to the switch and controlling the movement that way. However, we couldn’t figure how to attach the servo to the existing plastic switch. So we decided to build our own switch – we used my son’s erector set to create the harness for the bellows.
With the switch now hooked up, we had a way of controlling the bed. We then took the screw drive off of the hospital bed and attached it to the frame of my son’s bed.
We then attached some hinges from the box spring to the base and the bed pushes up about 45 degrees.
Now that we have the mechanical pieces worked out, we needed to tackle the electronics and coding -> basically from the servo on out. The first thing we did was take the Raspberry Pi and hook it up to our home network. Because the bed is not near any ethernet port, we took an old Asus Portable Wireless Router, configured it to be an Access Point, and plugged it into the Raspberry Pi’s ethernet port. To stand in as the servo, we followed the Windows IoT’s team code sample for the Blinky project and hooked up a light.
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.
I then headed over to Visual Studio and copy/pasted the “Blinky” project to the Pi and watched the light go on and off.
With that out of the way, I decided to look at controlling the light via Twitter and Azure. I spent a couple of days working though the Windows Azure Insider articles in MSDN by Terkaly and Villaobos (The Azure Service Bus And The Internet of Things) but the archtecutre was more for lots of telemetry data being blasted into Azure versus the occasion "eject" command. I decided to follow Occum's Razor and use a simple message queue. 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:
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
I then created a method to monitor the queue
internal async Task IsMessageOnQueue()
{
var storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=ejectabed;AccountKey=xxx";
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("sloan");
var queueExists = await queue.ExistsAsync();
if (!queueExists)
{
GpioStatus.Text = "Queue does not exist or is unreachable.";
return false;
}
var message = await queue.GetMessageAsync();
if (message != null)
{
await queue.DeleteMessageAsync(message);
return true;
}
GpioStatus.Text = "No message for the EjectABed.";
return false;
}
Then if there is a message, the PI would run the ejection sequence (in this case blink the light)
internal void RunEjectionSequence()
{
bedCommand.Eject();
bedTimer = new DispatcherTimer();
bedTimer.Interval = TimeSpan.FromSeconds(ejectionLength);
bedTimer.Tick += LightTimer_Tick;
bedTimer.Start();
}
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
class Program
{
static String storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=ejectabed;AccountKey=xxx";
static void Main(string[] args)
{
Console.WriteLine("Start");
Console.WriteLine("Press The 'E' Key To Eject. Press 'Q' to quit...");
var keyInfo = ConsoleKey.S;
do
{
keyInfo = Console.ReadKey().Key;
if (keyInfo == ConsoleKey.E)
{
CreateQueue();
WriteToQueue();
//ReadFromQueue();
}
} while (keyInfo != ConsoleKey.Q);
Console.WriteLine("End");
Console.ReadKey();
}
private static void CreateQueue()
{
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("sloan");
queue.CreateIfNotExists();
Console.WriteLine("Created Queue");
}
private static void WriteToQueue()
{
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("sloan");
var message = new CloudQueueMessage("Eject!");
queue.AddMessage(message);
Console.WriteLine("Wrote To Queue");
}
private static void ReadFromQueue()
{
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("sloan");
var queueExists = queue.Exists();
if (!queueExists)
Console.WriteLine("Queue does not exist");
var message = queue.GetMessage();
if (message != null)
{
queue.DeleteMessage(message);
Console.WriteLine("Message Found and Deleted");
}
else
{
Console.WriteLine("No messages");
}
}
I could then write to the queue and the PI would read and react. You can see it in action here:
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:
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)
type TwitterWorker() =
inherit RoleEntryPoint()
let storageConnectionString = RoleEnvironment.GetConfigurationSettingValue("storageConnectionString")
let createQueue(queueName) =
let storageAccount = CloudStorageAccount.Parse(storageConnectionString)
let client = storageAccount.CreateCloudQueueClient()
let queue = client.GetQueueReference(queueName);
queue.CreateIfNotExists() |> ignore
let writeToQueue(queueName) =
let storageAccount = CloudStorageAccount.Parse(storageConnectionString)
let client = storageAccount.CreateCloudQueueClient()
let queue = client.GetQueueReference(queueName)
let message = new CloudQueueMessage("Eject!")
queue.AddMessage(message) |> ignore
let writeTweetToQueue(queueName) =
createQueue(queueName)
writeToQueue(queueName)
let getKeywordFromTweet(tweet: ITweet) =
let keyword = "sloan"
let hasKeyword = tweet.Text.Contains(keyword)
let isFavourited = tweet.FavouriteCount > 0
match hasKeyword, isFavourited with
| true,false -> Some (keyword,tweet)
| _,_ -> None
override this.Run() =
while(true) do
let consumerKey = RoleEnvironment.GetConfigurationSettingValue("consumerKey")
let consumerSecret = RoleEnvironment.GetConfigurationSettingValue("consumerSecret")
let accessToken = RoleEnvironment.GetConfigurationSettingValue("accessToken")
let accessTokenSecret = RoleEnvironment.GetConfigurationSettingValue("accessTokenSecret")
let creds = Credentials.TwitterCredentials(consumerKey, consumerSecret, accessToken, accessTokenSecret)
Tweetinvi.Auth.SetCredentials(creds)
let matchingTweets = Tweetinvi.Search.SearchTweets("@ejectabed")
let matchingTweets' = matchingTweets |> Seq.map(fun t -> getKeywordFromTweet(t))
|> Seq.filter(fun t -> t.IsSome)
|> Seq.map (fun t -> t.Value)
matchingTweets' |> Seq.iter(fun (k,t) -> writeTweetToQueue(k))
matchingTweets' |> Seq.iter(fun (k,t) -> t.Favourite())
Thread.Sleep(15000)
override this.OnStart() =
ServicePointManager.DefaultConnectionLimit <- 12
base.OnStart()
Deploying to Azure was a snap
And now when I Tweet, 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.
With the connection from Twitter to the PI working well, I decided to hook up the bed to the PI by by replacing the light with a servo. I created an interface
public interface IBedCommand
{
Boolean Initialize();
void Eject();
void Reset();
void Sleep();
}
And added in a light implementation (the blinky code)
public class SingleLight : IBedCommand
{
private const int LED_PIN = 5;
private GpioPin pin;
private GpioPinValue pinValue;
public Boolean Initialize()
{
var gpio = GpioController.GetDefault();
if (gpio == null)
{
pin = null;
return false;
}
else
{
pin = gpio.OpenPin(LED_PIN);
pin.SetDriveMode(GpioPinDriveMode.Output);
pinValue = GpioPinValue.High;
pin.Write(pinValue);
return true;
}
}
public void Eject()
{
if (pin != null)
{
pinValue = GpioPinValue.Low;
pin.Write(pinValue);
}
}
public void Reset()
{
if (pin != null)
{
pinValue = GpioPinValue.Low;
pin.Write(pinValue);
}
}
public void Sleep()
{
if (pin != null)
{
pinValue = GpioPinValue.High;
pin.Write(pinValue);
}
}
}
I then went to implement a servo class. 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:
uint period = 20000;
uint duration = SERVO_NEUTRAL;
_servo = new PWM(PWMChannels.PWM_PIN_D5, period, duration, PWM.ScaleFactor.Microseconds, false);
_servo.Start();
_servoReady = true;
However, when I went to look for a PWM port on the Pi, 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
So I ordered a couple and then my son soldered the pins in
I then hooked up the shield to the servo and the PI
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
And then take some of my legacy code and push it out to the Netduino
public class Program
{
private static PWM _servo = null;
private static InputPort _trigger = null;
private static bool _servoReady = false;
private const uint SERVO_UP = 1250;
private const uint SERVO_DOWN = 1750;
private const uint SERVO_NEUTRAL = 1500;
public static void Main()
{
OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
SetUpTrigger();
SetUpServo();
while (true)
{
led.Write(true);
Thread.Sleep(250);
led.Write(false);
Thread.Sleep(250);
}
}
internal static void SetUpTrigger()
{
_trigger = new InputPort(Pins.GPIO_PIN_D10, false, Port.ResistorMode.PullDown);
_trigger.OnInterrupt += _trigger_OnInterrupt;
}
private static void _trigger_OnInterrupt(uint data1, uint data2, DateTime time)
{
_servoReady = false;
ActivateServoForBellows("UP", 20000);
ActivateServoForBellows("DOWN", 20000);
_servoReady = true;
}
internal static void SetUpServo()
{
uint period = 20000;
uint duration = SERVO_NEUTRAL;
_servo = new PWM(PWMChannels.PWM_PIN_D5, period, duration, PWM.ScaleFactor.Microseconds, false);
_servo.Start();
_servoReady = true;
}
internal static void ActivateServoForBellows(String direction, Int32 duration)
{
if(_servoReady)
{
if (direction == "UP")
{
_servo.Duration = SERVO_UP;
}
else if (direction == "DOWN")
{
_servo.Duration = SERVO_DOWN;
}
else
{
_servo.Duration = SERVO_NEUTRAL;
}
Thread.Sleep(duration);
_servo.Duration = SERVO_NEUTRAL;
}
}
So the Netduino code gets deployed to the Netduino and I can use that light class on the PI to tell the Netduino to move. 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.
So with all of the pieces in place, you can see the final result in action here:
All of the code is available on
GitHub here.
Comments