Nederlands | English

Mattress with motion

First idea: about the year 2000
Tried alternating pressure mattress: 2009 ... 2011
First version of my homemade mattress with motion: 2012



For many years I had the idea to keep my back and neck in motion during sleeping.
Because I only can lie on my back due to my neck problems, I lie too motionless during sleeping. So I wanted a mattress with active motion.
I talked about it with others and I even placed an advertisement in a local newspaper to ask others for help.
In the end, that didn't lead to any result.

I tried an alternating pressure mattress to keep my spine in motion while I am sleeping. That was not a success, because it was too much motion for my neck.
There are devices on the market with a vibrating motor for massage, but I want to keep my spine in motion with small and slow motions.

So I decided to make something myself.
I took me a lot of effort, and I often wandered if it would ever pay off. But only after a week with a first test, it turned out to have a positive effect.

A mechanical construction would become complicated. With air pockets and air pressure it would be easier and that is also safe.
During making my mattress with motion, I didn't know what the final result would look like. I thought that I would need somewhere between 2 to 40 air pockets, and I want to able to program each air pocket individually when they would inflate or deflate.

I tested a few pumps and a few solenoid valves. For my tests I used a flow meter, and a pressure sensor.
Flow meter
To measure the air flow of the pumps, I needed a air flow meter. I used the "balloon-in-the-bucket" flow meter.
I put an empty balloon in a bucket, en let the air flow inflate the balloon. As soon as the balloon is stuck in the bucket, I stop the stopwatch. The time to fill the balloon can be converted in liters per minute. The back pressure of the balloon was not that much (40 mbar).
homemade air flow meter

My measurements with the "balloon-in-the-bucket" method turned out to be not reliable.
So later I used an air compartment of non-elastic material (PVC). I first immersed it in water, to determine the contents. It turned out to be 2.4 liter. From then on, I just measured the time until is was completely filled. That greatly improved my measurements.

There are also air flow meters, which are used in cars. They are quite expensive, but also used ones are available. I didn't buy one, because measuring the air flow wat not that important anyway.

Pressure sensor
To measure the pressure, I would need a pressure meter that is able to measure both positive pressure and negative pressure (vacuum). The MPXH6400A sensor could do that. It works at 5V and measures the absolute pressure. It is therefor also a barometer. The output is an analog signal. It can measure from -0.8 bar to 3 bar.
absolute pressure sensor

After some tests I found out that some aquarium pumps almost supply enough pressure.
I choose a good aquarium pump, those are mechanical and electrical of much better quality than the cheap ones.
aquarium pump aquarium pump with sealant aquarium pump opened aquarium pump opened aquarium pump open aquarium pump with inlet and sealed aquarium pump cascade air filter
To get more pressure, I used two aquarium pumps to double the pressure.
Good aquarium pumps are silent and they last for many years. That is exactly what I needed.
Some aquarium pumps were able to deliver 0.2 bar pressure. By cascading them I had 0.4 bar pressure, which turned out to be well enough.
To cascade two pumps, I had to make an air inlet in the pumps. I made the inlet in the case, and I sealed the case with sealant.
Because I made an inlet, I now have to use a air filter to keep the pumps clean.

Because I used my mattress with layers, I could place the air pockets between the layers of my mattress.
I used bicycle inner tubes as air pockets.
bicycle inner tubes as air pockets bicycle inner tubes as air pockets
The rubber of the bicycle inner tubes is actually too thick, and this influences the mattress. Something better for the air pockets is hard to find.

I was able to buy used solenoid valves which were ideal for the low pressure and low airflow that I use.
solenoid valves solenoid valve opened
Fourteen valves fitted in the box that I bought for the control unit.
solenoid valves solenoid valves valves and control unit valves and control unit outside connectors to air pockets

The box was marked on the bottom as "PP-05". This means that it is made from polypropylene. If I would drill holes in, it would crack and break. So I made a drill bit hot over a flame and used it to make holes for the connectors for the tubes.

To be able to inflate or deflate all valves simultaneously, I would need two main lines: one line for a positive pressure and a line for negative pressure (vacuum). But then two valves would be needed for each air compartment, to be able to choose between the two main lines. That makes everything more complicated.
I want at least to use the pump to make the air compartments completely empty, so no air is left behind in the air compartments.
So I chose to use just one valve per air compartment, and they all are connected to one main line. But then I needed 4 valves to be able to use the pump to inflate or deflate the air compartments.
Perhaps less valves is possible, but to me it seems better not to let an reverse airflow go through the air filter.

