Quantcast
Channel: OpenEnergyMonitor aggregator
Viewing all 328 articles
Browse latest View live

JeeLabs: USB on STM32F10x µCs

$
0
0

Every µC from the STM32F10x family has hardware built-in to support USB. The earlier (i.e. smaller) STM32F103’s have a more limited implementation that more recent models. There are some really strange design choices in it - they look like a rushed-to-market implementation:

  • there’s a 512-byte buffer where everything USB-related happens, but this memory must be accessed as 256 16-bit words on 32-bit address boundaries, i.e. there’s a gap every 2 bytes
  • some registers live on the APB1 bus like the rest, others live inside this “Packet Memory”
  • some bits in the registers can’t be set directly, only toggled - this means you have to read the current value, determine what to toggle (via XOR), and then send those toggle bits (while carefully keeping others in a do-nothing state)

Having seen some FPGA designs recently, it’s clear that this is caused by the two different clock domains of the µC on the one hand (normally 72 MHz) and the USB hardware on the other (48 MHz, but not necessarily synchronised). Improving this would probably have required more silicon and engineering time, which perhaps wasn’t avaialable when the STM32F103 came out.

It all leads to very convoluted code!

USB is a fairly complex protocol. There is an excellent USB in a NutShell resource on the web which goes into all the details. A brief summary and some notes:

  • USB is driven by the host, the device side only needs to respond to requests
  • there are control messages and actual data, these use different “endpoints”, a bit like different signals in a multi-pin connector - control is always via endpoint zero
  • all low-level bit-stuffing, framing, and checksumming is handled by the µC’s hardware
  • a “Full Speed” USB link runs at 12 Mbps, with a “Start Of Frame” packet every 1 ms
  • the USB driver requires little CPU overhead - idling is fully handled by the hardware
  • two pins (D+/D-) encode the data differentially and signal special states, e.g. bus reset
  • there’s a complex “enumeration” phase on startup, for the host to learn what device it is

But the good news is, the currentwork-in-progress implementation is working (with a big thank you to Eckhart Köppen for his help!). Here’s an extract from the Linux kernel log:

[98361.944068] usb 2-1: new full-speed USB device number 6 using uhci_hcd
[98362.144095] usb 2-1: New USB device found, idVendor=0483, idProduct=5740
[98362.144106] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[98362.144112] usb 2-1: Product: Forth Serial Port
[98362.144118] usb 2-1: Manufacturer: Mecrisp (STM32F10x)
[98362.144124] usb 2-1: SerialNumber: C934CC37
[98362.149505] cdc_acm 2-1:1.0: ttyACM0: USB ACM device
[98443.520186] usb 2-1: USB disconnect, device number 6

And here’s what it looks like on Mac OSX:

(this is an earlier version, the serial number was not yet filled in)

As you can see, the STM32F103 presents itself as an “ACM” modem-like device (with device names chosen to identify this as a Mecrisp Forth context). The huge advantage here, is that such devices do not need any additional drivers - at least not on Linux and Mac OSX.

There is one quirk on Linux: if there’s a ModemManager running, it’ll start sending “AT” commands to the device the moment it is plugged in. This will confuse the Forth interpreter - you have to kill that modemmanager process first (who uses modems anyway, nowadays?).

The other good news is that serial I/O actually works - with the driver set up to send back everything it receives, you can open the serial port, type at it, and see everything echoed back.

In short: the USB driver is essentially already working!

But now comes the tricky bit: for this to become the main interface to Mecrisp Forth, we will need to re-route its input and output to use the USB device. There are four words defined in Forth to help with this: key?, key, emit?, and emit. There are also four “hooks” which allow pointing these to other words to “re-vector” all keyboard output and console output through.

Right now, a first attempt to redefine these words is still failing: for some reason, Mecrisp Forth refuses to start listening to the USB device. This may be related to some circular “ring buffers” added to solve the impedance mismatch between packet-oriented USB streams and the character-oriented mode required by Forth. It’s not clear at the moment where the problem originates - clearly more debugging will be needed to figure this out.

But then what? We can load the new USB driver in flash memory, on top of Mecrisp, and we can set it up to switch its I/O permanently to this code. But what if someone types “eraseflash”? That would wipe out our setup, and revert the entire system to its original serial-port-only state.

Whoops, bye bye USB - we will effectively have lost control of the system!

