Code Monkey home page Code Monkey logo

pi-pico-alarmclock's Introduction

pi-pico-alarmclock

Building a working alarmclock based on a Raspberry Pi Pico W

The general idea is to build a device that can reliably wake up my kids who are in an age range, where this is a challenge. So it should sport these features:

  • be somewhat Star wars - themed (not my idea)
  • have a display that shows the time and some other information
  • have a speaker that can play music or sounds, and loudly so
    • have a way to hold music/sound files for that purpose
  • have a thingy that can turn on a bright light, ideally an array of RGB LEDs or some such. Because "when my room is still dark, I cannot properly wake up"
  • have some buttons to set the alarm time and to toggle the alarm
    • without making it too easy to silence the actual alarm when it is going off
  • have the ability to both run on battery power or be powered by a USB charger
  • be built into some enclosure that looks nice, is easy enough to print and assemble and can somehow house the components in a sensible way
  • be fun for daddy to build and program

State of affairs: Wiring and coding is done. There is one issue left to fix (Sound is unreliable, will not turn on as expected 50% of the time). So this is next: Figure out this bug and then off to designing&printing a case.

Disclaimer

This project is one of my first projects using MicroPython and electronics. I have some background in software development with niche/proprietary languages and no background at all in electronics and Python. So most of this I figured out as I went. That being said: This is going to be full of imperfections. Use any of this at Your own risk and in case You spot my mistakes, I am glad to hear of them! Besides that I am happy if this is useful or fun for somebody.

Description

  • The display shows the current time. A lightsaber-symbol indicates, if the alarm is active or not. A battery indicator shows the battery charge or indicated the device is currently usb-powered.
  • To get the current time, on startup and every 6 hours subsequently the device will connect to Wifi, get the time from a timeserver and update the RTC with that.
  • Button-driven operations:
    • The green button will toggle the alarm active/inactive, when in normal operation mode
    • The yellow button will toggle system info being displayed or the normal time mode being displayed
    • The blue button will enter the mode for setting the alarm time. The current alarm time will be displayed (blinking) and the top area will display a symbol indicating that we are doing some sort of setup.
      • In this mode the green button will increase the hour digits and the yellow button will increase the minutes digits. Pressing blue again will save the new alarm time and resume normal operation.
  • Analog clock: The NeoPixel will show an approximation of an analog clock when in normal operation. The blue light indicates seconds, the green light indicates minutes and the red one indicates hours. When hands meet, their color is mixed. (I know... but I had way too much fun)
  • Alarm:
    • 5 Minutes before the alarm time the NeoPixel will turn off the analog clock and simulate a sunrise, going from few and red lights to more and warm-white lights.
    • At the same time in the indicator area of the display we will show a random color as text, using the button colors. Pressing the appropriate button will proceed to show the next color, until one has pressed all three buttons in the order the device wanted them. Completing the sequence will abort the alarm at any stage. The assumption is: If You are awake enough to have found and pressed the buttons in the random order that You were presented, You should be awake enough to get out of bed.
    • At the actual alarm time the sunrise will becompleted and the NeoPixel will display some fancy lighshow-things. At the same time the Music starts tom play, in my case here I chose the Imperial March, because that is what the kids love.

Prototype on breadboard

Code

The code project is structured into these main parts: main.py, classes, and drivers as well as settings and media.

part contents
main.py Main script, doing very little besides instantiating.
drivers Drivers and helpers for specific components. I have written none of these myself, each file has a comment saying where I found it.
classes Classes for each logical component used in this project.
settings A few json files to remember settings and magic strings.

One file in settings is not checked into this repository for obvious reasons. Add one wifi.json to settings, structured like this:

{
    "ssid": "YOUR SSID",
    "password": "YOUR PASSWORD"
}

Testing and Tests

After a while this projects code got quite messy and I spent a fair amount of time restructuring it into classes with a small brother of dependency injection. This not only helped me to escape dependency hell but also allows for easy mocking and testing in isolation. I did write some tests here and there, typically where I ran into some problem that i was too lazy to iron out by manually testing the full application over and over again. None of this is perfect, but it sure was fun to do.

What I still sorely miss is a way to debug MicroPython Code. Writing anything remotely complex without is no fun.

media

The folder contains images used for displaying things on the OLED display. The OLED driver provides only very limited support for text, there is no way i found to change font, font size and also (not an issue here) we cannot display all German letters. So if one wants some nicer numbers, some nice state indicators and the like one has to use images. I have used GIMP to create these images. I have used the Export as... function to export them as 'pbm' files. They have to be converted to indexed color and when exporting to pbm they have to be exported as 'raw' format. The OLED driver can then read these files and display them on the screen.

