mirror of
https://github.com/eddyem/mmpp.git
synced 2025-12-06 18:45:17 +03:00
463 lines
13 KiB
C
463 lines
13 KiB
C
/*
|
|
* This file is part of the libmmpp project.
|
|
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "libmmpp.h"
|
|
#include "tty_procs.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#if defined GETTEXT
|
|
#include <libintl.h>
|
|
#define _(String) gettext(String)
|
|
#define gettext_noop(String) String
|
|
#define N_(String) gettext_noop(String)
|
|
#else
|
|
#define _(String) (String)
|
|
#define N_(String) (String)
|
|
#endif
|
|
|
|
// tty device
|
|
static TTYdescr *dev = NULL;
|
|
static bool alive[3] = {false,true,true}; // ==true if controller answers, false if no
|
|
static bool reset[3] = {false,false,false}; // reset occured
|
|
|
|
/**
|
|
* @brief tty_tryopen - try to open serial device
|
|
* @param devnm - path to device
|
|
* @param spd - speed (number)
|
|
* @return 0 if all OK
|
|
*/
|
|
int mmpp_tryopen(char *devnm, int spd){
|
|
if(dev) close_tty(&dev);
|
|
dev = new_tty(devnm, spd, 256);
|
|
if(!dev) return 1;
|
|
if(!tty_open(dev, true)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief tty_close - close TTY device
|
|
*/
|
|
void mmpp_close(){
|
|
if(!dev) return;
|
|
close_tty(&dev);
|
|
}
|
|
|
|
/**
|
|
* test connection (1,2 -> ALIVE)
|
|
* @return 1 if none of MCU found, 0 if at least 1 found
|
|
*/
|
|
int mot_handshake(){
|
|
char buff[32], *ret;
|
|
int mcu;
|
|
FNAME();
|
|
for(mcu = 1; mcu < 3; ++mcu){
|
|
DBG("MCU #%d", mcu);
|
|
// check if MCU alive
|
|
sprintf(buff, "%d", mcu);
|
|
int notresp = 1;
|
|
alive[mcu] = false;
|
|
// make HANDSHAKE_TRIES tries
|
|
for(int tr = 0; tr < HANDSHAKE_TRIES; ++tr){
|
|
ret = tty_sendraw(buff);
|
|
if(ret && 0 == strcmp(ret, "ALIVE\n")){
|
|
notresp = 0;
|
|
break;
|
|
}
|
|
}
|
|
if(notresp){
|
|
continue;
|
|
}
|
|
alive[mcu] = true;
|
|
}
|
|
if(alive[1] == false && alive[2] == false) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get temperature of both MCUs
|
|
* @param t1, t2 (o) - temperatures of first and second MCUs (==-300 if error)
|
|
* @return amount of successful calls
|
|
*/
|
|
int get_temp(double *t1, double *t2){
|
|
char *val, buff[] = "xGT\n", *ans;
|
|
int ret = 0;
|
|
if(t1) *t1 = -300.;
|
|
if(t2) *t2 = -300.;
|
|
for(int i = 1; i < 3; ++i){
|
|
if(!alive[i]){
|
|
DBG("MCU %d didn't respond!", i);
|
|
continue;
|
|
}
|
|
buff[0] = '0' + (char)i;
|
|
if((ans = tty_sendraw(buff))){
|
|
val = keyval("TEMP", ans);
|
|
DBG("val: %s", val);
|
|
if(val){
|
|
++ret;
|
|
double t;
|
|
if(str2double(&t, val)){
|
|
if(i == 1){if(t1) *t1 = t/10.;}
|
|
else{if(t2) *t2 = t/10.;}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief stop_all - send commands to stop all motors
|
|
* @return 0 if all OK, else return amount of motors failed to stop
|
|
*/
|
|
int stop_all(){
|
|
int ret = 4;
|
|
if(alive[1]){
|
|
if(SEND_ALLOK == tty_sendcmd("1M0S")) --ret;
|
|
if(SEND_ALLOK == tty_sendcmd("1M1S")) --ret;
|
|
}
|
|
if(alive[2]){
|
|
if(SEND_ALLOK == tty_sendcmd("2M0S")) --ret;
|
|
if(SEND_ALLOK == tty_sendcmd("2M1S")) --ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief init_motors - BLOCKING (!!!) init all motors simultaneously (if they need to)
|
|
* @return 0 if all OK, or Nmcu*10+motnum for problem motor (motnum == 2 for problems with MCU)
|
|
* !!! in case of non-zero returning you should repeat initialisation
|
|
*/
|
|
int init_motors(){
|
|
FNAME();
|
|
char buf[32];
|
|
motor_state S[3];
|
|
#define RETVAL() (Nmcu*10+motnum)
|
|
int Nmcu, motnum, needinit = 0;
|
|
for(Nmcu = 1; Nmcu < 3; ++Nmcu){
|
|
if(!mot_getstatus(Nmcu, &S[Nmcu])) return 10*Nmcu+2;
|
|
for(motnum = 0; motnum < 2; ++motnum){
|
|
// check position
|
|
if(S[Nmcu].curpos[motnum] < 0) needinit = 1;
|
|
}
|
|
}
|
|
if(!needinit) return 0;
|
|
red("Need to init, start!\n");
|
|
for(Nmcu = 1; Nmcu < 3; ++Nmcu){
|
|
for(motnum = 0; motnum < 2; ++motnum){
|
|
int pos = S[Nmcu].curpos[motnum];
|
|
if(pos >= 0) continue;
|
|
// check if we are on zero endswitch
|
|
if(S[Nmcu].ESW_status[motnum][0] == ESW_HALL){ // move a little from zero esw
|
|
red("from esw\n");
|
|
sprintf(buf, "%dM%dM100", Nmcu, motnum);
|
|
if(SEND_ERR == tty_sendcmd(buf)){
|
|
return RETVAL();
|
|
}
|
|
mot_wait();
|
|
}
|
|
sprintf(buf, "%dM%dM-40000", Nmcu, motnum);
|
|
if(SEND_ALLOK != tty_sendcmd(buf)){
|
|
return RETVAL();
|
|
}
|
|
}}
|
|
mot_wait();
|
|
green("check\n");
|
|
// check current positions
|
|
for(Nmcu = 1; Nmcu < 3; ++Nmcu){
|
|
if(!mot_getstatus(Nmcu, &S[Nmcu])) return 10*Nmcu+2;
|
|
for(motnum = 0; motnum < 2; ++motnum){
|
|
if(S[Nmcu].curpos[motnum]){
|
|
return RETVAL();
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
#undef RETVAL
|
|
}
|
|
|
|
/**
|
|
* Wait while all motors are stopped
|
|
* @return 0 if all OK
|
|
*/
|
|
int mot_wait(){
|
|
int failcount = 0;
|
|
bool mov[3] = {false,true, true};
|
|
if(!alive[1] && !alive[2]) return 0; // all are dead here
|
|
while(failcount < FAIL_TRIES && (mov[1] || mov[2])){
|
|
for(int Nmcu = 1; Nmcu < 3; ++Nmcu){
|
|
usleep(10000);
|
|
DBG("alive=%d/%d, mov=%d/%d", alive[1],alive[2],mov[1],mov[2]);
|
|
if(!alive[Nmcu] || !mov[Nmcu]) continue;
|
|
motor_state S;
|
|
if(!mot_getstatus(Nmcu, &S)){
|
|
++failcount;
|
|
}else failcount = 0;
|
|
if(S.state[0] == STP_SLEEP && S.state[1] == STP_SLEEP)
|
|
mov[Nmcu] = false;
|
|
}
|
|
}
|
|
if(failcount >= FAIL_TRIES){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* read data from TTY
|
|
* WARNING! Not thread-safe!!!
|
|
* @return static buffer with data read or NULL
|
|
*/
|
|
char *tty_get(){
|
|
if(!dev) return NULL;
|
|
static char buf[TBUFLEN];
|
|
char *ptr = buf;
|
|
size_t L = 0, l = TBUFLEN;
|
|
double t0 = dtime();
|
|
*ptr = 0;
|
|
while(dtime() - t0 < TTYTIMEOUT && l){
|
|
size_t r = read_tty(dev);
|
|
if(!r) continue;
|
|
t0 = dtime();
|
|
if(r > l) r = l;
|
|
DBG("got %zd bytes: %s", r, dev->buf);
|
|
strncpy(ptr, dev->buf, r);
|
|
L += r; l -= r; ptr += r;
|
|
}
|
|
buf[L] = 0;
|
|
if(L){
|
|
return buf;
|
|
}
|
|
DBG("no answer");
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Send given string command to port
|
|
* @return 0 if failed
|
|
*/
|
|
int tty_send(char *cmd){
|
|
if(!dev) return 0;
|
|
size_t l = 0;
|
|
char *s = cpy2buf(cmd, &l);
|
|
if(!s) return 0;
|
|
if(write_tty(dev->comfd, s, l)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* send RAW string to port device
|
|
* @param string - string to send
|
|
* @return string received or NULL in case of error
|
|
*/
|
|
char* tty_sendraw(char *string){
|
|
if(!dev) return NULL;
|
|
DBG("sendraw %s", string);
|
|
if(!tty_send(string)) return NULL;
|
|
return tty_get();
|
|
}
|
|
|
|
/**
|
|
* Send given string command to port with answer analysis
|
|
* @return status
|
|
*/
|
|
ttysend_status tty_sendcmd(char *cmd){
|
|
DBG("SEND: %s", cmd);
|
|
if(!tty_send(cmd)) return SEND_ERR;
|
|
char *got = tty_get();
|
|
if(!got) return SEND_ERR;
|
|
if(strcmp(got, "ALLOK\n") == 0) return SEND_ALLOK;
|
|
else if(strcmp(got, "IsMoving") == 0) return SEND_ACTIVE;
|
|
else if(strcmp(got, "OnEndSwitch\n") == 0) return SEND_ESWITCH;
|
|
else if(strcmp(got, "ZeroMove") == 0) return SEND_ZEROMOVE;
|
|
else if(strcmp(got, "TooBigNumber") == 0) return SEND_TOOBIG;
|
|
return SEND_OTHER;
|
|
}
|
|
|
|
/**
|
|
* @brief mot_getstatus - get status of motors for given controller
|
|
* @param Nmcu - (1 or 2) MCU number
|
|
* @param s (o) - status
|
|
* @return true if all OK
|
|
*/
|
|
bool mot_getstatus(int Nmcu, motor_state *s){
|
|
char buff[32], *ans, cmd[4] = "xGS", *val;
|
|
if(Nmcu < 1 || Nmcu > 2) return false;
|
|
cmd[0] = '0' + Nmcu;
|
|
ans = tty_sendraw(cmd);
|
|
if(!ans){
|
|
alive[Nmcu] = false;
|
|
return false;
|
|
}
|
|
alive[Nmcu] = true;
|
|
motor_state S;
|
|
memset(&S, 0, sizeof(S));
|
|
val = keyval("WDGRESET", ans);
|
|
if(val) s->rst = RESET_WDG;
|
|
else{
|
|
val = keyval("SOFTRESET", ans);
|
|
if(val) s->rst = RESET_SW;
|
|
}
|
|
if(s->rst != RESET_NONE) reset[Nmcu] = true;
|
|
for(int i = 0; i < 2; ++i){
|
|
sprintf(buff, "POS%d", i);
|
|
val = keyval(buff, ans);
|
|
if(val){
|
|
S.curpos[i] = atoi(val);
|
|
}
|
|
sprintf(buff, "MOTOR%d", i);
|
|
val = keyval(buff, ans);
|
|
stp_state ms = STP_UNKNOWN;
|
|
if(val){
|
|
if(strcmp(val, "ACCEL") == 0) ms = STP_ACCEL;
|
|
else if(strcmp(val, "DECEL") == 0) ms = STP_DECEL;
|
|
else if(strcmp(val, "MOVE") == 0) ms = STP_MOVE;
|
|
else if(strcmp(val, "MOVETO0") == 0) ms = STP_MOVE0;
|
|
else if(strcmp(val, "MOVETO1") == 0) ms = STP_MOVE1;
|
|
else if(strcmp(val, "MVSLOW") == 0) ms = STP_MVSLOW;
|
|
else if(strcmp(val, "STOP") == 0) ms = STP_STOP;
|
|
else if(strcmp(val, "STOPZERO") == 0) ms = STP_STOPZERO;
|
|
else if(strcmp(val, "SLEEP") == 0) ms = STP_SLEEP;
|
|
S.state[i] = ms;
|
|
}
|
|
if(ms != STP_UNKNOWN && ms != STP_SLEEP){ // moving
|
|
sprintf(buff, "STEPSLEFT%d", i);
|
|
val = keyval(buff, ans);
|
|
if(val){
|
|
S.stepsleft[i] = atoi(val);
|
|
}
|
|
}
|
|
// end-switches
|
|
for(int j = 0; j < 2; ++j){
|
|
sprintf(buff, "ESW%d%d", i, j);
|
|
val = keyval(buff, ans);
|
|
if(val){
|
|
ESW_status s = ESW_ERROR;
|
|
if(strcmp(val, "RLSD") == 0) s = ESW_RELEASED;
|
|
else if(strcmp(val, "BTN") == 0) s = ESW_BUTTON;
|
|
else if(strcmp(val, "HALL") == 0) s = ESW_HALL;
|
|
S.ESW_status[i][j] = s;
|
|
}
|
|
}
|
|
}
|
|
if(s) *s = S;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief get_rst - get reset state for mcu #N
|
|
* @param N - number of MCU
|
|
* @param clear - true to clear reset state
|
|
* @return
|
|
*/
|
|
bool get_rst(int N, bool clear){
|
|
if(N < 1 || N > 2) return false;
|
|
bool state = reset[N];
|
|
if(clear) reset[N] = 0;
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* @brief get_alive - return true if MCU #N is alive
|
|
* @param N - number of MCU (1 or 2)
|
|
* @return alive[N]
|
|
*/
|
|
bool get_alive(int N){
|
|
if(N < 1 || N > 2) return false;
|
|
return alive[N];
|
|
}
|
|
|
|
/**
|
|
* @brief get_ADC - ADC values
|
|
* @param N - number of MCU (1 or 2)
|
|
* @param s (o) - state of all ADC channels
|
|
* @return true if all OK
|
|
*/
|
|
bool get_ADC(int N, ADC_state *s){
|
|
if(N < 1 || N > 2 || !s || !alive[N]) return false;
|
|
char buff[] = "xGAy", cmds[3] = "DIM", *ansv[] = {"VDD", "IMOT", "VMOT"};
|
|
double *vals[3] = {&s->Vdd, &s->Imot, &s->Vmot};
|
|
int got = 0;
|
|
buff[0] = '0' + N;
|
|
for(int i = 0; i < 3; ++i){
|
|
buff[3] = cmds[i];
|
|
char *ans = tty_sendraw(buff);
|
|
if(!ans) continue;
|
|
char *v = keyval(ansv[i], ans);
|
|
if(v){
|
|
double t;
|
|
if(str2double(&t, v)){
|
|
*vals[i] = t / 100.;
|
|
DBG("Got %s=%g", ansv[i], *vals[i]);
|
|
++got;
|
|
}
|
|
}
|
|
}
|
|
if(got != 3) return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief reset_MCU - reset given controller
|
|
* @param N - MCU # (1 or 2)
|
|
*/
|
|
void reset_MCU(int N){
|
|
if(N < 1 || N > 2) return;
|
|
char cmd[] = "xR";
|
|
cmd[0] = '0' + N;
|
|
tty_sendraw(cmd);
|
|
}
|
|
|
|
/**
|
|
* @brief movemotor - move motor
|
|
* @param mcu - MCU# (controller No, 1 or 2)
|
|
* @param motnum - motor# (0 or 1)
|
|
* @param steps - steps amount
|
|
* @param absmove - !=0 if steps are absolute position
|
|
* @return
|
|
*/
|
|
ttysend_status movemotor(int mcu, int motnum, int steps, int absmove){
|
|
if(mcu < 1 || mcu > 3 || motnum < 0 || motnum > 1) return SEND_OTHER;
|
|
char buf[32];
|
|
motor_state mstate;
|
|
if(!mot_getstatus(mcu, &mstate)) return SEND_ERR;
|
|
if(mstate.state[motnum] != STP_SLEEP) return SEND_ACTIVE;
|
|
int curpos = mstate.curpos[motnum];
|
|
if(curpos < 0){ // need to init
|
|
return SEND_NEEDINIT;
|
|
}
|
|
if(absmove){
|
|
if(motnum == 1){ // convert rotator angle to positive
|
|
int perrev = (mcu == 1) ? STEPSREV1 : STEPSREV2;
|
|
steps %= perrev;
|
|
if(steps < 0) steps += perrev;
|
|
}
|
|
if(steps < 0){
|
|
return SEND_NEGATMOVE;
|
|
}
|
|
steps -= curpos;
|
|
}
|
|
if(steps == 0){
|
|
return SEND_ZEROMOVE;
|
|
}
|
|
DBG("try to move motor%d of mcu %d for %d steps", motnum, mcu, steps);
|
|
snprintf(buf, 32, "%dM%dM%d", mcu, motnum, steps);
|
|
return tty_sendcmd(buf);
|
|
}
|