CC Blog Projects Research & Design Hub

Smoke Alarm Monitoring

Written by Brian Millier

With an ESP32

While machine learning algorithms on microcontrollers (MCUs) are getting popular, for the purpose of identifying a sounding smoke alarm, there is a much easier way to do it. In this column, Brian uses an I2S microphone and an ESP32 to handle the task.

Lately, there has been a lot of interest in voice/image recognition being done locally on modest microcontrollers (MCUs). Some of this is promoted by Edge Impulse and tinyML. I’ve even seen press releases from Aspinity, which has designed a sound recognition engine that performs its task in the analog domain. They claimed that this could be performed in the analog domain using much lower power consumption, and cited glass-break detection as a suitable application. It was an interesting concept, but while their website showed an evaluation board, it contained no information on what toolchain would be needed to develop an application using the device.

However, seeing that led me to revisit an idea that I had played around with in the past. I felt that it should be possible to monitor the sound in a room and distinguish when a smoke alarm was sounding. It’s impossible to ignore this shrill sound if you are at home when it occurs, but what if you’re away? I thought it would be useful to receive a real-time notification of such an alarm situation on the cell phone that I carry all the time.

I figured that I would need a microphone and whatever circuitry was necessary to convert the smoke alarm’s audio signal into a digital one that an MCU could handle. The MCU would require Wi-Fi capability to be able to contact a cloud service that could issue a notification to my iPhone. Alternately, it could have Bluetooth capability and connect to a home server acting as a gateway to the internet. It would be a bonus if I could build the device with low-enough power consumption to be powered by a small battery, but this wasn’t a deal breaker. There are plenty of power outlets within hearing range of the smoke alarms in my house.

I happened to have on hand an ESP-EYE board, made by Espressif (the company that designed the ESP32). It had a digital (I2S) microphone on-board. While I used this board for my proof of concept, it wasn’t designed to be easily physically mounted in an enclosure. Also, it had a camera on-board that was not needed, and which used up virtually all of the spare GPIO lines, some of which I would need for my own purposes.

However, using the ESP32-EYE, I was able to develop the software needed to try out the concept. As I’ll describe later, I switched to a bare ESP32 module and a separate digital microphone module for the final project.

For this project, I envisioned two operating modes:

  • A Learn mode in which the unit would listen for the presence of the smoke alarm’s sound and determine the dominant frequency present in the sound using an FFT routine. It would then store the FFT bin number of the dominant frequency in the ESP32’s flash memory, using an EEPROM emulation library.
  • An Operating mode in which the unit would periodically “listen” to the microphone’s signal, and then run it through the FFT routine to determine the dominant frequency. If the FFT bin number matched, and the duration of the sound exceeded a threshold, an alarm condition would be set, and a notification sent to my iPhone using the ESP32’s Wi-Fi function to connect to a cloud server on the internet.
HARDWARE IMPLEMENTATION

While I have used many different ESP32 development modules in the past, for this project, few GPIOs were needed. I settled upon the tiny ESP32 M5Stamp Pico. Figure 1 is a picture of this module with all of the pinout details added. This module has enough GPIO pins to handle an I2S microphone as well as the switches needed to reset and flash the device and the two mode switches. Unlike many ESP32 development modules, this module does not contain a USB-Serial bridge chip. This is OK, since it reduces the module’s overall power consumption.

FIGURE 1
This is the ESP32 M5Stamp module that I used in the project. It is about as small as you can get while still having enough GPIO for the project.
FIGURE 1
This is the ESP32 M5Stamp module that I used in the project. It is about as small as you can get while still having enough GPIO for the project.

Figure 2 is a schematic of the unit. I chose an Adafruit SPH0645LM4H I2S microphone module for the project. This module contains a high-quality microphone element, a Sigma-Delta ADC and an I2S output bus. It’s made by Knowles, who are well-known in the acoustic industry. While the SPH0645LM4H’s data sheet doesn’t specify the bit resolution of the internal ADC, the output is sent as a 24-bit I2S signal. In practice, when you monitor the values coming from this microphone, they are huge numbers, and I believe that the ADC’s 24-bit output is left-justified in the 32-bit I2S field for which the ESP32 library is configured. This module has virtually flat frequency response up to 5kHz with the 1.25MHz I2S bus clock used in this project. This is ideal as the standards for smoke alarms (not found in sleeping quarters) is a 3150Hz tone. Along with the standard 3-wire I2S bus, this module has a SEL pin which selects whether the unit transmits data as left channel or right channel (the I2S protocol is stereo/two-channel). So, you could connect two of these units to a single I2S bus and get stereo operation by setting the SEL line of one unit to GND and the other to the SPH0645LM4H’s VCC. In this project, I connect the SEL line to GND, which results in data being sent when the LRCLK line is low, which is technically the LEFT channel signal. However, I found that the I2S library provided by Espressif would only work with this module when it was configured with the following line:

