Update Project to ModusToolbox 2.2

Summary

This article walks you through the steps that I took to convert a ModusToolbox 2.1 project using the “dot lib” flow into a Modus Toolbox 2.2 project using the “mtb” flow.

Story

I have been working on way way to many different things and not finishing nearly enough things.  Oh well.  One of the projects that I have been working on is an implementation of a Bluetooth Observer to process iBeacon packets from a Tilt Hydrometer (more on that in a future article).  This project was started in Modus Toolbox 2.1.  It will continue to work perfectly in ModusToolbox 2.2 as it is 100% backwards compatible, but, I want to use some of the new features of 2.2.

As I discussed in a previous article, there are now two library systems in Modus Toolbox.

  • The “mtb” flow – which allows for “shared libraries”
  • The “dot lib” flow – which has all libraries embedded into the project.

If you want to convert your Modus Toolbox 2.1 project Modus Toolbox 2.2 you can follow a process like this:

  1. Backup the project into Git
  2. Create a template Modus Toolbox 2.2 project (so I have a “clean” copy of the Makefile and “mtb” files)
  3. Fix the Makefile
  4. Examine and Update the library dependencies
  5. Test

Backup Everything

Before you torch your perfectly good project you should back it up.  I typically use Git.  Actually I always use Git.  As I wrote this article I realized that I should have tagged the version before I started the updates.  To figure out which commit that was I ran a “git log” and then I scrolled through my new commits until I found the last one before the conversion to 2.2.  Which requires me to admit that when I left the project a couple of weeks ago there were two files which I hadn’t checked.  So I blindly checked them in and then told the truth in my comment.

commit c256b822b2c956b17ec6cb35868e171d71dda0fe (HEAD, tag: mtb2.1)
Author: iotexpert <engineer@iotexpert.com>
Date:   Sat Oct 10 06:47:40 2020 -0400

    updates which I have no idea what they are

Now that I have the right commit, I tagged with with a “mtb 2.1” tag.

arh (master) TiltHydrometer $ git tag -a "mtb2.1" c256b822b2c956b17ec6cb35868e171d71dda0fe

Now I can go back to my 2.1 project by running “git checkout mtb2.1”

Create a Template Project

In order to use the new flow you need to do two things.

  1. Fix the Makefile
  2. Change the “dot lib” files to “mtb” files

I never can remember the secret incantation to put in either place.  So, I create a blank project called “Simple22” to allow me to steal the bits that I want.

Makefile

In Modus Toolbox 2.2 we added two new variables to the Makefile which allow you to specify the location of the shared library.  First, here is the relevant section of the original 2.1 Makefile

################################################################################
# Paths
################################################################################

# Relative path to the project directory (default is the Makefile's directory).
#
# This controls where automatic source code discovery looks for code.
CY_APP_PATH=

# Relative path to the "base" library. It provides the core makefile build
# infrastructure.
CY_BASELIB_PATH=libs/psoc6make

# Absolute path to the compiler's "bin" directory.
#
# The default depends on the selected TOOLCHAIN (GCC_ARM uses the ModusToolbox
# IDE provided compiler by default).
CY_COMPILER_PATH=

And the new 2.2 Makefile where you can see the two new variables

  • CY_GETLIBS_SHARED_PATH=../
  • CY_GETLIBS_SHARED_NAME=mtb_shared
################################################################################
# Paths
################################################################################

# Relative path to the project directory (default is the Makefile's directory).
#
# This controls where automatic source code discovery looks for code.
CY_APP_PATH=

# Relative path to the shared repo location.
#
# All .mtb files have the format, <URI>#<COMMIT>#<LOCATION>. If the <LOCATION> field 
# begins with $$ASSET_REPO$$, then the repo is deposited in the path specified by 
# the CY_GETLIBS_SHARED_PATH variable. The default location is one directory level 
# above the current app directory.
# This is used with CY_GETLIBS_SHARED_NAME variable, which specifies the directory name.
CY_GETLIBS_SHARED_PATH=../

# Directory name of the shared repo location.
#
CY_GETLIBS_SHARED_NAME=mtb_shared

Dependencies & Libraries

The next thing to do is look at what libraries are in my original project.  To do this run the Library manager via “make modlibs”

All of these libraries are added to the project by creating “dot Libs” in the “deps” directory.  Specifically as I worked on the original project I added the libraries using the library manager, which created a dot lib for each library.  Here is a look at the original “deps” directory.

arh (master) TiltHydrometer $ ls deps
CY8CKIT-028-TFT.lib		abstraction-rtos.lib		display-tft-st7789v.lib		middleware-ntshell.lib		sensor-motion-bmi160.lib
TARGET_CY8CKIT-062S2-43012.lib	audio-codec-ak4954a.lib		emwin.lib			retarget-io.lib
TARGET_CY8CPROTO-062-4343W.lib	bluetooth-freertos.lib		freertos.lib			sensor-light.lib

When I did an “update” in Modus Toolbox 2.1 it ran “make depend” which brought in the libraries specified by the the “dot libs” into the “libs” directory.  Here it is a listing of the lib directory.

arh (master) TiltHydrometer $ ls libs
TARGET_CY8CKIT-062S2-43012	btstack.mtb			core-lib.mtb			mtb-hal-cat1.mtb		psoc6make
TARGET_CY8CPROTO-062-4343W	capsense			core-make.mtb			mtb-pdl-cat1.mtb		psoc6pdl
abstraction-rtos		capsense.mtb			display-tft-st7789v		mtb.mk				recipe-make-cat1a.mtb
abstraction-rtos.mtb		clib-support			emwin				psoc6cm0p			retarget-io
bluetooth-freertos		clib-support.mtb		freertos			psoc6cm0p.mtb
btstack				core-lib			middleware-ntshell		psoc6hal
arh (master) TiltHydrometer $