Here is one possible way out (but see below) - it requires a change in Mecrisp Forth:

  • introduce a new type of variable, perhaps “otp-var” (for “One-Time Variable”)
  • it behaves like any other variable, but its initial value can be changed once in Forth
  • the hook-key?, hook-key, etc vectors are changed to such otp-vars
  • the Mecrisp build uses the current settings, i.e. serial-key?, serial-key, etc.
  • two more variables are introduced, let’s calll them flash-start andflash-limit
  • flash-start marks the lowest erase point for Mecrisp’s flash erasure
  • similarly, flash-limit marks the top limit, beyond which it never erases
  • as shipped, these are set to $04000 and $10000, i.e. 16K and 64K, as they are now
  • and while we’re at it, let’s also add ram-limit, preset to $5000 (20K) on STM32F103’s

The above changes would not alter the way Mecrisp Forth operates, as shipped. But they will make it possible to alter the system (once!) to include more Forth code in Flash memory, and to make sure that code can never be lost again.

The trade-off is that once you make such a change, it becomes permanent (which is the whole point) - to change it again, the entire chip will have to be erased and re-flashed with a clean Mecrisp firmware image.

The flash-limit and ram-limit variables are not strictly necessary, but they allow altering the system to use more or less of the available flash and RAM. This neatly addresses the fact that there are variations of the STM32F103 (and others), which differ only in flash and RAM size.

It also allows reserving flash and/or RAM for other uses. And even do crazy things like putting the RAM limit very very high and allow Forth to allocate memory in external RAM chips attached via FSMC, for example.

On to the implementation of otp-var: this might not be very difficult: variables defined in flash already contain a copy of their initial value. Of course, these variables end up being allocated in RAM (as part of Mecrisp’s startup sequence), so maybe all that needs to be done is to add one extra 32-bit field to these variables in flash, preset to $FFFFFFFF, with logic to check this extra value first. If it’s still $FFFFFFFF, we use the other intial value, but if it isn’t, we use this new value instead. And then some code in Forth (it need not be part of the Mecrisp core) can figure out how to re-write that special value - once only.

One could even consider changing all variables in this way, i.e. allowing every variable defined in flash to be changed once. So the Mecrisp core can continue to set them up as usual, while still allowing an application to change them, once. This may lead to more cases where the entire chip will have to be erased and re-flashed (over serial or via SWD), but this reconfigurability really isn’t intended for mainstream use - it just adds the option to create an enhanced core, such as a USB-enabled variant of Mecrisp. It would also allow creating a smaller Mecrisp core, FWIW.

Update - Matthias Koch, Mecrisp’s author, has suggested some alternatives, which will avoid having to ever reflash the core (although you may still need a serial hookup to get out of one of the possible failure modes). Consider the above shelved: keeping the core as is, is preferable!


JeeLabs: And so Forth...

$
0
0

This week’s episode will be a short one, as I try to push though some issues and prepare for greater things to come.

First, a little progress report on getting USB going in Mecrisp Forth on the STM32F10x series µCs. This has been evolving nicely, but there are still some important hurdles to overcome - not all USB-related, actually.

The second post is my little attempt to “explain Forth” to someone not familiar with it. How do you read that funky syntax? How do you wrap your mind around what’s going on, especially since it has such a compact and unconventional notation?

Some other related news: there’s now a nicePWM module in theembello repository on GitHub. It shows off the built-in timers and how they can support LED blinking and dimming on up to 16 pins, as well as driving servos with 500-step resolution - all in hardware, i.e. without jitter.

There are also some short PWM docs (I’m looking for the most friction-less way to document this stuff, comments welcome).

Yet more: a snappyILI9163 128x128 colour LCD driver, a 3 12 digitvoltmeter, shown in the image above, and a way to switch the µC between 72 and 8 MHz operation.

JeeLabs: Forth in 7 easy steps

$
0
0

Forth is a simple language, with simple rules and a simple execution model. It’s also self-hosted and interactive - you can type this at the prompt:

1 2 + .<cr>

What you’ll get as response is this (details may differ, everything below is for Mecrisp Forth):

1 2 + . 3  ok.<lf>
       ^^^^^^^^^^^ added by Forth

All code is parsed into a sequence of “words”, which is anything between whitespace. The above code consist of 4 words: 1, 2, +, and . - these words operate on a data stack. Numbers “push themselves” onto the stack, i.e. first 1, then 2. The word + is predefined, it replaces the two stack entries by a single one, their sum. The word . pops the top value off the stack and prints it as number followed by a space.

