Wednesday, August 8, 2018

Espressif ESP32 Tutorial - IR Remote Control using Microsoft Azure




The Project


This tutorial will outline how to create an IR Remote using the ESP32 and then control it from the IoT hub on Microsoft Azure.

Driving an IR remote transmitter using an Arduino is simple, as there is a library, called IRremote.h which does all the hard work. You just need to connect your IR transmitter module signal pin to the appropriate Arduino pin, via a current limiting resistor and you are done. Connecting an Arduino to the cloud takes a bit more work (depending on the model you are using), which is why we wanted to use the ESP32.

Unfortunately, the standard IRRemote.h Arduino library only supports receiving IR signals on the ESP32 not transmitting them. Fortunately, Andreas Spiess has forked the standard library and added ESP32 transmission capability. You will need to download the ESP32-IRremote library, so we can use it with the ESP32. Andreas did this by using ledC PWM. You can now select any pin to use with IRsend(pin). Note this is only for the ESP32, the other board types have defined pins you have to use due to the assigned timers.

The Duinotech Infrared Transmitter Module


The IR transmitting module which I used is the one from Jaycar (branded Duinotech). There is a data sheet available on the Jaycar site but it is fairly sparse and doesn't clearly define the pins on the module.

It appears that this module is based on the KY-005 INFRARED TRANSMITTER MODULE. The specifications for which are:


Operating Voltage 5V
Forward Current 20 ~ 60 mA
Power Consumption 90mW
Operating Temperature -25°C to 80°C [-13°F to 176°F]
Dimensions  18.5mm x 15mm [0.728in x 0.591in]

This being the case, the pin out is as follows:



The signal pin is clearly labeled with an S, the middle pin is GND via a resistor (* which you have to fit yourself to the module) and GND is connected to the third pin (with the "-" adjacent to it). This module is just an infrared diode (which emits at a wavelength of 940 nm).

Thus, we can drive it like any other diode via a current limiting resistor. The value of the resistor depends on what voltage your micro controller digital outputs (DO) are switching, the desired diode forward current and the forward voltage drop characteristic of the diode. So for our design:

VDO = 3.3V
If = 20 mA
Vf = 1.2V (nominally 1.1V but I measured this using my LCR meter)

Then, R = (VDO - Vf) / If
              = (3.3 - 1.2) / 0.02
              = 105 Ω



We will use a 100 Ω resistor in our circuit.



The CIR (Commercial Infrared) Transmission Protocol


As there are usually other sources of infrared radiation (e.g. sunlight and incandescent or LED lights), the 940nm IR transmitter is modulated by a carrier frequency in the 32-40 kHz range. CIR receivers incorporate a bandpass filter tuned to this carrier frequency. This allows the receiver to discriminate between the modulated IR signal and any ambient, unmodulated IR. In effect, the IR receiver is double tuned both to the wavelength of the IR radiation and to the carrier frequency.



Three factors influence CIR range. In order of decreasing importance they are: the power level of the IR emitter, the IR wavelength, and the carrier frequency. An IR emitter's output is proportional to the current through the emitter. Increasing the current will increase the power. Because the duty cycle is usually 50% or less, the emitter can be driven with quite high currents. For optimal range, the IR wavelength of the emitter and receiver should match.



A similar protocol to CIR is IrDA. IrDA was popular in the late 1990's but has largely been replaced by Bluetooth and WiFi. IrDA was designed to be very short range (< 1 m). It does not use any secondary carrier but directly modulates the 850nm IR with the data. Because of this, it is susceptible to interference from ambient IR. In addition, the IrDA transmitter is usually lower power than a CIR transmitter.

An IrDA transmitter with a CIR receiver is a mismatch as is a CIR transmitter with a IrDA receiver. They operate on a different wavelength (940 nm vs 850 nm), IrDA isn't modulated, and the beam angles are different (IrDA limits the beam angle to ±15° while most CIR emitters are ±40° or greater). Such mismatches have major effects on range and reliability.

We will use CIR in our design.

For RF control, both the transmitter and receiver need to be tuned to the same carrier frequency and need to use the same type of modulation. Most RF remotes use ASK (Amplitude Shift Keying) or OOK (On-Off Keying). OOK is really just a special case of ASK. OOK is also called CPCA (Carrier Present, Carrier Absent). You can have a look at the IRremote library to see how this coding is achieved.

Microsoft Azure




Azure is Microsoft's catch all name for their cloud services. It covers over 100 different services. The service of interest to us is IoT Hub. You can use Azure IoT Hub to securely connect, monitor and manage billions of devices to develop Internet of Things (IoT) applications. To get started we will connect just one device!

