Using Python and a PLIST to Get pH and EC Values for a Plant into an iOS Swift App

Tags

, , , ,

The EC and pH level of a nutrient bath will be different depending on the plant type.  The Ladybug client software – at least initially an iOS app – gets the plant type from the user and then uses this info to determine the best pH level and nutrient level for the nutrient bath.  This way, the user doesn’t have to constantly look up what the pH and EC values should be.  For example, according to this table, a basil plant does best when the pH is between 5.5 and 6.5 and the EC is between 1.0-1.6mS.  This table says a tomato plant does best when the pH is between 5.5-6.5 and the EC is between 2.0-5.0.  I want to have a table/data base of plant types with the pH and EC values I need to set the nutrient bath.

The Goal

The goal of this post is to build a workflow that scrapes pH and EC values for herbs and vegetables from web sites and create a plist that can be used within an iOS app as an easy to use dictionary.  

Open Source

The files I used include:

Design Choices

  • plists or Core Data:  iOS has many ways an app can read/write a table of data.  I narrowed the choice to using either Core Data or plists.  I chose plists because the table of pH and EC values will most likely be less than 100 (although I assume I’ll be adding other plants over time) and the lookup is very simple – given a plant type, what should the pH and EC values of the nutrient bath be at?
  • Tool to scrape web pages and create plist: I chose to use the Python that is installed on my Mac’s OS X – Python 2.7 – and PyDev – the Python Eclipse plug-in.  The BeautifulSoup package makes it extremely easy to scrape web pages.

Code

Python

Scrape_pH_and_EC_into_plist.py (located at this GitHub location) scrapes:

for the pH and EC values of vegetable and herbs.  There are many sources for pH and EC values.  I am not sure which has the “best” values so I am starting with the values on these pages.  I use these because they come up near the top of Google searches for vegetable/herb pH/EC values.
 
The pH and EC values are text ranges like 5.5-6.5.  Python made it easy for me to parse these into the two numbers: 5.5 and 6.5, take the average and use that value as the “best” value for (in this case) the pH value of 6.0.
 
The plant name as well as the pH and EC values are written out into the file named pH_and_EC_values.plist .

Swift

I’m writing the Ladybug client in Swift.  I used a Playground to figure out the structure I wanted for the plist file as well as the Swift code I will use to access the pH and EC values.  I ended up deciding on a structure for entries within the plist to be:

<key>Basil</key>
     <dict>
          <key>pH</key>
          <string>6.0</string>
         <key>EC</key>
        <string>1.3</string>
     </dict>

I put a copy of the plist that is created from Scrape_pH_and_EC_into_plist.py at this GitHub location.

Here is the code I wrote in the playground to test accessing the entries in the plist:

//: Playground – noun: a place where people can play

 

import Foundation

if let path = NSBundle.mainBundle().pathForResource(“pH_and_EC_values”, ofType: “plist”) {

    print (“Path to plist: \(path)”)

    let fileManager = (NSFileManager .defaultManager())

    if fileManager.fileExistsAtPath(path) {

        if let plants = NSDictionary(contentsOfFile: path) {

            for key in plants.allKeys as! [String] {

                let plant = plants[key]

                print(“plant: \(key) pH: \(plant![“pH”]) | EC: \(plant![“EC”])”)

            }

        }

    }

}

In order to use a plist within a Playground, the plist file must be added to the Playground’s Resources folder. For example, I added a Resources folder within the Playground file I used (located at this GitHub location):

 

 

Debugging in a Playground with XCode 7

There seems to be some tweaks to displaying results in XCode 7. The tweaks confused me so I thought I’d document them here.  This will probably change in an upcoming release.

I ran across some settings that needed tweaking before the debugging experience was adequate.  Prior to changing settings, I would get very little info on what was going on.  For example: 

 

In the above example which I pulled from a Stackoverflow posting, notice how debugging just shows there are 4 items.  Viewing details just shows the last item.  To fix this, I had to hover over the popup that shows results and right click to change the view from “Latest Value” to “Value History”.

 

The other thing to do is to show the debug area by clicking the button on the far left:

 

 That’s it for now.  Thank you for reading this far.   Please find many things to smile about. 

Code Review and Error Handling: Ladybug Lite Blue

Tags

, , ,

Update (1/2-/2-16) Nordic just posted a useful article on nRF51 SDK error codes here.

I just finished a code review with Ron.  I wanted to capture a few of the recommendations Ron made.

The Goal

The goal of this post is to capture what I consider the biggest changes I will make to the existing Ladybug Lite Blue firmware as well as future coding based on Ron’s feedback.

Thanks To Those That Went Before

I was thrilled to have 1:1 time with Ron for a code review as an outcome of the Contextual Electronics course.  What a terrific opportunity to evolve my firmware programming abilities.  Ron did not disappoint. 

Open Source

The Ladybug firmware I am writing about in this blog post can be found at this github location.  Doxygen documentation is included within the Eclipse project’s Documentation/html/Search folder (see index.html).

I’d be grateful for feedback on how to improve the code. 

#define versus static const

My “NEW! IMPROVED! …Rule of Thumb:”

  • Use #define for magic numbers that are global in scope.  Use static const for variables that span functions within a .c file because:
    • #define is global in scope – which means there is a greater chance for conflicting use.  I might use a #define to name a time out period to 10 (#define TIMEOUT 10) and then use the same name within another .c file with a time out period of 20 (#define TIMEOUT 20).  Using the “static” keyword keeps the variable within the scope of the .c file.
    • static const variables are strongly typed.  I’m telling the compiler the exact data type to use for the variable.
    • the code is more readable.
Example of a magic number that is global in scope from the nRF51 SDK (ble_gap.h):

/**@brief GAP device name maximum length. */

#define BLE_GAP_DEVNAME_MAX_LEN           31

 Example where I should have used static const.  In the Ladybug_Hydro.c file I had:

/**

 * \brief mapping the FET pins to the schematic

 */

#define EC_VIN_FET0

#define EC_VOUT_FET7

I changed these to:

/**

 * \brief mapping the FET pins to the schematic

 */

static uint32_t m_EC_VIN_FET=0;

static uint32_t m_EC_VOUT_FET=7;

  • avoid making any changes to any bit of the code of an external SDK (like Nordic’s).  For example, I started changing stuff like:

#define APP_ADV_TIMEOUT_IN_SECONDS      0

to:

static uint32_t const      m_app_adv_timeout_in_seconds = 0;

I would have preferred to use a static const instead of #define however the SDK requires a precompiled value since it is used within another #define.
Note: I name variables m_<variable name> to identify variables that are scoped to a .c file (versus local to a function).  

Error Handling

I like the way the Nordic SDK supports error handling.  My code as well as SDK code are peppered with:

APP_ERROR_HANDLER(err_code);

and

APP_ERROR_CHECK(err_code);

both are #define’d in the app_error.h file of the nRF51 SDK.  These resolve into calling error routines I defined within main.c:

/**@brief Callback function for asserts in the SoftDevice.

 *

 * @details This function will be called in case of an assert in the SoftDevice.

 *

 * @warning This handler is an example only and does not fit a final product. You need to analyze

 *          how your product is supposed to react in case of Assert.

 * @warning On assert from the SoftDevice, the system can only recover on reset.

 *

 * @param[in] line_num   Line number of the failing ASSERT call.

 * @param[in] file_name  File name of the failing ASSERT call.

 */

void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)

{

  app_error_handler(DEAD_BEEF, line_num, p_file_name);

}

/**@brief Function for error handling, which is called when an error has occurred.

 *

 * @warning This handler is an example only and does not fit a final product. You need to analyze

 *          how your product is supposed to react in case of error.

 * \note I decided to use the SEGGER_RTT printing functionality.  My understanding is the SEGGER_RTT calls can be left in code with no effect

 * when there is no terminal to output.  The con of this approach is I can’t hook up a UART enabled terminal session and see what is going on without the debugger present.

 *

 * @param[in] error_code  Error code supplied to the handler.

 * @param[in] line_num    Line number where the handler is called.

 * @param[in] p_file_name Pointer to the file name.

 */