.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,

FIGURE 2
This is a schematic of the project. While I only show a 5V 1A wall adapter as a power source, I am actually using a power module containing a LiPo cell for backup in a power-failure.
FIGURE 2
This is a schematic of the project. While I only show a 5V 1A wall adapter as a power source, I am actually using a power module containing a LiPo cell for backup in a power-failure.

I am not sure why this discrepancy in their library exists. I only discovered it while trying to get the microphone to work, by trial and error.

Note that the ESP32’s I2S block can be mapped to various GPIO lines. In this project, I am assigning them to the pins that are normally reserved for the SPI port (and which are labelled as such on the ESP32 M5Stamp PICO).

Apart from the microphone unit, the only other external components needed are four switches:

  • Reset
  • Boot SEL (for flashing the ESP32)
  • Learn Mode
  • Test Prowl (the name of the iPhone notification app)

The state of both the Learn Mode and Test Prowl switches are only sampled at reset time. However, I use the Deep-Sleep mode in the program, which wakes up and resets the ESP32 after each Sleep cycle, so these two switches will be sampled at the start of each operational cycle.

The ESP32’s RX and TX lines are connected to a 3-pin header which is used to connect up a USB-Serial port cable—only needed during development, debugging and flash programming.

The unit needs a power source of 5V at >350mA. The ESP32 M5Stamp PICO has a 5V input and contains a 3.3V LDO regulator to provide power to both the ESP32 MCU and the SPH0645LM4H microphone unit. I decided to use a 5V power module that contains an 18650 LiPo cell, charger and 5V boost converter. This contains a micro-USB socket that a 5V, 500mA wall adapter plugs into. Figure 3 shows this power module.

FIGURE 3
This is the 5V Battery Boost module that I used for the project. A 5V phone charger (minimum 500mA) must be connected to the micro-USB socket for power/charging. You could eliminate this and use only the 5V 500mA wall adapter if battery backup was not needed.
FIGURE 3
This is the 5V Battery Boost module that I used for the project. A 5V phone charger (minimum 500mA) must be connected to the micro-USB socket for power/charging. You could eliminate this and use only the 5V 500mA wall adapter if battery backup was not needed.

While I send out status messages to the serial port during the various phases of operation, the serial port won’t be connected in normal use. The ESP32 M5Stamp PICO contains an addressable RGBW LED onboard. This provides status as follows:

  • A quick green flash at Reset (which occurs at every operational cycle);
  • A flashing red signal while the unit is attempting to connect with the home Wi-Fi access point;
  • A yellow signal while the unit is connecting to the Prowl server;
  • A 5 second green signal after the Prowl server has received and acknowledged the notification request (or a red signal if the transaction doesn’t complete successfully).

Figure 4 is a photo of the finished board for the project.

FIGURE 4
This is the completed project board. The label at the left of the board is meant to stick to the top of the M5Stamp module, but it obstructs the RGBW LED, so I placed it on the board—just for a pin-out reference.
FIGURE 4
This is the completed project board. The label at the left of the board is meant to stick to the top of the M5Stamp module, but it obstructs the RGBW LED, so I placed it on the board—just for a pin-out reference.
SOFTWARE IMPLEMENTATION

I wrote the software using the Arduino 1.8.13 IDE to which I had added the ESP32 board framework. The software required for this project did not take too much time to develop because there are high-level Arduino libraries that handle:

  • A DMA-driven driver that nicely handles the I2S Microphone data stream.
  • An FFT routine to convert the incoming time-domain microphone signal into the frequency domain.
  • An API that allows the unit to easily connect to the cloud-based Prowl server.

First, let’s look at the I2S library routines. I2S support is available in Arduino C++ by adding the #include “driver/i2s.h” at the start of the program. This library is basically a straight port of the I2S API from the Espressif IDF, and it’s in a different style than what Arduino users are accustomed to. To use I2S, you must first define two structures, i2s_config and pin_config, as shown in Listing 1. The variables in the i2s_config structure are fairly self-explanatory, except that the ESP32 contains two I2S ports and we have to specify, in this case, that we are using I2S port 0. The pin_config structure is used to generate the code which configures the ESP32’s pin multiplexer to route the I2S signals to the ESP32 M5Stamp PICO’s GPIO pins 18,26 and 36 (which are normally used for the SPI port and are labelled as such on the PICO’s pinout diagram). After these structures are defined, we then call the following library routines, with pointers to the two structures:

