Use Cypress Solar-Powered BLE Sensors, an Arduino UNO, and an HM10 BLE module to make your own multi-node temperature and humidity monitor and display.

Previously, we presented an overview of the new solar BLE beacons from Cypress Semiconductor. It is recommended that you read that article first to provide some background on BLE beacons in general, and the Cypress solar beacons, in particular.

In this project, we will test drive the beacons by building an Arduino-based device to display temperature and humidity from two solar beacons.


The Cypress Solar Beacons.


Project Requirements

  • Two Cypress Solar-powered BLE temperature and humidity sensors
  • Arduino UNO
  • I2C LCD display (2 × 16)
  • HM-10 or HM-11 BLE module
  • Weather-resistant sensor holder (optional, see text)
  • Coin cell battery with holder (optional, see text)


Beacon Advertisement

To monitor temperature and humidity, we need to 1) receive a beacon advertisement transmission, 2) identify which solar beacon the advertisement came from, and 3) read the temperature and humidity values embedded in the advertisement.

The figure below contains information about the contents of the solar beacon advertisement and some default values. Some of these fields can be programmed and others will vary, such as temperature and humidity.


Solar beacon advertising fields. Portions of the figure courtesy of Cypress Semiconductor.


Only the following fields are used in the project:

  • UUID - We will use the default value for the universally unique identifier (UUID) to identify any solar beacon. The default value should not be changed for use in the project.

  • Major - The two-byte major ID field will be used to identify which solar beacon initiated the advertisement. This field should be programmed to correspond to the sensors in use. In this regard, the included code uses sensor values of 0001 and 0002. My sensors came programmed and labeled with consecutive major ID values.

  • Humidity - One-byte value representing the sensor humidity (RH) measure. The calculation formula is: RH [%] = 125 × (HUMIDITY × 256) / 65536 – 6. As noted in the Si7020 datasheet (see section 5.1.1), when conditions approach 100% or 0% humidity, it is possible to receive humidity values above 100% or below 0%, respectively, due to sensor variations in accuracy. The datasheet suggests that when this occurs, it is appropriate for the host software to limit the humidity range to 0-100%. No artificial limits are used, however, in the included software, although this could be easily added to the code.

  • Temperature - One-byte value representing the sensor temperature measurement (in Celsius). The calculation formula is: TEMP [C] = 175.72 × (TEMPERATURE × 256) / 65536 – 46.85.


Arduino-Side BLE Module

In order to read the solar beacon advertisements, we need a BLE module on the Arduino side. In this regard, the beacon acts, essentially, as a peripheral device and the BLE module on the Arduino acts as a central device, although it does not actually connect to the beacon. The Central/Peripheral as well as the Master/Slave roles for BLE devices are a bit more complicated, and you can read much more about the relationship here (PDF).

A critical characteristic of this project is that the BLE module must be capable of acting as a central device; many of the shields available for the Arduino can only act as a peripheral device. It is notable that the restricted functionality is often a result of the firmware rather than the BLE chip used on the shield. For the BLE module on the Arduino side, I chose to use an HM-10 shield from Linksprite, which can be used as a central device.


The Linksprite HM-10 BLE Shield.


The HM-10 module on the shield is manufactured by Jinan Huamao Technology and is based on the TI CC2540/1 BLE SoC.

The module comes programmed with a set of “AT” commands which allow relatively easy, albeit very limited, functionality through a serial port. The firmware version for the module that I used is V546, which was the most recent at the time of this writing. It is important that you use this version as earlier versions may not have the same commands and functions. You can see what version is installed by sending the command “AT+VERR?”.

The modules can be upgraded fairly easily. The upgrade instructions and the firmware V546 file, are available for download from the Huamao site.

As noted in the downloaded instructions, however, if the procedure is not followed carefully, you can brick the device. I upgraded my device without issue, but it is advisable to become completely familiar with the instructions before attempting the procedure.

The Linksprite shield has a couple of niceties (see schematic), such as a reset switch, an I/O logic level switch, and jumper-selectable lines for the UART. For the Arduino UNO and the project programs contained here, I used the logic level switch set to 5v and the serial lines set to “2” for TX and “3” for RX (for use with software serial). The shield is available from several sources (e.g., here), and I was able to get mine for less than US$10.

I also tested a functionally similar HM-11 module from Seeed, which I also upgraded to V546 firmware. As far as I could tell, in this project, the HM-11 board performed identically to the HM-10 shield.


LCD Display

To visualize the temperature and humidity values, I used an old 2 × 16, I2C LCD display. These devices are ubiquitous in the Arduino world and many flavors exist. The one that I used is a typical 1602-controlled LCD with an I2C daughter board. The usual connections from the board to the Arduino (+5v->+5v, GND->GND, SDA->A4, SCL->A5) are used.


The I2C LCD Display (2 × 16).


Using the LCD is facilitated by an Arduino library, and I chose to use the New-Liquidcrystal Library by F. Malparida (under the Creative Commons Attribution-ShareAlike 3.0 Unported License). The library is very well documented and allows flexibility in specifying the control lines as you can see in this fragment from the included programs:

//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol  
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

You can look at this AAC project for some background on the issue, but I only needed this flexibility because the unit I used is quite old. Newer models are more standardized and you may not need to specify this level of detail for your display.

In fact, after initializing the LCD, the only commands that I use in the included programs are “lcd.home ();  // col 0 line 0”,  “lcd.setCursor (col,line)”, and “ lcd.print(string)”, so it is likely that your I2C-enabled LCD, with whatever library you are used to using, will work in the project.


Beacon Scanning

After the HM-10 has successfully executed a few initialization commands, we can scan for beacon advertisements by issuing the “AT+DISI?” command. The command will return an ASCII string that we have to examine to determine if it has received any BLE device advertisements. The HM-10 returns the results of an advertisement in a fixed format of 66 ASCII bytes as follows.

There are five fields [P0-P4] separated by a colon [:] character.

  • P0: [8 bytes] Factory ID
  • P1: [32 bytes] Beacon UUID
  • P2: [10 bytes] Major Value [4], Minor Value [4], Measured Power [2]
  • P3: MAC [12]
  • P4: RSSI [4]

For example, the following string (with no CR/LFs) was returned after an AT+DISI? command:


The response string begins with “OK+DISIS” and ends with “OK+DISCE”. Between those elements will be whatever advertisements are received, and each advertisement will be preceded by “OK+DISC”.

In this instance, advertisements from two devices were received. One of the advertisements was from a solar beacon: ‘:4C000215:0005000100001000800000805F9B0131:00014667C3:00A0500B1710:-078’

Parsing that advertisement yields the following fields.

  • 4C000215 – [P0] Company ID
  • 0005000100001000800000805F9B0131 – [P1] UUID
  • 00014667C3 – [P2] String Containing:
    • 0001 - Major ID
    • 4667 – Minor ID containing
      •   46 - Humidity (hex)
      •   67 - Temperature (hex)
    • C3 – RSSI (from device)
  • 00A0500B1710 – [P3] MAC Address
  • -078 – [P4] RSSI (dBm)

If a device is not a beacon, fields P0, P1, and P2 will contain only zeros.


Included Software

With a detailed understanding of the challenge, an explanation of how the included software addresses the challenge is in order. Three programs are included in the download at the end of the article.

SolarBeaconScannerA.ino. Scans for solar beacons (sensor 1 and sensor 2, as per the Major field); if a beacon is found, this program displays the received temperature and humidity values via the serial monitor. At the beginning of execution, it also echoes the AT commands and responses, to and from, the HM-10 shield. If there is a failure to initialize the HM-10, the error is sent to the serial monitor.

SolarBeaconScannerB.ino. Operates identically to SolarBeaconScannerA.ino, but also sends temperature and humidity values for each sensor to the LCD.

SolarBeaconScannerC.ino. Scans for solar beacons (sensor 1 and sensor 2, as per the Major field), as do the two preceding programs, but only displays the sensor values to the LCD. Additionally, this program employs a counter to display if data from either sensor has not been updated for a selectable length of time.

The counter tracks scans (which each take four seconds, plus overhead) and when the number of scans has exceeded a set amount, the “S” on the LCD associated with the sensor is changed to a lowercase “s”. As soon as a sensor update is received, the display is reset to an “S” for the sensor.

Multiple programs are offered to aid in understanding and to facilitate the development of the project or an extension.


Screen capture from SolarBeaconScannerA.ino (click to enlarge).


***Note the initialization commands and responses at the beginning of execution. A “.” is displayed for a scan that did not contain a solar beacon. If the scan did contain an advertisement from one or both sensors, the Sensor ID along with the humidity and temperature values are displayed.

To further aid in the understanding of the code, a brief explanation for each of the functions used in SolarBeaconScannerC.ino follows:

setup() - Initializes the LCD. Initializes the software serial port and flushes the buffer.

print_lcd() - Sends the display template to the LCD.

SendCmd() - Sends the AT initialization codes to the HM-10 and looks for the correct responses.

ReadScan(unsigned long duration). Reads the output of the HM-10 through the software serial port into the global String variable, “response”. You might think that the usual code

    while (sSerial.available() != 0) {
      chr =;

would work, but what I found is that the HM-10 seems to send the string in bursts. So, if you simply read the software serial port until no characters are available and then exit, you will find that other characters are on their way, but have not yet arrived, and you get into trouble.

The tactic that I chose to implement is to keep reading the serial port for a programmable amount of time (the variable “duration”). Documentation for the HM-10 suggests that the scan window is three seconds long; I use four seconds for the value of “duration”, and in my experience this works well.

Ideally, you always want to be scanning, and the price you pay for this scheme, which includes some time in which you are not scanning, is the possibility that you can miss a beacon advertisement. When the advertising interval is only a few seconds long, this is not an issue. When the advertising interval is over five minutes, as is the case for the lowest power “timer” mode, it can be a factor. See the “isstale” global variable in the program, and below, for more on this issue.

SearchScan(). This is the workhorse. This function looks for a beacon advertisement in the response string and, if found, identifies the sensor and determines the temperature and humidity values.

Then, it searches for another instance of a beacon advertisement in the remainder of the response scan and, if found, identifies the sensor and determines the temperature and humidity values.

A good deal of parsing and conversion of ASCII occurs in this function and it makes extensive use of the Arduino String (not string) object.

The function will return a value based on what it found (0 - no advertisement; 1 - sensor 1 advertisement found and values updated; 2 - sensor 2 advertisement found and values updated; 3 - advertisements for sensors 1 and 2 found and values updated).

loop(). After calling SendCMD() to initialize the HM-10, it loops through three sections: 1) sends the “AT+DISI?” to request a scan, 2) calls ReadScan() to get the response string, then 3) calls SearchScan to process the response string.

