Building the Eject-A-Bed


Editor’s note:   Instead of usual content, we wanted to post something on creative ways our MVPs are using our technology.  Enjoy!

The following post was written by Visual F# MVP Jamie Dixon 

 

Building the Eject-A-Bed

My youngest child has a real hard time getting out of bed in the morning.  Instead of getting mad, I put my hacker hat on and got building.  I found a hospital inversion table on Craig’s list and put it in the garage.

 

My daughter, Sonoma, and I first wanted to understand how the bed 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 motor.  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 mechanics set up, it was time to code the controller.  I created a new Netduino project and added the following class-level variables and some methods to control the servo:

        private static OutputPort _led = null;

        private static InterruptPort _button = null;

        private static Socket _serverSocket = null;

        private static PWM _servo = null;

 

        private const uint SERVO_UP = 1250;

        private const uint SERVO_DOWN = 1750;

        private const uint SERVO_NEUTRAL = 1500;

 

        private static bool _servoReady = true;

 

        private 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;

            Thread.Sleep(2000);

        }

        private static void ActivateServoForBellows(String direction, Int32 duration)

        {

            if (direction == "UP")

            {

                _servo.Duration = SERVO_UP;

            }

            else if (direction == "DOWN")

            {

                _servo.Duration = SERVO_DOWN;

            }

 

            Thread.Sleep(duration);

            _servo.Duration = SERVO_NEUTRAL;

        }

 

If you are not familiar, servos are controlled by pulses of low voltage electricity.  The width of the pulse is interpreted by the servo and it moves correspondingly. In this case, a pulse width of 1.5 milliseconds will send the servo to neutral so the bellow is neither up nor down.  A pulse width of 1.25 millisconds moves the servo to the position where the bellow is being compressed, and a pulse of width of 1.75 milliseconds moves the servo to the position where the bellow is being expanded.  So the Netduino continuously sends a PWM signal of 1.5 milliseconds.  Once the length is changed to either 1.25 or 1.75, the servo moves and stays in that position (simulating a person pressing the button down for a period of time).  Note that “Duration” is the position of the servo, “duration” is how long the servo stays in its position.  This is what a PWM signal looks on an oscilliscope:

 

 

I then wired the Netduino to the servo.

 

 