err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);

err = i2s_set_pin(I2S_PORT, &pin_config);

Both of these routines will return an error string of “ESP32_OK” unless something is wrong, which I’ve not encountered.

LISTING 1
This is the code required to configure the ESP32 for I2S input from the SPH0645LM4H I2S Microphone.
// Microphome constantsconst i2s_port_t I2S_PORT = I2S_NUM_0;  const int BLOCK_SIZE = 512;const double samplingFrequency = 20000;esp_err_t err;	//  I2S config	const i2s_config_t i2s_config = {		.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),		.sample_rate = samplingFrequency,		.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,		.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,		.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),  		.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,		.dma_buf_count = 4,		.dma_buf_len = BLOCK_SIZE	};	//  pin config 	i2s_pin_config_t pin_config = {		.bck_io_num = 18,  // = PICO SCK		.ws_io_num = 26,   // = PICO MOSI		.data_out_num = -1,//  not used microphone only		.data_in_num = 36  // = PICO MISO	};	// Configuring the I2S driver and pins.	// This function must be called before any I2S driver read/write operations.	err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);	if (err != ESP_OK) {		Serial.printf(“Failed installing driver: %d\n”, err);		while (true);	}	err = i2s_set_pin(I2S_PORT, &pin_config);	if (err != ESP_OK) {		Serial.printf(“Failed setting pin: %d\n”, err);		while (true);	}

In operation, you have to read out the 512 samples which are being collected, in the background using DMA. This is done using the library routine i2s_read() to which is passed the I2S_PORT number, a pointer to the samples array and a block size. When this routine returns, the samples array will be full of audio samples. As I mentioned earlier, when describing the SPH0645LM4H microphone unit, it produces 24-bit left-justified values in a 32-bit array. I therefore shift all of these values right by 8 bits before passing them on to the FFT routine.

THE FFT ROUTINE

The Arduino FFT library routine works with complex input values and expects these input values to be stored in the vReal and vImag arrays. I fill the vReal array with the scaled samples array, and zero out the vImag array—since the microphone does not produce imaginary (quadrature) values. Without going into detail, when you perform an FFT on only a distinct (512-sample) block of audio data (as opposed to repeatedly performing it on a continuous signal flow), there will be serious artifacts in the frequency domain data. These artifacts can be minimized by performing the FFF.Windowing() function and selecting the HAMMING window.

Finally, since the FFT routine produces two output arrays (real and imaginary), the FFT.ComplexToMagnitude() function is used to combine the two arrays into a single magnitude array. This involves taking the square root of the sum of the squares of the real and imaginary values. I have used such FFT routines with software-defined radio (SDR) software—in that case both the real and imaginary FFT input values are provided by a quadrature detector, and both are meaningful. In this case, the microphone is not providing an imaginary signal, so I’m not really sure if the FFTComplexToMagnitude() function is needed, but it was used in an example program I checked out, so I left it in place.

Since the sampling rate is 20,000 samples/second, and the FFT block size is 512 samples, the bin size of the FFT output is 20000/512, or 39Hz. In other words, the frequency resolution is 39 Hertz. An average smoke alarm will emit a signal close to 3150Hz, which will land in bin 80 or 81.

In the Learn mode of the program, I scan all of the bins and pick the bin with the highest value (which represents the dominant alarm frequency). The first 10 bins are ignored as they correspond to frequencies below 400Hz—a much lower frequency than any alarm transducers emit, and susceptible to false detections due to everyday noise. That bin value is stored in non-volatile EEPROM memory (which is emulated in program flash by the Arduino ESP32 Preferences class). Also, the detected amplitude, at that frequency, is stored in EEPROM.

To enter the Learn mode, the Learn switch must be activated at reset time. The smoke alarm must still be sounding when you deactivate the Learn switch—it is the last sampling that has been taken before the switch is deactivated, that is stored as the valid smoke alarm frequency. If a serial terminal is connected to the project during learning, it will print out both the FFT bin number and amplitude that has been saved to EEPROM.

In operational mode, only the FFT bins within ± 2 bins of the “learned” dominant frequency bin (as retrieved from the EEPROM) are examined. If any of those bins contain at least 50% amplitude of the learned value, it is assumed that the smoke alarm has sounded, and an alert must be generated. This allows for a slight variation in the smoke alarm’s siren frequency, as might occur with temperature changes. During the Learn mode, you would want to be about the same distance from the smoke alarm as the unit would normally be situated, so the amplitude criteria would be met.

THE PROWL CELLPHONE APP

