Alarm

Category : personal


Source Code

You can find the entire source code (minus API keys) for this project on my Github

Hardware

I’d describe myself as an evening person. Which generally means I’m not very good at waking up in the morning. Normal alarms are alright, but I just kept snoozing until the very last moment, at which point I always had to hurry to get to work on time. I also didn’t want to use my phone, because I don’t like taking my phone with me when I go to bed. I needed a better solution. So I decided to build my own alarm. One that is unable to snooze, gets louder the more time passes, and that won’t turn off until I’m physically out of bed.

Luckily, I had a Raspberry Pi and some hardware lying around that I could use. The Raspberry Pi runs Windows 10 for IoT, and is hooked up to the internet and a small speaker, to be able to use internet radio as the alarm.

To turn the alarm off, I have to physically get out of bed and open the bedroom door. For that I used a contact sensor on the door, that detects whether the door is open or closed, and turns the alarm off accordingly.

I also wanted to be able to tell the time, so I hooked up a small number segmented display as well. The way those work is quite interesting. Instead of simply turning individual line segments on and off, and only updating them when the number changes, you need to continously loop through the four adjecent numbers, use a bit-flag to enable the correct segments for 5 milliseconds, before turning it off again and moving to the next number. This means that, even though the numbers appear to be ‘on’ constantly, they are actually flickering really fast, with only one single number being ‘on’ simultaneously. It just flickers so fast that it appears to be constant.

What I noticed, however, was that the Raspberry Pi wasn’t looping through the numbers fast enough, because the update loop kept getting interrupted by the Operating System. Normal apps simply around allowed to use 100% of processing power all the time. To fix this, I hooked up an Arduino to my Raspberry Pi. An Arduino doesn’t have an OS, so it runs your code all the time, meaning it ìs actually fast enough to control the number display. So I opened a Serial Connection from the Raspberry Pi to the Arduino, to send the current time to be displayed to the Arduino whenever another minute passes, and all the Arduino does is continously loop through the numbers to display the time.

void showValues(int* values)
{
  for(int i = 0; i < numberOfValues; ++i)
  {
    showValue(values[i], i);
  }
}

void showValue(int value, int index)
{
  digitalWrite(valuePins[index], HIGH);
  for(int segment = 0; segment < numberOfSegments; ++segment)
  {
    boolean isBitSet = bitRead(value, segment);
    isBitSet = !isBitSet;
    digitalWrite(segmentPins[segment], isBitSet);
  }
  delay(5);
  digitalWrite(valuePins[index], LOW);
}

Philips Hue

At some point, I decided to get myself some Philips Hue lights. The reason I went with Philips Hue, is because they have an excellent API I can use. So I installed one of these lights in my bedroom, and hooked it up to the alarm. Now if it takes me longer than 9 minutes to get out of bed, the light will also turn on automatically and keep getting brighter, preparing me for the daylight.

public async void StartWakeUp()
{
    wakeupCancellationTokenSource = new CancellationTokenSource();

    try
    {
        await Task.Delay(TimeSpan.FromMinutes(9.0), wakeupCancellationTokenSource.Token);
    }
    catch (TaskCanceledException)
    {
        return;
    }

    byte brightness = 0;

    var action = new Action { on = true, bri = brightness, ct = 250 };

    while (brightness < 64)
    {
        action.bri = ++brightness;

        using (var client = new HttpClient())
        {
            var response = await client.PutAsync(new Uri($"{HUE_API}/groups/{bedroomID}/action"), new HttpStringContent(JsonConvert.SerializeObject(action)));
        }

        if (wakeupCancellationTokenSource == null) break;

        try
        {
            wakeupCancellationTokenSource = new CancellationTokenSource();

            await Task.Delay(TimeSpan.FromSeconds(10.0), wakeupCancellationTokenSource.Token);
        }
        catch (TaskCanceledException)
        {
            return;
        }
    }
}

Since I was detecting whether the door was open or not anyway, I also added a feature to make myself more lazy. I made it so the light automatically turns on when you open the bedroom door, and turns off when you close it again. The brightness and warmth of the light also changes depending on the time of day, with it being quite bright and near-white during the day, and a less bright, warm orange light during the evening, which is especially nice just before going to sleep.

Ambience

My girlfriend likes to have some ambience noises on in the background when we go to sleep, so I added support for that as well. It takes a YouTube url, downloads the HTML page through a proxy and scans it for an audio-stream url, and uses that to play just the audio from the YouTube video.

private async Task<string> GetAudioStreamURL(string videoUrl)
{
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Referrer = new Uri("http://localhost");
        var result = await client.GetAsync($"https://images{~~random.Next(0, 33)}-focus-opensocial.googleusercontent.com/gadgets/proxy?container=none&url={HttpUtility.UrlEncode($"https://www.youtube.com/watch?hl=en&v={videoUrl}")}");
        if(result.IsSuccessStatusCode)
        {
            var response = await result.Content.ReadAsStringAsync();
            response = response.Split("window.getPageData")[0].Replace("ytInitialPlayerResponse = null", "").Replace("ytInitialPlayerResponse=window.ytInitialPlayerResponse", "").Replace("ytplayer.config={args:{raw_player_response:ytInitialPlayerResponse}};", "");

            var regex = new Regex("(?:ytplayer\\.config\\s*=\\s*|ytInitialPlayerResponse\\s?=\\s?)(.+?)(?:;var|;\\(function|\\)?;\\s*if|;\\s*if|;\\s*ytplayer\\.|;\\s*<\\/script)", RegexOptions.ECMAScript | RegexOptions.Multiline);
            var matches = regex.Matches(response);
            if(matches.Count > 0)
            {
                var data = matches[0].Value.Replace("ytInitialPlayerResponse = ", "").Replace(";</script", "");

                var streamingData = JsonConvert.DeserializeObject<YoutubeResponse>(data).streamingData;
                var streams = new List<YoutubeResponse.StreamingData.Format>();

                if(streamingData.adaptiveFormats != null)
                {
                    streams.AddRange(streamingData.adaptiveFormats);
                }
                if(streamingData.formats != null)
                {
                    streams.AddRange(streamingData.formats);
                }

                var format = streams.FirstOrDefault(s => s.itag == 141) ?? streams.FirstOrDefault(s => s.itag == 140) ?? streams.FirstOrDefault(s => s.itag == 139);
                if(format != null)
                {
                    return format.url;
                }
            }
        }
    }

    return "";
}

Companion App

Now to control all this, I built a simple companion app in Xamarin for my phone. It allows me to set a time for the alarm, and to start and stop the ambience player. It does this by calling a simple API function hosted as an Azure Function, which in turn forwards my request to the Raspberry Pi, which is connected to an Azure IoT Hub.

private async void SetAlarm_Clicked(object sender, EventArgs e)
{
    using(HttpClient client = new HttpClient())
    {
        var data = new { CMD = "SetAlarm", AlarmTime = AlarmTimePicker.Time };
        string body = JsonConvert.SerializeObject(data);

        var result = await client.PostAsync(API_URL, new StringContent(body));
        if(result.IsSuccessStatusCode)
        {
            DependencyService.Get<IToast>().ShowToast($"Alarm set for {AlarmTimePicker.Time}");
        }
        else
        {
            DependencyService.Get<IToast>().ShowToast("Failed to set alarm");
        }
    }
}

Background

As a final addition, the background of the companion app is a photo of my dishwasher, to remind me to turn it on before going to bed.


About Vincent Booman

Hi, my name is Vincent Booman. For me, development is not about a specific language or software, it's about what kind of impact they enable.

Follow @Feathora