The valves are suitable for an air flow in only one direction. The inlet of the valve can withstand more pressure, and leak the least. When connecting the valves, I didn't care much about the direction of the airflow through the valves, but more on which side of the valve can withstand air pressure.

This is how the valves are connected (click on the image for a larger version):
The plus-sign indicates the side of the valves that can withstand pressure.
pneumatic connections

To control everything, I needed something that I could program.
The best choice is an AVR micro-controller, since there are many examples on the internet and many people are willing to help.
The Arduino is meant for such things, but both the hardware and software was too complicated for me.
When I had finished the mattress with motion, I did understood the Arduino. I used that knowledge for my footswitches with the Arduino Leonardo.
The Raspberry Pi was not available by then.
It was my job to write software to be used in devices. But due to my neck problems it took me a lot more effort to do such things. What I used to do in one day, takes me more than a week (and only if I was fit enough for it).
Therefor I started with a ATtiny13 microprocessor. Eventually I was able to make a blinking led with it. From that point I could go on.

The control unit has to be as simple as possible to make. I came up with the next design (click on the image for a larger version):
circuit diagram scheme

I choose these parts:
• ATmega88
The ATmega88 is a newer version of the ATmega8. It's got enough pins to control my 14 valves. It has 8kbyte memory, which should be well enough.
I used a ATmega88PA, which has an internal temperature sensor.
• ULN2803A
To control the valves, I could use transistors (for example BC639) or "logic level" MOSFETs. The ULN2803A however, makes things a lot easier. The valves are 100mA, which is well within the specifications of the ULN2803A.
No extra components are needed to keep the valves off during power-up.
• 7805
The 5V voltage regulator is without cooling, because only little current is used.
• Signal LED and light sensor
A LED can be used as a light sensor. That's why the LED (with a resistor) is between two pins of the ATmega88.
• USBasp
To program it, I use a USBasp connection. A USBasp programmer costs only 3 euros.

As shown in the schematic, there are two types of ground. That is because I want to have the possibility to connect an analog sensor, and I don't want the current through the valves to influence the analog section. Therefore I separated the grounds. They are connected at the 7805 voltage regulator.

This is such a USBasp programmer:
USBasp programmer

I did not manage to create a PCB design with a program. So I soldered everything with wires.
I had to do small steps over a few weeks of time to be able to finish it.
If I ever want more air pockets, then I will have to let a PCB be made for me.

circuit board pcb board circuit board soldering side

Below is the source code ("mattress.c"):

// ------------------------------------------------
//
//  "Mattress with Motion"
//
//  File    : mattress.c
//  Version : February 2012
//
//  Using the ATmega88PA with ULN2803A drivers.
//  A led is connected between two pins,
//  to be able to use it also as a light sensor.
//  A switch is in the hardware, but not used yet.
//
//  With avr-gcc 4.3.5 in Linux Mint 11 and avrdude 5.10.
//

// The ATmega88PA runs default at 1MHz,
// but can be set to 8MHz with a fuse.
#define F_CPU 1000000UL

#include <avr/io.h>
#include <stdbool.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/pgmspace.h>


// To set or clear a bit in a register, I use my own macro's.
#define BIT_SET(port,bit) port |= _BV(bit)
#define BIT_CLEAR(port,bit) port &= ~_BV(bit)
#define BIT_TEST(port,bit) (port & _BV(bit))
#define BIT_INVERT(port,bit) port ^= _BV(bit)

#define TRUE true
#define FALSE false


// Hardware specific definitions
#define SWITCH PB3
#define LED PB4
#define LED_GROUND PC3

// There are 14 valves.
// One output is used to switch the pump on and off.
// So one output is not used and is a spare.
// The wires of the valves are soldered without checking the output number.
// How the valves and air pockets are connected to the outputs is solved by software.

// The next definitions define all the valves and the pump.
// The order is not hardware related.
// The order is only for an index of the valve[][2] array.
enum output_names
{
  VALVE_1 = 0,
  VALVE_2,
  VALVE_3,
  VALVE_4,
  VALVE_5,
  VALVE_6,
  VALVE_7,
  VALVE_8,
  VALVE_9,
  VALVE_10,
  VALVE_OUTLET,
  VALVE_HIGH,
  VALVE_INLET,
  VALVE_LOW,
  PUMP,
  SPARE,
};


// The next definitions define how the air pockets are connected the valves.
// Most of the time, the first pocket is connected to the first valve, and so on.
enum air_pockets
{
  POCKET_1 = VALVE_1,
  POCKET_2 = VALVE_2,
  POCKET_3 = VALVE_3,
  POCKET_4 = VALVE_4,
  POCKET_5 = VALVE_5,
  POCKET_6 = VALVE_6,
  POCKET_7 = VALVE_7,
  POCKET_8 = VALVE_8,
  POCKET_9 = VALVE_9,
  POCKET_10 = VALVE_10,
};


