Setting up Visual Studio Code environment for Raspberry Pi development

I finally found some time recently to start putting together my P4wnP1 A.L.O.A. As I decided to put an OLED display on the Raspberry Pi, I also wanted to develop the OLED UI by myself. I initially wanted to set up the Remote SSH editing plugin, but I immediately started getting errors Unsupported architecture: armv6l. This raised two inconveniences that I'll attempt to solve in this blog post. The first one is keeping the files on the Raspberry in sync with my development environment, and the second one is debugging the code.

The setup

Before I jump to the solutions for the problems listed above, there is some initial setup that comes in handy when preparing the Raspberry Pi for the development. This includes connecting raspberry pi and the development machine in some way so that you can freely access a headless Raspberry system. This can be done in many different ways, but I used a USB cable and followed the steps described here. The basic idea is to set up the Raspberry Pi to act as a USB-connected wireless card. This way the host machine sees an interface through which it can SSH or SCP to the Raspberry Pi.

Warning: Security malpractice

There are several really bad practices listed in the article continuation. Here is the list of things never to do in production:

  • Don't ever use sshpass - it directly shows your password

  • Don't ever skip checking the server identity - it can easily be MITM-ed

  • Don't ever keep debugging code in the production deployment - someone might get more information than they are supposed to

Always assess and grade your risks, it can turn out that in your case these items are too risky, even for the development environment (for instance if they are exposed on the Internet).

File Synchronization (File Deployment)

As I'm assuming the Raspberry Pi is running headlessly (there is no monitor, keyboard, and mouse connected to it) and I wanted to develop in Visual Studio Code (and not Vim for instance) the VS Code needs to be set up on the development machine - the only machine running a desktop system (I'm using Kali). As I couldn't set up the remote development plugin, I decided to somehow synchronize the files.

The idea is simple, run scp on each file save to copy the entire code to Raspberry. For this, I used the "Run on save" plugin and set it up to run the appropriate scp command.

   "python.pythonPath": "/usr/bin/python3",
   "runOnSave.statusMessageTimeout": 3000,
   "runOnSave.commands": [
       "match": ".*",
       "notMatch": "",
       "command": "sshpass -p 'toor' scp -o 'StrictHostKeyChecking=no' -r ${workspaceFolder}/* root@",
       "runIn": "backend",
       "runningStatusMessage": "Pushing project to raspi...",
       "finishStatusMessage": "Done."
}Code listing 2: settings.json

The VS code settings in the previous code listing set up the environment with the following functionalities:

  • The sync happens when any file from the folder is saved

  • The secure copy command is initialized with the password so that the user doesn't need to input it on every save

  • The secure copy sets the option to skip checking for the server host key. This could be better handled by adding the Raspberry Pi key to the known hosts, but this is good enough for the development server.

  • The source and destination folders are set


The debugging can be easily achieved using the ptvsd library. It should first be installed using pip on the Raspberry Pi, then it can be initialized in the project. To turn on debugging, place the code in the following listing somewhere at the beginning of your project code.

import ptvsd
ptvsd.enable_attach(('', 3000))
#ptvsd.wait_for_attach() # Enable this if you want the code to stop here and wait for the debugger attachCode listing 4: Turn on debugging

If the third line is commented out, the code execution will turn on the debugging interface but the execution will move forward. You can still attach to a long-running code but the debugger will break somewhere later in your code execution cycle, depending on the code.

One more thing important for the debugging is to properly set the remote and local paths, otherwise, the breakpoints set in the VS Code will show as disabled and won't be hit properly. The detailed debugging setup is described on the VS Code remote debugging page here.

Debugging callbacks (addendum)

This section wasn't in the original post, but after it was published I found out the breakpoints don't get hit in the callbacks of some libraries using the previously described method. As an example, the case in point library was gpiozero library and its when_pressed callback. As it is extremely important to be able to debug the actions, I desperately wanted to find a workaround.

The workaround is to use these steps to always break into the debugger:

  • Put the code snippet below in the callback you want to debug

  • Turn off waiting for the debugger to be attached (see above which line to comment out)

  • Run the program - the code will run freely but the debugger will have the ability to be attached to it

  • Attach the debugger

  • Perform the action that triggers the callback (for me it was to press the OLED joystick down)

  • Check the debugger - it should be paused on the next instruction

import ptvsd
ptvsd.break_into_debugger()Code listing 6: Debugging callbacks

Check the following image how it looks after the debugger code was triggered. Notice how you don't need to set any breakpoints in the debugger, the code will jump to the debugger itself.


These techniques will enable you to program the code for your Raspberry Pi more seamlessly, but you should mind several security issues before making to production:

  • Update the user password to something more secure

  • Don't use the new password with sshpass

  • Remove any use of ptvsd references and code

VS Code, P4wnp1, Raspberry Pi