Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can this library be controlled programmatically (from another process)? #83

Closed
ianfixes opened this issue Feb 12, 2021 · 7 comments
Closed
Assignees
Labels
enhancement New feature or request

Comments

@ianfixes
Copy link

Hi-

I maintain a framework for testing Arduino libraries, including unit testing. One of the biggest shortcomings of this is that in some cases you would want to test a library against a particular circuit (or at least a simulated one). It looks like the https://wokwi.com/ project does exactly that (and I assume this repo is at least part of what powers that).

So I'm curious about potentially finding a way to connect the 2 of them, so that you could (as part of a unit test) simulate a change in values that would be read from the (fake) hardware in response to values previously written to the hardware.

Is this topic of any interest?

@urish urish self-assigned this Feb 12, 2021
@urish urish added the enhancement New feature or request label Feb 12, 2021
@urish
Copy link
Contributor

urish commented Feb 12, 2021

Nice to meet you Ian!

Yes, that would be lovely. How would you like to go about that?

@ianfixes
Copy link
Author

The way my test framework functions is to manually mock out all of the Arduino reference library, including the clock. What I'm envisioning is basically using my unit test framework as a frontend to the simulation backend -- passing the state of the pins onto the simulator, reading the state of the pins from the simulator, and possibly allowing the simulator to dictate certain things like how many microseconds it takes to read/write (i.e. how far to advance the clock when I perform an operation in software).

I'm assuming that I'll run into trouble immediately since this project appears to be browser-based. Is there a command-line way to interact with this library and/or and API that I could read about?

@urish
Copy link
Contributor

urish commented Feb 12, 2021

Sounds cool!

You can use the library both in the browser and Node.js. For instance, all the unit tests of the library are running on Node.js.

Here is a demo how to load a .hex file, simulate it for 100ms, and then print the status of all the Uno GPIO pins at the end of the simulation:

const {
  CPU,
  portBConfig,
  AVRTimer,
  AVRIOPort,
  PinState,
  timer0Config,
  timer1Config,
  timer2Config,
  portCConfig,
  portDConfig,
  avrInstruction,
} = require('avr8js');
const { parse } = require('intel-hex');
const { readFileSync } = require('fs');

const simulationTime = 100; // ms
const clockSpeed = 16000000; // Hz
const program = parse(readFileSync('blink.hex')).data;
const cpu = new CPU(new Uint16Array(program.buffer));
const timers = [
  new AVRTimer(cpu, timer0Config),
  new AVRTimer(cpu, timer1Config),
  new AVRTimer(cpu, timer2Config),
];
const gpio = [
  { port: new AVRIOPort(cpu, portDConfig), pins: ['0', '1', '2', '3', '4', '5', '6', '7'] },
  { port: new AVRIOPort(cpu, portBConfig), pins: ['8', '9', '10', '11', '12', '13'] },
  { port: new AVRIOPort(cpu, portCConfig), pins: ['A0', 'A1', 'A2', 'A3', 'A4', 'A5'] },
];

const numCycles = Math.floor((simulationTime * clockSpeed) / 1000);
while (cpu.cycles < numCycles) {
  avrInstruction(cpu);
  cpu.tick();
}

for (const { port, pins } of gpio) {
  for (let index = 0; index < pins.length; index++) {
    console.log(`Pin ${pins[index]}: ${PinState[port.pinState(index)]}`);
  }
}

and for testing, you can use the following blink.hex binary:

:100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C944F000C940000E6
:10008000259A1D9A88EE93E00E9448000C94410046
:10009000FFE0E0EA3197F1F70197D1F70895F8947E
:0200A000FFCF90
:00000001FF

(Source code: https://wokwi.com/arduino/projects/290348681199092237)

@ianfixes
Copy link
Author

Interesting, it looks like the simulation includes a compiled binary. I assume that your simulator is using the binary as a way of tracking the state of the Arduino pin outputs. Is there any way that I could replace that with my own virtual outputs and control the clock?

@urish
Copy link
Contributor

urish commented Feb 12, 2021

The compiled binary is the hex file that you'd normally upload to your Arduino Uno board.

You could add listeners to GPIO port events, e.g.

for (const { port } of gpio) {
  port.addListener(() => {
    console.log('Pin changed!');
  });
}

As for controlling the clock - it's simply a matter of running the simulation until the point in time you want. Then you can do some assertions, change GPIO pins, etc., and continue running it up to the next point.

There's also an AVRClock class that can convert the CPU clock cycles into nanos / millis / micros for you.

@urish
Copy link
Contributor

urish commented Apr 10, 2021

Closing as I believe I answered the question. Feel free to open a new issue if there are more questions!

@urish urish closed this as completed Apr 10, 2021
@ianfixes
Copy link
Author

It did answer my question -- thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants