My previous post Visual Studio Code for Arduino Basics left me with just one itch unscratched. I'd kind of come to love over-the-air (OTA) programming. My robotics project (using an ESP8266) communicates over WiFi, the "base station" app was all set up and communications using JSON were working perfectly. If I needed to deploy updated robot code I could do it without the hassle of cables.
So, does OTA work out of the box with Visual Studio Code? Yes! And no. I wished there was a simple blog post to explain it, so I've had to write it myself. Without further ado ...
I draw your attention to loop(). ArduinoOTA.handle(); must be executed on every loop. It doesn't matter if it runs a thousand times per second, but if it doesn't get run for a while, the OTA process will get tired of waiting for the Arduino to accept its upload and the process will fail. So make sure you understand the difference between Blink and Blink Without delay().
So, does OTA work out of the box with Visual Studio Code? Yes! And no. I wished there was a simple blog post to explain it, so I've had to write it myself. Without further ado ...
Hardware Requirements
We're assuming here you're using Arduino hardware that has WiFi. Mine is an ESP8266. Whatever hardware you have, I recommend you go back to the basic Arduino IDE and do some OTA programming from there. Just be sure the process works before you add the complexity of Visual Studio Code to the problem.
Networking
So that we can send code over OTA we have to know the device's IP address. So fix the address in code or do it with your WiFi router via a DHCP reservation.
OTA Code
If you haven't used OTA before you need this little primer: OTA is Arduino code you run on your Arduino that allows it to receive a new program over the network. So the sketch you upload has to have the Arduino OTA code in it, and it must work perfectly! If you make a mistake in your sketch, you'll most likely cause the OTA code not to work correctly and you won't be able to do any more OTA uploads!
Not only do you need to understand what the OTA code is doing, you need to ensure you don't get in its way or it won't work. If you implement OTA code with a poorly written Blink sketch, you'll break the OTA. So if you do an OTA upload and your device doesn't do what you want, and then it won't accept another upload, you've got an error in your code that has killed the OTA function. So beware!
This is the code I'm using. Apologies to the original author, I don't remember where I found it. But it's been reliable as hell.
Not only do you need to understand what the OTA code is doing, you need to ensure you don't get in its way or it won't work. If you implement OTA code with a poorly written Blink sketch, you'll break the OTA. So if you do an OTA upload and your device doesn't do what you want, and then it won't accept another upload, you've got an error in your code that has killed the OTA function. So beware!
This is the code I'm using. Apologies to the original author, I don't remember where I found it. But it's been reliable as hell.
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#ifndef STASSID
#define STASSID "YOUR_AP"
#define STAPSK "YOUR_PASSWORD"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
void setup() {
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
ArduinoOTA.handle();
// Blink-blink-blink
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(900); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(100); // wait for a second
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(100); // wait for a second
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(100); // wait for a second
}
I draw your attention to loop(). ArduinoOTA.handle(); must be executed on every loop. It doesn't matter if it runs a thousand times per second, but if it doesn't get run for a while, the OTA process will get tired of waiting for the Arduino to accept its upload and the process will fail. So make sure you understand the difference between Blink and Blink Without delay().
OTA Project from Scratch
On to the Visual Studio Code project. I'm using a Mac:
- Open Visual Studio Code
- From the File menu, select Open
- Create a folder for the project and Open. I'm creating OTA-Test-3 on my desktop.
- We need to open the Command Palette. On my Mac it's Shift-Command-P
- Type arduino and select Arduino: Initialize. You'll need to name your main code file. I just accept app.ino.
If we stop and look at the project we have just an Arduino code file (app.ino) and the configuration file .vscode/arduino.json. Now is a good time to take the OTA code and paste it into your app.ino file. You'll need to update the STASSID and STAPSK for your WiFi network.
We need to configure the board. From the footer (blue) click <Select Board Type> and pick your board from the Selected Board: list. You now get a set of other options. Change any other options that you need but notice the third option without a label that has OTA selected, great huh? No. Actually it's not. We'll fix it in a minute.
You might also notice we now have a build configuration file called c_cpp_properties.json. This is useful to know.
We're missing one last thing. The arduino.json file doesn't have an entry for the port. You can add it manually or hit <Select Serial Port> in the footer to add it. Once you have the port line in arduino.json, change the value to the IP address of your device.
{
"sketch": "app.ino",
"board": "esp8266:esp8266:espduino",
"configuration": "ResetMethod=v1,UploadTool=espota,xtal=80,vt=flash,exception=legacy,ssl=all,eesz=4M2M,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200",
"port": "192.168.1.13"
}
You'll notice the configuration line contains a number of key:value pairs. Among these is UploadTool=espota which, I would assume, is a good thing since I want to send my ESP an OTA update. But if you try to upload now, it fails. Oddly, we need to change the OTA option back to Serial. This has the effect of changing the configuration line in arduino.json to include UploadTool=esptool. OK, now we're ready.
Programming and Reprogramming
Checklist:
- Arduino already has OTA code uploaded over Serial
- Arduino has fixed IP address
- Visual Studio Code correctly configured with:
- Serial (not OTA)
- arduino.json has "port": "[arduino's-ip-address]"
- app.ino file contains OTA code that was already uploaded to Arduino
When you make your Arduino code file the active window, Visual Studio Code will give you an Upload icon at the top right. Hit it!
Beautiful!
When Things Go Wrong
The point of being so laborious about a three-step-proceedure is to make sure it works! But when it doesn't, here's what I do...
Rename the code file in your Visual Studio Code project. The Arduino IDE likes the main .ino file to have the same name as the folder containing it. So for my OTA-Test-3 project, I need to have OTA-Test-3.ino as my main code file. You can now open Arduino IDE and use the code file you created to do an OTA upload. Remember you can also select use external editor in the Arduino IDE preferences so you can use Visual Studio Code as the editor and Arduino IDE as the uploader. If you can upload from the Arduino IDE but not Visual Studio Code, you're just chasing down a VS.Code configuration issue. This is how I found the UploadTool=esptool issue. If you can't upload from either VS.Code or Arduino IDE, it's your code on the device or your networking.
Thanks for sharing! I like it and I recommend to check HC-SR04 and Arduino tutorial. it's very useful...
ReplyDeleteThank you soo much for this. Have battled this for months! Got it working ok with Arduino IDE long time ago, but never before in visual studio code. This opens up so much potential =)
ReplyDelete