Copilot AI can assist you code your Arduino project, but sometimes it gets things wrong. This tutorial explores ways to help GitHub Copilot generate better code suggestions for you!
Estimated ReadTime:Part 1: 15 min; Part 2: 30 min; Part 3: Varied
Estimated Cost: None
IntroductionThis guide provides tips, tricks, and other ideas to leverage GitHub Copilot successfully. This is part 2 of the Arduino with GitHub Copilot tutorial series, but no hardware or Arduino environment is required to apply the presented approaches.
More information about the AI tools:
GitHub Copilot: An AI code assistant built on OpenAI Codex.
OpenAI Codex: An “AI system that translates natural language to code.”
OpenAI: An AI research and deployment company and creators of ChatGPT. Their mission is to "ensure that artificial general intelligence benefits all of humanity."
PrerequisitesThis is the second publication in theArduino with GitHub Copilotseries. It assumes you have prior experience with VS Code and the GitHub Copilot extension. For help getting started with these tools, please view the first tutorial in the series: Accelerate your Arduino projects with GitHub Copilot AI.
You will need:
- GitHub account
- GitHub Copilot subscription:FREE 30-Day Trial
- VS Code
- GitHub Copilot extension for VS Code
The following was run on Windows 11.
Part 1: Challenges and solution ideasSolution ideas below are based on experience and are not guaranteed. If you have discovered successful solutions to any of these challenges (or others), please feel free to share in the comments!
Estimated Read Time: 15 min
Challenge 1: Copilot suggestions are repetitive or stagnantExample:
You wrote some code but changed your mind and decided to remove it. Now Copilot will only suggest what you just removed. In the image below, the suggested logic block had been written, but later deleted. Copilot keeps suggesting the deleted code block.
Solution Ideas:
1. Disable and re-enable Copilot.
- Select 'Deactivate Copilot' in the blue toolbar.
- Select either 'Disable Globally' or 'Disable for cpp'.
- Select 'Activate Copilot' in the blue toolbar.
2. Move to a different section of code or different file to work. Return later to the original section that had the repetitive Copilot suggestion and retry.
Why?
The underlying AI model operates in a context that acts like short-term memory. Copilot will use what it has previously seen you type to predict what you may want to type next, even if you have deleted the prior content. Taking a break from Copilot or providing it completely different input has been found to help provide fresh suggestions.
Challenge 2: Copilot does not provide inline suggestion alternativesExample:
You begin writing a comment and a completion suggestion appears. However Alt + [
or Alt + ]
does not provide alternatives. In the image below, toggling Alt + [
and Alt + ]
does nothing.
Solution Ideas:
1. Open the GitHub Copilot window for expanded suggestions.
- Select
Ctrl + Enter
to open the GitHub Copilot window.
2. Consider the solution ideas in Challenge 1: Copilot suggestions are repetitive or stagnant.
Why?
It is not abnormal for Copilot to provide only one inline suggestion. However, other times the same comment or code input could provide multiple different suggestions. The GitHub Copilot window shows expanded solutions that could help give you more ideas. If the suggestions still seem stagnant, try the solution ideas from Challenge 1: Copilot suggestions are repetitive or stagnant.
Challenge 3: Copilot suggestions appear instead of IntelliSenseExample:
You try to type a variable name, but a Copilot suggestion appears...
instead of the IntelliSense dropdown.
Solution Ideas:
1. Confirm all required libraries are included at the top of the file.
2. Confirm prior code is syntactically complete (e.g., a ;
ends the prior statement.)
3. Type the known API data member slowly.
4. Prior to the character that should cue IntelliSense (e.g., a .
), decline the Copilot suggestion.
- Delete the cuing character.
- Select
Esc
to decline the Copilot suggestion.
- Type the cuing character.
5. If IntelliSense still does not appear, disable Copilot. Re-enable Copilot once IntelliSense required coding is complete. See Solution Idea "1. Disable and re-enable Copilot." in Challenge 1: Copilot suggestions are repetitive or stagnantfor further instruction.
Why?
This can occur intermittently and is not fully understood. Copilot suggestions have been seen to take priority over IntelliSense if prior code is incomplete or code is typed too quickly. However, the most assured way to give IntelliSense priority may be to temporarily disable Copilot.
Challenge 4: Copilot provides incorrect or "made up" codeExample:
You begin to write code and Copilot provides a suggestion for a function or macro that is not part of the included library API. In the image below, the accepted code suggestions were "hallucinated" by Copilot.
Solution Ideas:
1. Use a different approach for the comment or code input (also called the prompt
). See Part 2: Advanced approachesbelow for ideas.
2. Confirm all required libraries are included at the top of the file.
3. Use IntelliSense, which will provide accurate suggestions. See Challenge 3: Copilot suggestions appear instead of IntelliSense for suggestions on how to give IntelliSense priority over Copilot.
4. Look at produced logs from compilation or run time to identify possible errors and recommendations. Manual fixes to the code may be required.
Compilation log examples:
- The log below shows the given accelerometer library is not found for the specified hardware. One solution is to be more specific in the comment input requesting to include the library. Alternatively, the correct library may need to be manually looked up and included.
- The log below shows certain functions and macros used in the code have not been defined. One solution is to remove the unrecognized code until the correct API is referenced.
Run time log examples:
- The log below does not show any errors, but the code does not produce the expected user experience. The user can analyze the log output, coded logic, and manually correct the code to produce the desired output.
5. Use official documentation to identify the correct library, API, or code usage.
Why?
This could be for a few reasons. Copilot may not have enough context from prior comments or code to understand what you are trying to do, the comment input may have too many requirements for Copilot to precisely follow, or Copilot could be "hallucinating" (making up an API that does not exist). Copilot is providing suggestions based on what it thinks you may write next, but it can be wrong. Checking logs for errors, testing, and manually fixing code may still be required.
Part 2: Advanced approachesIf Copilot isn't suggesting what you hoped or expected, it may not understand want you want based on the comment or code input typed. This section will look at different tips for writing Copilot input, also known as prompts. Prompts and prompt engineering will be briefly discussed at the end.
Estimated Read Time: 30 min
Tip 1: Confirm all required prior steps are included in the codeExample:
The image below contains no prior information on the API to use. As a result, Copilot will provide a reasonable suggestion based on the existing code.
New approach:
Add a prompt to include the accelerometer library. This will provide more context and guidance to Copilot so it can predict the code you may want to write.
The only change made in the image below was to include the correct library. Copilot now provides a more accurate suggestion.
Why?
The underlying AI model, Codex, has been trained on millions of lines of public code. Additionally, Copilot uses code and comments previously typed to create suggestions. As a result, the more context you can give Copilot earlier and along the way, the better suggestions it can provide.
Tip 2: Give the comment more detailExample:
The image below contains a comment that is too vague. Copilot will make a best guess at what library you want to include.
New approach:
Update the prompt to be more specific. This could take a few attempts to get right. For example, if writing a prompt to include a library for the Arduino Nano, specify which type of Arduino Nano.
Why?
This added detail is important because different hardware may require different libraries. Giving additional information helps Copilot understand what you are trying to accomplish.
Example:
The code and image below show a custom log()
function defined. However, Copilot does not know to use it in place of the standard Serial.println()
function.
// Include the accelerometer library for the Arduino Nano RP2040 Connect
#include <Arduino_LSM6DSOX.h>
#define PRINT_LOG 1
#define PRINT_ERROR 2
void log(int log_level, const char *str) {
if (log_level == PRINT_LOG) {
Serial.print("LOG: ");
} else if (log_level == PRINT_ERROR) {
Serial.print("ERROR: ");
}
Serial.println(str);
}
New approach:
Provide an example to Copilot on how the log()
function should be used. This will help Copilot understand the function and provide suggestions for it.
Additionally, Copilot correctly suggests how to use the log()
function in a possible ERROR case.
Why?
Adding a usage example for a function provides Copilot extra context. By giving Copilot a prompt that includes both a comment and the expected resulting code, Copilot can more accurately guess the desired code for similar comment prompts.
Tip 4: Break the task down into smaller stepsExample:
In the image below, a single comment is used to describe the behavior of an entire desired accelerometer application. There are multiple requirements and Copilot does its best to suggest what code to write.
However, the suggestion contains a few inaccuracies in the code. (1) There is no tiltDetected()
function in the API. (2) The logging does not occur once at each state change -- it only occurs on the first instance of each state.
On a re-write of the comment, Copilot adds one more inaccuracy. (3) It no longer blinks 10 times per second when tilted.
Copilot is having a hard time understanding all the logic that needs to be implemented to meet the requirements. This is a signal that the single prompt should be broken down into smaller steps.
New approach:
1. One sub-step is to detect tilt. Break out this step from the original prompt and create a separate prompt comprising only the function signature.
The suggestion is not accurate. Copilot suggests a "hallucinated" API function for this and does not implement the logic. However, by applying steps as seen in the first tutorial along with tips from this guide, the code can be correctly created with the assistance of Copilot.
bool tiltDetected()
{
float x, y, z;
// read the accelerometer
IMU.readAcceleration(x, y, z);
// check if the accelerometer is tilted
if (abs(x) > 0.5 || abs(y) > 0.5) {
return true;
}
else {
return false;
}
}
2. Within the loop()
function, a comment prompt to detect tilt is added and Copilot correctly suggests the new function tiltDetected()
.
The original comment prompt is then rewritten to provide Copilot the remaining requirements. In reviewing the suggestion, the developer can observe the variable tiltState
will be needed. This is added at the beginning of loop()
.
Copilot correctly understands to check for the state change and log only once at each change. However, it misses the LED.
3. The LED blink and the state change logging can be separated into two sub-steps. Break apart the single large comment prompt into two separate smaller prompts. The tiltState
variable should also have global scope, so it is moved out of the loop()
function.
This suggestion is correct and upon accepting the suggestion the application is now complete.
Why?
Writing a single comment prompt to cover a large task may be too general or provide too much information for Copilot to understand. Breaking the task down into smaller steps will not only help Copilot understand the desired behavior, but it will make you a better programmer!
The separate steps in this application consisted of: (1) Detect tilt, (2) Blink LED, and (3) Log state change. Each step is a smaller task that can be completed independently. This is good practice for working through any programming problem.
Below is the completed code application based on the steps above. However, it can still be improved upon. For example, it could use an enum for the tiltState
instead of a bool. This would allow for a neutral default tiltState
and more intuitive state naming. Also, the LED arguably only blinks 5 times per second, not 10. Adjust the delay value to your liking!
// include the accelerometer library for the Arduino Nano RP2040 Connect
#include <Arduino_LSM6DSOX.h>
bool tiltState;
void setup() {
// put your setup code here, to run once:
// initialize the serial port
Serial.begin(115200);
// wait for the serial port to open
while (!Serial);
// initialize the accelerometer
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
// initialize the LED
pinMode(LED_BUILTIN, OUTPUT);
// Create a default tilt state variable of false
tiltState = false;
}
bool tiltDetected()
{
float x, y, z;
// read the accelerometer
IMU.readAcceleration(x, y, z);
// check if the accelerometer is tilted
if (abs(x) > 0.5 || abs(y) > 0.5) {
return true;
}
else {
return false;
}
}
void loop() {
// put your main code here, to run repeatedly:
// Detect tilt on the Nano RP2040 Connect. Store the current detected tilt state in a local
// variable.
bool tilt = tiltDetected();
// While the state is tilt, blink the LED 10 times per second.
// While the state is not tilt, turn off the LED.
if (tilt) {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
}
else {
digitalWrite(LED_BUILTIN, LOW);
}
// Compare the current tilt state to the previous tilt state.
// If the tilt state changed from not tilted to tilted, log once there is tilt detected.
// If the state changed from tilted to not tilted, log once there is no tilt detected.
// Store the current tilt state as the previous tilt state.
if (tilt != tiltState) {
if (tilt) {
Serial.println("Tilt detected!");
}
else {
Serial.println("No tilt detected!");
}
tiltState = tilt;
}
}
Prompts & prompt engineeringA prompt is text given to an AI model so it can predict what you might write next. The result is called a completion. With GitHub Copilot, both prompts and completions typically look like comments or code.
Prompt engineering can be described as "the discipline of crafting prompts to coax specific behaviors from models." It is a bit of an art and science, filled with trial and error. Often you will start with an initial prompt and then adjust it to move the model in the desired direction. Applying the above tips to yield a better Copilot suggestion is prompt engineering!
The model behind Copilot is OpenAI's Codex. To explore prompt engineering with Codex and other models, check out the OpenAI playground. You can start for FREE during your first three months.
For more information on prompt engineering with Codex please view:
Part 3: Further readingBelow is a compilation of resources to further explore GitHub Copilot and OpenAI.
Estimated Read Time: Varied
1. 8 things you didn’t know you could do with GitHub Copilotcovers "unexpected yet valuable Copilot use cases".
Applications include: assisting non-native English speakers,preparing for technical interviews,sending tweets, and navigating a new codebase with Copilot Labs,
2. Configuration settingsfor GitHub Copilot in VS Code provides a few options to explore.
Topics include: setting up duplication detection and enabling/disabling code-snippet collection.
3. OpenAI API Overview details how to create your own application with OpenAI. This includes ChatGPT plugins!
SummaryEvery day, users like you are discovering the potential of GitHub Copilot and leveraging it in novel and useful ways. As you discover new challenges or solution ideas that could assist others, please feel free to comment below or create your own tutorial!
Comments
Please log in or sign up to comment.