// PORTB can not be used in a constant, because PORTB is a define with calculation.
// So define it with normal values to indicate the port.
enum port_defines
{
  _PORT_A,
  _PORT_B,
  _PORT_C,
  _PORT_D,
};


// Connections of valves.
// The next constant array is located in program code with 'PROGMEM'.
// It translates the definitions for the valves to the hardware connections.
// Both the order and the values should match the hardware.
const unsigned char valve[][2] PROGMEM =
{
  {_PORT_B, PB0},     //  VALVE_1
  {_PORT_B, PB1},     //  VALVE_2
  {_PORT_B, PB2},     //  VALVE_3
  {_PORT_B, PB6},     //  VALVE_4
  {_PORT_B, PB7},     //  VALVE_5
  {_PORT_C, PC0},     //  VALVE_6
  {_PORT_C, PC1},     //  VALVE_7
  {_PORT_C, PC2},     //  VALVE_8
  {_PORT_D, PD0},     //  VALVE_9
  {_PORT_D, PD1},     //  VALVE_10
  {_PORT_D, PD2},     //  VALVE_OUTLET
  {_PORT_D, PD3},     //  VALVE_HIGH
  {_PORT_D, PD4},     //  VALVE_INLET
  {_PORT_D, PD5},     //  VALVE_LOW
  {_PORT_D, PD6},     //  PUMP
  {_PORT_D, PD7},     //  SPARE
};


// defines for valves and pump.
#define OPEN true
#define CLOSE false
#define ON true
#define OFF false


// ###########################################################
// Delay functions
// 
// The library functions _delay_ms and _delay_us
// can only delay for about 20 ... 200.
// The actual maximum depends on the frequency of the chip.
// So a workaround should always be added.
// The slight overhead adds some extra delay.

void delay_ms (uint16_t milliseconds)
{
  while ( milliseconds )
  {
    _delay_ms (1);
    milliseconds--;
  }
}

void delay_s (uint16_t seconds)
{
  while (seconds)
  {
    delay_ms(1000);
    seconds--;
  }
}


// ###########################################################
// Function Valve
//
// This function translates the requested valve to the hardware output.
// A valve is closed (or off) if not activated.
// Turning it 'ON' or 'OPEN' activates the output and activates the valve.
// This function can be used with either VALVE_1, or PUMP, and also with POCKET_1, etc.
// The second parameter can be TRUE, FALSE, ON, OFF, OPEN, CLOSE.

void Valve (uint8_t byteValve, uint8_t byteOpen)
{
  uint8_t portdef, pin;
  volatile uint8_t *pPort=NULL;

  portdef = pgm_read_byte (&valve[byteValve][0]);
  pin     = pgm_read_byte (&valve[byteValve][1]);

  // Translate the definition for the port to the actual io-port.
  // Since 'PORTB' is the actual port in the io-register area,
  // using a pointer seems to be the best way to do this.
  switch (portdef)
  {
  case _PORT_B:
    pPort = &PORTB;
    break;
  case _PORT_C:
    pPort = &PORTC;
    break;
  case _PORT_D:
    pPort = &PORTD;
    break;
  }

  if (pPort != NULL)
  {
    // Set the valve (or pump).
    if (byteOpen)
    {
      BIT_SET (*pPort, pin);
    }
    else
    {
      BIT_CLEAR (*pPort, pin);
    }
  }
}


// ###########################################################
// Function SlowWave_Next
//
// A sequence for the air pockets.
// This sequence is a slow wave.
// A static variable is used to remember the previous air pocket.
// This way some of the pressurized air can be used for the next air pocket.
//
// This function assumes that all valves and the pump are off.