Since there are no more words left, Mecrisp then prints ok. followed by a linefeed, and waits for a new line of text to parse, so it can process more words.

That’s it. The essence of Forth. A few more steps like this, and you’ll have the complete story.

To define this as a new demo word, type this (let’s omit the obvious closing <cr> from now on):

: demo  1 2 + . ;

The : word starts a definition and ; ends it. Nothing interesting happens, but now this works:

demo 3 ok.
    ^^^^^^ added by Forth

New words must be defined in terms of existing ones. Here’s another definition:

: JC's-triple-demo  demo demo demo ;

Word names can be anything other than whitespace (including UTF-8, and even 1 or 2 - but that could lead to major confusion).

That’s step 2, now you know how to extend the language.

The definition of words is an important mechanism. You’ve already seen the data stack, but there is also a stack for words and their compiled code, which is called the “dictionary” in Forth. New words are added to the end, and words are looked up in reverse order, so that the last one will be used when words are re-defined.

The : word is quite special: it will parse the next word in the input stream and add it as a fresh definition to the dictionary. It also sets a“state” flag to compile mode. And then it returns to the main loop in Forth to process all remaining words.

And here’s The Big Trick, part 1: the main loop will parse more words, but since the state flag is set, it will append calls to these words to the dictionary instead of executing them.

At some point, Forth will need to finish the definition and return to run mode.

The Big Trick, part 2: a word can be marked as “immediate”. When this is the case, it overrides the state logic in the main loop, and gets executed right away, even in compile mode. So there’s an immediate ; word in Forth, which does two things: add a “return” statement to the end of the dictionary, and reset the state flag to zero.

Immediate words enable magical behaviour in Forth, because they’ll switch back to run mode during compilation. They can do anything (or to be more precise: they are in fact the compiler).

That’s step 3. This is how Forth unifies “run mode” and “compile mode”.

Forth has conditionals and loops. Here’s a rewritten version of the above:

: looping-demo  3 0 do demo loop ;

This should be readable by now: 3 and 0 just get added to the stack, do pops them as loop limits (in funky reverse order), then comes the loop body, followed by loop which presumably knows how to count and repeat, and the closing semicolon to finish the definition of looping-demo.

It should come as no surprise that do and loop are immediate words. They append code to the dictionary to implement the do loop and use the data stack (while in compile mode) to track branch offsets. There’s also a word called i to push the current loop value on the stack. As you can see, common words in Forth tend to have very short names.

This example loops until a key is pressed (key? pushes a flag on the stack, which until uses):

: boring-demo  begin demo key? until ;

Since if, do, begin, etc generate code, they can only be used inside word definitions. You can’t use them interactively, i.e. in run mode, but youcan enter a definition and call it, all on one line.

Immediate words are also used to implement if, else, then, and several other jump-based words. The funky order of else and then takes a little getting used to, but it’s trivial stuff:

: even-demo  123 2 mod 0 = if ." EVEN!" then ;
: even-odd-demo  123 2 mod 0 = if ." EVEN!" else ." ODD!" then ;

Several new words have been used here. The mod word is used to calculate modulo-two, = will compare two values, and ." ..." prints a string. Note the space after the opening quote: in Forth, everything is a word, so the print-string word is called ." and must be enclosed in spaces. Note also that the closing quote does not need a space in front, because the ." word plays special tricks with parsing.

Such unconventional syntax details come from the fact that Forth uses stacks and treats everything as words - the price of a simple uniform data + parsing model.

Congratulations, this was step 4, with a little peek inside the compiler!

Words can call other words, and do loops can be nested (it might help to view do loops as a special way to “call” their body repeatedly).

This nesting is actually what all other languages also have, using a “return” stack. In Forth, the return stack is separate from the data stack. This is what gives the language itsconcatenative properties (a term coined decades after Forth was invented).

When a defined word is executed, it pushes the current instruction pointer on the return stack, and starts executing its own code. When it returns, it pops the instruction pointer back off the return stack and resumes where it left off. Do loops also use the return stack to store some state.

The return stack is fully exposed in Forth, which allows some amazing tricks, such as aone-line coroutine implementation, as well as a simple collaborativemulti-tasker implementation.

In day-to-day use, the data and return stacks are the only ones that matter. The dictionary (i.e. code stack) and a stack to allocate RAM variables from can safely be ignored most of the time.

What about other data? Here is a constant and a variable definition:

123456789 constant MY-CONST
987654321 variable my-var

