Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Sunday, July 29, 2018

Circuit Python on Nordic nRF52

Background

I've been using Python for about 5 years now for light scripting utilities that I use for make embedded development easy (e.g. parsers, serial to tcp bridges, etc.).  I really enjoy using Python because its incredibly easy to use, but I never really understood what was going on underneath the hood.  As a bare-metal embedded developer it has always bothered me to not understand how things are working under the hood, since that affects my decision making on developing appropriate solutions to a problem. 

So recently I stumbled upon Circuit Python from AdaFruit and was tempted to buy one until I found that it supported a Nordic nRF52 PCA10040 that I had lying around at work unused.  Before starting, I read the Circuit Python guide to figure out what I was getting myself into and realized it was really simple to get started.  So today is the day I deep dive a bit into the underpinnings of python.  But first step is to put Circuit Python into the nRF52. 

The Setup


The steps are described cleary in their documentation, but I had to figure out a few things to make it work on the nRF52 PCA10040 which I highlight in GREEN.  So here are my steps:
  • Following the steps in 
  • Build their cross tools
    • make -C mpy-cross
  • Now we build the nRF52 target
    • cd ports/nrf
    • make BOARD=pca10040
      • However this caused an error and suggested that I needed to install the ble_stack
        • ../drivers/bluetooth/download_ble_stack.sh
    • make BOARD=pca10040 SD=s132

Installing

Problems

Now this is when I ran into problems.  It looks like you need to specify the value for BOOT_SETTINGS_ADDR, which I had no idea what they would be:
  • make BOARD=pca10040 BOOT_SETTINGS_ADDR=<????> flash 
So instead I commented out the following from the Makefile since I wasn't doing any fancy booting:
# Also update to bootloader settting to validate application and skip checksum ( app valid = 0x0001, crc = 0x0000 )
flash: $(BUILD)/$(OUTPUT_FILENAME).hex
    nrfjprog --program $< --sectorerase -f $(MCU_VARIANT)
    #nrfjprog --erasepage $(BOOT_SETTING_ADDR) -f $(MCU_VARIANT)
    #nrfjprog --memwr $(BOOT_SETTING_ADDR) --val 0x00000001 -f $(MCU_VARIANT)
    nrfjprog --reset -f $(MCU_VARIANT)

Now I can run with no issues
  • make BOARD=pca10040 flash

Running Circuilt Python

Now that it installed successfully, I had to test whether REPL works.  But then I couldn't find a serial device to communicate to.  After some google searching, it looks like the nRF52 PCA10040 uses the on board SEGGER JLink as a UART bridge under /dev/ttyACM0.   Unlike the FTDI implementation of /dev/ttyUSBx which I could have used minicom or putty, I needed to use a different terminal program that supports ttyACMx like screen

So installing screen and then connecting to it
  • sudo apt-get install screen
  • sudo screen /dev/ttyACM0 115200
SUCCESS, I'm able to finally talk to REPL.  Now my next step is to find a 

Monday, January 5, 2015

Hierarchical State Machines in Python

Recently I've been messing around with Python for some project at work.  I thought it was a good time to dig up some of my old interests on Hierarchical State Machines using Python.  I've wrote this last year just for experiments, but I decided to try integrating it with a project at work.

So what exactly is a Hierarchical State Machine (HSM) anyway?  It is a super set of a Finite State Machines(FSM).  And like all FSM, they are basically event filters that only react to an event if the state handles it.  For a given state, certain events can invoke an action or cause a transition to another state.  This is very common design technique to model behavior in an embedded systems.  But one of the problems with realizing the state machine in code is that you end up with too many states and too many transitions that makes it complicated and unmanageable for non trivial systems.

Enter the HSM which basically allows states to be nested.  The advantages of nesting a state into another state is that the child state doesn't need to handle every event/signal.  If the current state doesn't have a handler for it, then it can pass the event to its parent and see if this has a handler for the event/signal.  Otherwise that state can continue passing that event up to the state hierarchy.  You can think of this as a sort of class inheritance.  This offers some big advantages such as:
  1. More opportunities for refactoring code - similar behavior can be pushed up to the parent
  2. Less event handlers - Common events can be pushed up to the parent instead of every child state
  3. Less transitions - Each state doesn't need to handle the transition
  4. Consistent setup and teardown - Each state handles specific operations, while parent handle common operations
