Thursday 30 November 2023

Pimoroni Yukon - Interfacing a Raspberry Pi to an Micro-controller running Micropython

I've been working on testing some new pre-release modular robotics hardware from Pimoroni. The Yukon board.


I am planning to build my Pi Wars robot around this, which means I need a way to talk to a Micro-python board from a Raspberry Pi. It would be really neat to be able to do this over the USB lead. This board is not a HAT, so needs powering via USB. I thought it would be great to use the USB port on the Pi to power the Yukon board, and for communication between the two.

This opened up a rabbit hole of learning. First to work out how to send data over USB serial from Python on the Pi, and how to read the data in Micropython on a connected micro-controller. I discovered that certain byte values get changed (line ending conversions) and that some kill the micro-python program (the same way pressing the 'stop' button in the Thonny IDE does). I learned how to disable this feature in micropython, and then how difficult it is to get control back when main.py auto-runs on power up and turns off the ability of the IDE to intercept and stop the program!

To avoid these problems I decided to hex encode each byte I wanted to send, so it is sent as a pair of bytes represented by ASCII characters which are safe to send. This topic raised some interest on social media, so I cleaned up the example and made it run on a vanilla Raspberry Pi Pico board. You can find the code on github.com/Footleg/rpi-pico/tree/main/micropython/serial_comms_over_usb

Saturday 11 November 2023

Developing a small robot as a code test platform

I have learned from previous experience in the Pi Wars robotics competition how important it is to get testing code early. So rather than working on the physical build of my robot, I have been working on programming robotics code around areas which are new to me. I was already developing a small robot for STEM outreach events, based around the Inventor HAT mini board from Pimoroni.

This board supports encoder motors, something I have not developed code for previously. I wanted more feedback from my robot to better control it autonomously. Encoders enable you to read the speed of rotation of your motors, and count how many rotations they have made. This makes speed control possible, and distance travelled to be tracked. All my previous robots have been manual control only, so this was all new for me. I also added and IMU, which would enable measuring the orientation of the robot. This makes accurate turns possible.

The triangular tracked robot I was already developing is a little too small for Pi Wars, but the code developed on it should be portable to my competition robot once I have that built. I wrote a hardware abstraction interface so that my code talks to an interface which is independent of the specific hardware it is running on. I converted my manual control robot program from my STEM event robots to use the interface, creating this Universal Robot project on Github.

Saturday 30 September 2023

Pi Wars 2024 - The Beginning

Hard to believe it is almost 5 years since my last Pi Wars blog entry. Since I last entered as a competitor in 2019, I have mentored school teams each year the competition has run, and so decided not to enter myself after discovering just how much work that was in 2019! This year my plans to run another after school robotics club fell through when the Computer Science teacher I worked with last time left the school. So I thought I should enter again myself. I've built quite a few robots over the years since my last time, so I've been promoted to the 'veterans' category this time around. I just need a name and an idea now!

With the entry deadline fast approaching, I decided the most visually exciting robot I could think of would use 4 RC tracks in the place of wheels on a 4 'wheeled' rocker-bogie suspension. I've had these tracks in the parts box for a while, intending to build a robot out of them.


So that's the idea. In keeping with the theme 'Disaster Zone', I needed a robot name. So I decided on:

Team name: Footleg Robotics

Robot name: Ragnarök Rescue


Just as I had decided to enter, my daughter told me she was going to enter as a young person team with a school friend, and wanted me to be their team mentor. So here I am again, entering myself, and mentoring a young persons team at the same time. Will I never learn?

Saturday 28 August 2021

Running Robot Code on Start-up with systemd

Following the excellent blog post by Brian Starkey @usedbytes to set up my robot programs to launch on start-up on a Raspberry Pi, I found several things which didn't work with my particular programs. This article aims to extend the information from Brian's post.

First a quick recap on why use systemd services instead of rc.local or autostart desktop files. For me the main advantages of this method are:

  • The automatic start up of the robot code on boot can be turned on and off, which really helps when developing code and you don't want the robot program to launch each time you boot up.
  • You can configure the code to be restarted if it stops for any reason. Great for if an unexpected bug crashes your robot code in the middle of a demo or competition. No need to wait for a reboot to get the code running again.
