Showing posts with label ESP8266. Show all posts
Showing posts with label ESP8266. Show all posts

2019-01-20

Making a simple sunrise alarm clock

Purpose

I like to get up early to have more time to work and design in a quiet place. (Well, I have a joke that lazy people get up earlier to have more time to do nothing). I noticed that in summer it's easy to wake up with the first sun rays, but in winter it's more difficult. And in order to optimize your sleep cycles you don't want to be woken up by a noisy alarm clock. So I've long wanted to build my own progressive lighting with a programmable clock.

Possible components

The controller


Years ago I thought about building an alarm clock the old way, with a clock derived from the 50Hz on the mains, using an AVR microcontroller, with 7-segment digits. It would require an interface to set the clock up, etc.

Then when playing with my first ESP8266 I quickly managed to implement a remote controlled power plug and figured it wouldn't be hard to use this to make my alarm clock, and that it would simply retrieve the current time from a regular NTP server. It started to sound good.

The lights


I first thought about using an halogen lamp and a triac, but I didn't like the idea of letting such a fire igniter run by itself if I'm not at home, thus I started to experiment a lot with high power LEDs (1, 10, 50, 100W). High power LEDs are difficult to cool due to the high power density which requires a massive heat sink. My largest fan-less one is a 100W one that I limit to 45W. Also I figured that having such a huge power directly in your face is not the thing that makes you want to get up the most.

I then discovered LED strips. These ones are very convenient. The power is spread over a long length and thus a large surface. They don't need to be as powerful as compact LEDs because they already cover a long range, and they are easier to stare at.

Final choice

I opted for an ESP8285 made of a PSF-B85 board. It's extremely compact and has everything I needed, plus it's supported by the NodeMCU firmware :


For the display, instead of starting with 7-segment LED digits, I wanted to experiment with some I2C OLED displays which are also supported by the firmware. I found very cheap 128x32 graphics displays like this one. If I had to pick a new one though, I'd pick a larger one as this one is very small.

For the lights, I've opted for white LED strips. It is important not to pick the cheapest ones though. I discovered that some are not adhesive, and some expectedly adhesive ones have a very poor quality adhesive which quickly dries on your shelf and doesn't stick anymore when you unroll it. Also, some come with incorrectly soldered LEDs or even some with reverse polarity. I had to fix mine by hand, about 5% of the LEDs wouldn't turn on due to poor soldering. Preferably pick one from a vendor with a very good reputation, and stay away from misleading or confusing descriptions which often indicate lack of care for quality.

This time I needed to put this into an enclosure with buttons. I had some (very) old PVC enclosures made for MIDI adapters which were of the appropriate size, so I could start with this.

Design

PWM driver

Controlling a LED for progressive lighting requires some PWM. The NodeMCU firmware running on the device supports emitting a PWM signal on one pin. But I needed to amplify this PWM signal to drive tens of Watts.
I restarted from the design I came up with for my high power tv-b-gone, and adapted it, resulting in this :



Planning for supporting high power, I changed the MOSFET for a larger one at the last minute. The diagram above shows an IRF7313 (dual FET) but I preferred to pick an IRLR3715 which stands higher currents pulses and better spreads heat into the PCB (supports 54A/71W vs 6.5A/2W).

Programming

This time I didn't want to have to solder wires to reflash the device if things went wrong, so I decided to start with an easy solution. I implemented the adapter I designed here with a 6-pin connector so that I could simply attach an FTDI adapter to flash the board. In addition, among the two buttons, the first one will be used to switch to recovery mode during boot in case things go wrong. This was used quite a number of times during development :-)

Voltage regulation

Like most ESP boards, this one requires a regulated 3V input. I preferred to use a switching DC-DC module instead of using a linear regulator, because the linear regulator would heat a lot by dissipating the difference between 12V and 3V.

PCB

I had everything ready to start designing the PCB. Only add one decoupling capacitor, a power connector, another connector for the LED strip, and the two buttons. The DC-DC module will be external for simplicity. As usual I did it the old way with the pen since it remains the fastest method :



Construction

Time to throw the PCB into the etching bath. I should document my alternating etcher by the way :


Ten minutes later, I picked it out of the bath, cleaned it and drilled it. The result is quite good :


Let's place the SMD component first. Below two versions, one raw, and another one with all the signals annotated :



Then place the PTH components and connectors :


Connecting to the PSF-B85 is not easy due to the 1.27mm pitch between the pins. The solution I turned to was to use some 1.27mm pitch floppy drive cables, allowing me to easily connect pins and still have long enough wires to go to the various places on the board. The result doesn't look pretty but it allows to move the board around to check signals and to replace it if needed :


