Question Authors' Forum

Arduino and CodeRunner

Arduino and CodeRunner

by Richard Lobb -
Number of replies: 21

A correspondent asks: "I am a middle school teacher. I teach my students to program Arduinos. How hard would it be to add the Arduino programming language to Code Runner?"

The Arduino language (so-called "AVR_C") is essentially GNU C++, which is built in to CodeRunner. The differences are in the linking and libraries. You could teach basic programming concepts out of the box, but asking for Arduino-specific code is a lot harder. You would need to have test implementations ("mocks") of any Arduino library classes and functions that the student code requires. This could become almost arbitrarily complex if you wanted to specify the particular peripherals being used and the wiring diagram in order to check that the code worked with that hardware configuration.

But it all depends what sort of question you wanted to ask. Here's a simple example (based on this tutorial) showing what you can do without too much effort.

Screenshot of Arduino question

That question uses a customised C++ program question with the following template:

#include <stdio.h>
#define LED_BUILTIN 13
#define LOW 0
#define HIGH 1
#define OUTPUT 1

int time = 0; // msecs

void pinMode(int pin, int mode) {
    if (pin != LED_BUILTIN) {
        printf("  pinMode: setting unknown pin %d\n", pin);
    } else if (mode != OUTPUT) {
        printf("  pinMode: setting mode to unknown value\n");
    } else {
        printf("  pinMode: pin %d set as an output\n", LED_BUILTIN);
    }
}

void delay(int deltaMillisecs) {
    time += deltaMillisecs;
}

void digitalWrite(int pin, int state) {
    if (pin != LED_BUILTIN) {
        printf("  digitalWrite: setting unknown pin %d\n", pin);
    } else if (state != LOW && state != HIGH) {
        printf("  digitalWrite: setting pin to illegal value %d\n", state);
    } else {
        printf("  digitalWrite: pin %d set to %d at time %d msecs\n", pin, state, time);
    }
}
{{ STUDENT_ANSWER }}


int main() {
    printf("Setting up:\n");
    setup();
    printf("Looping:\n");
    for (int i = 1; i <= 5; i++) { // 5 iterations should be enough
        loop();
    }
}