A really good tutorial on how to create these images can be found here. I would not have figured this out without.

Components and Wiring

The central part of this project is the Raspberry Pi Pico W. I am sure everything could be done with a different microcontroller, as long as MicroPython is supported. I chose the Pico W because... I had one.

Components

Component Description
Microcontroller Raspberry Pi Pico W
OLED Display SSD1306 compatible I²C OLED Display 128*64 pixels with two color yellow/blue. Input Voltage 3.3V - 5V, but must support 3.3V because that is what we are using. Single-color will work just as well, since the colors are actually fixed to sections of the display and cannot be coded.
battery I use a 18650 Lithium-Polymer battery with 3350mAh. Anything else will work, als long as it fits with the charger module, battery holder and puts out no more than 5V, since You will be plugging it into Pi Pico and the step-up-converter.
battery holder really anything will do, that can hold one Lithium-Polymer unit. Consider adding a switch between battery and charger or be as lazy as me and take out the battery whenever You want to truly power down.
charger module I used a TC4056A module, capable of drawing power from Micro-USB or input pads and capable to charge/discharge the above battery safely.
NeoPixel ring I used WS2812B with 16 RGB LED on it. If You want more LED make sure to check if You can power them safely. Each LED will draw ~60mA and that quickly exceeds either the 300mA the Pico can supply directly or what the step-up-converter attached to the USB or battery power supplies can deliver. In my case here the step-up-converter is rated for 1000mA and so this should be okay.
step-up converter A step up capable to convert 2.5V - 5V to steady 5V. I used BS01 (sometimes found as MT-3608 or HW-085). Required to power the NeoPixel-Ring. There are a ton of these things available everywhere, make sure it is DC-DC and can put out 1000mA for 5V and can convert to that from whatever You expect to come out of Your power supply.
speaker I have used DFplayer Mini 3 Watt 8 Ohm speaker, 703015mm. They can be found in some flavors from multiple vendors. Depending on the form factor You may need to adjust the case, but I guess that cannot be helped since there are so many different of these speakers around.
p-channel MOSFET Two used to switch power modules. I have used IRF9540 which is overkill and and more efficient options exist. These things are rated for up to 100V and the switching voltage is uncomfortably close to USB typical ~5V. So, maybe they now have a higher voltage drop from source to drain, than I would like.
n-channel MOSFET One used to control power supply to mp3 module. I have used IRLZ44N.
Schottky diode One used to prevent power from flowing back into Pi Picos VBUS when powering from battery. Anything rated for up to 5V will do.
mp3 module I have used DFR0299 (DFPlayer), which was easy to wire. It has a micro-sd card slot and an internal amplifier, greatly reducing overall complexity compared to other solutions I found. Also unfortunately my last struggle left... not yet reliable.
micro sd card Really, anything You have lying around, formatted to FAT32.
push button Three used. 13mm diameter, 8mm hight caps on 12x12x7.3mm button - these should be fairly standard. One caps each in yellow, green and blue.

Wiring

Here is my best attempt at a wiring diagram. I have used Circuit Diagram to create it.

Alarm Clock Circuit Power Bypassing Pico

A few things to note here:

  • The buttons are debounced in code, so they have no debouncing circuits.
  • The mp3-module does not normally require a switchable power supply. In my case here I struggled with the DFPlayer I bought, it just would not work anymore after a certain time of idling. The same problem could be replicated by sending it into standby. Not all drivers I found even have the wake-up command implemented and even the ones I tested with never seemed to wake the device up again. At the same time the mp3-module is the most unused one in this entire application, but draws 20mA regardless of whether it does anything or not. So: I decided to power it up and down as needed. To achieve this I used an n-channel-MOSFET. The gate is connected to a GPIO and source to drain sit between the power and the device. That way I can pull the GPIO high to allow the mp3-module to be powered and pull it back low as soon as I do not need it any more. Solves my standby issue and enhances battery life.
  • The power circuit is strongly inspired by the Raspberry Pi Pico documentation. I use one p-channel-MOSFET to cut power from the battery supply, as long as VBUS is powered externally. This is achieved by running a wire from VBUS to the MOSFET gate (VBUS is directly connected to the Micro-USB of the Pico). The second p-channel-MOSFET is used in the same fashion to cut power from the battery to the NeoPixel, as long as VBUS is powered.
  • The NeoPixel needs 5V and so is either supplied from VBUS or from the battery via the step-up-converter. The wire from VBUS to the step-up-converter has a Schottky-diode to prevent power from backfeeding into VBUS when powered from the battery.