Now take the dramatic step of:

  • rm deps/*
  • rm -rf libs

I decided that the easiest thing to do to create the “mtb” files was to run the library manager.  But, with nothing in the deps directory, the library manager doesn’t know what to do.

arh (master *) TiltHydrometer $ make modlibs
Tools Directory: /Applications/ModusToolbox/tools_2.2
/Applications/ModusToolbox/tools_2.2/make/startex.mk:380: *** Build support for the target device not found. Run "make getlibs" to ensure all required build and code dependencies are present..  Stop.
arh (master *) TiltHydrometer $ 

So, I copy the target file from the Simple22 project.

arh (master *) TiltHydrometer $ cp ../Simple22/deps/TARGET_CY8CKIT-062S2-43012.mtb deps
arh (master *) TiltHydrometer $ more deps/TARGET_CY8CKIT-062S2-43012.mtb 
https://github.com/cypresssemiconductorco/TARGET_CY8CKIT-062S2-43012#latest-v2.X#$$ASSET_REPO$$/TARGET_CY8CKIT-062S2-43012/latest-v2.X

I was sure that I would be able to run the library manager now.  But it gave me the bird.  At least it gave me the hint of running “make getlibs” first.  Which I do:

arh (master *) TiltHydrometer $ make modlibs
Tools Directory: /Applications/ModusToolbox/tools_2.2
/Applications/ModusToolbox/tools_2.2/make/startex.mk:380: *** Build support for the target device not found. Run "make getlibs" to ensure all required build and code dependencies are present..  Stop.
arh (master *) TiltHydrometer $ make getlibs
Tools Directory: /Applications/ModusToolbox/tools_2.2

==============================================================================
= Importing libraries =
==============================================================================
Git is git version 2.24.3 (Apple Git-128), found at /usr/bin/git

Resolving dependencies...
QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once QUrl("https://github.com/iotexpert/mtb2-iotexpert-manifests/raw/master/iotexpert-super-manifest.xml")
/Users/arh/proj/TiltHydrometer/libs/capsense.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/core-lib.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/core-make.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/mtb-hal-cat1.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/mtb-pdl-cat1.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/psoc6cm0p.mtb was added or updated
/Users/arh/proj/TiltHydrometer/libs/recipe-make-cat1a.mtb was added or updated
Dependencies resolved.

Searching application directory (.mtb)...
Found 8 .mtb file(s)
    Processing file "/Users/arh/proj/TiltHydrometer/deps/TARGET_CY8CKIT-062S2-43012.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/capsense.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/core-lib.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/core-make.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/mtb-hal-cat1.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/mtb-pdl-cat1.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/psoc6cm0p.mtb"
    Processing file "/Users/arh/proj/TiltHydrometer/libs/recipe-make-cat1a.mtb"
Libraries processed.
Created file "/Users/arh/proj/TiltHydrometer/libs/mtb.mk".


==============================================================================
= Import complete =
==============================================================================

arh (master *) TiltHydrometer $ 

Now when I run “make modlibs” I can click on all of the libraries that I used in my project.

Once I click go.  It brings in all of the needed libraries.  Look at the “deps” directory now.  We are rocking.

arh (master *) TiltHydrometer $ ls deps
TARGET_CY8CKIT-062S2-43012.mtb	display-tft-st7789v.mtb		freertos.mtb			retarget-io.mtb
bluetooth-freertos.mtb		emwin.mtb			middleware-ntshell.mtb
arh (master *) TiltHydrometer $ cat deps/*
https://github.com/cypresssemiconductorco/TARGET_CY8CKIT-062S2-43012#latest-v2.X#$$ASSET_REPO$$/TARGET_CY8CKIT-062S2-43012/latest-v2.X
https://github.com/cypresssemiconductorco/bluetooth-freertos#latest-v1.X#$$ASSET_REPO$$/bluetooth-freertos/latest-v1.X
https://github.com/cypresssemiconductorco/display-tft-st7789v#latest-v1.X#$$ASSET_REPO$$/display-tft-st7789v/latest-v1.X
https://github.com/cypresssemiconductorco/emwin#latest-v5.X#$$ASSET_REPO$$/emwin/latest-v5.X
https://github.com/cypresssemiconductorco/freertos#latest-v10.X#$$ASSET_REPO$$/freertos/latest-v10.X
https://github.com/iotexpert/middleware-ntshell#latest-v2.X#$$ASSET_REPO$$/middleware-ntshell/latest-v2.X
https://github.com/cypresssemiconductorco/retarget-io#latest-v1.X#$$ASSET_REPO$$/retarget-io/latest-v1.X
arh (master *) TiltHydrometer $

And when I look in the shared library directory which is “../mtb_shared” you can see all of the source files.

arh (master *) TiltHydrometer $ ls ../mtb_shared/
TARGET_CY8CKIT-062S2-43012	capsense			display-tft-st7789v		mtb-hal-cat1			retarget-io
abstraction-rtos		clib-support			emwin				mtb-pdl-cat1
bluetooth-freertos		core-lib			freertos			psoc6cm0p
btstack				core-make			middleware-ntshell		recipe-make-cat1a

Build and Test

All the libraries are now fixed.  So run a “make -j build” and see what we have.

arh (master *) TiltHydrometer $ make -j build
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Prebuild operations complete
Commencing build operations...

Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Initializing build: mtb-example-psoc6-empty-app Debug CY8CKIT-062S2-43012 GCC_ARM

Auto-discovery in progress...
-> Found 237 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 38 .a file(s)
-> Found 665 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete

Constructing build rules...
Build rules construction complete

==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 218 file(s)
    Compiling app file app_bt_cfg.c
    Compiling app file bluetoothManager.c
    Compiling app file capsenseManager.c

.... a bunch of compile messages deleted....

    Compiling ext file cy_wdt.c
    Compiling ext file psoc6_01_cm0p_sleep.c
    Compiling ext file psoc6_02_cm0p_sleep.c
    Compiling ext file psoc6_03_cm0p_sleep.c
    Compiling ext file psoc6_04_cm0p_sleep.c
    Compiling ext file cy_retarget_io.c
    Linking output file mtb-example-psoc6-empty-app.elf
==============================================================================
= Build complete =
==============================================================================

Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og

   ---------------------------------------------------- 
  | Section Name         |  Address      |  Size       | 
   ---------------------------------------------------- 
  | .cy_m0p_image        |  0x10000000   |  6044       | 
  | .text                |  0x10002000   |  322616     | 
  | .ARM.exidx           |  0x10050c38   |  8          | 
  | .copy.table          |  0x10050c40   |  24         | 
  | .zero.table          |  0x10050c58   |  8          | 
  | .data                |  0x080022e0   |  2544       | 
  | .cy_sharedmem        |  0x08002cd0   |  8          | 
  | .noinit              |  0x08002cd8   |  148        | 
  | .bss                 |  0x08002d70   |  40048      | 
  | .heap                |  0x0800c9e0   |  990752     | 
   ---------------------------------------------------- 

  Total Internal Flash (Available)          2097152    
  Total Internal Flash (Utilized)           333408     

  Total Internal SRAM (Available)           1046528    
  Total Internal SRAM (Utilized with heap)  1033500    

  
arh (master *) TiltHydrometer $

A functioning project.  Sweet!

ModusToolbox 2.2 Template Project – FreeRTOS + NTShell

Summary

This article discusses the new library structure that was released with ModusToolbox 2.2.  I explain it by showing the creation of a template project that use FreeRTOS and NT Shell.

Story

I have often started projects from the IoT Expert FreeRTOS template project.   I realized the other day that almost always the first thing I do after creating the project is add the NT Shell library.  My friend Hassane has a personal mantra that if he is going to do the same job more than once he will always automate it.  I should have listened to him on this one because I have done it a bunch of times.

In Modus Toolbox 2.2 we have created a new library scheme which allows sharing of libraries between projects.  So this will also be a good example of how that works.

This will also give you another example of adding template projects to your own manifest.

Here is what I am going to do:

  1. Create a project from the IoT Expert FreeRTOS Template
  2. Add the NTShell Library & Examine New Library Structure
  3. Update the Project and Program
  4. Add the Task List functionality (a nice feature of FreeRTOS)
  5. Put the new template on GitHub
  6. Update the IoT Expert App Manifest
  7. Test the new Template

Create & Test a project from the IoT Expert FreeRTOS Template

I will start the whole process by creating  new project using my existing base template.  The kit that I happen to have on my desk right now is the CY8CKIT-062S2-43012.

Select the IoT Expert FreeRTOS Template and give it a name.  Notice that I add “NTShell” to the name (because that is what Im gonna add)

When you click create, Modus will do its magic and build you a complete project.

Today Im going to edit using Visual Studio Code.  Actually almost always I edit using Visual Studio Code.  You can do all of these tasks using Eclipse as well.  To turn my created project into a VSCODE project run “make vscode”

Before getting to far down the road I like to run a build to make sure everything is OK.  So “make -j build”

arh (master) IoT_Expert_FreeRTOS_NTShell_Template $ make -j build
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Prebuild operations complete
Commencing build operations...

Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Initializing build: MTBShellTemplate Debug CY8CKIT-062S2-43012 GCC_ARM

Auto-discovery in progress...
-> Found 195 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 6 .a file(s)
-> Found 491 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete

Constructing build rules...
Build rules construction complete

==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 183 file(s)
    Compiling app file lowPower.c
    Compiling app file main.c
    Compiling ext file startup_psoc6_02_cm4.S
    Compiling ext file cy_syslib_gcc.S
    Compiling ext file cycfg.c
    Compiling ext file cycfg_capsense.c
    Compiling ext file cycfg_clocks.c

....a bunch of stuff deleted

    Compiling ext file psoc6_04_cm0p_sleep.c
    Compiling ext file cy_retarget_io.c
    Linking output file MTBShellTemplate.elf
==============================================================================
= Build complete =
==============================================================================

Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og

   ---------------------------------------------------- 
  | Section Name         |  Address      |  Size       | 
   ---------------------------------------------------- 
  | .cy_m0p_image        |  0x10000000   |  6044       | 
  | .text                |  0x10002000   |  30280      | 
  | .ARM.exidx           |  0x10009648   |  8          | 
  | .copy.table          |  0x10009650   |  24         | 
  | .zero.table          |  0x10009668   |  8          | 
  | .data                |  0x080022e0   |  1320       | 
  | .cy_sharedmem        |  0x08002808   |  8          | 
  | .noinit              |  0x08002810   |  148        | 
  | .bss                 |  0x080028a4   |  1324       | 
  | .heap                |  0x08002dd0   |  1030704    | 
   ---------------------------------------------------- 

  Total Internal Flash (Available)          2097152    
  Total Internal Flash (Utilized)           39848      

  Total Internal SRAM (Available)           1046528    
  Total Internal SRAM (Utilized with heap)  1033504    

  
arh (master) IoT_Expert_FreeRTOS_NTShell_Template $

Then program it, just to make sure.  “make program”

arh (master) IoT_Expert_FreeRTOS_NTShell_Template $ make program
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Prebuild operations complete
Commencing build operations...

Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Initializing build: MTBShellTemplate Debug CY8CKIT-062S2-43012 GCC_ARM

Auto-discovery in progress...
-> Found 195 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 6 .a file(s)
-> Found 491 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete

Constructing build rules...
Build rules construction complete

==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 183 file(s)
==============================================================================
= Build complete =
==============================================================================

Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og

   ---------------------------------------------------- 
  | Section Name         |  Address      |  Size       | 
   ---------------------------------------------------- 
  | .cy_m0p_image        |  0x10000000   |  6044       | 
  | .text                |  0x10002000   |  30280      | 
  | .ARM.exidx           |  0x10009648   |  8          | 
  | .copy.table          |  0x10009650   |  24         | 
  | .zero.table          |  0x10009668   |  8          | 
  | .data                |  0x080022e0   |  1320       | 
  | .cy_sharedmem        |  0x08002808   |  8          | 
  | .noinit              |  0x08002810   |  148        | 
  | .bss                 |  0x080028a4   |  1324       | 
  | .heap                |  0x08002dd0   |  1030704    | 
   ---------------------------------------------------- 

  Total Internal Flash (Available)          2097152    
  Total Internal Flash (Utilized)           39848      

  Total Internal SRAM (Available)           1046528    
  Total Internal SRAM (Utilized with heap)  1033504    


Programming target device... 
Open On-Chip Debugger 0.10.0+dev-4.1.0.1058 (2020-08-11-03:45)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
adapter speed: 2000 kHz
adapter srst delay: 25
adapter srst pulse_width: 25
** Auto-acquire enabled, use "set ENABLE_ACQUIRE 0" to disable
cortex_m reset_config sysresetreq
cortex_m reset_config sysresetreq
Info : Using CMSIS loader 'CY8C6xxA_SMIF' for bank 'psoc6_smif0_cm0' (footprint 14672 bytes)
Warn : SFlash programming allowed for regions: USER, TOC, KEY
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : KitProg3: FW version: 1.14.514
Info : KitProg3: Pipelined transfers disabled, please update the firmware
Info : VTarget = 3.221 V
Info : kitprog3: acquiring the device...
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x6ba02477
Info : psoc6.cpu.cm0: hardware has 4 breakpoints, 2 watchpoints
***************************************
** Silicon: 0xE453, Family: 0x102, Rev.: 0x12 (A1)
** Detected Device: CY8C624ABZI-S2D44
** Detected Main Flash size, kb: 2048
** Flash Boot version: 3.1.0.378
** Chip Protection: NORMAL
***************************************
Info : psoc6.cpu.cm4: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for psoc6.cpu.cm0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for psoc6.cpu.cm4 on 3334
Info : Listening on port 3334 for gdb connections
Info : SWD DPIDR 0x6ba02477
Info : kitprog3: acquiring the device...
psoc6.cpu.cm0 halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x00000190 msp: 0x080ff800
** Device acquired successfully
** psoc6.cpu.cm4: Ran after reset and before halt...
psoc6.cpu.cm4 halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0000012a msp: 0x080ff800
** Programming Started **
auto erase enabled
Info : Flash write discontinued at 0x1000179c, next section at 0x10002000
Info : Padding image section 0 at 0x1000179c with 100 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
Info : Padding image section 1 at 0x10009ba0 with 96 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
wrote 37888 bytes from file /Users/arh/mtb22/IoT_Expert_FreeRTOS_NTShell_Template/build/CY8CKIT-062S2-43012/Debug/MTBShellTemplate.hex in 1.402638s (26.379 KiB/s)
** Programming Finished **
** Verify Started **
verified 37692 bytes in 0.080973s (454.579 KiB/s)
** Verified OK **
** Resetting Target **
Info : SWD DPIDR 0x6ba02477
shutdown command invoked
Info : psoc6.dap: powering down debug domain...
Warn : Failed to power down Debug Domains
  
arh (master) IoT_Expert_FreeRTOS_NTShell_Template $

Add the NTShell Library & Examine New Library Structure

Everything is working and my basic project has FreeRTOS and a blinking LED.  Now let’s add the NT Shell Library.  To do this run the library manager by running “make modlibs” (or click on the button in Eclipse).  Select Libraries –> IoT Expert –> ntshell

When you press update, the library manager will do its thing again.

When I look in the “deps” directory, I see some new file types called “.mtb”.  These files tell your project where to find each of the libraries.  Notice that the middleware-ntshell.mtb points to “$$ASSET_REPO$$”.  Where is that?

If you have a aook at the Makefile it tell you that it is “../” and “mtb_shared”.

# Relative path to the shared repo location.
#
# All .mtb files have the format, <URI><COMMIT><LOCATION>. If the <LOCATION> field 
# begins with $$ASSET_REPO$$, then the repo is deposited in the path specified by 
# the CY_GETLIBS_SHARED_PATH variable. The default location is one directory level 
# above the current app directory.
# This is used with CY_GETLIBS_SHARED_NAME variable, which specifies the directory name.
CY_GETLIBS_SHARED_PATH=../

# Directory name of the shared repo location.
#
CY_GETLIBS_SHARED_NAME=mtb_shared

To start editing I will run Visual Studio Code by typing “code .”.  The first thing that happens is that it notices that this is a workspace instead of just a directory.

And when you look at the workspace you can see that it knows about both the example project as well as “mtb_shared”.  That is sweet.

Update the Project and Program

Now follow the instructions from the middlware-ntshell readme by copying “usrcmd.*” into my project.

arh (master) IoT_Expert_FreeRTOS_NTShell_Template $ cp ../mtb_shared/middleware-ntshell/latest-v2.X/template/psoc6sdk/usrcmd.* .

Then I edit main.c to

  1. #include “usrcmd.h” on line 8
  2. Start the shell task which is called “usrcmd_task” on line 39

Now buid/project by running “make -j program”

arh (master *) IoT_Expert_FreeRTOS_NTShell_Template $ make -j program
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Prebuild operations complete
Commencing build operations...

Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Initializing build: MTBShellTemplate Debug CY8CKIT-062S2-43012 GCC_ARM

Auto-discovery in progress...
-> Found 205 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 6 .a file(s)
-> Found 503 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete

Constructing build rules...
Build rules construction complete

==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 193 file(s)
    Compiling app file main.c
    Linking output file MTBShellTemplate.elf
==============================================================================
= Build complete =
==============================================================================

Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og

   ---------------------------------------------------- 
  | Section Name         |  Address      |  Size       | 
   ---------------------------------------------------- 
  | .cy_m0p_image        |  0x10000000   |  6044       | 
  | .text                |  0x10002000   |  52780      | 
  | .ARM.exidx           |  0x1000ee2c   |  8          | 
  | .copy.table          |  0x1000ee34   |  24         | 
  | .zero.table          |  0x1000ee4c   |  8          | 
  | .data                |  0x080022e0   |  1688       | 
  | .cy_sharedmem        |  0x08002978   |  8          | 
  | .noinit              |  0x08002980   |  148        | 
  | .bss                 |  0x08002a14   |  2136       | 
  | .heap                |  0x08003270   |  1029520    | 
   ---------------------------------------------------- 

  Total Internal Flash (Available)          2097152    
  Total Internal Flash (Utilized)           62716      

  Total Internal SRAM (Available)           1046528    
  Total Internal SRAM (Utilized with heap)  1033500    


Programming target device... 
Open On-Chip Debugger 0.10.0+dev-4.1.0.1058 (2020-08-11-03:45)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
adapter speed: 2000 kHz
adapter srst delay: 25
adapter srst pulse_width: 25
** Auto-acquire enabled, use "set ENABLE_ACQUIRE 0" to disable
cortex_m reset_config sysresetreq
cortex_m reset_config sysresetreq
Info : Using CMSIS loader 'CY8C6xxA_SMIF' for bank 'psoc6_smif0_cm0' (footprint 14672 bytes)
Warn : SFlash programming allowed for regions: USER, TOC, KEY
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : KitProg3: FW version: 1.14.514
Info : KitProg3: Pipelined transfers disabled, please update the firmware
Info : VTarget = 3.225 V
Info : kitprog3: acquiring the device...
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x6ba02477
Info : psoc6.cpu.cm0: hardware has 4 breakpoints, 2 watchpoints
***************************************
** Silicon: 0xE453, Family: 0x102, Rev.: 0x12 (A1)
** Detected Device: CY8C624ABZI-S2D44
** Detected Main Flash size, kb: 2048
** Flash Boot version: 3.1.0.378
** Chip Protection: NORMAL
***************************************
Info : psoc6.cpu.cm4: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for psoc6.cpu.cm0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for psoc6.cpu.cm4 on 3334
Info : Listening on port 3334 for gdb connections
Info : SWD DPIDR 0x6ba02477
Info : kitprog3: acquiring the device...
psoc6.cpu.cm0 halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x00000190 msp: 0x080ff800
** Device acquired successfully
** psoc6.cpu.cm4: Ran after reset and before halt...
psoc6.cpu.cm4 halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0000012a msp: 0x080ff800
** Programming Started **
auto erase enabled
Info : Flash write discontinued at 0x1000179c, next section at 0x10002000
Info : Padding image section 0 at 0x1000179c with 100 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
Info : Padding image section 1 at 0x1000f4f4 with 268 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
wrote 60928 bytes from file /Users/arh/mtb22/IoT_Expert_FreeRTOS_NTShell_Template/build/CY8CKIT-062S2-43012/Debug/MTBShellTemplate.hex in 2.017097s (29.498 KiB/s)
** Programming Finished **
** Verify Started **
verified 60560 bytes in 0.119193s (496.175 KiB/s)
** Verified OK **
** Resetting Target **
Info : SWD DPIDR 0x6ba02477
shutdown command invoked
Info : psoc6.dap: powering down debug domain...
  
arh (master *) IoT_Expert_FreeRTOS_NTShell_Template $

Now I have a functional shell.  Here is the serial console.

Add the Task List functionality

In the main.c above I started up the usrcmd_task with a stack size of config_MINIMAL_STACK_SIZE * 4.  Where did I get that?  Well the first couple of times I did this it crashed by running out of stack so I tried bigger and bigger numbers until it stopped crashing.  This is a kind of a pain in the ass.   If you know FreeRTOS there is a function called “vTaskList” which will give you stats about the tasks.

In order to use that function you need to turn it on.  Notice that I #ifdef and #if to see if it is turned on inside of usrcmd.c

#ifdef configUSE_TRACE_FACILITY
#if configUSE_STATS_FORMATTING_FUNCTIONS ==1
static int usrcmd_list(int argc, char **argv);
#endif
#endif

So let’s turn it on by editing “FreeRTOSConfig.h” and enabling the two required defines.

#define configUSE_TRACE_FACILITY					1
#define configUSE_STATS_FORMATTING_FUNCTIONS        1

Now build/program.

arh (master *) IoT_Expert_FreeRTOS_NTShell_Template $ make -j program
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Prebuild operations complete
Commencing build operations...

Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk

Initializing build: MTBShellTemplate Debug CY8CKIT-062S2-43012 GCC_ARM

Auto-discovery in progress...
-> Found 205 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 6 .a file(s)
-> Found 503 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete

Constructing build rules...
Build rules construction complete

==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 193 file(s)
    Compiling app file lowPower.c
    Compiling app file main.c
    Compiling app file usrcmd.c
    Compiling ext file croutine.c
    Compiling ext file event_groups.c
    Compiling ext file list.c
    Compiling ext file heap_1.c
    Compiling ext file heap_2.c
    Compiling ext file heap_3.c
    Compiling ext file heap_4.c
    Compiling ext file heap_5.c
    Compiling ext file port.c
    Compiling ext file queue.c
    Compiling ext file stream_buffer.c
    Compiling ext file tasks.c
    Compiling ext file timers.c
    Compiling ext file psoc6_ntshell_port.c
    Linking output file MTBShellTemplate.elf
==============================================================================
= Build complete =
==============================================================================

Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og

   ---------------------------------------------------- 
  | Section Name         |  Address      |  Size       | 
   ---------------------------------------------------- 
  | .cy_m0p_image        |  0x10000000   |  6044       | 
  | .text                |  0x10002000   |  54876      | 
  | .ARM.exidx           |  0x1000f65c   |  8          | 
  | .copy.table          |  0x1000f664   |  24         | 
  | .zero.table          |  0x1000f67c   |  8          | 
  | .data                |  0x080022e0   |  1688       | 
  | .cy_sharedmem        |  0x08002978   |  8          | 
  | .noinit              |  0x08002980   |  148        | 
  | .bss                 |  0x08002a14   |  2136       | 
  | .heap                |  0x08003270   |  1029520    | 
   ---------------------------------------------------- 

  Total Internal Flash (Available)          2097152    
  Total Internal Flash (Utilized)           64812      

  Total Internal SRAM (Available)           1046528    
  Total Internal SRAM (Utilized with heap)  1033500    


Programming target device... 
Open On-Chip Debugger 0.10.0+dev-4.1.0.1058 (2020-08-11-03:45)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
adapter speed: 2000 kHz
adapter srst delay: 25
adapter srst pulse_width: 25
** Auto-acquire enabled, use "set ENABLE_ACQUIRE 0" to disable
cortex_m reset_config sysresetreq
cortex_m reset_config sysresetreq
Info : Using CMSIS loader 'CY8C6xxA_SMIF' for bank 'psoc6_smif0_cm0' (footprint 14672 bytes)
Warn : SFlash programming allowed for regions: USER, TOC, KEY
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : KitProg3: FW version: 1.14.514
Info : KitProg3: Pipelined transfers disabled, please update the firmware
Info : VTarget = 3.220 V
Info : kitprog3: acquiring the device...
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x6ba02477
Info : psoc6.cpu.cm0: hardware has 4 breakpoints, 2 watchpoints
***************************************
** Silicon: 0xE453, Family: 0x102, Rev.: 0x12 (A1)
** Detected Device: CY8C624ABZI-S2D44
** Detected Main Flash size, kb: 2048
** Flash Boot version: 3.1.0.378
** Chip Protection: NORMAL
***************************************
Info : psoc6.cpu.cm4: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for psoc6.cpu.cm0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for psoc6.cpu.cm4 on 3334
Info : Listening on port 3334 for gdb connections
Info : SWD DPIDR 0x6ba02477
Info : kitprog3: acquiring the device...
psoc6.cpu.cm0 halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x00000190 msp: 0x080ff800
** Device acquired successfully
** psoc6.cpu.cm4: Ran after reset and before halt...
psoc6.cpu.cm4 halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0000012a msp: 0x080ff800
** Programming Started **
auto erase enabled
Info : Flash write discontinued at 0x1000179c, next section at 0x10002000
Info : Padding image section 0 at 0x1000179c with 100 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
Info : Padding image section 1 at 0x1000fd24 with 220 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
wrote 62976 bytes from file /Users/arh/mtb22/IoT_Expert_FreeRTOS_NTShell_Template/build/CY8CKIT-062S2-43012/Debug/MTBShellTemplate.hex in 2.092903s (29.385 KiB/s)
** Programming Finished **
** Verify Started **
verified 62656 bytes in 0.123619s (494.968 KiB/s)
** Verified OK **
** Resetting Target **
Info : SWD DPIDR 0x6ba02477
shutdown command invoked
Info : psoc6.dap: powering down debug domain...

When I run help you can see I have a new command called “tasks” which lists all of the tasks and their stack high water marks.

Put the Template on GitHub

I am happy with my new template.  So, I go to GitHub and create a new repository.

Then on my current project:

  1. I blow away the git history (didnt really have to do that).
  2. Create a new git repo “git init .”
  3. Add a pointer to GitHub “git remote add….”
  4. Add all of the files “git add *”
  5. Add the .gitignore “git add .gitignore”
  6. Commit the changes “git commit…”
  7. Push it to GitHub “git push …”
arh (master *) IoT_Expert_FreeRTOS_NTShell_Template $ rm -rf .git
arh IoT_Expert_FreeRTOS_NTShell_Template $ git init .
Initialized empty Git repository in /Users/arh/mtb22/IoT_Expert_FreeRTOS_NTShell_Template/.git/
arh (master #) IoT_Expert_FreeRTOS_NTShell_Template $ git remote add origin git@iotexpert.github.com:iotexpert/mtb2-freertos-ntshell-template.git
arh (master #) IoT_Expert_FreeRTOS_NTShell_Template $ git add * 
The following paths are ignored by one of your .gitignore files:
build
Use -f if you really want to add them.
arh (master #) IoT_Expert_FreeRTOS_NTShell_Template $ git add .gitignore
arh (master #) IoT_Expert_FreeRTOS_NTShell_Template $ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .gitignore
	new file:   FreeRTOSConfig.h
	new file:   LICENSE
	new file:   MTBShellTemplate.code-workspace
	new file:   Makefile
	new file:   README.md
	new file:   deps/TARGET_CY8CKIT-062S2-43012.mtb
	new file:   deps/freertos.mtb
	new file:   deps/middleware-ntshell.mtb
	new file:   deps/retarget-io.mtb
	new file:   global.h
	new file:   lowPower.c
	new file:   main.c
	new file:   openocd.tcl
	new file:   usrcmd.c
	new file:   usrcmd.h

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.vscode/

arh (master #) IoT_Expert_FreeRTOS_NTShell_Template $ git commit -m "Initial commit"
[master (root-commit) 26b5d3c] Initial commit
 16 files changed, 994 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 FreeRTOSConfig.h
 create mode 100644 LICENSE
 create mode 100644 MTBShellTemplate.code-workspace
 create mode 100644 Makefile
 create mode 100644 README.md
 create mode 100644 deps/TARGET_CY8CKIT-062S2-43012.mtb
 create mode 100644 deps/freertos.mtb
 create mode 100644 deps/middleware-ntshell.mtb
 create mode 100644 deps/retarget-io.mtb
 create mode 100644 global.h
 create mode 100644 lowPower.c
 create mode 100644 main.c
 create mode 100644 openocd.tcl
 create mode 100644 usrcmd.c
 create mode 100644 usrcmd.h
arh (master) IoT_Expert_FreeRTOS_NTShell_Template $ git push -u origin master
Enumerating objects: 19, done.
Counting objects: 100% (19/19), done.
Delta compression using up to 12 threads
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 16.21 KiB | 5.40 MiB/s, done.
Total 19 (delta 0), reused 0 (delta 0)
To iotexpert.github.com:iotexpert/mtb2-freertos-ntshell-template.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
arh (master) IoT_Expert_FreeRTOS_NTShell_Template $

Update the Manifests

To get access to the new template I need to add it to the IoT Expert App Manifest.  I edit the xml file to have the new app

<apps>
  <app>
    <name>IoT Expert FreeRTOS Template</name>
    <id>mtb2-freertos-template</id>
    <uri>https://github.com/iotexpert/mtb2-freertos-template</uri>
    <description>This template provide a starting point for FreeRTOS projects.  Including a starting blinking LED task</description>
    <req_capabilities>psoc6</req_capabilities>
    <versions>
      <version>
        <num>Latest 1.X release</num>
        <commit>master</commit>
      </version>
    </versions>
  </app>
    <app>
    <name>IoT Expert FreeRTOS NTShell Template</name>
    <id>mtb2-freertos-ntshell-template</id>
    <uri>https://github.com/iotexpert/mtb2-freertos-ntshell-template</uri>
    <description>This template provide a starting point for FreeRTOS projects.  Including a starting blinking LED task and shell</description>
    <req_capabilities>psoc6</req_capabilities>
    <versions>
      <version>
        <num>Latest 1.X release</num>
        <commit>master</commit>
      </version>
    </versions>
  </app>
</apps>

Now I need to git add, git commit and git push it to GitHub.

arh (master) mtb2-iotexpert-manifests $ code .
arh (master) mtb2-iotexpert-manifests $ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   iotexpert-app-manifest.xml

no changes added to commit (use "git add" and/or "git commit -a")
arh (master *) mtb2-iotexpert-manifests $ git add iotexpert-app-manifest.xml
arh (master +) mtb2-iotexpert-manifests $ git commit -m "Updated with ntshell example"
[master 47a7bb1] Updated with ntshell example
 1 file changed, 13 insertions(+)
arh (master) mtb2-iotexpert-manifests $ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 554 bytes | 554.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To iotexpert.github.com:iotexpert/mtb2-iotexpert-manifests.git
   28ed2d0..47a7bb1  master -> master

Test the new Template

Everything should be working so make a new project.

Cool.  There is the new template.

When I program it… everything is cool.

arh (master) TestNTS $ make -j program
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk
Prebuild operations complete
Commencing build operations...
Tools Directory: /Applications/ModusToolbox/tools_2.2
CY8CKIT-062S2-43012.mk: ../mtb_shared/TARGET_CY8CKIT-062S2-43012/latest-v2.X/CY8CKIT-062S2-43012.mk
Initializing build: MTBShellTemplate Debug CY8CKIT-062S2-43012 GCC_ARM
Auto-discovery in progress...
-> Found 205 .c file(s)
-> Found 46 .S file(s)
-> Found 23 .s file(s)
-> Found 0 .cpp file(s)
-> Found 0 .o file(s)
-> Found 6 .a file(s)
-> Found 503 .h file(s)
-> Found 0 .hpp file(s)
-> Found 0 resource file(s)
Applying filters...
Auto-discovery complete
Constructing build rules...
Build rules construction complete
==============================================================================
= Building application =
==============================================================================
Generating compilation database file...
-> ./build/compile_commands.json
Compilation database file generation complete
Building 193 file(s)
Compiling app file lowPower.c
Compiling app file main.c
Compiling app file usrcmd.c
Compiling ext file startup_psoc6_02_cm4.S
Compiling ext file cy_syslib_gcc.S
Compiling ext file cycfg.c
Compiling ext file cycfg_capsense.c
Compiling ext file cycfg_clocks.c
Compiling ext file cycfg_peripherals.c
Compiling ext file cycfg_pins.c
Compiling ext file cycfg_qspi_memslot.c
Compiling ext file cycfg_routing.c
Compiling ext file cycfg_system.c
Compiling ext file system_psoc6_cm4.c
Compiling ext file cybsp.c
Compiling ext file cy_capsense_centroid.c
Compiling ext file cy_capsense_control.c
Compiling ext file cy_capsense_csd.c
Compiling ext file cy_capsense_csx.c
Compiling ext file cy_capsense_filter.c
Compiling ext file cy_capsense_processing.c
Compiling ext file cy_capsense_selftest.c
Compiling ext file cy_capsense_sensing.c
Compiling ext file cy_capsense_structure.c
Compiling ext file cy_capsense_tuner.c
Compiling ext file croutine.c
Compiling ext file event_groups.c
Compiling ext file list.c
Compiling ext file heap_1.c
Compiling ext file heap_2.c
Compiling ext file heap_3.c
Compiling ext file heap_4.c
Compiling ext file heap_5.c
Compiling ext file port.c
Compiling ext file queue.c
Compiling ext file stream_buffer.c
Compiling ext file tasks.c
Compiling ext file timers.c
Compiling ext file ntlibc.c
Compiling ext file ntshell.c
Compiling ext file text_editor.c
Compiling ext file text_history.c
Compiling ext file vtrecv.c
Compiling ext file vtsend.c
Compiling ext file psoc6_ntshell_port.c
Compiling ext file ntopt.c
Compiling ext file ntstdio.c
Compiling ext file cyhal_adc.c
Compiling ext file cyhal_analog_common.c
Compiling ext file cyhal_clock.c
Compiling ext file cyhal_comp.c
Compiling ext file cyhal_comp_ctb.c
Compiling ext file cyhal_comp_lp.c
Compiling ext file cyhal_crc.c
Compiling ext file cyhal_crypto_common.c
Compiling ext file cyhal_dac.c
Compiling ext file cyhal_deprecated.c
Compiling ext file cyhal_dma.c
Compiling ext file cyhal_dma_dmac.c
Compiling ext file cyhal_dma_dw.c
Compiling ext file cyhal_ezi2c.c
Compiling ext file cyhal_flash.c
Compiling ext file cyhal_gpio.c
Compiling ext file cyhal_hwmgr.c
Compiling ext file cyhal_i2c.c
Compiling ext file cyhal_i2s.c
Compiling ext file cyhal_interconnect.c
Compiling ext file cyhal_lptimer.c
Compiling ext file cyhal_not_implemented.c
Compiling ext file cyhal_opamp.c
Compiling ext file cyhal_pdmpcm.c
Compiling ext file cyhal_pwm.c
Compiling ext file cyhal_qspi.c
Compiling ext file cyhal_rtc.c
Compiling ext file cyhal_scb_common.c
Compiling ext file cyhal_sdhc.c
Compiling ext file cyhal_spi.c
Compiling ext file cyhal_syspm.c
Compiling ext file cyhal_system.c
Compiling ext file cyhal_tcpwm_common.c
Compiling ext file cyhal_timer.c
Compiling ext file cyhal_trng.c
Compiling ext file cyhal_uart.c
Compiling ext file cyhal_udb_sdio.c
Compiling ext file cyhal_usb_dev.c
Compiling ext file cyhal_utils.c
Compiling ext file cyhal_wdt.c
Compiling ext file cyhal_psoc6_01_104_m_csp_ble.c
Compiling ext file cyhal_psoc6_01_104_m_csp_ble_usb.c
Compiling ext file cyhal_psoc6_01_116_bga_ble.c
Compiling ext file cyhal_psoc6_01_116_bga_usb.c
Compiling ext file cyhal_psoc6_01_124_bga.c
Compiling ext file cyhal_psoc6_01_124_bga_sip.c
Compiling ext file cyhal_psoc6_01_43_smt.c
Compiling ext file cyhal_psoc6_01_68_qfn_ble.c
Compiling ext file cyhal_psoc6_01_80_wlcsp.c
Compiling ext file cyhal_psoc6_02_100_wlcsp.c
Compiling ext file cyhal_psoc6_02_124_bga.c
Compiling ext file cyhal_psoc6_02_128_tqfp.c
Compiling ext file cyhal_psoc6_02_68_qfn.c
Compiling ext file cyhal_psoc6_03_100_tqfp.c
Compiling ext file cyhal_psoc6_03_49_wlcsp.c
Compiling ext file cyhal_psoc6_03_68_qfn.c
Compiling ext file cyhal_psoc6_04_64_tqfp.c
Compiling ext file cyhal_psoc6_04_68_qfn.c
Compiling ext file cyhal_psoc6_04_80_tqfp.c
Compiling ext file cyhal_triggers_psoc6_01.c
Compiling ext file cyhal_triggers_psoc6_02.c
Compiling ext file cyhal_triggers_psoc6_03.c
Compiling ext file cyhal_triggers_psoc6_04.c
Compiling ext file cy_ble_clk.c
Compiling ext file cy_canfd.c
Compiling ext file cy_crypto.c
Compiling ext file cy_crypto_core_aes_v1.c
Compiling ext file cy_crypto_core_aes_v2.c
Compiling ext file cy_crypto_core_cmac_v1.c
Compiling ext file cy_crypto_core_cmac_v2.c
Compiling ext file cy_crypto_core_crc_v1.c
Compiling ext file cy_crypto_core_crc_v2.c
Compiling ext file cy_crypto_core_des_v1.c
Compiling ext file cy_crypto_core_des_v2.c
Compiling ext file cy_crypto_core_ecc_domain_params.c
Compiling ext file cy_crypto_core_ecc_ecdsa.c
Compiling ext file cy_crypto_core_ecc_key_gen.c
Compiling ext file cy_crypto_core_ecc_nist_p.c
Compiling ext file cy_crypto_core_hmac_v1.c
Compiling ext file cy_crypto_core_hmac_v2.c
Compiling ext file cy_crypto_core_hw.c
Compiling ext file cy_crypto_core_hw_v1.c
Compiling ext file cy_crypto_core_mem_v1.c
Compiling ext file cy_crypto_core_mem_v2.c
Compiling ext file cy_crypto_core_prng_v1.c
Compiling ext file cy_crypto_core_prng_v2.c
Compiling ext file cy_crypto_core_rsa.c
Compiling ext file cy_crypto_core_sha_v1.c
Compiling ext file cy_crypto_core_sha_v2.c
Compiling ext file cy_crypto_core_trng_v1.c
Compiling ext file cy_crypto_core_trng_v2.c
Compiling ext file cy_crypto_core_vu.c
Compiling ext file cy_crypto_server.c
Compiling ext file cy_csd.c
Compiling ext file cy_ctb.c
Compiling ext file cy_ctdac.c
Compiling ext file cy_device.c
Compiling ext file cy_dma.c
Compiling ext file cy_dmac.c
Compiling ext file cy_efuse.c
Compiling ext file cy_flash.c
Compiling ext file cy_gpio.c
Compiling ext file cy_i2s.c
Compiling ext file cy_ipc_drv.c
Compiling ext file cy_ipc_pipe.c
Compiling ext file cy_ipc_sema.c
Compiling ext file cy_lpcomp.c
Compiling ext file cy_lvd.c
Compiling ext file cy_mcwdt.c
Compiling ext file cy_pdm_pcm.c
Compiling ext file cy_pra.c
Compiling ext file cy_pra_cfg.c
Compiling ext file cy_profile.c
Compiling ext file cy_prot.c
Compiling ext file cy_rtc.c
Compiling ext file cy_sar.c
Compiling ext file cy_scb_common.c
Compiling ext file cy_scb_ezi2c.c
Compiling ext file cy_scb_i2c.c
Compiling ext file cy_scb_spi.c
Compiling ext file cy_scb_uart.c
Compiling ext file cy_sd_host.c
Compiling ext file cy_seglcd.c
Compiling ext file cy_smartio.c
Compiling ext file cy_smif.c
Compiling ext file cy_smif_memslot.c
Compiling ext file cy_sysanalog.c
Compiling ext file cy_sysclk.c
Compiling ext file cy_sysint.c
Compiling ext file cy_syslib.c
Compiling ext file cy_syspm.c
Compiling ext file cy_systick.c
Compiling ext file cy_tcpwm_counter.c
Compiling ext file cy_tcpwm_pwm.c
Compiling ext file cy_tcpwm_quaddec.c
Compiling ext file cy_tcpwm_shiftreg.c
Compiling ext file cy_trigmux.c
Compiling ext file cy_usbfs_dev_drv.c
Compiling ext file cy_usbfs_dev_drv_io.c
Compiling ext file cy_usbfs_dev_drv_io_dma.c
Compiling ext file cy_wdt.c
Compiling ext file psoc6_01_cm0p_sleep.c
Compiling ext file psoc6_02_cm0p_sleep.c
Compiling ext file psoc6_03_cm0p_sleep.c
Compiling ext file psoc6_04_cm0p_sleep.c
Compiling ext file cy_retarget_io.c
Linking output file MTBShellTemplate.elf
==============================================================================
= Build complete =
==============================================================================
Calculating memory consumption: CY8C624ABZI-S2D44 GCC_ARM -Og
---------------------------------------------------- 
| Section Name         |  Address      |  Size       | 
---------------------------------------------------- 
| .cy_m0p_image        |  0x10000000   |  6044       | 
| .text                |  0x10002000   |  54876      | 
| .ARM.exidx           |  0x1000f65c   |  8          | 
| .copy.table          |  0x1000f664   |  24         | 
| .zero.table          |  0x1000f67c   |  8          | 
| .data                |  0x080022e0   |  1688       | 
| .cy_sharedmem        |  0x08002978   |  8          | 
| .noinit              |  0x08002980   |  148        | 
| .bss                 |  0x08002a14   |  2136       | 
| .heap                |  0x08003270   |  1029520    | 
---------------------------------------------------- 
Total Internal Flash (Available)          2097152    
Total Internal Flash (Utilized)           64812      
Total Internal SRAM (Available)           1046528    
Total Internal SRAM (Utilized with heap)  1033500    
Programming target device... 
Open On-Chip Debugger 0.10.0+dev-4.1.0.1058 (2020-08-11-03:45)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
adapter speed: 2000 kHz
adapter srst delay: 25
adapter srst pulse_width: 25
** Auto-acquire enabled, use "set ENABLE_ACQUIRE 0" to disable
cortex_m reset_config sysresetreq
cortex_m reset_config sysresetreq
Info : Using CMSIS loader 'CY8C6xxA_SMIF' for bank 'psoc6_smif0_cm0' (footprint 14672 bytes)
Warn : SFlash programming allowed for regions: USER, TOC, KEY
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : KitProg3: FW version: 1.14.514
Info : KitProg3: Pipelined transfers disabled, please update the firmware
Info : VTarget = 3.221 V
Info : kitprog3: acquiring the device...
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x6ba02477
Info : psoc6.cpu.cm0: hardware has 4 breakpoints, 2 watchpoints
***************************************
** Silicon: 0xE453, Family: 0x102, Rev.: 0x12 (A1)
** Detected Device: CY8C624ABZI-S2D44
** Detected Main Flash size, kb: 2048
** Flash Boot version: 3.1.0.378
** Chip Protection: NORMAL
***************************************
Info : psoc6.cpu.cm4: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for psoc6.cpu.cm0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for psoc6.cpu.cm4 on 3334
Info : Listening on port 3334 for gdb connections
Info : SWD DPIDR 0x6ba02477
Info : kitprog3: acquiring the device...
psoc6.cpu.cm0 halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x00000190 msp: 0x080ff800
** Device acquired successfully
** psoc6.cpu.cm4: Ran after reset and before halt...
psoc6.cpu.cm4 halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0000012a msp: 0x080ff800
** Programming Started **
auto erase enabled
Info : Flash write discontinued at 0x1000179c, next section at 0x10002000
Info : Padding image section 0 at 0x1000179c with 100 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
Info : Padding image section 1 at 0x1000fd24 with 220 bytes (bank write end alignment)
[100%] [################################] [ Erasing     ]
[100%] [################################] [ Programming ]
wrote 62976 bytes from file /Users/arh/mtb22/TestNTS/build/CY8CKIT-062S2-43012/Debug/MTBShellTemplate.hex in 2.082329s (29.534 KiB/s)
** Programming Finished **
** Verify Started **
verified 62656 bytes in 0.122516s (499.425 KiB/s)
** Verified OK **
** Resetting Target **
Info : SWD DPIDR 0x6ba02477
shutdown command invoked
Info : psoc6.dap: powering down debug domain...
arh (master) TestNTS $

And the project seems to be doing the needful.

Stupid Python Tricks: Implementing a c-switch

Summary

A discussion of a simple method in Python to implement the equivalent of a c programming style switch.

Story

Recently, I was working on an example where I would have used something like this if I was programming in C (emphasis on like)

#include <stdio.h>
typedef enum {
op0,
op1,
op2,
op3
} opcodes_t;
int main(int argc,char **argv)
{
opcodes_t excode = op1;
switch(excode)
{
case 0:
printf("opcode 0");
break;
case 1:
printf("opcode 1");
break;
case 2:
printf("opcode 2");
break;
case 3:
printf("opcode 3");
break;
}
}

And, as you know, I am not a real Python programmer.  I tried switch… obviously to no avail.  So now what?  Well in the example above I have

  • A list of keys, the enumerated opcode_t
  • That have a values e.g. “opcode 0” etc.

Sounds like a dictionary.  Here is the equivalent code in Python:

opcode_t = {
0:"opcode 0",
1:"opcode 1",
2:"opcode 2",
3:"opcode 3",
}
ex1 = 1
print(opcode_t[ex1])

But what happens if a key is missing?  The bracket[] method of a dictionary is equivalent to the dictionary method “get”.   The “get” method has a default case if they key is missing from the dictionary.  Here is the example code:

opcode_t = {
0:"opcode 0",
1:"opcode 1",
2:"opcode 2",
3:"opcode 3",
}
ex1 = 1
# will return "not found" if the key is not in the dictionary
print(opcode_t.get(ex1,"Not Found"))

What if you want to do something?  Values in dictionaries can be “function pointers” (is what I would call them in C).  They are probably properly called references in Python.  Regardless, here is some absurd code that demonstrates the example.

def opcode0():
return "opcode 0"
def opcode1():
return "opcode 1"
def opcode2():
return "opcode 2"
def opcode3():
return "opcode 3"
opcode1_t = {
0:opcode0,
1:opcode1,
2:opcode2,
3:opcode3,
}
ex1 = 1
print(opcode1_t[ex1]())

My mDNS Example

The example that lead me to this problem was decoding mDNS headers which have 8 fields of different lengths and possible values.  Here is what I actually did:

    qrText = {0:"Query",1:"Response"}
opcodeText = {0:"Query",1:"IQuery",2:"Status",3:"reserved",4:"Notify",5:"Update"}
aaText = {0:"Non Authoratative",1:"Authoratative"}
tcText = {0:"No Truncation",1:"Truncation"}
rdText = {0:"No Recursion",1:"Recursion"}
raText = {0:"No Recursion Available",1:"Recursion Available"}
zText = {0:"Reserved"}
rcodeText = {
0:"No Error",
1:"Format Error",
2:"Server Failure",
3:"Name Error",
4:"Not implemented",
5:"Refused - probably a policy reason",
6:"A name exists when it should not",
7:"a resource record set exists that should not",
8:"NX RR Set - A resource record set that should exist does not",
9:"Not Authorized",
10:"Not Zone",
}
def printHeader(self):
print(f"id = {self.id}")
print(f"qr = {self.qr} {self.qrText.get(self.qr,'Unknown')}")
print(f"opcode = {self.opcode} {self.opcodeText.get(self.opcode,'Unknown')}")
print(f"aa = {self.aa} {self.aaText.get(self.aa,'Unknown')}")
print(f"tc = {self.tc} {self.tcText.get(self.tc,'Unknown')}")
print(f"rd = {self.rd} {self.rdText.get(self.rd,'Unknown')}")
print(f"ra = {self.ra} {self.raText.get(self.ra,'Unknown')}")
print(f"z = {self.z} {self.zText.get(self.z,'Unknown')}")
print(f"response code = {self.rcode} {self.rcodeText.get(self.rcode,'Unknown')}")
print(f"rc question count = {self.qdcount}")
print(f"answer count = {self.ancount}")
print(f"name server count = {self.nscount}")
print(f"additional record count = {self.arcount}")

Stupid Python Tricks: C-Structures using the ctypes Module (part 2)

Summary

A discussion of overriding __new__ and __init__ to simplify the creation Python cstruct.Structure objects.

Creating a new cytpes.BigEndianStructure

Here is a simple example of a 2-byte structure using the ctypes.BigEndianStructure class.

In this example I:

  • Derive a new class called MyStruct from the BigEndianStructure
  • Declare three fields called first (4-bits), second (4-bits) and third (8-bits).
  • Create a new object of MyStruct type called “a”
  • Set the values of the three fields
  • Print it out.
import ctypes
class MyStruct(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [    ("first",ctypes.c_uint8,4),
("second",ctypes.c_uint8,4),
("third",ctypes.c_uint8,8),
]
# Create a blank MyStruct
a = MyStruct()
a.first  = 0xa
a.second = 0xb
a.third  = 0xcd
print(bytes(a))

When I run this you can see that indeed I get 0xABCD as the result.  Several comments about this:

  • Notice that it is BigEndian (which is good since that is what I declared)
  • 0xA = 4-bits, 0-xB=4-bit so 0xAB is the first byte.
(venv) $ python ex-struct.py 
b'\xab\xcd'

You can also create a new structure by calling the class method “from_buffer_copy” with parameter of bytes type.

# Create a MyStruct initialized to Hex ABCD
b = MyStruct.from_buffer_copy(b"\xab\xcd")
print(bytes(b))

When you run this, you get the same result.

(venv) $ python ex-struct.py 
b'\xab\xcd'

What I was really hoping to be able to do is create a new structure from an array of bytes like this:

# Create a new MyStruct from an Array of bytes ... this is gonna crash
c = MyStruct(b"\x0abb\xcd")
print(bytes(c))

But that crashes.

(venv) $ python ex-struct.py 
b'\xab\xcd\x00\x00'
b'\nbb\xcd'
Traceback (most recent call last):
File "ex-struct.py", line 24, in <module>
c = MyStruct(b"\x0abb\xcd")
TypeError: an integer is required (got type bytes)
(venv) $

And for some reason this took me a really long time to figure out.  You probably say to yourself, “Im not surprised it took him so long to figure out.  He is programming in Python so he probably isn’t very smart anyway”

Overriding __new__ & __init__

While working to understand, I ran into the article “A better way to work with raw data types in Python” which I found interesting.  Here is a screen shot of a bit of the code.

OK.  So lets add the dunder init and dunder new methods to my class.

class MyStruct1(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [    ("first",ctypes.c_uint8,4),
("second",ctypes.c_uint8,4),
("third",ctypes.c_uint8,8),
]
def __new__(self,sb=None):
if(sb):
return self.from_buffer_copy(sb)
else:
return ctypes.BigEndianStructure.__new__(self)
def __init__(self,sb=None):
pass
print("Next case")
c = MyStruct1()
c.first  = 0xa
c.second = 0xb
c.third  = 0xcd
print(bytes(c))
d = MyStruct1(b'\xab\xcd')
print(bytes(d))

Now when I run it, things are good.

(venv) $ python ex-struct.py 
Next case
b'\xab\xcd'
b'\xab\xcd'

Why do I need the __init__?

So, why do I need the dunder init that doesn’t actually do anything? Presumably if I was a real python programmer I would have already known the answer.  But I’m not, so I didn’t.

The first question I had is what does the Python keyword “pass” do?  The answer is nothing.  It is a nop or void if you prefer and is there just to make the program legal as a function has to have some function.

Then onto the Python documentation where I found that if you have an __new__ method that returns an object of the subtype Python will automatically call the __init__ function.

A Conundrum

The other interesting function that the author added was added was:

def __str__(self):
return buffer(self)[:]

This function actually crashes because there is no member called “buffer”.  I am not sure if the cause is:

  • The buffer attribute was left out of the class by the author
  • The buffer was formerly an attribute of the Python 2.x ctypes base class (this is what I suspect)

Stupid Python Tricks: C-Structures using the ctypes Module

Summary

A discussion of reading data out of stream of bytes (encoded in a C-like structure) using the Python ctypes module.  The data in the stream is a UDP packet that represents an mDNS query or request.  The purpose of this article is to explain a process for decoding bytes streams in Python

Story

While I was working on A-Class Linux implementations I fell down the rabbit hole of mDNS.  mDNS is a part of the set of protocols that make up “Zero Configuration Networking”.  In order to understand the protocol I decided to implement (partially) an mDNS server.  You can read about that protocol and my implementation – when I get done :-).  However, all of that isn’t really important to this article, but it did bring me to dig into techniques for examining bytes in Python.

I doubt that this article is canonical, but I hope that it is at least useful.  I did find quite a few partial discussions of this topic, but I had to dig into to really understand.

Python Comment
bytes A built in object to represent an immutable sequence of single bytes.
bytearray A built in object to represent a mutable sequence of single bytes.
struct A module to encode and decode bytes from c-like structures (unfortunately the byte is an atomic unit of the struct module)
ctypes A module to interface to C functions and data.  It contains a bunch of classes which can be used to interface with C-Structures (like the struct module)

There are bunches of web hits on this topic.  However, here are a few which I found useful.

Link Comment
link A basic discussion of the ctypes module and the basic classes
link A discussion of the ctypes.sizeof function
link A discussion of the bytearray
link A Better Way to Work with Raw Data Types in Python

The UDP Header for a mDNS Packet

The IETF RFC 6895 documents the header format for mDNS (and DNS) packets.  The header contains data in Big Endian format encoded into 12 bytes that are broken up into bits, several-bits, and a few 16-bit integers.  Here is snapshot from the RFC.

 

A Red Herring

OK, I admit it.  I am a C-Programmer from way back.  My first inclination to decode the bytes looked like this:

  • Using shifts and or’s to assemble the bytes into big endian uint16s e.g. line 2
  • Using bit masks and logic “and” with or’s and shifts to pick out bit fields e.g. line 12
  • Using a tower of if/elif/elif/else to decode the individual values e.g. lines

Here is my first crack at this.

    id = message[0] << 8 | message[1]
print(f"Id = {id}")
QRFlag = (message[2] & 0x80) >> 7
if QRFlag == 0:
QRFlagText = "QUERY"
else:
QRFlagText = "RESPONSE"
print(f"Query Flag = {QRFlagText}")
opCode = (0b01111000 & message[2]) >> 3
if opCode == 0:
opCodeText = "Query"
elif opCode == 1:
opCodeText = "IQUERY"
elif opCode == 2:
opCodeText = "Status"
elif opCode == 3:
opCodeText = "Reserved"
elif opCode == 4:
opCodeText = "Notify"
elif opCode == 5:
opCodeText = "Update"
else :
opCodeText = "Unknown"
print(f"Opcode ={opCode} {opCodeText}")
AAFlag = (message[2] & 0b00000100) >> 2
AAFlagText = "Authoritative" if AAFlag == 1 else "Non-Authoritative"
print(f"AA Flag = {AAFlag} {AAFlagText}")
TCFlag = (message[2] & 0b00000010) >> 1
TCFlagText = "Truncation" if TCFlag == 1 else "No Truncation"
print(f"TCFlag = {TCFlag} {TCFlagText}")
RDFlag = message[2] & 0b00000001
RDFlagText = "Recursion" if RDFlag == 1 else "No Recursion"
print(f"RDFlag = {RDFlag} {RDFlagText}")
RAFlag = (message[3] & 0b10000000) >> 7
RAFlagText = "Recursion Available" if RDFlag == 1 else "No Recursion Available"
print(f"RAFlag = {RAFlag} {RAFlagText}")
ZFlag =  (message[3] & 0b01110000) >> 4
print(f"Reserved ZFlag = {ZFlag}")
RCCode = message[3] & 0b00001111
if RCCode == 0:
RCCodeText = "No Error"
elif RCCode == 1:
RCCodeText == "Format Error"
elif RCCode == 2:
RCCodeText == "Server Failure"
elif RCCode == 3:
RCCodeText == "Name Error"
elif RCCode == 4:
RCCodeText == "Not Implemented"
elif RCCode == 5:
RCCodeText == "Refused"
elif RCCode == 6:
RCCodeText == "Yx Domain"
elif RCCode == 7:
RCCodeText == "YX RR Set"
elif RCCode == 8:
RCCodeText == "NX RR Set"
elif RCCode == 9:
RCCodeText == "Not Authorized"
elif RCCode == 10:
RCCodeText == "Not Zone"
print(f"RCCode = {ZFlag} {RCCodeText}")
QDCount = message[4]<<8  | message[5]
ANCount = message[6]<<8  | message[7]
NSCount = message[8]<<8  | message[9]
ARCount = message[10]<<8 | message[11]
print(f"Questions = {QDCount} Answers = {ANCount} Name Servers = {NSCount} Additional Records = {ARCount}")

Encoding a c-structure with Bits

I didn’t really like the above implementation.  So I kept digging.  After a while I found the ctypes module.  This lets you

  • Derive a new class from the BigEndianStructure class (line 1)
  • Pack all of the bits and bytes next to each other (line 2)
  • Specify the field names, type and optionally the length in bits (line
class dnsHeader(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [    ("id",ctypes.c_uint,16),
("qr",ctypes.c_uint,1),
("opcode",ctypes.c_uint,4),
("aa",ctypes.c_uint,1),
("tc",ctypes.c_uint,1),
("rd",ctypes.c_uint,1),
("ra",ctypes.c_uint,1),
("z",ctypes.c_uint,3),
("rcode",ctypes.c_uint,4),
("qdcount",ctypes.c_uint16),
("ancount",ctypes.c_uint16),
("nscount",ctypes.c_uint16),
("arcount",ctypes.c_uint16),
]

When you receive data from a socket you will get a tuple that contains

  1. a “bytes” type object containing the raw bytes of the message
  2. a “tuple” containing the IP address (not relevant to this discussion)
    (message, address) = UDPServerSocket.recvfrom(bufferSize)

Now that you have the bytes you can create an object of dnsHeader type to interpret the bytes.  The ctypes class method “from_buffer_copy” will take an array of bytes that is at least the length of the structure and return an object of the type of “dnsHeader”.

    dnsh = dnsHeader.from_buffer_copy(message)

Then you can look at the individual fields like this:

print(f"id = {self.id}")

Stupid Python Tricks – Ensure PIP & Virtual Environments

Summary

This article will show you how to fix your Python setup such that virtual environments that you create will have the correct version of PIP

The Story

I frequently take screen shots as part of my article writing process.  And it absolutely drives me crazy to have “crap” on the screen e.g. warning messages.  I was recently working on an article about PyVISA – a Python package for interacting with Lab Instruments – when I got this error message about the wrong PIP version.

But how can this be as I know that I have the correct version (which at the time was 20.0.2).  You obviously can “fix” this by running an upgrade of pip in your virtual environment.

But that doesn’t really “fix” it.  It just means that the virtual environment you just created has the most up to date PIP.  What is frustrating is that when you exit the virtual environment, the PIP version is correct (look 20.0.2)

Why are the environments differ?   The answer is when you create a virtual environment it will copy Python, pip and easy_install to your binary directory.

As part of doing this it will pickup the “ensurepip” version of PIP which is embedded in the Python installation on your computer.  Ensure pip just ensures that there will be a pip version available in any given Python installation even though Pip is not installed with Python.  On my computer the ensurepip is embedded deeply in a Hombrew directory:

In the screen above you can see that my ensurepip has “19.2.3” when the rest of my computer is set to “20.0.2”. So how do you fix the ensurepip?  Well I first thought that perhaps running the “ensurepip” with the upgrade flag would do it.  Here is what happens.  It seems to fix it.  (but it doesn’t).

The only way that I know how to fix it is install the Python module upgrade_ensurepip.  Here are the commands

  • pip3 install upgrade_ensurepip
  • python3 -m upgrade_ensurepip

Now you when create a virtual environment you will end up with the correct PIP

If you look in the directory you will find that it adds the “pip… whl” director into the ensurepip directory

And it modifies the “_PIP_VERSION” in the ensurepip package __init.py

Keithley 2380-500-15 & 2380-120-60

Summary

In this article I discuss my ridiculous motivation for buying a new Keithley 2830-120-60 to replace my very functional Keithey 2380-500-15

2380-500-15 & 2380-120-60

While working on the IRDC3894 I spent a significant amount of time using the Keithley 2380 in Constant Current Mode.  The development kit that I was testing was configured to enable 1.2v output at 15A.  In the picture below I am pulling 1A.  You can see the Keithley set to pull 1A and it is actually drawing 0.9998A (plenty close)

While I was testing the setup I would slowly increase the current.  In the picture below you can see that I got to 5.4A with no problem.

But at 5.5A the trouble starts.  In the picture below you can see that I am asking for 5.5A but I am only getting 5.48A

And the gap gets worse as I increase the current.

So I posted on the Keithley site trying to figure out what was happening.  Was the Keithley dead?

And unfortunately there is the answer.  The load has a minimum operating voltage of 4.5v when it is in the 15A mode.

But the 2380-120-60 has a 1.8V operating voltage at 60A

And when I get it plugged in I find that it will happily deliver 16A at 1.2V

And it doesn’t start to roll over until 17A (at 1.2V)

Keithley DAQ6510 & 7700

Summary

This article walks you through the first use of a Keithley 7700 20-channel multiplexer module attached to a Keithley DAQ6510.

Keithley describes the module in the data sheet as “The 7700 plug-in module offers 20 channels of 2-pole or 10 channels of 4-pole multiplexer switching that can be configured as two independent banks of multiplexers. There are two additional protected channels for current measurements. Automatic CJC
is provided so that no other accessories are required to make thermocouple temperature measurements. In addition, the 7700 contains latching electromechanical relays that enable signal bandwidths of up to 50 MHz. The 7700 is ideal for RTD, thermistor, and thermocouple temperature applications.”  And they give a nice picture:

And a “schematic”:

The Story

When I bought my original DAQ6510 from Mouser, they did not have a 7700 multiplexer module in stock.  So, I decided to buy one on eBay, which I was really hoping would work.  The module was salvaged out of some installation somewhere in California by a company called “Silicon Salvage”  I was a little bit worried about it because the multiplexor uses actual mechanical relays which wear out in somewhere between 100K and 100M switches.  That seemed like a lot, but who knows.

Assemble

When the unit arrived it seemed OK.  So I put my lab assistant to assembling and testing it.  To test it I bought a bunch of really inexpensive alligator to banana plug wires from China.

Then Nicholas clipped off the alligator ends and tinned the wires.  How about that classic soldering vice?  That was bought at an antique sale in Georgetown Kentucky a few years ago and works great for this kind of thing.  It is also heavy enough to kill Zombies with.

Then he installed the jumper wires onto the board.

Try it out

When everything was button up it was time to test.  Start with turning on the meter and pressing the rear button.

Then press “Build Scan”

Press the “Plus” symbol (to create a new list of channel and settings) to scan

Select some channels and press OK.  It turned out that we tested 3-wires at a time because I used a 3-channel power to supply to setup the voltages to test.

Then pick out DC Voltage

Press the Start Button to launch the meter to scan through the channels and save the values.

And the screen will look like this.

If you press the view scan status you will end on a screen like this.  Notice that you can only see channel 120.  To fix this press the “120”

Then select the other two channels

And you will now see all of the voltages

Here is a picture of the whole thing

Channel Grid App

The DAQ also have a function to display a grid of the channel values.  To get there Press the Apps button

Press “Channel Grid” then Run

It will then ask you to start the Scan

And when it is done you will have the voltages.

It would be really nice if this App had button to re-scan.  Or potentially a way to run the scans in a loop.  I am pretty sure that they give you a way to create Apps to run on this meter… so I suppose I’ll need to fix their App.

Reading Table

You can also view the scan data in a table.  To do this, press “Menu”

Press the “Reading Table”

Which will take you to see a table of the previous scan values.

Step Scan

You can also manually scan the channels by running a “Step Scan”.  Press the “Step Scan” button.

Which will read and display the first channel.

Then you can repeatedly click to work your way through all of the channels.

Stupid Python Tricks: VSCODE c_cpp_properties.json for Linux Kernel Development

Summary

This article shows you how to create a Python program that creates a Visual Studio Code c_cpp_properties.json which will enable intellisense when editing Linux Kernel Modules for Device Drivers.

Story

I am working my way through understanding, or at least trying to understand, Linux Kernel Modules – specifically Linux Device Drivers.  I have been following through the book Linux Device Drivers Development by John Madieu

There are a bunch of example codes in the book which you can “git”

$ git remote -v
origin  https://github.com/PacktPublishing/Linux-Device-Drivers-Development (fetch)
origin  https://github.com/PacktPublishing/Linux-Device-Drivers-Development (push)
$

When I started looking at the Chapter02 example I first opened it up in Visual Studio Code.  Where I was immediately given a warning about Visual Studio Code not knowing where to find <linux/init.h>

And, when you try to click on the “KERN_INFO” symboled you get this nice message.

In order to fix this you need to setup the “c_cpp_propertiese.json” to tell Visual Studio Code how to make intellisense work correctly. But, what is c_cpp_properties.json?

C_CPP_PROPERTIES.JSON

On the Visual Studio Code website they give you a nice description of the schema.  Here is a screenshot from their website.

The “stuff” that seems to matter from this list is the “includePath” which tells intellisense the #includes and #defines.  OK.  How do I figure out where to find all of the files and paths for that?

make –dry-run

When you look at the Makefile it doesn’t appear to help very much.  I don’t know about you guys, but I actually dislike “Make” way more than I dislike Python :-).  What the hell is this Makefile telling you to do?

On line 3 the Makefile creates a variable called “KERNELDIR” and sets the value to the result of the shell command “uname -r” plus “/lib/modules” at the first and “/build” at the end.  If I run the command “uname -r” on my system I get “4.15.0-99-generic”

Then on line 9 it calls make with a “-C” option

obj-m := helloworld-params.o helloworld.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
all default: modules
install: modules_install
modules modules_install help clean:
$(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@

Which tells Make to “change directory”

$ make --help
Usage: make [options] [target] ...
Options:
-b, -m                      Ignored for compatibility.
-B, --always-make           Unconditionally make all targets.
-C DIRECTORY, --directory=DIRECTORY
Change to DIRECTORY before doing anything

OK when I look in that directory I see a bunch of stuff.  Most importantly a Makefile.

$ cd /lib/modules/4.15.0-99-generic/build
$ ls
arch   crypto         firmware  init    Kconfig  Makefile        net      security  ubuntu
block  Documentation  fs        ipc     kernel   mm              samples  sound     usr
certs  drivers        include   Kbuild  lib      Module.symvers  scripts  tools     virt
$

When I look in that Makefile I start to sweat because it is pages and pages of Make incantations.  Now what?

# SPDX-License-Identifier: GPL-2.0
VERSION = 4
PATCHLEVEL = 15
SUBLEVEL = 18
EXTRAVERSION =
NAME = Fearless Coyote
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
# More info can be located in ./README
# Comments in this file are targeted only to the developer, do not
# expect to learn how to build the kernel reading this file.
# That's our default target when none is given on the command line
PHONY := _all
_all:
# o Do not use make's built-in rules and variables
#   (this increases performance and avoids hard-to-debug behaviour);
# o Look for make include files relative to root of kernel src
MAKEFLAGS += -rR --include-dir=$(CURDIR)
# Avoid funny character set dependencies
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC

All hope is not lost.  I turns out that you can have make do a “dry run” which will tell you what are the commands that it is going to execute.  Here is part of the output for the Chapter02 example.  Unfortunately, that is some ugly ugly stuff right there.  What am I going to do with that?

The answer is that I am going to do something evil – really evil.  Which you already knew since this is an article about Python and Make you knew coming in that it was going to be evil.  If you notice above there is a line that contains “…. CC [M] …”  That is one of the lines where the compiler is actually being called.  And you might notice that on the command line there are an absolute boatload of “-I” which is the gcc compiler option to add an include path.

The Python Program

What we are going to do here is write a Python program that does this:

  1. Runs make –dry-run
  2. Looks for lines with “CC”
  3. Splits the line up at the spaces
  4. Searches for “-I”s and adds them to a list of include paths
  5. Searches for “-D”s and adds them to a list of #defines
  6. Spits the whole mess out into a json file with the right format (from the Microsoft website)

I completely understand that this program is far far from a robust production worthy program.  But, as it is written in Python, you should not be too surprised.

To start this program off I am going to use several Python libraries

  1. JSON
  2. OS (Operation System so that I can execute make and uname)
  3. RE (Regular expressions)
import json
import os
import re

The next thing to do is declare some global variables.  The first three are Python Sets to hold one copy each of the includes, defines, other options and double dash options.  The Python Set class allows you to add objects to a set that are guaranteed to be unique (if you attempt to add a duplicate it will be dropped)

includePath = set()
defines = set()
otherOptions = set()
doubleDash = set()
outputJson = dict()

The next block of code is a function that:

  1. Takes as an input a line from the makefile output
  2. Splits the line up into tokens by using white space.  The split function take a string and divides it into a list.
  3. Then I iterate over the list (line 27)
  4. I use the Python string slicer syntax – the [] to grab part of the string.  The syntax [:2] means give me the first two characters of the string
  5. I use 4 if statements to look to see if it is a “-I”, “-D”, “–” or “-” in which case I add it to the appropriate global variable.

Obviously this method is deeply hardcoded the output of this version of make on this operating system… but if you are developing Linux Device Drivers you are probably running Linux… so hopefully it is OK.

#
# Function: processLine
#
# Take a line from the make output
# split the line into a list by using whitespace
# search the list for tokens of
# -I (gcc include)
# -D (gcc #define)
# -- (I actually ignore these but I was curious what they all were)
# - (other - options which I keep track of ... but then ignore)
def processLine(lineData):
linelist = lineData.split()
for i in linelist:
if(i[:2] == "-I"):
if(i[2:2] == '/'):
includePath.add(i[2:])
else:
includePath.add(f"/usr/src/linux-headers-{kernelVersion}/{i[2:]}")
elif (i[:2] == "-D"):
defines.add(i[2:])
elif (i[:2] == "--"):
doubleDash.add(i)
elif (i[:1] == '-'):
otherOptions.add(i)

The next block of code runs two Linux commands (uname and make –dryrun)  and puts the output into a string.  On line 51 I split the make output into a list of strings one per line.

# figure out which version of the kernel we are using
stream = os.popen('uname -r')
kernelVersion = stream.read()
# get rid of the \n from the uname command
kernelVersion = kernelVersion[:-1]
# run make to find #defines and -I includes
stream = os.popen('make --dry-run')
outstring = stream.read()
lines = outstring.split('\n')

In the next block of code I iterate through the makefile output looking for lines that have the “CC” in them.  I try to protect myself by requiring that the CC have white space before and after.  Notice one line 56 that I use a regular expression to look for the “CC”.

for i in lines:
# look for a line with " CC "... this is a super ghetto method
val = re.compile(r'\s+CC\s+').search(i)
if val:
processLine(i)

The last block of code actually create the JSON and writes it to the output file c_cpp_properties.json.

# Create the JSON 
outputJson["configurations"] = []
configDict = {"name" : "Linux"}
configDict["includePath"] = list(includePath)
configDict["defines"] = list(defines)
configDict["intelliSenseMode"] = "gcc-x64"
configDict["compilerPath"]= "/usr/bin/gcc"
configDict["cStandard"]= "c11"
configDict["cppStandard"] = "c++17"
outputJson["configurations"].append(configDict)
outputJson["version"] = 4
# Convert the Dictonary to a string of JSON
jsonMsg = json.dumps(outputJson)
# Save the JSON to the files
outF = open("c_cpp_properties.json", "w")
outF.write(jsonMsg)
outF.close()

Thats it.  You can then:

  1. Run the program
  2. move the file c_cpp_properties.json in the .vscode directory

And now everything is more better 🙂  When I hover over the “KERN_INFO” I find that it is #define to “6”

I will say that I am not a fan of having the compiler automatically concatenate two strings, but given that this article is written about a Python program who am I to judge?

What could go wrong?

There are quite a few things that could go wrong with this program.

  1. The make output format could change
  2. There could be multiple compiles that have conflicting options
  3. I could spontaneously combust from writing Python programs
  4. The hardcoded cVersion, cStandard, compilerPath, intelliSenseMode could change enough to cause problems

All of these things could be fixed, or at least somewhat mitigated.  But I already spent more time down this rabbit hole that I really wanted.

The Final Program

# This program runs "make --dry-run" then processes the output to create a visual studio code
# c_cpp_properties.json file
import json
import os
import re
includePath = set()
defines = set()
otherOptions = set()
doubleDash = set()
outputJson = dict()
# Take a line from the make output
# split the line into a list by using whitespace
# search the list for tokens of
# -I (gcc include)
# -D (gcc #define)
# -- (I actually ignore these but I was curious what they all were)
# - (other - options which I keep track of ... but then ignore)
def processLine(lineData):
linelist = lineData.split()
for i in linelist:
if(i[:2] == "-I"):
if(i[2:2] == '/'):
includePath.add(i[2:])
else:
includePath.add(f"/usr/src/linux-headers-{kernelVersion}/{i[2:]}")
elif (i[:2] == "-D"):
defines.add(i[2:])
elif (i[:2] == "--"):
doubleDash.add(i)
elif (i[:1] == '-'):
otherOptions.add(i)
# figure out which version of the kernel we are using
stream = os.popen('uname -r')
kernelVersion = stream.read()
# get rid of the \n from the uname command
kernelVersion = kernelVersion[:-1]
# run make to find #defines and -I includes
stream = os.popen('make --dry-run')
outstring = stream.read()
lines = outstring.split('\n')
for i in lines:
# look for a line with " CC "... this is a super ghetto method
val = re.compile(r'\s+CC\s+').search(i)
if val:
processLine(i)
# Create the JSON 
outputJson["configurations"] = []
configDict = {"name" : "Linux"}
configDict["includePath"] = list(includePath)
configDict["defines"] = list(defines)
configDict["intelliSenseMode"] = "gcc-x64"
configDict["compilerPath"]= "/usr/bin/gcc"
configDict["cStandard"]= "c11"
configDict["cppStandard"] = "c++17"
outputJson["configurations"].append(configDict)
outputJson["version"] = 4
# Convert the Dictonary to a string of JSON
jsonMsg = json.dumps(outputJson)
# Save the JSON to the files
outF = open("c_cpp_properties.json", "w")
outF.write(jsonMsg)
outF.close()

 

AnyCloud – Wireless Connection Manager (Part 1)

Summary

A discussion of using the Infineon (Cypress) PSoC 6 with a CYW4343W and the AnyCloud Connection Manager with Modus Toolbox.  The AnyCloud Connection Manager is an RTOS thread that lets you manage a connection to a WiFi network.  It knows how to scan for networks, attach, detach etc. and was recently released for use with PSoC6 and 43xxx WiFi combos.

The Story

In the WICED WiFI SDK there is an example program called “test.console” which allows you to use a UART command line to do networking “stuff”.  With the release of the new AnyCloud SDK I decided that I should rebuild some of that program using the PSoC and WiFi.  Basically a set of commands like “scan” to interact with the Radio World!  In the picture you below you can see that I use the command “scan” to list out the networks at my house.

You can also use the command “help” to list out the commands.

Architecture

My implementation will have three tasks

  1. A Blinking LED Task (which doesn’t interact with any other tasks)
  2. The NT Shell – which will send commands to the network queue.
  3. The Network Task which will receive commands from the NT Shell task and trigger the Wireless Connection Manager to do something
  4. The Wireless Connection Manager which will interact with the WiFI Radio via the Wireless Host driver.

Make the Basic Project

To build this project start by creating a new project.  The development board that I had plugged into my computer when I began this project is a CY8CPROTO-062-4343W so this is the one I will select.

In the new project creator pick the “CY8CPROTO-062-4343W”

I created a FreeRTOS template project that does all of the right stuff.  I wrote an article about how to use the IoT Expert Manifestt here, and until you add the IoT Manifest to your setup you will not get the FreeRTOS Template project.

After the new project work is done you should see

Add the nt-shell, which comes from my IoT Expert library (read about how to add the IoT Expert library here).

I have been on a kick of using Visual Studio code.  So, run “make vscode” to setup the project for Visual Studio Code.

In the finder, copy the template files from nt-shell into your project.

You can also do it with the command line.

Edit main.c to include the configuration.

#include "ntshell.h"
#include "psoc6_ntshell_port.h"

Update the main.c to have the ntShellThread

// Global variable with a handle to the shell
ntshell_t ntshell;
void ntShellTask()
{
printf("Started ntshell\n");
setvbuf(stdin, NULL, _IONBF, 0);
ntshell_init(
&ntshell,
ntshell_read,
ntshell_write,
ntshell_callback,
(void *)&ntshell);
ntshell_set_prompt(&ntshell, "AnyCloud> ");
vtsend_erase_display(&ntshell.vtsend);
ntshell_execute(&ntshell);
}

And start the ntshell task.

    xTaskCreate(ntShellTask, "nt shell task", configMINIMAL_STACK_SIZE*2,0 /* args */ ,4 /* priority */, 0);

