Tuesday, 30 May 2017

The ESP8266 Alarm Can Now Send Emails

Here's the result:

An email alert as seen in my inbox:

The code is here. The credentials and recipient email address are defined in email.h at the moment, so changing settings requires a reflashing at the moment.

The ESP8266 does a DNS lookup for gmail's SMTP servers, then connects with SSL, logs into an email address I set up specifically for this device and sends off an email. I have only tested this with gmail, where I enabled the option to allow "less secure apps" and also created an app-specific password (I don't know if other email services provide this) - which is a necessary precaution, as I haven't implemented certificate verification yet.

If you ever want/need to debug SMTP over TLS/SSL, as I had to do a lot of, here's a useful command to run on Linux if you have OpenSSL installed (you most likely do):

openssl s_client -crlf -connect <server>:<port>

It lets you talk directly to the server in plaintext from a terminal, just as if you were using a telnet terminal to talk over an unencrypted connection. The only thing to note is, do not send the character 'R' as the OpenSSL terminal intercepts this and interprets it as a command to renegotiate. This caught me up for a long time when I was trying to send "RCPT TO: ..." as you do for SMTP; lowercase is a better choice it seems.

Here's a screenshot of sending an email with SMTP, as I thought would be a good idea to figure out how to do by hand before implementing it on the ESP8266. The lines with numbers 1-7 are steps/commands that I sent. The authentication string (step 2) is just the username concatenated with the password, all encoded in base 64.

Some additions to the web page interface: 

The page now looks like this before any sensors are triggered:


The additions are the new options to arm/disarm the alarm and time stamping. Pressing these buttons achieves the same thing as pressing the wireless key fob which came with the original alarm base station, which this alarm system now listens for.

After some sensors have been detected:

 A printed case:

"But it looked alright on the computer"

Definitely only a first revision enclosure. I designed it a bit small, with walls too thin as well, so it doesn't quite close properly, and the overlap/interlocking bits are tiny (can't really see them in the photos).
Also - oops: I tried polishing the printed plastic with an ABS plastic and acetone solvent solution... but it was white ABS dissolved in the acetone.

Conclusion:

In all, seemed like it would be an easy feature to add; SMTP is simple after all, but to get this one working reliably was a challenge with the complexity of needing SSL on the ESP8266. So if you are looking to get SSL working on the ESP8266 and need more working examples take a look at the code.

Wednesday, 17 May 2017

Engraving a Prototype PCB

I put the CNC engraver to use milling a prototype single-sided PCB for the ESP8266 alarm system I've been working on. The tutorials on the internet for PCB2Gcode and KiCAD were pretty easy to follow, so this post is not a tutorial, but just for documenting my experience and some gotchas.

Design

The final design of the board in KiCAD