void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name)

{

  // This call can be used for debug purposes during application development.

  // The SEGGER_RTT APIs will not cause a problem in production so I’m leaving them in.

  SEGGER_RTT_WriteString(0,“—>>>BUMMER!! In app_error_handler\n”);

  SEGGER_RTT_printf(0,“error code: %d (or 0X%X if assert..base for BLE = 0x3000) Line number: %d file name: “,error_code,error_code,line_num);

  SEGGER_RTT_WriteString(0,p_file_name);

  SEGGER_RTT_WriteString(0,“\n”);

  //  ble_debug_assert_handler(error_code, line_num, p_file_name);

}

For now, error checking ends up printing out information about what/where the error occurred.  This has led to quickly figuring out where the error is and an idea of why it is happening.  In the future I could see mapping these error routines to LED indicators.  I use the SEGGER_RTT APIs because they are robust and work when the code is running in both run time and debug mode. Contrast this version of printf to the serial port to what I discussed in this post.

Checking for NULL Pointers

The basic point here is to check input values of called functions.  This is simple stuff that I should just do. For example, I was not checking for NULL pointer input to functions.  For example, I would pass in  a pointer to an int16_t array:

static void get_EC_reading(int16_t *p_EC) {

and assume p_EC pointed to a valid address.  I added in a simple check:

  if (p_EC == NULL){  //Shouldn’t be passing in a null pointer given the EC Vin and Vout values are planned to be stored at this memory location.

      APP_ERROR_HANDLER(LADYBUG_ERROR_NULL_POINTER);

  }

I also added error codes unique to the Ladybug to the Ladybug_Error.h file. 

Implement Time Outs

When using the pstorage APIs to access the nRF51822’s flash, the Nordic examples use code that has the potential to hang the system if feedback from the pstorage is not given to the pstorage handler in my code.  The pstorage APIs manage access to flash when the BLE stack is running.  This allows the BLE stack to figure out when it is safe to read from and (more difficult!) write to flash.  The function ladybug_Flash_Init() in the Ladybug_Flash.c file registers the ladybug_Flash_handler() function to be called back when a flash activity has completed.  The ladybug_flash_read() and ladybug_flash_write() functions rely on the static variable m_mypstorage_wait_flag to let them know when the flash action has been completed.  These functions wait until completion.  If the ladybug_Flash_Handler() is never called, the program will hang without having an app timer set up to fire after an amount of time.  I discuss using the nRF51 SDK’s app timers in this post.  Here is the code I use to surround a flash action.  In this case within ladybug_flash_write():

 // Start the timer up again to timeout if writing to flash doesn’t happen

  start_timer();

  m_mypstorage_wait_flag = 1;

  err_code = pstorage_store(p_handle, p_bytes_to_write, num_bytes_to_write, 0);

  while(m_mypstorage_wait_flag) {  }

  APP_ERROR_CHECK(err_code);

  stop_timer();

If the timer fires, the caller’s call back function is invoked.  This returns an error code to the caller.  It is then up to the caller to act on the error code.  Here is the definition of ladybug_flash_write() showing how the call back function did_flash_action() is included within the call to ladybug_flash_write():

void ladybug_flash_write(flash_rw_t what_data_to_write, uint8_t *p_bytes_to_write,pstorage_size_t num_bytes_to_write,void(*did_flash_action)(uint32_t err_code));

I call ladybug_flash_write() in main.c.  Thus main.c also include the call back function:

/**

 * \callgraph

 * \brief call back from Ladybug_Flash.c to let us know if the flash write was successful (or not)

 * @param err_code          0 if successful

 */

void did_flash_write(uint32_t err_code) {

  if (err_code == 0) {

      SEGGER_RTT_WriteString(0,“…Flash write SUCCESS!”);

  }

  else {

      APP_ERROR_HANDLER(err_code);

  }

}

That’s it for now.   Thank you for reading this far.  Please find many things to smile about.

Ladybug Lite Blue – A Tale of Two Power Sources

After using the Ladybug Lite Blue in my nutrient baths, I’ve decided to change the hardware design from just using a coin cell battery to using a DC power source when available and switching to a coin cell battery when not available.  At some point I envision designing an outdoor version that switches between solar power and battery so I am excited to explore using multiple power sources.

The Goal

The goal of this post is to add switching between a DC and battery power source to the Ladybug Lite Blue Schematic and board layout.

Note: I will be using the acronym LBL to refer to the Ladybug Lite Blue hardware.

Thanks to Those that went Before

Thank you (as always) to  Chris Gammell of Contextual Electronics!  I came up with a design to switch between the two power sources using an op amp and a p-channel mosfet.  I showed my design to Chris who without hesitation – and I might add a lot of respect/kindness! – steered me to a much better design using Schottky diodes – which I discuss below.  In fact, Chris pointed out he had covered this a month or so ago in our Contextual Electronics course!  Shame on me for needing Chris’s nudge when he had already provided the “better” solution.  There is so much Chris is teaching us that I need to go back and figure out what else I have missed.

Thank you Afrotechmods for your excellent video on the Schottky diode.

Open Source

Kicad v4.01 files and LTSpice simulation that is discussed below can be downloaded from this github location.

The Solution

  • use a commonly available power source for DC power: USB and CR2032 3V coin cell battery.
  • use Schottky diodes along with DC power being at a higher voltage than battery to use DC power when plugged in.  

LTSpice

The LTSpice simulation is simple.  I defined a DC and battery power source.  The DC power source pulses 3.3 volts on and off.  This allows the voltage graph to show the voltage at the load when DC power is on as well as off.

Features of the Design

  • When both power sources are available, current flows through D1 (i2) and through D2 (i1).
  • The voltage rises to 3.3V – the maximum DC voltage, which is .3V more than the maximum battery voltage of 3V.
  • Diode D1 is getting a reverse current.  At this point, power is being driven by the DC source.

Benefits of Schottky Diodes

Features of a Schottky diode that makes it a good fit for this scenario include:
  • A diode’s forward voltage drop is ~ .7V.  The Schottky diode has ~ .2V forward voltage drop (this depends on variables like the amount of current).
  • Switching from allowing current to flow to not allowing current to flow happens instantly.

Schematic and Board Layout

The Kicad v4.01 schematic and board layout are available at this github location.

 

 

 

A short one.   Thank you for reading this far.  Please find many things to smile about.

Source Line Debugging FRDM-KL25Z Firmware

Tags

, , , ,

In my previous post, I started out on a journey to program the FRDM-KL25Z.  I documented how I:

  • got OpenOCD talking to the FRDM-KL25Z
  • integrated OpenOCD with Eclipse
  • “got to blinky” by loading and running a binary on the FRDM-KL25Z and
  • installed the mbed library

The Goal

The goal of this post is to step through a simple C program running on the FRDM-KL25Z from within Eclipse through the OpenOCD interface to the GDB debugger.

Thanks To Those That Went Before

A special shout out to the OpenOCD mailing list (openocd-user@lists.sourceforge.net)  It amazes me that such gifted folks as Paul and Tim are there to answer my questions!  So far I have been impressed with the quality of people and their passion behind the OpenOCD effort.

  • Paul Ferster – through the OpenOCD mailing list –  yet again came to my rescue when I could not get the GDB debugger to connect to the FRDM-KL25Z.  THANK YOU.
  • Tim Wescott provided me with insights on interacting with OpenOCD as well his thoughts on documentation.  I agree with his thoughts. The one thing I might consider for the OpenOCD effort is to move from a mailing list to StackExchange/StackOverflow (I am not sure about the difference). It is my experience as a passionate learner of electronics/programming that we’ve moved from RTFM learning to asking peers via a search that lands us on StackExchange/StackOverflow.  In doing so, the community becomes the documentation.  A mailing list to me is more about a few experts answering questions.  Which doesn’t scale as well because these poor experts are taken away from improving the code base to answer questions from clueless folks (um…like me :-) ).  THANK YOU Tim.
  • Ron Sousa  of HDE for providing guidance and direction as I sorted out what he is teaching us within the embedded systems section of Contextual Electronics.