When you want to add a command to the shell you need to do three things

  1. Add a function prototype for the command
  2. Add the command to the command list
  3. Write the function
static int usrcmd_help(int argc, char **argv);
static int usrcmd_info(int argc, char **argv);
static int usrcmd_clear(int argc, char **argv);
static int usrcmd_printargs(int argc, char **argv);
static const cmd_table_t cmdlist[] = {
{ "help", "This is a description text string for help command.", usrcmd_help },
{ "info", "This is a description text string for info command.", usrcmd_info },
{ "clear", "Clear the screen", usrcmd_clear },
{ "printargs","print the list of arguments", usrcmd_printargs},
};

Here is an example of of the printargs command.  The shell will call your function with a ARGC=number of arguments and ARGV[] an array of the pointers to the actual arguments.

static int usrcmd_printargs(int argc, char **argv)
{
printf("ARGC = %d\n",argc);
for(int i =0;i<argc;i++)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
return 0;
}

Build and test your program.

Turn on the Connection Manager and Core Middleware Library

Now, add the wifi-mw-core library & Connection Manager using the library manager.  You can start it with “make modlibs”.  These two libraries are in the “WiFi Middleware” category.

Copy the FreeRTOSConfig.h from the libs/wifi-mw-core/configs directory into your project (so you can modify it)