If a sensor has been updated, it displays the new data on the LCD.

It also counts the scans since an updated reading and compares the value to a programmed value (in the global variable “isstale”). If the number of scans without a sensor update exceeds the programmed limit, the LCD display for that sensor is modified. That is, “S” on the display is changed to an “s”, indicating stale data. When the data are updated for the sensor, the display is changed back to “S”.

It is important to have some way to detect when the program has stopped receiving advertisements. This can occur if the beacon has stopped transmitting or if there is some kind of interference. The measure is also useful if you are using the beacon’s timer mode where the scanning interval is over five minutes.

Using a value for isstale equivalent to 16 minutes as a default—16 minutes corresponds to missing three consecutive advertisements at the long interval rate, which is 5.17 minutes—I would, on very rare occasions, see the occurrence of “stale” data. Subsequently, however, the display would refresh, but it is worthwhile to report the occurrence. When using the beacons in demo mode, with sufficient light, this never occurred because the advertising intervals are much shorter.


LCD screen output from SolarBeaconScannerC.ino. Temperature (T) is in Fahrenheit.


Making a Beacon Weather-Resistant for Outdoor Use

At this point, the project presentation is basically completed; it will read and display data from two sensors that you can position at different inside locations. I wanted to stretch the application a bit by seeing if I could outfit one sensor in a container that would make it usable outdoors.

To allow a solar beacon to be used outdoors, I needed to make it weather-resistant. Note that it does not mean it is weatherproof or waterproof. The requirement was most challenging for several reasons.

First, I am notably lacking when it comes to constructing any kind of package for electronic devices. I simply lack the skills and am in awe of those who possess them.

With that self-deprecating qualification out of the way, the second challenge is to maintain the solar cell capability. That is, the sensor container has to allow access to light. Furthermore, it is desirable to keep the temperature sensor out of direct sunlight.

The final and most difficult challenge is that the sensor container must allow enough airflow to sense humidity while not allowing exposure to water in the form of rain or snow. To get a reasonably accurate humidity measurement we need a certain amount of airflow around the sensor. Therefore, the sensor container cannot be airtight. 

With these requirements in mind, I decided to repurpose the shell from an inexpensive commercial solar-powered LED lawn light. I picked mine up on sale at an AJ Moore craft store (2 for US$3); it looks very similar to devices I have seen online, such as this one here.

I basically removed the inside contents (adding to my junk box of components) and also removed the metal wrapping. I mounted the sensor on a piece of circuit board, with a coin cell battery (CR2025) underneath (see Part 1 for making the sensor a hybrid). The sensor is mounted to the circuit board using mounting putty which makes it easy to position it such that the temperature and humidity sensor is at the edge of the window, keeping it away from direct sunlight. Then I mounted a piece of mylar sheet over the window and glued it in place twice, once with a contact cement and then with a silicone sealant. As you can see from the picture below, it is not pretty.


The unmodified container (A), the inside with mounted solar beacon replacing original parts (B), and the modified and sealed container (C).


Be forewarned, your results may be different, but at the time of this writing, mounted on a porch railing, it has performed well for about two months, including multi-day snow/sleet/rain/wind storms. It is notable that during times of prolonged high humidity levels, such as 3-4 consecutive rainy days, the polymer sensor film inside the sensor can become wet and produce inaccurately high humidity values. This is a known characteristic of the sensor (see sections 4.3 and 4.6 of the Si7020 datasheet). It is recommended that a baking procedure be used, but it is also noted that accurate values will return over time under less extreme humidity conditions. I have noticed this wetting to occur and, while I didn't specifically use the baking procedure, I simply took the sensor indoors and placed it near a strong light for a few hours, and that seemed to work. I have also seen the humidity values appear to recover left alone after a day of drier conditions.


Neither snow nor rain nor heat nor gloom of night will prevent my beacon from advertising (with apology to the USPS).


Closing Thoughts

My impression of these solar beacons remains favorable. This project demonstrates that the "other side" of the BLE communication can be implemented without too much difficulty or expense and without using only the ubiqutous smartphone "app". That opens up a number of interesting possibilities and I am anxious to work further in that arena. The lowest power mode, however, results in a very long advertising interval which can pose some challenges. Nevertheless, the technology in the solar beacons themselves continues to impress—particularly if you want to monitor well-illuminated areas.


Program files for the project can be downloaded by clicking the link below.

  Arduino Solar Beacon Files