-1

Using a stepper motor and a range finder (TFLuna), I am trying to build a "Lidar" (like a Radar, but with light). Here is the algorithm:

  • Turn motor one step, calculate angle, measure distance to whatever it hits, giving polar coordinates;
  • Calculate Cartesian coordinates and send to Python;
  • Collect a few hundred measurements and show the scatter plot.

This works, but only if I restart the sketch and the Python routine at the same time. After I collected all the data for one plot, I cannot restart the Python script to collect another round of data - it shows that there is no data in waiting. Also, the Arduino script never hangs due to the buffer getting full (as Python has stopped reading).

How can I make it so that the Arduino and the motor do their thing (swinging back and forth 180º, looking at stuff), and I am able to collect an amount of data in Python intermittently -- whenever I choose to restart the script one more time?

Here is the code. First, Arduino. "count" is the number of steps by which the motor has rotated; tflI2C is the range-finder object.

#include <Stepper.h>
#include <Arduino.h>
#include <Wire.h>        // Instantiate the Wire library
#include <TFLI2C.h>      // TFLuna-I2C Library v.0.1.1
#include <math.h>

TFLI2C tflI2C;
int16_t  tfDist;    // distance in centimeters
int16_t  tfAddr = TFL_DEF_ADR;  // Use this default I2C address

const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution

// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
int stepCount = 0;         // number of steps the motor has taken

void serialFlush(){
  while(Serial.available() > 0) {
    char t = Serial.read();
  }
}


void setup() {
  // initialize the serial port:
  Serial.begin(115200);
  Wire.begin();           // Initalize Wire library
  myStepper.setSpeed(10);
  //myStepper.step(stepsPerRevolution/2);
  //delay(1000);
  //myStepper.step(-stepsPerRevolution/2);
}

void coordinates(int count){
  struct Polar {
    float angle;
    int16_t distance;
  } pPoint;
  struct Cartesian {
    float xCoord;
    float yCoord;
  } cPoint;

  float angle = count*(2.0*PI)/stepsPerRevolution;
  byte flag = 0;
  //Serial.flush();
  while (Serial.availableForWrite() < sizeof(struct Cartesian)){}
  if(tflI2C.getData(tfDist, tfAddr)){
    pPoint.angle = angle + PI/2.0;
    pPoint.distance = tfDist;
    cPoint.xCoord = pPoint.distance * cos(pPoint.angle);
    cPoint.yCoord = pPoint.distance * sin(pPoint.angle);
    if (pPoint.distance <= 50 ){
      Serial.write((char*)&cPoint, sizeof(struct Cartesian));
    }
  }
  return;
}

void loop() {
  // step one step:
  for (int k=0; k<stepsPerRevolution/4; k++){
    myStepper.step(1);
    coordinates(stepCount);
    stepCount += 1;
    delay(10);
  }
  for (int k=0; k<stepsPerRevolution/2; k++){
    myStepper.step(-1);
    coordinates(stepCount);
    stepCount -= 1;
    delay(10);
  }
  for (int k=0; k<stepsPerRevolution/4; k++){
    myStepper.step(1);
    coordinates(stepCount);
    stepCount += 1;
    delay(10);
  }
  delay(5000);
}

and here is Python:

ardu = serial.Serial('/dev/cu.usbserial-1460', baudrate=115200, timeout=.1)
radarImage = []
structSize = struct.calcsize("ff")
ardu.reset_input_buffer()
    
i = 0;
while True:
    if ardu.in_waiting >= structSize:
        arduRead = ardu.read(structSize)
        point = list(struct.unpack("ff",arduRead))
        if i <= 10:
            print(point)
        radarImage.append(point)
        i += 1
    else:
        pass
    if i > 2000:
        break
ardu.close()

Here is a sample from a "first round" data collection. Yes, my desk is messy...

enter image description here

9
  • 1
    the arduino code does not compile without error
    – jsotola
    Commented Nov 14, 2023 at 1:36
  • I'm not surprised - I only gave you the code that pertains to the serial communication. I'll pop in the entire thing if that helps. Hold on...
    – Matthias
    Commented Nov 14, 2023 at 1:44
  • RE: "I cannot restart the Python script to collect another round of data" - perhaps you have an issue with the python code too?
    – VE7JRO
    Commented Nov 14, 2023 at 2:39
  • What Arduino are you using? It is unclear to me, what exactly happens, when you restart the python script. When exactly did you try to restart it? During the 5s delay? Or during a measurement round? What exactly does happen then? Currently your Serial code doesn't have any possibility to sync between Arduino and python. The python script is not able to detect the start of a new point struct inside the serial stream (you didn't implement any protocol for that).
    – chrisl
    Commented Nov 14, 2023 at 6:57
  • You might want to reduce your sources to the minimum, reproducible and compilable example that shows the behavior. For example, start removing the Lidar stuff and replace it, if necessary, with a simple dummy. Commented Nov 14, 2023 at 7:06

1 Answer 1

2

I am puzzled why the Arduino code isn't simply stopping at "Serial.availableForWrite()" when I stop reading data on the Python side -- it keeps on running.

This depends on the Arduino board used - specifically on if it has native USB support. Boards without native USB support (like Uno or Nano) use a second chip, which receives UART (Serial) data from the main chip and interfaces that to USB. The main chip cannot know about the state of the USB chip, so it will send regardless of a program listening.

Boards with native USB support somewhat depend on the OS/driver/program to collect the data from the USB endpoints. If your program doesn't collect that data, but also doesn't close the connection, then the data will pile up in the buffer and availableForWrite() should return 0 when the buffer is full (blocking further writing to Serial). But if your program closes the connection correctly (as it should on exiting), then the Serial data doesn't even get send, but discarded. See Majenkos answer to this question, which is relevant here.

So generally: No, the Arduino won't stop sending data, only because you don't listen. Building a simple command system to tell the Arduino to start/stop sending data is a common way to handle this situation. Or you could implement a protocol, which lets the python script correctly detect start and end of each data packet.

1
  • This was way more effort than what was warranted for a quick-and-dirty data dump and visualization, but ultimately I did succeed, sort of, by implementing a "Send 'TURN' command to Arduino, Arduino calculates struct and sends to Python, Python sends next 'TURN' command," song and dance. After that it was STILL hanging randomly until I added a whole bunch of explicit buffer flushes and carefully timed "delay()-s" into both codes. The irony is that the data ultimately didn't even look that impressive -- not impressive enough to polish the project further.
    – Matthias
    Commented Nov 16, 2023 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.