So what went wrong when I tried to implement the services as described in Brian's blog? First I want to point out that nothing in Brian's blog was wrong. I just needed some additional configuration for my programs. The first problem I ran into was that I had only installed the python packages my program needed as the pi user, but found that the service was running as root. This can be fixed by installing all the packages as root, but I found an alternative option. You can specify the user to run the service as by putting User=pi in the service definition file.

The second problem I ran into was that my service ran in a headless mode, even though my Raspberry Pi was running a full desktop OS. This works fine for a python program which does not use the display, but my robot programs are usually built on top of pygame and use this to display information and sometimes control menus in a GUI. Running my programs as a service caused pygame to exit with 'pygame.error: Unable to open a console terminal'. So I had to learn how to run a service with access to the display device. This required a couple of lines to be added to the [Service] section:

Environment="DISPLAY=:0"
Environment="XAUTHORITY=/home/pi/.Xauthority"

I also needed to change the [Install] section WantedBy value:

[Install]
WantedBy=graphical.target

 

I am not going to repeat all the information from Brian's blog article here. You should read that first if you are not familiar with how to set up and configure services to run on boot. Once I had everything working this is what my runrobot.service file looked like.

[Unit]
Description=Robot Runner

[Service]
Type=exec
ExecStart=/usr/bin/python3 /home/pi/pygame-controller/examples/SentinelBoard/Turbo4WD.py
User=pi
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/home/pi/.Xauthority"
Restart=always
RestartSec=5s

[Install]
WantedBy=graphical.target

Command Quick Reference

The service file needs to be copied into /etc/systemd/system in order to work:
sudo cp runrobot.service /etc/systemd/system

As long as the filename ends '.service' you do not need to specify the full filename in all the commands. You can just use the service name (runrobot in this case).

If you change the .service file then the changes will not be picked up until the next reboot, or after you run this command:
sudo systemctl daemon-reload

To enable service (make it run on boot):
sudo systemctl enable runrobot

Disable service (stop it running on boot):
sudo systemctl disable runrobot

Start service (run the robot program without doing a reboot):
sudo systemctl start runrobot

Stop service (stop it running):
sudo systemctl stop runrobot

View output from the service/program since last boot:
sudo journalctl -b0 --unit=runrobot


Tuesday 29 June 2021

Dr Footleg on YouTube

My video content can be found at Dr Footleg on YouTube for my own channel. For much of my content recorded for other organisations I post links to it all on my Ko-Fi page so this is the place to find my talks on home robotics and PCB design.

Wednesday 23 September 2020

Arduino Development using VSCode

The Arduino IDE provides a means to write code for microcontrollers, with library management, compilation and uploading of your code. But the code development environment is lacking many of the features of a modern IDE. Enter VSCode to the rescue! Providing syntax checking, auto-indentation (with clean up), and code completion via the IntelliSense feature. VSCode is free and provides a capable development environment which supports multiple languages, remote code development over SSH and many other capabilities via a comprehensive library of extensions. In this article, we will be looking at options to use it to develop code for Arduino (and Arduino supported boards).

Before starting with VSCode, you need to install the Arduino Desktop IDE (at time of writing this was v1.8.13). You can download it from www.arduino.cc/en/main/software. The VSCode extensions use the Arduino IDE under the hood, so you need this to be working with your boards.  Check that example sketches can be compiled and deployed onto your board using  the Arduino IDE. 

Now we can set up VSCode. You can download the editor from code.visualstudio.com/


We will be writing C/C++ code for Arduino, so we need to install the extension to add support for this language. 

Down the lefthand side of the VSCode window is a vertical toolbar. Click on the Extensions button to open the extensions manager. 



Find and install the C/C++ extension:


There are two options to add support for Arduino development. The more obvious looking one is the Arduino extension. I was able to get this working with some configuration required, but a number of features did not work for me. I could not open the Serial Monitor, Teensy boards were not supported and I found the IntelliSense feature reported problems in my code which were not problems for the Arduino compiler. I did learn a few tricks to make things workable and I have detailed these at the end of this article if you want to use this extension. But I found things worked better for me using the alternative option, the PlatformIO IDE extension. You probably don't want to install both as the IntelliSense feature configurations of each are not compatible.

PlatformIO IDE

The PlatformIO IDE extension provides support for more boards (including the Teensy family), but it is not as tolerant of the non-standard C/C++ syntax which Arduino sketches allow. You will need to convert any .ino files into .cpp files in your projects to work with this extension. You will also find that functions have to be declared before they are used, and global variables and functions declared in one file in a project will not be in scope in other files. More details on this later, but aside from these code compatibility issues I found it very easy to set up and most of the configuration was automatic.


