diff --git a/examples/projects/product/custom_gate/src/custom_json_conversion.c b/examples/projects/product/custom_gate/src/custom_json_conversion.c index f94697004..6b749be8a 100644 --- a/examples/projects/product/custom_gate/src/custom_json_conversion.c +++ b/examples/projects/product/custom_gate/src/custom_json_conversion.c @@ -14,6 +14,10 @@ const char *Convert_CustomStringFromType(luos_type_t type) { return "point_2D"; } + if (type == POWER_TYPE) + { + return "power"; + } return NULL; } diff --git a/examples/projects/product/laser/lib/Galvo/galvo.c b/examples/projects/product/laser/lib/Galvo/galvo.c index d7c5d013b..9f673abd1 100644 --- a/examples/projects/product/laser/lib/Galvo/galvo.c +++ b/examples/projects/product/laser/lib/Galvo/galvo.c @@ -6,6 +6,7 @@ ******************************************************************************/ #include "galvo.h" #include "xy2-100.h" +#include "galvo_config.h" /******************************************************************************* * Definitions @@ -37,7 +38,7 @@ void Galvo_Init(void) revision_t revision = {.major = 1, .minor = 0, .build = 0}; Luos_CreateService(Galvo_MsgHandler, POINT_2D, "galvo", revision); stream = Streaming_CreateChannel(stream_buf, sizeof(stream_buf), 2 * sizeof(uint16_t)); - period = TimeOD_TimeFrom_s(1.0 / 10000.0); // Configure the trajectory samplerate at 100Hz + period = TimeOD_TimeFrom_s(1.0 / DEFAULT_SAMPLE_FREQUENCY); // Configure the trajectory samplerate at 100Hz control.flux = STOP; } diff --git a/examples/projects/product/laser/lib/Galvo/galvo_config.h b/examples/projects/product/laser/lib/Galvo/galvo_config.h index 2db89c415..194d40310 100644 --- a/examples/projects/product/laser/lib/Galvo/galvo_config.h +++ b/examples/projects/product/laser/lib/Galvo/galvo_config.h @@ -94,4 +94,9 @@ #define GALVO_BUFFER_SIZE 8000 // Buffer size need to be a multiple of 40 #endif +// ********************* DEFAULT VALUES ********************* +#ifndef DEFAULT_SAMPLE_FREQUENCY + #define DEFAULT_SAMPLE_FREQUENCY 10000.0 +#endif + #endif /* _GALVO_HAL_CONFIG_H_ */ diff --git a/examples/projects/product/laser/lib/laser/README.md b/examples/projects/product/laser/lib/laser/README.md new file mode 100644 index 000000000..8acc42a00 --- /dev/null +++ b/examples/projects/product/laser/lib/laser/README.md @@ -0,0 +1,21 @@ +Luos logo + +![](https://github.com/Luos-io/luos_engine/actions/workflows/build.yml/badge.svg) +[![](https://img.shields.io/github/license/Luos-io/luos_engine)](https://github.com/Luos-io/luos_engine/blob/master/LICENSE) + +[![](https://img.shields.io/badge/Luos-Documentation-34A3B4)](https://www.luos.io/docs/) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/luos/library/luos_engine.svg)](https://registry.platformio.org/libraries/luos_engine/luos_engine) + +[![](https://img.shields.io/discord/902486791658041364?label=Discord&logo=discord&style=social)](http://bit.ly/JoinLuosDiscord) +[![](https://img.shields.io/badge/LinkedIn-Share-0077B5?style=social&logo=linkedin)](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fgithub.com%2Fluos-io) + +# Laser driver for a CO2 cutting/engraving machine + +Driver for using a laser in your projects with Luos. + +# Linked project + +This driver is linked to the [laser project](../../Projects/NUCLEO_L476RG/laser). + +[![](https://img.shields.io/badge/Luos-Documentation-34A3B4)](https://www.luos.io) +[![](https://img.shields.io/badge/LinkedIn-Follow%20us-0077B5?style=flat&logo=linkedin)](https://www.linkedin.com/company/luos) diff --git a/examples/projects/product/laser/lib/laser/laser.c b/examples/projects/product/laser/lib/laser/laser.c new file mode 100644 index 000000000..c5e03f15c --- /dev/null +++ b/examples/projects/product/laser/lib/laser/laser.c @@ -0,0 +1,309 @@ +/****************************************************************************** + * @file laser + * @brief driver example a laser + * @author Luos + * @version 0.0.0 + ******************************************************************************/ +#include "laser.h" +#include "product_config.h" +#include "main.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +ratio_t stream_buf[4096]; +streaming_channel_t stream; +time_luos_t period; +control_t laser_control; +buffer_mode_t laser_buffer_mode = SINGLE; +TIM_TypeDef *pwmtimer = LASER_PWM_TIMER; + +/******************************************************************************* + * Function + ******************************************************************************/ +static void Laser_MsgHandler(service_t *service, const msg_t *msg); + +/****************************************************************************** + * @brief DRV_DCMotorHWInit + * @param None + * @return None + ******************************************************************************/ +static void laser_pwmInit(void) +// { +// /////////////////////////////// +// // GPIO Init +// /////////////////////////////// +// PWM_PIN_CLK(); + +// GPIO_InitTypeDef GPIO_InitStruct = {0}; + +// GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; +// GPIO_InitStruct.Pin = LASER_PWM_PIN; +// GPIO_InitStruct.Alternate = LASER_PWM_AF; +// HAL_GPIO_Init(LASER_PWM_PORT, &GPIO_InitStruct); + +// /////////////////////////////// +// // Timer PWM Init +// /////////////////////////////// +// LL_TIM_InitTypeDef TimerInit; +// LL_TIM_OC_InitTypeDef TimerConfigOC; +// LL_TIM_StructInit(&TimerInit); +// LL_TIM_OC_StructInit(&TimerConfigOC); + +// // initialize clock +// PWM_TIMER_CLK(); + +// TimerInit.Autoreload = PWM_PERIOD; +// TimerInit.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; +// TimerInit.CounterMode = LL_TIM_COUNTERMODE_UP; +// TimerInit.Prescaler = 0; +// TimerInit.RepetitionCounter = 0; + +// while (LL_TIM_Init(LASER_PWM_TIMER, &TimerInit) != SUCCESS) +// ; + +// TimerConfigOC.OCMode = LL_TIM_OCMODE_PWM1; +// TimerConfigOC.CompareValue = 0; +// while (LL_TIM_OC_Init(LASER_PWM_TIMER, LASER_PWM_CHANNEL, &TimerConfigOC) != SUCCESS) +// ; + +// LL_TIM_EnableCounter(LASER_PWM_TIMER); +// LL_TIM_CC_EnableChannel(LASER_PWM_TIMER, LASER_PWM_CHANNEL); +// } + +{ + /////////////////////////////// + // Timer PWM Init + /////////////////////////////// + // Initialize clock + PWM_TIMER_CLK(); + TIM_HandleTypeDef htim; + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + htim.Instance = LASER_PWM_TIMER; + htim.Init.Prescaler = 0; + htim.Init.CounterMode = TIM_COUNTERMODE_UP; + htim.Init.Period = PWM_PERIOD; + htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim.Init.RepetitionCounter = 0; + htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + if (HAL_TIM_PWM_Init(&htim) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = 0; + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; + sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; + if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, LASER_PWM_CHANNEL) != HAL_OK) + { + Error_Handler(); + } + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; + sBreakDeadTimeConfig.DeadTime = 0; + sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; + if (HAL_TIMEx_ConfigBreakDeadTime(&htim, &sBreakDeadTimeConfig) != HAL_OK) + { + Error_Handler(); + } + + HAL_TIM_Base_Init(&htim); + HAL_TIM_Base_Start(&htim); + HAL_TIM_PWM_Init(&htim); + HAL_TIM_PWM_Start(&htim, LASER_PWM_CHANNEL); + /////////////////////////////// + // GPIO Init + /////////////////////////////// + PWM_PIN_CLK(); + + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pin = LASER_PWM_PIN; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = LASER_PWM_AF; + HAL_GPIO_Init(LASER_PWM_PORT, &GPIO_InitStruct); +} + +/****************************************************************************** + * @brief init must be call in project init + * @param None + * @return None + ******************************************************************************/ +static void laser_pwmSetPower(ratio_t power) +{ + LASER_PWM_TIMER->CCR2 = (uint32_t)((RatioOD_RatioTo_Percent(power) * (PWM_PERIOD)) / 100.0); +} + +/****************************************************************************** + * @brief init must be call in project init + * @param None + * @return None + ******************************************************************************/ +void Laser_Init(void) +{ + // Init the timer generating the PWM + laser_pwmInit(); + + // Init the Luos service + revision_t revision = {.major = 1, .minor = 0, .build = 0}; + Luos_CreateService(Laser_MsgHandler, POWER_TYPE, "laser", revision); + stream = Streaming_CreateChannel(stream_buf, sizeof(stream_buf), sizeof(ratio_t)); + period = TimeOD_TimeFrom_s(1.0 / DEFAULT_SAMPLE_FREQUENCY); // Configure the trajectory samplerate at 100Hz + laser_control.flux = STOP; +} + +/****************************************************************************** + * @brief loop must be call in project loop + * @param None + * @return None + ******************************************************************************/ +void Laser_Loop(void) +{ + static time_luos_t last_time = {.raw = 0.0}; + if (laser_control.flux == PLAY) + { + // Check if it's time to update the trajectory + if ((Luos_Timestamp().raw - last_time.raw) > period.raw) + { + if ((Streaming_GetAvailableSampleNB(&stream) > 0)) + { + // We have available samples + // update the last time + last_time = Luos_Timestamp(); + // Get the next point + ratio_t point; + Streaming_GetSample(&stream, (void *)&point, 1); + // Send the point to the laser + laser_pwmSetPower(point); + } + else + { + // We don't have any available samples + switch (laser_buffer_mode) + { + case SINGLE: + // We are in single mode, we have to loop on the ring buffer + // Put the read pointer at the begining of the buffer + stream.sample_ptr = stream.ring_buffer; + // stop the trjectory + laser_control.flux = STOP; + break; + case CONTINUOUS: + // We are in continuous mode, we have to loop on the ring buffer + // Put the read pointer at the begining of the buffer + stream.sample_ptr = stream.ring_buffer; + // Get the first sample + if (Streaming_GetAvailableSampleNB(&stream) == 0) + { + // We don't have any new sample to compute + // stop the trajectory + laser_control.flux = STOP; + return; + } + // update the last time + last_time = Luos_Timestamp(); + // Get the next point + ratio_t point; + Streaming_GetSample(&stream, (void *)&point, 1); + // Send the point to the laser + laser_pwmSetPower(point); + break; + case STREAM: + // We are in stream mode, we don't have any new data so we stop the trajectory + laser_control.flux = STOP; + break; + default: + LUOS_ASSERT(0); + break; + } + } + } + } + else + { + // Stop the laser + ratio_t power; + power.raw = 0; + laser_pwmSetPower(power); + } +} + +/****************************************************************************** + * @brief Msg Handler call back when a msg receive for this service + * @param Service destination + * @param Msg receive + * @return None + ******************************************************************************/ +static void Laser_MsgHandler(service_t *service, const msg_t *msg) +{ + if (msg->header.cmd == GET_CMD) + { + // The laser don't send anything back + return; + } + if (msg->header.cmd == CONTROL) + { + // Get the laser_control value + ControlOD_ControlFromMsg(&laser_control, msg); + } + if (msg->header.cmd == RATIO) + { + if (laser_buffer_mode == STREAM) + { + // The laser is in single mode, we can consider it as a streaming of point that will be consumed + Luos_ReceiveStreaming(service, msg, &stream); + } + else + { + // The laser is in SINGLE or CONTINUOUS mode, The buffer need to be loaded with the trajectory and the laser will play it from the begining of the buffer to the end. + int size = Luos_ReceiveData(service, msg, (uint8_t *)&stream_buf); + if (size > 0) + { + LUOS_ASSERT(size <= sizeof(stream_buf)); + Streaming_ResetChannel(&stream); + Streaming_AddAvailableSampleNB(&stream, size / stream.data_size); + } + } + } + if (msg->header.cmd == BUFFER_MODE) + { + laser_buffer_mode = msg->data[0]; + } + if (msg->header.cmd == TIME) + { + // Get the time + TimeOD_TimeFromMsg(&period, msg); + } +} diff --git a/examples/projects/product/laser/lib/laser/laser.h b/examples/projects/product/laser/lib/laser/laser.h new file mode 100644 index 000000000..7ad3d0d33 --- /dev/null +++ b/examples/projects/product/laser/lib/laser/laser.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * @file laser + * @brief driver example a laser + * @author Luos + * @version 0.0.0 + ******************************************************************************/ +#ifndef LASER_H +#define LASER_H + +#include "luos_engine.h" +#include "main.h" +/******************************************************************************* + * Definitions + ******************************************************************************/ + +#define PWM_PIN_CLK() \ + do \ + { \ + __HAL_RCC_GPIOB_CLK_ENABLE(); \ + } while (0U) + +#define PWM_TIMER_CLK() \ + do \ + { \ + __HAL_RCC_TIM15_CLK_ENABLE(); \ + } while (0U) + +#define PWM_PERIOD 5000 - 1 + +#define LASER_PWM_PIN GPIO_PIN_15 +#define LASER_PWM_PORT GPIOB +#define LASER_PWM_AF GPIO_AF14_TIM15 + +#ifndef LASER_PWM_TIMER + #define LASER_PWM_TIMER TIM15 +#endif +#define LASER_PWM_CHANNEL TIM_CHANNEL_2 + +#ifndef DEFAULT_SAMPLE_FREQUENCY + #define DEFAULT_SAMPLE_FREQUENCY 10000.0 +#endif + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Function + ******************************************************************************/ +void Laser_Init(void); +void Laser_Loop(void); + +#endif /* LASER_H */ diff --git a/examples/projects/product/laser/lib/laser/library.json b/examples/projects/product/laser/lib/laser/library.json new file mode 100644 index 000000000..a85a25e80 --- /dev/null +++ b/examples/projects/product/laser/lib/laser/library.json @@ -0,0 +1,14 @@ +{ + "name": "Laser", + "keywords": "robus,network,microservice,luos,operating system,os,embedded,communication,service,ST", + "description": "a laser driver", + "version": "1.0.0", + "authors": { + "name": "Luos", + "url": "https://luos.io" + }, + "dependencies": { + "luos_engine": "^3.1.0" + }, + "licence": "MIT" +} diff --git a/examples/projects/product/laser/node_config.h b/examples/projects/product/laser/node_config.h index 38dfb3dc9..d178cb8af 100644 --- a/examples/projects/product/laser/node_config.h +++ b/examples/projects/product/laser/node_config.h @@ -47,7 +47,7 @@ * NBR_RETRY | 10 | Send Retry number in case of NACK or collision ******************************************************************************/ -#define MAX_LOCAL_SERVICE_NUMBER 1 +#define MAX_LOCAL_SERVICE_NUMBER 2 #define MAX_MSG_NB 50 #define MSG_BUFFER_SIZE 2048 diff --git a/examples/projects/product/laser/platformio.ini b/examples/projects/product/laser/platformio.ini index a72a8361f..a8d434ed9 100644 --- a/examples/projects/product/laser/platformio.ini +++ b/examples/projects/product/laser/platformio.ini @@ -22,6 +22,7 @@ lib_deps = luos_engine@^3.1.0 serial_network Galvo + Laser debug_tool = stlink upload_protocol = stlink diff --git a/examples/projects/product/laser/src/main.c b/examples/projects/product/laser/src/main.c index 364fa9c94..e6775087a 100644 --- a/examples/projects/product/laser/src/main.c +++ b/examples/projects/product/laser/src/main.c @@ -26,6 +26,7 @@ #include "luos_engine.h" #include "serial_network.h" #include "galvo.h" +#include "laser.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -90,6 +91,7 @@ int main(void) Luos_Init(); Serial_Init(); Galvo_Init(); + Laser_Init(); /* USER CODE END 2 */ /* Infinite loop */ @@ -102,6 +104,7 @@ int main(void) Luos_Loop(); Serial_Loop(); Galvo_Loop(); + Laser_Loop(); } /* USER CODE END 3 */ } diff --git a/examples/projects/product/power.py b/examples/projects/product/power.py new file mode 100644 index 000000000..7bf4b9a9c --- /dev/null +++ b/examples/projects/product/power.py @@ -0,0 +1,80 @@ +from pyluos.services.service import Service +import numpy as np + +class Power(Service): + + # control modes + _PLAY = 0 + _PAUSE = 1 + _STOP = 2 + + # buffer modes + _SINGLE = 0 + _CONTINUOUS = 1 + _STREAM = 2 + + def __init__(self, id, alias, device): + Service.__init__(self, 'Power', id, alias, device) + self._power = 0.0 + self._control = 0 + self._buffer_mode = self._SINGLE + self._sampling_freq = 100.0 + self._value = 0.0 + + @property + def power_ratio(self): + self._value + + @power_ratio.setter + def power_ratio(self, new_val): + data = [] + # check if new_val is a list + if hasattr(new_val, "__len__"): + # If this is a list , put all the tuple couple of values on a list + for i in range(len(new_val)): + data.append(min(max(new_val[i], 0.0), 100.0)) + # send those values in an optimized way as a table of uint16_t + self._push_data('power_ratio', [len(data) * 4], np.array(data, dtype=np.float32)) + else: + new_val = min(max(new_val, 0.0), 100.0) + self._value = new_val + self._push_value("power_ratio",new_val) + + def play(self): + self._control = self._PLAY + self._push_value('control', self._control) + + def pause(self): + self._control = self._PAUSE + self._push_value('control', self._control) + + def stop(self): + self._control = self._STOP + self._push_value('control', self._control) + + def single(self): + self._buffer_mode = self._SINGLE + self._push_value('buffer_mode', self._buffer_mode) + + def continuous(self): + self._buffer_mode = self._CONTINUOUS + self._push_value('buffer_mode', self._buffer_mode) + + def stream(self): + self._buffer_mode = self._STREAM + self._push_value('buffer_mode', self._buffer_mode) + + @property + def sampling_freq(self): + return self._sampling_freq + + @sampling_freq.setter + def sampling_freq(self, sampling_freq): + self._sampling_freq = sampling_freq + self._push_value("sampling_freq", sampling_freq) + + + def _update(self, new_state): + Service._update(self, new_state) + if 'linear_pos_2D' in new_state.keys(): + self._point = new_state['linear_pos_2D'] diff --git a/examples/projects/product/product_config.h b/examples/projects/product/product_config.h index b2c5d5004..6c17e2bfd 100644 --- a/examples/projects/product/product_config.h +++ b/examples/projects/product/product_config.h @@ -12,7 +12,8 @@ // Definition of a custom service type typedef enum { - POINT_2D = LUOS_LAST_TYPE + POINT_2D = LUOS_LAST_TYPE, + POWER_TYPE } custom_service_type_t; // Definition of a custom service command