However, this works only if the student uses the expected constants and library functions; if they get "creative" (e.g. they googled, found some random code they didn't understand, and pasted it in) their answer won't be accepted. You may or may not care. If you do care, and certainly when you start asking more advanced questions, you'll have to provide mock implementations for lots more of the Arduino library constants, functions and classes.

Richard

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

Thank you for your response. I didn't realize you could use the C++ program question type to make it look like a standard Arduino program. This is really cool and opens up a lot of possibilities.

Currently, most of the assignments I give involve students completing some task that is fairly well explained. For example, "The light should come on when the button is clicked." They simply call me over once they've figured out how to do it, I verify they've done it, and give them credit for the assignment (I scan a QR code they've brought up for that particular assignment). Since I am interested in more than just whether the code works (style, whether they've saved it correctly, etc.), at this point I'm okay continuing to grade in this fashion.

What's killing me right now is the inability of my students to troubleshoot their code. Generally they don't read error messages, and when they do they have difficulty understanding what the messages mean. So, I've created an Arduino Troubleshooter database that allows students to search for common error messages and potential solutions.

My current plan is to create a lesson that presents students with the same (or similar) programs but with different types of mistakes each time. For example:

int greenLED = 13;
int yellowLED = 12

void setup()
{
  pinMode(greenLED, OUTPUT);
  pinmode(yellowLED, OUTPUT);
}

void loop()
{
  digitalWrite(greenLED, HIGH);
  digitalWrite(yellowLED, LOW);
  delay(1000);
  digitalWrite(greenLED, LOW);
  digitalWrite(yellowLED, HIGH);
  delay(1000);
}

In the above example, the semicolon has been deliberately left off the end of the second line. If students try to run this code, they will get an error message. I plan to ask three related questions for each type of error:

  1. What is the Arduino Troubleshooter entry that corresponds to your error? (Probably numeric)
  2. Where is the error? (Students would indicate this by using a marker from the drag and drop marker question type)
  3. Rewrite the line of code correctly. (Verified using a short answer question type)

The second question is a little time consuming to make and the third question presents some difficulties when it comes to grading (extra spaces or deleted spaces, etc.). I'll probably download the regex question type and handle it that way, but I was wondering how you would tackle teaching students how to troubleshoot their code (either with or without CodeRunner). I'm having a little trouble envisioning how CodeRunner would be much of an advantage in this particular situation.

I also have many, many questions that are more conventional that I'd like to ask students using CodeRunner, but the issue had always been getting things to look like Arduino. Specifically, allowing students to print output using Arduinos standard Serial.print() and Serial.println() commands. From what I can gather from above though, it looks as though that is a nonissue - just create a wrapper for printf?

Again, thanks so much for getting back to me. I can clearly see the potential your plugin has for taking my curriculum to the next level. I want to take full advantage of it.

Tags:
In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Thanks for explaining your context. I've never taught students at middle-school level; it's undoubtedly a whole different world from mine (University level).

I'm not sure how much use CodeRunner will be to you in your current context. It's not likely to be useful for your questions 1 and 2, but could help with 3. However, there's a shortcoming with the above template approach that I perhaps should have warned you about. If students submit code with syntax errors, the error messages are going to be even more confusing than those from the Arduino IDE. Because of the extra code inserted at the start of the program, the line numbers in the error messages don't match the line numbers in the students' code. There are ways around this but they're complex and I doubt it's worth your time trying to program in all the extra functionality required. I think it would be better if students were told to get their code to compile in the Arduino IDE before submitting it to CodeRunner for final checking/assessment/recording.

Richard

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I've been thinking a lot about how to use CodeRunner to improve the quality of questions I'm asking my students without having to provide mock implementations for the Arduino library constants, functions and classes reference in the programs students write. I think I may have a solution. I would like to present questions in such a way that students only modify a line or two, but see the code in a larger context. Here's a picture that shows what I'd like to accomplish.


The only code that would actually be run by CodeRunner would be the single line the student inputs, so that's the only function/class I'd have to mock. It looks as though this might be possible by changing the input UI to HTML and adding the code around an input box, but I'm not sure how. Is there documentation for that feature that I'm missing?

If you have a better idea to attain the same end goal, I'm all ears.

In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Hah! Funny you should mention that. Here are a couple of screen shots of questions currently running on my development system.

Gap-filler question

html-ui question

The first one uses a new UI component called GapFiller. The second uses a resurrected version of the long-defunct HTML UI. Both depend on a new field in the question authoring form called Global extra. For the GapFiller UI, you put the code you wish to display, with special {[ width ]} placeholders for the entry fields, into Global Extra. With the more general HTML UI you put any arbitrary HTML into Global Extra, using a special class for input elements whose values are to captured. In both cases the serialised version of the UI contents consists of all the values of the data entry fields.

I think the GapFiller UI will fit your needs. Entry fields can be standard HTML text <input> elements or multiline <textarea>s. The HTML UI could also be used, but it's much more complex and I'm still not sure I wish to make it a supported component of CodeRunner.

However, I'm afraid you'll have to wait until I'm ready to push the code to github. This is likely to be several weeks away - probably not until at least the end of the current semester in 6.5 weeks time. Although ... if you really want to live on the bleeding edge, I can push the current code to the development branch on github.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I think I'll wait until you work out the kinks. I can make due with Rezeau's short answer regex question type until then, but please let me know as soon as this feature is official. I think it has the potential to make CodeRunner really work for my situation.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -
I see you've made some commits since this post. Do they include the GapFiller functionality?
In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Yes, it's there now. There's a demo quiz linked off the front page of this site. It's here.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I've finally gotten around to putting together a question that uses Gap-filler. I can get it to work in python, but when I try to get it to work in C, I run into an issue. I believe the problem is that I need to decode the student's answer which is sent to the template as a JSON list of strings. I just don't know how. Is there a built in parser for JSON in the jobe server?

I've attached my experimental questions. The one I'm having trouble with is C Loop 0-4. If there's a better way to do this, I'm all ears.

Also, I'm interested in checking for correct white space on future questions I make. I suspect the practical way to do this would be to create a c_via_python_whitespace question type. Am I mistaken, or is that the way to go. If so, do you have a questions template or prototype you can share? 

Thanks for making this plugin available. I think it will make a big difference for my students.

In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Yes, writing the template in C is problematic with the gap-filler UI. Twig unfortunately doesn't have a json decode filter. 

In your C Loop 0-4 example, there is a hacky solution. Since the STUDENT_ANSWER variable is a string of the form

["for (int x = 0; x < 5; x++)"]

you can actually get away with just stripping off the first two and last two characters and plugging the result into the code directly. The template for that is

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main() {
    {{ STUDENT_ANSWER[2:-2] }}
        printf("%d\n", x);
    return 0;
}

If you also reduce the answer box rows to 1, the resulting run looks like:


But this won't work with multiple gaps, and there are probably other gotchas as well.

So I think you'd be better off writing a variant of the C_via_python question type as you suggest.

I don't quite understand your question about checking for whitespace but whatever it is I'm pretty sure my answer will be "Use Python" :)

I attach the C gapfiller prototype and example of its use that I showed in the earlier posting. Be warned I haven't used this question type in practice so you might have to debug it a bit.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

Awesome! I got v2 of my question working (see attachment). My next goal is to get things to look like they would in the Arduino IDE. So, instead of:

int main()
{
  {[30]}
    printf("%d\n", x);
}

I'd like it to look like:

void setup()
{
  Serial.begin(9600);
  {[30]}
    Serial.println(x);
}

void loop()
{}

Earlier in this post you show how to do this by altering the template. I understood your response and think I could edit the template if it were C. However, I must confess I have no clue how to alter the c_via_python template that I'm using for this question.