To get started with the PlatformIO IDE, I suggest you follow their quickstart guide. That is what I did, and there is little point in me repeating that information here. See https://docs.platformio.org/en/latest/integration/ide/vscode.html#quick-start 

Once I had built a basic blink sketch following the quickstart guide, I successfully imported an existing Arduino project from the extension home page, ticking the option to use the Arduino libraries already installed on my computer. This created a copy of the project under my <user>/Documents/PlatformIO/Projects directory, with the configuration set up to include libraries from the existing Arduino IDE installation on my computer. You select the board you want to compile for in the import dialog. I already had the Teensyduino packages installed, and found the Teensy boards listed in the board picker. The IDE warned me that I should change the .ino file to a .cpp file in order for IntelliSense to work. The project source was also put under a 'src' folder. Projects with both cpp and header files should be reorganised by moving the h files from the src folder into the include folder. I just renamed my single ino file through the VSCode explorer pane and I was able to compile and upload the project to my Teensy 3.2 board.

One additional configuration change I made was in the platformio.ini file under my project. I added a build_cache_dir line as follows in my ini file. You can point this to any folder you like outside your project. By doing this, libraries compiled as part of your sketch will not need recompiling every time you rebuild. So after the first build, subsequent builds will only need to recompile classes with code changes, which will be faster.

[platformio]
; Set a path to a cache folder
build_cache_dir = C:/temp/PlatformIO_buildcache

You can also add additional library paths in this file. I was working on a project which uses a library I am also developing on my computer. So I added a path to my library code in development after the path to the Arduino libraries (note the syntax ~ to refer to your user home directory):

[env:teensy31]
platform = teensy
board = teensy31
framework = arduino
lib_extra_dirs = 
    ~/Documents/Arduino/libraries
    ~/Documents/dev/Cpp/RGBMatrixAnimations