Anyhow, there are many resources about HSM that you can google or find here.

From my perspective, I only care about being able to draw a HSM state machine using some UML diagramming tool and then map it directly into some code.  So here is my python implementation of HSM @ https://github.com/howard-chan/HSM.git

Of course for this to be useful, I need an example to demonstrate its capabilities.  So using Visual Paradigm as my UML drawing tool, I created this simple HSM model of a camera.

Here we have a camera that really has 4 states (Off, OnShoot, OnDispPlay, OnDispMenu) which are nested into the parent states (On, OnDisp).

Here we see that any state will handle the PWR event.  If we are in any one of the child On states (OnShoot, OnDispPlay, OnDispMenu), we see that each child doesn't directly handle the PWR event so it pushes the event up to the parent which does handle the PWR event.  In this case, its the On state which will handle the PWR event and cause a transition to the Off state.  As the HSM transitions from say the "OnDispMenu" to "Off" state, each of the "exit" event handling are invoked to clean up the state, til it enter the "Off" state which triggers the "entry" even handling to setup the state.

So for the following event sequence fed to the Camera:
    # Instantiate Camera
    basic = Camera("Canon")
    # Turn on the Power
    basic.Run(evt.PWR)
    # Take a picture
    basic.Run(evt.RELEASE)
    # Take another picture
    basic.Run(evt.RELEASE)
    # Playback the photo
    basic.Run(evt.MODE)
    # Oops, pushed the release button by accident
    basic.Run(evt.RELEASE)
    # Go to menu settings
    basic.Run(evt.MODE)
    # Uh oh, low battery
    basic.Run(evt.LOWBATT)
    # Time to turn it off
    basic.Run(evt.PWR)

We get the following output:
Run  Canon[Off](evt:PWR)
Tran Canon[Off -> On]
  Canon[Off](EXIT)
Exit Low Power Mode
  Canon[On](ENTRY)
Open Lens
  Canon[On](INIT)
Tran Canon[On -> On.Shoot]
  Canon[On.Shoot](ENTRY)
Enable Sensor
  Canon[On.Shoot](INIT)
Run  Canon[On.Shoot](evt:RELEASE)
CLICK!, save photo
Run  Canon[On.Shoot](evt:RELEASE)
CLICK!, save photo
Run  Canon[On.Shoot](evt:MODE)
Tran Canon[On.Shoot -> On.Disp.Play]
  Canon[On.Shoot](EXIT)
Disable Sensor
  Canon[On.Disp](ENTRY)
Turn on LCD
  Canon[On.Disp.Play](ENTRY)
Display Pictures
  Canon[On.Disp.Play](INIT)
Run  Canon[On.Disp.Play](evt:RELEASE)
  evt:RELEASE unhandled, passing to Canon[On.Disp]
  evt:RELEASE unhandled, passing to Canon[On]
  evt:RELEASE unhandled, passing to Canon[:root:]
Unhandled event:RELEASE Canon[<hsm.State instance at 0x00000000058EA708>]
Run  Canon[On.Disp.Play](evt:MODE)
Tran Canon[On.Disp.Play -> On.Disp.Menu]
  Canon[On.Disp.Play](EXIT)
  Canon[On.Disp.Menu](ENTRY)
Display Menu
  Canon[On.Disp.Menu](INIT)
Run  Canon[On.Disp.Menu](evt:LOWBATT)
  evt:LOWBATT unhandled, passing to Canon[On.Disp]
  evt:LOWBATT unhandled, passing to Canon[On]
Beep low battery warning
Run  Canon[On.Disp.Menu](evt:PWR)
  evt:PWR unhandled, passing to Canon[On.Disp]
  evt:PWR unhandled, passing to Canon[On]
Tran Canon[On.Disp.Menu -> Off]
  Canon[On.Disp.Menu](EXIT)
  Canon[On.Disp](EXIT)
Turn off LCD
  Canon[On](EXIT)
Close Lens
  Canon[Off](ENTRY)
Enter Low Power Mode
  Canon[Off](INIT)

Well that is it for now, I'll probably elaborate on how the HSM.py works on my next entry