Update the Makefile to include the WiFi components (line 71) and the MBED TLS configuration (line 86)

COMPONENTS=FREERTOS PSOC6HAL LWIP MBEDTLS 4343W
# Like COMPONENTS, but disable optional code that was enabled by default.
DISABLE_COMPONENTS=
# By default the build system automatically looks in the Makefile's directory
# tree for source code and builds it. The SOURCES variable can be used to
# manually add source code to the build process from a location not searched
# by default, or otherwise not found by the build system.
SOURCES=
# Like SOURCES, but for include directories. Value should be paths to
# directories (without a leading -I).
INCLUDES=
# Add additional defines to the build process (without a leading -D).
DEFINES=MBEDTLS_USER_CONFIG_FILE='"configs/mbedtls_user_config.h"' CYBSP_WIFI_CAPABLE

Create networkTask.h/.c & Start the Task in main.c

Now let’s create the networkTask.  This task will control all of the networking functions in the system.  The first file, networkTask.h, is the public interface.  It declares a Queue where you can push messages (line 5) an enumeration of the messages (lines 7-14), a definition of the message data (lines 17-22) and finally the network task declaration (line 24).

#pragma once
#include "FreeRTOS.h"
#include "queue.h"
extern QueueHandle_t networkQueue;
typedef enum {
net_scan,
net_connect,
net_disconnect,
net_printip,
net_printmac,
} networkCmd_t;
typedef struct {
networkCmd_t cmd;
uint32_t val0;
uint32_t val1;
} networkQueueMsg_t;
void networkTask(void *arg);

Ill go ahead and modify main.c to start the yet to be written network task.  You need to include the header file for the task and define a handle for the task.

#include "networkTask.h"
TaskHandle_t networkTaskHandle;

Finally in main function, start the task.

    xTaskCreate(networkTask, "networkTask", configMINIMAL_STACK_SIZE*8,0 /* args */ ,4/* priority */, &networkTaskHandle);

Create the file networkTask.c.  Make a bunch of includes.

#include <stdio.h>
#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#include "cy_wcm.h"
#include "cy_wcm_error.h"
#include "whd_types.h"
#include "queue.h"
#include "semphr.h"
#include "networkTask.h"

Now let’s define the actual task function.  This function will

  1. Call the wcm_init function to make a WiFi Station (lines 201-203)
  2. Tell the task that you would like to be called back when things happen (line 205)
  3. Initialize the Queue to receive command messages from other tasks.
  4. Make an infinite loop to receive the commands and process the messages.  Notice that I dont do anything with the message for now.  Ill add the code later.
// The networkTask will:
// - startup the wireless connection manager
// - sit waiting on the rtos queue... getting messages from other tasks
// - and do ( net_scan, net_connect, net_disconnect, net_printip, net_printmac,)
void networkTask(void *arg)
{
cy_rslt_t result;
cy_wcm_config_t config;
cy_wcm_scan_filter_t scanFilter;
memset(&config, 0, sizeof(cy_wcm_config_t));
config.interface = CY_WCM_INTERFACE_TYPE_STA;
cy_wcm_init	(&config);
cy_wcm_register_event_callback(	wcmCallback	);
networkQueue = xQueueCreate( 5, sizeof(networkQueueMsg_t));
while(1)
{
networkQueueMsg_t msg;
xQueueReceive(networkQueue,(void *)&msg,portMAX_DELAY);  // Wait for commands from other tasks
switch(msg.cmd)
{
case net_scan: // 0=stop scan !0=start scan
break;
case net_connect:
break;
case net_disconnect:
break;
case net_printip:
break;
case net_printmac:
break;
}
}
}

Create Utilities for Printing out the IP and MAC Address

It the network task I want to be able to print out IP address (both IPV4 and IPV6).  I also want to be able to print out 6-byte MAC address.  In the Cypress drivers, an IP address is a structure that holds either a IPV4 or and IPV6 address.  It then contains the 4 or 6 bytes of the address.  Here is the type definition.

/**
* IP Address Structure
*/
typedef struct
{
cy_wcm_ip_version_t version;  /**< IP version */
union
{
uint32_t v4;     /**< IPv4 address bytes */
uint32_t v6[4];  /**< IPv6 address bytes */
} ip;                /**< IP address bytes */
} cy_wcm_ip_address_t;

The IP v ersion is just an enumeration.

/**
* IP Version
*/
typedef enum
{
CY_WCM_IP_VER_V4 = 4,      /**< Denotes IPv4 version */
CY_WCM_IP_VER_V6 = 6       /**< Denotes IPv6 version */
} cy_wcm_ip_version_t;

To print an address, figure out which version you are working on.  Then dump the raw bytes.  Notice in the IPV4 case it is encoded into a uint32_t as 4 continuous bytes.

void printIp(cy_wcm_ip_address_t *ipad)
{
if(ip_addr.version == CY_WCM_IP_VER_V4)
{
printf("%d.%d.%d.%d",(int)ipad->ip.v4>>0&0xFF,(int)ipad->ip.v4>>8&0xFF,(int)ipad->ip.v4>>16&0xFF,(int)ipad->ip.v4>>24&0xFF);
}
else if (ip_addr.version == CY_WCM_IP_VER_V6)
{
for(int i=0;i<4;i++)
{
printf("%0X:",(unsigned int)ip_addr.ip.v6[i]);
}
}
else
{
printf("IP ERROR %d",ipad->version);
}			
}

A MAC address is just an array of uint8_t of length CY_WCM_MAC_ADDR_LEN  (which we pound defined to 6)

typedef uint8_t cy_wcm_mac_t[CY_WCM_MAC_ADDR_LEN];                   /**< Unique 6-byte MAC address */

So, the print is just a loop. (probably should have done something slightly better so I dont end up with the trailing : – oh well)

void printMac(cy_wcm_mac_t mac)
{
for(int i=0;i<CY_WCM_MAC_ADDR_LEN;i++)
{
uint8_t val = mac[i];
printf("%02X:",val);
}
}

Add the Scan Command

For the scan I want to print out the:

  1. SSID
  2. RSSI (in dBM)
  3. Channel
  4. Band
  5. Speed
  6. Type of Ap
  7. Country Code
  8. MAC address of the Access Point (AKA BSSID)
  9. Type of Security

In order to have the WCM run a scan you need to call the function cy_wcm_start_scan.  What this will do is tell the 4343W to scan all the channels on the 2.4GHZ band and listen for access point beacons.  This function has three arguments

  1. A function pointer to call back when you find an AP
  2. A user settable data pointer
  3. A filter (which can limit to an SSID, BSSID or RSSI)
cy_rslt_t cy_wcm_start_scan(cy_wcm_scan_result_callback_t callback, void *user_data, cy_wcm_scan_filter_t *scan_filter)

Here is my version of the scanCallback which I put in the networkTask.c file.

// This function is called back when the user asks for an overall scan.
// It just prints out the information about the networks that it hears about
void scanCallback( cy_wcm_scan_result_t *result_ptr, void *user_data, cy_wcm_scan_status_t status )

The result_ptr is a pointer to a structure that contains the data for the found access point.

/**
* Structure for storing scan results
*/
typedef struct
{
cy_wcm_ssid_t                SSID;             /**< Service Set Identification (i.e. Name of Access Point)                    */
cy_wcm_mac_t                 BSSID;            /**< Basic Service Set Identification (i.e. MAC address of Access Point)       */
int16_t                      signal_strength;  /**< Receive Signal Strength Indication in dBm. <-90=Very poor, >-30=Excellent */
uint32_t                     max_data_rate;    /**< Maximum data rate in kilobits/s                                           */
cy_wcm_bss_type_t            bss_type;         /**< Network type                                                              */
cy_wcm_security_t            security;         /**< Security type                                                             */
uint8_t                      channel;          /**< Radio channel that the AP beacon was received on                          */
cy_wcm_wifi_band_t           band;             /**< Radio band                                                                */
uint8_t                      ccode[2];         /**< Two letter ISO country code from AP                                       */
uint8_t                      flags;            /**< flags                                                                     */
uint8_t                      *ie_ptr;          /**< Pointer to received Beacon/Probe Response IE(Information Element)         */
uint32_t                     ie_len;           /**< Length of IE(Information Element)                                         */
} cy_wcm_scan_result_t;

The other parameter to the callback is the status.  The status will either be CY_WCM_SCAN_COMPLETE or CY_WCM_SCAN_INCOMPLETE.  The first thing that to do is see if this callback is the one that tells me that the scan is complete, if so I just return (and don’t print anything)

	if(status == CY_WCM_SCAN_COMPLETE)
return;

Then I print out the raw SSID, Signal Strength and Channel.

	printf("%32s\t%d\t%d\t",result_ptr->SSID,result_ptr->signal_strength,result_ptr->channel);

Next I figure out what channel we are talking about.  The 4343W is single band 2.4GHZ, however, other Cypress chips have dual band.

	switch(result_ptr->band)
{
case CY_WCM_WIFI_BAND_ANY:
printf("ANY");
break;
case CY_WCM_WIFI_BAND_5GHZ:
printf("5.0 GHZ");
break;
case CY_WCM_WIFI_BAND_2_4GHZ:
printf("2.4 GHZ");
break;
}

Then I printout the maximum data rate, which is 0 for all of my test cases.  I have no idea why.

	printf("%d",(int)result_ptr->max_data_rate);

Then I printout what type of AP we are talking about.

	switch(result_ptr->bss_type)
{
case CY_WCM_BSS_TYPE_INFRASTRUCTURE:
printf("INFR");
break; 	
case CY_WCM_BSS_TYPE_ADHOC: 
printf("ADHOC");
break;	
case CY_WCM__BSS_TYPE_ANY: 	
printf("ANY");
break;
case CY_WCM_BSS_TYPE_MESH:
printf("MESG");
break;
case CY_WCM_BSS_TYPE_UNKNOWN: 
printf("UNKWN");
break;
}

Then the country code.

	printf("%c%c",result_ptr->ccode[0],result_ptr->ccode[1]);

Then the Basic Service Set ID of the AP, which is also known as the MAC address of the AP.

	printMac(result_ptr->BSSID);

Then the security type.

	switch(result_ptr->security)
{
case CY_WCM_SECURITY_OPEN:
printf("OPEN");
break;
case CY_WCM_SECURITY_WEP_PSK:
printf("WEP_PSK");
break;
case CY_WCM_SECURITY_WEP_SHARED:
printf("WEP_SHARED");
break;
case CY_WCM_SECURITY_WPA_TKIP_PSK:
printf("WPA_TKIP_PSK");
break;
case CY_WCM_SECURITY_WPA_AES_PSK:
printf("WPA_AES_PSK");
break;
case CY_WCM_SECURITY_WPA_MIXED_PSK:
printf("WPA_MIXED_PSK");
break;
case CY_WCM_SECURITY_WPA2_AES_PSK:
printf("WPA2_AES_PSK");
break;
case CY_WCM_SECURITY_WPA2_TKIP_PSK:
printf("WPA2_TKIP_PSK");
break;
case CY_WCM_SECURITY_WPA2_MIXED_PSK:
printf("WPA2_MIXED_PSK");
break;
case CY_WCM_SECURITY_WPA2_FBT_PSK:
printf("WPA2_FBT_PSK");
break;
case CY_WCM_SECURITY_WPA3_SAE:
printf("WPA3_SAE");
break;
case CY_WCM_SECURITY_WPA3_WPA2_PSK:
printf("WPA3_WPA2_PSK");
break;
case CY_WCM_SECURITY_IBSS_OPEN:
printf("IBSS_OPEN");
break;
case CY_WCM_SECURITY_WPS_SECURE:
printf("WPS_SECURE");
break;
case CY_WCM_SECURITY_UNKNOWN:
printf("UNKNOWN");
break;
case CY_WCM_SECURITY_FORCE_32_BIT:
printf("FORCE_32_BIT");
break;
}