Programming

I've reused the iot-core framework I developed on top of NodeMCU, and had to iterate several times using NodeMCU-build because my code ended up using lots of memory and I had to remove lots of unused modules to save memory.

Among the unexpected "innovations" there, I had to implement several programs depending on the displayed screen. NodeMCU can reclaim memory of an unused program so the final program is very similar to what you're used to do when using lots of UNIX commands at a shell. The program is like a shell which launches programs. Switching to another program is fast enough so you don't notice. All the code is available here. Feel free to duplicate it and use it.

I just had to connect my FTDI adapter to the purposely installed 6-pin connector and the programming was trivial :



One nice thing of keeping the voltage regulator external is that during development, the FTDI adapter is sufficient to power the board so you have total control over it. Keeping the leftmost button pressed during boot is enough to drop to the Lua interpreter shell (this is handled by the iot-core framework which refrains from loading the application in this case).

I implemented a few simple functions to draw large 7-segment digits on the OLED display. These ones are explained in the source code. The result is quite good (the differences in intensity are  caused by the refreshing) :


The buttons are used like this :
  • button 1 : switch between multiple light modes (on/off/rise/fall ...)
  • button 2 : switch between multiple screens (big digits, status, alarm setting)
When setting the alarm time, an underscore is placed below the digit being edited. Button 1 increments this digit and button 2 switches to the next digit, so setting 3:40 above roughly requires 7 + 4 button pressures. The first digit can be turned to "-1" to disable the alarm.

The lighting modes are very simple : by default, the output is in mode 0 which is off. Once in mode 1, the PWM output slowly rises from 0 to 100% over one minute, then it reaches mode 2 which is always on. Mode 3 falls from current value to 0. This one is convenient to leave the room after pressing the button. I had to change from linear to quadratic progression, because the eye doesn't react linearly to the LED intensity, and when used linearly you feel like there's little difference between half-lit and full-lit, so it was too fast at the beginning.

A completely useless feature I implemented was to indicate the day of week on the left of the hour and on the status screen between parenthesis. But I figured I never used it. I thought I would make the alarm programmable by day of week but there's no need for this, it's so easy to adjust twice a week that it's not worth the pain of making the configuration more complex :




Final assembly

The PCB was placed inside the plastic enclosure, with some foam to plug the rear holes.


The finally assembled box looks like this :


For the LED strips, I've placed two 3-meters wide ones in parallel and connected them using some thin telephone wire. Since the ceiling is oblique in my room, I thought it was the perfect place to install it so that it doesn't send the light into the face while still casting it strong enough to wake up :



Results and lessons learned

It has been running flawlessly for more than one year. In that regard the NodeMCU firmware is quite stable. I faced some stability issues with the first PSF-B85 module I used. The device would randomly reset several times a day and sometimes switch to the wrong baud rate. I spent a lot of time trying to debug this and improving power decoupling and pull-ups, to come to the conclusion that the device was faulty. I replaced it with another one and all issues disappeared.

The amount of RAM is too small for such applications to remain convenient to implement. What I like with NodeMCU is that you have a shell so you can debug and develop live without having to implement everything in your code, rebuild and upload. But it also means that files are compiled on the device before being processed. This takes a lot of RAM. The solution consists in saving them pre-compiled but it's not possible anymore once the application is loaded. It causes me issues twice a year when changing the daylight savings time. For this I have to connect over telnet, edit the configuration file, save it, reboot in safe mode by holding the button, recompile the config file, and reboot. It's quite of a pain.

I thought about re-implementing all of it in C using a different framework but it would take quite some time just to save half an hour twice a year... Not very tempting. The best long-term option would be to switch to the ESP32 which has way more RAM, but the NodeMCU kit is still in development for this device so I don't know if it works or not by now.

The OLED display is not great. First, the MCU doesn't have enough memory to draw on the screen at once, so it has to use pages and to draw the same image 4 times with a mask and only send one page at a time. Not only this is slow to compute, but it also requires quite some I2C bandwidth. Each screen change takes slightly less than half a second. It's annoying when changing the alarm time. The digits are small and very bright. At night it can be slightly blinding and possibly not always very readable for people who wear glasses. Larger red digits would be better in and less aggressive the end.
Also the OLED display wears quickly. The areas having the most segments lit are now fainter than the other ones.

The LED strip is too white ("cold" in LED vocabulary). It's a 6500K white. I hesitated with a 3200K that I already had and which I found too yellow. I think a good approach is to mix two of them. I'd need a bit more power as well. Right now the strips draw between 15 and 20W. I should go to 25-30W for a better effect

