RoadTest: Panasonic PAN 1780 Bluetooth LE Eval Kit
Author: BigG
Creation date:
Evaluation Type: Development Boards & Tools
Did you receive all parts the manufacturer stated would be included in the package?: True
What other parts do you consider comparable to this product?: Nordic Semiconductor's nRF52840 DK, Taiyo Yuden's EKSKJNZWB, Fanstel's EV-BT840F, u-blox's BMD-340-EVAL, Laird’s BL654 development kit
What were the biggest problems encountered?: Website navigation, understanding how 3v3 / 5v connection worked on dev board
Detailed Review:
It’s seldom that I come across an evaluation kit (EVK) which includes two development boards and 2 NFC antennae. So I was mightily pleased when I was notified that I was selected to road test this EVK as I could foresee that having two boards would provide more flexibility to test novel applications using the PAN1780 module in either a central and/or peripheral roles. I also could see that having two development boards would make it easier to carry out specific functional tests such as throughput testing using different coded PHY settings and with an output power of up to 8 dBm.
So is this evaluation kit really useful for rapid application development and testing?
Let’s find out.
According to the Panasonic product literature, they have two Bluetooth 5.0 modules in production. One is the PAN1740, which is an ARM Cortex M0 (Dialog DA14585) based IC, with 96kB SRAM and 64kB Flash/OTP, and the other is the PAN1780, which is an ARM Cortex M4 (Nordic nRF52840) based IC, with 256kB RAM and 1MB Flash.
According to the Nordic Semiconductor website there are over fortynRF52840 third party modules available on the market at the moment. Not all 3rd party modules break out all available pins. The PAN1780 module breaks out 48 pins, which is the maximum available, and provides all available peripherals such as UART, I2C, I2S, PDM, SPI (32 MHz?), QSPI (32MHz), PWM, 8 channel 12 bit ADC, Temperature Sensor, AES and CRYPTOCELL (and more).
The PAN1780 module is one of the more compact around when compared to similar modules (>45 pins):
When I compared the Panasonic PAN1780 to other nRF52840 SoC modules offering a similar GPIO counts (+45) on distributor websites, I found this module to be very competitively priced (both for a single purchase and at volume).
{gallery:autoplay=false} Development Kit |
---|
The evaluation kit consists of two PAN1780 development boards and two NFC-A tag antennas (anti-static bags not shown). It does not include any USB cables (I have plenty so this was actually appreciated).
The kit is neatly packaged inside a slide-out tray. The branded sleeve contains all a stick-on label with a QR code for scanning and a URL to obtain for further information and technical documentation. On the rear of the sleeve there are four bullet points, which really don’t tell you anything, and the addresses of 3 Panasonic locations.
According to Panasonic documentation (WM PAN1780 Module Integration Guide), the reference design of the 4-layer evaluation board, including the provided peripherals, was inspired by the design of the nRF52840 Development Kit from Nordic Semiconductors. It does not however, offer a similar Arduino R3 pinout profile. This development board layout is unique to Panasonic.
The development board is breadboard friendly but only with a wider option (model shown here is the RS PRO Solderless Breadboard 83 x 147 x 19mm). It does not fit on a more common 400-point solderless breadboard. Alternatively you could also use two smaller breadboards, which I found worked pretty well too.
{gallery:autoplay=false} Solderless breadboard options |
---|
The development board provides 3 voltage outputs: 1.8V, 3.3V and 5V. There are 23 pins available for use as well as four buttons and four user LED's, which all use additional GPIO's.
The schematics are not provided as a separate document. Instead they are found in the pdf document (WM PAN1780 Module Integration Guide). I have extracted these out for reference.
{gallery:autoplay=false} Schematics |
---|
The development board schematic did reveal a couple of little surprises.
The first surprise is also revealed in the “building blocks” diagram found in the PAN1780 integration documentation.
This development board includes a USB Hub (uses a Microchip component: USB2512B), with two serial ports exposed when a USB cable is attached to USB “X2” (on a Linux OS computer these show up as ttyACM# and ttyUSB#). A FT232RQ chip is then connected to the USB2512B’s USB DN2 IO pins and forms a bridge between the USB hub and PAN1780 via the designated TX/RX/CTS/RTS IO pins. This seemed quite an elaborate setup, which I had never come across before but it works especially when handling debugging and that’s the main thing.
There is also another USB port labelled “X5” which is a direct connection to the nRF52840’s D+ (G5) and D- (G6) data IO lines. I never tested this option. I would hazard a guess and suggest that this probably provides you with an option to set up and test a virtual serial port.
Then there is this JP12 breakout, which is labelled in the Integration Guide as “Module VCC 5V Breakout”. Without a search facility it was rather difficult to spot where JP12 was on the schematic.
Lastly, the dev board actually has two onboard 3V3 LDO’s (LD3985 and LD39100). They are not easy to find but here they are:
Now, when it came to using the dev board, I found a couple of things, which I did not really like. The first was that the user LED’s are not prominent enough compared to the 1 x red and the 2 x green LED’s, which are too bright in my opinion (and a little distracting). The second was that I had one occasion, when trying to place a TFT module directly onto my breadboard where the USB port got in the way when I had a USB cable connected. Finally, it was not that clear how the 5V or 3V3 power output pins had to be manually set using shunt connectors.
{gallery:autoplay=false} Devboard Issues |
---|
The thing I really liked about this kit was the price. I could not find any other option where I got two development boards with debugging capability via USB for the same price. You could purchase 2 x Adafruit Feather nRF52840 Express development boards for around $50 but this does not include the JTAG / SWD debugger.
As mentioned in the previous section, the PAN1780 evaluation kit arrives in a nicely branded slim box with a slide out tray. On the front side of the packaging there is a sticker giving you a URL (website address) and a QR code to get you started.
I really liked the QR code and saw this as a very good start to the "getting to know you" process. However…
If I had typed in the URL address myself I would’ve thought that this was some sort of mistake. Naturally I looked to the top menu for guidance but clicking on “Products” only left me more confused.
So I returned back to the landing page… and pondered a bit. There was absolutely nothing on that landing page to tell the user what to do next. What a bizarre starting point. Since when should a customer have to choose a geographic location just to learn about a product. In my opinion, this landing page unravelled all the good work that had been put into the product packaging.
I decided to click on the European (EU) Sales Office location link first to see what next is presented on this website. Here you are presented with a generic landing page about Wireless Connectivity. It was not product specific.
The North American (NA) Sales Office website was no better.
So I thought why not use the search option to see if it pulls up what I need to know. When I did this, I thought it was rather odd to see how different the user experience was, depending on which website you chose. The results returned were also quite different. The EU website took longer but returned with a comprehensive list of documents. The NA option only returned one result.
{gallery:autoplay=false} Search Results |
---|
This was not encouraging, but I pushed on.
I returned to the landing page(s) and scrolled down. Here the EU website provided you with a very clear click through option to get to the product details while, in my opinion, the NA website was less clear.
{gallery:autoplay=false} Click through options |
---|
Clicking through I was then presented with another set of choices. Here the NA website provided me with a more direct route to what I was after as it provided me with an option for “evaluation kits”. The EU website simply focused on the module.
{gallery:autoplay=false} ClickThrough_Modules |
---|
I was finally down at the website detail I needed. So while the EU webpage did not have a direct link to the evaluation kit, this webpage actually contained more relevant content the NA webpage. The EU page provided a nice Technical Specification table and there was a comprehensive list of documents at the bottom of the page. The NA page on the other hand only had a “resources” button, which when clicked just provided you with 2 document links.
{gallery:autoplay=false} Web content |
---|
It was time to evaluate the documentation and start learning how to use this PAN1780 dev board.
Looking at the documentation list provided on the EU or the NA website, it was not immediately clear which document provided information about the development board.
It took some searching but I did find this information in the EU document titled “WM PAN1780 Module Integration Guide” or the NA document title “Panasonic PAN1780 Series Module Integration Guide”. It turns out that the EU website document is version 1.2 while the NA website has document version 1.1. Looking at the dates in the revision history (section 1.2 of either document), it looks like EU version 1.2 is in fact the later version of the same document found on the NA website.
Beside this confusion over document versions, I found this document to give me everything I needed although it was on the brief side and I suspect a person who has never used some of the required software like SES or nRF5 SDK would struggle. Overall, I thought that this document was nicely structured and the content was fairly easy to follow.
In my opinion, it would have been nice to have the schematics in a separate PDF document for easier viewing.
One aspect, which I found was difficult to grasp straight away was the configuration settings section, especially the option for module UART to breakout pin or FTDI. As I did not need to change this config, I left this well alone as it worked as is for my requirements.
Section 5 of this document covers the overall “Getting Started” process and should not be confused with section 5.5 which is also titled “Getting Started”.
As I already had the latest version of Segger Embedded Studio (SES) and the relevant toolchain installed, which includes all the latest Segger J-Link SWD Debugger and FTDI drivers, I did not spend much time looking at this section in detail. The document does provide links to the Segger website for all the relevant information. I did not check to see if these links still work (as there is nothing worse than broken html links in a PDF document). Suffice to say that the installation details provided on the Segger website is pretty good and I did not experience any problems installing using this information. So, if you are having problems understanding the brief instructions given in this document, I would suggest that you rely more on the Segger website for installation details.
Similarly, I also did not experience any problems getting all the relevant Nordic Semiconductor software and SDK’s downloaded and installed onto my Linux OS (Ubuntu 18.04) computer.
Looking at this document, it does appear to be giving you more than you really need. For example, if you do not have a Bluetooth module on your computer or you do not intend to link your computer with the device via Bluetooth then there is no real need for the nRFconnect desktop software. It is worth noting that Segger Embedded Studio also allows you to select and flash your device with precompiled hex files once you have connected your computer to the device via the USB cable.
So putting the documentation aside it’s time to try some examples.
As someone who is now more familiar with the nRF52 SDK it seemed pointless to start at the very beginning with a “blinky” example. I wanted to jump straight in and test something more advanced which needed two development boards.
So, the obvious example to start with, using our PAN1780’s, was the BLE throughput performance test.
This example can be found in the BLE Central and Peripheral examples folder within your nRF5 SDK folder.
To use this example it is important to first understand the parameters that have been exposed and what the default settings are. As you will discover, these are far from optimised. In fact as my video demo will show (if you can make out the serial terminal output) the throughput achieved using the default settings was circa. 340 kilobits per sec.
To achieve close to the optimum throughput values of > 1.3Mbits per second you would need to adjust the Data length to 251 and the Connection interval to 320 units (400 ms).
Another PAN1780 road tester (parasquid) provided a very useful reference website in one of their PAN1780 blogs. I’m including the same link for the sake of convenience: https://www.novelbits.io/bluetooth-5-speed-maximum-throughput/
Here is a short video demonstrating just how easy it is to use a pair of PAN1780 dev boards to perform this test. As you will see, no modification was required to get the code to work. Note that I achieved similar throughput values as presented in that parasquid blog (note that my test results are not shown in the video). Certainly with these two boards you have a great opportunity to experiment with the different parameters.
For example, I discovered that with smaller data lengths of less that 64 bytes you achieved a higher throughput result using the 1M PHY (I never knew that).
For example, I discovered that if you do not record and then validate your findings you will come up with the wrong conclusions. I discovered that when trying to repeat this "phy discovery" it proved my conclusion wrong. A 2M PHY is still quicker than a 1M PHY with smaller data lengths... and here are my findings.
Thankfully with this dev board these tests were fairly quick to perform.
In fact I found that almost all of the code examples I tested worked perfectly without modification*. It was only when I needed to add in sensors (e.g. hardware peripheral examples) that I had modify the code so that the firmware knew my chosen pin IO’s etc.
*Well, I think I have discovered one example that does require modification. This is with the ANT multichannel encryption example.
The code example provided in the SDK is only available for nRF52832 devices, even though soft device s212 is "theoretically" compatible with nRF52840 devices. When I get the chance I will find out.
TBC...
So in light of the fact that I had no problems with the examples I decided to move straight onto the next section where I review my experience stepping out and testing something new.
For my "stepping out" attempt, I decided to create an application that used the key features of the development board, namely BLE and NFC. I decided to also add in a hardware peripheral API as I thought an e-paper display would be a great addition as this uses the SPI bus. So I came up this design which would display a QR code on the EPD and would use NDEF messaging to send a URL.
In this project I am using a Waveshare EPD module and the nRF52840 chipset's built-in NFC tag type-4 capability.
I am using a port of the Waveshare demo code library which can be found on the Waveshare GitHub page. For QR code generation I am using another open source library which is also on GitHub. For the NDEF URL write code I based this off the NFC Type 4 example which is found in the NordicSemi nRF5 SDK.
I initially used the nRFX_SPIM library but I discovered that this does not work very well when you incorporate the BLE functionality (I used the s140 soft device option). So I reverted back to the nRF_SPI driver library.
Here is the main.c code for reference:
/** * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** @file * * @defgroup ble_sdk_uart_over_ble_main main.c * @{ * @ingroup ble_sdk_app_nus_eval * @brief UART over BLE application main file. * * This file contains the source code for a sample application that uses the Nordic UART service. * This application uses the @ref srvlib_conn_params module. */ #include <stdint.h> #include <string.h> #include "nordic_common.h" #include "nrf.h" #include "ble_hci.h" #include "ble_advdata.h" #include "ble_advertising.h" #include "ble_conn_params.h" #include "nrf_sdh.h" #include "nrf_sdh_soc.h" #include "nrf_sdh_ble.h" #include "nrf_ble_gatt.h" #include "nrf_ble_qwr.h" #include "app_timer.h" #include "ble_nus.h" #include "app_uart.h" #include "app_util_platform.h" #include "bsp_btn_ble.h" #include "nrf_pwr_mgmt.h" #if defined (UART_PRESENT) #include "nrf_uart.h" #endif #if defined (UARTE_PRESENT) #include "nrf_uarte.h" #endif #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "EPD_2in9.h" // Image Data #include "pic/ImageData.h" // QR code generator #include "QRcode/qrcodegen.h" // NFC NDEF #include "nfc_t4t_lib.h" #include "nrf_log_ctrl.h" #include "nfc_uri_msg.h" static const char *DefltText = "https://pideu.panasonic.de/products/wireless-modules.html"; // Global buffer used QR code and NFC tag static char *EPDNFCtext; // NFC NDEF buffer uint8_t m_ndef_msg_buf[NDEF_FILE_SIZE]; /**< Buffer for NDEF file. */ static nfc_uri_id_t URI_IDcode; //Create a new image cache for EPD static uint8_t *BlackImage; // Flag to update the EPD volatile bool updateEPD; // Flag to clear the screen volatile bool clearEPD; #define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. */ #define DEVICE_NAME "PAN1780" /**< Name of device. Will be included in the advertising data. */ #define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN /**< UUID type for the Nordic UART Service (vendor specific). */ #define APP_BLE_OBSERVER_PRIO 3 /**< Application's BLE observer priority. You shouldn't need to modify this value. */ #define APP_ADV_INTERVAL 64 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */ #define APP_ADV_DURATION 18000 /**< The advertising duration (180 seconds) in units of 10 milliseconds. */ #define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(75, UNIT_1_25_MS) /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */ #define SLAVE_LATENCY 0 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */ #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 256 /**< UART RX buffer size. */ BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */ NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */ NRF_BLE_QWR_DEF(m_qwr); /**< Context for the Queued Write module.*/ BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */ static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */ static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifier. */ { {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE} }; /**@brief Function for assert macro callback. * * @details This function will be called in case of an assert in the SoftDevice. * * @warning This handler is an example only and does not fit a final product. You need to analyse * how your product is supposed to react in case of Assert. * @warning On assert from the SoftDevice, the system can only recover on reset. * * @param[in] line_num Line number of the failing ASSERT call. * @param[in] p_file_name File name of the failing ASSERT call. */ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) { app_error_handler(DEAD_BEEF, line_num, p_file_name); } /**@brief Function for initializing the timer module. */ static void timers_init(void) { ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); } /** * @brief Callback function for handling NFC events. */ static void nfc_callback(void * p_context, nfc_t4t_event_t event, const uint8_t * data, size_t dataLength, uint32_t flags) { (void)p_context; switch (event) { case NFC_T4T_EVENT_FIELD_ON: bsp_board_led_on(BSP_BOARD_LED_1); break; case NFC_T4T_EVENT_FIELD_OFF: bsp_board_led_off(BSP_BOARD_LED_1); bsp_board_led_off(BSP_BOARD_LED_2); break; case NFC_T4T_EVENT_NDEF_READ: bsp_board_led_on(BSP_BOARD_LED_2); break; default: break; } } /**@brief Function for generating and display a QR Code. */ static bool GenerateDisplay_QRcode(uint8_t QRstarted) { // QR Code Generator enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_MEDIUM; // Error correction level (qrcodegen_Ecc_LOW, qrcodegen_Ecc_MEDIUM) // Make and print the QR Code symbol uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; bool QR_ok = false; if (!QRstarted) { printf("...initialise QR text: %s\r\n", DefltText); QR_ok = qrcodegen_encodeText(DefltText, tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); } else { if (EPDNFCtext != NULL) { printf("...update QR text: %s\r\n", EPDNFCtext); QR_ok = qrcodegen_encodeText((const char*)EPDNFCtext, tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); } } if (QR_ok) { int size = qrcodegen_getSize(qrcode); uint8_t prntScale = 3; uint8_t x_offset = 45; uint8_t y_offset = 15; if ((size - 17) / 4 == 2) y_offset = 30; printf("QR code ready (size: %d x %d)...\r\n", (size - 17) / 4, (size - 17) / 4); Paint_SelectImage(BlackImage); Paint_Clear(WHITE); for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) Paint_DrawRectangle(x_offset + x*prntScale, y_offset + y*prntScale, x_offset + (x+1)*prntScale, y_offset + (y+1)*prntScale, qrcodegen_getModule(qrcode, x, y) ? BLACK : WHITE, DOT_PIXEL_1X1, DRAW_FILL_FULL); } Paint_DrawString_EN(165, 55, "SCAN ME!", &Font20, WHITE, BLACK); EPD_2IN9_Display(BlackImage); printf("QR Code updated.\r\n"); return true; } return false; } static void GenerateDisplay_NFCtag(uint8_t NFCstarted) { /* Provide information about available buffer size to encoding function */ uint32_t ndeflen = sizeof(m_ndef_msg_buf); /* Initialise the URI message buffer */ memset(m_ndef_msg_buf, '\0', ndeflen); if (NFCstarted) { printf("stop NFC emulation\r\n"); APP_ERROR_CHECK(nfc_t4t_emulation_stop()); printf("...update NFC text: %s\r\n", EPDNFCtext); uint8_t offset = 0; switch(URI_IDcode) { case NFC_URI_HTTP_WWW: offset = 11; break; case NFC_URI_HTTPS_WWW: offset = 12; break; case NFC_URI_HTTP: offset = 7; break; case NFC_URI_HTTPS: offset = 8; break; } APP_ERROR_CHECK(nfc_uri_msg_encode(URI_IDcode, EPDNFCtext+offset, strlen(EPDNFCtext)-offset, m_ndef_msg_buf, &ndeflen)); /* Set created message as the NFC payload */ APP_ERROR_CHECK(nfc_t4t_ndef_rwpayload_set(m_ndef_msg_buf, ndeflen)); } else { printf("...initialise NFC text: %s\r\n", DefltText); APP_ERROR_CHECK(nfc_uri_msg_encode(NFC_URI_HTTPS, DefltText+8, strlen(DefltText)-8, m_ndef_msg_buf, &ndeflen)); } /* Set created message as the NFC payload */ APP_ERROR_CHECK(nfc_t4t_ndef_rwpayload_set(NULL, 0)); APP_ERROR_CHECK(nfc_t4t_ndef_rwpayload_set(m_ndef_msg_buf, ndeflen)); /* Start sensing NFC field */ APP_ERROR_CHECK(nfc_t4t_emulation_start()); printf("NFC Tag started.\r\n"); } /**@brief Function for the GAP initialization. * * @details This function will set up all the necessary GAP (Generic Access Profile) parameters of * the device. It also sets the permissions and appearance. */ static void gap_params_init(void) { uint32_t err_code; ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *) DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); memset(&gap_conn_params, 0, sizeof(gap_conn_params)); gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; gap_conn_params.slave_latency = SLAVE_LATENCY; gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; err_code = sd_ble_gap_ppcp_set(&gap_conn_params); APP_ERROR_CHECK(err_code); } /**@brief Function for handling Queued Write Module errors. * * @details A pointer to this function will be passed to each service which may need to inform the * application about an error. * * @param[in] nrf_error Error code containing information about what went wrong. */ static void nrf_qwr_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } /**@brief Function for handling the data from the Nordic UART Service. * * @details This function will process the data received from the Nordic UART BLE Service and send * it to the UART module. It will update EPD and NFC Tag if it detects a URL * * @param[in] p_evt Nordic UART Service event. */ /**@snippet [Handling the data received over BLE] */ static void nus_data_handler(ble_nus_evt_t * p_evt) { const char* URLchecker[3] = { "http://", "https://", "www."}; if (p_evt->type == BLE_NUS_EVT_RX_DATA) { uint32_t err_code; uint16_t url_len; char TempText[NDEF_FILE_SIZE]; // reset the buffer memset(TempText, '\0', NDEF_FILE_SIZE); NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART."); NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length); for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++) { do { if (i < NDEF_FILE_SIZE) { TempText[i] = p_evt->params.rx_data.p_data[i]; url_len = i; } // Push character to UART err_code = app_uart_put(p_evt->params.rx_data.p_data[i]); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) { NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code); APP_ERROR_CHECK(err_code); } } while (err_code == NRF_ERROR_BUSY); } if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r') { while (app_uart_put('\n') == NRF_ERROR_BUSY); } printf("\r\n"); // add this for formating serial output // Let's check what's in the message buffer url_len++; if (url_len > 7) { if (strcmp(TempText, URLchecker[0]) >= 0 || (strcmp(TempText, URLchecker[1]) >= 0)) { if (EPDNFCtext != NULL) free(EPDNFCtext); EPDNFCtext = malloc(url_len+1); memset(EPDNFCtext, '\0', url_len+1); memcpy(EPDNFCtext, TempText, url_len); printf("...valid URL detected: %s\r\n", EPDNFCtext); if (strcmp(TempText, URLchecker[0]) >= 0) { if (strcmp(TempText, URLchecker[2]) >= 0) URI_IDcode = NFC_URI_HTTP_WWW; else URI_IDcode = NFC_URI_HTTP; } else if (strcmp(TempText, URLchecker[1]) >= 0) { if (strcmp(TempText, URLchecker[2]) >= 0) URI_IDcode = NFC_URI_HTTP_WWW; else URI_IDcode = NFC_URI_HTTPS; } // reset this variable for reuse memset(TempText, '\0', url_len); url_len = 10; memcpy(TempText, "~valid_URL", url_len); updateEPD = true; } else { printf("...No URL format detected [%s]\r\n", TempText); memset(TempText, '\0', url_len); url_len = 12; memcpy(TempText, "~URL_invalid", url_len); } } else { memset(TempText, '\0', url_len); url_len = 14; memcpy(TempText, "~URL_too_short", url_len); printf("...String too short for URL\r\n"); } do { err_code = ble_nus_data_send(&m_nus, TempText, &url_len, m_conn_handle); if ((err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_RESOURCES) && (err_code != NRF_ERROR_NOT_FOUND)) { APP_ERROR_CHECK(err_code); } } while (err_code == NRF_ERROR_RESOURCES); } } /**@snippet [Handling the data received over BLE] */ /**@brief Function for initializing services that will be used by the application. */ static void services_init(void) { uint32_t err_code; ble_nus_init_t nus_init; nrf_ble_qwr_init_t qwr_init = {0}; // Initialize Queued Write Module. qwr_init.error_handler = nrf_qwr_error_handler; err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); APP_ERROR_CHECK(err_code); // Initialize NUS. memset(&nus_init, 0, sizeof(nus_init)); nus_init.data_handler = nus_data_handler; err_code = ble_nus_init(&m_nus, &nus_init); APP_ERROR_CHECK(err_code); } /**@brief Function for handling an event from the Connection Parameters Module. * * @details This function will be called for all events in the Connection Parameters Module * which are passed to the application. * * @note All this function does is to disconnect. This could have been done by simply setting * the disconnect_on_fail config parameter, but instead we use the event handler * mechanism to demonstrate its use. * * @param[in] p_evt Event received from the Connection Parameters Module. */ static void on_conn_params_evt(ble_conn_params_evt_t * p_evt) { uint32_t err_code; if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) { err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE); APP_ERROR_CHECK(err_code); } } /**@brief Function for handling errors from the Connection Parameters module. * * @param[in] nrf_error Error code containing information about what went wrong. */ static void conn_params_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } /**@brief Function for initializing the Connection Parameters module. */ static void conn_params_init(void) { uint32_t err_code; ble_conn_params_init_t cp_init; memset(&cp_init, 0, sizeof(cp_init)); cp_init.p_conn_params = NULL; cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; cp_init.disconnect_on_fail = false; cp_init.evt_handler = on_conn_params_evt; cp_init.error_handler = conn_params_error_handler; err_code = ble_conn_params_init(&cp_init); APP_ERROR_CHECK(err_code); } /**@brief Function for putting the chip into sleep mode. * * @note This function will not return. */ static void sleep_mode_enter(void) { uint32_t err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); // Prepare wakeup buttons. err_code = bsp_btn_ble_sleep_mode_prepare(); APP_ERROR_CHECK(err_code); // Go to system-off mode (this function will not return; wakeup will cause a reset). err_code = sd_power_system_off(); APP_ERROR_CHECK(err_code); } /**@brief Function for handling advertising events. * * @details This function will be called for advertising events which are passed to the application. * * @param[in] ble_adv_evt Advertising event. */ static void on_adv_evt(ble_adv_evt_t ble_adv_evt) { uint32_t err_code; switch (ble_adv_evt) { case BLE_ADV_EVT_FAST: err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING); APP_ERROR_CHECK(err_code); break; case BLE_ADV_EVT_IDLE: sleep_mode_enter(); break; default: break; } } /**@brief Function for handling BLE events. * * @param[in] p_ble_evt Bluetooth stack event. * @param[in] p_context Unused. */ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { uint32_t err_code; switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: printf("BLE Connected\r\n"); err_code = bsp_indication_set(BSP_INDICATE_CONNECTED); APP_ERROR_CHECK(err_code); m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle); APP_ERROR_CHECK(err_code); // Clear the EPD //EPD_2IN9_Clear(); break; case BLE_GAP_EVT_DISCONNECTED: printf("BLE Disconnected\r\n"); // LED indication will be changed when advertising starts. m_conn_handle = BLE_CONN_HANDLE_INVALID; // Display QR Code again on EPD break; case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { NRF_LOG_DEBUG("PHY update request."); ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_AUTO, .tx_phys = BLE_GAP_PHY_AUTO, }; err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); APP_ERROR_CHECK(err_code); } break; case BLE_GAP_EVT_SEC_PARAMS_REQUEST: // Pairing not supported err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); APP_ERROR_CHECK(err_code); break; case BLE_GATTS_EVT_SYS_ATTR_MISSING: // No system attributes have been stored. err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0); APP_ERROR_CHECK(err_code); break; case BLE_GATTC_EVT_TIMEOUT: // Disconnect on GATT Client timeout event. err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break; case BLE_GATTS_EVT_TIMEOUT: // Disconnect on GATT Server timeout event. err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break; default: // No implementation needed. break; } } /**@brief Function for the SoftDevice initialization. * * @details This function initializes the SoftDevice and the BLE event interrupt. */ static void ble_stack_init(void) { ret_code_t err_code; err_code = nrf_sdh_enable_request(); APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. uint32_t ram_start = 0; err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); APP_ERROR_CHECK(err_code); // Enable BLE stack. err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); // Register a handler for BLE events. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); } /**@brief Function for handling events from the GATT library. */ void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt) { if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)) { m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH; NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len); } NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x", p_gatt->att_mtu_desired_central, p_gatt->att_mtu_desired_periph); } /**@brief Function for initializing the GATT library. */ void gatt_init(void) { ret_code_t err_code; err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler); APP_ERROR_CHECK(err_code); err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE); APP_ERROR_CHECK(err_code); } /**@brief Function for handling events from the BSP module. * * @param[in] event Event generated by button press. */ void bsp_event_handler(bsp_event_t event) { uint32_t err_code; switch (event) { case BSP_EVENT_KEY_1: printf("Btn_2 - Clear Screen\r\n"); clearEPD = true; break; case BSP_EVENT_SLEEP: sleep_mode_enter(); break; case BSP_EVENT_DISCONNECT: err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } break; case BSP_EVENT_WHITELIST_OFF: if (m_conn_handle == BLE_CONN_HANDLE_INVALID) { err_code = ble_advertising_restart_without_whitelist(&m_advertising); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } } break; default: break; } } /**@brief Function for handling app_uart events. * * @details This function will receive a single character from the app_uart module and append it to * a string. The string will be be sent over BLE when the last character received was a * 'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length. */ /**@snippet [Handling the data received over UART] */ void uart_event_handle(app_uart_evt_t * p_event) { static uint8_t data_array[BLE_NUS_MAX_DATA_LEN]; static uint8_t index = 0; uint32_t err_code; switch (p_event->evt_type) { case APP_UART_DATA_READY: UNUSED_VARIABLE(app_uart_get(&data_array[index])); index++; if ((data_array[index - 1] == '\n') || (data_array[index - 1] == '\r') || (index >= m_ble_nus_max_data_len)) { if (index > 1) { NRF_LOG_DEBUG("Ready to send data over BLE NUS"); NRF_LOG_HEXDUMP_DEBUG(data_array, index); do { uint16_t length = (uint16_t)index; err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle); if ((err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_RESOURCES) && (err_code != NRF_ERROR_NOT_FOUND)) { APP_ERROR_CHECK(err_code); } } while (err_code == NRF_ERROR_RESOURCES); } index = 0; } break; case APP_UART_COMMUNICATION_ERROR: APP_ERROR_HANDLER(p_event->data.error_communication); break; case APP_UART_FIFO_ERROR: APP_ERROR_HANDLER(p_event->data.error_code); break; default: break; } } /**@snippet [Handling the data received over UART] */ /**@brief Function for initializing the UART module. */ /**@snippet [UART Initialization] */ static void uart_init(void) { uint32_t err_code; app_uart_comm_params_t const comm_params = { .rx_pin_no = RX_PIN_NUMBER, .tx_pin_no = TX_PIN_NUMBER, .rts_pin_no = RTS_PIN_NUMBER, .cts_pin_no = CTS_PIN_NUMBER, .flow_control = APP_UART_FLOW_CONTROL_DISABLED, .use_parity = false, #if defined (UART_PRESENT) .baud_rate = NRF_UART_BAUDRATE_115200 #else .baud_rate = NRF_UARTE_BAUDRATE_115200 #endif }; APP_UART_FIFO_INIT(&comm_params, UART_RX_BUF_SIZE, UART_TX_BUF_SIZE, uart_event_handle, APP_IRQ_PRIORITY_LOWEST, err_code); APP_ERROR_CHECK(err_code); } /**@snippet [UART Initialization] */ /**@brief Function for initializing the Advertising functionality. */ static void advertising_init(void) { uint32_t err_code; ble_advertising_init_t init; memset(&init, 0, sizeof(init)); init.advdata.name_type = BLE_ADVDATA_FULL_NAME; init.advdata.include_appearance = false; init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE; init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); init.srdata.uuids_complete.p_uuids = m_adv_uuids; init.config.ble_adv_fast_enabled = true; init.config.ble_adv_fast_interval = APP_ADV_INTERVAL; init.config.ble_adv_fast_timeout = APP_ADV_DURATION; init.evt_handler = on_adv_evt; err_code = ble_advertising_init(&m_advertising, &init); APP_ERROR_CHECK(err_code); ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); } /**@brief Function for initializing buttons and leds. * * @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up. */ static void buttons_leds_init(bool * p_erase_bonds) { bsp_event_t startup_event; uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler); APP_ERROR_CHECK(err_code); err_code = bsp_btn_ble_init(NULL, &startup_event); APP_ERROR_CHECK(err_code); *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA); } /**@brief Function for initializing the nrf log module. */ static void log_init(void) { ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } /**@brief Function for initializing power management. */ static void power_management_init(void) { ret_code_t err_code; err_code = nrf_pwr_mgmt_init(); APP_ERROR_CHECK(err_code); } /**@brief Function for handling the idle state (main loop). * * @details If there is no pending log operation, then sleep until next the next event occurs. */ static void idle_state_handle(void) { if (NRF_LOG_PROCESS() == false) { nrf_pwr_mgmt_run(); } } /**@brief Function for starting advertising. */ static void advertising_start(void) { uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); } /**@brief Application main function. */ int main(void) { bool erase_bonds; updateEPD = false; clearEPD = false; // Initialize. uart_init(); log_init(); timers_init(); buttons_leds_init(&erase_bonds); power_management_init(); printf("\r\nNRF_SPI EPD_2IN9 NFC & BLE Demo.\r\n"); NRF_LOG_FLUSH(); EPD_2IN9_Init(); EPD_2IN9_SetRefreshType(EPD_2IN9_FULL); NRF_LOG_INFO("e-Paper Init"); NRF_LOG_FLUSH(); EPD_2IN9_Clear(); /* you have to edit the startup_stm32fxxx.s file and set a big enough heap size */ uint16_t Imagesize = ((EPD_2IN9_WIDTH % 8 == 0)? (EPD_2IN9_WIDTH / 8 ): (EPD_2IN9_WIDTH / 8 + 1)) * EPD_2IN9_HEIGHT; if((BlackImage = (uint8_t *)malloc(Imagesize)) == NULL) { NRF_LOG_ERROR("Failed to apply for black memory..."); NRF_LOG_FLUSH(); return -1; } NRF_LOG_INFO("Show Panasonic PAN1780 Image..."); NRF_LOG_FLUSH(); Paint_NewImage(BlackImage, EPD_2IN9_WIDTH, EPD_2IN9_HEIGHT, 90, WHITE); Paint_SelectImage(BlackImage); Paint_Clear(WHITE); Paint_DrawBitMap(gImage_2in9); EPD_2IN9_Display(BlackImage); nrf_delay_ms(1000); /* Set up NFC */ NRF_LOG_INFO("Initialising NFC..."); NRF_LOG_FLUSH(); if (GenerateDisplay_QRcode(0)) { APP_ERROR_CHECK(nfc_t4t_setup(nfc_callback, NULL)); GenerateDisplay_NFCtag(0); } ble_stack_init(); gap_params_init(); gatt_init(); services_init(); advertising_init(); conn_params_init(); // Start execution. NRF_LOG_INFO("BLE UART advertising starting..."); NRF_LOG_FLUSH(); printf("APP is ready!\r\n"); nrf_delay_ms(100); advertising_start(); // Enter main loop. for (;;) { if (updateEPD) { if (GenerateDisplay_QRcode(1)) GenerateDisplay_NFCtag(1); updateEPD = false; } else { if (clearEPD) { EPD_2IN9_Clear(); clearEPD = false; } else idle_state_handle(); } } } /** * @} */
Overall I found that this project was not too difficult to get working on Segger Embedded Studio, thanks to the PAN1780 dev board debugging feature.
So my overall impression is that I found the PAN1780 Evaluation Kit to be very useful and it certainly helped me with my development effort. I very much liked the fact that you got two development boards in the kit and there is also an option to buy a single board if you need more.
Initially I thought that the board dimensions were rather odd as it did not fit on a standard 400-point solderless breadboard but once I found a solution I actually found this format to work pretty well. The only criticism that remains are the three LED's which are way too bright - it makes no sense using such a low resistance value (270ohm) for this purpose. Also the USB cable position did get in the way on occasion.
Given the price for this kit, I certainly think this kit is great value. So, if you are looking for a suitable nRF52840 development kit this is definitely one you should seriously consider.
Top Comments
There are options to use different RF channels but these are all local area network stuff, such as BLE, ANT, WPAN (802.15.4), or proprietary. The Nordic SDK does have one multi-shared RF channel type example…
Hallo (hello) Lara
Thank you for your comments. It is very much appreciated.
Best regards
Colin Gerrish