That was all I needed to do to get up and running with simple single file sketches. But to make multi-file projects compatible I needed to make some code changes. The Arduino IDE does some rewriting of your code before it compiles it. All .ino files in the project are essentially combined into one large code file, and any functions defined in them have declarations added to the code before they get called. You do not get any of this in PlatformIO. This does force you to write more standards compliant code though, so it is no bad thing. To get my more complex project to compile I had to do the following:

  1. All .ino files in a project need to be changed to the extension .cpp
  2. Functions defined in my main cpp file needed to either be defined before they are called, or have forward declarations added near the top of the file. (This article explains it in more detail: https://community.platformio.org/t/order-of-function-declaration/4546/2 ).
  3. To call any code in other .cpp files requires an include for that .cpp file adding to the file calling that code. This also means you need to add includes for any library header files to all the cpp files which use those libraries (rather than just once in the main project code file).
  4. Global variables declared in the main code file can no longer be accessed from code in other files. So you'll need to pass them to the code using them in other files (either via class constructors or as parameters to any methods).
I avoided declaring functions directly in secondary cpp files, instead using these files only for class declarations. You can then pass variables to these classes via their constructors, or setter member methods. This gives tighter scope control and makes for more robust code. Overall I found the PlatformIO extension worked better for me than the Arduino extension, but if you want a more Arduino like coding experience rather than standards compliant C++ then you may find the Arduino extension suits you better.

Arduino Extension

If you want to use the Arduino extension instead of PlatformIO (maybe you want to work on projects with .ino files for example), then install that extension instead.


This adds several Arduino commands to the command palette. With this installed, when you open an Arduino sketch (.ino) file in VSCode you should see a custom footer bar with clickable options to select the connected board, COM port and Programmer (if you are using one). Click on the <select board> item and the board manager should open (you can also open it via the Command Palette). I found the <select PORT> command was not working, so I had to set it manually in the arduino.json file. Open this file from the .vscode folder in your workspace, and add the COM port line as follows (setting the correct numbered COM port matching the one you used to program your board from the Arduino IDE).

"port""COM4",

You should now be able to verify sketches and upload them to your board using the Arduino commands.

Open Command Palette (Ctrl + Shift + P), and type 'Arduino' to filter the commands list in the command picker. You will see 'Arduino: Verify' and 'Arduino: Upload' commands. Note these also have keyboard shortcuts displayed. e.g. You can compile and Upload the open sketch using Ctrl + Alt + U.

The first time you do this for a project, you will be asked which .ino file is the main one (if you have more than one in the sketch). Once you select this it is added into the arduino.json configuration file (so you know where to go to change it if you change the main file later).

You may see a warning at the start of the compiler output, saying 'Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README.'. This means that each time you make a code change and go to upload again, the compiler has the recompile the entire project including all libraries, which can take a long time. To allow it to reuse the output from a previous compile and only recompile the files which have been changed, you need to define and output folder in the arduino.json file. This folder should not be under the project folder. You can add the following line to set the output to a folder named ArduinoOutput located alongside your project. Alternatively give a full path to a folder, maybe in your temp directory.

    "output""../ArduinoOutput",

This should get you to the point where you can open, edit, compile and upload sketches to your boards. But you may notice the code IDE still shows lots of errors in the code because the VSCode IntelliSense feature does not know where all the Arduino libraries are located. We need to add these to the workspace include paths:

Using the command palette, run the command: C/C++: Edit Configurations (UI)

This will open the UI to edit your C/C++ configuration. You can edit your win32 configuration, or create a new one. I decided to create a new one named 'arduino'.

In the 'Include Path' field, make sure all 3 of these paths are added in addition to the workspaceFolder item:

${env:userprofile}/AppData/Local/Arduino15/packages/**
${env:userprofile}/Documents/Arduino/libraries/**
C:/Program Files (x86)/Arduino/hardware/arduino/avr/libraries/**

That last one assumes you installed the Arduino IDE into the default location under C:\Program Files (x86)\Arduino. If you installed to a different location then change it as appropriate. Note the use of the userprofile environment variable to make the configuration work on any computer. These configuration files can be checked into source control so that your projects can be checked out and run quickly on different computers regardless of the logged in username.


My complete arduino configuration section looked like this:

        {
            "name""arduino",
            "includePath": [
                "${workspaceFolder}/**",
                "${env:userprofile}/AppData/Local/Arduino15/packages/**",
                "${env:userprofile}/Documents/Arduino/libraries/**",
                "C:/Program Files (x86)/Arduino/hardware/arduino/avr/libraries/**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compilerPath""C:\\Program Files (x86)\\mingw-w64\\i686-8.1.0-posix-dwarf-rt_v6-rev0\\mingw32\\bin\\gcc.exe",
            "cStandard""gnu18",
            "cppStandard""gnu++14",
            "intelliSenseMode""gcc-x64"
        }

Finally, open the command palette again and run the command C/C++: Select a configuration, and select the 'arduino' configuration. This should clear most of the reported 'problems' in the code IDE, and IntelliSense code completion suggestions should now be working. There are some exceptions to this however.

The Arduino IDE appears to treat all .ino files in a project directory as being part of the main .ino file. So any code split across different .ino files will be compiled by the Arduino IDE as if that code was all in one big file. However the VSCode IntelliSense environment does not do this. One way to avoid this problem is to only have a single .ino file in your project, and use the extension .cpp for any other code files. You will then have to add #include statements as required to include these .cpp files in other files which reference them, and you will no longer be able to access globals defined in the main .ino file from code in the .cpp files. It will make you write better code, forcing references to be passed to objects rather than being accessible everywhere via globals. But you may find projects published by others do not work with the IntelliSense feature in VSCode.

In some cases, libraries will have been written to be compatible with both Arduino and Windows/Linux C/C++ programs. These can cause problems because the VSCode IntelliSense environment does not realise it is running in an Arduino environment. So it tries to check the code against windows libraries which will not be on your Arduino project path. You can make it behave like an Arduino environment by adding the following line to the start of your main sketch .ino file:

#define ARDUINO 100

But I suggest commenting out the line before compiling as the Arduino IDE will set it's own define, which may be different to this. Better to just live with these reported include errors since they will not apply when the Arduino IDE compiles the code.

Some data types in my project were flagged as not defined by default in standard C/C++. These included:

  • byte
  • boolean
  • uint8_t/uint16_t/uint_32_t
If you want to avoid these being flagged as errors by IntelliSense then you can use bool in place of boolean, uint8_t in place of byte. Then to make IntelliSense aware of the uint types, I added the following #if defines which I use in code designed to be compatible with both Arduino and non-Arduino environments:

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#elif defined(ARDUINO)
#include "WProgram.h"
#else
#include <stdint.h>
#include <stdio.h>
#endif

I am not sure how much of this was really needed, as the IntelliSense appeared somewhat temperamental about which errors it reported. Sometimes if would just be complaining that included libraries referenced header files it could not find, but then it would suddenly report most of my file contained errors.

I was unable to get the serial monitor working under the Arduino extension. When I clicked the icon in the footer toolbar to open it, I saw the following error: .vscode\extensions\vsciot-vscode.vscode-arduino-0.3.2\out\node_modules\usb-detection\build\Release\detection.node is not a valid Win32 application.

I uninstalled the Arduino extension after I discovered the PlatformIO IDE extension, which appears to be working much better for me. If you have more success then please share in the comments.

Thursday 14 March 2019

A Complete Rebuild in Acrylic

It was always my plan to make my competition robot in transparent laser cut acrylic, so that the insides could be seen. Plus it just looks so nice! Having learned the art of designing and building a laser cut robot using 3 mm plywood I was now ready to re-cut the refined design in acrylic. The main modification I made was to shorten the legs by 15 mm. I had realised from driving my prototype that it really did not need such high ground clearance as the small wheels could not climb over anything but small obstacles. It was also a little unstable. I could get it to lift some of the wheels off the ground doing sharp turns at speed!

I added some engraving to various parts and also added holes for threading all the motor and servo wires through the legs. I had drilled these holes by hand on the wooden model. Now I knew where I wanted them I added them to the CAD model so these could be laser cut on the acrylic parts. I had already designed an acrylic mount for my main display and had this mounted on the wooden chassis.

With so many parts to cut I wanted to make optimum use of my acrylic sheet material. So I used an online SVG nesting tool to layout the parts. I prepared an SVG file of all the parts in Inkscape, including a containing rectangle required by the nesting tool to limit the area and shape to fit the parts into. I uploaded this to https://svgnest.com/ which generated a cutting layout for me to download.

An optimised parts layout for laser cutting

The complete set of laser cut acrylic parts ready to assemble

First I assembled the 4 steering legs. One snag I ran into is the inconsistency in thickness of acrylic sheets. A 3 mm sheet can vary in thickness between around 2.8 mm and 3.2 mm. This is due to the surface tension on the sheet when it is being cast, which results in a curved top surface dipping down towards the middle of the sheet. The sheet sizes you typically buy are cut from this larger cast sheet. Depending on which part of the cast sheet your piece was cut from you get different thicknesses across your piece. Some of my sheets exceeded 3 mm thickness towards one end, so some of my parts did not slot together as all my slots are cut at 3 mm wide. I had to re-cut a few parts with slightly wider slots. But I did not want to make all my slots wider by default, particularly the motor mounting plates which wobbled too much when the slots were not a tight fit. This an area which could do with some more refinement so that the rigidity of the motor mounts is not so dependent on tight tolerances. In the process of assembly I accidentally snapped one part in half and cracked another by over tightening the bolts holding the parts together. Acrylic is a much more brittle material than plywood and is easy to crack, especially around rectangular holes with sharp internal corners. I later modified some of my parts to increase the width of material around the outside edges of the slots from 3 mm to 4 mm to increase the strength of some parts. After re-cutting (plus a few spares) I had two completed Rocker Bogie arms.

The Rocker Bogie arms remade in acrylic with cable routing holes

As I assembled my robot using the new parts, I tried to extensively photo-document every step of the process. My plan is to write up a full build guide and possibly release a kit of parts for others to build their own rover from my design.

Some of the many photographs I took to document the assembly process

Another problem which came from switching to acrylic was that the plates did not grip each other like the plywood had. Some of my chassis body side plates were not staying together at the ends furthest from the bolts holding them in place. I had to add some additional T-slots and bolts to hold everything firmly together. I had designed a new front plate to mount a Pixy2 camera on, and this was not held on with anything more than the friction of the tabs in the slots. This would need more refinement for the competition as it was also going to be holding my Pi Noon attachment and I did not want that falling off mid-battle. For for now I could push it on and it looked good enough for me to shoot some photos. I set up my filming lights to provide some nice back lighting and took a few photos to try and capture the beauty of the acrylic design.

My final photo submitted for the Pi Wars 2019 program