#firmware #automation #cnc #robot #machine #action #driver

no-std robokit

Build custom firmware for simple robotic automation

3 releases (breaking)

0.3.0 Apr 19, 2023
0.2.0 Apr 5, 2023
0.1.0 Apr 4, 2023

#602 in Embedded development

Apache-2.0

89KB
2.5K SLoC

Robo Kit 🤖

Build custom firmware for simple robotic automation

Modules

Firmwares

About

The short-term goal is to build an automated machine for grid beam production.

The long-term goal is to provide a (real-time interrupt-driven) actor-based foundation for robotic automation or CNC machine control.

If you're here and like what's happening, please give this a star and say hi! 👋

Features

  • Minimal
    • Designed for no-std environments
  • Extensible
    • Setup your robot with your own actuators with your own names
      • E.g. Isn't limited to only x, y, z linear axes
  • Command system (like G-Code)
    • Run a sequence of commands (one at a time)
    • Run setup commands at beginning and/or teardown commands at end (in parallel)
  • Actuators:
    • Led
      • Actions:
        • Set { is_on }
        • Blink { duration }
    • Linear Axis
      • Drivers: Stepper
      • Actions:
        • MoveRelative { max_acceleration, distance }
        • MoveAbsolute { max_acceleration, position }
        • Home { max_acceleration, back_off_distance }
    • Spindle
      • Drivers:
        • JmcHsv57
      • Actions:
        • Set(On { rpm })
        • Set(Off)
    • Relay (Pneumatic actuator)
  • Sensors
    • Input switch
      • Button
      • Limit switch
    • Rotary encoder
    • Linear encoder
  • Interfaces
    • Physical controls
    • JSON-RPC
    • Web

Example

./blinky/src/main.rs

(for Nucleo-F767ZI)

#![no_main]
#![no_std]

use blinky as _;

use core::task::Poll;
use cortex_m_rt::entry;
use defmt::Debug2Format;
use fugit::ExtU32;
use robokit::{
    actuator_set, Command, LedAction, LedDevice, RobotBuilder, Sensor, SuperTimer, SwitchDevice,
    SwitchStatus,
};
use stm32f7xx_hal::{pac, prelude::*};

use blinky::init_heap;

const TICK_TIMER_HZ: u32 = 1_000_000;
const ACTIVE_COMMANDS_COUNT: usize = 1;

actuator_set!(
    Led { Green, Blue, Red },
    LedAction<TICK_TIMER_HZ>,
    LedId,
    LedSet,
    LedSetError
);

fn get_run_commands<const TIMER_HZ: u32>() -> [Command<TIMER_HZ, LedId, (), ()>; 6] {
    [
        Command::Led(
            LedId::Green,
            LedAction::Blink {
                duration: 50.millis(),
            },
        ),
        Command::Led(
            LedId::Blue,
            LedAction::Blink {
                duration: 100.millis(),
            },
        ),
        Command::Led(
            LedId::Red,
            LedAction::Blink {
                duration: 200.millis(),
            },
        ),
        Command::Led(
            LedId::Red,
            LedAction::Blink {
                duration: 50.millis(),
            },
        ),
        Command::Led(
            LedId::Blue,
            LedAction::Blink {
                duration: 100.millis(),
            },
        ),
        Command::Led(
            LedId::Green,
            LedAction::Blink {
                duration: 200.millis(),
            },
        ),
    ]
}

#[entry]
fn main() -> ! {
    init_heap();

    defmt::println!("Init!");

    let p = pac::Peripherals::take().unwrap();

    let rcc = p.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(216.MHz()).freeze();

    let gpiob = p.GPIOB.split();
    let gpioc = p.GPIOC.split();

    let tick_timer = p.TIM5.counter_us(&clocks);
    let mut super_timer = SuperTimer::new(tick_timer, u32::MAX);

    let user_button_pin = gpioc.pc13.into_floating_input();
    let user_button_timer = super_timer.sub();
    let mut user_button = SwitchDevice::new_active_high(user_button_pin, user_button_timer);

    let green_led_pin = gpiob.pb0.into_push_pull_output();
    let green_led_timer = super_timer.sub();
    let green_led = LedDevice::new(green_led_pin, green_led_timer);

    let blue_led_pin = gpiob.pb7.into_push_pull_output();
    let blue_led_timer = super_timer.sub();
    let blue_led = LedDevice::new(blue_led_pin, blue_led_timer);

    let red_led_pin = gpiob.pb14.into_push_pull_output();
    let red_led_timer = super_timer.sub();
    let red_led = LedDevice::new(red_led_pin, red_led_timer);

    let mut robot = RobotBuilder::new()
        .with_leds(LedSet::new(green_led, blue_led, red_led))
        .build()
        .with_run_commands(&get_run_commands())
        .build::<ACTIVE_COMMANDS_COUNT>();

    super_timer.setup().expect("Failed to setup super time");
    loop {
        super_timer.tick().expect("Failed to tick super timer");

        if let Some(user_button_update) = user_button.sense().expect("Error reading user button") {
            if let SwitchStatus::On = user_button_update.status {
                robot.toggle();
            }
        }

        if let Poll::Ready(Err(err)) = robot.poll() {
            defmt::println!("Unexpected error: {}", Debug2Format(&err));

            robot.stop();
        }
    }
}

Development

See docs/dev.md

License

Copyright 2023 Village Kit Limited

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Dependencies

~5.5MB
~109K SLoC