I need some help communicating between python's pyserial and an STM32f030R8 microcontroller. This is for a 2 DOF robot which has a python base GUI. The GUI will send, via serial communication, the coordinates for the robot and some other commands to engage or disengage the toolhead. On the python side, the relevant code snippets are the following:
def GoToCoords(X, Y):
Msg = "M," + "{0:0=4d}".format(int(X)) + "," + "{0:0=4d}".format(int(Y))
#Msg = "{0:0=4d}".format(int(X))
print(Msg)
ser.write(bytes(Msg, 'UTF-8'))
def DeployMarker():
Msg = "E,0000,0000"
print(Msg)
ser.write(bytes(Msg, 'UTF-8'))
def StowMarker():
Msg = "D,0000,0000"
print(Msg)
ser.write(bytes(Msg, 'UTF-8'))
# set up serial comms
print("Serial coms connecting...")
ser = serial.Serial('com4', 9600, timeout=10) # create Serial Object, baud = 9600, read times out after 10s
time.sleep(3) # delay 3 seconds to allow serial com to get established
print("Serial com connected")
... some code which builds the GUI and calls the above functions...
You can see the code is attempting to send the command type and the relevant data in the format M,####,####. The first character dictates what kind of command is being sent (M = move, E = engage toolhead, D = disengage toolhead).
Onto the hardware, the computer is connected to a HiLetgo FT232RL Mini USB to TTL Serial Converter Adapter Module (https://www.amazon.com/dp/B00IJXZQ7C?psc=1&ref=ppx_yo2ov_dt_b_product_details).
Next on the STM32 side we have the following code:
main.c:
#include "main.h"
#include "WhiteBoardRobotFuncts.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
const uint8_t MsgLength = 11;
char UART2_rxBuffer[11];
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim6;
TIM_HandleTypeDef htim15;
UART_HandleTypeDef huart2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM3_Init(void);
static void MX_TIM6_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM15_Init(void);
////---------[ UART Data Reception Completion CallBackFunc. ]---------
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength); //You need to toggle a breakpoint on this line!
}
int main(void) {
int x_msg = 0;
int y_msg = 0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_TIM6_Init();
MX_USART2_UART_Init();
MX_TIM15_Init();
HAL_TIM_Base_Start(&htim3); //start timer
HAL_TIM_Base_Start(&htim6); //start timer
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
StepperStartup();
SetSpeed(50);
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength);
while (1) {
if (UART2_rxBuffer[0] == 'D') {
DisengagePen();
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
if (UART2_rxBuffer[0] == 'E') {
EngagePen();
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
if (UART2_rxBuffer[0] == 'M') {
sscanf (UART2_rxBuffer,"M,%d,%d",&x_msg, &y_msg);
LinearMove(x_msg, y_msg);
HAL_UART_Receive_IT(&huart2, (uint8_t*)UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
Error_Handler();
}
}
/**
* @brief TIM3 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM3_Init(void) {
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 8;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig)
!= HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}
/**
* @brief TIM6 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM6_Init(void) {
/* USER CODE BEGIN TIM6_Init 0 */
/* USER CODE END TIM6_Init 0 */
/* USER CODE BEGIN TIM6_Init 1 */
/* USER CODE END TIM6_Init 1 */
htim6.Instance = TIM6;
htim6.Init.Prescaler = 8;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 65535;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM6_Init 2 */
/* USER CODE END TIM6_Init 2 */
}
/**
* @brief TIM15 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM15_Init(void) {
/* USER CODE BEGIN TIM15_Init 0 */
/* USER CODE END TIM15_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
TIM_OC_InitTypeDef sConfigOC = { 0 };
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = { 0 };
/* USER CODE BEGIN TIM15_Init 1 */
/* USER CODE END TIM15_Init 1 */
htim15.Instance = TIM15;
htim15.Init.Prescaler = 0;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 65535;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim15) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &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(&htim15, &sConfigOC, TIM_CHANNEL_1)
!= HAL_OK) {
Error_Handler();
}
__HAL_TIM_DISABLE_OCxPRELOAD(&htim15, TIM_CHANNEL_1);
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(&htim15, &sBreakDeadTimeConfig)
!= HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM15_Init 2 */
/* USER CODE END TIM15_Init 2 */
HAL_TIM_MspPostInit(&htim15);
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void) {
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void) {
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void) {
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1) {
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
The WhiteBoardRobotFuncts.c is as follows:
/*
* WhiteBoardRobotFuncts.c
*
* Created on: Jan 13, 2024
* Author: Ericw
*/
#include "main.h"
#include <math.h>
#include "WhiteBoardRobotFuncts.h"
//#include "PWMFunctions.h"
#include "stm32f0xx.h"
float MoveVelocity = 50;
float CurrentLl = 0;
float CurrentLr = 0;
float CurrentX = X_init;
float CurrentY = Y_init;
float CurrentStepl = 0;
float CurrentStepr = 0;
void setPortAndPinForOutput(GPIO_TypeDef *portOnMCU, int pinNumber) {
//ENABLE PORTS
if (portOnMCU == GPIOA) {
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
}
if (portOnMCU == GPIOB) {
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
}
if (portOnMCU == GPIOC) {
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
}
if (portOnMCU == GPIOD) {
RCC->AHBENR |= RCC_AHBENR_GPIODEN;
}
if (portOnMCU == GPIOF) {
RCC->AHBENR |= RCC_AHBENR_GPIOFEN;
}
//SET PIN TO OUTPUT, HIGH SPEED, PP, NO PUPD
portOnMCU->MODER |= 1 << (2 * pinNumber);
portOnMCU->MODER &= ~(1 << (2 * pinNumber + 1));
portOnMCU->OSPEEDR |= (1 << (2 * pinNumber)) | (1 << (2 * pinNumber + 1));
portOnMCU->OTYPER &= ~(1 << (pinNumber));
portOnMCU->PUPDR &= ~(1 << (pinNumber));
}
void Delay_uS(uint16_t us) {
TIM3->PSC = 8;
TIM3->ARR = 65535;
TIM3->CNT = 0x00000000; // set the counter value a 0
while (TIM3->CNT < us) {
} // wait for the counter to reach the us input in the parameter
}
void Delay_mS(uint16_t ms) {
for (int i = 0; i < ms; i++) {
Delay_uS(1000);
}
}
void StepperStartup() {
//setup Servo PWM
//PWMSetup_CH4();
//SETUP RIGHT STEPPER
setPortAndPinForOutput(dirPortR, dirPinR);
setPortAndPinForOutput(StepPortR, StepPinR);
setPortAndPinForOutput(EnablePortR, EnablePinR);
EnablePortR->BSRR |= (1 << EnablePinR); //disable stepper
//SETUP LEFT STEPPER
setPortAndPinForOutput(dirPortL, dirPinL);
setPortAndPinForOutput(StepPortL, StepPinL);
setPortAndPinForOutput(EnablePortL, EnablePinL);
EnablePortL->BSRR |= (1 << EnablePinL); //disable stepper ,
CurrentLl = sqrt(pow(X_init, 2) + pow(Y_init, 2));
CurrentLr = sqrt(pow(X_init - Ls, 2) + pow(Y_init, 2));
// //temporary trouble shooting pin
// setPortAndPinForOutput(GPIOB, 13);
// GPIOB->BRR |= (1 << 13); //TURN ON LED
}
void SetSpeed(float speedInput) {
MoveVelocity = speedInput;
//StepDelay [us/step] = [min/rev][s/min][us/s][rev/deg][deg/step]
//StepDelay = floor((1 / speedInput) * 60. * 1000000. * (1. / 360.) * (1.8 / NumMicrosteps));
//StepDelay = floor((300000 / (speedInput * NumMicrosteps)));
}
void LinearMove(float x, float y) {
//convert (x,y) coords to steps--------------------------------------------------
float TargetLl = sqrt(pow(x, 2) + pow(y, 2));
float TargetLr = sqrt(pow(Ls - x, 2) + pow(y, 2));
int StepsToTakeL = floor(
StepsPerRad * ((TargetLl - CurrentLl) / SpoolRadius));
int StepsToTakeR = floor(
StepsPerRad * ((TargetLr - CurrentLr) / SpoolRadius));
int TargetStepL = StepsToTakeL + CurrentStepl;
int TargetStepR = StepsToTakeR + CurrentStepr;
float incrementerL = 0;
float incrementerR = 0;
//determine Move distance --> find move time (given move speed) --> find delays for each stepper
float MoveLinearTime = sqrt(pow(x - CurrentX, 2) + pow(y - CurrentY, 2))
/ MoveVelocity;
//Find Left and right stepper delays
int StepperDelay_L = ceil(1000000 * MoveLinearTime / abs(StepsToTakeL * 2)); //us / step
int StepperDelay_R = ceil(1000000 * MoveLinearTime / abs(StepsToTakeR * 2)); //us / step
//Determine Directions for both steppers
if (StepsToTakeL < 0) {
dirPortL->BRR |= (1 << dirPinL);
incrementerL = -0.5;
} else {
dirPortL->BSRR |= (1 << dirPinL);
incrementerL = 0.5;
}
if (StepsToTakeR < 0) {
dirPortR->BSRR |= (1 << dirPinR);
incrementerR = -0.5;
} else {
dirPortR->BRR |= (1 << dirPinR);
incrementerR = 0.5;
}
//enable steppers
EnablePortR->BRR |= (1 << EnablePinR);
EnablePortL->BRR |= (1 << EnablePinL);
//start each clk at zero
TIM3->CNT = 0x00000000; // set the counter value a 0
TIM6->CNT = 0x00000000; // set the counter value a 0
//WHILE EITHER STEPPER ISNT AT ITS TARGER
while ((CurrentStepl != TargetStepL) | (CurrentStepr != TargetStepR)) {
if ((CurrentStepl != TargetStepL) & (TIM3->CNT >= StepperDelay_L)) {
TIM3->CNT = 0x00000000; // clear counter
StepPortL->ODR ^= (1 << StepPinL); //flip pin value with xor
CurrentStepl += incrementerL; //note half a step has been taken
}
if ((CurrentStepr != TargetStepR) & (TIM6->CNT >= StepperDelay_R)) {
TIM6->CNT = 0x00000000; // clear counter
StepPortR->ODR ^= (1 << StepPinR);
CurrentStepr += incrementerR;
}
}
//after move is complete update Ll and Lr
CurrentLl = TargetLl;
CurrentLr = TargetLr;
CurrentX = x;
CurrentY = y;
//disable steppers
EnablePortR->BSRR |= (1 << EnablePinR);
EnablePortL->BSRR |= (1 << EnablePinL);
}
void EngagePen() {
WriteServoAngle(0);
}
void DisengagePen() {
WriteServoAngle(179);
}
void PWMSetPulseAndPeriod_inPulse(int PeriodInMS, int PeriodInPulses, int PulseWidth){
int prescaler;
int NewPeriodInPulses;
if (PeriodInMS>0){
prescaler = floor(((HSI_VALUE/1000) * PeriodInMS)/65536);
NewPeriodInPulses= floor(((HSI_VALUE/(prescaler+1)) / 1000) * PeriodInMS);
}else if(PeriodInPulses>0){
prescaler = floor(PeriodInPulses/65536);
NewPeriodInPulses= floor(PeriodInPulses/(prescaler+1));
}
//SET ARR AND PRESCALER
TIM15->PSC = prescaler;
TIM15->ARR = NewPeriodInPulses;
int newPulseWidth = floor(PulseWidth/(prescaler+1));
//set pulse width
TIM15->CCR1 = newPulseWidth;
}
void PWMSetPulseAndPeriod_inDutyCycle(int PeriodInMS, int PeriodInPulses, int dutyCycle){
int prescaler;
int NewPeriodInPulses;
if (PeriodInMS>0){
prescaler = floor(((HSI_VALUE/1000) * PeriodInMS)/65536);
NewPeriodInPulses= floor(((HSI_VALUE/(prescaler+1)) / 1000) * PeriodInMS);
}else if(PeriodInPulses>0){
prescaler = floor(PeriodInPulses/65536);
NewPeriodInPulses= floor(PeriodInPulses/(prescaler+1));
}
//SET ARR AND PRESCALER
TIM15->PSC = prescaler;
TIM15->ARR = NewPeriodInPulses;
int newPulseWidth = floor(NewPeriodInPulses*dutyCycle/100);
//set pulse width
TIM15->CCR1 = newPulseWidth;
}
void WriteServoAngle(float angle){
int widthPulses = floor(4000. + angle*800./9.);
PWMSetPulseAndPeriod_inPulse(20,0,widthPulses);
}
So what is my problem:
When I send a move command from my python GUI, on the first command the robot always moves to the same incorrect point on the board, regardless of my commanded coordinates. Then when I attempt to send additional move commands, the robot does not move (meaning the passed x, y coordinates are the same as in the previous calls of the function). My DeployMarker() and StowMarker() commands work and I have no issues controlling those via the python GUI. Additionally, the LinearMove command works flawlessly when I am not trying to feed it coordinates received via serial communication. Therefore, I have concluded that the issue must be in the way I am sending/receiving/parsing the x and y coordinates. So my question, in short, is what should I be doing differently to send/receive/parse a message in the format of "M,%d,%d" ?
As an aside, it is probably pretty obvious that my knowledge of Serial and Uart communication is pretty limited, so I am all ears if anyone has any recommended resources for me to learn more!
Update: I commanded x=100 and y= 130 in my python GUI and had the STM reply with the exact same message, as shown in the following code:
STM:
while (1) {
if (UART2_rxBuffer[0] == 'D') {
DisengagePen();
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
if (UART2_rxBuffer[0] == 'E') {
EngagePen();
HAL_UART_Receive_IT(&huart2, (uint8_t*) UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
if (UART2_rxBuffer[0] == 'M') {
sscanf (UART2_rxBuffer,"M,%d,%d",&x_msg, &y_msg);
//LinearMove(x_msg, y_msg);
HAL_UART_Transmit(&huart2, UART2_rxBuffer, sizeof(UART2_rxBuffer), 100);
Delay_mS(100);
HAL_UART_Receive_IT(&huart2, (uint8_t*)UART2_rxBuffer, MsgLength);
//clear buffer
for (int i = 0; i < MsgLength; i++)
UART2_rxBuffer[i] = 0;
}
}
/* USER CODE END 3 */
}
python:
def GoToCoords(X, Y):
Msg = "M," + "{0:0=4d}".format(int(X)) + "," + "{0:0=4d}".format(int(Y))
print(Msg)
ser.write(bytes(Msg, 'UTF-8'))
while (True):
if ser.in_waiting:
print(ser.readline())
break
This gives the following from in python:
M,0100,0130
b'M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Likewise, just returning just x_msg gives:
M,0100,0130
b'\x00\x00\x00\x00'