As for the white space, I am referring to how far students indent lines of code depending on whether they are in a loop, an if statement, or a loop within an if statement. I'd like to assess a penalty when students submit code that is improperly indented. Is that something you do with CodeRunner for any of your questions?

In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

The easiest way to get the extra code (the dummy Serial class) into the program is just to put a line like

#include "arduinostuff.cpp"

at the start of the program in globalextra. Then add to the question a support file arduinostuff.cpp that contains all the extra code.

If you want to hide the include from students you can change the line in the template

raw_prog = """{{ QUESTION.globalextra | e('py') }}"""

to

raw_prog = """#include "arduinostuff.cpp"\n{{ QUESTION.globalextra | e('py') }}"""

For more flexibility again you could set up a template parameter that specified the name(s) of extra files to be included. That way the same question type can be used with different support files.

Checking the indentation of the C code is probably not relevant with gap fillers. But when the student is writing whole functions or programs it's certainly nice to check it. Assuming you're running some variant of the c_via_python question type, the submitted code (for non gap-filler questions) is available as as a string e.g. by

student_answer = """ {{ STUDENT_ANSWER | e('py') }}"""

and it's over to you to check it however you like.

Our own horrendously complex C question type runs the astyle program over the student's code and checks if the code has changed as a result. If so, it prints any discrepancies and aborts execution. Student's are told that they are required to lay out the code exactly as astyle does (with a particular style convention) and their code editor has a button to run astyle with a single mouse click or control-key. So they have no excuse for submitting non-astyle-compliant code.

Our checking also includes parsing the submitted C code with the pycparser Python module and checking things like function length and choice of identifiers. However you can't use pycparser with Arduino C as it's really C++. I don't know of any C++ parser written in Python.

To be fair to the student, you need to provide a Precheck button that does all style checking so they don't pay penalties for trivial stylistic defects.

I suggest you start small and just add simple checks to your base question type and built up its capabilities over time (possibly years).

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I tried adding the support file MockArduino.cpp (attached). I then created code in the Global extra field that referenced the MockArduino.cpp file (see attached image of my error message). I think the compiler is expecting to see the setup inside the support file with main. Is there a way to get it to look to the global extra field? Is there another solution?

Attachment ErrorMessage.png
In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

The code from MockArduino.cpp is simply being inserted into the code at the point of the "#include". So if it contains the main function and if you want that to call setup then either put the #include at the end of the program (possibly by hiding it in the template) or (probably nicer) put a forward declaration of setup into MockArduino.cpp, before the main function definition, e.g.

void setup();
// And if you want to put the definition of loop in the student code ...
void loop();  
int main() { ... etc }

Both should work.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I put in a forward declaration of setup and everything worked! The next step is to create a mock of the Serial object. I did so in my MockArduino.cpp using struct. Unfortunately, I'm getting an error that I don't understand (see attached image). I've also attached an exported version of the question as well as the latest version of my MockArduino.cpp file.

Attachment MockSerialError.png
In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Is your base question type C or C++? Arduino C is actually C++ and your Serial class requires C++.

In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -

I'm using the PROTOTYPE_c_gapfiller and C gapfiller example that you provided links to above. Do you have a PROTOTYPE_cpp_gapfiller and example I could use?

In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

You should be able to run C++ instead simply by changing the name prog.c to prog.cpp and changing the Python line that compiles the code from

return_code = subprocess.call(['gcc', '-Wall', '--std=c99', '-Werror', '-o', 'prog', 'prog.c'])

to

return_code = subprocess.call(['g++', '-Wall', '-Werror', '-o', 'prog', 'prog.cpp'])

Give that a go.

Sorry, that's all I can offer at present. I'm afraid I can't construct question prototypes for every problem - every author has their own special needs.


In reply to Richard Lobb

Re: Arduino and CodeRunner

by Michael Backus -
It works! Thanks for all your help. I'm going to make a quiz on loops this weekend. One quick question I have about the user interface is whether there is a way to remove the "For example" from above the test cases that I have chosen to show as an example.

Also, I'd like to know how I can contribute. I now have a working cpp question prototype that I'm happy to share, as well as a MockArduino.cpp file I'm willing to share and plan to expand on in the future. Is there a repository that I can submit pull requests to? I'm also willing to share the questions I write with other educators. Let me know how I can contribute.
Attachment Success.png
In reply to Michael Backus

Re: Arduino and CodeRunner

by Michael Backus -

Awhile back you added me to the Python question repository course. I propose you change the course name to Question Repository, and then I'll add the questions I'm developing if you want. I'll put them in a category called Arduino.

In reply to Michael Backus

Re: Arduino and CodeRunner

by Richard Lobb -

Many thanks for the offer to contribute, Michael.

Since the language that the question type offers to students is C++, I'd prefer that Arduino questions went into the C++ repository rather than the Python repository. So I've changed its name to C++ and Arduino and added you as a teacher. Please create a separate question category for Arduino, then contribute away!