1.0 Introduction
Having revisited the Cypress Solar Powered IoT Device (SPID) kit in my earlier blog, I decided to utilise it's energy harvesting PMIC capabilities and embark on this small project to measure and transmit, via BLE, temperature, relative humidity and UV index data using the Eddystone Beacon messaging format.
The network topology will look something like this, where any user can read the Temperature, %RH and UV Index data anonymously. The data can also be collected and stored in the cloud using a BLE gateway and made available as an open data set.
The remote sensor unit is made up of 3 modules:
- The Energy Harvesting PMIC
- The BLE module
- The sensors
My previous roadtest on the Solar Powered IoT Device (SPID) Kit discusses the BLE module. Let me now introduce the other two modules to give you a better understanding.
1.1 The Energy Harvesting S6AE101A PMIC
According to the website and datasheet, the S6AE101A is a Power Management IC (PMIC) for energy harvesting that has a consumption current of only 250 nA and startup power of only 1.2 μW. The S6AE101A PMIC will store power generated by solar cells to an output capacitor using built-in switch control, and it will turn on a power switching circuit while the capacitor voltage is within a preset maximum and minimum range for supplying energy to a load.
The S6AE101A also provides other “smarts” allowing for a hybrid Energy Harvesting System that allows you to combine a solar cell Energy Harvesting Device along with a coin cell battery, and/or an optional vibration (piezoelectric) EHD with an external diode bridge. With a hybrid scenario, if the power generated from solar cells is not enough, energy can then be supplied, in the same way as solar cells, from a connected battery or batteries for auxiliary power (note that there is no battery charging capability via the PMIC from the solar cell, as you often see with other MPPT solar charge controllers).
Then there is also, an over voltage protection (OVP) function built into the input pins of the solar cells, and the open voltage of solar cells is used by this IC to prevent an over-voltage state. The OVP voltage is set using upper (VOVPH = 5.4V) and lower (VOVPL = 5.3V) bands. Then, when VDD voltage reaches the OVP detection voltage (VOVPH) or higher, the OVP current (IOVP) from the VDD pin is drawn in, limiting the increase in the VDD pin voltage, thus preventing damage to the IC. When the OVP release voltage (VOVPL) or less is reached, the drawing-in of the OVP current is stopped.
This PMIC uses a power gating function (SW1), a power storage switch (SW2) and a solar cell/battery changeover switch (SW4) for the external load. It integrates 5-kΩ switches (SW2 and SW4) to limit input currents, which then optimises the IC to acquire the microampere (µA) of current output from the solar cell. Then once the VSTORE1 pin voltage has reached the VOUT maximum voltage (VVOUTH), the VSTORE1 pin and VOUT pin are connected by an internal switch until the VOUT minimum voltage (VVOUTL) is reached. Then when the internal SW1 switch disconnects the VSTORE1 and VOUT1 path, the discharge circuit is activated between the VOUT1 pin and GND. The power of the VOUT1 pin is then discharged to the GND level. Switches SW7 and SW9 are used to charge a capacitor (CVINT) that drives the internal circuit.
An interesting aspect of the IC is the setting of output voltage (VOUT1). The output voltage is determined by the value of the resistor connecting the SET_VOUTH pin and SET_VOUTL pin.
According to the datasheet, this is because the VOUT maximum voltage (VVOUTH) and VOUT minimum voltage (VVOUTL) are set based on the connected resistance. The SET_VOUTFB pin outputs a reference voltage for setting the VOUT maximum voltage and VOUT minimum voltage. Resistor voltage division can be performed on this reference voltage outside the IC for creating a voltage applied to the SET_VOUTH pin and SET_VOUTL pin.
So, for example, if we set R1, R2 & R3 to 6.8 MΩ, 2.7 MΩ and 9.1 MΩ, respectively we get a VOUT maximum voltage (VVOUTH) ≈ 3.3V and a VOUT minimum voltage (VVOUTL) ≈ 2.6V.
If we look at the schematic for the SPID kit, we see that the resistor values used for R1, R2 and R3 are 6.8 MΩ, 4.7 MΩ and 6.8 MΩ, respectively. In this case, the calculation gives us VVOUTH ≈ 3.3V and VVOUTL ≈ 1.9V
For more information on the PMIC described here, please refer to the Energy Harvesting PMICs page on the Cypress Semiconductor website.
1.2 The Sensors
For this project, I am using the on-board Si7020 sensor for temperature and humidity measurements and have added a Si1145 sensor to capture UV measurement in order to calculate the UV index. Both these sensors are I2C slave devices, allowing me to communicate with both these sensors via the I2C bus. The derived temperature, %RH and UV index values will be broadcast publicly via the CYBLE-022001 module using the Eddystone beacon messaging format.
(Si7020-A10) Temperature and Humidity sensor
According to the Silicon Labs product datasheet, the Si7020-A10 I2C Humidity and Temperature Sensor is a monolithic CMOS IC integrating humidity and temperature sensor elements, an analog-to-digital converter, signal processing, calibration data, and an I2C Interface.
{gallery:autoplay=false} Si7020-A10 |
---|
source: Silicon Labs datasheet for Si7020-A10 |
source: EH Motherboard Schematic (Cypress) |
The main features, as per the datasheet, are noted as:
- Precision Relative Humidity Sensor ± 4% RH (max), 0–80% RH
- High Accuracy Temperature Sensor ±0.4 °C (max), –10 to 85 °C
- 0 to 100% RH operating range
- Up to –40 to +125 °C operating range
- Wide operating voltage (1.9 to 3.6 V)
- Low Power Consumption: 150 μA active current and 60 nA standby current
(Si1145) Digital UV and Light sensor
According to Silicon Labs product datasheet, the Si1145 is a low-power, reflectance-based, infrared proximity, ultraviolet (UV) index, and ambient light sensor with I2C digital interface and programmable event interrupt output.
{gallery:autoplay=false} Si1145 |
---|
source: Silicon Labs datasheet for Si1145/46/47 |
source: https://learn.adafruit.com/adafruit-si1145-breakout-board-uv-ir-visible-sensor/downloads |
The power features, as per the datasheet, are noted as:
- 1.71 to 3.6 V supply voltage
- 9 µA average current (LED pulsed 25.6 µs every 800 ms at 180 mA plus 3 µA Si114x supply)
- < 500 nA standby current
- Internal and external wake support
- Built-in voltage supply monitor and power-on reset controller
- 25.6 µs LED “on” time keeps total power consumption duty cycle low without compromising performance or noise immunity
To learn more about this sensor, I would suggest reading this learning guide on Adafruit.com:
https://learn.adafruit.com/adafruit-si1145-breakout-board-uv-ir-visible-sensor
If you want to up your game and get a more accurate measurement, Adafruit recommend this sensor (I have one on my order list):
https://learn.adafruit.com/adafruit-veml6075-uva-uvb-uv-index-sensor
2.0 Converting the PsoC 4 BLE Eddystone Example
I wanted to see how well the CYBLE module would work, while using solar energy harvesting, when running the available PSoC 4 BLE Eddystone example without modifying any of the BLE advertising intervals. So, to get started, all I had to do was port this ready-made code example on PSoC Creator across to my CYBLE device by making a few changes.
Let's take a quick look at what I did.
The TopDesign page for the PsoC4 BLE on PsoC Creator looked like this:
There were a couple of resource constraints this design. Firstly, the Cypress SPID only has one LED on the board. This is fine for our purposes, as we cannot use LED's when the board is powered just through the solar panel + storage capacitor.
The other constraint, which is a bit of an oversight, in my opinion, is that there is no access to the Vref pin on the CYBLE module, so we cannot measure battery or input voltage.
The pin assignment on the PsoC 4 BLE board is as follows:
As such, the TopDesign page for my SPID CYBLE module on PsoC Creator looks like this:
For the sake of simplicity, I kept the ADC Interface the same and hence Vref is an unconnected GPIO, where I have assigned the input pin with a pulldown resistor. I found that within the code I can also disable this measurement so that it is never used.
I added in a Bootloadable component, with its dependencies linked to the existing Bootloader project for the SPID CYBLE module.
I also added in another input pin (ref USB_DETECT) which is assigned to P3_5, which is unique to the board as it is linked to USB power via pullup and pulldown resistors.
The one and only LED is used to show BLE connection status and is only available if the USB_DETECT pin is high.
The pin layout for the CYBLE module is as follows (note we have much fewer pins available to us for use):
{gallery:autoplay=false} Pin Layout |
---|
2.1 Eddystone Firmware for CYBLE module
The firmware is almost identical to the existing PSoC4 BLE Eddystone example. You will notice that with this example, we have a "main.h" file. Ok, nothing unusual about this, but the point of interest for our purposes is the following:
#define TEMPERATURE_SENSOR_ENABLE (YES) #define BATTERY_MEASUREMENT_ENABLE (NO)
As you can you see we have a means of enabling or disabling the Temperature and Battery measurement functions. As we are not able to measure battery voltage, I set this to "NO" which then removes portions of the code through a precompiler directive.
With that out the way let's start with the main routine in "main.c":
/******************************************************************************* * Function Name: main ******************************************************************************** * * Summary: * Main function. * * Parameters: * None * * Return: * int * *******************************************************************************/ int main() { /* Enable global interrupt mask */ CyGlobalIntEnable; /* This function will initialize the system resources such as BLE and ADC */ Initialization(); while (1) { /* BLE stack processing state machine interface */ CyBle_ProcessEvents(); /* Process BLESS states */ IncrementAdvPacketCount(); /* Update the TLM Adv packets with "advPacketCount" and * "SecCnt" */ UpdateTLMAdvPacket(); /* Put CPU and BLESS to low power mode */ LowPower(); if(stopAdv && (CyBle_GetState() == CYBLE_STATE_ADVERTISING)) { /* Reset the stop advertisement flag. */ stopAdv = false; /* Stop advertisement. */ CyBle_GappStopAdvertisement(); } } }
Here you see that all the initialisation functionality has been bundled into a function called "Initialization()". This function is very well documented explaining how the WCO and ECO clocks are configured to set up low power operation.
/******************************************************************************* * Function Name: Initialization ******************************************************************************** * * Summary: * This function is used to initialize all blocks of the application * * Parameters: * none * * Return: * none *******************************************************************************/ void Initialization(void) { /* Set the divider for ECO, ECO will be used as source when IMO is switched * off to save power, to drive the HFCLK */ CySysClkWriteEcoDiv(CY_SYS_CLK_ECO_DIV8); /* Do the following for achieving lowest possible WCO & ECO startup current: * 1. Shut down the ECO (to reduce power consumption while WCO is starting) * 2. Enable WDT counter 0 to wakeup the system after 500ms * (500ms = WCO startup time) * 3. Configure PSoC 4 BLE device in DeepSleep mode for the 500ms WCO * startup time * 4. After WCO is enabled, restart the ECO so that BLESS interface can * function * 5. Enable WDT counter 1 to wakeup the system after 1ms * (1ms = ECO startup time) * 5. Configure PSoC 4 BLE device in DeepSleep mode for the 1ms ECO startup * time */ /* Shutdown the ECO and later re-start in low power mode after WCO is turned * on. */ CySysClkEcoStop(); /* Initialize WDT interrupt */ WDT_Interrupt_StartEx(WDT_Handler); /* Enable WCO & ECO in low power mode using WDT counter 0/1 as system wakeup * sources respectively */ WCO_ECO_LowPowerStart(); /* Start BLE component and register the EddystoneEventHandler function. This * function exposes the events from BLE component for application use. */ CyBle_Start(EddystoneEventHandler); //cyBle_attValuesLen[16].actualLength = DEFAULT_URL_LENGTH; cyBle_attValuesLen[16].actualLength = E14_URL_LENGTH; /* Start the ADC component for temperature measurement. */ ADC_Start(); /* Put ADC to sleep and wake it up only for measurements. */ ADC_Sleep(); /* Start and register the SW1 ISR. */ ConnectionAdv_ISR_StartEx(interruptHandlerSW1); }
The initialization() function also sets up various event handlers, namely
- a watchdog (WDT) interrupt handler (WDT_Handler)
- a BLE event handler (EddystoneEventHandler)
- an interrupt handler for the button (interruptHandlerSW1)
The event handler of interest is the EddystoneEventHandler as this deals with all the Eddystone advertising and messaging.
/******************************************************************************* * Function Name: EddystoneEventHandler ******************************************************************************** * * Summary: * This is an event callback function to receive events from the CYBLE * Component. * * Parameters: * uint32 event: Event from the CYBLE component. * void* eventParam: A structure instance for corresponding event type. The * list of event structures is described in the component * datasheet. * * Return: * None * *******************************************************************************/ void EddystoneEventHandler(uint32 event, void* eventParam) { CYBLE_API_RESULT_T apiResult; /* To prevent compiler warning. */ eventParam = eventParam; switch (event) { /********************************************************** * General Events ***********************************************************/ /* This event is received when component is Started */ case CYBLE_EVT_STACK_ON: /* Configure WDT counter 0 with 2 second interval */ WDT_Initialize(CY_SYS_WDT_COUNTER0, TWO_SECOND_INTERRUPT_COUNT); /* Enable WDT counter 0 */ WDT_EnableCounter(CY_SYS_WDT_COUNTER0_MASK); /* Ignore the initial delay. Start counter to track the time since * power ON. */ SetEnableSecCnt(true); beaconCurrentRole = eddystoneImplenmentation; ConfigureAdvPacket(); /* Start advertisement */ apiResult = CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_CUSTOM); if(apiResult != CYBLE_ERROR_OK) { CYASSERT(0); } break; case CYBLE_EVT_GAPP_ADVERTISEMENT_START_STOP: if(CyBle_GetState() != CYBLE_STATE_ADVERTISING) { /* On advertisement timeout, restart advertisement. Before * restarting previous type of advertisement, check if the other * type is triggered. If so, switch to the other type of * advertisement. */ if(IsConnAdvStart() == true && USB_DETECT_Read()) { CyBle_GattsDisableAttribute ( CYBLE_EDDYSTONE_SERVICE_HANDLE ); ConfigureConnAdvPacket(); apiResult = CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_CUSTOM); if(apiResult != CYBLE_ERROR_OK) { CYASSERT(0); } } else { /* On advertisement timeout, switch between URI/URL and * TLM packets. */ if( (beaconCurrentRole == EDDYSTONE_UID) || (beaconCurrentRole == EDDYSTONE_URL) ) { beaconCurrentRole = EDDYSTONE_TLM; } else if(beaconCurrentRole == EDDYSTONE_TLM) { beaconCurrentRole = eddystoneImplenmentation; } ConfigureAdvPacket(); apiResult = CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_CUSTOM); if(apiResult != CYBLE_ERROR_OK) { CYASSERT(0); } } } else { SetAdvPacketCount(GetAdvPacketCount() + 1); } break; case CYBLE_EVT_GAP_DEVICE_DISCONNECTED: CyBle_GattsEnableAttribute(CYBLE_EDDYSTONE_SERVICE_HANDLE); beaconCurrentRole = eddystoneImplenmentation; ConfigureAdvPacket(); CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_CUSTOM); break; case CYBLE_EVT_GATTS_WRITE_REQ: ProcessWriteReq(*(CYBLE_GATTS_WRITE_CMD_REQ_PARAM_T*) eventParam); break; default: break; } }
As you may notice in the code, there are only a couple of BLE stack events that we worry about, namely:
- CYBLE_EVT_STACK_ON: this event is triggered when the BLE component is Started
- CYBLE_EVT_GAPP_ADVERTISEMENT_START_STOP: this event is triggered when BLE advertisements are stopped or started. When started we configure the advertising packet - there is a specific function that deals with this (ConfigureAdvPacket), which handles the URL/UID/TLM messaging formats. Within the code that deals with this event trigger we also check if USB_DETECT is high (i.e. USB powered). When Advertising is stopped we increment the advertising packet count.
- CYBLE_EVT_GAP_DEVICE_DISCONNECTED: this event is triggered when upon BLE disconnect (only applies when USB powered)
- CYBLE_EVT_GATTS_WRITE_REQ: this event is triggered if any data is written to the device from a connected mobile app, for example (when device is USB powered)
Then once that function has completed we then indefinitely loop through 4 function calls and a check to see if we need to start advertising again (this occurs when we have connected and then disconnected to the BLE module for runtime config purposes). The 4 function calls are:
- CyBle_ProcessEvents() - this is a standard BLE function
- IncrementAdvPacketCount() - this is a custom function This function keeps track of adv packets based on BLESS states.
- UpdateTLMAdvPacket() - this is a custom function. If device is advertising TLM packets then will update the TLM packets with temperature, time since power on, number of packets that are advertised, etc.
- LowPower() - this is a custom function. It puts the CPU and BLESS into low power mode
The code referred to here and used in this demo is provided as an attachment: EdwardSolarStone-000.cywrk.Archive01.zip
2.2 Demo 1: Eddystone URL/TLM beacon messaging without sensor data
This demo was done indoors under normal office lighting (lux value yet to be measured).
3.0 Adding in the sensor data to the Eddystone UID messaging format
To be continued...
Top Comments