You will need to sign up for a free Azure account. Follow the link above and do this. For some reason Microsoft make you provide credit card details, even for the free account. Note that when you sign up, the email address you provide becomes the name of the default active directory (which wouldn't be my first preference).

It will be interesting comparing the Microsoft IoT hub functionality with node-red, which is another dashboard option that we have had experience with. At this stage I suspect that node-red is much cheaper (free) and simpler but Azure is more robust, secure and scalable. The key features of IoT hub are:
  1. Bidirectional communication with LOTS of devices. Use device-to-cloud telemetry data to understand the state of your devices and define message routes to other Azure services without writing any code. In cloud-to-device messages, reliably send commands and notifications to your connected devices – and track message delivery with acknowledgement receipts. Device messages are sent in a durable way to accommodate intermittently connected devices.
  2. Authentication per device. Set up individual identities and credentials for each of your connected devices, and help retain the confidentiality of both cloud-to-device and device-to-cloud messages. To maintain the integrity of your system, selectively revoke access rights for specific devices as needed.
  3. Automated device registration. Speed up your IoT deployment by registering and provisioning devices with zero touch in a secure and scalable way. IoT Hub Device Provisioning Service supports any type of IoT device compatible with IoT Hub.
  4. Use IoT Edge. Take advantage of IoT Edge to make hybrid cloud and edge solutions. IoT Edge provides orchestration between code and services so they flow securely between cloud and edge to distribute intelligence across a range of devices. Enable artificial intelligence and other advanced analytics at the edge.
Once you have signed up for Azure, you will be presented with a dashboard similar to that shown above.

Create an IoT Hub




Microsoft call their menus "blades" in Azure. No idea why, maybe because it sounds cooler than menu? Anyway, click on the + Create a resource link on the blade to the left of the dashboard. This will open the Azure Marketplace.

In the Marketplace, click on Internet of Things. This will provide a new list of menu options to the right.


We want IoT Hub at the top. Click on this to setup your hub. For subscription select Free Trial and for Resource Group, Create new.

The free tier is intended for testing and evaluation. It allows 500 devices to be connected to the IoT hub and up to 8,000 messages per day. Each Azure subscription can create one IoT Hub in the free tier.

A resource group is a container that holds related resources for an Azure solution. The resource group can include all the resources for the solution, or only those resources that you want to manage as a group.

Select the Region closest to your location. In Australia the options are East and Southeast, which I think refer to Sydney and Melbourne respectively.

To create an IoT hub, you must name the IoT hub. This name must be unique across all IoT hubs. The IoT hub will be publicly discoverable as a DNS endpoint, so make sure to avoid any sensitive information while naming it. Once created, the name can't be changed.

Click the button at the bottom labelled - Next: Size and Scale >>



For pricing and scale tier, select F1: Free tier. That is all that you can adjust on this screen. Click Review + create.

When all previous steps are complete, you can create the IoT hub. Click Create to start the back-end process to create and deploy the IoT hub with the options you chose.

It can take a few minutes to create the IoT hub as it takes time for the back-end deployment to run on the appropriate location servers. Once your new IoT resource has been created, you can customise your dashboard.


Add an IoT Device



Before a device or module can connect to your IoT hub, there must be an entry for that device in the IoT hub's identity registry. A device must also authenticate with the IoT hub based on credentials stored in the identity registry. The device or module ID stored in the identity registry is case-sensitive.

To add a new IoT device, click on + Add, and the Add Device blade will be displayed.

  • Device ID: A case-sensitive string (up to 128 characters long) of ASCII 7-bit alphanumeric characters.
  • Authentication Type: Symmetric Key or X.509 Certificate. I used Symmetric Key. The differences are:
    • Symmetric Key: a unique identity key (security tokens) for each device, which can be used by the device to communicate with the IoT Hub.
    • X.509 Certificate: uses an on-device X.509 certificate and private key as a means to authenticate the device to the IoT Hub. This authentication method ensures that the private key on the device is not known outside the device at any time, providing a higher level of security.
  • Auto Generate Keys: tick.
  • Connect device to IoT hub: enable.

Click on Save, and your new device will be added to the hub. Click on the device ID of the newly added device to see the security keys and connection strings. You will need the device ID and a copy of the primary connection string for insertion into your ESP32 sketch. Now onto the ESP32.

ESP32 Software


You can download a copy of my ESP32 sketch from the Reefwing Gist.  You will need to fill in your SSID, password and primary connection string where indicated.



I spent quite a bit of time trying different tool chains to get everything configured and talking. My initial preference was to use Eclipse with the Arduino tool chain. This would give me a proper IDE and a remote control library that I knew was compatible with all the Arduino's out there.

Unfortunately, importing custom libraries is a bit problematic for the two Eclipse Arduino plug-ins available. It is theoretically possible but I ran out of patience trying to get it to work. The ESP-IDF has its own remote control library but it is not as well documented as the Arduino library. I'm also not familiar with coding the ESP32's natively.



While looking for a way to receive the messages sent from the ESP32 to the cloud I discovered a plug in for Visual Studio Code. It so happens that there is also a plug in for Arduino. Since I was already using this to monitor my IoT hub traffic, I decided to give it a crack with programming the ESP32. It just worked - I was astonished! It does use the Arduino IDE tool chain, so that may be why it was so seamless as I had already got everything working with that first. If IntelliSense complains about a missing library, right click on the light build and edit the c_cpp_properties.json file which contains the include path.




The other thing you will probably have to do is add:

"output": "../build",

To the .vscode/arduino.json file, which can be found under the work space for your sketch. The same location as the c_cpp_properties.json file.  If output is not set, Arduino will create a new temporary output folder each time it compiles your sketch, which means it cannot reuse the intermediate result of the previous build, leading to long verify/upload time. So it is recommended to set the field. Arduino requires that the output path should not be the workspace itself or subfolder of the workspace, otherwise, it may not work correctly. By default, this option is not set. Again, no idea why, you will get a warning if it isn't set when you verify.



Whether you use the Arduino IDE or Visual Studio Code, you need to download the Azure IoT library: ESP32_AzureIoT - An Azure IoT Hub library for ESP32 devices in Arduino.  Unzip and copy this to your Arduino libraries folder.

As a first test, load the GetStarted.ino sketch from the examples folder in the library you just downloaded. This sketch will connect to the IoT hub and continuously send messages containing fake data. You will need to fill in the following blanks in the sketch:

  • DEVICE_ID - copy from your IoT registered device;
  • connectionString - copy from the primary connection string;
  • ssid - the displayed name for your WiFi network; and
  • password - for your WiFi network.

Connect to your ESP32, check the port and board type, then compile and upload the sketch. Open up the serial monitor at 115,200 baud so that you can see what is happening. The monitor should be displaying something like the following.


To confirm that the IoT hub is receiving these messages have a look at the Azure dashboard and you should see these messages arriving.


If you have Visual Studio Code, you can use the Azure IoT extension to monitor messages to your IoT hub. Just select your device and then right click and Start monitoring D2C (Device to Cloud) message. You can also send messages from the cloud to your device from here.


Once this was working, I updated the code to just send a heart beat message back to the cloud, letting us know that it was still alive. Every time the ESP32 does this, it broadcasts an IR remote control code three times. This is the usual methodology for remote controls. Currently it is just broadcasting the Sony power code, but we will look at ways we can start/stop the broadcast and change the code via Azure.

Cloud to Device Message Lifecycle




To guarantee at-least-once message delivery, IoT Hub persists cloud-to-device messages in per-device queues. Devices must explicitly acknowledge completion for IoT Hub to remove them from the queue. This approach guarantees resiliency against connectivity and device failures.

When the IoT Hub service sends a message to a device, the service sets the message state to Enqueued. When a device wants to receive a message, IoT Hub locks the message (by setting the state to Invisible), which allows other threads on the device to start receiving other messages. When a device thread completes the processing of a message, it notifies IoT Hub by completing the message. IoT Hub then sets the state to Completed.

The max delivery count property on IoT Hub determines the maximum number of times a message can transition between the Enqueued and Invisible states. After that number of transitions, IoT Hub sets the state of the message to Dead lettered.

The diagram above shows the lifecycle state graph for a cloud-to-device message in IoT Hub. Luckily, sending messages is a lot more straight forward than implementing the message lifecycle.

Controlling the ESP32 via Azure


Now that we have our ESP32 talking to Azure and broadcasting an IR code burst every 10 seconds we want to be able to control this via the cloud. The easiest way to do this is using Visual Studio Code again.


If you right click on the device, shown in Explorer under Azure IOT HUB DEVICES, then the window above is displayed. The two ways we will look at communicating with our device via the cloud is:

  1. Cloud to Device (C2D) Messaging; and
  2. Triggering a defined device method.
You can try out both.

Cloud to Device Messaging


After uploading the sketch to your ESP32, open the serial monitor from the Arduino IDE. I found the serial monitor function in Visual Studio Code was a bit dodgy. Select Send C2D (Cloud to Device) Message to Device, and a message entry window will open. Type in whatever you want and hit return.

In the Azure IoT Toolkit output window you should see:

[C2DMessage] Sending message to [ESP32_IRBeacon_1] ...
[C2DMessage] [Success] Message sent to [ESP32_IRBeacon_1]

A second or so later, the following will appear in the Serial Monitor:

Info: >>>Received Message [1], Size=14 Message test message
Message callback:
test message

The function which handles message handling in the ESP32 code is MessageCallback(const char* payLoad, int size). The call back function is set during setup() using:

Esp32MQTTClient_SetMessageCallback(MessageCallback);


Invoke a Direct Method


This is the way that I chose to control the IR beacon (since that is its purpose). You can define what methods you want to support in your code. Currently we are only handling start and stop but it would be trivial to add another method to set the IR code transmitted.

The process for invoking a method is the same as for sending a C2D message. Right click on your device in Explorer and select "Invoke Direct Method". A text entry window will open, type in your method name (e.g. stop) and hit enter. In the Azure IoT Toolkit output window you should see:

[DirectMethod] Invokeing Direct Method [stop] to [ESP32_IRBeacon_1] ...
[DirectMethod] Invokeing Direct Method [start] to [ESP32_IRBeacon_1] ...

Yes whoever wrote this code couldn't spell invoking! Then in the Serial monitor:

Info: Try to invoke method stop
Info: Stop sending IR burst and heart beat

The function which handles method handling in the ESP32 code is DeviceMethodCallback(). This call back function is set during setup() using:

Esp32MQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);