void SlowWave_Next (uint8_t nNewPocket)
{
  static uint8_t nOldPocket = POCKET_1;

  // Let the pressure flow to new air pocket.
  Valve (nOldPocket, OPEN);
  Valve (nNewPocket, OPEN);
  delay_s (10);

  // let first one deflate.
  Valve (nNewPocket, CLOSE);
  Valve (VALVE_HIGH, OPEN);
  Valve (VALVE_OUTLET, OPEN);
  delay_s (10);
  Valve (VALVE_HIGH, CLOSE);
  Valve (VALVE_OUTLET, CLOSE);
  Valve (nOldPocket, CLOSE);

  // Pressurize second one further.
  Valve (VALVE_HIGH, OPEN);
  Valve (VALVE_INLET, OPEN);
  Valve (PUMP, ON);
  Valve (nNewPocket, OPEN);
  delay_s (50);

  // Deflate first one further, using the pump.
  Valve (nNewPocket, CLOSE);
  Valve (VALVE_HIGH, CLOSE);
  Valve (VALVE_INLET, CLOSE);
  Valve (nOldPocket, OPEN);
  Valve (VALVE_LOW, OPEN);
  Valve (VALVE_OUTLET, OPEN);
  delay_s (10);

  // Hold pressure in the second one.
  Valve (PUMP, OFF);
  Valve (VALVE_LOW, CLOSE);
  Valve (VALVE_OUTLET, CLOSE);
  Valve (nOldPocket, CLOSE);
  delay_s (40);

  nOldPocket = nNewPocket;
}


// ###########################################################
// main

int main(void)
{
  uint8_t i;


  // The next code is very hardware specific.
  //    The code could be like this:
  //      PORTB = 0xC7;
  //    Or the definitions of valve[][] could be used,
  //    but that would add extra program code.
  DDRB = 0xC7;                        // Select pins for output
  DDRC = 0x07;                        // Select pins for output
  DDRD = 0xFF;                        // Select pins for output

  // The next code is normal code, for the led and switch.
  BIT_SET (DDRB, LED);                // make it output
  BIT_SET (DDRC, LED_GROUND);         // make it output
  BIT_CLEAR (DDRB, SWITCH);           // make it input.
  BIT_SET (PORTB, SWITCH);            // activate internal pull-up resistor

  BIT_CLEAR (PORTC, LED_GROUND);      // make ground for led a '0'.

  // The best sequence for the air pockets is just a wild guess.
  // At this moment there is only one sequence: a slow wave.
  // This slow wave is done with the first 5 air pockets.

  while (1)
  {
    for (i = POCKET_1; i <= POCKET_5; i++)
    {
      SlowWave_Next (i);
    }
  }
}

This is the script to compile, link and programming the firmware into the avr chip ("go.sh"):

# ------------------------------------------------
#
#  "Mattress with Motion"
#
#  File    : go.sh
#  Version : February 2012
#
#
#  With avr-gcc 4.3.5 in Linux Mint 11 and avrdude 5.10.
#

avr-gcc -Wall -Os -mmcu=atmega88pa -c mattress.c
avr-gcc -mmcu=atmega88pa -o mattress.elf mattress.o
avr-objcopy -j .text -j .data -O ihex mattress.elf mattress.hex

# The ATmega88PA is not yet in the database of avrdude 5.10
# So use the ATmega88, with the (dangerous) flag '-F'.
# The new avrdude 5.11 does support the atmega88p.

sudo avrdude -c usbasp -p atmega88 -F -U flash:w:mattress.hex

# Show memory usage
avr-size -C --mcu=atmega88pa mattress.elf

Download all files: Mattress-with-Motion.zip.

The sequence for the air compartments to inflate and deflate is now programmable.
Perhaps I will make a number of sequences, and perhaps I will change between them. Maybe a random sequence is also something to try.
By using my trolley-computer I can program the sequence while I am lying in bed, and I can try it right away.

In 2012 I made already some changes.
I tried a number of different air compartments, but they were not strong enough. So eventually I used ten bicycle tires. The program was changed to run the sequence faster, because with ten bicycle tires a cycle lasted too long.
I added a pressure sensor, so the bike tires were inflated and deflated to a certain pressure. I placed the pressure sensor in the "Main Line", that is the central point where all the valves to the air compartments are connected.
The air pumps made only little noise but they did vibrate, that made the cover of the box in which it was to vibrate and making noise. So I replaced the air pumps with 12V air pumps. Those could build up sufficient pressure (700 mbar), but gave too little air. So I used two of them parallel.

The two air pumps didn't have a connection for an air inlet. Therefore I placed the air pumps in a box, and an opening in the box would be the air inlet. I could not find a plastic box with the right dimensions, therefore I made a box of foam rubber (EVA foam) and I used ms-polymer sealant/kit to seal it. When the air pumps empty an air compartment with (a slight) vacuum, the walls are of that box are pulled hollow. But it seems to be going well and after a year it still works.
air pumps in box

In 2013, the majority still worked well, but the air pumps were not able to build up a lot of pressure and the valves leaked. That was not a problem yet. I use only 130 mbar for a fully inflated air compartment and a pressure of -25 mbar to detect that it is empty.

Rights: The movie about my "Mattress with Motion"
and the photos and drawings and software code
on this page are made by myself
and are Public Domain, unless otherwise noted.
Last change to this page: April 2013