Another surprising point is that one LED is constantly lit despite the output showing zero volt. The reason is voltage leaking by capacitive coupling between the power supply and the wall! The lowest voltage LED is the one which turns on. During day it's not visible but at night it is. Installing a 1K resistor in parallel to the strip is enough to turn it off, but it would dissipate some power. I really don't care so I didn't fix it.

The LED still turns too fast from 0 to 100%, especially from 0 to, say, 10%. I think that making it more progressive over 10 minutes would be better. Ideally some experiments should be made with RGB lights such as WS2812B to better mimmick the sun's colors in the sky :
  • start reddish for a few minutes
  • progressively add the green component to make it yellow
  • then add the blue component to turn white
I'm not sure how much power I can pull from a WS2812B strip though, since it works on 5V and it will not accept well to take many amps. I've also read some stories about these devices being sensitive to interference on long distances. This can be amplified by the high current required to have enough power to properly light the room.

I found it convenient to keep the light on for 30 minutes. It starts 20 minutes before the buzzer-based alarm, and almost always manages to wake me up so that I can stop the alarm before it rings. Thus it seems that with this amount of light, I need 20 minutes to detect the light (possibly finish a sleep cycle).

Interestingly such devices increase the probability that you wake up at the end of a cycle, and help you measure the length of your sleep cycles. You need to keep in mind to note the first hour you observe when waking up. I could measure mine to be multiples of 100 minutes (well more precisely 97 to 99 in fact), which is convenient to count since 300 minutes are 5 hours. I managed several times to wake up easily after 200 minutes (3h20). What I don't know yet is if in reality these are multiples of 50 minutes. I'm not yet sure about the effectiveness of 4h10 (250 minutes), since around this duration my few experiments have shown irregular results. Anyway I know it would not be enough for me over the long term.

Overall my sleep is much better and I'm much fresher in the morning. No more headaches when sleep is suddenly interrupted by the buzzer. I long thought I needed 6 hours but I feel way better with 5 hours with a soft wake up like this than with 6 hours using the buzzer previously. It once stopped working (poor wiring connection on the DC-DC board) and repairing it went very high on my priority list!

Ideally I should build a new and better version of this device, but I wouldn't value the improvements that much for the time required to make another one. So let's wait for it to die first :-)

2017-08-23

What ESP8266 modules should look like

Context

I've been using ESP8266 modules over the last 2 years for various IoT projects. These modules come with a number of very annoying characteristics which make their adoption problematic to beginners :

  1. they don't boot by default, it is mandatory to solder a number of wires and pull-up resistors just in order to get them to boot.
  2. their pitch is not 2.54 mm, so it's not possible to plug them on a breadboard to simply connect the required resistors and wires
  3. there are two pins (RST and GPIO0) to change between operation and programming, so when you soldered to achieve point 2 above, you have to desolder and solder differently just to flash, keeping a flying wire for the reset which must not stay connected (obviously)
Some vendors understood this and started to propose very nice development boards like the NodeMCU or Wemos D1. The NodeMCU clearly is too big to serve as a production model, but it exposes all I/O, integrates a USB UART, a "flash" and a "reset" button, and connects the UART's RTS and DTR signals to the RST and GPIO0 wires so that the programming software can automatically toggle these lines to program. The Wemos D1 provides all these features except the "flash" button, and is half as small. In fact it's only twice as large as the ESP12 module. This one may be used as-is for some projects.

But both boards present a big problem : the USB UART cannot be disconnected and it draws a lot of power. So if you want to use these boards for production, you can only use them for mains-powered devices, not battery powered ones. Some people explain how to cut wires on these boards to reduce the power consumption but it's a real pain to do. Thus often you're back to using the raw ESP12 device as-is and solder the wires yourself.

But then comes another problem : most USB UART devices adopt the now ubiquitous "FTDI" pinout, which exposes GND, VCC, RXD, TXD, DTR and CTS. The problem is that while DTR is an output and may be connected to GPIO0, CTS is an input and you don't have another output to use to select between running and programming, so it's still required to plug/unplug wires during programming. Some more advanced circuits implement an automatic RST+GPIO combo signal based on DTR only. But for me all of them have proven very unreliable, even after various modification attempts. First, GPIO0 sometimes emits a strong ~20 MHz signal preventing the DTR pin from going low and triggering the RST ; this does not happen while RST is held down however. Second, most often as soon as you open a terminal , DTR is triggered and resets the device again, which is not fun. Fortunately, during this time, RTS is low so it's possible to consider the combination of RTS and DTR instead of each individual signal. So as if it was not enough, DTR and RTS must not be directly connected to GPIO0 and RST, it's required to implement an exclusion between the two so that GPIO0 is triggered only when DTR is low and RTS is high, and RST is triggered only when RTS is low and DTR is high.

