Compare commits

...

15 Commits

  1. 41
      .gitignore
  2. 125
      drivers/a4988.cpp
  3. 87
      hal/stepper.cpp
  4. 85
      include/a4988.h
  5. 36
      include/stepper.h
  6. 25447
      lib/json.hpp
  7. 38
      main.cpp
  8. 7
      motioneye/down
  9. 7
      motioneye/up
  10. 18
      settings.json

41
.gitignore

@ -0,0 +1,41 @@
*.swp
bin/*
# Created by https://www.toptal.com/developers/gitignore/api/c++
# Edit at https://www.toptal.com/developers/gitignore?templates=c++
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# End of https://www.toptal.com/developers/gitignore/api/c++

125
drivers/a4988.cpp

@ -0,0 +1,125 @@
#include <include/a4988.h>
#include <pigpio.h>
#include <queue>
#include <cmath>
#include <thread>
#include <string>
#include <memory>
#include <iostream>
namespace simon {
namespace a4988 {
c_allegro_4988::c_allegro_4988(std::unique_ptr<a4988_settings_t> ptr_set)
: m_ptr_set(std::move(ptr_set)), m_initialized(false), m_alive(true)
{
}
void c_allegro_4988::run(void)
{
if (!m_initialized) {
initialize();
}
while(m_alive)
{
if (!m_queue.empty()) {
auto cmd = m_queue.front();
// Distinguish between different tytpes of commands and derive type
if (*cmd == "move") {
auto move = std::static_pointer_cast<c_move>(cmd);
if (!set_pwm_freq(std::round(1e6/move->period_us))) {
// TODO: proper error handling
}
gpioHardwarePWM(m_ptr_set->gpio_step, m_pwm_freq, PI_HW_PWM_RANGE/2);
gpioWrite(m_ptr_set->gpio_dir, move->dir);
auto t_sleep = move->period_us*move->steps;
std::this_thread::sleep_for(std::chrono::microseconds(t_sleep));
gpioHardwarePWM(m_ptr_set->gpio_step, m_pwm_freq, 0);
}
else if (*cmd == "enable") {
auto enable = std::static_pointer_cast<c_enable>(cmd);
gpioWrite(m_ptr_set->gpio_en, enable->enabled);
}
else if (*cmd == "kill") {
m_alive = false;
}
else {
// TODO: Throw an exception here
}
m_queue.pop();
}
else {
// Idle
}
}
}
void c_allegro_4988::post(std::shared_ptr<c_cmd> cmd)
{
m_queue.push(cmd);
}
void c_allegro_4988::post_move(const unsigned steps, const bool dir,
const unsigned period_us)
{
auto move(std::make_shared<c_move>(steps, dir, period_us));
auto cmd = std::static_pointer_cast<c_cmd>(move);
m_queue.push(cmd);
}
void c_allegro_4988::initialize(void)
{
// TODO: proper error management throwing exceptions
if(m_ptr_set == nullptr) {
return;
}
// Configure clock source for PWM
gpioCfgClock(CFG_SAMPLE_RATE_US, CFG_PERIPHERAL_SRC_PCM, CFG_DEPRECATED_ARG);
// Initialize pigpio library
gpioInitialise();
// Configure as outputs
gpioSetMode(m_ptr_set->gpio_en, PI_OUTPUT);
gpioSetMode(m_ptr_set->gpio_dir, PI_OUTPUT);
// Initialize outputs
enable();
gpioWrite(m_ptr_set->gpio_dir, 0);
// Set PWM to 0
m_pwm_freq = 1000;
gpioHardwarePWM(m_ptr_set->gpio_step, m_pwm_freq, 0);
m_initialized = true;
}
int c_allegro_4988::enable(void)
{
// TODO: proper error handling
auto enable(std::make_shared<c_enable>(true));
auto cmd = std::static_pointer_cast<c_cmd>(enable);
m_queue.push(cmd);
return 0;
}
int c_allegro_4988::disable(void)
{
// TODO: proper error handling
auto enable(std::make_shared<c_enable>(false));
auto cmd = std::static_pointer_cast<c_cmd>(enable);
m_queue.push(cmd);
return 0;
}
void c_allegro_4988::kill(void)
{
// TODO: proper error handling
auto kill(std::make_shared<c_kill>());
auto cmd = std::static_pointer_cast<c_cmd>(kill);
m_queue.push(cmd);
}
int c_allegro_4988::set_pwm_freq(const unsigned pwm_freq)
{
if (pwm_freq > PI_HW_PWM_MAX_FREQ) {
return 1;
}
m_pwm_freq = pwm_freq;
return 0;
}
unsigned c_allegro_4988::get_microsteps(void)
{
return m_ptr_set->gpio_microsteps;
}
} // namespace a4988
} // namespace simon

87
hal/stepper.cpp

@ -0,0 +1,87 @@
#include <include/stepper.h>
#include <thread>
#include <functional>
#include <iostream>
#include <fstream>
#include <memory>
#include <cmath>
namespace simon {
namespace stepper {
c_stepper::c_stepper(const std::string& axis, const std::string& fname)
{
auto file_content = read_file(fname);
auto j_set = json::parse(file_content);
if (!j_set["axis_configurations"].is_array()) {
std::cout << "Expected an array in the configuration below \"axis_configurations\"." << "\n";
// TODO: Throw exception
}
extract_settings(j_set, axis);
m_driver_thread = std::thread(std::bind(&a4988::c_allegro_4988::run, &(*m_ptr_a4988)));
}
c_stepper::~c_stepper()
{
m_ptr_a4988->kill();
m_driver_thread.join();
}
void c_stepper::extract_settings(const json& j_set, const std::string& axis)
{
int ind = 0;
for (const auto& j_axis_conf : j_set["axis_configurations"]) {
ind++;
if (j_axis_conf["id"].is_string()) {
if (j_axis_conf["id"] == axis) {
auto a_ptr = std::make_unique<a4988::a4988_settings_t>(
a4988::a4988_settings_t ({
// TODO: Do more sanitizing before retrieving this data
.gpio_en = j_axis_conf["a4988"]["gpio_en"],
.gpio_step = j_axis_conf["a4988"]["gpio_step"],
.gpio_dir = j_axis_conf["a4988"]["gpio_dir"],
.gpio_microsteps = j_axis_conf["a4988"]["microsteps"]
})
);
m_ptr_a4988 = std::make_unique<a4988::c_allegro_4988>(std::move(a_ptr));
m_stepper = {
// TODO: Do more sanitizing before retrieving this data
.gear_reduction = j_axis_conf["stepper"]["gear_reduction"],
.steps_per_revolution = j_axis_conf["stepper"]["steps_per_revolution"],
.clockwise = j_axis_conf["stepper"]["clockwise"]
};
break;
}
} else {
// TODO:Throw exception
std::cerr << "Expected a string in the configuration below [\"axis_configurations\"][" << ind << "][\"id\"]\n";
}
}
}
std::string c_stepper::read_file(const std::string& fname) const
{
std::ifstream input_stream(fname);
if (!input_stream) {
// TODO: Throw exception here
std::cerr << "Can't open input file!";
}
std::string line;
std::string jstring;
while (getline(input_stream, line)) {
jstring += line;
}
return jstring;
}
float c_stepper::rotate(const float& degrees)
{
auto steps_360 = m_stepper.gear_reduction
* m_stepper.steps_per_revolution
* static_cast<float>(m_ptr_a4988->get_microsteps());
auto steps = static_cast<unsigned>((std::abs(degrees)/360.0f) * steps_360);
bool direction = (degrees < 0) ? !m_stepper.clockwise : m_stepper.clockwise;
m_ptr_a4988->post_move(steps, direction, 100);
return steps;
}
}
}

85
include/a4988.h

@ -0,0 +1,85 @@
#ifndef INCLUDED_DRIVER_ALLEGRO_4988
#define INCLUDED_DRIVER_ALLEGRO_4988
#define CFG_SAMPLE_RATE_US 5
#define CFG_PERIPHERAL_SRC_PCM 1
#define CFG_DEPRECATED_ARG 0
#include <queue>
#include <string>
#include <memory>
namespace simon {
namespace a4988 {
typedef struct
{
const unsigned gpio_en;
const unsigned gpio_step;
const unsigned gpio_dir;
const unsigned gpio_microsteps;
} a4988_settings_t;
class c_cmd
{
public:
explicit c_cmd(const std::string cmd) : m_cmd(cmd) {}
bool operator==(const std::string& vgl) {
return (vgl==m_cmd ? true : false);
}
virtual ~c_cmd(void) noexcept {}
private:
const std::string m_cmd;
};
class c_move : public c_cmd
{
public:
const unsigned steps;
const bool dir;
const unsigned period_us;
c_move(const unsigned isteps, const bool idir,
const unsigned iperiod_us)
: c_cmd("move"), steps(isteps), dir(idir), period_us(iperiod_us){}
};
class c_enable : public c_cmd
{
public:
const bool enabled;
explicit c_enable(const bool ienabled)
: c_cmd("enable"), enabled(ienabled) {}
};
class c_kill : public c_cmd
{
public:
c_kill(void)
: c_cmd("kill") {}
};
class c_allegro_4988
{
public:
explicit c_allegro_4988(std::unique_ptr<a4988_settings_t> ptr_set);
int enable(void);
int disable(void);
void kill(void);
void post(std::shared_ptr<c_cmd> cmd);
void post_move(const unsigned steps, const bool dir,
const unsigned period_us);
void run(void);
unsigned get_microsteps(void);
private:
std::unique_ptr<a4988_settings_t> m_ptr_set;
bool m_initialized;
std::queue<std::shared_ptr<c_cmd>> m_queue;
unsigned m_pwm_freq;
bool m_alive;
private:
void initialize(void);
int set_pwm_freq(const unsigned pwm_freq);
};
} // namespace a4988
} // namespace simon
#endif /*INCLUDED_DRIVER_ALLEGRO_4988*/