With a complete scanCallback function I can now update the networkTask to deal with the queued commands of net_scan type.  This simply either stops or starts the scan based on the message parameter.

			case net_scan: // 0=stop scan !0=start scan
if(msg.val0 == 0)
cy_wcm_stop_scan();
else
{
printf("\n");
result = cy_wcm_start_scan(scanCallback,0,0);
if(result != CY_RSLT_SUCCESS)
printf("Scan error\n");
}
break;

The last things to do is to add the scan command to the usrcmd.c

static int usrcmd_scan(int argc, char **argv)
{
static int state=0;
if(argc==1)
{
state = (state ==0)?1:0;
}
else if(argc == 2)
{
if(strcmp(argv[1],"on") == 0)
{
state=1;
} 
else if(strcmp(argv[1],"off") == 0)
{
state = 0;
}
else
{
printf("usage: scan [on|off]\n");
return 0;
}
}
else
{
printf("usage scan: [on|off]\n");
return 0;
}
networkQueueMsg_t msg;
msg.cmd = net_scan;
msg.val0 = state;
xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY);
return 0;
}

OK.  Let it rip.  You should be able to run network scans.  In the next Article I will add a connect and disconnect commands.

You can find all of this example code at https://github.com/iotexpert/wcm_example

IoT AdvantEdge AnyCloud Webinar

Summary

This Article contains the exact flow and screen shots that I will follow for the IoT AdvantEdge Webinar.  There will be 7 sections:

  1. Modus Toolbox 2.1 & AnyCloud SDK
  2. Basic Eclipse Flow
  3. Command Line Flow
  4. Visual Studio Code Flow
  5. Connectivity Core Middleware
  6. Low Power Assistant
  7. AnyCloud Libraries

Modus Toolbox 2.1 & AnyCloud SDK

What is Modus Toolbox 2.1 and the AnyCloud SDK?

  • Integrated platform for compute and connectivity
  • Windows, Linux and Mac
  • IAR, Eclipse, ARM MDK, Visual Studio Code, MBED Studio and Command line
  • We have complete integration at the company level for both IAR and the ARM MDK – Cypress is native in their tools
  • Amazon FreeRTOS, FreeRTOS, MBED OS
  • The entire PSoC 6 family
  • The WiFi Bluetooth Combo’s 4343 and 43012
  • All the Cypress 20xxx Family Bluetooth Chips (a huge step forward in making WICED and PSoC 6 look the same)
  • Low Power Assistant (to build combination solutions with PSoC6 and WiFI/BT)
  • A generic new project creator
  • All of our configurators run on all of the platforms
  • A bunch of new code examples.
  • Offline support (so you don’t have to be connected to the internet)
  • Major updates to the documentation
  • A bunch of improvements in testing
  • The AnyCloud SDK which contains Wireless Connection Manager, LWIP, MBEDTLS, SecureSockets, Low Power Assistant, Secure OTA ToolKit, New Host Driver

Basic Flow

Startup the Modus Toolbox Elipse IDE…

Press “New Application” in the Quick Panel

Make a new project.  The development kit that I happen to be using today is the “CY8CPROTO-062-4343W”.  Press “Next”

Start with the Empty project starter, notice that it goes to the GitHub on the internet because we UNCOUPLED the IDE from the SDKs and BSP (but you can do offline mode).  It means we can make updates to libraries without having to change everything.  In addition you can extend Modus Toolbox.

You can also start from your own template (using the import button)

Pick the “Empty PSoC6 App” and press Create.  Look at the output window … What is it doing?  Simple, loading all of our code into your project as libraries.

Close and notice that it takes you back to Eclipse and imports the project.

Look at the dependencies directory… then in the libraries directory.  Notice that the libraries directory can get re-generated anytime by the files in the dependencies directory.

Run a build.

Press Program on the Quick Panel.  Notice that we support JLINK and KitProg out-of-the-box.

Run the library manager by clicking in the Quick Panel.  This is a utility to let you manipulate the libraries in your project.

The Library Manager gives you the ability to add a new BSP to the project and change to the new BSP.  Notice below that I switched to CY8CKIT-062S2-43012

You can also upgrade latest to the latest BSP or lock to a specific version

Notice that it added a file TARGET_CYKIT-062S2-43012.lib to the project.  And downloaded that library into your libs directory.

Open the Makefile.  Notice the target is now the CY8CKIT-062S2-43012.  You can have multiple BSPs active in your project, but only one active at a time.  The inactive BSPs will be ignored by the build process.

And when you build it you will get a new Hex & Elf for that target.

Command Line

Start the command line interface, just a terminal in Mac or Linux.  On Windows you can run the program “<user>/ModusToolbox/tools_2.1/modus-shell/Cygwin.bat”  Change directory to your Workspace / Project folder.  Then run “make modlibs”