So I spend quite some time scratching my head trying to find the appropriate solution. The important points are :
  1. the board must be able to boot in running mode with only the power connected. This means that the pull-ups and pull-downs must be connected
  2. the USB-UART controller must not be physically present on the board, as it sucks power, takes space and costs money. Instead only the 6 pins required to connect a serial adapter must be present
  3. the board must use 2.54mm pitch, be very narrow, not larger than the ESP12 itself so that there are still pins left around it on breadboards
  4. the serial connector must respect the FTDI-compatible pinout so that any adapter will fit at least to allow regular communication with the device, and emergency flashing by moving RST by hand if needed.
  5. the RST pin must not be connected to the serial adapter but be available for RTC-based wakeup if required. Thus the module must use the CH_PD pin instead (also called "EN" or "CHIP_EN")
  6. the serial adapter must be modified to route RTS instead of CTS on pin 2
  7. the serial adapter must provide the logic to combine RTS and DTR as described above. If small enough it can be placed on the final board, otherwise it's better to have it as an intermediary board.
By searching for existing designs I found one part of the solution : Wemos not only does nice ESP boards, they also make an FTDI compatible USB UART on which you can decide to route RTS instead of CTS to pin 2. That confirmed to me that point 6 above can be addressed and become a prerequisite. I ordered a few and decided that in the mean time I'd modify my FTDI adapters.

For point 3, I cut some experimentation board to the same dimensions as the ESP12, plus one row for an optional serial connector. It happens that the 8 pin rows on each side are the same length as the module once expanded to 2.54mm, and that exactly 6 pins fit in the module's width!
I decided to place 2.54mm male connectors in the middle, spaced by 7.62mm so that they can fit on the middle row of a breadboard and even in a DIL16 socket on any board.

This left a central area where a few wires were routed and where there's enough room to install the pull-ups and pull-downs, addressing point 1 (I moved them elsewhere on this prototype as it was a pain to solder them after the connectors were in place).

By placing the serial connector close to the antenna there's no risk of touching the SPI pins at the bottom. Also the antenna is generally supposed to be located in an accessible place so it makes sense to install this connector at the same place. It also turns out that it was convenient to route RXD and TXD.

For point 5, I noticed during tests that a weak pull-up would still be better on RST otherwise it catches RF noise around (ie if you touch it with your finger). But that's a minor detail and doesn't prevent it from working.

So let's proceed with these various steps in order of dependencies

Making a usable ESP board

I didn't want to start making my own PCBs, it was a late afternoon's project. I decided to go with experimentation board, that I cut to the appropriate dimensions to hold the ESP-12E module and a 6-pin connector for  the serial port. Then I've cut unused traces as well as a central area to have two sets of connections. For those interested in trying it, you need to keep 11 rows of 6 holes. It's mandatory that it's single-sided because the ESP module will be placed on top of it and we don't want to risk accidental contacts :


Then I prepared the ESP-12E module. I've soldered very thin wires in each hole. The wires need to be long, at least 4-5 cm, or it will be a pain to place them into the PCB holes later. For this I've cut multi-strand wires and used their thin individual wires :


Now the difficult part starts. Before proceeding, it's important to pull the wires to ensure they're firmly attached. If some wire pops on the other side of the module, it needs to be cut. While pulling the wires, try to arrange them so that each side is approximately parallel with wires approximately 2.54mm apart. These ones will be placed into the PCB. Inserting the first side is not very difficult but requires patience. What is difficult is to insert the second one without removing any wire from the first one :-)

Given that the pitch between the two series of solders is not the same, some wires could cross. Of course we don't want this. So we want to insert the wires into the second row of holes on the PCB, like this :


It should then ressemble approximately this after you pull hard enough on the wires to ensure that none is blocked by another one or touching a neighbour :



Then quickly solder them, cut the remaining wires very short, and grind the solders so that the're as flat as possible :


Then the fun can begin. Cut two 8-pin right-angled male connectors that you solder 7.62mm apart, above the 2nd and 5th columns precisely, and solder them on the outside :