The Code

Ron has us using the FRDM_SERIAL example project that is available on mbed’s developers web site.  As I searched, I stumbled across this post which walked me through the steps to download/prepare the FRDM_SERIAL example from mbed’s developer site.  I found the post easy to follow.  Please check it out. 

 The post uses the FRDM-K64F.  But as the image above shows it is easy to select the FRDM-KL25Z.  Selecting a platform is also a simple way to see which mbed boards are supported the most.

I found this online method of getting familiar with mbed programming/building/debugging/running code on the FRDM-KL25Z to be simple and unique.  My knowledge level about mbed in the previous post assumed I would need to download the mbed sdk and create an mbed library.  And I did this.  The mbed library created running the python scripts created the mbed library: /Applications/mbed/build/mbed/TARGET_KL25Z/TOOLCHAIN_GCC_ARM/libmbed.a

The mbed library build steps I went through that were documented in this post were not necessary.  Starting off with an Exported project from the mbed developer web site brings in the libmbed.a linked for the specified chip, in my case the FRDM-KL25Z.  However, running the tests using make.py was very useful because it helped me confidently know mbed was working with the FRDM-KL25Z and familiarized me with sending/receiving data from the FRDM-KL25Z to my Mac.  As in the journey is the reward :-).  Or what I’m finding – the journey is where I actually learn stuff.

FRDM-SERIAL Project

FRDM_SERIAL is a simple mbed app.  As pointed out in the post: “You’ll see it doesn’t do much , it makes a serial connection over the USB port using Serial pc(USBTX, USBRX);it sends “Hello World” to the pcit then loops toggling the LED and sending  the loop counter to the pc.”  Here’s the code:

#include“mbed.h”

 

DigitalOut myled(LED_GREEN);

Serial pc(USBTX, USBRX);

 

int main()

{

    int i = 0;

    pc.printf(“Hello World!\n”);

 

    while (true) {

        wait(0.5f); // wait a small period of time

        pc.printf(“%d \n”, i); // print the value of variable i

        i++; // increment the variable

        myled = !myled; // toggle a led

    }

}

Prior to unzipping the FRDM_SERIAL bundle that I exported from mbed’s compiler web site, I had created an Eclipse workspace.  I briefly discussed Eclipse workspaces in this post.  I then unzipped the bundle and copied the files within a subdirectory of the workspace.

The project includes a makefile.  The makefile means we can build within Eclipse with all the dependencies and variable definitions set.  If I was to grow the project with additional code/libraries, I would consider moving the makefile into a project with a managed make file as I did for the nRF51822 projects.  Managed make is – as the name implies :-) – easier to manage within Eclipse.

Source Line Debugging

I’ve got my project set up in Eclipse with the Eclipse settings I discussed in the previous post.  Source line debugging involves integrating the latest stable version of OpenOCD within Eclipse.  I’ve got the openOCD environment variables set up:

 The debug configuration needs to be set up:

Config Options

All should be filled out except for the all important Config options.  Make sure to have a path to the KL25Z.cfg file.  Note: I’ve seen different config files on the net for the KL25Z.cfg.  It is best to stick to the one that comes with the “official” OpenOCD software.

Explained in the OpenOCD Project Setup documentation: “To use OpenOCD with your development projects, you need to do more than just connect the JTAG adapter hardware (dongle) to your development board and start the OpenOCD server. You also need to configure your OpenOCD server so that it knows about your adapter and board, and helps your work. You may also want to connect OpenOCD to GDB, possibly using Eclipse or some other GUI.”

-f <path to KL25Z.cfg>

As explained in the OpenOCD documentation under configuration basics, passing in the correct config file is super important.  

-c “init;reset halt” 

When I didn’t give these additional options, I would get the following from the gdb server:

GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716 (2015-05-19-12:55)

Licensed under GNU GPL v2

For bug reports, read

http://openocd.org/doc/doxygen/bugs.html

Info : only one transport option; autoselect ‘swd’

srst_only separate srst_gates_jtag srst_open_drain connect_deassert_srst

Info : add flash_bank kinetis kl25.flash

adapter speed: 1000 kHz

srst_only separate srst_nogate srst_open_drain connect_deassert_srst

cortex_m reset_config sysresetreq

Started by GNU ARM Eclipse

Info : CMSIS-DAP: SWD  Supported

Info : CMSIS-DAP: Interface Initialised (SWD)

Info : CMSIS-DAP: FW Version = 1.0

Info : SWCLK/TCK = 0 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1

Info : CMSIS-DAP: Interface ready

Info : clock speed 1000 kHz

Info : SWD IDCODE 0x0bc11477

Info : kl25.cpu: hardware has 2 breakpoints, 2 watchpoints

Info : MDM: Chip is unsecured. Continuing.

Info : accepting ‘gdb’ connection on tcp/3333

Warn : Cannot communicate… target not halted.

Error: auto_probe failed

Error: Connect failed. Consider setting up a gdb-attach event for the target to prepare target for GDB connect, or use ‘gdb_memory_map disable’.

Error: attempted ‘gdb’ connection rejected

The GDB Server finds the OpenOCD service running on the FRDM-KL25Z but can’t connect to it.

The “helpful hints” (i.e.: Consider setting…) were not helpful.  I could not debug this without asking the OpenOCD mailing list .  Like last time, this mailing list was quick to respond with the right solution.  

I plan to use this command string any time I set up a debug configuration for OpenOCD.

Once the command was included, The YIPPEE! moment occurred.  I was able to walk through the code as well as see the output within a CoolTerm session (I discussed using CoolTerm in the previous post):

 

 

 Thanks for reading this far.  Please find many things to smile about. 

 

Playing with the FRDM-KL25Z – Getting to Blinky

Tags

, ,

UPDATE (12/28/2015): Erich cleared up my confusion on what ARM processor (cortex-m0+ or cortex-m4) was used in the KL25Z.  From Erich: The KL2x (note the L) has an ARM Cortex M0+, while the K2x is indeed having a M4 core…. this post has been updated to take note of this difference.  Yet again – THANK YOU Erich!

Ron is mentoring us through embedded systems programming on using a Freedom board.  I’m excited about the opportunity to compare/contrast with what I am learning about the nRF51822.

I am using a FRDM-KL25Z board.  

The Goal

The goals of this post include:

  • Installation and verification of OpenOCD with my FRDM-KL25Z board.
  • Setting upEclipse/OpenOCD.
  • Installing the mbed SDK and running test binaries built using the mbed SDK on the FRDM-KL25Z board.

Thanks to Those That Went Before

Paul Ferster

