Kommunikation hat funktioniert
This commit is contained in:
5
.gitignore
vendored
Executable file
5
.gitignore
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
# ignore all files in bin folder
|
||||
bin/
|
||||
|
||||
._*
|
||||
.DS_Store
|
||||
179
can/can_client.c
Executable file
179
can/can_client.c
Executable file
@@ -0,0 +1,179 @@
|
||||
|
||||
#include <can/can_client.h>
|
||||
#include <io/io.h>
|
||||
#include <mqtt/mqtt_client.h>
|
||||
|
||||
struct MOTOR_CONTROL_DATA motctrl[MOTOR_COUNT];
|
||||
struct CAN_INTERFACE_DATA intf_data[MOTOR_COUNT];
|
||||
|
||||
/// @brief Open socket of CAN interface for the given motor
|
||||
/// @param iMotorIndex
|
||||
/// @param ifacename
|
||||
/// @return
|
||||
int Can_OpenInterface(int iMotorIndex, const char * ifacename)
|
||||
{
|
||||
strcpy(intf_data[iMotorIndex].iface_name, ifacename);
|
||||
Can_SetMotorGear(iMotorIndex, 0);
|
||||
Can_SetMotorPower(iMotorIndex, 0);
|
||||
|
||||
// first we have to create a socket
|
||||
if ((intf_data[iMotorIndex].socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
|
||||
{
|
||||
printf("Could not create socket for motor %d!\n", iMotorIndex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// retrieve the interface index
|
||||
strcpy(intf_data[iMotorIndex].ifr.ifr_name, intf_data[iMotorIndex].iface_name);
|
||||
if (ioctl(intf_data[iMotorIndex].socket, SIOCGIFINDEX, &intf_data[iMotorIndex].ifr) < 0)
|
||||
{
|
||||
printf("Could not get interface index for motor %d!\n", iMotorIndex);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// bind the socket to the CAN interface
|
||||
memset(&intf_data[iMotorIndex].addr, 0, sizeof(intf_data[iMotorIndex].addr));
|
||||
intf_data[iMotorIndex].addr.can_family = AF_CAN;
|
||||
intf_data[iMotorIndex].addr.can_ifindex = intf_data[iMotorIndex].ifr.ifr_ifindex;
|
||||
if (bind(intf_data[iMotorIndex].socket, (struct sockaddr *)&intf_data[iMotorIndex].addr, sizeof(intf_data[iMotorIndex].addr)) < 0)
|
||||
{
|
||||
printf("Could not bind socket to inteface for motor %d!\n", iMotorIndex);
|
||||
return 3;
|
||||
}
|
||||
|
||||
printf("Interface %s (motor %d) opened!\n", ifacename, iMotorIndex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Close socket of CAN interface for the given motor
|
||||
/// @param iMotorIndex
|
||||
void Can_CloseInterface(int iMotorIndex)
|
||||
{
|
||||
if (close(intf_data[iMotorIndex].socket) < 0)
|
||||
{
|
||||
printf("Could not close socket of motor %d!\n", iMotorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Interface %s (motor %d) closed.\n", intf_data[iMotorIndex].iface_name, iMotorIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Set gear for the given motor
|
||||
/// @param iMotorIndex
|
||||
/// @param iGear (-1=reverse, 0=neutral, 1=forward)
|
||||
void Can_SetMotorGear(int iMotorIndex, int iGear)
|
||||
{
|
||||
if (iGear > 0)
|
||||
{
|
||||
if (motctrl[iMotorIndex].iMotorGear != MOTOR_GEAR_FORWARD)
|
||||
{
|
||||
MqttClient_Publish_MotorGear(iMotorIndex, iGear);
|
||||
motctrl[iMotorIndex].iMotorGear = MOTOR_GEAR_FORWARD;
|
||||
}
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, HIGH);
|
||||
printf("Motor[%d]: Set gear forward.\n", iMotorIndex);
|
||||
}
|
||||
else if (iGear < 0)
|
||||
{
|
||||
if (motctrl[iMotorIndex].iMotorGear != MOTOR_GEAR_REVERSE)
|
||||
{
|
||||
MqttClient_Publish_MotorGear(iMotorIndex, iGear);
|
||||
motctrl[iMotorIndex].iMotorGear = MOTOR_GEAR_REVERSE;
|
||||
}
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, HIGH);
|
||||
printf("Motor[%d]: Set gear reverse.\n", iMotorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (motctrl[iMotorIndex].iMotorGear != MOTOR_GEAR_NEUTRAL)
|
||||
{
|
||||
MqttClient_Publish_MotorGear(iMotorIndex, iGear);
|
||||
motctrl[iMotorIndex].iMotorGear = MOTOR_GEAR_NEUTRAL;
|
||||
}
|
||||
Can_SetMotorPower(iMotorIndex, MOTOR_PWR_MIN_PCT);
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, LOW);
|
||||
printf("Motor[%d]: Set gear neutral.\n", iMotorIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Set power for the given motor
|
||||
/// @param iMotorIndex
|
||||
/// @param iPower (Range: 0..100)
|
||||
void Can_SetMotorPower(int iMotorIndex, int iPower)
|
||||
{
|
||||
if (iPower <= MOTOR_PWR_MIN_PCT)
|
||||
{
|
||||
motctrl[iMotorIndex].iMotorPowerPct = MOTOR_PWR_MIN_PCT;
|
||||
}
|
||||
else if (iPower >= MOTOR_PWR_MAX_PCT)
|
||||
{
|
||||
motctrl[iMotorIndex].iMotorPowerPct = MOTOR_PWR_MAX_PCT;
|
||||
}
|
||||
else
|
||||
{
|
||||
motctrl[iMotorIndex].iMotorPowerPct = iPower;
|
||||
}
|
||||
MqttClient_Publish_MotorPower(iMotorIndex, motctrl[iMotorIndex].iMotorPowerPct);
|
||||
|
||||
motctrl[iMotorIndex].iMotorPower = 250 * motctrl[iMotorIndex].iMotorPowerPct / 100;
|
||||
|
||||
printf("Motor[%d]: Set power to %d%% -> %d\n",
|
||||
iMotorIndex, motctrl[iMotorIndex].iMotorPowerPct, motctrl[iMotorIndex].iMotorPower);
|
||||
}
|
||||
|
||||
|
||||
/// @brief Send CAN protocol for motor gear for the given motor
|
||||
/// @param iMotorIndex
|
||||
void Can_TransmitMotorGear(int iMotorIndex)
|
||||
{
|
||||
// Transmission rate: 100ms
|
||||
struct can_frame frame;
|
||||
|
||||
frame.can_id = 0x18F005D0;
|
||||
frame.can_id |= CAN_EFF_FLAG;
|
||||
frame.can_dlc = 8;
|
||||
frame.data[0] = motctrl[iMotorIndex].iMotorGear;
|
||||
frame.data[1] = 0xFF;
|
||||
frame.data[2] = 0xFF;
|
||||
frame.data[3] = 0xFF;
|
||||
frame.data[4] = 0xFF;
|
||||
frame.data[5] = 0xFF;
|
||||
frame.data[6] = 0xFF;
|
||||
frame.data[7] = 0xFF;
|
||||
|
||||
if (write(intf_data[iMotorIndex].socket, &frame, sizeof(frame)) != sizeof(frame))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Send CAN protocol for motor power for the given motor
|
||||
/// @param iMotorIndex
|
||||
void Can_TransmitMotorPower(int iMotorIndex)
|
||||
{
|
||||
// Transmission rate: 50ms
|
||||
struct can_frame frame;
|
||||
|
||||
frame.can_id = 0x0CF003D0;
|
||||
frame.can_id |= CAN_EFF_FLAG;
|
||||
frame.can_dlc = 8;
|
||||
frame.data[0] = 0xFF;
|
||||
frame.data[1] = motctrl[iMotorIndex].iMotorPower; // motor power 0 = 0%, 250 = 100%
|
||||
frame.data[2] = 0xFF;
|
||||
frame.data[3] = 0xFF;
|
||||
frame.data[4] = 0xFF;
|
||||
frame.data[5] = 0xFF;
|
||||
frame.data[6] = 0xFF;
|
||||
frame.data[7] = 0xFF;
|
||||
|
||||
if (write(intf_data[iMotorIndex].socket, &frame, sizeof(frame)) != sizeof(frame))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
52
can/can_client.h
Executable file
52
can/can_client.h
Executable file
@@ -0,0 +1,52 @@
|
||||
#if !defined(__CAN_CLIENT_H__)
|
||||
#define __CAN_CLIENT_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
|
||||
#define MOTOR_COUNT 2
|
||||
|
||||
// motor gear: 0x7C=reverse, 0x7D=neutral, 0x7E=forward
|
||||
#define MOTOR_GEAR_REVERSE 0x7C
|
||||
#define MOTOR_GEAR_NEUTRAL 0x7D
|
||||
#define MOTOR_GEAR_FORWARD 0x7E
|
||||
|
||||
#define MOTOR_PWR_MIN_PCT 5
|
||||
#define MOTOR_PWR_MAX_PCT 100
|
||||
#define MOTOR_PWR_STEP 14
|
||||
|
||||
struct MOTOR_CONTROL_DATA
|
||||
{
|
||||
int iMotorGear;
|
||||
int iMotorPower;
|
||||
int iMotorPowerPct;
|
||||
};
|
||||
extern struct MOTOR_CONTROL_DATA motctrl[MOTOR_COUNT];
|
||||
|
||||
struct CAN_INTERFACE_DATA
|
||||
{
|
||||
int socket;
|
||||
char iface_name[20];
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_can addr;
|
||||
};
|
||||
|
||||
int Can_OpenInterface(int iMotorIndex, const char * ifacename);
|
||||
void Can_CloseInterface(int iMotorIndex);
|
||||
|
||||
void Can_SetMotorGear(int iMotorIndex, int iGear);
|
||||
void Can_SetMotorPower(int iMotorIndex, int iPower);
|
||||
|
||||
void Can_TransmitMotorGear(int iMotorIndex);
|
||||
void Can_TransmitMotorPower(int iMotorIndex);
|
||||
|
||||
#endif
|
||||
211
io/io.c
Executable file
211
io/io.c
Executable file
@@ -0,0 +1,211 @@
|
||||
|
||||
#include "io.h"
|
||||
#include <can/can_client.h>
|
||||
|
||||
struct GPIO_KEY_DATA gpioKeyStop;
|
||||
struct GPIO_KEY_DATA gpioKeyPwrUp;
|
||||
struct GPIO_KEY_DATA gpioKeyPwrDown;
|
||||
|
||||
int IO_Init()
|
||||
{
|
||||
if (wiringPiSetupPinType(WPI_PIN_BCM))
|
||||
{
|
||||
printf("IO: Set up wiringPi failed!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// IO-Pins für Tasten konfigurieren
|
||||
SetupKeyPin(&gpioKeyStop, GPIO_KEY_STOP);
|
||||
SetupKeyPin(&gpioKeyPwrUp, GPIO_KEY_PWRUP);
|
||||
SetupKeyPin(&gpioKeyPwrDown, GPIO_KEY_PWRDOWN);
|
||||
|
||||
// IO-Pins für Ausgänge konfigurieren
|
||||
SetupOutputPin(GPIO_LED_MOTRUN);
|
||||
|
||||
// Einschaltsequenz
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, HIGH);
|
||||
delay(500);
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, LOW);
|
||||
delay(500);
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, HIGH);
|
||||
delay(500);
|
||||
WriteOutputPin(GPIO_LED_MOTRUN, LOW);
|
||||
delay(500);
|
||||
|
||||
printf("IO initialized successfull!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SetupKeyPin(struct GPIO_KEY_DATA *pdata, int iKeyPin)
|
||||
{
|
||||
pdata->iKeyPin = iKeyPin;
|
||||
pdata->iKeyValue = 0;
|
||||
pdata->iKeyRisingEdge = 0;
|
||||
pdata->iKeyFallingEdge = 0;
|
||||
pdata->nHighCycleCounter = 0;
|
||||
pdata->nLowCycleCounter = 0;
|
||||
pdata->iKeyPressedCycleCounter = 0;
|
||||
pdata->iKeyRepeatCycleCounter = 0;
|
||||
|
||||
// Wenn Eingang verwendet wird
|
||||
if (pdata->iKeyPin > 0)
|
||||
{
|
||||
pinMode(pdata->iKeyPin, INPUT);
|
||||
pullUpDnControl(pdata->iKeyPin, PUD_UP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReadKey(struct GPIO_KEY_DATA *pdata)
|
||||
{
|
||||
if (pdata->iKeyPin > 0)
|
||||
{
|
||||
int newval = pdata->iKeyValue;
|
||||
|
||||
if (digitalRead(pdata->iKeyPin) == LOW) // invertierte Logik weil wir PullUp-Widerstand bei Betätigung auf low ziehen
|
||||
{
|
||||
// Signal liegt an
|
||||
if (pdata->nLowCycleCounter > 0)
|
||||
{
|
||||
pdata->nLowCycleCounter--;
|
||||
}
|
||||
|
||||
if (pdata->nHighCycleCounter < KEY_RISING_FILTERCYCLES)
|
||||
{
|
||||
pdata->nHighCycleCounter++;
|
||||
if (pdata->nHighCycleCounter >= KEY_RISING_FILTERCYCLES)
|
||||
{
|
||||
// gewünschte Anzahl Zyklen stabil
|
||||
newval = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Signal liegt nicht an
|
||||
if (pdata->nHighCycleCounter > 0)
|
||||
{
|
||||
pdata->nHighCycleCounter--;
|
||||
}
|
||||
|
||||
if (pdata->nLowCycleCounter < KEY_FALLING_FILTERCYCLES)
|
||||
{
|
||||
pdata->nLowCycleCounter++;
|
||||
if (pdata->nLowCycleCounter >= KEY_FALLING_FILTERCYCLES)
|
||||
{
|
||||
// gewünschte Anzahl Zyklen stabil
|
||||
newval = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newval && !pdata->iKeyValue)
|
||||
{
|
||||
// Taster wurde betätigt
|
||||
pdata->iKeyRisingEdge = 1;
|
||||
pdata->iKeyValue = newval;
|
||||
pdata->iKeyPressedCycleCounter = 0;
|
||||
pdata->iKeyRepeatCycleCounter = 0;
|
||||
}
|
||||
else if (pdata->iKeyValue && !newval)
|
||||
{
|
||||
// Taster wurde losgelassen
|
||||
pdata->iKeyFallingEdge = 1;
|
||||
pdata->iKeyValue = newval;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keine Änderung
|
||||
pdata->iKeyRisingEdge = 0;
|
||||
pdata->iKeyFallingEdge = 0;
|
||||
|
||||
if (pdata->iKeyValue)
|
||||
{
|
||||
// Wenn Taste gedrückt ist
|
||||
if (pdata->iKeyPressedCycleCounter < KEY_START_REPEAT_CYCLECOUNT)
|
||||
{
|
||||
// Zyklen zählen
|
||||
pdata->iKeyPressedCycleCounter++;
|
||||
}
|
||||
|
||||
if (pdata->iKeyPressedCycleCounter >= KEY_START_REPEAT_CYCLECOUNT)
|
||||
{
|
||||
// Wenn Taste länger als KEY_START_REPEAT_CYCLECOUNT gedrückt ist
|
||||
pdata->iKeyRepeatCycleCounter++;
|
||||
if (pdata->iKeyRepeatCycleCounter >= KEY_REPEAT_CYCLECOUNT)
|
||||
{
|
||||
// alle KEY_REPEAT_CYCLECOUNT Zyklen Tastendruck signalisieren
|
||||
pdata->iKeyRisingEdge = 1;
|
||||
pdata->iKeyRepeatCycleCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SetupOutputPin(int iOutPin)
|
||||
{
|
||||
if (iOutPin > 0)
|
||||
{
|
||||
pinMode(iOutPin, OUTPUT);
|
||||
digitalWrite(iOutPin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteOutputPin(int iOutPin, int iValue)
|
||||
{
|
||||
if (iOutPin > 0)
|
||||
{
|
||||
digitalWrite(iOutPin, iValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IO_DoCyclic()
|
||||
{
|
||||
ReadKey(&gpioKeyStop);
|
||||
ReadKey(&gpioKeyPwrUp);
|
||||
ReadKey(&gpioKeyPwrDown);
|
||||
|
||||
if (gpioKeyStop.iKeyValue)
|
||||
{
|
||||
// Stop-Taste betätigt -> hat Vorrang vor den anderen Tasten
|
||||
if (gpioKeyStop.iKeyRisingEdge)
|
||||
{
|
||||
Can_SetMotorGear(0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gpioKeyPwrUp.iKeyRisingEdge)
|
||||
{
|
||||
// Leistung erhöhen
|
||||
if (motctrl[0].iMotorGear == MOTOR_GEAR_NEUTRAL)
|
||||
{
|
||||
Can_SetMotorGear(0, 1);
|
||||
Can_SetMotorPower(0, MOTOR_PWR_MIN_PCT);
|
||||
}
|
||||
else
|
||||
{
|
||||
Can_SetMotorPower(0, motctrl[0].iMotorPowerPct + MOTOR_PWR_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
if (gpioKeyPwrDown.iKeyRisingEdge)
|
||||
{
|
||||
// Leistung verringern
|
||||
if (motctrl[0].iMotorPowerPct > MOTOR_PWR_MIN_PCT)
|
||||
{
|
||||
Can_SetMotorPower(0, motctrl[0].iMotorPowerPct - MOTOR_PWR_STEP);
|
||||
}
|
||||
else
|
||||
{
|
||||
Can_SetMotorGear(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
io/io.h
Executable file
37
io/io.h
Executable file
@@ -0,0 +1,37 @@
|
||||
#if !defined(__IO_H__)
|
||||
#define __IO_H__
|
||||
|
||||
#include <wiringPi.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define GPIO_LED_MOTRUN 17 // GPIO Pin fuer LED Motor läuft
|
||||
|
||||
#define GPIO_KEY_STOP 26 // GPIO Pin fuer Taster Stop
|
||||
#define GPIO_KEY_PWRUP 5 // GPIO Pin fuer Taster Leistung-Erhöhen
|
||||
#define GPIO_KEY_PWRDOWN 6 // GPIO Pin fuer Taster Leistung-Verringern
|
||||
|
||||
#define KEY_RISING_FILTERCYCLES 5 // Filterwert für Eingänge steigende Flanke
|
||||
#define KEY_FALLING_FILTERCYCLES 15 // Filterwert für Eingänge (Zyklen-Zähler)
|
||||
#define KEY_START_REPEAT_CYCLECOUNT 50 // Anzahl Zyklen, nach denen Wiederholungen beginnen
|
||||
#define KEY_REPEAT_CYCLECOUNT 50 // Anzahl Zyklen, nach den wiederholt wird
|
||||
|
||||
// Datenstruktur für einen Taster
|
||||
struct GPIO_KEY_DATA
|
||||
{
|
||||
int iKeyPin;
|
||||
int iKeyValue;
|
||||
int iKeyRisingEdge;
|
||||
int iKeyFallingEdge;
|
||||
unsigned int nHighCycleCounter;
|
||||
unsigned int nLowCycleCounter;
|
||||
int iKeyPressedCycleCounter;
|
||||
int iKeyRepeatCycleCounter;
|
||||
};
|
||||
|
||||
int IO_Init();
|
||||
void IO_DoCyclic();
|
||||
void SetupKeyPin(struct GPIO_KEY_DATA *pdata, int iKeyPin);
|
||||
void SetupOutputPin(int iOutPin);
|
||||
void WriteOutputPin(int iOutPin, int iValue);
|
||||
|
||||
#endif
|
||||
253
main.c
Executable file
253
main.c
Executable file
@@ -0,0 +1,253 @@
|
||||
|
||||
#include <main.h>
|
||||
#include <mqtt/mqtt_client.h>
|
||||
#include <can/can_client.h>
|
||||
#include <io/io.h>
|
||||
|
||||
// Period info of the realtime task
|
||||
struct period_info pinfo;
|
||||
int iThreadControl = 0; // 0: thread is running, <0: thread shall exit, >0 thread has exited
|
||||
|
||||
/// @brief Initialize period_info with period_ms for cyclic task
|
||||
/// @param period_ms
|
||||
/// @param pinfo
|
||||
static void periodic_task_init(long period_ms, struct period_info *pinfo)
|
||||
{
|
||||
/* for simplicity, hardcoding a 1ms period */
|
||||
pinfo->period_ns = period_ms * 1000000;
|
||||
pinfo->cyclecounter = 0;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &(pinfo->next_period));
|
||||
pinfo->fStartTime = (float)pinfo->next_period.tv_sec + ((float)pinfo->next_period.tv_nsec / 1000000000.0);
|
||||
}
|
||||
|
||||
|
||||
/// @brief Wait in cyclic task the rest of time until the next cycle
|
||||
/// @param pinfo
|
||||
static void wait_rest_of_period(struct period_info *pinfo)
|
||||
{
|
||||
pinfo->next_period.tv_nsec += pinfo->period_ns;
|
||||
|
||||
while (pinfo->next_period.tv_nsec >= 1000000000) {
|
||||
/* timespec nsec overflow */
|
||||
pinfo->next_period.tv_sec++;
|
||||
pinfo->next_period.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
/* for simplicity, ignoring possibilities of signal wakes */
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &pinfo->next_period, NULL);
|
||||
}
|
||||
|
||||
|
||||
/// @brief Get monotonic time in sec as float
|
||||
/// @return
|
||||
float clock_gettime_s()
|
||||
{
|
||||
struct timespec tm;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tm);
|
||||
return (float)tm.tv_sec + ((float)tm.tv_nsec / 1000000000.0);
|
||||
}
|
||||
|
||||
|
||||
/// @brief Cyclic task, is called from thread_func every ms
|
||||
/// @param pinfo
|
||||
static void do_cyclic_1ms(struct period_info *pinfo)
|
||||
{
|
||||
uint16_t nCalled = 0;
|
||||
|
||||
if ((pinfo->cyclecounter % 10) == 0)
|
||||
{
|
||||
// called every 10ms
|
||||
nCalled |= 0x1000;
|
||||
IO_DoCyclic();
|
||||
}
|
||||
|
||||
if ((pinfo->cyclecounter % 50) == 0)
|
||||
{
|
||||
// called every 50ms
|
||||
nCalled |= 0x0001;
|
||||
Can_TransmitMotorPower(0);
|
||||
}
|
||||
|
||||
if (((pinfo->cyclecounter + 10) % 100) == 0)
|
||||
{
|
||||
// called every 100ms
|
||||
nCalled |= 0x0010;
|
||||
Can_TransmitMotorGear(0);
|
||||
//printf("%.3f: 100ms-Cycle %ld...\n", clock_gettime_s() - pinfo->fStartTime, pinfo->cyclecounter);
|
||||
}
|
||||
|
||||
if (((pinfo->cyclecounter + 20) % 100) == 0)
|
||||
{
|
||||
// called every 100ms
|
||||
nCalled |= 0x0100;
|
||||
MqttClient_Refresher();
|
||||
}
|
||||
|
||||
if (((pinfo->cyclecounter + 30) % 500) == 0)
|
||||
{
|
||||
// called every 250ms
|
||||
MqttClient_Publisher();
|
||||
}
|
||||
|
||||
if (nCalled > 0)
|
||||
{
|
||||
//printf("%.3f: Called 0x%.4X at cycle %ld...\n", clock_gettime_s() - pinfo->fStartTime, nCalled, pinfo->cyclecounter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Our one and only realtime task
|
||||
/// @param data
|
||||
/// @return
|
||||
void *thread_func(void *data)
|
||||
{
|
||||
// Open CAN interface first motor
|
||||
if (Can_OpenInterface(0, "can0"))
|
||||
{
|
||||
printf("Can_OpenInterface() failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Connect to mqtt broker
|
||||
while (MqttClient_Connect() && (iThreadControl == 0))
|
||||
{
|
||||
printf("MqttClient_Connect() failed!\n");
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
// Initialize IO Ports
|
||||
if (IO_Init())
|
||||
{
|
||||
printf("IO_Init() failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// initialize cyclic task
|
||||
periodic_task_init(1, &pinfo);
|
||||
|
||||
// cyclic call of do_cyclic_1ms()
|
||||
while (iThreadControl == 0)
|
||||
{
|
||||
pinfo.cyclecounter++;
|
||||
if (pinfo.cyclecounter >= 86400000)
|
||||
{
|
||||
// Reset cycle counter every 24h
|
||||
pinfo.cyclecounter = 0;
|
||||
}
|
||||
do_cyclic_1ms(&pinfo);
|
||||
wait_rest_of_period(&pinfo);
|
||||
}
|
||||
|
||||
// Disconnect from mqtt broker
|
||||
MqttClient_Close();
|
||||
|
||||
// Close CAN interface
|
||||
Can_CloseInterface(0);
|
||||
|
||||
iThreadControl = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// @brief catch signals and set flag to terminate for the realtime thread
|
||||
/// @param signo
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
if ((signo == SIGINT) || (signo == SIGTERM))
|
||||
{
|
||||
printf("Received signal %d\n", signo);
|
||||
iThreadControl = -1; // signal realtime thread to exit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Hauptfunktion Echtzeit-Task erstellen und starten
|
||||
/// @param argc
|
||||
/// @param argv
|
||||
/// @return
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
struct sched_param param;
|
||||
pthread_attr_t attr;
|
||||
pthread_t thread;
|
||||
int ret;
|
||||
|
||||
// catch signals
|
||||
if (signal(SIGTERM, sig_handler) == SIG_ERR)
|
||||
{
|
||||
printf("Can't catch SIGTERM\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR)
|
||||
{
|
||||
printf("Can't catch SIGINT\n");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
/* Lock memory */
|
||||
if(mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
|
||||
{
|
||||
printf("mlockall failed: %m\n");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
/* Initialize pthread attributes (default values) */
|
||||
ret = pthread_attr_init(&attr);
|
||||
if (ret)
|
||||
{
|
||||
printf("init pthread attributes failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set a specific stack size */
|
||||
ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
|
||||
if (ret)
|
||||
{
|
||||
printf("pthread setstacksize failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set scheduler policy and priority of pthread */
|
||||
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
||||
if (ret)
|
||||
{
|
||||
printf("pthread setschedpolicy failed\n");
|
||||
goto out;
|
||||
}
|
||||
param.sched_priority = 99; // Priority between 1 (low) and 99() high)
|
||||
ret = pthread_attr_setschedparam(&attr, ¶m);
|
||||
if (ret)
|
||||
{
|
||||
printf("pthread setschedparam failed\n");
|
||||
goto out;
|
||||
}
|
||||
/* Use scheduling parameters of attr */
|
||||
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
||||
if (ret)
|
||||
{
|
||||
printf("pthread setinheritsched failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* Create a pthread with specified attributes */
|
||||
iThreadControl = 0;
|
||||
ret = pthread_create(&thread, &attr, thread_func, NULL);
|
||||
if (ret)
|
||||
{
|
||||
printf("create pthread failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// join the thread and wait for it to exit
|
||||
ret = pthread_join(thread, NULL);
|
||||
if (ret)
|
||||
{
|
||||
printf("faild to join thread!\n");
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
26
main.h
Executable file
26
main.h
Executable file
@@ -0,0 +1,26 @@
|
||||
#if !defined(__MAIN_H__)
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
struct period_info
|
||||
{
|
||||
struct timespec next_period;
|
||||
long period_ns;
|
||||
unsigned long cyclecounter;
|
||||
float fStartTime;
|
||||
};
|
||||
|
||||
extern struct period_info pinfo;
|
||||
|
||||
#endif
|
||||
26
makefile
Executable file
26
makefile
Executable file
@@ -0,0 +1,26 @@
|
||||
# UNAME = $(shell uname -o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wextra -Wall -std=gnu99 -I. -Wno-unused-parameter -Wno-unused-variable -Wno-duplicate-decl-specifier
|
||||
|
||||
|
||||
MQTT_SOURCES = mqtt/mqtt_client.c
|
||||
CAN_SOURCES = can/can_client.c
|
||||
IO_SOURCES = io/io.c
|
||||
PROG = bin/CanRtDriver
|
||||
BINDIR = bin
|
||||
|
||||
|
||||
all: $(BINDIR) $(PROG)
|
||||
|
||||
bin/CanRtDriver: main.c $(CAN_SOURCES) $(MQTT_SOURCES) $(IO_SOURCES)
|
||||
$(CC) $(CFLAGS) $^ -lpthread -lmosquitto -lwiringPi -o $@
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -rf $(BINDIR)
|
||||
|
||||
check: all
|
||||
./$(MQTT_C_UNITTESTS)
|
||||
281
mqtt/mqtt_client.c
Executable file
281
mqtt/mqtt_client.c
Executable file
@@ -0,0 +1,281 @@
|
||||
|
||||
|
||||
/*
|
||||
alternativer Client: libmosquitto-dev:
|
||||
https://mosquitto.org/api/files/mosquitto-h.html
|
||||
*/
|
||||
|
||||
#include <main.h>
|
||||
#include <mqtt/mqtt_client.h>
|
||||
#include <can/can_client.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mosquitto.h>
|
||||
|
||||
|
||||
const char* mqtt_topic_status_cyclecounter = "Pool/Status/CycleCounter";
|
||||
|
||||
const char* mqtt_topic_motor1_gear = "Pool/Motor1/Gear";
|
||||
int iMqttMotor1Gear = 0;
|
||||
const char* mqtt_topic_motor1_power = "Pool/Motor1/Power";
|
||||
int iMqttMotor1Power = 0;
|
||||
const char* mqtt_topic_motor2_gear = "Pool/Motor2/Gear";
|
||||
int iMqttMotor2Gear = 0;
|
||||
const char* mqtt_topic_motor2_power = "Pool/Motor2/Power";
|
||||
int iMqttMotor2Power = 0;
|
||||
|
||||
const char* mqtt_broker_addr = "127.0.0.1";
|
||||
const int mqtt_broker_port = 1883;
|
||||
struct mosquitto *mosq; /**< Libmosquito MQTT client instance. */
|
||||
|
||||
int iHadConnectError = 0;
|
||||
|
||||
void my_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
|
||||
{
|
||||
char* topic_value = (char *)malloc(message->payloadlen + 1);
|
||||
memcpy(topic_value, message->payload, message->payloadlen);
|
||||
topic_value[message->payloadlen] = '\0';
|
||||
|
||||
if (strcmp(message->topic, mqtt_topic_motor1_gear) == 0)
|
||||
{
|
||||
int val = 9999;
|
||||
if (sscanf(topic_value, "%d", &val))
|
||||
{
|
||||
printf("%ld: Received value for mqtt_topic_motor1_gear: %d\n", pinfo.cyclecounter, val);
|
||||
iMqttMotor1Gear = val;
|
||||
Can_SetMotorGear(0, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: Received mqtt_topic_motor1_gear: %s\n", pinfo.cyclecounter, topic_value);
|
||||
}
|
||||
}
|
||||
else if (strcmp(message->topic, mqtt_topic_motor1_power) == 0)
|
||||
{
|
||||
int val = 9999;
|
||||
if (sscanf(topic_value, "%d", &val))
|
||||
{
|
||||
printf("%ld: Received value for mqtt_topic_motor1_power: %d\n", pinfo.cyclecounter, val);
|
||||
iMqttMotor1Power = val;
|
||||
Can_SetMotorPower(0, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: Received mqtt_topic_motor1_power: %s\n", pinfo.cyclecounter, topic_value);
|
||||
}
|
||||
}
|
||||
else if (strcmp(message->topic, mqtt_topic_motor2_gear) == 0)
|
||||
{
|
||||
int val = 9999;
|
||||
if (sscanf(topic_value, "%d", &val))
|
||||
{
|
||||
printf("%ld: Received value for mqtt_topic_motor2_gear: %d\n", pinfo.cyclecounter, val);
|
||||
iMqttMotor2Gear = val;
|
||||
Can_SetMotorGear(1, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: Received mqtt_topic_motor2_gear: %s\n", pinfo.cyclecounter, topic_value);
|
||||
}
|
||||
}
|
||||
else if (strcmp(message->topic, mqtt_topic_motor2_power) == 0)
|
||||
{
|
||||
int val = 9999;
|
||||
if (sscanf(topic_value, "%d", &val))
|
||||
{
|
||||
printf("%ld: Received value for mqtt_topic_motor2_power: %d\n", pinfo.cyclecounter, val);
|
||||
iMqttMotor2Power = val;
|
||||
Can_SetMotorPower(1, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: Received mqtt_topic_motor2_power: %s\n", pinfo.cyclecounter, topic_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: Received publish('%s'): %s\n", pinfo.cyclecounter, message->topic, topic_value);
|
||||
}
|
||||
|
||||
free(topic_value);
|
||||
}
|
||||
|
||||
|
||||
int MqttClient_Connect()
|
||||
{
|
||||
int major, minor, revision;
|
||||
|
||||
mosquitto_lib_version(&major, &minor, &revision);
|
||||
printf("Libmosquitto version: %d.%d.%d\n", major, minor, revision);
|
||||
|
||||
// libmosquitto initialization
|
||||
mosquitto_lib_init();
|
||||
|
||||
// create an instance of a mosquitto lib object
|
||||
mosq = mosquitto_new(NULL, true, NULL);
|
||||
if (mosq == NULL)
|
||||
{
|
||||
printf("Failed to create mosquitto client!/n");
|
||||
iHadConnectError++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Define a function which will be called by libmosquitto client every time when there is a new MQTT message
|
||||
mosquitto_message_callback_set(mosq, my_message_callback);
|
||||
|
||||
// Connect to MQTT broker
|
||||
if (mosquitto_connect(mosq, mqtt_broker_addr, mqtt_broker_port, 60) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("Error: connecting to MQTT broker failed\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (iHadConnectError)
|
||||
{
|
||||
/* Wenn wir Verbindungsfehler hatten, dann befinden wir uns wohl in Boot-Prozess und der Mosquitto ist
|
||||
gerade erst gestartet. Wir müssen hier etwas warten, sonst funktioniert das Subscriben nicht */
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
// publish all topics we want to subscribe
|
||||
char message[10];
|
||||
snprintf(message, sizeof(message), "0");
|
||||
if (mosquitto_publish(mosq, NULL, mqtt_topic_motor1_gear, strlen(message), &message, 0, false) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_publish(mqtt_topic_motor1_gear) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 10;
|
||||
}
|
||||
if (mosquitto_publish(mosq, NULL, mqtt_topic_motor1_power, strlen(message), &message, 0, false) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_publish(mqtt_topic_motor1_power) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 11;
|
||||
}
|
||||
if (mosquitto_publish(mosq, NULL, mqtt_topic_motor2_gear, strlen(message), &message, 0, false) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_publish(mqtt_topic_motor2_gear) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 12;
|
||||
}
|
||||
if (mosquitto_publish(mosq, NULL, mqtt_topic_motor2_power, strlen(message), &message, 0, false) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_publish(mqtt_topic_motor2_power) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 13;
|
||||
}
|
||||
|
||||
// subscribe all needed topics
|
||||
if (mosquitto_subscribe(mosq, NULL, mqtt_topic_motor1_gear, 0) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_subscribe(mqtt_topic_motor1_gear) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 20;
|
||||
}
|
||||
if (mosquitto_subscribe(mosq, NULL, mqtt_topic_motor1_power, 0) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_subscribe(mqtt_topic_motor1_power) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 21;
|
||||
}
|
||||
if (mosquitto_subscribe(mosq, NULL, mqtt_topic_motor2_gear, 0) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_subscribe(mqtt_topic_motor2_gear) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 22;
|
||||
}
|
||||
if (mosquitto_subscribe(mosq, NULL, mqtt_topic_motor2_power, 0) != MOSQ_ERR_SUCCESS)
|
||||
{
|
||||
printf("mosquitto_subscribe(mqtt_topic_motor2_power) fehlgeschlagen!\n");
|
||||
MqttClient_Close();
|
||||
iHadConnectError++;
|
||||
return 23;
|
||||
}
|
||||
|
||||
printf("MQTT: Connected successfull!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void MqttClient_Close()
|
||||
{
|
||||
//Clean up/destroy objects created by libmosquitto
|
||||
mosquitto_destroy(mosq);
|
||||
mosquitto_lib_cleanup();
|
||||
|
||||
printf("MQTT: Disconnected!\n");
|
||||
}
|
||||
|
||||
|
||||
void MqttClient_Refresher()
|
||||
{
|
||||
mosquitto_loop(mosq, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
void MqttClient_Publisher()
|
||||
{
|
||||
char message[100];
|
||||
|
||||
snprintf(message, sizeof(message), "%ld", pinfo.cyclecounter);
|
||||
mosquitto_publish(mosq, NULL, mqtt_topic_status_cyclecounter, strlen(message), message, 0, false);
|
||||
}
|
||||
|
||||
|
||||
void MqttClient_Publish_MotorGear(int iMotorIndex, int iGear)
|
||||
{
|
||||
if (iMotorIndex == 0)
|
||||
{
|
||||
if (iGear != iMqttMotor1Gear)
|
||||
{
|
||||
iMqttMotor1Gear = iGear;
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "%d", iGear);
|
||||
mosquitto_publish(mosq, NULL, mqtt_topic_motor1_gear, strlen(message), message, 0, false);
|
||||
}
|
||||
}
|
||||
else if (iMotorIndex == 1)
|
||||
{
|
||||
if (iGear != iMqttMotor2Gear)
|
||||
{
|
||||
iMqttMotor2Gear = iGear;
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "%d", iGear);
|
||||
mosquitto_publish(mosq, NULL, mqtt_topic_motor2_gear, strlen(message), message, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MqttClient_Publish_MotorPower(int iMotorIndex, int iPower)
|
||||
{
|
||||
if (iMotorIndex == 0)
|
||||
{
|
||||
if (iPower != iMqttMotor1Power)
|
||||
{
|
||||
iMqttMotor1Power = iPower;
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "%d", iPower);
|
||||
mosquitto_publish(mosq, NULL, mqtt_topic_motor1_power, strlen(message), message, 0, false);
|
||||
}
|
||||
}
|
||||
else if (iMotorIndex == 1)
|
||||
{
|
||||
if (iPower != iMqttMotor2Power)
|
||||
{
|
||||
iMqttMotor2Power = iPower;
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "%d", iPower);
|
||||
mosquitto_publish(mosq, NULL, mqtt_topic_motor2_power, strlen(message), message, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
mqtt/mqtt_client.h
Executable file
13
mqtt/mqtt_client.h
Executable file
@@ -0,0 +1,13 @@
|
||||
#if !defined(__MQTT_CLIENT_H__)
|
||||
#define __MQTT_CLIENT_H__
|
||||
|
||||
#define USE_MOSQUITTO_LIB
|
||||
|
||||
int MqttClient_Connect();
|
||||
void MqttClient_Close();
|
||||
void MqttClient_Refresher();
|
||||
void MqttClient_Publisher();
|
||||
void MqttClient_Publish_MotorGear(int iMotorIndex, int iGear);
|
||||
void MqttClient_Publish_MotorPower(int iMotorIndex, int iPower);
|
||||
|
||||
#endif
|
||||
23
scripts/CanRtDriver.service
Executable file
23
scripts/CanRtDriver.service
Executable file
@@ -0,0 +1,23 @@
|
||||
## Service Definition
|
||||
## Datei gehört in /etc/systemd/system
|
||||
## aktivieren dann mit:
|
||||
## - sudo systemctl daemon-reload
|
||||
## - sudo systemctl enable CanRtDriver.service
|
||||
## - sudo systemctl start CanRtDriver.service
|
||||
|
||||
[Unit]
|
||||
Description=CAN-Bus Treiber für Gegenstromanlage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/home/veltmann/Documents/CanRtDriver/bin/CanRtDriver
|
||||
# Oder: ExecStart=/usr/bin/python3 /home/pi/meinprogramm.py
|
||||
WorkingDirectory=/home/veltmann/Documents/CanRtDriver/bin
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
Restart=always
|
||||
#User=veltmann
|
||||
# Optional: Group=meinbenutzergruppe
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
7
scripts/can_link_up.sh
Executable file
7
scripts/can_link_up.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
ip link set can0 up type can bitrate 250000
|
||||
ip link set can1 up type can bitrate 250000
|
||||
ifconfig can0 txqueuelen 65536
|
||||
ifconfig can1 txqueuelen 65536
|
||||
|
||||
2
scripts/candump.sh
Executable file
2
scripts/candump.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
candump can1
|
||||
|
||||
Reference in New Issue
Block a user