Constants are just that, they can be used wherever a value is needed, and push their value onto the data stack when executed. The convention is to write them in uppercase, but Forth is case insensitive (for ASCII characters, not for UTF-8!), so it won’t matter during use.

A variable pushes its address. To fetch (and then print) the value, you need to use the @ word:

my-var @ . 987654321  ok.
          ^^^^^^^^^^^^^^^ added by Forth

To store a value, there’s the ! word, which expects a value and an address on the stack:

123 my-var !  my-var @ . 123  ok.
                        ^^^^^^^^^ added by Forth

For allocating larger memory areas in RAM, there’s the buffer: word:

200 buffer: my-buffer

This sets aside a 200-byte word-aligned area in RAM. It remains available as long as my-buffer is in the dictionary. Executing my-buffer will push its buffer address on the (data) stack.

You’ve made it through step 5, now you know all about stacks and memory.

As you can imagine, there are a large number of words in Forth, each with their own behaviour and stack effect. Inline comments between words called ( and) are normally used to document a word, followed by a \ comment about what it does. If ! were defined in Forth (it isn’t, it’s a primitive), it could have been documented as follows:

: ! ( u|n a-addr -- ) \ Stores single number in memory
  ... ;

Where u|n means: an unsigned or signed integer, and a-addr means aligned address. Everything before the -- is what is expected as stack input, everything after is the stack result (nothing in this example). These are just comments and conventions, Forth will skip all that.

Likewise, + might have been defined as:

: + ( u1|n1 u2|n2 -- u3|n3 ) ... ; \ Addition

Many words affect only the data stack, but a few mess with the return stack. Like so:

: >r ( x -- ) ( R: -- x ) ... ;

What this says is: the value on the data stack before calling >r will end up on the return stack afterwards: so >r moves an item from the data stack to the return stack (better get it off again with r> or rdrop before the current word returns, else the code will probably crash!).

These stack effect comments are a critical part of the documentation of each word, since there are usually no local variables in Forth.

Here is the glossary of the pre-defined words in Mecrisp Forth. There are a few hundred of them, but no worries: you can explore and gradually expand your vocabulary - only a fraction of these are needed to start programming in Forth.

Yeay, step 6 - you’re all set to build up your Forth vocabulary!

The last step that remains, is to try things out and look for examples and more documentation on the web. See the “Dive into Forth” series, partone,two, andthree for a recent exploration here at JeeLabs. ThePWM module (documentedhere) shows one example of how to implement a hardware feature in Forth.

For old, but still useful material, see the Thinking Forth and Starting Forth books by Leo Brodie, Julian Noble’sintroduction, documents on the forth.org site, and links mentioned on theforum.

A last point to make, is that Forth lives extremely“close to the metal”. Any suggestion of high-level coding is purely smoke-and-mirrors. It has just enough machinery to be reasonably useful, and to let you compile and extend it with more definitions to do whatever you need.

And there you have it: Forth in sixteen hundred, ehm… words. Hopefully this intro can help you wrap your mind around an intriguingly powerful and concise programming language.

Nathan Chantrell.net: Amazon Echo Alexa Skills Kit integration with Node-RED

$
0
0
Amazon Echo
Amazon Echo

I have been using my Amazon Echo and a Raspberry Pi running AlexaPi for voice control of lights, appliances and modes using ha-bridge, a Java app that emulates the Philips Hue API which is one of the few devices that the Echo supports natively. I use ha-bridge to do http GET requests to endpoints on my Node-RED based home automation system which allows me to control lights and appliances etc. by saying things such as “Alexa turn on the kitchen lights” or “Alexa switch the stairs lights off” or “Alexa turn cinema mode on”.

This works very well and as it is piggybacking on functionality that the Echo/AVS has built in there is no need for additional keywords but for something more advanced than on/off/brightness control we need to use the Alexa Skills Kit (ASK).

With ASK skills you can add new functionality which is operated by saying something like “Alexa ask the house to….” or “Alexa tell the house to….” where “the house” is the name of your custom skill and what follows can be anything you like. You can also customise the response that the Echo returns to the user so this opens up a lot of opportunities.

I’ve used “Echo” throughout this post but this will work with a real Echo or a Raspberry Pi running AlexaPi or Amazon’s new Java client. It’s easy to set up with Node-RED and certainly beats all that tedious mucking about in hyperspace with IFTTT.

First create a new skill:

Log in to the Amazon developer site at https://developer.amazon.com/