Conclusion




If all you want is a dashboard for your IoT application then Node-Red is MUCH simpler to implement. If you need an industrial strength solution then you need something like Azure IoT hub.

Actually, using Visual Studio Code (VSC) was a pleasure, and this will be my go to IDE for Arduino from now on. Controlling and Monitoring your IoT devices via VSC was also very easy once you work out how it operates. The user interface is not very discoverable, you need to hit F1 to access most of the Arduino and Azure commands.

I will publish a short follow up article providing a simple PCB to mount the diagnostic LED's, IR transmitting module and the ARM/DEBUG switch. It is a simple enough circuit that you could do it on a breadboard or veroboard. Note that the Duinotech ESP32 doesn't leave any pins free on one side of a standard breadboard, due to its width. See the photo above. This is one reason I decided to use a PCB.


Monday, July 23, 2018

Espressif ESP32 Tutorial - Programming (Eclipse)

Eclipse (Photon)





The Eclipse IDE is one of the most popular desktop development environments. Being free and open source helps! It also supports most of the common programming languages through Language Server-based plugins. The current release is called Photon.

The C/C++ Development Toolkit (CDT) is a collection of Eclipse-based features that provides the capability to create, edit, navigate, build, and debug projects that use C and/or C++ as a programming language.

The CDT does not include the necessary compilers and debuggers to convert C/C++ code into executable programs and to debug those programs, but it does provide the frameworks that allow such tools to be integrated in a consistent fashion. We will be using the CDT version of Eclipse to program the ESP-32.


Installing the Eclipse IDE





The Eclipse IDE gives you a graphical integrated development environment (IDE) for writing, compiling and debugging ESP-IDF projects. It is quite a bit easier to use than the command line ESP-IDF provided by Espressif. It is also more sophisticated and better suited to larger projects than the Arduino IDE.

As Eclipse doesn't provide an ESP-32 compiler or the ability to flash the DevKit, you will need to have already installed the ESP-IDF tool chain for your operating system. If you haven't done this, refer to our earlier tutorial on the ESP-IDF.

Using ESP-IDF with Eclipse on Windows requires different configuration steps. See the Eclipse IDE on Windows guide if you are using Windows. The following is an overview of the steps for those using a Mac. This content is based on the material from the Espressif ESP-IDF Programming Guide but I have noted any areas where I had difficulty and included screenshots to make some of the explanations clearer.

1. Download the Eclipse Installer for your platform from eclipse.org.

2. Create a working directory, uncompress the download and run the installer app.

mkdir -p ~/eclipse
cd ~/eclipse
tar -xzf ~/Downloads/eclipse-inst-mac64.tar.gz 
open -a 'Eclipse Installer.app'

3. When running the Eclipse Installer, choose “Eclipse for C/C++ Development” (in the documentation you’ll see this referred to as CDT.)

4. When running the installer, you may get the following error.


Note that this is NOT referring to the Java Runtime Environment which you would update via System Preferences.


If you get the JVM error, you need to update the Java Development Kit