The next step was to have the Netduino receive commands to move the servo.  I purchased an inexpensive portable router and plugged it into the Netduino.  I then coded up a series of methods that would set up the router and listen for requests.  If a valid request came in, the servo would move:

        private static void SetUpWebServer()

        {

            _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Any, 80);

            _serverSocket.Bind(ipEndpoint);

            _serverSocket.Listen(10);

            ListenForWebRequest();

        }

 

        public static void ListenForWebRequest()

        {

            while (true)

            {

                using (Socket clientSocket = _serverSocket.Accept())

                {

                    if (_servoReady)

                    {

                        _servoReady = false;

                        String request = GetRequestFromSocket(clientSocket);

                        Thread thread = new Thread(() => HandleWebRequest(request));

                        thread.Start();

                        SendActivateResponse(clientSocket);

                    }

                    else

                    {

                        SendBusyResponse(clientSocket);

                    }

                }

            }

        }

 

        private static string GetRequestFromSocket(Socket clientSocket)

        {

            int bytesReceived = clientSocket.Available;

            if (bytesReceived > 0)

            {

                byte[] buffer = new Byte[bytesReceived];

                int byteCount = clientSocket.Receive(buffer, bytesReceived, SocketFlags.None);

                return new String(Encoding.UTF8.GetChars(buffer));

            }

            return String.Empty;

        }

 

        private static void HandleWebRequest(String request)

        {

            RequestValues requestValues = GetRequestValuesFromWebRequest(request);

            Thread.Sleep(requestValues.Duration);

            if (requestValues.Duration > 0)

            {

                ActivateServoForBellows(requestValues.Direction, requestValues.Duration);

            }

            _servoReady = true;

        }

 

        public static RequestValues GetRequestValuesFromWebRequest(String request)

        {

            RequestValues requestValues = new RequestValues();

 

            if (request.Length > 0)

            {

                String[] chunkedRequest = request.Split('/');

                for (int i = 0; i < chunkedRequest.Length; i++)

                {

                    chunkedRequest[i] = chunkedRequest[i].Trim();

                    chunkedRequest[i] = chunkedRequest[i].ToUpper();

                }

                requestValues.Verb = chunkedRequest[0];

                requestValues.Direction = chunkedRequest[1];

 

                Int32 duration = 0;

                if (chunkedRequest[2] == "HTTP")

                {

                    duration = 15000;

                }

                else

                {

                    try

                    {

                        duration = Int32.Parse(chunkedRequest[2]);

                    }

                    catch (Exception)

                    {

                        try

                        {

                            String[] chunkedDuration = chunkedRequest[2].Split(' ');

                            duration = Int32.Parse(chunkedDuration[0]);

                        }

                        catch (Exception)

                        {

 

                        }

                    }

 

                }

                requestValues.Duration = duration;

            }

 

            return requestValues;

 

        }

 

        private static void SendActivateResponse(Socket clientSocket)

        {

            String response = "The Eject-A-Bed activated at " + DateTime.Now.ToString();

            String header = @"HTTP/1.0 200 OK\r\nContent-Type: text;charset=utf-8\r\nContent-Length: " +

                response.Length.ToString() + "\r\nConnection: close\r\n\r\n";

 

            clientSocket.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None);

            clientSocket.Send(Encoding.UTF8.GetBytes(response), response.Length, SocketFlags.None);

        }

 

        private static void SendBusyResponse(Socket clientSocket)

        {

            String response = "The Eject-A-Bed is busy at " + DateTime.Now.ToString();

            String header = @"HTTP/1.0 503 Service Unavailable\r\nContent-Type: text;charset=utf-8\r\nContent-Length: " +

                response.Length.ToString() + "\r\nConnection: close\r\n\r\n";

 

            clientSocket.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None);

            clientSocket.Send(Encoding.UTF8.GetBytes(response), response.Length, SocketFlags.None);

 

        }

 

And there is 1 data structure that is used to communicate the request

    public class RequestValues

    {

        public String Verb { get; set; }

        public String Direction { get; set; }

        public Int32 Duration { get; set; }

    }

So now when I send a browser request on my local Ethernet, we have a way of actuating the hospital bed screw drive via a wireless command:

 

 https://www.youtube.com/watch?v=bHZvxBTxPds&feature=player_embedded

 

The next step was no code but still lots of fun (anytime you can use air tools, it is fun).  We removed the screw drive from the hospital bed and attached it to my son’s real bed:

 

And then we can throw him out of bed via a simple http request from anywhere in the house:

https://www.youtube.com/watch?v=QJG2seTFcxQ

 

About the author

Jamie Dixon has been writing code for as long as he can remember and has been getting paid to do it since 1995.  He was using C#and javascript almost exclusively until discovering F# and now combines all three languages for the problem at hand.  He has a passion for discovering overlooked gems in data sets and merging software engineering techniques with scientific computing.  When he codes for fun, he spends his time using the .NET micro framework with Phidgets, Netduinos, and the Kinect.  Follow him on Twitter @jamie_dixon

About MVP Mondays

 

 

The MVP Monday Series is created by Melissa Travers. In this series we work to provide readers with a guest post from an MVP every Monday. Melissa is a Community Program Manager, formerly known as MVP Lead, for Messaging and Collaboration (Exchange, Lync, Office 365 and SharePoint) and Microsoft Dynamics in the US. She began her career at Microsoft as an Exchange Support Engineer and has been working with the technical community in some capacity for almost a decade. In her spare time she enjoys going to the gym, shopping for handbags, watching period and fantasy dramas, and spending time with her children and miniature Dachshund. Melissa lives in North Carolina and works out of the Microsoft Charlotte office.


 

 

Comments (1)

  1. NIce post thanks for the sharing with us …………..

Skip to main content