Enclosure and Assembly

See enclosure for information on hoe to print and assemble.

Power Consumption

These are the assumptions I made regarding power consumption. I did not measure any of these but researched online to arrive at some idea. In general my calculation based on these assumptions matches what I can observe.

Component Current (mA) Current (Ah)
Pi Pico 40.00 0.04
OLED 11.00 0.011
NeoPixel 18.00 0.018
all Wires, Diodes, MOSFET 8.00 0.0080
Step-up-converter loss 4.30 0.0043

The DRF0299 mp3-module is powered down the entire time, until it needs to play the alarm sound. After that it is powered down again. I am not inclufing it in the above table. In the specs it is rated for 20mA when powered.

The NeoPixel is only active when the alarm is not active and of course during alarm. I put it into the table, as a worst-case scenario where it is never switched off.

The hardest part to put a number on is the Pi Pico itself. Online sources say that under full load it consumes a fraction under .1Ah but then I do not put it under very much load here. On the other hand we also never not use the Pico, there is at least one timer firing every 3.7s to drive the analog clock. I am also still unsure if the main loop iterating over sleepn and then idle is really the best way to keep the application alive, power-wise. Anyway, I guessed .04Ah for constantly doing stuff but not doing very heavy calculations.

For the OLED I found sources where people measured power consumption of similar devices and from one diagram I found I took the .011Ah. For the NeoPixel I found in the specs that one uses 60mA on full power. The analog clock running most of the time has three LEDs on at 10% brightness, so that should be .018Ah. For all the wires and MOSFET and the diode I basically asked ChatGPT for typical loss and got a fairly believable answer, broken down into a number of assumptions. Best I have, so that would be .0085Ah. The step-up converter actually has an efficiency in the specs and that could be used to calculate the power loss with some accuracy, but in my case here the step-up will either be used to convert from ~5V to 5 or if the battery is powering the battery will be at a voltage somewhere between 2.5V and 4V... so the conversion required will differ all the time. So I assume we will be on 3.5V input on average and to be safe I assume on average 90% efficiency and that ends us at .0043Ah.

So here is what I did to reduce power consumption:

  • Pico: The microcontroller is the most power-hungry component by a long shot. The only reason we need it doing things frequently is the seconds hand on the analog clock. I reduced clock speed to 80Mhz in normal operation, when the NeoPixel is not needed. Unfortunately one has to use the full frequency if the NeoPixel is used, because it is driven by state machines and they in turn cycle with the clock -> anything else than 125Mhz throws the timing off balance and the Neopixel will show weird things. I tried to go lower than 80Mhz, but then the OLED Display starts acting strangely.
  • OLED: Is not needed at full brightness and should use less power when dimmed. For now I have set contrast to 10 (maximum is 255), that is bright enough.
  • I managed to cut the power to DFPlayer while not used - actually when troubleshooting it not returning from sleep and requiring a fair bit of additional wiring.
  • I managed to cut power to Wifi while not used - also actually when trobleshooting it not being capable of reconnecting properly.

So... before all that I observed ~36h of battery life. I am curious if I now can do better.

pi-pico-alarmclock's People

Contributors

1-rafael-1 avatar

Stargazers

 avatar Markus Hedenborn avatar

Watchers

 avatar

Forkers

vtt-info

pi-pico-alarmclock's Issues

introduce option to set clock manually if wifi not accessible

Right now there is no way to use the device, if it boots to not finding wifi

Optimum solution:

Booting, attempt to connect wifi, on failure: Interrupt booting, display the time blinking, let the user set hours and minurtes and confirm with blue button, continue booting.

a better way to organize and run tests

tests are great and good, but mine here rot -> every time i change stuff i have no convenient way to run all the tests. So I don't.
Tht way i never catch the full impact of what I am doing and also at no point will all tests pass.
Not ideal, there must be a smarter way to do this.

analog clock is too distracting at night

the NeoPuxel is too bright even at its lowest possible intensity and the constant movement of the hands is distracting.
-> The analog clock should only show, when the alarm is not active. That way we can still have the nice eyecatcher, but when actually using the alarm clock at night it will not be disturbing.

analog clock unexpectedly active after quitting alarm sequence

expected: active alarm = no analog clock
observed: as above, but after alarm sequence -> analog clock active