This starts up the library manager (the exact same one you used from inside of Eclipse.  This is a standalone GUI tool which runs on Mac, Linux and Windows.  Click on the Libraries tab, then add FreeRTOS

Look at the deps and libs directory

Now you can run make build

You might have noticed that there were some warning messages during the build.  This is because the FreeRTOSConfig.h we provide has a warning in it saying that you should customize it.

Lets fix that by moving the template into our project with “cp libs/freertos/Source/portable/FreeRTOSConfig.h .”  Then I will run emacs and fix the #warning line

When I run “make build” everything is better

Now I can run “fw-loader –device-list” (on your computer you can find it in the Modus Toolbox directory) and it will show me that I have the development kit attached to this computer

Then I can program with “make program”

Visual Studio Code

You can also use Visual Studio Code with your project.  To do this run “make vscode” which sets up the files needed to make Visual Studio Code work.  Then you can run Visual Studio Code with the command “code .”  or you can just open the directory from the Visual Studio Code file menu.

And in Visual Studio Code you can…

… build with “Run Build Task…”

Pick out the build that you want (in this case “Debug”)

The make build system will “do the needful”

Then you can actually Program the project and start the debugger with “Run -> Start Debugging”

Which will program the chip and start up the debugger.  Then it will halt at main.

Connectivity Core Middleware

Now run “make modlibs” so we can start to add connectivity to the project.


Notice that on the library tab you can see a bunch of “WiFi middleware libraries”. Choose “wifi-mw-core” and press apply

Notice that it adds lwip, mbedTLS, secure-sockets.  The lwip, mbedTLS come from GitHub.  We aren’t hosting them, but we do the configuration for you.

Look in mbed_tls_user_config.h

And lwip_opts.h

Go back to the library manager and add the WiFi Connection Manager. [edit: I also should have checked LPA here]

Find the doc directory for the connection manager directory.  Then open the file “api_reference.html”

Double click on it and open it up in the web browser. 

Low Power Assistant

The key to low power is to STAY ASLEEP, but when you wake up,  get after it!  The pairing of PSoC 6 & 43xxx is key to making super lower power projects.  We give you a tool called the “Low Power Assitant”.  To run it start up “make config”.  Then go to the “System->Power” tab.

Now click on the “CYW4…” tab.  Go to the “Power” section.  Then enable “wi-fi”

We give you a set of default filters which you can enable with “Add a Minimal Set of Keep Filters”

Look at the Preview tab and you will see a preview of the code that will be generated for you.

Hit save, now you can look at the actual generated code in your project.  It is just normal C-Code.

AnyCloud Libraries

Quit Visual Studio Code and go back to Eclipse.  This project has all of the stuff that you changed and is still a perfectly functional Eclipse project.

Now lets create a new project.  Click on “New Application” in the Quick Panel.

Choose the “CY8CPROTO-062-4343W” BSP

And pick the “AnyCloud MQTT Client” starter project.

After a flash you will get a message like this in your New Project Wizard window.  Click “Close”

And you will have the project

Notice that you now have some new libraries.  Specifically:

  • aws-iot-device-sdk-embeded-c
  • mqtt
  • secure-sockets

In order to make a connection to the Amazon Cloud you need to have some cryptography keys.  When you create “things” on Amazon you will be given the chance to download them.  I did this earlier, so I will just copy them into my project.

Now you can see the file in the project.  Notice that I also defined “AWS_BROKER” to be the DNS name of my Amazon WebService IoT MQTT broker.

Edit the “mqtt_client_config.h” to include your keys.  And to define the MQTT broker address. 

 

 

Get rid of the unused keys definitions:

Edit the WiFI SSID and Password in “wifi_config.h”

Finally program it.  Once it is programmed you can see the output on a serial terminal

And on the AWS Web Console I can subscribe to the topic.

And I will see the button press messages:

Keithley DAQ6510 Unboxing

Summary

A bunch of pictures of the unboxing of my new Keithley DAQ6510.  DAQ stands for Data Acquisition.  This box is essentially a 6.5 digit multimeter with a 20+ channel multiplexor attached.

The Story

My lab assistant, Nicholas, says that Unboxing is big in social media and that I really need to do an unboxing for IoT Expert.  Well here it is.  As I am sure you know, I have been working on a power supply design for a new IoT device.  As part of this I needed the ability to measure voltage and current from several different places and my trusty Fluke wasn’t going to be able to do that.

I bought this from my good friends at Mouser.  Here is the box.

When you open it here is what you see.

If I take everything out there is

  1. The meter
  2. Some test leads
  3. The manual
  4. a USB cable
  5. And a calibration certificate

On the back there is two places to plug in multiplexers + a LXI ethernet connection, power, USB, two BNC triggers, and a communication port.

It is always fun to tear off the film over the screen.

On the top there is a label with QR codes to documentation and software

When I turn it on, the system boots…

And then drops into DC voltage measurements.  Good new is that it measures 0

Unfortunately I very quickly ended up with the Blue Screen of Death (BSOD).

So I downloaded and installed the latest firmware (and it now seems to be stable).

When I turned on the current measurement with a PSoC 6 (in LP Active Mode)… I get numbers that make sense.

 

And this is cool… when I side swipe the screen it get a graph of the last 5ish minutes.

 

Modus Toolbox 2.1 Released

Summary

Yesterday, the Cypress software team released an update to Modus Toolbox, specifically to the “tools package”.  I am super excited about a bunch of the new features and I thought that I would should show you a few of the updates.  Specifically:

  1. The New Project Wizard
  2. Updated Directory Structure for Projects
  3. Visual Studio Code Integration
  4. Eclipse Integration
  5. Updates to the HAL

New Project Wizard

The first interesting thing about Modus Toolbox is that we designed it to be independent of IDE.  If you want to use Eclipse, OK.  If you want to use Visual Studio Code or IAR or MDK or … that is cool with us as well.  We did tons of work to make sure that our tools are completely independent of the operating system and the IDE.

You can still create your projects from inside of Eclipse from the Quick Panel.

The update in Modus Toolbox 2.1 is that the new project will launch an external project creation tool.

That will look like this:

However, if you want, you can totally ditch Eclipse and start the new project wizard from outside (via the Start menu or the Launchpad).  The new project creation wizard is a stand alone program that is written in C++ program and Qt that works on Mac, Windows, and Linux.  When you run the new project creation tool you will see this (at least on a Mac).  Look, it is exactly the same as the one you get from inside Eclipse (no surprise since it is exactly the same thing)

Notice that there is NO requirement that you run this tool from Eclipse.  On Windows you can find it from the Start menu and on Mac you can find it on the Launchpad (as project-creator).

First pick out a “Kit name” which will select the board support package for your project.  Ill Pick “CY8CKIT-062-WIFI-BT” (because that is what happens to be on my desk and press Next.

When the Project Creator starts it will go to the Cypress GitHub site and load in a file that will tell it all of the example projects and BSPs available.  This means that Cypress can release new BSPs and Example templates without you having to update your version of Modus Toolbox.

I give my example a name of “ExampleMTB21” then pick out the “Empty PSoC6 App” and press Create.


To create a project we basically “git clone” a bunch of different repositories.  So, in the output window, you will mostly see the output of Git doing its thing.

When it is all done you can press “Close”.  Now you can look in the file browser and you will see a complete project.

Updated Directory Structure

When you look at the files inside of a file browser you should notice two things.   First there is a directory called “deps” which has two files called “dot Libs”.  These two files contain URLs to the GitHub repositories for two of the BSPs.  In Modus Toolbox 2.0 we stored the dotLib files in the same directory with the actual libraries.  When you look in the Libs directory you will see the actual libraries which are required to build your project.  The Libs directory is now “Generated Source” which means you can blow it away and it can be re-generated with “make getlibs” (which will read the dotLibs from the Deps directory”

Command Line Interface

If you don’t like IDE’s you can now build and program this with our command line interface by running “make build”

And then you can program your board with “make program”

Visual Studio Code Integration

The command line stuff is cool, but I know that there are tons of people out there who like Visual Studio Code which we we now officially support.  To use it you can run “make vscode” and it will create the files required to use your project in Visual Studio Code.

When I look at the project I see that Modus Toolbox create a directory called “.vscode” which has all of the secret sauce to make Visual Studio Code work.

Now I can run “code .” (on my Mac) to start Visual Studio Code.  Or on a PC you can run Visual Studio Code from the Start and just open the directory.  Notice that when you run the “make vscode” we give you the instructions.  Inside of Visual Studio Code you will see the project.

You can press cmd-shift-b to build the project

Which will send all of the output to the Visual Studio Code console.

And you can then press F5 to start the programmer/debugger

Eclipse Integration

But if you are an Eclipse user where does this leave you?  Notice that you don’t have the Eclipse project files in your project anymore… AHHHHH???!?!?!   Relax.  We still love you.  To get your project back into Eclipse you just need to run “make eclipse” and we will recreate the Eclipse project for you.

If you want to add this to an existing workspace you can run “Import”

Then pick out your directory.

Press “Generate Launches for …” on the Quick Panel.  Then Press Build.

And now you have a fully functioning Modus Toolbox Project inside of your Eclipse workspace.

HAL Updates

Another one of my favorite things was a massive update to the documentation inside of the Hardware Abstraction Layer.  At this point most of the HAL drivers should have Code snippets to give you an example of what to do.

ModusToolbox 2.0 – Libraries an Example for emWin & Super Manifests

Summary

In this article I will walk you through creating a library for ModusToolbox 2.0 that will glue the Cypress RTOS Abstraction layer, emWin, the SSD1306 and the PSoC 6 together.  In the end, the library will become available in the Modus Toolbox Library Manager by creating a Super Manifest file.

NOTE:  This blog was originally written about the Modus Toolbox 2.0 library scheme.  With the release of Modus Toolbox 2.2 this scheme was changed and this blog is now obsolete! Instead, you can use the Infineon display-oled-ssd1306 library.

The Story

I like using the Segger emWin graphics library.  And, I have always hated getting the “glue” into my project that makes the library work.  The glue includes the configuration files for emWin plus the hardware driver functions to talk to the SSD1306 screen.  I have always thought that it would be really nice to have a simple library scheme – yes I know that there are lots of schemes out there but I wanted one that was neatly integrated into our tools.  Well, with ModusToolbox 2.0 my wish was granted.

In ModusToolbox 2.0 if you create a file called “someLibraryName.lib” that contains a URL to a GitHub (or Git) repository, the make system will know how to bring that library into your project.  The make target “getlibs” does exactly that.  And, once it is on your computer in your project the Modus Toolbox make system will know how to include it as part of your project.

For this article I will create a library called “p6sdk-ssd1306-emWin-freerots-config” which will contain:

Files Purpose
GUIConf.h Configures emWins abilities, fonts etc.
GUIConf.c Defines & Assigns RAM for the GUI,initializes the font
GUI_X_CYRTOS.c Makes a connection between emWin and Cypress RTOS abstraction for things like timing, semaphore etc.
LCDConf.h A blank file
LCDConf.c Functions to configure screen,connect the emWin APIs to the I2C write functions, configures the display driver
SSD1306Driver.h Functions prototypes to initialize the I2C and read/write the I2C
SSD1306Driver.c Initialize the SSD1305 driver, write commands, write data, write datastream functions which are called by the LCDConf.c functions
template A directory (which is not compiled) that contains template c files for use as an example

SSD1306 Driver

In order to glue the hardware to the emWin library you need to provide functions that

  1. Initialize the driver – (Tell it what I2C hardware and I2C address the display is connected to)
  2. Write data/command bytes and streams
  3. Read data streams (which it actually never does)

The SSD1306Driver.h provides a public interface for these functions which are used in the LCDConf.c file.

#ifndef SSD1306_DRIVER_H
#define SSD1306_DRIVER_H
#include "cyhal.h"
void SSD1306DriverInit(cyhal_i2c_t *obj,uint8_t oledAddress);
void          SSD1306_WriteCommandByte(unsigned char c);
void          SSD1306_WriteDataByte(unsigned char c);
void          SSD1306_WriteDataStream(unsigned char * pData, int NumBytes);
void          SSD1306_ReadDataStream(unsigned char * pData, int NumBytes);
#endif

The first part of SSD1306Driver.c makes a pointer to and Cypress HAL I2C object.  In the initialization code, it connects the provided I2C object and the static pointer.  The purpose of this is to allow the application developer to use the I2C for other devices on the bus, in other words the I2C bus is shared.  On most of my screens the I2C address is 0x3C,  so I let the user not provide an I2C address.  Probably in hindsight I should have just made them always provide an I2C address.

#include "SSD1306Driver.h"
#include "GUI.h"
#include "cyhal.h"
#include "cybsp.h"
#include <stdlib.h>
/*********************************************************************
*
*       Defines: Configuration
*
**********************************************************************
Needs to be adapted to custom hardware.
*/
/* I2C port to communicate with the OLED display controller */
static cyhal_i2c_t *I2C=0;
/* I2C slave address, Command and Data byte prefixes for the display controller */
#define OLED_CONTROL_BYTE_CMD       (0x00)
#define OLED_CONTROL_BYTE_DATA      (0x40)
static uint8_t OLED_I2C_ADDRESS     =    (0x3C);
void SSD1306DriverInit(cyhal_i2c_t *obj,uint8_t oledAddress)
{
CY_ASSERT(obj);
I2C=obj;
if(oledAddress)
OLED_I2C_ADDRESS = oledAddress;
}

In order for emWin to update a display it needs to be able to write data to the display via “commands” and “data” writes.  However, it doesn’t know anything about the fact that these displays are typically attached via I2C.  The function SSD1306_WriteCommandByte uses the Cypress HAL master write API to send a command byte to the display.  This function is called by emWin via the configuration in LCDConf.c

void SSD1306_WriteCommandByte(unsigned char c)
{
uint8_t buff[2];
/* The first byte of the buffer tells the display that the following byte
is a command */
buff[0] = OLED_CONTROL_BYTE_CMD;
buff[1] = (char)c;
/* Write the buffer to display controller */
cyhal_i2c_master_write(I2C, OLED_I2C_ADDRESS, buff, 2, 0, true);
}

And the write data byte function works exactly the same way as the write command byte, except it send a different first byte.

void SSD1306_WriteDataByte(unsigned char c)
{
uint8_t buff[2];
/* First byte of the buffer tells the display controller that the following byte
is a data byte */
buff[0] = OLED_CONTROL_BYTE_DATA;
buff[1] = c;
/* Write the buffer to display controller */
cyhal_i2c_master_write(I2C, OLED_I2C_ADDRESS, buff, 2, 0, true);
}

emWin Configuration Files

I have written extensively about how to configure emWin and specifically how to setup the files for the SSD1306.  You can see the articles here.  So, I am not going to describe those files in detail except to say that the ones that you need are GUIConf.h/.c, LCDConf.h/.c and GUI_X_CYRTOS.c.  The only changes from the previous configurations of LCDConf.c is to hookup the APIs we defined in the previous section.

    PortAPI.pfWrite8_A0  = SSD1306_WriteCommandByte;
PortAPI.pfWrite8_A1  = SSD1306_WriteDataByte;
PortAPI.pfWriteM8_A1 = SSD1306_WriteDataStream;

GitHub

I checked in all of these files into GitHub at git@github.com:iotexpert/p6sdk-ssd1306-emwin-cyrtos-config.git  Now they can be used as a Modus Toolbox library.

Test Library

Lets build a test project.  Start by creating  new project.  Notice that I am not using the built in project creator, but a standalone project creator which does not require Eclipse.  This is automatically installed for you as part of the Modus Toolbox installation (so, if you are one of the legions of people who don’t like Eclipse you don’t have to use it).  Run it from the start menu.

For this demo I will use the CY8CKIT-062-WiFi-Bt kit… but this works on all of of our PSoC 6 kits with Arduino headers.

I start using the IoT Expert FreeRTOS template.   And I store the project in the directory “~/iotexpert-projects/xxx”.  A long time ago I started using directories named “xxx” to mean that I can “rm -rf xxx” and I never store anything that matters in those files/directories.  Give the project a name then press next.

Now you have everything setup.  So click “Create”

And our software will do its thing.

Now if I look in the directory, I will have a complete project.

Now lets manually add the .lib for the p6sdk-ssd1306-emWin-cyrtos-config.  You can use whatever editor you want, but it’s emacs for me.

Enter the URL for the library.  Then put a “/”.  Then a branch.

Now that the file is updated you can run “make getlibs” which will search for all of the “.lib” files.  Then make sure those libraries are part of your project.

After the make getlibs is run you can see that the p6sdk-ssd1306-emWin-cyrtos-config directory is in your libs directory, with all of the stuff you need.

The next step is to edit your main.  I like to use vscode.  If you run  “make vscode” our build system will create a vscode project for you.  You can start vscode by running “code .”

Before the emWin library work you will need to add the EMWIN_OSNTS and FREERTOS components to your Makefile.

COMPONENTS=EMWIN_OSNTS FREERTOS

Here is what it looks like in vscode

Now, write the your main.c code to display a little message.

#include "cybsp.h"
#include "GUI.h"
#include "SSD1306Driver.h"
int main(void)
{
/* Set up the device based on configurator selections */
cybsp_init();
cyhal_i2c_t I2C;
/* Configuration to initialize the I2C block */
static cyhal_i2c_cfg_t i2c_config = {
.is_slave = false,
.frequencyhal_hz = 400000
};
cyhal_i2c_init(&I2C, CYBSP_I2C_SDA, CYBSP_I2C_SCL, NULL);
cyhal_i2c_configure(&I2C, &i2c_config);
SSD1306DriverInit(&I2C,0x3C);
GUI_Init();
GUI_SetColor(GUI_WHITE);
GUI_SetBkColor(GUI_BLACK);
GUI_SetFont(GUI_FONT_8_ASCII);
GUI_SetTextAlign(GUI_TA_CENTER);
GUI_DispStringAt("Hello World", GUI_GetScreenSizeX()/2,GUI_GetScreenSizeY()/2 - GUI_GetFontSizeY()/2);
}

You can build your project by running “Build” from vscode or your can built it on the command line with “make build”

After building you should have output like this:

You can program by Pressing selecting or by pressing F5 or selecting “Run->Start Debugging”

Now you can press the play button and you are off to the races

Or on the command line you can program with “make program”

OK.  Looks like the library works!

Updating the IoT Expert MTB2 Manifests

I showed you how to manually add a library to your project.  But what if you want to have the library you built show up in the Library manager?  In the picture below you can see that is exactly what I did by adding a new category called “iotexpert”

To make this work you need a “Super Manifest” which is just a XML file that contains URLs references to your custom Application Templates manifest file(s), Middleware manifests file(s), and BSP manifest file(s). A “manifest” file is just an XML file with a list of URLs to Git Repos and some meta data.

In words you need:

  1. A file in your home directory called ~/.modustoolbox/manifest.loc which contains a URL for your cusom super manifest file
  2. A super manifest file that optionally contains a references to your application templates manifest file(s)
  3. And/Or contains a reference to your middleware manifest file(s)
  4. And/Or contains a reference to your board support manifest(s)

I start by creating the manifest.loc file to refer to a specific file on GitHub. “https://github.com/iotexpert/mtb2-iotexpert-manifests/raw/master/iotexpert-super-manifest.xml”.  Notice that it is stored in “.modustoolbo” which is a directory that starts with a “.” which is a PITA on Windows.  The only way I know how to edit/create this is by using the “modus shell” (which we provided as part of the Modus Toolbox installation.  Here is a screenshot from my Mac.  Notice that I use the GitHub URL mechanism to get to the “raw” file.

In my SuperManifest file I create references to the

  • board-manifest-list (of which I have none)
  • app-manifest-list which links to my application template manifest
  • middleware-manifest which links to my middleware manifest file

Notice that both of these files are on the same GitHub repository.

<super-manifest>
<board-manifest-list>
</board-manifest-list>
<app-manifest-list>
<app-manifest>
<uri>https://github.com/iotexpert/mtb2-iotexpert-manifests/raw/master/iotexpert-app-manifest.xml</uri>
</app-manifest>
</app-manifest-list>
<board-manifest-list>
</board-manifest-list>
<middleware-manifest-list>
<middleware-manifest>
<uri>https://github.com/iotexpert/mtb2-iotexpert-manifests/raw/master/iotexpert-mw-manifest.xml</uri>
</middleware-manifest>
</middleware-manifest-list>
</super-manifest>

Then if you look at the actual middleware manifest you will see that I have two (currently) middleware repositories

  • (The NTSHell) https://github.com/iotexpert/middleware-ntshell
  • (The SSD1306 Middleware in this article) https://github.com/iotexpert/p6sdk-ssd1306-emWin-cyrtos-config

The rest of the XML is meta data which specifies how the middleware is presented by the library manager.

<middleware>
<middleware>
<name>ntshell</name>
<id>ntshell</id>
<uri>https://github.com/iotexpert/middleware-ntshell</uri>
<desc>NT Shell</desc>
<category>iotexpert</category>
<req_capabilities>psoc6</req_capabilities>
<versions>
<version>
<num>Latest 1.X release</num>
<commit>master</commit>
<desc>Latest 1.X release</desc>
</version>
</versions>
</middleware>
<middleware>
<name>SSD1306-emwin-cyrtos-config</name>
<id>SSD1306-emwin-cyrtos-config</id>
<uri>https://github.com/iotexpert/p6sdk-ssd1306-emwin-cyrtos-config</uri>
<desc>SSD1306-emwin-cyrtos-config</desc>
<category>iotexpert</category>
<req_capabilities>psoc6</req_capabilities>
<versions>
<version>
<num>Latest 1.X release</num>
<commit>master</commit>
<desc>Latest 1.X release</desc>
</version>
</versions>
</middleware>
</middleware>

You can see that my GitHub repository contains

  1. iotexpert-super-manifest.xml  – amazingly enough the super manifest
  2. iotexpert-mw-manifest.xml – my middleware manifest file
  3. iotexpert-app-manifest.xml – my application template manifest file

Now if you look at the bottom of the library manager you will see that when it starts up it read the Cypress Super Manifest file, as well as the IoT Expert Super Manifest.

To be clear.  If you add my “manifest.loc” file to your ~/.modustoolbox directory you will access to all of my libraries.

Cypress Type-C Barrel Connector Replacement + Infineon Buck DC/DC (Part 3)

Summary

This article walks you through the steps to test the CY4533 Type-C BCR & IR3894 under the load conditions from 1A to 12A.  In the previous article, I supplied power to the IR3894 using a bench top power supply.  For this set of experiments I will use a Type-C wall wart connected to the Cypress 4533 BCR development kit to supply power.

Test the BCR

The first thing that I do is connect the whole mess together like this:

Here is how it looks on my desk.  Note that the Keithey can measure current and voltage… but that I don’t have a way in this setup to measure either the voltage/current from the power supply or the current out of the CY4533

I step the output load from 1A to 12A in 1A increments.  I am super happy to see that the output voltage of the IR3894 is perfectly regulated to 1.198V.  It is also interesting to see that the Type-C power supply is able to keep the voltage within 3.25% of nominal even when I am using 12A on the IRDC3894 output (probably around 1.5A from the Type-C)

Measure the Input Current

In the previous article I used the current measurement from the Keithley bench top power supply.  In the setup above I don’t have a way to measure the actual input current.  To fix this put my new Keithley DAQ6510 in series with the IRDC3894 board.  Like this:

Then I step through the 1A-12A load conditions.  Once again the IR3894 provide a very well regulated voltage and current (exactly the same as before so I didn’t write them down)

Here is a table with the data from the previous post (without the Type-C power supply) versus the Type-C power supply.

2230-30-1 Power Supply With 6510 current meter in input path
Vin Iin Win Vout Iout Wout Eff Vin Iin Win Eff-C Loss
12 0.27 3.24 1.198 0 0 0%
12 0.129 1.548 1.198 0.998 1.195604 77% 11.91 0.129 1.53639 77.8% -0.6%
12 0.239 2.868 1.198 1.998 2.393604 83% 11.8 0.242 2.8556 83.8% -0.4%
12 0.345 4.14 1.198 2.998 3.591604 87% 11.7 0.352 4.1184 87.2% -0.5%
12 0.454 5.448 1.198 3.998 4.789604 88% 11.59 0.467 5.41253 88.5% -0.6%
12 0.564 6.768 1.198 4.999 5.988802 88% 11.47 0.586 6.72142 89.1% -0.6%
12 0.677 8.124 1.198 5.998 7.185604 88% 11.36 0.709 8.05424 89.2% -0.8%
12 0.792 9.504 1.198 6.998 8.383604 88% 11.25 0.837 9.41625 89.0% -0.8%
12 0.909 10.908 1.198 7.998 9.581604 88% 11.13 0.97 10.7961 88.8% -0.9%
12 1.029 12.348 1.198 8.998 10.779604 87% 10.95 1.115 12.20925 88.3% -1.0%
12 1.152 13.824 1.198 9.999 11.978802 87% 10.85 1.258 13.6493 87.8% -1.1%
12 1.277 15.324 1.198 10.998 13.175604 86% 10.8 1.401 15.1308 87.1% -1.1%
12 1.406 16.872 1.198 11.997 14.372406 85% 10.68 1.558 16.63944 86.4% -1.2%

These measurements use 1A/3A range on the Keithley DAQ6510 DMM, which means that they have a 100mΩ shunt resistor in series which drops the voltage by V=IR or about 0.1-ish volts.  This explains most of the difference from the Power Supply to the Type-C setup.

It is actually very interesting to look at the data to see the impact of lowering the input voltage on the efficiency of the IR3894.  It appears that at the highest load and lowest input voltage the efficiency is down by 1.2%

Watch the Sunrise

While I was sitting there at my desk thinking about what to do next, I decided that the best thing to do was go sit in my hottub and watch the sunrise on God’s country.

USB C Power Meter Tester

I was hoping to be able to measure the input current and voltage from the Type-C power supply so that I could calculate the efficiency of the CY4533 EZ BCR.  And as a result the efficiency of the whole system.  There wasn’t a place on the Type-C development kit to make these measurements, but the Cypress Apps manager for Type-C – Palani – said I should buy something like this from Amazon.

 

So I did.  You can plug it into Type-A or Type-C and it will tell you how much V/I are coming out.  In the picture below you can see 20.4v@0.11A

Even better it has a handy-dandy mode where it can display Chinese?

Here is a picture in my actual setup:

And a picture of the whole crazy setup.

Now I step through my 12 load conditions from 1A to 12A and record the V/I from the Fluke and the USB Power Tester.

Here is the data in table form with power and efficiency added.

Type C Power Tester
Vin Iin Win Eff-No Meter
11.99 0.15 1.7985 66.5%
11.95 0.26 3.107 77.0%
11.92 0.36 4.2912 83.7%
11.88 0.48 5.7024 84.0%
11.85 0.59 6.9915 85.7%
11.82 0.7 8.274 86.8%
11.79 0.82 9.6678 86.7%
11.75 0.94 11.045 86.8%
11.71 1.07 12.5297 86.0%
11.68 1.2 14.016 85.5%
11.64 1.33 15.4812 85.1%
11.6 1.46 16.936 84.9%

Next, I plot the new data with the previous two plots.  Obviously, it is screwed up.  I would bet money that the data points at 2A, 4A and 12A are wrong.  But, I don’t think that it is worthwhile to take steps to figure out the real current.  So, I suppose that is what you get from a $19 power meter.

Efficiency of CY4533 EZ-PD BCR

I had really wanted to measure the efficiency of the BCR setup.  To do that I needed to be able to measure the output power (V/I) and the input power (V/I).  Unfortunately the power meter doesn’t seem to be very good… so I suppose that I will have to wait to build my real board where I can install some power jumpers the real numbers.