The three ways that I know of to produce a real-time notification on an iPhone:

  • Send an email to my email account.
  • Send an SMS message to my cellphone number.
  • Use the Apple iOS Push notification system.

In the past I have used the email option, using the Gmail SMTP server. Increasingly, Google has had to strengthen the authentication methods needed to access their SMTP server, and it became impossible (or at least inconvenient) to use this method. I also used the IFTTT cloud service for a while—it would send out SMS messages when you accessed a web-socket that you had previously established with IFTTT. Accessing this web-socket was no different than accessing any website, and was easy to do with the ESP32, running an Arduino sketch. However, IFTTT killed that service quite a while back, and whatever they replaced it with was not practical for me.

That left the Apple iOS Push notification system. Note that this is an internet-based protocol—it’s not an SMS message that is delivered via the cell phone infrastructure. Therefore, your iPhone must have internet connectivity at the time of notification—not just an active cell phone tower connection.

I discovered the Prowl iOS application, which provides an iOS notification service which is triggered by an incoming message over the internet. There is an Arduino library which allows an ESP32 to interact with Prowl’s API and send a topic string and notification message string to the target iPhone.

Unlike many commercial cloud services, Prowl does not impose a terribly complicated login/authentication procedure to access your Prowl account. You must go to the App Store and purchase and install the Prowl app on your target iPhone. You then must set up your account and supply your iPhone’s cellphone number, and so forth. You may then obtain a 40-digit API key which is generated on the Prowl website. Your ESP32 program only has to call a few Prowl API functions using this API key and your subject and notification message strings. Note that the Prowl server will send back an acknowledgment status message, allowing the ESP32 to know it has gotten through. Listing 2 shows all that is necessary to send a Prowl notification (assuming that the ESP32 has already made a Wi-Fi connection).

LISTING 2
This is all of the code necessary to contact the Prowl server and deliver a push notification to the iPhone. Note that my API has been blanked out.

EspProwl.begin();  // For Prowl, go to  //   https://www.prowlapp.com/api_settings.php  // to create an API key.  // If you don’t, the server will return a 401 error code.  EspProwl.setApiKey(“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”);  EspProwl.setApplicationName(“EspProwl”);  Serial.print(“Sending push notification...”);  int returnCode = EspProwl.push(“Smoke Alarm sounding”, “From #1”, 2);  if (returnCode == 200) {     Serial.println(“OK.”);       leds[0] = CRGB::Green; FastLED.show();     } else {     Serial.print(“Error. Server returned: “);     Serial.print(returnCode);       leds[0] = CRGB::Red; FastLED.show();     }

The whole process is simple. Setting up your account, defining the API key and running an example program from the Prowl ESP32 API library, you can receive your first ESP32-triggered Prowl notification in less than 15 minutes!

There is no monthly/yearly charge for this service. Instead, you pay $3 USD to purchase the app on the App Store, and that covers the service as well. With your account, you can generate numerous API keys if you want to connect a number of different IoT devices performing different functions. If you wanted to push notifications to more than one phone, you would need to buy the Prowl app on each phone and set up the API key accordingly.

When a Prowl notification comes in, it will show up immediately on your iPhone screen—both the topic and the notification message. If you open up the Prowl app, it will show a chronological list of all past notifications, which can be cleared.

You don’t have to actually open the Prowl app in order to receive notifications. It runs all the time in the background. I use an iPad, and it works on that as well. The Prowl service has been running now for at least 13 years, so I am not worried about it disappearing any time soon.

POWER CONSUMPTION

Having built several ESP32-based IoT devices, I knew that this project would not lend itself to stand-alone battery operation. This is due to the fact that:

  • I want to monitor for the smoke alarm sounding fairly often (I chose every 120 seconds).
  • One must spend 6 seconds or more “listening” for the 3000Hz tone to ensure that it is long enough in duration to eliminate erroneous detections from random sounds/noise.
  • The ESP32 is not a very low-power device. One can basically ignore the high power that it draws when connecting to a Wi-Fi access point and contacting the Prowl server: this would only happen infrequently—hopefully never! However, the current that the ESP32 draws during the normal monitoring cycle (every 120 seconds) is significant.

Figure 5 shows the screen capture I obtained when using my Joulescope JS220 to monitor the unit’s power during the monitoring cycle. It shows five of the sound collection events which occur during every operational cycle. This shows an average current of 33mA over 64.3ms (the region bounded by the two green markers). There are 100 of these events and the full monitoring cycle is 6.43 seconds.

FIGURE 5
This is the Joulescope readout of the current consumption during a part of the active monitoring cycle.
FIGURE 5
This is the Joulescope readout of the current consumption during a part of the active monitoring cycle.