The last part for this board is to install the 6-pin connector for the serial adapter, and to connect the wires and components. I've used only 10K resistors, except in series with GPIO0 where I've added a 470R to protect the serial adapter against the output signal that's sometimes present on this pin during reset. I've also installed a 10 µF decoupling capacitor between VCC and GND because there was enough room for it and it was easy. The wiring diagram looks like this (it's easier than a schematics given that these are almost only wires), followed by the final assembly :

 

 

It's worth mentionning that if you don't see the pull-up resistor for the RESET pin on the photo, it's because I omitted it (as not strictly required), but I'll change this as the reset pin is now too sensitive to my finger, and I tend to reset the device when I touch it.

Modifying a USB-TTL serial adapter to provide both DTR and RTS

I'm using different flavours of FTDI adapters, and all of them have CTS on pin 2 :


I need to have RTS here. So I expected to be able to cut the PCB trace and solder a wire but I can't follow this trace which probably is under the IC so I don't want to damage my board.

Instead I decided to proceed differently by using a 6-pin male-female connector to extend the existing connector, not connecting pin 2 to the adapter but instead cutting the other pins, bending pin #2 over the PCB and connecting it to RTS which is on the chip's pin #3 according to the datasheet. That's all! Now you have a modified FTDI adapter with RTS on pin 2! This modification is very simple to operate. However don't make the same mistake I did, you need to glue the connector once it works, otherwise it will come out of the adapter once you unplug the adapter and pull off your wire. BTW since I couldn't find a 6-pin connector, I had to cut  a 8-pin one.

In the mean time I found that Wemos proposes such an adapter on which pin #2 can be configured to be RTS or CTS, so I will probably not develop further on this adapter's mod.





Converting the DTR/RTS signals to GPIO0/EN

It's not practical to directly connect the signals to the board, it's required to implement exclusion between the signals so that when DTR is low but RTS high, the chip is forced to flash mode, and when DTR is high and RTS low, the chip is reset. Otherwise the chip will work in a single mode, or all software will have to be modified to consider your protocol.

The truth table looks like this :


So it's as simple as doing :
  • GPIO0 = DTR | !RTS
  • EN = RTS | !DTR
In practice it's often made with NPN transistors and resistors on their base, but I do have a few low-voltage dual-mosfets in SOP8 packages which are very convenient because they don't require to cross PCB traces nor to add resistors. Some of them are IRF7313, but IRF7301, IFR7303 and FDS6990 will work fine as well. I also have a few other ones which have the exact opposite pinout, but I don't remember their identifier, so I stopped at the first one I found that I knew. The IRF7313's pinout is this one :


 

 The schematics we want is trivial (GPIO0 resistor included here but it's better placed on the ESP board) :



So the wiring can even be made using flying wires :


But I decided to be reasonable and to use another piece of PCB to make this one, so that connectors are firmly attached. The purpose will be to use the same pinout as the modified FTDI adapter on one side, and as the new ESP PCB on the other side. The result comes below :




I noticed that I wrote the pinout on the least convenient side, so it's better to do it the other way around to match what is displayed on the FTDI
module and the ESP module. And it worked like a charm on first test!


Now what is needed in the end ?

After all these modifications, and after having found the Wemos serial adapter which supports RTS, I concluded that the only part which does not exist is the ESP module for end users. So in fact given that the dual mosfet above is very small and consumes zero power, it should fit on the small board I installed the ESP-12 on. This board would then be programmable and compatible with deep-sleep. Also, even smaller MOSFETs exist, I've seen some dual in SOT363 package.

So by having just a board with 2.54mm pitch, the same pinout as the FTDI board for the serial connection, the MOSFET mounted and the few pull-ups, it would be possible to have the equivalent of the current ESP-12E module, but which could easily be programmed, either for development, or just for production. I think it should be sold with the connectors unsoldered. This way everyone can use it as a simple replacement for the current ESP-12 with a different pitch, yet program it and have it run by simply sending the power. Those who want to turn it into a development model can solder the two 8-pin barrels underneath, and it becomes compatible with breadboards and DIL16 adapters.

Those who simply want the ability to reprogram it in field just have to solder the 6-pin barrel connector and they'll be able to plug their FTDI-like adaptor in situ to reprogram it.

The best thing that could happen would be that some of the vendors like AI-Thinker create a new ESP module with such characteristics. It would solve all those issues at once. Right now when you look up "ESP8266" on Google image, you find tons of connection diagrams explaining how to flash them, which proves it's all but easy, or the NodeMCU models which provide the solution for development only. Let's think about it a bit further and make it usable for everyone! I'm not an electronician, just an occasional hobbyist and I could make it. But for people like me, it takes an amazing amount of time. Having the correct boards from the factory would be so much great!

I've ordered some ESP8285 which include the flash, maybe its possible to rebuild a board from scratch featuring an appropriate pinout. The same could be said for ESP32 which doesn't seem to have improved anything in this area unfortunately :-/