A HUGE thank you to Paul Fertser.  While I am not exactly sure what Paul’s role is in the OpenOCD community, Paul went out of his way helping me debug an OpenOCD install on Ubuntu. We use Ubuntu in Contextual Electronics because it provides a common environment.  I got introduced to Paul after signing up for the OpenOCD mailing list.  I was desperate to figure out why OpenOCD wouldn’t run on my Ubuntu VM.  It couldn’t have been more than two minutes before Paul sent me a reply.  I’m clueless when it comes to *nix commands and had no idea what  was doing with OpenOCD.  It was the holidays, and I wanted to catch up to Ron’s Contextual Electronics videos.  After much debugging, Paul suggested we get the problem figured out on the #openOCD IRC chat channel.  To further show my ignorance, at first I had no idea what he was asking since I’ve used IRC maybe twice and immediately thought of Twitter when he put a # before a word (sad I know :-) ).  The problem ultimately turned out to be an old version of OpenOCD.  But it was more confusing than that.  I found three different versions of OpenOCD on the machine!  The Ubuntu version was outdated…and on…and…I learned A LOT about OpenOCD from Paul.  But more importantly, I met someone who kindly helped me, was very gifted in programming and all things *nix and OpenOCD, and was extremely passionate to get the OpenOCD environment “right”.  When I was discussing the experience with Paul, I shared my unhappiness with the amount of time this effort took.  I pointed out that time is the most important currency.  Paul made me rethink my feelings on time – and gave me a glimpse into how time is spent – that when stuff just will not work – that chance opportunity to meet and talk with someone who has an extremely interesting perspective, is super smart, and helpful is very important.  Thank you Paul.

Note: Given the challenges I had with Ubuntu, I moved this project to Mac OS X.  

Erich Styger

Erich Styger for his excellent series of posts on his MCU On Eclipse blog.  Often Erich has written a post on the topic I am trying to learn.  This time I found this post to be very helpful.  Thank you Erich.

Ronald Sousa

Ron teaches embedded systems programming for Contextual Electronics.  I continue to learn A LOT from Ron.  Ron’s post on committing and installing OpenOCD was extremely helpful.  Thank you Ron.

Getting To Know the FRDM-KL25Z

From this page: “The FRDM-KL25Z has been designed by Freescale in collaboration with mbed for prototyping all sorts of devices…and includes a built-in USB Flash programmer.”  From that description, it appears to me like an alternative to an Arduino…a gateway into getting deeper into programming NXP µCU …with a lot of great peripherals, OpenOCD, and mbed support.  BWA-HA-HA!!!!  (As in..You WILL see the value in dropping any other µCU and buying NXP’s SOIC…BWA-HA-HA-HA-HA ).

Details on the FRDM-KL2x series of µCU can be found here.  The User’s Guide gives a nice overview.

Block Diagran

The block diagram:

 

 

From here

Highlights the KL2X’s capabilities.  I’m looking forward to getting to know the µCU better by watching these videos.

Debug Interface

Important for this post is the debug interfaces.  This pdf document (chapter 9 – debugging) pointed out the FRDM-KL25Z implements Arm’s JTAG/SWD debugging support:

 

Like the nRF51822, the FRDM-KL25Z supports Arm’s SWD interface.  YIPPEE!…this means we’ll be able to use OpenOCD software in order to do source line debugging of FRDM-KL25Z code within Eclipse.

mbed

We’ll be using mbed software to develop firmware for the board.  I’ve been curious what mbed is all about.  What I found interesting about the development environment was the focus on a simple online IDE where code is handled within a web browser.  What a clever way to get started!  No messy IDE install.  An easy way to jump start playing with a board.  It seems to me a terrific way to have a starter IDE.

The other option for firmware development is using the mbed SDK.  This is what we’ll need to use in order to use Eclipse as the IDE.

Debugging the FRDM-KL25Z Within Eclipse 

We’ll be using OpenOCD software in order to do source line debugging of FRDM-KL25Z code within Eclipse.

The Eclipse -> OpenOCD debugging path is discussed handily by Erich Styger in  his post “OpenOCD/CMSIS-DAP Debugging with Eclipse and without an IDE”:

 

Lucky for us, the FRDM-KL25Z is supported on the latest stable release of OpenOCD (0.9).  Getting (OpenOCD) debugging working between the FRDM-KL25Z and Eclipse involve the following steps:

Step 1: Firmware

  • Update the firmware on the FRDM-KL25Z.  This was challenging for me because I use a Mac. The mbed boot loader that was loaded on my board when I got it in the mail was version 1.09.  To update firmware from 1.09 to 1.11 (the latest), I had to find a Win 7 machine.  Luckily, I have a machine I’ve kept around for 6 years!  The mbed boot firmware is located here.  Updating the boot loading firmware is confusing because what I found out there is an nbed boot loader and a non-MBed boot loader.  We are using MBed for:
    • USB drag and drop programming
    • USB Virtual COM port for serial terminal
    • CMSIS-DAP interface for programming and debugging from offline tools
    • rich SDK
    I went through the instruction on Arm’s web site.  All seemed to work :-).
     
  • Download the “Hello World!” binary onto the FRDM-KL25Z.  This is incredibly easy to do.  As stated earlier, the FRDM-KL25Z was a joint effort between mbed and Freescale.  So once the firmware was updated, the board shows up as a USB device on my Mac:

It is then just a matter of downloading the HelloWorld_KL25Z.bin file onto the FRDM-KL25Z board.  Go to here for more info.  Luckily, this “just worked” for me :-).  Although I find the continual blinking of the red light to be annoying.

Onto OpenOCD and the mbed SDK….

Step 2: OpenOCD

  • Download and install the OpenOCD binaries from here.  There are directions for the different operating systems.  WHAT A RELIEF to have stable binary distributions of the latest releases.  THANK YOU!! If I had to, I’d build everything myself.  However, it is a time saver having the Eclipse community provide this for us.  I am using OpenOCD v0.9, the most recent stable version as of this date.  Make sure to use at least 0.9!
  • Note: In case there is a need to build OpenOCD from source, this page looked promising.
  • After installing on my Mac, I changed the directory path structure to get rid of the spaces in the “GNU ARM Eclipse” directory name that was created to “GNUARMEclipse”…I find spaces in names can cause unwelcome problems that I wish to deal with.
  • Check the OpenOCD version by opening a terminal and running openocd -v .  This requires a path to the openocd.  I used the default install directory and then changed the GNU ARM Eclipse directory name as noted above, so the command I used was:

$ /Applications/GNUARMEclipse/OpenOCD/0.9.0-201505191004/bin/openocd –version

GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716 (2015-05-19-12:55)

Licensed under GNU GPL v2

For bug reports, read

http://openocd.org/doc/doxygen/bugs.html

  • Check to see if OpenOCD can talk to the FRDM-KL25Z.  OpenOCD uses a configuration file for each board it supports.  Whereas the OpenOCD binary is loaded within the …/bin directory, the configuration files for the boards are located within …/scripts/board .  The configuration file for the FRDM-KL25Z is frdm-kl25z.cfg .  Making sure the board was plugged into a USB port, I ran the following from the command line:

$ /Applications/GNUARMEclipse/OpenOCD/0.9.0-201505191004/bin/openocd -f /Applications/GNUARMEclipse/OpenOCD/0.9.0-201505191004/scripts/board/frdm-kl25z.cfg

GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716 (2015-05-19-12:55)

Licensed under GNU GPL v2

For bug reports, read

http://openocd.org/doc/doxygen/bugs.html

Info : only one transport option; autoselect ‘swd’

srst_only separate srst_gates_jtag srst_open_drain connect_deassert_srst

Info : add flash_bank kinetis kl25.flash

adapter speed: 1000 kHz

srst_only separate srst_nogate srst_open_drain connect_deassert_srst

cortex_m reset_config sysresetreq

Info : CMSIS-DAP: SWD  Supported

Info : CMSIS-DAP: Interface Initialised (SWD)

Info : CMSIS-DAP: FW Version = 1.0

Info : SWCLK/TCK = 0 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1

Info : CMSIS-DAP: Interface ready

Info : clock speed 1000 kHz

Info : SWD IDCODE 0x0bc11477

Info : kl25.cpu: hardware has 2 breakpoints, 2 watchpoints

Info : MDM: Chip is unsecured. Continuing.

OOH…um…YIPPEE!!!  IT WORKED.  (side note – I originally spent way too many hours trying to get this working within Ubuntu..I mean *way* too many hours.  I was going to include details of this painful process.  However, I decided to vent a bit here and press on).
 