probably just one line in the code for exiting alarm, but that also means the structure is not ideal, find a better way to re-initialize things after alarm

battery charge state is imprecise

The battery charge lower bound should be 2.5V by the charger module specs, but the device powers down before that, so maybe either the calculation or the charger or whatever behave unexpectedly.

alarm sound is unreliable

On a freshly started system, the alarm sound can be played from the class SoundManager. I have also had successful runs with the alarm sound playing in an actual alarm sequence.

Most of the time, though, the DFPlayer appears unresponsive to the commands sent to it. This is hard to test and pinpoint, but ist seems to me the device is not initialized and handled the way it should be.

Things to check:

  • no reliable repro yet: isolate the cirsumstance, in which the module stops responding as expected
  • unconfirmed assumpotion: when testing I usually reset the code execution, but do not cut power. Maybe the device remains active and then does not like being re-initialized. That would fit general documentation on UART devices in the MicroPython Docs -> find a way to deinitialize properly; Test if cutting power will reliably make the device respond again.
  • unconfirmed assumption 2: taking the SD Card out of the device and re-inserting it seems to re-establish functionality. Maybe this is some sort of reset, maybe connected to the above.
  • unconfirmed assumption 3: there is a command to send the device to standby, but the device specs do not clearly say what must be done to wake the device up again... there is a normalWorking, ... but what does that do?
  • the driver does not seem to implement all the device communication, but nothing obviously related to my problem comes to mind. There seem to be some state messages that can be read from the device and this may help. Requires extending the driver to read from the device, ATM the driver only sends commands.

add startup info on the display

in the enclosure there will be no way to visually determine, if the device is starting up or not. Some of the startup log should be displayed on the oled display in some fashion.

enclosure design: bottom fastening too weak. Top should be printed separately.

The bottom can be attached once and fits well, but the bolts break very easily when removing the bottom -> redesign to use proper screws, so that tge enclosure can be opened for maintenance.

The top works well as such, but it needs to be printed upside-down and so the outer layer must be printed on the build plate. While that works, it does not look as nice as expected. -> redesign to separate the top from the enclosure body. Doesd not need to be opened again for maintenance, so can be glued on in assembly.

introduce rolling log for system data

see #13 and #14 and likely more things: There is no log to establish what happened, when something happened.

There should be a simple log file to record what happens on the device.

Introduce log_mgr class, instantiate this when some setup config says that we should.

make a log.txt file of some sort, it should be rolling so it will not grow too much.

This should be logged:

  • activate alarm
  • deactivate alarm
  • set alarm time
  • raise alarm
  • quit alarm by timeout
  • quit alarm by manual intervention
  • connect wifi
  • disconnect wifi
  • call time server
  • handle time server response
  • update rtc
  • start sunrise
  • start of each neopixel thingy
  • start sound
  • end sound
  • every 10min: battery data, memory data
  • enter menu
  • exit menu
  • enter system info
  • exit system info
  • .... whatever else comes to mind

there is no way to power the device up or down

This is a stupid one - how did I forget this?
While developing the need never arose, but there actually is no way to actively switch the entire device off (and back on).

Ideally a way shpuld be found, to make this work without adding more hardware.
So, maybe this will work:

  • add a power down sequence to the system info, need to expand that to enable this (see below)
  • when powered down, actually enter deepsleep, woken by any button pin going high
  • the device should in theory just start the program as if just being turned on
  • when we run the battery dry in deepsleep, powering the device via USB will also initiate the normal start

sytem info menu:
Right now this is just one page with a few system info thingies. We could do it like this:

  • yellow button no longer starts the sytem info, but shows some text menu: (green: sys. info, blue: power down)
  • yellow again: like today, go back to normal mode
  • green: open the system info
  • blue: initiate power down, maybe add a screen saying goodbye.

updating the rtc is unreliable

every 6 hours the RTC is updated ATM. To do this a wifi connection is established, a service called and then the rtc gets updated and the wifo connection gets closed.

This does not seem to work, sometimes the onboard led (indicating connection) is lit up but never shut off again.

Either: The wifi connection cannot be closed
Or: The service is unavailable
Or: The response times out

Normal operation resumes oncompromised.

test for missing settings and missing media files

after the nth time i had exceptions ENOENT or if buried deeper just plain "an unexpected exception..." originating from just a missing file, I am fed up.

-> introduce some sort of test for required files, either just in a test (see #23 ) or meaybe even in the normal startup

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.