Go to Apps & Services and then Alexa and click the button to get started with the Alexa Skills Kit

Click “Add a New Skill” and you will have 3 boxes to fill in:

Name– this is the name that will appear in the Alexa app or on the alexa.amazon.com site.

Invocation Name– this is the name you will activate the skill with, so if you want to say “Alexa tell the house I am going out” the invocation name would be “the house”.  You can use “tell” or “ask” to invoke a skill, both will work automatically so don’t include this in your invocation name.

Endpoint– enter the link to an endpoint on your Node-RED install, eg. https://your-server/echo and select the HTTPS option.
Note that you must have HTTPS set up on Node-RED and it must be publicly visible. Rather than expose all my Node-RED http endpoints to the internet using port forwarding I do this using the Apache ProxyPass directive to redirect a single URL to the relevant endpoint on Node-RED. Apache is already running on the server so this made sense for me but you can also do this with ha-proxy.

Click Next to move on to the next page where you will see boxes for:

Intent Schema – Here we have to enter a JSON list with an intent for each command we want to create. eg. here are four, one for a status report and three to set different home/away modes.

{"intents": [
    {"intent": "StatusReportIntent"
    },
    {"intent": "ModeHomeIntent"
    },
    {"intent": "ModeAwayIntent"
    },
    {"intent": "ModeBedIntent"
    }
  ]
}

Sample Utterances– this is a list of the phrases that you want to use along with which intent they relate to, you can add multiple phrases to each intent eg.

StatusReportIntent for a status report
StatusReportIntent for status
StatusReportIntent about status
ModeHomeIntent I'm home
ModeHomeIntent I'm back
ModeHomeIntent I'm awake
ModeHomeIntent to wake up
ModeBedIntent I'm going to bed
ModeBedIntent I'm off to bed
ModeAwayIntent I'm leaving
ModeAwayIntent I'm going out
ModeAwayIntent I'm away
ModeAwayIntent to close down

Click Next and you will see a spinner next to interaction model on the left indicating that the model is being built.

The next section is SSL certificate, check “My development endpoint has a certificate from a trusted certificate authority” and click next. (I’m assuming you are using a trusted SSL certificate, there’s really no excuse not to now but if you must you can use a self-signed certificate but will need to upload it to Amazon in X.509 format)

Select “no” for Account Linking on the next page then click next, you should now see the testing page states “This skill is enabled for testing on your account.”

That’s the Amazon side done, now onto Node-RED.

The Node-RED side

There is a link to basic flow that can be copied and pasted to do this below but I thought a full explanation would be helpful to understand what is going on. Firstly drag an http in node onto the workspace, set the method as POST and the URL as the one you used for the endpoint in the ASK configuration above, so if you had used the example of https://your-server/echo above you would enter /echo here.

http-in

Now add a switch node and change the property to msg.payload.request.type and add rules for the three types of request we might get. Those are LaunchRequest, IntentRequest and SessionEndedRequest, the first and last allow for a more interactive session but I will only be using a “one shot” IntentRequest in this example.

switch-node

Link the output from the http in node to the input.

alexa1

Add another switch node and add a rule for each of the intents that you created in the ASK developer site, eg.

switch-node2

Connect the output for the IntentRequest rule in  the first switch node to the input of the second switch node.

alexa2

The outputs from the second switch node can now go to something you want to do when that particular intent is triggered, for example a function node or just to send something to an MQTT topic etc. In my ModeHomeIntent I use the change node to set the payload and topic to put the house into home mode and link the output of that to an MQTT node.

alexa2b

We also need to send a response back to the ASK service to tell it that we are done and to give a voice confirmation for the user, this could be a simple “OK” or something more involved like my status report which grabs information from a number of global contexts and builds a message which the Echo speaks.

Add a template node with the following:

{"version": "1.0","response": {"outputSpeech": {"type": "PlainText","text": "{{payload}}"
    },"shouldEndSession": true
  }
}

The msg.payload we send into this template node will be what the Echo speaks.

Finally the output from the template needs to go via a JSON node into an http response node to send the response to Amazon.

alexa3

If you want to save a bit of time here is a copy of the above flow that you can just copy and paste into the Node-RED import from clipboard window.

You can then add more intents to do more things, just remember that everything should ultimately link into the “Format response” template node to send a response back to Amazon. If you don’t send this response the intent will still fire and Node-RED will do what you ask but the Echo will respond to the user with an error that the skill took too long respond.