…pressing on to using Eclipse + OpenOCD as my IDE of choice….
  • Update Eclipse to know where the OpenOCD binary is located by following the “Update OpenOCD path” steps documented here.
  • Update Eclipse plug-ins for OpenOCD support by follow the steps outlined in “OpenOCD Debugging plug-ins documented here

 

 I already had these installed.  However, I went through the process.  A lot of updates were applied.

 And …finally… the mbed SDK

 Step 3: mbed

My attention at this point turns to what is needed to get an mbed app using the serial port of the FRDM-KL25Z to communicate with a terminal program on my mac through SSH.

The steps I took to install the mbed SDK come from here.

  • Download the mbed project from github: git clone https://github.com/mbedmicro/mbed.git
  • Create a private_settings.py file at the location noted in the web post.  Add the path to the GNU ARM GCC.  I got the path from within an Eclipse project->Properties page->C/C++ Build->Settings  under the Toolchains tab, the Toolchain path.  Mine is /usr/local/gcc-arm-none-eabi-4_9-2015q1/bin
  • I did not have the following python libraries installed on my mac so I ran:
    • sudo easy_install colorama (an overview of colorama can be found here).
    • sudo easy_install jinja2 (overview is here).
    • sudo easy_install pyserial (overview is here).
  • Run the build python script.  Python comes with OS X.  I opened a terminal window in the /Applications/mbed/workspace_tools directory (where build.py is) and ran: 

$ python build.py -m KL25Z -t GCC_ARM 

 

$ python build.py -m KL25Z -t GCC_ARM

Building library CMSIS (KL25Z, GCC_ARM)

Copy: arm_common_tables.h

Copy: arm_const_structs.h

Copy: arm_math.h

Copy: core_ca9.h

Copy: core_ca_mmu.h

Copy: core_caFunc.h

Copy: core_caInstr.h

Copy: core_cm0.h

Copy: core_cm0plus.h

Copy: core_cm3.h

Copy: core_cm4.h

Copy: core_cm4_simd.h

Copy: core_cm7.h

Copy: core_cmFunc.h

Copy: core_cmInstr.h

Copy: core_cmSimd.h

Copy: core_sc000.h

Copy: core_sc300.h

Copy: cmsis.h

Copy: cmsis_nvic.h

Copy: MKL25Z4.h

Copy: system_MKL25Z4.h

Copy: MKL25Z4.ld

Compile: startup_MKL25Z4.S

Compile: cmsis_nvic.c

Compile: system_MKL25Z4.c

Copy: startup_MKL25Z4.o

Copy: cmsis_nvic.o

Copy: system_MKL25Z4.o

Building library MBED (KL25Z, GCC_ARM)

Copy: AnalogIn.h

Copy: AnalogOut.h

Copy: BusIn.h

Copy: BusInOut.h

Copy: BusOut.h

Copy: CallChain.h

Copy: CAN.h

Copy: can_helper.h

Copy: CircularBuffer.h

Copy: CThunk.h

Copy: DigitalIn.h

Copy: DigitalInOut.h

Copy: DigitalOut.h

Copy: DirHandle.h

Copy: Ethernet.h

Copy: FileBase.h

Copy: FileHandle.h

Copy: FileLike.h

Copy: FilePath.h

Copy: FileSystemLike.h

Copy: FunctionPointer.h

Copy: I2C.h

Copy: I2CSlave.h

Copy: InterruptIn.h

Copy: InterruptManager.h

Copy: LocalFileSystem.h

Copy: LowPowerTicker.h

Copy: LowPowerTimeout.h

Copy: LowPowerTimer.h

Copy: mbed.h

Copy: mbed_assert.h

Copy: mbed_debug.h

Copy: mbed_error.h

Copy: mbed_interface.h

Copy: platform.h

Copy: PortIn.h

Copy: PortInOut.h

Copy: PortOut.h

Copy: PwmOut.h

Copy: RawSerial.h

Copy: rtc_time.h

Copy: semihost_api.h

Copy: Serial.h

Copy: SerialBase.h

Copy: SPI.h

Copy: SPISlave.h

Copy: Stream.h

Copy: Ticker.h

Copy: Timeout.h

Copy: Timer.h

Copy: TimerEvent.h

Copy: toolchain.h

Copy: Transaction.h

Copy: wait_api.h

Copy: analogin_api.h

Copy: analogout_api.h

Copy: buffer.h

Copy: can_api.h

Copy: dma_api.h

Copy: ethernet_api.h

Copy: gpio_api.h

Copy: gpio_irq_api.h

Copy: i2c_api.h

Copy: lp_ticker_api.h

Copy: pinmap.h

Copy: port_api.h

Copy: pwmout_api.h

Copy: rtc_api.h

Copy: serial_api.h

Copy: sleep_api.h

Copy: spi_api.h

Copy: ticker_api.h

Copy: us_ticker_api.h

Copy: clk_freqs.h

Copy: gpio_object.h

Copy: objects.h

Copy: PeripheralPins.h

Copy: PortNames.h

Copy: device.h

Copy: PeripheralNames.h

Copy: PinNames.h

Compile: PeripheralPins.c

Compile: gpio_irq_api.c

[Warning] gpio_irq_api.c@86: In function ‘gpio_irq_init’: ‘irq_n’ may be used uninitialized in this function [-Wmaybe-uninitialized]

[Warning] gpio_irq_api.c@100: In function ‘gpio_irq_init’: ‘vector’ may be used uninitialized in this function [-Wmaybe-uninitialized]

[Warning] gpio_irq_api.c@103: In function ‘gpio_irq_init’: ‘ch_base’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Compile: mbed_overrides.c

Compile: serial_api.c

[Warning] serial_api.c@177: In function ‘serial_format’: ‘parity_enable’ may be used uninitialized in this function [-Wmaybe-uninitialized]

[Warning] serial_api.c@177: In function ‘serial_format’: ‘parity_select’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Compile: spi_api.c

[Warning] spi_api.c@55: 22:0: ‘extosc_frequency’ defined but not used [-Wunused-function]

[Warning] spi_api.c@117: 22:0: ‘mcgpllfll_frequency’ defined but not used [-Wunused-function]

Compile: analogin_api.c

[Warning] analogin_api.c@55: 21:0: ‘extosc_frequency’ defined but not used [-Wunused-function]

[Warning] analogin_api.c@117: 21:0: ‘mcgpllfll_frequency’ defined but not used [-Wunused-function]

Compile: analogout_api.c

Compile: gpio_api.c

Compile: i2c_api.c

[Warning] i2c_api.c@55: 21:0: ‘extosc_frequency’ defined but not used [-Wunused-function]

[Warning] i2c_api.c@117: 21:0: ‘mcgpllfll_frequency’ defined but not used [-Wunused-function]

Compile: pinmap.c

Compile: port_api.c

Compile: pwmout_api.c

Compile: rtc_api.c

[Warning] rtc_api.c@117: 18:0: ‘mcgpllfll_frequency’ defined but not used [-Wunused-function]

Compile: sleep.c

[Warning] sleep.c@60: In function ‘deepsleep’: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]

Compile: us_ticker.c

[Warning] us_ticker.c@117: 19:0: ‘mcgpllfll_frequency’ defined but not used [-Wunused-function]

Compile: BusIn.cpp

Compile: BusInOut.cpp

Compile: BusOut.cpp

Compile: CAN.cpp

Compile: CallChain.cpp

Compile: Ethernet.cpp

Compile: FileBase.cpp

Compile: FileLike.cpp

Compile: FilePath.cpp

Compile: FileSystemLike.cpp

Compile: I2C.cpp

Compile: I2CSlave.cpp

Compile: InterruptIn.cpp

Compile: InterruptManager.cpp

Compile: LocalFileSystem.cpp

Compile: RawSerial.cpp

Compile: SPI.cpp

Compile: SPISlave.cpp