The board is a connector board really. It powers the ESP8266 module at 3.3v and the 433MHz module at 5v, pulls up the reset and CH_PD pins on the ESP8266 and the buttons allow it to be easily reset and programmed (there's a UART header on the board too). For the wireless alarm system side of things it has a connector for the 433MHz wireless module to plug into, the output of which is voltage divided to get it down from 5v to somewhere in the neighbourhood of 3.3v, which the ESP8266 can handle.

Toolpath generation

Converting the Gerber and drill files from KiCAD into G-code instructions with PCB2Gcode. 
You always have to watch out for potential issues with mirroring at this point in the process, otherwise you might mill and drill a PCB only to find it "off" somehow. That reminds me of an early trap I ran into in KiCAD of the SMD component pads being on the opposite side of the board to the traces. This of course makes no sense for a single sided board without vias. The side that traces and SMD footprints are on can be changed, however.

The drill files were a bit of a nightmare because this PCB needed about five different drill sizes ( which I have), but it means lots of tool changes. The G-code that PCB2Gcode outputs also seems to expect your machine to be capable of automatic tool changes, which mine cannot do. I got around this by manually separating the G-code for each drill size into its own program. I'll have to look into writing a script to do this for me and/or insert a pause command into the G-code, if the feature does not already exist in PCB2Gcode.

It's important that Grbl doesn't have its position zeroed out between programs or it'll make a mess of things. Alternatively, and to be on the safe side, have the machine return to (0,0,0) position at the end of each file and don't do any homing, just so you always have a reference point.

Engraving/Milling


The tooling that was used. None of the tiny little drills were broken, to great surprise. The v bits on the other hand are made of a fair hunk of carbide and are really tough - I accidentally cut the board outline in a single pass (~1.2mm cut depth) and there weren't any issues.

The CNC engraving setup with the board in the process of being engraved.
The finished board.
The clamps holding the board down were scarily easy to undo after all the vibration of cutting; I'll have to put spring washers under the bolts to stop them coming loose. If the board had come loose in the middle of cutting I would have had to start over again without any way to align the board to the bed.
Even more closeup. Not all traces have been fully isolated, and some have too much taken off. The board must not have been completely flat and level, and I think the settings have underestimated the dimensions of the cutting bit.
A bit more tuning of the settings in PCB2Gcode will be needed, and probably also a program which probes the surface of the board before cutting to determine where the surface of the board is, so that a constant cutting depth can be achieved.

The populated board. The ESP-01 breakout board is socketed (can't be seen from this angle).

EEEEKKK! Not nice soldering. I blame the lack of soldermask and the pads coming out undersized - did not make for very forgiving conditions. There's also one bodge wire, for a trace that was completely wiped out; it isn't very easy to make out in the photo however.

Assembled and ready for programming.
There's a 0.1mm copper wire soldered (and secured with a blob of hot glue) onto the actual QFN package of the ESP8266 to get access to an additional GPIO pin because the ESP-01 board does not break out enough free GPIO. This was actually factored into the design, so the board I designed has a pad which the other end of the jumper lead is soldered onto.

More to come on the ESP8266 wireless alarm system soon...

Thursday, 23 February 2017

3020T CNC "Engraving Machine"


I bought a light CNC milling/engraving machine off eBay to mill PCBs and other parts in future. I'd have liked to try building my own, but that will have to be a project for the future, if I need a sturdier or larger machine.

The machine, of model "CNC 3020T" (which refers to its travel area of 30 x 20 x ~7cm), came pre-assembled in a large box. There are a lot of similar CNC machines on eBay/Aliexpress in terms of size and style of construction, and there appears to be the option of a model with ball screws, as opposed to the trapezoidal lead screws in the "T" model. I haven't had any issues with backlash yet, but I imagine that depends on loading and how worn the nuts are.

The spindle takes ER11 collets, so I also bought a set of them separately, as well as some PCB drills and V-bits. Some of the collets are sized for bits large enough that I doubt the spindle would have enough torque be able to mill with them, nor the machine have enough rigidity, but I shall see.

All I had to do after pulling the machine out of the box was fit the stepper motors and wire them into the control box that accompanied it - well not quite... The control box that came with the machine had a parallel port for interfacing with the controlling computer; I do not have any computers with a parallel port, and USB to parallel port converters are not suitable for controlling CNCs due to the tight timing which the step pulses must be adhere to for motion to be accurate and smooth.


Arduino-based control with Grbl: 


But there was an easy solution that brought the CNC into the modern era of USB - that was via inserting an Arduino, and making use of the fact that the original control box is fairly basic. It has three stepper motor drivers (one for each axis), a linear power supply (presumably with three rails to supply the logic, stepper motors and the spindle motor), and a PWM controller for adjusting the speed of the spindle. The parallel port on the back has two control pins per axis, which feed straight into the motor drivers via opto-couplers. The control pins are the "step" pin, which causes the corresponding stepper motor to move one step when it is driven high; and the direction pin, where high or low indicates the desired direction of movement. "Step/direction" is the exact scheme Grbl uses and is something of a standard in CNC. Some searching provided guidance on how the parallel port was likely laid out, but I tested the pinout by connecting an Arduino running a sketch which sent 100 pulses over a second, then reversed the direction before sending another 100 pulses, and so on, which confirmed that the pins were correct as one motor at a time started moving. This was after verifying ground with a multimeter and that nothing would obviously blow up my Arduino if I connected it. The pinout was as follows:

DB-25 Pin   | Function
2                 | X step
3                 | X direction
4                 | Y step
5                 | Y direction
6                 | Z step
7                 | Z direction
18 - 25         | Ground

The rest of the pins on the parallel port I left unconnected, as they are unused on my machine because it has no endstops nor extra axes, etc.

After that, I uploaded the Grbl CNC software onto the Arduino, then configured the steps per mm (400) and "inverted" the appropriate axes so that the machine follows the right hand rule and the back right corner is 0,0 (the back-right corner is just personal preference, as opposed to the front-left corner, which also works). This configuration was done via some Grbl serial commands which store these settings to EEPROM so that I don't have to re-send the commands every time on startup nor change a config file and then re-compile and re-upload Grbl. Here is Grbl running the CNC according to G code commands I sent manually over serial:





A dedicated control server:

Note: no USB hub is installed yet, so I just plug in either the CNC or the 3D printer
To avoid a big PC sitting in the garage alongside the CNC and 3D printer, I hoped to make use of a BeagleBone running OctoPrint to control both my CNC and 3D printer - it should be possible after all, what with both machines running on G code sent over serial. OctoPrint is a fancy webserver and G code sender written for controlling 3D printers via a browser interface; so any computer on your home network can simply connect up to the webserver on the BeagleBone to control/monitor the printer and upload jobs to it - no more tethering a dedicated laptop!

Well in theory - and indeed 99% of it is there, but OctoPrint wouldn't "connect" with Grbl and so let me send G code, likely because Grbl doesn't support some of the custom commands which RepRap firmware has for reporting temperature, version number, etc. rather than because Grbl lacks any basic G code commands. OctoPrint, as of my admittedly dated install, finds these custom commands crucial. All very frustrating given OctoPrint picked up the serial port fine and both ends supported the same baud rate and were communicating in plain ASCII.

So when controlling the CNC, instead of using OctoPrint, I use the stream.py script that comes in the Grbl repository. At present, this means that I have to SSH into the BeagleBone and transfer G code to it using SFTP, then start jobs over the command line instead of a nice, easy web interface - gah!

Some other hiccups along the way:

  • Ubuntu on the BeagleBone wouldn't detect hot-plugged USBs (restart needed every time the USB port was plugged/unplugged) - solved this by installing Arch Linux, which must've had a less-funky kernel, because hot-plugging works now (and more free space - didn't want a GUI anyway)!
  • The USB port on the BeagleBone would get disabled when I turned the power to the CNC controller off while the Arduino was connected to the BeagleBone's USB port. But how?! The Arduino was connected to the stepper drivers through opto-couplers (inside the control box). Even more puzzling, the solution was to change the USB cable to a higher quality one. One where the shielding was actually grounded on both ends. I don't think the BeagleBone was acting as a ground path, because it was on a floating power supply, and the control box, which does have a ground-referenced power supply, also has a grounded case which should stop EM emissions, so I can't really make sense of it. My laptop, also floating, had no issues with either cable.


Conclusion:


All in all, I'm happy with the purchase - nothing was broken or missing, and postage was quick. The control box is useful and easily interfaced with Grbl. I've got a post on isolation routing PCBs coming (and then also a post on the project that I made the PCB for), spoilers: this CNC is more than capable of making a good job of PCBs.

Wednesday, 30 November 2016

I2C with the ESP8266 and in General

The ESP8266 has no hardware support for I2C, however the Espressif SDK (version 2.0.0 non-os) exposes low level functions which implement the protocol robustly enough on a pair of GPIO pins through bit-banging. As a result of being done in software, the implementation does tie up a bit of processor time, and in addition, the functions block while they write or read, etc. Note also that this SDK version reportedly does not support clock-stretching nor being the slave device (neither of which I have tried).

As a demonstration, and to learn how to use this I2C functionality, I got the ESP8266 to talk to a HMC5883L compass module which I had successfully used with an Arduino previously.

Code example of talking to an I2C compass module here.


The SDK functions are as follows:


To initialise (this only needs to be done once, usually on boot):

  • i2c_master_gpio_init(), and then
  • i2c_master_init()


For the actual I2C communications you have these functions with which to indirectly manipulate the GPIOs and craft a datastream:

  • i2c_master_start()
  • i2c_master_writeByte(your_8_bits_of_data)
  • i2c_master_readByte(), simply clocks out 8 pulses on SCL and reads SDA on each rising edge then returns the read data as an 8 bit integer. Handling the state of the slave so that it is ready to have data clocked out of it is not handled by this function, however the datasheet of whatever slave device you are using will usually give details on its I2C  r/w addresses, how to configure internal registers and pointers - and thus how to ready the device to send data.
  • i2c_master_stop()


  • i2c_master_checkAck(), will return true if the slave ACKs, false otherwise. For almost all I2C devices, this will need to be called after a byte is written so that the ESP8266 produces a 9th clock pulse in which the slave has the opportunity to ACK or NACK. i2c_master_writeByte does not take care of this crucial part of writing a byte to a slave.


The following two functions are intended to be called only when reading data from the slave. Either an ACK or NACK is sent to the slave after receiving a packet; this is to signal whether more packets are going to be read by the master or that the master is done reading from the slave:

  • i2c_master_send_ack()
  • i2c_master_send_nack()

These are all quite straightforward functions that do as they say on the tin, however the functions relating to acknowledging packets required some investigation and probing on the oscilloscope to figure out.

Some problems that I ran into:


I initially had issues because the ESP8266 was not producing the 9th clock pulse which the HMC5883L expected when being written to, causing the two chips to go out of sync, hence the warning about needing to call i2c_master_checkAck.

Also, I had a nasty bug which was caused by ACKing the last byte that I intended to read from the HMC5883L instead of NACKing it. This would cause it to anticipate more clock pulses coming and believe that the ESP8266 was going to try to read from it, as such, it retained control of SDA, stopping the ESP8266 from controlling SDA, which is necessary for issuing a stop condition. As the ESP8266 tried to pull SDA high to signal a stop, the HMC5883L would sink all the current coming out of that GPIO pin, effectively being a short, however this didn't seem to stop or crash the ESP8266, and serial remained fine despite the serial indicator LED lighting up solid. The only noticeable effect other than the serial LED going solid was the numerical readings from the HMC5883L turning to gibberish. It took a long time to find the cause of this bug because it simply wouldn't appear unless the compass was rotated to certain orientations, otherwise all would work fine. Eventually putting the oscilloscope on revealed that the compass was hogging SDA; before that, I had suspected corruption in either the ESP8266 or the HMC5883L, or some code struggling with certain values being read, or even EMI, so I completely re-flashed the ESP8266, added pullup resistors on SDA and SCL and combed over the code - to no avail.

A third problem that I had, specific to the HMC5883L, was with the datasheet, which said that to read from the compass, the number of bytes to be read has to be sent in a second packet after the read address. This second packet specifying the number of bytes to be read was always NACKed by the compass and turned out to be unnecessary, and further, stopped data from being read.


Decoding an I2C transmission on an oscilloscope:



Above: three bytes being successfully written to the HMC5883L. SDA is in yellow on top, SCL on the bottom in blue. It can be seen that the slave is ACKing on the 9th clock pulses by pulling SDA low; notice that when it is the HMC5883L pulling SDA low, it goes slightly closer to 0V than when the ESP8266 is pulling it low, this is related related to the differing amount of current each can sink, possibly due to differing gate types/sizes.

Anyway, to read that I2C waveform above, keep foremost in mind that a high clock state indicates that SDA is stable and thus ready to be read, and that a low clock state indicates that SDA may now be changed, and that it is not stable because the signal may still be propagating or settling. At the start of the transmission, both SDA and SCL are high, then SDA is pulled low while SCL is high; this violates the cardinal rule of only changing SDA when SCL is low to transfer data, but it is done in I2C to indicate the beginning of a transmission. The stop condition is a mirror of this and is also notable for violating the rule - SDA is allowed to go high while SCL is already high.

So from beginning to end of the I2C transmission above:


  • Start condition

  • First byte: 00111100 (transmitted by the ESP8266 over eight clock pulses)
  • ACK from HMC5883L (one clock pulse)
  • SDA floats high as it is released before the next byte and after the ACK. Its state does not actually matter in this region (SCL is low)

  • Second byte: 00000001
  • ACK
  • SDA floats high

  • Third byte: 00100000
  • ACK
  • SDA floats high then is pulled low again to prepare for the stop condition



  • Stop condition


This particular captured transmission writes to the HMC5883L to configure it. The first byte is the compass' write address, which it acknowledges to indicate that it recognises that it is being talked to. The second byte is the address of the register inside the compass which we want to write to in order to configure it (the HM5883L moves its internal register pointer to point to this address). The third byte is the value we want to set this register to. This syntax for writing a register is specific to the HMC5883L, but I imagine that it is similar for other devices; once again, the datasheet is your friend - the purpose of this section was more to demonstrate how to decipher I2C.

An I2C read is similar:

  • Start condition
  • Master sends 8 bit read address
  • Slave acknowledges

  • Slave sends 8 bits (but it is the master that controls the clock)
  • Master acknowledges
  • Repeat previous two steps until last packet...

  • Slave sends 8 bits
  • Master does not acknowledge (NACK)
  • Stop condition


Tuesday, 19 April 2016

Combining the ESP8266 WiFi module with cheap 433MHz sensors

The ESP8266 is an incredibly cheap and powerful WiFi chip which is geared towards "internet of things" applications, it comes from an obscure Chinese company, but they've released its SDK as open source. With its high availability on eBay, it was hard to resist, so I bought one to play with. At the moment, recently inspired by cnlohr's work, I've got it serving webpages and detecting when "dumb" wireless PIRs and door sensors around my house are triggered.

A long story (skip it if you're here only to see stuff working):

For a long time after purchase, it sat unused like all useful electronics junk off the 'Bay. The thing was frustrating: I didn't have a 3.3v regulator, and I didn't have a USB to serial adapter to talk to the thing. Nevertheless, an Arduino board has both those things, so I powered it off the Arduino's 3v3 pin, and connected it to the tx and rx pins, using a voltage divider to make sure the ESP8266 didn't get fried. The aim was to get the Arduino to talk to it and send "AT commands" to it - the default firmware on the ESP8266 makes it act as a modem which can be controlled over serial/UART. This was a long shot. The Arduino's 3.3v rail is far undersized for an ESP8266, and three devices, one of them at a different voltage, on a single serial line, is asking for undefined behavior.

On testing, it didn't work; the ESP8266 never joined my WiFi network as it was supposed to. The Arduino was printing AT commands fine though (I could only sniff the Arduino's output because that is where the FTDI's rx is wired to). The setup not working wasn't surprising because it was a terrible way of doing things and there were too many unknowns.

Trying to make do without having to order then wait weeks for the proper parts off eBay, I forgot about talking to the Arduino, and tried to communicate directly with the ESP8266 through a serial monitor on the computer. To do this, the ESP8266's rx needs to be wired to the Arduino rx pin and tx to tx. The idea was to have the ESP8266 talk to the FTDI adapter and have the Arduino's ATmega328 remain silent. From memory, I think this worked just well enough to flash the chip, but commands and such often failed and caused resets because the Arduino's 3.3v regulator couldn't supply enough current. That sort of explains the first setup not working either.

This is the point where the ESP8266 went into storage for a long time.


Renewed interest:

The ESP8266 has become much more powerful and flexible with the release of its SDK, so I gave it another shot. I setup the SDK on Ubuntu in a virtual machine, I would highly recommend this; it works very well with USB pass through, and Linux is a joy to develop on (in my opinion). Also, I made a programming/breakout board for the module, otherwise it's a bit of a PITA as it can't be plugged directly into a breadboard.


The breakout board simply passes through all the pins to a header, but CH_PD (chip power down) and RST (reset) are pulled high through 4.7k resistors; this is a pre-requisite for normal operation (I'll go into the ESP8266's strange boot modes a bit later on).


The jumper on the breakout board, when inserted, pulls GPIO 0 to ground. This boots the ESP8266 into flash programming mode when the chip is reset by pulling RST low or by powering the chip off and on. To change the jumper, the ESP8266 has to be removed and then reinserted, this became a pain, so I started temporarily sticking a wire where the jumper would go and then briefly tapping a lead connected to RST to GND. The TX LED on the ESP8266 module would flash very briefly when RST was grounded, then stop completely - this behaviour indicates (not with certainty) that the chip is booted in flashing mode and is waiting for a program to be sent over serial to it. GPIO 0 can be un-grounded at this point. 

If you were to be listening to the serial port at this point, you'd see a bunch of garbage, unless your baud rate was set to 74880. This is the baud rate which the ESP8266's bootloader prints a bunch of information at on boot. One important piece of information is boot mode. Boot mode (1,6) or (1,7) indicates the module is in flashing mode. Boot mode (3,6) or (3,7) indicates it will load and run the program stored in flash. Listening to the ESP8266's serial output is the most reliable way to verify if it has booted into UART flashing mode properly, rather than trying to judge the board's TX LED.

It is important to leave GPIO 2 floating or pull it high (it has an internal pull up resistor anyway) whenever you reset or boot the ESP8266, otherwise it goes haywire, possibly trying to boot into SD card mode - but of course there is no SD card. This advice stands whether you are trying to boot into flashing mode or run-from-flash mode. GPIO 0 and 2 act as perfectly normal IO ports after booting, but the fact that they are so important at reset and when power is applied make them hard to use in any permanent application if you want to attach sensors. For example, with this project, I had to detach the 433MHz radio receiver from GPIO 2 every time the ESP8266's bootloader had control (on resets or power applied) then reconnect the receiver once booted.

This is the radio receiver I had lying around and used. It comes with a matching 433MHz transmitter, but I didn't need it.  The receiver has two data pins which are connected together; I'm not sure of the point of doing this. Anyway, the module drives the data pin high when it detects any carrier being present in the vicinity of 433MHz, and low when there isn't.

An antenna has to be soldered onto the board, or the reception range is merely a few centimetres. For 433MHz, a quarter wave monopole should be about 17 cm long. Use solid core wire for an antenna.
The 433MHz receiver module is regenerative one, rather than being a more expensive heterodyning one. From what I can tell, it has two stages of RF tuning and amplification: the first being being wide band and the second being more selective. Then the signal is fed into an op-amp, which I guess provides the massive gain which makes the output seem digital: high when a carrier is present, and low when there isn't.


This is an example of a 433MHz door/window sensor which the receiver can listen to. The door sensor simply chirps an ID repeatedly for a few hundred milliseconds when the magnet is taken away. The short transmission occurring only on triggering is pretty much a requirement for saving battery and not hogging the shared frequency (there is no collision avoidance or re-transmission). The downside is that the base station has to keep track of the state of all sensors and not miss any transmissions. Also, the battery in a sensor can die without the user knowing. This system would also be easily jammed or otherwise hacked, so these alarms are just toys, but that tends to be the nature of wireless - wireless on the cheap, anyway. These observations came from listening in with an RTL-SDR dongle, which I'll go into more detail on later.

The sensor's PCB is mostly un-populated. The 433MHz oscillator on the top right and the sheer lack of components (especially RF ones) hinted that data modulation wouldn't be much more sophisticated than switching a 433MHz carrier on and off like a Morse code transmitter. The name for this is on-off keying (OOK), which is a subset of amplitude-shift keying (ASK). The datasheet for the HS1527 (the IC on the sensor board), would've confirmed this guessing, had I had it at the time.

So here's an annotated screenshot from SDR Sharp, tuned in to the 433MHz area. I had just triggered a door sensor and a PIR sensor, which can be seen on the waterfall chart. Just quickly, the whole RTL-SDR project is amazing - it's made radio affordable to get into and incredibly flexible. Definitely check it out if you're interested even slightly in radio or what $15 of technology can do.

Signal 1. I figured was unrelated, or at least irrelevant, because it occurred every ~20 seconds and was comparatively weak. The weak signal suggested it came from farther away than the other two signals. Additionally, if it came from a door or PIR sensor, then I would have seen more than one instance of this signal, as there are about 8 sensors around my house. I wondered if it was the base station, but what use is it having the base station transmit when none of the remote sensors have receivers in them? Anyway, I was intending to bypass the existing base station.

Signal 2. and 3. appeared when I triggered a PIR and then a door sensor by walking along a corridor, stopping, opening a door, then walking back. So the burst which appeared twice must surely be from the PIR, having detected movement twice, leaving the other signal to be the door sensor.

Additionally, it was apparent that all the transmissions came from transmitters which must have cost the lesser part of a few dollars in RF-related components. It was something between the massive bandwidth the signals occupied, the ever-so-slight frequency drifting and how far off the labelled oscillator frequency of 433.92MHz they were. But the spread in frequency just made it easier to tell sensors apart from a glance in SDR Sharp, so it's not all bad.

The next step from here was to record the signals as audio and take a closer look in Audacity. I set SDR Sharp to AM demodulation and set a decent squelch, so noise wouldn't appear between transmissions in recordings. I made audio recordings on the lowest sampling quality setting. On-off keying is a subset of amplitude modulation (but with only two signal levels), so this was most appropriate.

Above is one of the signals, shown in Audacity, zoomed in on a single packet from a much larger transmission. A typical transmission is made up of the same packet repeated over and over again. This is done so that a receiver's AGC and noise suppression have time to respond (if present) and also to provide some noise immunity. Much like the output of the hardware receiver the ESP8266 was connected to, we see a high when the transmitter's oscillator has power and a low when it doesn't. In short, the waveform shows the state of the output pin of the HS1527 encoder inside a sensor over a period of time.

The datasheet for the HS1527 indicates that in its on-off encoding scheme, a short pulse is a 0 and a long pulse is a 1, additionally, the unique identifier each chip spits out is 20 bits long. Below is the output of the 433MHz receiver module; it shows a different sensor from the one in the Audacity screenshot transmitting, hence some of the differences:
This output on the oscilloscope was promising because it looked exactly like what the RTL-SDR snifffed, showing that the 433MHz receiver was receiving the sensors well. The software decoder I wrote for the ESP8266 works by interrupting on a rising edge, then on a falling edge, and so forth, to measure the duration of pulses and gaps between them. Measuring the gaps, in addition to the pulses, turned out to be useful for detecting the end of a packet or a whole transmission and for rejecting non-conforming signals.

The packet on the oscilloscope screen, despite its similarities, is a different length to the one recorded in SDR Sharp. This is because, in the 10ms radio silence between packets, the RTL-SDR's AGC re-adjusted, raising the noise floor, which triggered the squelch (seen in the recording where the slight noise disappears completely). This would then have had to all be undone when transmission resumed, hence why the first two bits are missing in the Audacity screenshot. Set a fixed gain and turn squelch off if you want to record in SDR Sharp. I didn't even notice at the time.

But back to analysing the packet captured on the oscilloscope, which is actually complete... The datasheet for the HS1527 says that: a short pulse is a zero; a long pulse is a one; a short zero pulse with a slightly longer gap afterwards is a pre-amble bit. This works out well: the pulse at the start of the packet is a pre-amble; then there are 20 bits, which is the sensor ID; then there are the last 4 bits, which supposedly indicate the state of four digital inputs on the chip.

When I wrote the ESP8266 OOK decoder, I didn't know what was inside the sensors, let alone have the datasheet for the HS1527 chip. I mistakenly treated the whole packet, including pre-amble and digital input state, as the sensor ID. Luckily the pre-amble is constant and the digital inputs never change for these simple sensors (they aren't connected to anything), so I got away with it.

Above is a packet from signal 1. received through the 433MHz receiver module. Even though it wasn't relevant to this project, I had a look at it anyway. At first glance it looks like it might use the same on-off keying scheme as the sensors in my alarm system, but it is actually encoded using Manchester encoding from what I can tell. If you try interpreting this signal using the same coding scheme as the previous signal, you might notice that the length of time each apparent bit takes up is varying, i.e. pulses aren't padded predictably with gaps. This is odd and tends to indicate that the signal had been misinterpreted because data streams usually have a constant data rate.

These two captures show the typical noise which appeared on the 433MHz module's output when there was no transmission present. Though this noise is easily rejected in software because it doesn't conform to what an expected OOK signal looks like, all the edges unfortunately create a lot of interrupts for the ESP8266 to deal with. It did not cause problems, from what I could tell, but if it did, the number of edges could be reduced with a small capacitor between the data line and ground. The capacitor would need to be connected to the output via a resistor to increase charge/discharge times, and then the ESP8266's GPIO would have to be connected to the junction of the resistor and capacitor.

More noise - in more detail.

Above: the ESP8266 all connected up. The board at the top is a Zigbee CC2530 development board. I discovered it lying around and detached the Zigbee module to reduce power usage. It was perfect for the ESP8266 because it has a CP2102 USB-to-UART/serial converter on it, a beefy 3.3V rail and a 5V rail fed from USB power or the 3xAA battery pack on the bottom of it. Importantly, all the buttons, LEDs, UART pins and power rails come out to header pins so they were easy to connect over to my ESP8266 programming board. Also, it's hard to see in the photo, but I added a decoupling capacitor between the ESP8266's VCC and GND, anecdotally it seemed to improve it's accuracy when flashing and stability in general. I did this because a few things made me suspicious that my programs were getting corrupted when I flashed them.

The grey wire and green wire left dangling grounded RST. A reset button on the programming board would have been much better. Also, another annoyance was having to disconnect the 433MHz receiver every time the ESP8266 was reset or turned on (I've mentioned this quite a few times now), hence the connection via an orange jumper and blue jumper, which made for easier disconnection.

The circuit above didn't work well though: the 433MHz receiver module's reception range was atrociously short because I was powering it off 3.3v so that I wouldn't have to level-shift its output for the ESP8266 and then have two power rails if I made a dedicated board for this project in future. The 433MHz receiver is designed to be powered off 5V, but I tested it briefly with 3.3V on the oscilloscope and it seemed fine. Granted, it was being tested with a nearby sensor. Later, I switched to powering it off 5V and used a voltage divider on the output; this improved reception dramatically, as expected. 

Results/final product:

On startup, or when there are no triggered sensors:

When sensors have been triggered:

The source code for this project is here and could be useful for finding examples of how to use the ESP8266's interrupts and create a simple HTTP web server on it. The ESP8266's SDK is slightly thin on coherent documentation, but looking through other people's code and the SDK's header files is enough information to figure most things out.

Other than that, be mindful of clearing dynamic memory as soon as possible, and be careful where your pointers point before passing them to functions or otherwise using them. If you try writing or reading from a memory address which is obviously invalid e.g. outside dynamic memory, the ESP8266 tends to catch it and print some debugging information then reset itself, like this: 

Fatal exception (29):
epc1=0x400127f0, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000064, depc=0x00000000

Exception 29 indicates that the program tried to write to a prohibited memory address. This is similar to exception 28, which results from trying to read a prohibited address. In this particular exception, the program instruction at 0x400127f0 has tried to write to 0x00000064... epc1 and excvaddr are the two most important details.

The rest of my run time debugging consisted of os_printf statements and commenting out lines.

An important consideration when working with the ESP8266 is that it has a whole bunch of internal registers which aren't changed when you flash a program to it. For example, WiFi credentials, station mode and whether to automatically reconnect to WiFi. These are modified at run time by making calls in your program to functions such as wifi_station_set_auto_connect(1);. If you are not explicit in setting or disabling settings such as this in your program, expect undefined and inconsistent behavior. It also appears that sometimes these persistent internal registers get corrupted or misconfigured, which apparently results in the alarming but not-always-fatal error MEMCHECK FAIL!!! being printed. I found that I stopped getting this message after upgrading the version of the SDK I was using.

It felt like there were endless traps and general weirdness with the ESP8266, but it is possible to do useful stuff using it and its SDK. 

Friday, 5 February 2016

LED Night Lamp

A bit more of an artistic project I had a play round with to make use of a 10W LED and driver combo I got off eBay. The files are here: http://www.thingiverse.com/thing:1318807

The bottom piece was designed in OpenSCAD, the top piece - the "box" - was designed in Blender because it was originally meant to have complex patterns on the inside which would only be visible when lit.

Finished product:

The 10W LED is still surprisingly bright when covered, the colour output of the LED is white but the plastic box unfortunately yellows the light a lot.


Make sure you fit the hidden captive M3 nuts on the back side of those screw holes. This can be done by poking a long M3 screw through the hole, threading a nut on and then pulling the nut down into the nut trap. If the nut trap is undersized, heat the nut up with a soldering iron then quickly pull it into the nut trap.
Small 12v switch mode PSU on the left with a ~0.8A current limiter integrated which is important for powering LEDs and not having them burn up due to their non-ohmic behavior. The quality of the PSU isn't bad either, there's filtering and proper separation between high and low voltage, I didn't inspect the transformer's wingdings but I hope it's of the same quality inside as the rest of the board. The LED "chip" is on the right.

The makeshift bending process for the dual aluminium heat sink/outer casing. Had to keep that protective wrap on till the end so it didn't scratch! Those edges where the wrapping is curling up did get scratched though - turns out toothpaste is a scarily effective (and still pretty coarse) abrasive if you don't have anything else to polish with.

Oops... something went wrong, but this wasn't totally unexpected given the tooling. More important than the total height of the base or the slightly mis-matched bend radii was getting the sides level so I cut down the left side.
Here it is drilled and re-sized through much chiseling, and sanding. The amount I had to take off was a bit small for a hacksaw, too large to sand and don't even think about grinding aluminium!

Make the aluminium outer first and see how it comes out then modify the printed base to suit the errors - that's why it's parametric. In this case the radius has been made larger on one side and the overall height reduced. Some gaps are visible, and the aluminium isn't flat in the middle so viewing distance, angle and favourable lighting are late additions to the BOM.

All wired up, the PSU is hot glued to the base because it has no mounting holes anyway. Since the device runs on 240V mains and has exposed metal, a three pronged plug and lead with earth are crucial - even more crucial is to connect the ground lead to the metal casing: note the bottom right screw pillar which was made slightly shorter to allow room for a connection. I'm not an electrician or nuanced in electrical codes - so wiring is your own responsibility and most LED drivers from China aren't certified either. I don't leave this light running for long periods of time unattended.