36
include/stepper.h

@ -0,0 +1,36 @@
#ifndef INCLUDED_HAL_STEPPER
#define INCLUDED_HAL_STEPPER
#include <include/a4988.h>
#include <lib/json.hpp>
#include <thread>
namespace simon {
namespace stepper {
using json = nlohmann::json;
typedef struct {
float gear_reduction;
float steps_per_revolution;
bool clockwise;
} settings_t;
class c_stepper
{
public:
c_stepper(const std::string& axis, const std::string& fname);
~c_stepper();
float rotate(const float& degrees);
private:
settings_t m_stepper;
std::unique_ptr<a4988::c_allegro_4988> m_ptr_a4988;
std::thread m_driver_thread;
private:
std::string read_file(const std::string& fname) const;
void extract_settings(const json& j_set, const std::string& axis);
};
} // namespace stepper
} // namespace simon
#endif /*INCLUDED_HAL_STEPPER*/

25447
lib/json.hpp

File diff suppressed because it is too large

38
main.cpp

@ -0,0 +1,38 @@
#include <iostream>
#include <include/stepper.h>
#include <boost/program_options.hpp>
int main(int argc, char *argv[])
{
using namespace simon;
namespace po = boost::program_options;
po::options_description desc("This small program allows to control SMcam. Allowed options");
desc.add_options()
("help", "Produce help message")
("axis", po::value<std::string>(), "Name of the axis to rotate")
("settings", po::value<std::string>(), "Settings file name")
("rotate", po::value<int>(), "Rotation in degrees. Positive means clockwise.");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << "\n";
return EXIT_FAILURE;
}
stepper::c_stepper* ptr_stepper = nullptr;
if (vm.count("axis") && vm.count("settings")) {
auto axis = vm["axis"].as<std::string>();
auto fname_settings = vm["settings"].as<std::string>();
ptr_stepper = new stepper::c_stepper(axis, fname_settings);
if (vm.count("rotate")) {
auto degrees = vm["rotate"].as<int>();
ptr_stepper->rotate(degrees);
}
} else {
std::cerr << "Please specify axis and settings file.\n";
return EXIT_FAILURE;
}
if (ptr_stepper != nullptr)
delete ptr_stepper;
return EXIT_SUCCESS;
}

7
motioneye/down

@ -0,0 +1,7 @@
#!/bin/bash
SAMCAM_HOME=/home/pi/camera_steering_sw
SETTINGS_FILE=$SAMCAM_HOME/settings.json
ANGLE=-10
AXIS=elevation
export PATH=$SAMCAM_HOME/bin:$PATH
SaMcam --axis $AXIS --settings $SETTINGS_FILE --rotate $ANGLE

7
motioneye/up

@ -0,0 +1,7 @@
#!/bin/bash
SAMCAM_HOME=/home/pi/camera_steering_sw
SETTINGS_FILE=$SAMCAM_HOME/settings.json
ANGLE=10
AXIS=elevation
export PATH=$SAMCAM_HOME/bin:$PATH
SaMcam --axis $AXIS --settings $SETTINGS_FILE --rotate $ANGLE

18
settings.json

@ -0,0 +1,18 @@
{
"axis_configurations": [
{
"stepper": {
"gear_reduction": 64.0,
"steps_per_revolution": 32,
"clockwise" : false
},
"a4988": {
"microsteps": 16,
"gpio_dir": 22,
"gpio_en": 17,
"gpio_step": 18
},
"id": "elevation"
}
]
}
Loading…
Cancel
Save