Compile: Serial.cpp

Compile: SerialBase.cpp

Compile: Stream.cpp

Compile: Ticker.cpp

Compile: Timeout.cpp

Compile: Timer.cpp

Compile: TimerEvent.cpp

Compile: assert.c

Compile: board.c

Compile: error.c

Compile: gpio.c

Compile: lp_ticker_api.c

Compile: mbed_interface.c

Compile: pinmap_common.c

Compile: retarget.cpp

Compile: rtc_time.c

Compile: semihost_api.c

Compile: ticker_api.c

Compile: us_ticker_api.c

Compile: wait_api.c

Library: libmbed.a

Copy: mbed_overrides.o

Copy: board.o

Copy: retarget.o

 

Completed in: (4.55)s

 

Build successes:

  * GCC_ARM::KL25Z

 

  • Test by running make.py.  I wanted to echo characters over a serial port, which is the 8th test.  To run this test, I needed to use a SSH application. I could use terminal.  However, I am not well versed in terminal so I downloaded and started to use coolterm.  I found coolterm to be useful so I donated $25.  I appreciate efforts such as these.  
    • after launching CoolTerm, go into options and set the Port to the USB port represented by the FRDM-KL25Z.  The USB port used by the FRDM-KL25Z can be found by starting a terminal session and typing: 

$ ls /dev/tty.*

/dev/tty.Bluetooth-Incoming-Port/dev/tty.usbmodem1452

/dev/tty.Bluetooth-Modem

    • Go into the Terminal settings dialog box and change the Key Emulation to LF and check Local Echo:

 

  • open a Terminal window and run a test.  For example, I opened a terminal window at the folder where the make.py file resides.  I ran test 8 (the echo serial test – you can get a list of the test by running make.py without parameters).  The command I used was: 

python make.py  -m KL25Z -t GCC_ARM -d /Volumes/mbed -s /dev/tty.usbmodem1452 -p 8

YIPPEE!!! Here’s the output in CoolTerm:

 That’s It For Now

The goals for this post were:
  • Installation and verification of OpenOCD with my FRDM-KL25Z board.
  • Setting upEclipse/OpenOCD.
  • Installing the mbed SDK and running test binaries built using the mbed SDK on the FRDM-KL25Z board.

I feel these goals (after many hours of…well… :-) ) were met.

My next “unknown” is to step through source code within an OpenOCD debugging session on Eclipse.

 

THANKS for reading this far.  Please find many things to smile about.

Calibrating the Ladybug’s EC Probe

Tags

,

I just passed a YIPPEE! moment…I’m able to calibrate and read pH values from a Ladybug Blue Lite using my iPad.  While the code is definitely prototype, the nRF51822 firmware talks quite well with the iOS app written in Swift.  By the way, did I mention how delightful it is to code in Swift?  I could wax on about the wonders of Playgrounds and the sheer brilliance that went into the entire iOS development environment.  But that is not what this post is all about.

I have not spent time on EC calibration. It is time I did.

The Goal

The Goal of this post is to walk through the thought process and design of how EC calibration will be done for Ladybug Blue Lite EC probes.

Thanks To Those That Went Before

  • A huge thanks to Apple for the delightful Swift language.  I get these mini explosions of excitement every time I dig deeper into Swift…like the power Swift has given enums….who woulda thought?  Well – certainly them!
  • As always, a huge and warm thanks to Chris Gammell and the team at Contextual Electronics.  I would not be able to do this stuff without Chris and the Contextual Electronics courses.

How EC is Measured  

EC is a measurement of conductivity.  EC measurements are given in Siemens/meter (or in my case µS/cm).  But an EC reading measures Siemens (µS).  A measurement of Siemens is a measurement of conductance.  The dependency relationship between Conductance (represented by G), voltage, and current is  G=I/V=1/R (since -as we all know- according to Ohm’w law: V=IR).  As I have noted in previous posts, the EC circuit measures resistance.  G is then calculated from 1/R.

To go from µS to µS/cm, the geometry of the two electrodes making up the EC probe need to be known.   A common ideal geometry for an EC probe is this one:

ECProbeDimensions

As noted hereThe conductance depends on the total concentration of ions in the solution as well as on the length and area of the solution through which the current passes.  Thus, even if the concentration of the ions remains the same, the conductance will change if the length or area of the current path changes.

What this means is the length/area of the electrodes need to be taken into account.  

EC = G*(distance between the electrodes)/(the cross-sectional area of an electrode – i.e.: each electrode is identical).  (The Distance) / (The cross-sectional area) is referred to as “K” or “the cell constant.”

Calibration

Calibration is calculating the actual K value from an ideal K value.  The ideal K value for the probe I am using = 1.  Using a calibrated EC solution of 1413µS, my probe should measure 1413µS.  But it measured 1188µS.  Since (EC measurement read) * actual K = 1413µS/cm, 1182*(actual K) = 1413 or actual K = 1.19.

What is used for the actual K value of an EC probe is the K slope.

From this post

Two Points Are Better Than One

In my earlier calculation, I used one point calibration.  While a one point calibration can be used, the K slope value will be more accurate if two EC calibration solutions are used.  This is similar to the pH calibration method in which I use a pH4 and pH7 calibration solution to calculate the probe’s variance from an ideal probe.

The Ladybug will support up to two EC calibration readings to calculate the EC probe’s K value.

 

 

OKIDOKIE…that’s it for now.  THANK YOU for reading this far.  Please find many things to smile about.  Just smile.

 

 

  

 

Using a Union and Structs to Store pH Calibration Data.

Tags

,

I need to store information about pH calibration within the Ladybug Blue Lite’s (LBL) nRF51822 Flash.  The values I store include:

  • a word for the write check. The write check is a known value the program checks to check if the values for the pH calibration have been stored before – or if the values written are corrupted (this assumes if the write check bytes are corrupted, the bytes holding the pH calibration info is also corrupted).  The value I will use is 0x01020304
  • a word for the pH 4 millivolts read when the probe is in the pH 4 calibration solution.  This should be +/- 415 mV.  I cover why in earlier posts on the pH circuit….As well as why calibrating the probe to the LBL circuits is important….
  • a word for the pH 7 millivolts read when the probe is in the pH 7 calibration solution.  This also should be +/- 415 mV.
  • a word to hold the date the calibration was taken.  pH probes should be regularly checked.  By storing the calibration date, the smartphone can notify the hydro farmer it is time to calibrate.

Unions are a wonderful way to map a bunch-o-bytes to the same memory location as formatted values.  Examples where this is useful is storing data to Flash as well as sending data over a connection (e.g.: BLE).

Thanks to Those That Went Before

Ron Sousa provided us with excellent videos within the embedded section of Contextual Electronics that gave me a much more practical view on when to use a Union in firmware.

The Goal

I am sure twiddling data structures is very familiar to most of you.  I struggle with setting up Unions and Structs in a way that makes it easiest to access bytes based on different views.  The goal of this post is to highlight how a Union (plus structs) can provide easier access to the two most common views: 1) a bunch of sequential bytes that need to go from one place to the other (e.g.: RAM to Flash, a micro controller to a Smartphone…) 2) data types (like a pH value) that map to bytes in memory that most likely need to be swapped in some fashion to go from a bunch of bytes to an int32_t (for example) pH value.

The Data Structure for pH Calibration

Measuring the pH means using a pH probe (and circuitry like that on the LBL) that will not give the “ideal” values.  Calculating the pH means mapping mV values to pH values where the ideal step (slope) between two pH values is 59.16mV.  Most likely a pH probe will not get exactly 0 mV for pH 7 or 3*59.16 (lower pH values register positive voltage values, pH 4 is 3 away from pH 7) = 177.5mV for pH 4.  Once the actual values of a pH probe’s reading when the probe is emerged in a calibration solution, the slope measurement of 59.16 = (177.5 – 0)/(7-4) = 177.5/3 = 59.16 mV can be adjusted so subsequent pH measurements when in the nutrient bath can accommodate for the difference in slope.  For example, I measured a pH of 165 mV for pH4 and -10 mV for pH 10.   The slope is: (165 – -10)/(7-4) = 175/3 = 58.3 mV.