You can create multiple skills that all terminate at the same endpoint, for example I have “the house” for controlling home related things and “the camera” is used to control a Raspberry Pi pan/tilt camera.

node-red-alexa
Something a bit more involved

 

JeeLabs: Preparing for serial re-flashing

$
0
0

The STM32F103 µC chips all have a ROM-based boot loader on board. Most chips variants can only be re-flashed via USART1, using the PA9 and PA10 pins, so we will need to connect to that serial port initially - even if the board is intended to be used only over USB or RF later on!

This is the “Blue Pill” board which will be used in this example:

Boards like these are very common, low-cost, and easy to find (on eBay, for example).

Many different STM32F103xx chip variants can be used, as long as they have at least 64 KB flash and 20 KB RAM. Most of the low-cost boards contain a 48-pinC8 (64K flash) or CB (128K) chip. The R8 and RB chips have 64 pins. If the chip is an xC, it has 256K flash and 48K RAM, and if it’s an xE, it’s 512K flash with 64K RAM. The Vx chips are 100-pin and the Zx’s even have a 144 pins. And then there’s the smallHyTiny board - marked TB, with 128K flash and 36 pins.

So much for picking a board - as you can see, there are many candidates!

The serial connection can be very simple, all we need is power, ground, and the transmit plus receive signals. The Arduino-style DTR/RESET and CTS/RTS signals are not used here:

Four wires need to be connected as follows (you could also use jumpers or a breadboard):

(ignore the wires on the left, these are for the radio - they are not needed at this point)

A note on “RX” and “TX”: these are named from the perspective of the FTDI board i.e. header pin RX receives data sent out from pin PA9, which is therefore called TX on the µC’s USART1 - and similarly, header pin “TX” connects to PA10, which is USART1’s RX. It’s all crossed over.

Power to the board should be 5V, and be fed to the on-board regulator, not directly to the STM32F103 chip - applying 5V directly to the µC chip will damage it. The RX/TX signal levels should be 3.3V, but this will work with 5V on PA9/PA10 (not all pins are “five volt tolerant”!).

The last thing to note about connections is that the STM32F103 µC needs to be placed in a special “boot mode” to erase and re-flash its memory. On the above board, there are two jumpers for this, one for BOOT0 and one for BOOT1 (which rarely needs to be changed, if ever).

Here is how to place this board in boot mode, by lowering BOOT0 on the right to logic “1”:

Now insert USB + board to power up again.

Perfect, we’re all set for the next step: uploading the firmware - stay tuned…

(don’t forget to put this jumper back and press reset again once the firmware upload is done)

JeeLabs: From Zero To Wireless

$
0
0

This week, I’m going to set up a wireless node from scratch, using an STM32F103 board, RFM69 radio module, serial USB interface board, and very simple software:

The result is a node with all the software needed to send / receive RF packets, with a complete Forth system on board, ready with drivers for digital and analog I/O, PWM, I2C, and SPI.

Let’s get going - 1 article per day, as usual:

The board used here is this one, but any other with the same µC should also work.

JeeLabs: A new serial tool: vive la Folie!

$
0
0

Working with embdded µC boards involves quite a few steps: apart from the hardware itself, you need to connect to it and figure out how to upload code, of course. But you also think about the development cycle and revision control. The list of tools needed to get going can grow quickly…

Not so with Forth. It’s all self-contained and self-hosted. The embedded µC does it all, it just needs to be sent the source code. The same applies to uploading the initial firmware image: all you need, is a way to send the proper bytes to the µC while it is in a special boot loader mode.

In the Arduino world, this is handled by anIDE, which combines an editing environment, a cross-compiler for the µC you’re using, the“avrdude” or “stlink” utilities to handle firmware uploads, and a built-in serial terminal for actually interacting with the code, once it’s running. The success of the Arduino is probably largely due to the fact that this all-in-one approach has been properly packaged as a single “app” which runs on Windows, Mac OSX, and Linux.

Could something similar be done in a Forth-based environment?

Yes, it sure can, but quite differently: meet the Forth LineEvaluator -Folie, in short!

Folie combines a number of functions into an installation-free (!), single-file (!) executable, and is available for Windows, Mac OSX, and Linux (both Intel and ARM):

  • it’s a serial terminal to let you talk to the attached µC board
  • there’s command history to re-use easily previous entries via up-arrow
  • there’s a file include mechanism to send sources as if you had typed them in
  • all lines sent are throttled to avoid over-running the Forth word parser

