The PSoC 6 is the latest addition to Cypress’s powerful PSoC series of processors. The PSoC 6 Pioneer IoT Add-On Shield is the development tool associated with this processor line, sporting an onboard debugger, Arduino compatible headers, CapSense widgets, and more, all tied to a PSoC 6 processor. The processor is a dual-core device, with a Cortex-M0+ low power processor and a Cortex-M4 high power processor tied together via shared peripherals and memory space.
This tutorial will show you how to get up and running with the Pioneer Board, using the Pioneer Add-on Shield to expand the capabilities of the PSoC 6 device. We’ll show you how to communicate with a Raspberry Pi via BLE and WiFi (using an XBee WiFi module), as well as how to communicate between a PSoC 4 BLE Pioneer Board and the PSoC 6 Pioneer Board via BLE.
The examples in this tutorial are meant for use with the PSoC 6 Pioneer Kit, which can be purchased directly from Cypress or from Digi-Key.
Obviously, you’ll also need a Pioneer Add-on Shield. You’ll also need an XBee WiFi Module. There are a few XBee WiFi options depending on your setup: Trace, RP-SMA Connector w/ external 2.4GHz antenna, or Wire . The easiest would be to get XBee with the wire antenna.
You’ll also need the Raspberry Pi 3 Starter Kit. This will become your target for communication from the PSoC6 Pioneer Board. You can, of course, just purchase the Pi 3 separately along with a breadboard, jumper wires, resistors and LEDs, but we think the starter kit is an exceptional deal and is well worth getting.
Let’s go over the features of the Pioneer Kit IoT Add-on Board in detail.
MicroSD Card Slot – The pins for this slot map to the SPI peripheral on most Arduino compatible boards, including the PSoC 6 BLE Pioneer Board.
XBee Header – This header is spaced to accept the standard XBee footprint. It is compatible with all official XBee modules.
Qwiic Connector – This connector adds support for all of SparkFun’s Qwiic modules. It supplies 3.3V.
MicroB USB Power Connector – Data lines on this connector are not connected to anything. It provides 5V to the 3.3V regulator for the XBee module, overriding the 5V coming from the Arduino header and allowing high power XBee modules (such as the cellular, wifi, or Pro models) to function properly.
D7 and D9 Buttons – Two user buttons tied to pins D7 and D9 (P0.2 and P13.1 on the PSoC 6, or P1.0 and P0.4 on PSoC 4 BLE).
3.3V Regulator – Switch mode 3.3V power regulator capable of sourcing up to 1.5A, depending upon the upstream supply sourcing capacity. Draws power from 5V supply on Arduino pins or MicroB power connector. Supplies power to XBee header only.
Level Shift Buffer – Down converts from 5V signals to 3.3V signals. Allows board to be used in 3.3V or 5V systems.
I2C Level Shift Circuitry – Converts I2C signals from 3.3V to 5V, if necessary.
Voltage Supply Selection Jumper – Selects level to which I2C level shift circuitry translates. Default set to 3.3V. Set to 5V for use with 5V systems. Both the PSoC 4 and PSoC 6 Pioneer BLE boards are 3.3V systems.
XBee DIO5 LED – DIO5 defaults to some useful functions, especially on the WiFi module, where it shows connectivity to the configured WiFi network.
Example: WiFi to Raspberry Pi Using the PSoC 6 Pioneer Kit
This example demonstrates how to send a signal to a Raspberry Pi via WiFi. It will show you how to access an XBee WiFi module, interprocess communication between the two cores of the PSoC 6, and how to receive and parse commands with a Raspberry Pi.
Following this example is going to require some setup, so let’s walk through that now.
PSoC 6 Pioneer Kit Setup: Hardware
The Pioneer Kit side setup is trivial: insert the XBee WiFi module into the Pioneer IoT Add-on Shield and insert the shield into the Pioneer Kit Board’s Arduino header.
The Raspberry Pi side requires more explanation. You’ll need to setup both some hardware and some software on the Raspberry Pi.
PSoC 6 Pioneer Kit Setup: Software
The software project for the Pioneer Kit is available on GitHub.
Once you have downloaded and extracted the file somewhere, you can open the example (XBee_WiFi_Example) in PSoC Creator.
Before you do anything else, you need to open the “main_cm4.c” file and make a couple of changes. You’ll find a section of code that looks like this:
char ssid = “your_ssid_here”;
char rpi_ip = “raspi_ip_here”;
char ssid_pw = “wifi_pw_here”;
int dest_port = 5000;
char encrypt_mode = WPA2;
Hopefully, it’s obvious what you need to do: change these settings to match your network setup. The encrypt_mode value can be WPA, WEP, WPA2, or (hopefully not!) NO_SECURITY. rpi_ip is a dotted quad (e.g., “10.8.253.193”) that you can obtain from typing “ifconfig” in a command window on your Raspberry Pi (see below for instructions on opening a command window) and looking at the “wlan0” section.
To program the board, connect it to your PC via the included USB-A to USB-C cable. Then, click the “Program” button in the toolbar (as shown below) to automatically build the project and program the board.
You may get a window as below asking you to choose a target to program. It does not matter which entry you choose under the “KitProg2” list item, either one will program the flash correctly.
Raspberry Pi Setup: Hardware
First, let’s look at how the hardware is connected:
As you can see, we’ve connected an LED (with 330 ohm resistor) to pins 3 (GPIO 2) and 6 (ground) of the Raspberry Pi. This will allow us to toggle GPIO2 and see the result on the LED.
Raspberry Pi Setup: Software
We’re going to assume that you have a Raspberry Pi set up with the latest version of Raspbian (the full install, not the lite version) running, and that it’s connected to a keyboard, mouse, monitor, and local WiFi network. If this is not the case, please take a few moments to set this up. You can review our tutorial on setting up the Pi here.
Let’s start from the desktop of the Raspberry Pi. You should have a screen up that looks something like this:
You’ll need to click the little logo at the top of the screen (shown below) to open a command line. The rest of this tutorial will assume that you have that command line open.
That will open a command line window. This allows you to tell the Raspberry Pi to directly execute commands.
We’ll start by executing the command to install Flask. Flask is a web framework for Python that allows you to make a web front end that runs Python scripts on the sever backend fairly trivially. Type in the following command, then hit “Enter”.
sudo pip install flask
A whole bunch of stuff will happen in the command line window and at the end of it, Flask will be installed on your Raspberry Pi.
The next step is to install the software that we’ve written to support this project from GitHub. The command for doing that is
git clone https://github.com/sparkfun/Flask_Tutorial
Again, you’ll see some text scroll across the command line, and when the prompt returns, that’ll be your indication that the install process is complete. Once that’s complete, enter this command:
sudo python Flask_Tutorial/Python/app.py
That will launch the app and begin listening for input over TCP/IP from the Pioneer board. You should now be able to turn the LED connected to the Raspberry Pi on and off by pressing the D7 and D9 buttons on the IoT Shield. Neat!
So What’s Going On Here? Pt. 1: The Pioneer Kit
Let’s take a look at what exactly is happening, starting from a high level view of the PSoC 6 software project. Look at the Workspace Explorer frame on the left hand side of the screen. We’ll walk through that frame, highlighting the files of importance and how they relate to the overall project.
Having a hard time seeing the Workspace Explorer? Click the image for a closer look.
The top level of the project has six entries: the schematic (“TopDesign.sch”), the Design Wide Resources (“XBee_WiFi_Example.cydwr”), the source files associated with the Cortex-M0+ core (“CM0p (Core 0)”), the source files associated with the Cortex-M4 core (“CM4 (Core 1)”), files to be shared between the two (“Shared Files”), and support files generated by the IDE (“Generated_Source”).
The schematic for this project is very simple, having a couple of LEDs, a couple of switches, and the UART used to transfer data to and from the XBee WiFi module. The LEDs are actually unused in the current implementation of the project.
We’re going to skip over the contents of the .cydwr file. This file contains the pin assignments for the signals used in the schematic, the clock generation, and core configuration constants. If you want to investigate it more, feel free to dig in a little bit. Much of it should be self-explanatory.
Moving on down the list, we reach our Cortex-M0+ source files. You’ll note that the top level in this subdomain has five entries: “Header Files”, “Source Files”, and three other files. We only need concern ourselves with the contents of the “Header Files” and “Source Files” subdomains, and in fact, only with one file in those: the “main_cm0p.c” file. This is where the main() function for the code which runs on the Cortex-M0+ processor lives.
As you may have guessed from the structure of the workspace, there are two entirely separate codebases running for the two different cores. “main_cm0p.c” is the entry point for the Cortex-M0+ core’s code, and then that core starts the Cortex-M4 core. There exists a similar subdomain for the Cortex-M4 core with similar files and again, we only need to worry about the “Header Files” and “Source Files” subdomains.
Finally, we have the “Shared Files” section. Most of these files are automatically generated, save the “ipc_common.c” and “ipc_common.h” files. These files are helpers for interprocess communication developed for this project.
The Cortex-M0+ Main File
Now that we’ve highlighted the important contents, let’s take a look at the important bits of the code, one file at a time, starting with the “main_cm0p.c” file. This file handles all the activity that the Cortex-M0+ core does for the system: monitoring the two pushbuttons and sending a signal to the Cortex-M4 when one or the other of them is pressed.
However, this is not as straightforward as it seems, as the Cortex-M4 needs to be able to clear the signal once it has dealt with it, and that means multiple processes accessing the same data. Any time you have multiple processes working on the same dataset, you need to consider the effects of a write collision. What happens if one process attempts to change the data in the middle of the other process trying to change the same data? To deal with this we use a system protected reads and writes, set up in the “ipc_common” files.
To understand how this works, one must first understand the concept of an IPC channel. IPC channels use semaphores to write data from one process to another while guaranteeing that there will be no collision between the two core. At the beginning of application execution for each core you must establish endpoints for the IPC channels to be used during execution. Consider these two lines of code:
D9IpcHandle = Cy_IPC_Drv_GetIpcBaseAddress(7);
The first creates a pointer for a struct which defines the characteristics of an IPC channel. The second actually sets that struct to point to a specific memory location, that of system IPC channel 7. We use channel 7 because channels 0-6 are reserved for system use.
Next we must, of course, tell the other core which memory address is associated with this IPC channel. That is what this function call does.
while(Cy_IPC_Drv_SendMsgPtr(D9IpcHandle, CY_IPC_NO_NOTIFICATION, &D9Button) != CY_IPC_DRV_SUCCESS);
D9Button is a variable set up earlier in the code. The function call is enclosed in a while() loop because we want to repeat the function call until we receive verification that the other process (i.e., the code running on the Cortex-M4 core) has received this information. We also want to wait until the lock on the variable is released, indicating that the Cortex-M4 has finished reading the pointer value.
Finally, we drop into our infinite loop for the application, where the custom functions ReadSharedVar() and WriteSharedVar() handle updating the shared variables which communicate button status with the other core. We’ll delve into those functions later.
The Cortex-M4 Main File
In the Cortex-M4 main() function, we repeat some of the same operations as we did in the Cortex-M0+ main() function vis-a-vis setting up of the IPC channel.
D9IpcHandle = Cy_IPC_Drv_GetIpcBaseAddress(7);
Once that’s completed we must then call some code to “catch” the message that was sent from the Cortex-M0+ process, containing the address of the shared variable to be accessed.
while (Cy_IPC_Drv_ReadMsgPtr(D9IpcHandle, (void *)&D9Button) != CY_IPC_DRV_SUCCESS);
Again, we enclose this call in a while() loop so that it gets continually called until the message is sent from the Cortex-M0+. Then we must release the lock on that IPC channel so that the Cortex-M0+ process knows that it can continue operation and use the IPC channel in the future. There is no need to enclose this in a while() loop because it is open ended: the call only needs to be issued once to release the lock, versus checking that the lock has been released, which must be repeated until such time as the lock is released.
Once all of this has been completed, we must set up the XBee WiFi shield to access our local network. We won’t duplicate all that code here, as it’s well documented in the example.
In the infinite loop that runs the application code, we again call the custom functions ReadSharedVar() and WriteSharedVar() to access the variables holding the button status, which are shared with the Cortex-M0+ core. Let’s take a closer look at what these functions do.
Read More Info……