Skip to content

Commit

Permalink
feat: add motor, controller, device SDK functions
Browse files Browse the repository at this point in the history
  • Loading branch information
doinkythederp committed Nov 18, 2024
1 parent 766f39e commit ab9d644
Show file tree
Hide file tree
Showing 15 changed files with 440 additions and 321 deletions.
155 changes: 11 additions & 144 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,158 +1,25 @@
# vexide Template
# Hydrozoa Runtime

[![Build status](https://github.com/vexide/vexide-template/actions/workflows/build.yml/badge.svg)](https://github.com/vexide/vexide-template/actions/workflows/build.yml)
> Run WebAssembly programs on VEX V5 brains
> Ready-to-use template for developing VEX V5 robots in Rust.
## About

Seasoned vexide user? Delete README.md and update Cargo.toml as needed.
Hydrozoa Runtime is the behind-the-scenes magic that makes it possible to use interpreted languages like Java and Kotlin on VEX Robots. It uses a WebAssembly interpreter that runs directly on the VEX V5 Brain to run programs, and defines a set of functions the program can use to interface with the VEX SDK.

## Table of Contents
## Building

- [vexide Template](#vexide-template)
- [Table of Contents](#table-of-contents)
- [Using This Template](#using-this-template)
- [Getting Started (Windows)](#getting-started-windows)
- [Getting Started (macOS)](#getting-started-macos)
- [Getting Started (NixOS)](#getting-started-nixos)
- [Getting Started (Debian/Ubuntu Linux)](#getting-started-debianubuntu-linux)
- [Getting Started (Fedora Linux)](#getting-started-fedora-linux)
- [Learn](#learn)
- [Development](#development)
- [Compiling and uploading to a VEX V5 robot](#compiling-and-uploading-to-a-vex-v5-robot)
- [Viewing program output](#viewing-program-output)
Hydrozoa programs need the `hydrozoa.bin` runtime file to upload their code to a robot, which can be generated by building this repository.

## Using This Template
On a system with the Rust programming language installed, start by running this command to install `cargo-v5`:

To start a project using this template, click the "Use this template" button in the upper right corner of the GitHub repository. Choose an appropriate name and clone the new repository using Git. Finally, update the package name in `Cargo.toml`:

```toml
[package]
name = "my-vex-robot"
version = "0.1.0"
edition = "2021"
```

You can also configure your program slot and upload behavior in `Cargo.toml`:

```toml
[package.metadata.v5]
slot = 1
icon = "cool-x"
compress = true
cargo install cargo-v5
```

> See our [Building & Uploading tutorial](https://vexide.dev/docs/building-uploading/) for more information.
## Getting Started (Windows)

Follow the instructions [here](https://www.rust-lang.org/tools/install) to install `rustup`.

Run the following commands in Powershell to set up your PC for development on Windows.

- Switch to the `nightly` rust toolchain and add the `rust-src` component:

```console
rustup default nightly
rustup component add rust-src
```

- Install cargo-v5:

```console
cargo install cargo-v5
```

## Getting Started (macOS)

Follow the instructions [here](https://www.rust-lang.org/tools/install) to install `rustup` on your Mac.

Run the following commands in a terminal window to setup development with vexide.

- Open a terminal and configure `rustup` to build for the V5's platform target:

- Switch to the `nightly` rust toolchain and add the `rust-src` component:

```console
rustup default nightly
rustup component add rust-src
```

- Install cargo-v5:

```console
cargo install cargo-v5
```

## Getting Started (NixOS)

The Nix flake includes a devshell with every tool you need for building and uploading vexide projects.

There is a `.envrc` file for Nix + Direnv users.

## Getting Started (Debian/Ubuntu Linux)

Follow the instructions [here](https://www.rust-lang.org/tools/install) to install `rustup`. You may also prefer to install it from your system package manager or by other means. Instructions on that can be found [here](https://rust-lang.github.io/rustup/installation/other.html).
Then, run this command to build the project:

Run the following terminal commands to set up development on Debian or Ubuntu.

- Switch to the `nightly` rust toolchain and add the `rust-src` component:

```console
rustup default nightly
rustup component add rust-src
```

- Install cargo-v5:

```console
cargo install cargo-v5
```

## Getting Started (Fedora Linux)

Run the following terminal commands to set up your PC for development on Fedora.

- Install Rust:

```console
sudo dnf install rustup
rustup-init -y --default-toolchain nightly
```

- Close and reopen the terminal, and finish installing vexide:

```console
rustup component add rust-src
cargo install cargo-v5
```

## Learn

[Check out the documentation](https://vexide.dev/docs/) on the official vexide website for walkthrough-style guides and other helpful learning resources!

An [API reference](https://docs.rs/vexide) is also provided by docs.rs.

## Development

### Compiling and uploading to a VEX V5 robot

Use the cargo-pros terminal utility to build and upload this vexide project.

```console
cargo v5 build
```

Use a USB cable to connect to your robot brain or to your controller before using the `upload` subcommand to build and upload the project. Make sure to specify a program slot.

```console
cargo v5 upload
cargo v5 build --release
```

### Viewing program output

You can view panic messages and calls to `println!()` using the PROS terminal.
Use a USB cable to connect to your robot brain or controller, then start the terminal:

```console
cargo v5 terminal
```
The resulting `hydrozoa.bin` file is located in `./target/armv7a-vex-v5/release/`.
3 changes: 3 additions & 0 deletions packages/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "runtime"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "hydrozoa"

# These fields configure default behavior for uploads with `cargo v5`.
[package.metadata.v5]
slot = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![no_std]

use anyhow::Context;
use runtime::{platform, sdk, teavm};
use runtime::{platform, sdk, teavm, Data};
use vexide::{core::program::exit, prelude::*};
use vexide_wasm_startup::{startup, CodeSignature, ProgramFlags, ProgramOwner, ProgramType};
use wasm3::{Environment, Store};
Expand All @@ -11,7 +11,9 @@ extern crate alloc;

fn main(_peripherals: Peripherals) {
let env = wasm3::Environment::new().expect("Unable to create environment");
let mut store = env.create_store(8192).expect("Unable to create runtime");
let mut store = env
.create_store(8192, Data::default())
.expect("Unable to create runtime");

if let Err(mut err) = run(&env, &mut store) {
if let Some(info) = store.take_error_info() {
Expand All @@ -21,7 +23,7 @@ fn main(_peripherals: Peripherals) {
}
}

fn run(env: &Environment, store: &mut Store) -> anyhow::Result<()> {
fn run(env: &Environment, store: &mut Store<Data>) -> anyhow::Result<()> {
let wasm_bytes = platform::read_user_program();
let module = env
.parse_module(wasm_bytes)
Expand Down
5 changes: 5 additions & 0 deletions packages/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ mod libc_support;
pub mod platform;
pub mod sdk;
pub mod teavm;

#[derive(Default)]
pub struct Data {
pub teavm: Option<teavm::TeaVM>,
}
100 changes: 94 additions & 6 deletions packages/runtime/src/sdk.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
#![allow(non_snake_case)]

use core::ffi::c_double;

use anyhow::Context;
use wasm3::{Instance, Store};
use vex_sdk::{
V5MotorBrakeMode, V5MotorControlMode, V5MotorEncoderUnits, V5MotorGearset, V5_ControllerId,
V5_ControllerIndex, V5_Device, V5_DeviceT, V5_DeviceType,
};
use wasm3::{store::AsContextMut, Instance, Store};

use crate::{teavm::TeaVM, Data};

macro_rules! link {
($instance:ident, $store:ident, mod $module:literal {
$( fn $name:ident ( $($arg:ident: $arg_ty:ty $(,)?),* ) $(-> $ret:ty)?);* $(;)?
$( fn $name:ident ( $($arg:ident: $arg_ty:ty $(as $wrapper:expr)? $(,)?),* ) $(-> $ret:ty $(, in .$field:tt)?)? );* $(;)?
}) => {
{
$(
$instance.link_closure(
_ = $instance.link_closure(
&mut *$store,
$module,
stringify!($name),
#[allow(unused_parens)]
|_ctx, ($($arg),*): ($($arg_ty),*)| {
#[inline]
fn inner($($arg: $arg_ty),*) $(-> $ret)? {
unsafe { vex_sdk::$name($($arg),*) }
unsafe {
vex_sdk::$name(
$($($wrapper)? ($arg as _)),*
) $($(.$field)? as $ret)?
}
}
Ok(inner($($arg),*))
}
).context(concat!("Unable to link ", $module, "::", stringify!($name), " function"))?;
).context(concat!("Unable to link ", $module, "::", stringify!($name), " function"));
)*
}
};
}

pub fn link(store: &mut Store, instance: &mut Instance) -> anyhow::Result<()> {
pub fn link(store: &mut Store<Data>, instance: &mut Instance<Data>) -> anyhow::Result<()> {
link!(instance, store, mod "vex" {
// Display
fn vexDisplayForegroundColor(col: u32);
Expand Down Expand Up @@ -68,10 +80,86 @@ pub fn link(store: &mut Store, instance: &mut Instance) -> anyhow::Result<()> {
// fn vexDisplayVCenteredString(nLineNumber: i32, format: *const c_char, args: VaList);
// fn vexDisplayVBigCenteredString(nLineNumber: i32, format: *const c_char, args: VaList);

// Controller
fn vexControllerGet(id: u32 as V5_ControllerId, index: u32 as V5_ControllerIndex) -> i32;
fn vexControllerConnectionStatusGet(id: u32 as V5_ControllerId) -> u32, in .0;

// Device
fn vexDeviceGetByIndex(index: u32) -> u32;


// Motor
fn vexDeviceMotorVelocitySet(device: u32, velocity: i32);
fn vexDeviceMotorVelocityGet(device: u32) -> i32;
fn vexDeviceMotorActualVelocityGet(device: u32) -> c_double;
fn vexDeviceMotorDirectionGet(device: u32) -> i32;
fn vexDeviceMotorModeSet(device: u32, mode: u32 as V5MotorControlMode);
fn vexDeviceMotorModeGet(device: u32) -> u32, in .0;
fn vexDeviceMotorPwmSet(device: u32, pwm: i32);
fn vexDeviceMotorPwmGet(device: u32) -> i32;
fn vexDeviceMotorCurrentLimitSet(device: u32, limit: i32);
fn vexDeviceMotorCurrentLimitGet(device: u32) -> i32;
fn vexDeviceMotorCurrentGet(device: u32) -> i32;
fn vexDeviceMotorPowerGet(device: u32) -> c_double;
fn vexDeviceMotorTorqueGet(device: u32) -> c_double;
fn vexDeviceMotorEfficiencyGet(device: u32) -> c_double;
fn vexDeviceMotorTemperatureGet(device: u32) -> c_double;
fn vexDeviceMotorOverTempFlagGet(device: u32) -> bool;
fn vexDeviceMotorCurrentLimitFlagGet(device: u32) -> bool;
fn vexDeviceMotorZeroVelocityFlagGet(device: u32) -> bool;
fn vexDeviceMotorZeroPositionFlagGet(device: u32) -> bool;
fn vexDeviceMotorReverseFlagSet(device: u32, reverse: bool);
fn vexDeviceMotorReverseFlagGet(device: u32) -> bool;
fn vexDeviceMotorEncoderUnitsSet(device: u32, units: u32 as V5MotorEncoderUnits);
fn vexDeviceMotorEncoderUnitsGet(device: u32) -> u32, in .0;
fn vexDeviceMotorBrakeModeSet(device: u32, mode: u32 as V5MotorBrakeMode);
fn vexDeviceMotorBrakeModeGet(device: u32) -> u32, in .0;
fn vexDeviceMotorPositionSet(device: u32, position: c_double);
fn vexDeviceMotorPositionGet(device: u32) -> c_double;
// fn vexDeviceMotorPositionRawGet(device: u32, timestamp: *mut u32) -> i32;
fn vexDeviceMotorPositionReset(device: u32);
fn vexDeviceMotorTargetGet(device: u32) -> c_double;
fn vexDeviceMotorServoTargetSet(device: u32, position: c_double);
fn vexDeviceMotorAbsoluteTargetSet(device: u32, position: c_double, veloctiy: i32);
fn vexDeviceMotorRelativeTargetSet(device: u32, position: c_double, velocity: i32);
fn vexDeviceMotorFaultsGet(device: u32) -> u32;
fn vexDeviceMotorFlagsGet(device: u32) -> u32;
fn vexDeviceMotorVoltageSet(device: u32, voltage: i32);
fn vexDeviceMotorVoltageGet(device: u32) -> i32;
fn vexDeviceMotorGearingSet(device: u32, gearset: u32 as V5MotorGearset);
fn vexDeviceMotorGearingGet(device: u32) -> u32, in .0;
fn vexDeviceMotorVoltageLimitSet(device: u32, limit: i32);
fn vexDeviceMotorVoltageLimitGet(device: u32) -> i32;
fn vexDeviceMotorVelocityUpdate(device: u32, velocity: i32);
// fn vexDeviceMotorPositionPidSet(device: u32, pid: *mut V5_DeviceMotorPid);
// fn vexDeviceMotorVelocityPidSet(device: u32, pid: *mut V5_DeviceMotorPid);
fn vexDeviceMotorExternalProfileSet(device: u32, position: c_double, velocity: i32);

// Misc
fn vexTasksRun();
fn vexCompetitionStatus() -> u32;
});

_ = instance
.link_closure(
&mut *store,
"vex",
"vexDeviceGetStatus",
|mut ctx, devices: i32| {
let teavm = ctx.data().teavm.clone().unwrap();
let array_ptr = (teavm.byte_array_data)(ctx.as_context_mut(), devices).unwrap();
let memory = ctx.memory_mut();
let devices = &mut memory
[array_ptr as usize..(array_ptr as usize + vex_sdk::V5_MAX_DEVICE_PORTS)];

let devices = unsafe {
// SAFETY: V5_DeviceType is a repr(transparent) struct holding a u8
core::mem::transmute::<*mut u8, *mut V5_DeviceType>(devices.as_mut_ptr())
};
Ok(unsafe { vex_sdk::vexDeviceGetStatus(devices) })
},
)
.context("Unable to link vex::vexDeviceGetStatus function");

Ok(())
}
Loading

0 comments on commit ab9d644

Please sign in to comment.