Here is a brief interactive session as example:

$ folie -p /dev/cu.usbserial-A8009L2N
Connected to: /dev/cu.usbserial-A8009L2N
  ok.
7 8 * . 56  ok.
\       >>> include a
1 2 + . 3  ok.
3 a 0 1 2  ok.
\       <<<<<<<<<<< a (3 lines)
\ done.
8 9 * . 72  ok.
^D
$

To clarify this further, here is what was typed in, literally:

<CR>
7 8 * .<CR>
include a<CR>
8 9 * .<CR><CTRL-D>

The contents of the “a” file is:

1 2 + .
: a 0 do i . loop ;
3 a

As you can see, it’s more or less just a line-by-line serial terminal, whereby each line is sent when you hit return (it can be edited locally until then). That and (nestable) include file expansion.

The line starting with “include” was not sent - the file’s contents was sent instead. Lines which generate no output will not echo back, only lines which cause some other effect will be shown. Which is why the “: a ...;” text does not show on the screen, but everything else does.

Folie is clearly not an IDE - it’s not even trying to be one. It only handles the communication with an attached µC running Mecrisp Forth. Folie is in fact intended to be usedalongside your own preferred editor. Once done editing, switch to Folie and enter “include somefile.fs” to apply the changes (or hit up-arrow plus return). With proper definitions at the beginning, this’ll replace or extend what was already loaded - you can even include commands to start things up.

That’s the whole Forth development cycle: edit, send changes, explore interactively, repeat…

One more thing: to get started, Mecrisp Forth needs to be uploaded and “flashed” onto the µC. Since Folie has support for the STM32F103 ROM boot loader, it can also be used for this step:

  • set up the µC board to have the BOOT0 pin tied high, with the jumper in position “1”
  • insert the USB interface and board to power it up
  • launch Folie as follows: folie -p <comport> -u <firmware-file>

If all is well, you should see something like this:

$ folie -p /dev/cu.usbserial-A8009L2N -u mf224.hex
Connected to: /dev/cu.usbserial-A8009L2N
        File: mf224.hex
       Count: 15684 bytes (converted from Intel HEX)
    Checksum: 4e555979 hex
 Synchronise: .+ OK
 Boot loader: 22 hex
   Chip type: 0410 hex - STM32F1, performance, medium-density
   Unprotect: OK
      Resume: .+ OK
  Mass erase: OK
   Uploading: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ OK
$

Now restore the BOOT0 jumper to its normal “0” setting, and press reset.

Note: as of this writing (end March 2016), uploading works on Mac OSX and Linux, but not on Windows (grrr!) - you will need to use another mechanism, see this article for some options.

The latest Folie binary releases can be foundhere. If you haveGo installed and properly set up, you can also build from source (onGitHub) using this magic incantation:

go install github.com/jeelabs/embello/tools/folie

Folie is a fairly small open source application, written in Go - it is still evolving quite rapidly at the moment, but everything should work as expected w.r.t. what has been described so far. A lot of its magic comes from the excellent chzyer/readline andtarm/serial packages it is based on.

The one remaining issue is: what firmware do we upload to the STM32F103?

If you’re really impatient, you can extract the latest“mecrisp-stellaris-stm32f103.hex” image from the Mecrisp release and get your feet wet in the world of Forth on ARM chips - or … check out the next article for a more complete image with RFM69 wireless radio support!

JeeLabs: Adding the RFM69 module

$
0
0

The last step to create a wireless node is to hook up the RFM radio. Here is what we’re after:

There are 4 signals and 2 power pins to connect (more can be added later, for pin interrupts):

RFM69CWSTM32F103BOTH
SSEL (NSS)PA4+3.3V
SCLKPA5GND
MISOPA6
MOSIPA7

And here is the other side, all hooked up with thin “Kynar” wirewrap wire in this case:

It doesn’t matter which of the two ground pins on the RFM69CW you connect, either one is fine. Also, don’t forget to attach an antenna wire of about 82 mm to the ANT pin (for 868 MHz).

So much for the hardware. If you have no other RFM69 nodes already in operation, you’ll need to build a pair of these to try out actual wireless communication, of course.

To get all the software needed for the RFM69CW into the µC, and assuming you’re starting from scratch, the easiest will be to install a single “combined” image, containing Mecrisp Forth for the STM32F103 plus a slew of Forth definitions from theembello/explore/1608-forth/flib/ area, in particular theRF69 driver (which in turn depends on SPI words, etc):

  • make sure you haveFolie ready on your machine (see the previous article)
  • download the latestmf224-ztw.hex file (it’s a text file, but nevertheless gibberish)
  • the file contains Mecrisp Forth 2.2.4 plus the pre-built “Zero To Wireless” demo code
  • go through the steps described before to put the board in “boot mode”

Now launch Folie as follows: folie -p <comport> -u mf224-ztw.hex

If all went well, the output will be similar to this (add the -v flag to see more):

$ folie -p /dev/cu.usbserial-A8009L2N -u mf224-ztw.hex
Connected to: /dev/cu.usbserial-A8009L2N
        File: mf224-ztw.hex
       Count: 32768 bytes (converted from Intel HEX)
    Checksum: c0574666 hex
 Synchronise: .+ OK
 Boot loader: 22 hex
   Chip type: 0410 hex - STM32F1, performance, medium-density
   Unprotect: OK
      Resume: .+ OK
  Mass erase: OK
   Uploading: ++++++++++++++++++++++<etc>++++++++++++++++++++++ OK
$

Good, almost there! Just restore the BOOT0 jumper to “0” and press reset. If you now connect again with Folie in normal mode, and type “reset”, this is what should appear:

$ folie -p /dev/cu.usbserial-A8009L2N
Connected to: /dev/cu.usbserial-A8009L2N
  ok.
reset ?Mecrisp-Stellaris 2.2.4 for STM32F103 by Matthias Koch
64 KB <ztw> 32a92103

Those last two lines are the key: Mecrisp reports its version, platform, and creator on one line, and then the auto-launched init word prints a second line with some more tidbits:

  • the chip has been identified as containing 64 KB of flash memory
  • the wordset currently in flash corresponds to the “Zero To Wireless” demo fromhere
  • a 32-bit hex “name” for the chip, derived by xor-ing this chip’s 96-bit unique hardware ID

If you type “list”, you will see a dump of all the words currently defined on your board.

At this point, the board is working, Mecrisp is working, and there’s a bunch of Forth code in flash, ready to serve you. The next step is to find out whether the RFM69 radio is also working.

Type “rf69-init” to initialise SPI and the radio (using net group 42 @ 868.6 MHz defaults):

rf69-init  ok.

Now type “rf69.” (with the period at the end) to get a listing of the radio’s internal registers:

rf69.
     0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
00: -- 04 00 02 8A 02 E1 D9 26 40 41 60 02 92 F5 20
10: 24 9F 09 1A 40 B0 7B 9B 08 4A 42 40 80 06 1C 00
20: 00 00 00 02 FF 00 07 80 00 A0 00 00 00 05 88 2D
30: 2A 00 00 00 00 00 00 D0 42 00 00 00 8F 12 00 00
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
50: 00 01 88 08 00 00 01 00 1B 09 55 80 70 33 CA 08 ok.

Excellent - we’re seeing all sorts of settings. You could look them up in the HopeRF RFM69 datasheet, if you’re so inclined…

Now the tricky bit: as we all know, wireless needs (at least) two to tango!

  • set up a second node, then launch Folie a second time using that other serial port
    • … or skip this step, if you have other nodes already sending data out
    • verify that the second node also works and produces a good rf69. register dump
  • now turn the first node into a listener, by typing: rf69-listen
    • this starts a loop which waits for incoming packets and reports each one
    • that loop will continue to run until any command (or a return) is entered
  • on the second node, type: 12345 rf69-txtest
    • this will send out a single packet, with “12345” converted to ASCII bytes

If all goes well, the first node should now report a new incoming packet:

RF69 21EE2A7C040022801805 3132333435
         ^^            ^^ ^^^^^^^^^^
          |            |  |
      group      5 bytes  payload

Bingo! Lift-off! Wireless! Communication! Magic! Drumroll!

To select a different net group or frequency, you can adjust these variables:

123 rf69.group !  ok.
8683 rf69.freq !  ok.

Well, that’s the ideal case anyway. If for any reason this is not immediately working for you as expected, please consider registering and posting on theJeeLabs Forum or submitting an issue on GitHub. This code is still very young - getting it working under all circumstances (especially from Windows), is bound to take some more effort and detective work.

Still… it’s worth pointing out that we only needed two files to get a complete RF69 development environment going: the Folie serial utility, and the latest firmware hex dump. Once that works, everything else can be done in Forth, including reloading updated versions of the code in flash.


Viewing all 328 articles
Browse latest View live