Figure 6 is another such measurement taken during the 120 second Deep-sleep interval. This is measured at an average of 0.375mA and is made up of both the ESP32’s Deep-sleep current and the quiescent power consumption of the SPH0645LM4H microphone.

FIGURE 6
This is the Joulescope readout of the current consumption during the Deep-sleep phase of the cycle.
FIGURE 6
This is the Joulescope readout of the current consumption during the Deep-sleep phase of the cycle.

Plugging the above figures into an online power consumption calculator, it looks like the 2500mAh LiPo cell that I am using should be able to run the unit for about a month if a power failure occurred. When doing the power consumption calculations while I was writing the article, it occurred to me I could have reduced the Deep-sleep current a bit by allowing the ESP32 to power-down the SPH0645LM4H during Deep-sleep. In this case, I didn’t feel that was worthwhile.

CONCLUSIONS

A year or more passed between the time when I did the proof of concept using the ESP32-EYE and when I got back to doing this project. In the interim, I had also toyed around with the idea of doing the same monitoring function with an analog electret microphone/preamplifier and an AVR ATtiny85 MCU. The idea here was to monitor the audio signal’s zero-crossings with the ATtiny85’s comparator block and to use one of its timer blocks to determine the time between zero-crossings. From that, I would derive the incoming sound’s frequency and compare it to that of the smoke alarm. To make a long story short, I found that the only electret microphone/preamplifier combination that would work anywhere decently was one that contained a MAX9814 —which contained an automatic level control (ALC). However, merely measuring zero-crossings to derive a frequency didn’t turn out to be reliable enough when I tried it with an actual smoke alarm signal. Therefore, I went back to my original idea, and built this project.

While I haven’t experienced it, I suppose that the beeper in a microwave oven or other such kitchen appliance might have a frequency which matched that of a smoke alarm. However, you would have to be at home to be using those appliances, and if a false notification was sent to your phone, it would be easy enough to dismiss it. 

— ADVERTISMENT—

Advertise Here

REFERENCES
[1] Brian Millier, “GUI-O: A “Virtual” Front Panel For ESP32 Projects.” Circuit Cellar 389, December 2022.
[2] App Inventor Particle Photon Test on GitHub: https://github.com/TeamPracticalProjects/MIT-App-Inventor-Particle-Photon-test
[3] Brian Millier, “PICKING UP MIXED SIGNALS: Particle IoT Platform Update: Part 1.” Circuit Cellar 402, January 2024.
[4] DFRobot ESP32-C3 Beetle module (DFR0868): https://www.digikey.ca/en/products/detail/dfrobot/DFR0868/16678683?s=N4IgTCBcDaICIDEBKAGAHANjSAugXyA
[5] DFRobot DFR0478 FireBeetle: https://www.digikey.com/en/products/detail/dfrobot/DFR0478/7398878
[6] Pulse Electronics W3334B0100 External Bluetooth Antenna: https://www.digikey.ca/en/products/detail/dfrobot/DFR0868/16678683?s=N4IgTCBcDaICIDEBKAGAHANjSAugXyA
[7] Particle Tools—Particle Docs: https://docs.particle.io/tools/tools/
[8] Brian Millier, “PICKING UP MIXED SIGNALS: Easing into the IoT Cloud (Part 2): Modules in Action.” Circuit Cellar 342, January 2019.

SOURCES
ESP32 M5Stamp PICO: Digi-Key Part Number 2221-K051-ND
Datasheet: https://media.digikey.com/pdf/Data%20Sheets/M5Stack%20PDFs/C050-B_K051_K051-B.pdf
SPH0645LM4H I2S Microphone breakout board: Adafruit product ID 3241
Datasheet: https://www.knowles.com/docs/default-source/default-document-library/sph0645lm4h-1-datasheet.pdf
Battery Boost Module/charger module: https://www.amazon.ca/Battery-Module-Lithium-Charging-Protection/dp/B089KNW6N9/

RESOURCES
Adafruit | www.adafruit.com
Espressif Systems | www.espressif.com
Knowles | www.knowles.com
Prowl | www.prowl.com

Code and Supporting Files

PUBLISHED IN CIRCUIT CELLAR MAGAZINE • MAY 2024 #406 – Get a PDF of the issue

Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.


Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Brian Millier runs Computer Interface Consultants. He was an instrumentation engineer in the Department of Chemistry at Dalhousie University (Halifax, NS, Canada) for 29 years.

Supporting Companies

Upcoming Events


Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2026 KCK Media Corp.

Smoke Alarm Monitoring

by Brian Millier time to read: 17 min