commit 9b3c4b3fd23160858dbf8c96e9be384b7fe9dd3c Author: Bernhard Date: Sat Dec 6 19:20:49 2025 +0100 Kommunikation hat funktioniert diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..0bdc11d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# ignore all files in bin folder +bin/ + +._* +.DS_Store diff --git a/can/can_client.c b/can/can_client.c new file mode 100755 index 0000000..3e56234 --- /dev/null +++ b/can/can_client.c @@ -0,0 +1,179 @@ + +#include +#include +#include + +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)) + { + + } +} diff --git a/can/can_client.h b/can/can_client.h new file mode 100755 index 0000000..fc21011 --- /dev/null +++ b/can/can_client.h @@ -0,0 +1,52 @@ +#if !defined(__CAN_CLIENT_H__) +#define __CAN_CLIENT_H__ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 diff --git a/io/io.c b/io/io.c new file mode 100755 index 0000000..a2c6ef9 --- /dev/null +++ b/io/io.c @@ -0,0 +1,211 @@ + +#include "io.h" +#include + +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); + } + } + } +} diff --git a/io/io.h b/io/io.h new file mode 100755 index 0000000..418586a --- /dev/null +++ b/io/io.h @@ -0,0 +1,37 @@ +#if !defined(__IO_H__) +#define __IO_H__ + +#include +#include + +#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 diff --git a/main.c b/main.c new file mode 100755 index 0000000..c1b72c7 --- /dev/null +++ b/main.c @@ -0,0 +1,253 @@ + +#include +#include +#include +#include + +// 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; +} + diff --git a/main.h b/main.h new file mode 100755 index 0000000..8907a5a --- /dev/null +++ b/main.h @@ -0,0 +1,26 @@ +#if !defined(__MAIN_H__) +#define __MAIN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct period_info +{ + struct timespec next_period; + long period_ns; + unsigned long cyclecounter; + float fStartTime; +}; + +extern struct period_info pinfo; + +#endif \ No newline at end of file diff --git a/makefile b/makefile new file mode 100755 index 0000000..8b73eb4 --- /dev/null +++ b/makefile @@ -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) diff --git a/mqtt/mqtt_client.c b/mqtt/mqtt_client.c new file mode 100755 index 0000000..b9b1313 --- /dev/null +++ b/mqtt/mqtt_client.c @@ -0,0 +1,281 @@ + + +/* + alternativer Client: libmosquitto-dev: + https://mosquitto.org/api/files/mosquitto-h.html +*/ + +#include +#include +#include +#include + +#include + + +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); + } + } +} \ No newline at end of file diff --git a/mqtt/mqtt_client.h b/mqtt/mqtt_client.h new file mode 100755 index 0000000..cecee53 --- /dev/null +++ b/mqtt/mqtt_client.h @@ -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 diff --git a/scripts/CanRtDriver.service b/scripts/CanRtDriver.service new file mode 100755 index 0000000..2234218 --- /dev/null +++ b/scripts/CanRtDriver.service @@ -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 diff --git a/scripts/can_link_up.sh b/scripts/can_link_up.sh new file mode 100755 index 0000000..1c20b14 --- /dev/null +++ b/scripts/can_link_up.sh @@ -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 + diff --git a/scripts/candump.sh b/scripts/candump.sh new file mode 100755 index 0000000..e3cc501 --- /dev/null +++ b/scripts/candump.sh @@ -0,0 +1,2 @@ +candump can1 +