Here are the Union and struct typedefs I use:

typedef  struct {

  uint32_t   write_check;

  int32_t    pH4;

  int32_t    pH7;

  uint8_t    month;//eg: 1 for January, 12 for December

  uint8_t    day;//eg: 30 for the 30th day of the month

  uint16_t   year;//eg: 2015

}pH_cal_t;

 

typedef  union {

  uint8_t    bytes[16];

  pH_cal_t   pH_cal;

}pH_data_t;


Here is part of the test code I wrote:

  pH_data.pH_cal.write_check = 0x01020304;

  pH_data.pH_cal.pH4 = 165;

  pH_data.pH_cal.pH7 = -10;

  pH_data.pH_cal.day = 20;

  pH_data.pH_cal.month = 11;

  pH_data.pH_cal.year = 2015;

  pstorage_wait_handle = block_0_handle.block_id; //Specify which pstorage handle to wait for.

  pstorage_wait_flag = 1;

  pstorage_store(&block_0_handle, pH_data.bytes, 16, 0);

  while(pstorage_wait_flag) { power_manage(); }

  SEGGER_RTT_WriteString(0,“data written:\n”);

  for (int i = 0; i < 16; i++)

    { 

      if (i > 0) SEGGER_RTT_WriteString(0,“:”);

      SEGGER_RTT_printf(0,“%02X”,pH_data.bytes[i]);

    }

  SEGGER_RTT_WriteString(0,“\n”);

  SEGGER_RTT_WriteString(0,“\n”);

  SEGGER_RTT_printf(0,“write_check: %0X\n”,pH_data.pH_cal.write_check);

  SEGGER_RTT_printf(0,“pH 4 calibration: %d\n”,pH_data.pH_cal.pH4);

  SEGGER_RTT_printf(0,“pH 7 calibration: %d\n”,pH_data.pH_cal.pH7);

  SEGGER_RTT_printf(0,“day             : %d\n”,pH_data.pH_cal.day);

  SEGGER_RTT_printf(0,“month           : %d\n”,pH_data.pH_cal.month);

  SEGGER_RTT_printf(0,“year            : %d\n”,pH_data.pH_cal.year);

The code uses the nRF51’s persistent storage manager (pstorage) APIs to read and write from flash.  There turned out to be some “gotchas” reading and writing to flash that took away my enthusiasm to talk directly to the NVMC registers.  I’ll probably cover my flash read/write exploration in a future post.

Here is what is printed out to the terminal screen: 

04:03:02:01:A5:00:00:00:F6:FF:FF:FF:0B:14:DF:07

 

write_check: 1020304

pH 4 calibration: 165

pH 7 calibration: -10

day             : 20

month           : 11

year            : 2015
 

The side by side comparison of the pH_data.bytes view of the data and the pH_data.pH_cal view helped me better see how the bytes are internally stored versus how the program’s data type interpret them.  For example, pH 4 calibration is 4 millivolts.  It is the second word with a LSB of 0xA5 followed by 0’s for the other three bytes.  The int32_t representation is 0x000000A5 = 165.

Using Unions and structs to go between different data views – the view the micro controller’s storage has on the data versus a data type’s interpretation of the bytes that are stored – made it much easier to understand what the bytes are all about.

 

 

That’s it for now.  Thanks for reading this far.

The Ladybug Blue Lite is Measuring pH/EC and sending Results to an iPhone!

Tags

, , ,

A YIPPEE!! day for me.  I received the Ladybug Blue Lite Beta 2 PCBs from OSH Park yesterday.  Today I tested reading pH and EC values.

It worked!  It worked!…well, results were the exact values but they were within an acceptable range.  Particular given the pH probe is an inexpensive probe I got from eBay about a year ago.  Since pH probes “go bad”, I am not surprised results were a bit off from what the model predicts.

It is such a great feeling.  BLE, pH, EC…working on a small PCB!  Off the Arduino and shields!  I plan to use this one to help me grow tomatoes this winter.

Here are the measurements I read from the AINs:

pH

The probe was placed in the pH 4 and then pH 10 calibration solution.

  VGND(mV) AIN(mV) pH (mV) pH
pH 4 1455 1620 165 4.2
pH 10 1455 1314 -141 9.4

I discuss calculating the pH in earlier posts.  The first thing is to calculate the mV reading from the AIN.  The AIN is not a differential reading so there are two AINs for pH measurements.  One is the virtual ground (VGND) used to move the rails of the op amp to accommodate the AC waveform of the pH reading.  The other is the pH measurement (above virtual ground).

For pH 4, the pH in mV = 1620 – 1455 = 165 mV. pH =  7 – 165/59.16 = 4.2

For pH 10, the pH = 1314 – 1455 = -141 mV. pH = 7 – (-141/59.16) = 9.4

If I decide to use this probe, I would calibrate readings to be on the slope of the line for the 4.2 and 9.4 readings.

EC

I placed the EC probe into a 1000TDS/2mS calibration solution.

VGND(mV) EC VIN (mV) EC VOUT (mV) Gain Resistance(Ω) EC (mS)
1455 1650 2085 3.2 448.3 2.2

Gain = Vout/Vin = (2085-1455)/(1650-1455) = 3.2.  Resistance = 1000/(3.2-1) = 448.3Ω.  EC = 1/448.3 = 2.2mS.  Pretty darn close!

 

Off to finish the firmware and iOS app…

 

Thanks for reading this far.  Please find many things to smile about.

Why I am not a fan of ARM’s Semihosting feature

Tags

,

I figured out why my nRF51822 applications loaded just fine on the debugger and not on a power cycle / pin reset.  ….just in case this happens to you…

—>>

The quick answer: if you are using Eclipse and the GNU ARM toolchain, check to see if the link option:

—specs=rdimon.specs

is one of the linker options.  If it is, semihosting has been enabled and the binary will not load unless a debugger is present.

<<–

What the heck is semihosting? I liked Erich Styger’s definition the best: Semihosting is a technique to do printf() debugging through an active debug connection. So instead using a physical connection like RS-232 or USB CDC, the connection to the host machine is through the debugger.

Seems that semihosting is something concocted by Arm…Looking at this page from Arm, “if you are compiling for an ARMv6-M or ARMv7-M, for example a Cortex-M1 or Cortex-M3 processor, semihosting is implemented using the BKPT instruction.”  The nRF51822 uses the cortex-m0, which is based on the ARMv6-M architecture.

As noted on this page from Arm, The specific instruction sent by the binary to invoke semihosting is:

BKPT 0xAB

It turns out my .hex files all had the BKPT 0xAB command somewhere near the beginning of the file.  As the .hex was loaded and run, the cortex-m0 ran across the BKPT 0xAB and then waited patiently for the debugger to start communicating with it.  Since I hadn’t attached a debugger, “waiting patiently” turned into not running my nRF51822 app.

I found this out by exploring the .hex file with jlinkexe.  After loading the binary onto the chip, I opened a terminal window and started jlinkexe:

 jlinkexe -device nRF51422_xxAC

Once in the jlink utility, I executed the go command:

jlink>g

and (I think) the step command:

jlink>s

BKPT 0xAB

now the code sits at the breakpoint….

After reading Erich’s post on semihosting with the GNU ARM Eclipse Debug plugins, it turned out that yes indeed, the C template I had started with in Eclipse had included the —specs=rdimon.specs  !  Once this option had been removed, the binaries ran after a power cycle without the debugger.  YIPPEE!

I also removed the debugger settings Erich pointed out.

Determining the Revision, FLASH and RAM size of the nRF51822