After that you should be rewarded with the following installation screen when you run the provided install app.



Setting up Eclipse





After the new Eclipse installation launches, follow these steps:

1. Import a New Project

Eclipse uses the Makefile capability in ESP-IDF. This means you need to start by creating an ESP-IDF project. You can use the skeleton project from github or one of the example projects in the esp-idf examples subdirectory. We will use our old mate Blink again. Refer to our previous tutorial on the ESP-IDF if you don't know how to copy an example project.

Once Eclipse is running, choose File -> Import...

In the dialog that pops up, choose “C/C++” -> “Existing Code as Makefile Project” and click Next.



On the next page, in the “Existing Code Location” field, use the directory of your IDF project. Don’t specify the path to the ESP-IDF directory itself (we will do that in Project Properties). The directory you specify should contain a file named “Makefile” (the project Makefile).



On the same page, under “Toolchain for Indexer Settings” choose “Cross GCC”. Then click Finish.

2. Project Properties



The new project will appear in the left hand Project Explorer column. Right-click the project and choose Properties from the context menu.

Click on the “Environment” properties page under “C/C++ Build”. Click “Add…” and enter name BATCH_BUILD and value 1.

Click “Add…” again, and enter name IDF_PATH. The value should be the full path where ESP-IDF is installed. If you completed our previous ESP-IDF tutorial, you shouldn't have to do this step as the path will already be updated from the .profile preferences file.



Edit the PATH environment variable. Keep the current value, and append the path to the Xtensa toolchain installed as part of IDF setup, if this is not already listed on the PATH. 

A typical path to the toolchain looks like /home/user-name/esp/xtensa-esp32-elf/bin. Note that you need to add a colon : before the appended path.

On macOS, add a PYTHONPATH environment variable and set it to:

/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages. 

This is so that the system Python, which has pyserial installed as part of the setup steps, overrides any built-in Eclipse Python. I didn't need to do this on my Mac.

Navigate to “C/C++ General” -> “Preprocessor Include Paths” property page:

Click the “Providers” tab

In the list of providers, click “CDT Cross GCC Built-in Compiler Settings”. Under “Command to get compiler specs”, replace the text ${COMMAND} at the beginning of the line with xtensa-esp32-elf-gcc. This means the full “Command to get compiler specs” should be xtensa-esp32-elf-gcc ${FLAGS} -E -P -v -dD "${INPUTS}".



In the list of providers, click “CDT GCC Build Output Parser” and type xtensa-esp32-elf- at the beginning of the Compiler command pattern. This means the full Compiler command pattern should be xtensa-esp32-elf-(g?cc)|([gc]\+\+)|(clang)



Navigate to “C/C++ General” -> “Indexer” property page:

Uncheck “Allow heuristic resolution of includes”. When this option is enabled Eclipse sometimes fails to find correct header directories.



3. Building in Eclipse

Before your project is first built, Eclipse may show a lot of errors and warnings about undefined values. This is because some source files are automatically generated as part of the esp-idf build process. These errors and warnings will go away after you build the project.

Click OK to close the Properties dialog in Eclipse.

Outside Eclipse, open a command line prompt. Navigate to your project directory, and run make menuconfig to configure your project’s esp-idf settings. This step currently has to be run outside Eclipse.

If you try to build without running a configuration step first, esp-idf will prompt for configuration on the command line - but Eclipse is not able to deal with this, so the build will hang or fail.

Back in Eclipse, choose Project -> Build All to build your project.

TIP: If your project had already been built outside Eclipse, you may need to do a Project -> Clean before choosing Project -> Build. This is so Eclipse can see the compiler arguments for all source files. It uses these to determine the header include paths.

4. Flash from Eclipse

You can integrate the “make flash” target into your Eclipse project to flash using esptool.py from the Eclipse UI:

Right-click your project in Project Explorer (important to make sure you select the project, not a directory in the project, or Eclipse may find the wrong Makefile.)

Select Build Targets -> Create… from the context menu.
Type “flash” as the target name. Leave the other options as their defaults.



Make sure that the ESP-32 is connected and the correct serial port  has been selected using menuconfig then use Project -> Build Target -> Build (Shift+F9) to build the custom flash target, which will compile and flash the project.





Your program will be compiled and flashed to the ESP-32 and the results shown in the CDT build console.

Note that you will need to use “make menuconfig” to set the serial port and other config options for flashing. “make menuconfig” still requires the command line terminal (see the instructions for your platform on the Espressif site or refer to our ESP-IDF tutorial.)



Follow the same steps to add bootloader and partition_table targets, if necessary.