Use Visual Studio Code for your Particle development needs


#26

Hrm, what I wrote above no longer seems to work. The thing I mentioned there, that the string “workspace” was oddly prepended to console lines, does not seem to happen anymore.

I am not sure if I changed something in my environment or shell, or maybe it was part of an update to VSCode or the particle-cli or backend build system since then.

So now, using the following tasks.json file, the warning & error parser should work:

{ 
"version": "0.1.0", 
"command": "particle", 
"isShellCommand": true, 
"args": [], 
"showOutput": "always", 
"echoCommand": true, 
"tasks": [ 
    { 
        "taskName": "compile", 
        "suppressTaskName": false, 
        "isBuildCommand": true,             
        "args": ["electron","${workspaceRoot}", "--saveTo", "${workspaceRoot}/firmware.bin"],
        // Use the standard less compilation problem matcher.
            "problemMatcher": {
                "owner": "cpp",
                "fileLocation": ["relative", "${workspaceRoot}"],
                "pattern": {
                    "regexp": "^(.*?):(\\d+):(\\d+):\\s+(warning|error|fatal error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                },
                "severity":"error"
            }
    }, 
    { 
        "taskName": "flash",             
        "suppressTaskName": false, 
        "isTestCommand": true, 
        "args": ["--usb", "firmware.bin"] 
    }    
]     
} 

This is almost exactly the same as this SO response for Makefiles, and the binding for F8 is also useful: https://stackoverflow.com/questions/30269449/how-do-i-set-up-vscode-to-compile-c-code

Here’s a screenshot of how the problems should be picked up by the problem matcher when running the build task:


Teaching Electron Programming
#27

oh wow, this is beautiful and extremely useful!
I can confirm it works in Ubuntu with VS Code 1.13.0.

You mentioned earlier that this would only work in cpp files, but I’m happy to see it works on ino files too :slight_smile:

Do you mind if I add this info to the hackster project?
thanks!
Gustavo.


#28

Sure, go for it.
It’s kind of nuts to think about how much time I spent and how much code I generated using Atom and the web IDE with little to no error parser. Now, I would not be able to live without it.


#29

Oh, I think the .ino/.cpp is another recent change in the Particle backend or particle-cli ; it used to report back error in Main.ino as Main.cpp, so the error parser went looking for a file that did not exist. But now ther eis name consistency, so it works.


#30

If you’re a Visual Studio user, I strongly recommend using Visual studio for your C++ work. You need to abstract the hardware stuff so you get to use all of Visual Studio features including the debugging etc. I’m guessing most of your time is spent in your logic rather than accessing the hardware (directly).

For anything more complicated than Blinky, this is what I do myself. It gives you a lot of opportunity to clean up your code and build good abstractions since you have a really good code editor with refactoring and debugger at your disposal. You can use include the GSL NuGet to get really good Code analysis and guidance as per the C++ Guidelines Support Library.

For example, here is my hardware abstraction interface (abstract class).

/*
Hal.h - Hardware Abstraction Layer for Arduino
Abstracting some basic functions of the Arduino Library
So as to make it possible to decouple Arduino "things"
from other classes, decoupling those classes from Arduino
Created by Shiv Kumar, 2014.
website: http://www.matlus.com
Released into the public domain.
*/
#pragma once

#ifndef Hal_h
#define Hal_h

class Hal {
protected:
public:
	Hal() {}
	virtual ~Hal() {}
	virtual void doPinMode(uint16_t pin, uint8_t mode) = 0;
	virtual void doDigitalWrite(uint16_t pin, uint8_t value) = 0;
	virtual void doAnalogWrite(uint16_t pin, int8_t value) = 0;
	virtual void attach(uint8_t pin) = 0;
	virtual void detach() = 0;
	virtual void write(int position) = 0;
	virtual int doConstrain(int numberToConstrain, int lowerBound, int upperBound) = 0;
	virtual int doMap(int value, int sourceMin, int sourceMax, int targetMin, int targetMax) = 0;
};

#endif

For my Visual Studio project and I have an implementation (descendant) of this class that uses the Console (std::cout). I have about 7 classes that I can design, develop and test to ensure my application works as expected. Then I simply move all these classes into the Wed IDE or Particle DEV. Of course I have an implementation of the Hal class called HalSpark that looks like this:

/*
  HalSpark.h - Implementation of the Hal abstract class
  where this implementation delegates to the Arduino
  library functions
  Created by Shiv Kumar, 2014.
  website: http://www.matlus.com
  Released into the public domain.
*/
#pragma once

#ifndef HalSpark_h
#define HalSpark_h
#include "Hal.h"
#include "spark_wiring.h"

class HalSpark : public Hal {
public:
  HalSpark();
  ~HalSpark();
  void doPinMode(uint16_t pin, uint8_t mode) override final;
  void doDigitalWrite(uint16_t pin, uint8_t value) override final;
  void doAnalogWrite(uint16_t pin, int8_t value) override final;
  void attach(uint8_t pin) override final;
  void detach() override final;
  void write(int position) override final;
  int doConstrain(int numberToConstrain, int lowerBound,
                  int upperBound) override final;
  int doMap(int value, int sourceMin, int sourceMax, int targetMin,
            int targetMax) override final;
};

#endif
#include "HalSpark.h"

HalSpark::HalSpark() {}

HalSpark::~HalSpark() {}

void HalSpark::doPinMode(uint16_t pin, uint8_t mode) {
  PinMode pMode = PinMode::OUTPUT;

  switch (mode) {
  case 0:
    pMode = PinMode::INPUT;
    break;
  case 1:
    pMode = PinMode::OUTPUT;
    break;
  case 2:
    pMode = PinMode::INPUT_PULLUP;
    break;
  }

  pinMode(pin, pMode);
}

void HalSpark::doDigitalWrite(uint16_t pin, uint8_t value) {
  digitalWrite(pin, value);
}

void HalSpark::doAnalogWrite(uint16_t pin, int8_t value) {
  analogWrite(pin, value);
}

void HalSpark::attach(uint8_t pin) {
  // No implementation for this yet.
  // Will need to include the Servo Library
}

void HalSpark::detach() {
  // No implementation for this yet.
  // Will need to include the Servo Library
}

void HalSpark::write(int position) {
  // No implementation for this yet.
  // Will need to include the Servo Library
}

int HalSpark::doConstrain(int numberToConstrain, int lowerBound,
                          int upperBound) {
  return constrain(numberToConstrain, lowerBound, upperBound);
}

int HalSpark::doMap(int value, int sourceMin, int sourceMax, int targetMin,
                    int targetMax) {
  return map(value, sourceMin, sourceMax, targetMin, targetMax);
}

I use a similar thing for sensors and the like.

If the VS Code solution here works, then that’s good/preferable I would think.


#31

I’ve gotten just about everything to work by using the tasks.json file that @gusgonnet posted here: https://www.hackster.io/gusgonnet/use-visual-studio-code-for-your-particle-development-needs-9e23bc

However, I do need to add #include “application.h” to the top of my main.cpp. I am still using the default c_cpp_properties.json and do not know what paths to include so that VScode will know where to look for application.h. Does anyone know how where I can go to find that or how I should set up my c_cpp_properties.json so that I don’t get the squiggly warnings?


#32

It’s possible but a super huge pain to get all of the paths correct. You’ll need both the system firmware headers and the headers from the gcc-arm toolchain.

This post shows how to do it with Visual Studio 2017 15.5 preview, which I don’t actually recommend that you try to switch to, however there are enough similarities to VS Code that you should be able to make the same sort of settings and get rid of all of the squiggly warnings in Code.


#33

Hey Alex, I do not know how to do that, so I ignore the warnings… not ideal I admit.
Good luck and let us know if you figured it out with the post above from Rick.
Gustavo.


#34

I think that I need to include the path where “application.h” is on my computer, but I don’t actually know where that is. I feel like that should be something easy, but I’m having a hard time figuring that out.


#35

It’s not just application.h. You need everything that’s included from application.h or Particle.h. Also several of the gcc-arm header files. Also some defines and compiler options. For a Photon using 0.7.0, it’s:

{
			"name":"Particle photon",
			"includePath":[
				"${workspaceRoot}\\..\\firmware\\communication\\src",
				"${workspaceRoot}\\..\\firmware\\dynalib\\inc",
				"${workspaceRoot}\\..\\firmware\\hal\\inc",
				"${workspaceRoot}\\..\\firmware\\hal\\shared",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\api",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\include",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\libraries\\crypto",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\WWD\\include\\",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\platform\\GCC\\",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\platform\\include\\",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\security\\BESL",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\security\\BESL\\crypto",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\security\\BESL\\host\\WICED\\",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\security\\BESL\\include",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\photon\\wiced\\security\\BESL\\supplicant\\",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\stm32",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\stm32f2xx",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\CMSIS\\Device\\ST\\Include",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\CMSIS\\Include",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\SPARK_Firmware_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_StdPeriph_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_Device_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_Host_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_OTG_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\shared\\STM32\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\shared\\inc",
				"${workspaceRoot}\\..\\firmware\\services\\inc",
				"${workspaceRoot}\\..\\firmware\\system\\inc",
				"${workspaceRoot}\\..\\firmware\\user\\inc",
				"${workspaceRoot}\\..\\firmware\\wiring\\inc",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\arm-none-eabi",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\arm-none-eabi\thumb",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\ext",
				"${workspaceRoot}\\..\\gcc-arm\\lib\\gcc\\arm-none-eabi\\5.3.1\\include",
				"${workspaceRoot}\\src"
			],
			"defines":[
				"BOOTLOADER_SDK_3_3_0_PARTICLE",
				"DFU_BUILD_ENABLE",
				"INCLUDE_PLATFORM=1",
				"LOG_INCLUDE_SOURCE_INFO=1",
				"LOG_MODULE_CATEGORY=\"\\\"app\\\"\"",
				"MODULAR_FIRMWARE=1",
				"MODULE_DEPENDENCY2=0,0,0",
				"MODULE_DEPENDENCY=4,2,207",
				"MODULE_FUNCTION=5",
				"MODULE_INDEX=1",
				"MODULE_VERSION=5",
				"PARTICLE=1",
				"PARTICLE_DCT_COMPATIBILITY",
				"PARTICLE_USER_MODULE",
				"PLATFORM_ID=6",
				"PLATFORM_NAME=photon",
				"PLATFORM_THREADING=1",
				"PRODUCT_FIRMWARE_VERSION=65535",
				"PRODUCT_ID=6",
				"RELEASE_BUILD",
				"SPARK=1",
				"SPARK_PLATFORM",
				"SPARK_PLATFORM_NET=BCM9WCDUSI09",
				"START_DFU_FLASHER_SERIAL_SPEED=14400",
				"START_YMODEM_FLASHER_SERIAL_SPEED=28800",
				"STM32F2XX",
				"STM32_DEVICE",
				"SYSTEM_VERSION_STRING=0.7.0",
				"USBD_PID_CDC=0xC006",
				"USBD_PID_DFU=0xD006",
				"USBD_VID_SPARK=0x2B04",
				"USER_FIRMWARE_IMAGE_LOCATION=0x80A0000",
				"USER_FIRMWARE_IMAGE_SIZE=0x20000",
				"USE_STDPERIPH_DRIVER",
				"_GNU_SOURCE",
				"_WINSOCK_H"
			],
			"compilerSwitches":"-Wall -Wno-error=deprecated-declarations -Wno-switch -Wundef -fcheck-new -fdata-sections -ffunction-sections -fmessage-length=0 -fno-builtin-free -fno-builtin-malloc -fno-builtin-realloc -fno-exceptions -fno-rtti -fno-strict-aliasing -mcpu=cortex-m3 -mthumb -std=gnu++11 ",
			"intelliSenseMode":"linux-gcc-arm"
		},

#36

I was able to remove the red underline from arduino.h by adding this as one of the items in the “configurations” array in my c_cpp_properties.json

"includePath": [
                "/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino"
            ]

Now just Arduino.h is underlined because it cannot open “avr/pgmspace.h”


#37

Thanks for this info, do you have it for an Electron? I’m trying to get VS code building electron offline.

To help others… I have been able to get OpenOCD and GDB fired up and connecting through VScode.

Here is my launch.json file, I’d be keen for any updates on this. your value may vary.

{
            "name": "GDB OpenOCD - particle",
            "type": "cppdbg",
            "request": "launch",
            "targetArchitecture": "arm",
            "cwd": "${workspaceRoot}",
            "program": "${workspaceRoot}\\path\\to\\system-part1\\platform-10-m\\system-part1.elf", 

            "debugServerPath": "C:/openocd-0.10.0/bin-x64/openocd.exe",
            "debugServerArgs": "-f interface/stlink-v2.cfg -f target/stm32f2x.cfg  -c \"telnet_port 4444\"",// -c init -c \"reset init\" ", //particle
            
            "filterStderr":"true",
            "filterStdout":"false",
            "serverStarted": "stm32f2x.cpu: hardware has 6 breakpoints, 4 watchpoints",
            "serverLaunchTimeout": 5000,


            "MIMode": "gdb",
            "miDebuggerPath" : "C:/Program Files (x86)/GNU Tools ARM Embedded/5.3 2016q1/bin/arm-none-eabi-gdb.exe",
            
            
            "setupCommands": [
                { "text": "-target-select remote localhost:3333", "description": "Connect to target",     "ignoreFailures": false },
                { "text": "-file-exec-and-symbols C:/path/to/source/firmware/build/target/system-part1/platform-10-m/system-part1.elf", "description": "Executable location",     "ignoreFailures": true },
                //{ "text": "-file-exec-and-symbols ${workspaceRoot}\\build\\target\\system-part1\\platform-10-m\\system-part1.elf", "description": "Executable location",     "ignoreFailures": true },
                
                //{ "text": "-interpreter-exec console \"monitor endian little\"", "ignoreFailures": false },
                { "text": "-interpreter-exec console \"monitor reset\"", "ignoreFailures": false },
                { "text": "-interpreter-exec console \"monitor halt\"", "ignoreFailures": false },
                { "text": "-interpreter-exec console \"monitor arm semihosting enable\"", "ignoreFailures": false },
                { "text": "-target-download", "description": "flash target", "ignoreFailures": false },
                { "text": "-enable-pretty-printing", "description": "Enable pretty-printing for gdb",     "ignoreFailures": true },



            ],
            
            "logging": {
                "engineLogging": true,
                "traceResponse": true
            },
    
        },

This starts OpenOCD, and when started launches GDB which connects to the OpenOCD session.

To download the code with GDB you need to turn off the write protection on the uC first, I did this with ST-link, it fails otherwise.

I haven’t been able to get it to connect unless it is in DFU mode, but I was using the LED on D7 so that maybe the cause. I’m still testing it out.


#38

Yes, these are the include paths for the Electron 0.7.0:

		{
			"name":"Particle electron",
			"includePath":[
				"${workspaceRoot}\\..\\firmware\\communication\\src",
				"${workspaceRoot}\\..\\firmware\\dynalib\\inc",
				"${workspaceRoot}\\..\\firmware\\hal\\inc",
				"${workspaceRoot}\\..\\firmware\\hal\\shared",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\electron",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\stm32",
				"${workspaceRoot}\\..\\firmware\\hal\\src\\stm32f2xx",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\CMSIS\\Device\\ST\\Include",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\CMSIS\\Include",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\SPARK_Firmware_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_StdPeriph_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_Device_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_Host_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\STM32F2xx\\STM32_USB_OTG_Driver\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\MCU\\shared\\STM32\\inc",
				"${workspaceRoot}\\..\\firmware\\platform\\shared\\inc",
				"${workspaceRoot}\\..\\firmware\\services\\inc",
				"${workspaceRoot}\\..\\firmware\\system\\inc",
				"${workspaceRoot}\\..\\firmware\\user\\inc",
				"${workspaceRoot}\\..\\firmware\\wiring\\inc",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\arm-none-eabi",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\arm-none-eabi\thumb",
				"${workspaceRoot}\\..\\gcc-arm\\arm-none-eabi\\include\\c++\\5.3.1\\ext",
				"${workspaceRoot}\\..\\gcc-arm\\lib\\gcc\\arm-none-eabi\\5.3.1\\include",
				"${workspaceRoot}\\src"
			],
			"defines":[
				"DFU_BUILD_ENABLE",
				"INCLUDE_PLATFORM=1",
				"LOG_INCLUDE_SOURCE_INFO=1",
				"LOG_MODULE_CATEGORY=\"\\\"app\\\"\"",
				"MODULAR_FIRMWARE=1",
				"MODULE_DEPENDENCY2=0,0,0",
				"MODULE_DEPENDENCY=4,2,207",
				"MODULE_FUNCTION=5",
				"MODULE_INDEX=1",
				"MODULE_VERSION=5",
				"PARTICLE=1",
				"PARTICLE_USER_MODULE",
				"PLATFORM_ID=10",
				"PLATFORM_NAME=electron",
				"PLATFORM_THREADING=1",
				"PRODUCT_FIRMWARE_VERSION=65535",
				"PRODUCT_ID=10",
				"RELEASE_BUILD",
				"SPARK=1",
				"SPARK_PLATFORM",
				"SPARK_PLATFORM_NET=UBLOXSARA",
				"START_DFU_FLASHER_SERIAL_SPEED=14400",
				"START_YMODEM_FLASHER_SERIAL_SPEED=28800",
				"STM32F2XX",
				"STM32_DEVICE",
				"SYSTEM_VERSION_STRING=0.7.0",
				"USBD_PID_CDC=0xC00A",
				"USBD_PID_DFU=0xD00A",
				"USBD_VID_SPARK=0x2B04",
				"USER_FIRMWARE_IMAGE_LOCATION=0x8080000",
				"USER_FIRMWARE_IMAGE_SIZE=0x20000",
				"USE_STDPERIPH_DRIVER",
				"_GNU_SOURCE",
				"_WINSOCK_H"
			],
			"compilerSwitches":"-Wall -Wno-error=deprecated-declarations -Wno-switch -Wundef -fcheck-new -fdata-sections -ffunction-sections -fmessage-length=0 -fno-builtin-free -fno-builtin-malloc -fno-builtin-realloc -fno-exceptions -fno-rtti -fno-strict-aliasing -mcpu=cortex-m3 -mthumb -std=gnu++11 ",
			"intelliSenseMode":"linux-gcc-arm"
		}

#39

That’s great thankyou!


#40

2018 update

Starting with version 1.27.2, Visual Studio Code deprecated the use of tasks 0.1.0 and hence the scripts described here did not work anymore. I fixed that now.

Thanks
Gustavo.


#41

Hello, @gusgonnet
Thank you for the nice instruction.
I followed everything in the tutorial and was able to compile by pressing ctrl+shift+b however it does not proceed to flash the firmware.bin to the board.
I cannot select “flash” per this instruction.

Ctrl+P, then entering task and space, then selecting flash from the list

Following is the output from the terminal when I press ctrl+shift+B

running command> particle compile electron D:\Particle_WS --saveTo D:\Particle_WS/firmware.bin

Compiling code for electron

Including:
blink.ino
attempting to compile firmware
downloading binary from: /v1/binaries/5c780131c6c1a910e76a321c
saving to: D:\Particle_WS/firmware.bin
Memory use:
text data bss dec hex filename
4380 116 1492 5988 1764 /workspace/target/workspace.elf

Compile succeeded.
Saved firmware to: D:\Particle_WS\firmware.bin


#42

glad you like it! what I see there potentially wrong is the / instead of the \ before the word firmware

could it be?


#43

Just for the records: I don’t quite know how @gusgonnet’s setup compares to it but there is the officially supported Particle Workbench that’s also based on VS Code but probably provides some extra stuff.

Gustavo deserves all credit for bringing this out before Particle - great job :+1: and this should not diminishing his efforts in any way.

However Workbench provides auto downloading of device OS firmware and toolchain for all platforms and multiple versions, platform switching, library management, debugging and intellisense are a bit more mature on the official version.
It’s available as offical extension in VS Code without the need for any leg work on your side :wink:


#44

thank you and that is correct!

You can get the Particle Workbench here:


#45

2019 UPDATE
Particle released Particle Workbench, which runs on top of VS Code.
It is much better than what I described in this tutorial few years ago, so I’d suggest you use it instead!
Here’s the announcement.

Cheers,
Gustavo.