Tags

Update

Philip in the comments noted the current version of the nRF51 Compatibility Matrix (discussed below) can be downloaded here.  THANK YOU Philip!

End of Update

I just got another rev of the Ladybug Lite (LBL) PCB from OSH Park….excuse me for one second….YIPPEE!!!!….

The first thing I did was to solder a MDBT40 nRF51822 module (discussed in a previous post). I also soldered on the header pins that connect to the SWD pins on the MDBT40.  So now I can “talk” to the MDBT40 on this rev of the LBL PCB by using the nRF51 DK’s debug out (discussed in a previous post).

The MDBT40 has the nRF51822 inside.  Because of this, I can’t get to the markings on the chip that could identify the amount of RAM, Flash, and hardware identification into.

So how was I going to figure out the chip revision?  How much RAM / Flash is on it?  

The Goal

The goal of this post is to use the JLinkEXE SEGGER utility on my Mac to figure out the revision, amount of RAM and amount of Flash on the MDBT40 module I soldered onto the LBL Alpha 5 board.

Thanks to Those That Went Before

Lucky for me, Ron Sousa has been covering accessing micro controllers through registers in our Contextual Electronics Embedded course.  Armed with the confidence to not only read a data sheet but figure out what to do :-)…

It’s All About the FICR

I found the Factory Information Configuration Registers (FICR) described in chapter 7 of the nRF51_Series_Reference_manual v3.0.pdf.

The FICR’s base address is 0x1000 0000.  From the base address, there are several registers that can be used to ask the nRF51822 how much RAM, Flash, and the chip revision.

The challenge I was having is interpreting Table 13 in the nRF51_Series_Reference lists the registers.   Perhaps my lack of background working with micro controller firmware makes it difficult for me to interpret the very brief descriptions……however, some of the naming was promising such as CODESIZE and SIZERAMBLOCKS and NUMRAMBLOCKS.  

How do I take the values in these registers and turn them into the amount of RAM and Flash?

A nice thing about the Nordic’s nRF51 SDK is there are A LOT of helper APIs that help me figure out how to interact with the nRF51822’s registers.  In this case, there is the .c and .h nrf_ic_info files.  Looking at the nrf_ic_info.h file:

/**@brief Enum identifying the IC revision as described in the nRF51 Series Compatibility Matrix. */

typedefenum

{

    IC_REVISION_NRF51_REV1,    /**< IC Revision 1. */

    IC_REVISION_NRF51_REV2,    /**< IC Revision 2. */

    IC_REVISION_NRF51_REV3,    /**< IC Revision 3. */

    IC_REVISION_NRF51_UNKNOWN  /**< IC Revision unknown. */

} nrf_ic_revision_t;

 

 /**@brief IC information struct containing the IC revision, RAM size, and FLASH size. */

typedefstruct

{

    nrf_ic_revision_t ic_revision;    /**< IC revision. */

    uint16_t          ram_size;       /**< RAM size in kB (16 = 16 kB RAM). */

    uint16_t          flash_size;     /**< FLASH size in kB (256 = 256 kB FLASH). */

} nrf_ic_info_t;

 

 

/**@brief  Function for returning information about the IC revision, the RAM size, and the FLASH size.

 *

 * @param[out]  p_ic_info  Struct containing IC revision, RAM size, and FLASH size.

and then at the function that uses these typedefs:

void nrf_ic_info_get(nrf_ic_info_t * p_ic_info)

{

    uint32_t ic_data;

 

    ic_data = (((*((uint32_t volatile *)0xF0000FE8)) & 0x000000F0) >> 4);

 

    p_ic_info->ram_size = (uint16_t) NRF_FICR->NUMRAMBLOCK * (NRF_FICR->SIZERAMBLOCKS / 1024);

    p_ic_info->flash_size = (uint16_t) NRF_FICR->CODESIZE;

    switch (ic_data)

    {

        /** IC revision 1 */

        case 1:

            p_ic_info->ic_revision = IC_REVISION_NRF51_REV1;

            break;

 

        /** IC revision 2 */

        case 4:

            p_ic_info->ic_revision = IC_REVISION_NRF51_REV2;

            break;

 

        /** IC revision 3 */

        case 7:

            /* fall through */

        case 8:

            /* fall through */

        case 9:

            p_ic_info->ic_revision = IC_REVISION_NRF51_REV3;

            break;

 

        default:

            p_ic_info->ic_revision = IC_REVISION_NRF51_UNKNOWN;

            break;

    }

the ic_data seems like “gobbly gook” to me.  I found it more informative to check out the tables in the nRF51_Series_Compatibility_Matrix_v1.1.pdf (download link) against what is stored for the hardware ID in the NRF_FICR->CONFIGID register (which I’ll use below).

Figure out Flash/RAM/Revision

MDBT40

I then figured out the RAM/Flash/Revision for the MDBT40 nRF51822 module on the latest LBL:

Amount of RAM = NRF_FICR->NUMRAMBLOCK * (NRF_FICR->SIZERAMBLOCKS / 1024);  /**< RAM size in kB (16 = 16 kB RAM). */

Amount of Flash = NRF_FICR->CODESIZE  /**< FLASH size in kB (256 = 256 kB FLASH). */ 

Start JLinkExe

Hooking up the SWD cables on the LBL to the nRF51 DK’s P19 debug out port:

 

 From a terminal window on the Mac, I started jlinkexe: 

jlinkexe -device nRF51822

nRF51822 on the LBL Alpha 5

and looked at the contents of the NRF_FICR->CODESIZE register. The nRF51 Series Reference Manual gives the register offset of the CODESIZE register as 0x0014.

J-Link>mem32 10000014,1

10000014 = 00000100 

and 0x100 = 256.  This means the nRF51822 on the LBL has 256KB of Flash.

 NRF_FICR->NUMRAMBLOCK’s register offset is 0x0034:

J-Link>mem32 10000034,1

10000034 = 00000002

NRF_FICR->SIZERAMBLOCKS’s register offset is 0x0038:

 

J-Link>mem32 10000038,1

 

10000038 = 00002000  0x2000 = 8192)

The amount of RAM = 2 * (8192/1024) = 16KB

Revision is in NRF_FICR->CONFIGID

J-Link>mem32 1000005C,1

1000005C = FFFF0072

 the last four bytes – in this case 0x0072 give the revision number of the chip.  Table 2 in the nRF51_Series_Compatibility_Matrix_v1.1.pdf (downloaded from this link) notes 72 = revision 3, packet variant QF AA in a QFN48 package.  Revision 3 has three package variants: QF AA, QF AB, QF AC.  The difference is in the amount of Flash and Ram.  The QF AA – which is the one I am using in this build of the LBL PCB- has  256K Flash, 16KB RAM.  The one that I have ordered – which has the same amount of Flash and Ram as the nRF51 chip on the nRF51 DK – is the QF AC.  The QF AC is a FN-48 package containing 256K Flash and 32K Ram.

nRF51822 on the nRF51 DK

looking at the same register contents on the nRF51 DK:

  • NRF_FICR->NUMRAMBLOCK = 4
  • NRF_FICR->SIZERAMBLOCK = 8192
  • NRF_FICR->CODESIZE = 256
  • NRF_FICR->CONFIGID = FFFF 0086

RAM = 4 * (8192/1024) = 32KB, Flash = 256KB.  The CONFIGID – 86 – is not listed in a table of the nRF_Series_Compatibility_Matrix_v1.1.pdf.  The closest is 85 – which is a QF AC nRF51422 which is a QFN48 package containing 256K Flash and 32K Ram.  Given what Nordic advertises about the chip on the nRF51 DK, I make a leap and assume the identification of 85 and 86 are the same.

 

 

That was fun!  Please find many things to smile about.

 

 

Follow

Get every new post delivered to your Inbox.

Join 33 other followers