How to drive stepper motor using Arduino Accelstepper library with ros message? - ros

At the moment I am working on a code that should send a message to a arduino uno through rosserial to drive the stepper motor to move a speciafied number of steps. I was using a 28BYJ-48 with ULN2003 driver. Everything is hooked up correctly. My goal is to send a message using raspberry pi 2b to arduino uno through rosserial and that then the stepper moves to a certain position. However, I am not succeeding in this. I am using AccelStepper and at the moment I have the following code:
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include <WProgram.h>
#endif
// Include the AccelStepper Library
#include <AccelStepper.h>
#include <ros.h>
#include <std_msgs/UInt16.h>
#include <std_msgs/Empty.h>
// Define step constant
#define HALFSTEP 8
// Creates an instance
// Pins entered in sequence IN1-IN3-IN2-IN4 for proper step sequence
AccelStepper stepper(HALFSTEP, 4, 6, 5, 7);
uint8_t run_flag = 0;
void startStepper(const std_msgs::Empty& start_msg) {
run_flag = 1;
digitalWrite(13, HIGH);
}
void runStepper(const std_msgs::UInt16& pos_msg) {
run_flag = 2;
stepper.moveTo(pos_msg.data);
}
ros::NodeHandle nh;
ros::Subscriber<std_msgs::Empty> stepperStart("stepper/start", &startStepper);
ros::Subscriber<std_msgs::UInt16> stepperRun("stepper/run", &runStepper);
void setup() {
pinMode(13, OUTPUT);
// set the maximum speed, acceleration factor,
// initial speed and the target position
stepper.setMaxSpeed(1000.0);
stepper.setAcceleration(50.0);
stepper.setSpeed(200);
stepper.moveTo(2038);
nh.initNode();
nh.subscribe(motorStart);
nh.subscribe(motorRun);
}
void loop() {
if (run_flag == 1) {
stepper.run();
if (stepper.distanceToGo() == 0) {
run_flag = 0;
digitalWrite(13, LOW);
stepper.setCurrentPosition(0);
}
}
nh.spinOnce();
}
Run "roscore" in the terminal;
Open a new terminal and run the command "rosrun rosserial_python serial_node.py /dev/ttyACM0"
Open a new terminal and run the command "rostopic pub stepper/start std_msgs/Empty --once"
The code could run correctly for the first time after the arduino uno was power on. After that the arduino did not respond to the message from raspberry pi.
Any help you can offer me is appreciated! Thanks for your time.

Related

BNO055 to control Nema 17 stepper motor with a4988 driver via rosserial

My goal is to control the position and speed of a Nema 17 stepper motor based on the euler angle of a BNO055 inertial measurement unit. I am using an ESP32 to flash the code via WIFI to rosserial. I am powering the Nema 17 with a 12V power source and the BNO055 with a small external 5V battery pack.
In summary, the stepper motor should move between 0-4100 steps which would be mapped to -90 and 90 degrees of the BNO055's y-axis.
For this, I need to read the output of the BNO055 sensor as often as possible and only change directions of the Nema 17 when the BNO055 has changed position relative to the mapping.
The PROBLEM I am having is that when I incorporate reading the sensor in my code, my motor starts to shake and does not rotate smoothly. I am wondering how I can get both things to work simultaneously (reading sensor and moving nema 17).
PS: I will control speed by calculating a PI control with the BNO055 sensor and adjusting the delayMicroseconds() accordingly... but first thing is to get the readings and motor movement smooth.
Below is a code snippet I am using to debug this problem:
#include <WiFi.h>
#include <ros.h>
#include <Wire.h>
#include <std_msgs/Header.h>
#include <std_msgs/String.h>
#include <geometry_msgs/Quaternion.h>
#include <HardwareSerial.h>
#include <analogWrite.h>
#include <MultiStepper.h>
#include <AccelStepper.h>
#include <Stepper.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <math.h>
//////////////////////
// BNO055 //
//////////////////////
Adafruit_BNO055 bno_master = Adafruit_BNO055(55, 0x29);
Adafruit_BNO055 bno_slave = Adafruit_BNO055(55, 0x28);
geometry_msgs::Quaternion Quaternion;
std_msgs::String imu_msg;
#define I2C_SDA 21
#define I2C_SCL 22
TwoWire I2Cbno = TwoWire(0); // I2C connection will increase 6Hz data transmission
float ax_m, ay_m, az_m, ax_s, ay_s, az_s; // accelerometer
float gw_m, gx_m, gy_m, gz_m, gw_s, gx_s, gy_s, gz_s; // gyroscope
float ex_m, ey_m, ez_m, ex_s, ey_s, ez_s; // euler
float qw_m, qx_m, qy_m, qz_m, qw_s, qx_s, qy_s, qz_s; // quaternions
//////////////////////
// WiFi Definitions //
//////////////////////
const char* ssid = "FRITZ!Box 7430 PN"; // Sebas: "WLAN-481774"; Paula: "FRITZ!Box 7430 PN"; ICS: ICS24; Hotel Citadelle Blaye
const char* password = "37851923282869978396"; // Sebas: "Kerriganrocks!1337"; Paula: "37851923282869978396"; ICS: uZ)7xQ*0; citadelle
IPAddress server(192,168,178,112); // ip of your ROS server
IPAddress ip_address;
WiFiClient client;
int status = WL_IDLE_STATUS;
//long motorTimer = 0, getImuDataTimer = 0, millisNew = 0; //millisOld = 0,
//////////////////////
// Stepper motor //
//////////////////////
int stepPin = 4;
int stepPinState = LOW;
int dirPin = 2;
int dirPinState = HIGH;
unsigned long millisOld1 = 0;
unsigned long millisOld2 = 0;
long motorTimer = 1; // in milliseconds
long getImuDataTimer = 10; // in milliseconds
double maxPosition = 4100;
double stepsMoved = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class WiFiHardware {
public:
WiFiHardware() {};
void init() {
// do your initialization here. this probably includes TCP server/client setup
client.connect(server, 11411);
}
// read a byte from the serial port. -1 = failure
int read() {
// implement this method so that it reads a byte from the TCP connection and returns it
// you may return -1 is there is an error; for example if the TCP connection is not open
return client.read(); //will return -1 when it will works
}
// write data to the connection to ROS
void write(uint8_t* data, int length) {
// implement this so that it takes the arguments and writes or prints them to the TCP connection
for(int i=0; i<length; i++)
client.write(data[i]);
}
// returns milliseconds since start of program
unsigned long time() {
return millis(); // easy; did this one for you
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int i;
void chatterCallback(const std_msgs::String& msg) {
i = atoi(msg.data);
// s.write(i);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setupWiFi()
{
// WIFI setup
WiFi.begin(ssid, password);
Serial.print("\nConnecting to "); Serial.println(ssid);
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
if(i == 21){
Serial.print("Could not connect to"); Serial.println(ssid);
while(1) delay(500);
}
Serial.print("Ready! Use ");
Serial.print(WiFi.localIP());
Serial.println(" to access client");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ros::Subscriber<std_msgs::String> sub("message", &chatterCallback);
ros::Publisher pub("imu_data/", &imu_msg);
ros::NodeHandle_<WiFiHardware> nh;
void setup() {
// set the digital pins as outputs
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
Serial.begin(57600);
setupWiFi();
// I2C connection IMUs
Wire.begin(I2C_SDA, I2C_SCL);
I2Cbno.begin(I2C_SDA, I2C_SCL, 400000);
bno_master.begin();
bno_slave.begin();
// get imu calibrations
uint8_t system, gyro, accel, mg = 0;
bno_master.getCalibration(&system, &gyro, &accel, &mg);
bno_slave.getCalibration(&system, &gyro, &accel, &mg);
bno_master.setExtCrystalUse(true);
bno_slave.setExtCrystalUse(true);
nh.initNode();
nh.advertise(pub);
}
/////////////////////////////
/// GET IMU DATA FUNCTION ///
/////////////////////////////
int get_imu_data(){
imu::Vector<3> Euler_s = bno_slave.getVector(Adafruit_BNO055::VECTOR_EULER); // 100 Hz capacity by BNO055 // IF I COMMENT THIS LINE OUT AND SET VARIABLES BELOW TO SET VALUES, MY MOTOR RUNS PERFECTLY
// Euler
float ex_s = Euler_s.x();
float ey_s = Euler_s.y();
float ez_s = Euler_s.z();
// putting data into string since adding accel, gyro, and both imu data becomes too cumbersome for rosserial buffer size. String is better for speed of data
String data = String(ex_s) + "," + String(ey_s) + "," + String(ez_s) + "!";
int length_data = data.indexOf("!") + 1;
char data_final[length_data + 1];
data.toCharArray(data_final, length_data + 1);
imu_msg.data = data_final;
pub.publish(&imu_msg);
nh.spinOnce();
Serial.println(ey_s);
return ey_s; // ex_s, ey_s, ez_s
}
/////////////////////////////
// MAIN LOOP //
/////////////////////////////
void loop() {
unsigned long currentMillis = millis();
//////////////////
// GET IMU DATA //
//////////////////
if(currentMillis - millisOld2 >= getImuDataTimer)
{
ey_s = get_imu_data();
Serial.print(ey_s);
}
////////////////
// MOVE MOTOR //
////////////////
// later, the direction will depend on the output of ey_s
if((dirPinState == HIGH) && (currentMillis - millisOld1 >= motorTimer))
{
if(stepsMoved <= maxPosition)
{
digitalWrite(dirPin, dirPinState);
millisOld1 = currentMillis; // update time
stepsMoved += 5;
for(int i =0; i<=5; i++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(1200); // constant speed
digitalWrite(stepPin, LOW);
}
Serial.println(stepsMoved); // checking
}
else if(stepsMoved > maxPosition)
{
dirPinState = LOW;
millisOld1 = currentMillis; // update time
stepsMoved = 0;
}
}
if((dirPinState == LOW) && (currentMillis - millisOld1 >= motorTimer))
{
if(stepsMoved <= maxPosition)
{
digitalWrite(dirPin, dirPinState);
millisOld1 = currentMillis; // update time
stepsMoved += 5;
for(int i =0; i<=5; i++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(1200); // constant speed
digitalWrite(stepPin, LOW);
}
Serial.println(stepsMoved); // checking
}
else if(stepsMoved > maxPosition)
{
dirPinState = HIGH;
millisOld1 = currentMillis; // update time
stepsMoved = 0;
}
}
}
I have tried the AccelStepper.h library but not getting the outputs desired in terms of position control and speed updates.
Arduino's all-in-one loop() is not the correct architecture for controlling real-time systems. Motor control requires rather accurate timing - e.g. looks like you wish to update motor control output with a frequency of 833 Hz (from the 1.2 ms delay) which should then be fairly accurate and stable.
Unfortunately you're not getting anywhere near this, as you're doing a bunch of non-critical stuff in each loop which potentially takes a very long (and undeterministic) amount of time - waiting for the IMU to give you a sample, printing to the serial port, talking to some ROS component, etc. Meanwhile the real-time critical control signal to your motor is waiting for all this to finish before it can do its work. Note that printing a few lines to the serial could already take dozens of milliseconds, so your delayMicroseconds(1200); is analogous to measuring a cut with a caliper and then making the cut with an axe with your eyes closed.
A real-time critical process should execute in its own thread which has higher priority than the non-real-time critical stuff. In your case it should probably run off a timer with a 1.2 ms period. The timer handler should execute with higher priority than all the other stuff, calculate desired output to motor using last received sensor input (i.e. don't go asking the IMU for a fresh reading when it's time to move the motor) and exit.
Then you can run all the other stuff from the loop() in idle priority which simply gets pre-empted when the motor control does its work.
Depending on how critical the accurate timing of IMU input is, you may want to run this also in a separate thread with a priority somewhere between the motor control interrupt and idle (remember to yield some CPU cycles to loop() or it'll starve).

Arduino UNO displaying -999 value for humidity and temperature

I am using DHT11 sensor for displaying humidity and temperature values. I dont know where I did wrong. It's displaying only -999 value for both humidity and temperature. Here's my code
#include <dht.h>
dht DHT;
#define DHT11_PIN A0
void setup(){
Serial.begin(9600);
}
void loop()
{
DHT.read11(DHT11_PIN);
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(1000);
}
Picture of Arduino
Output
Your code seems to be correct. Try to make different connections and use different libraries. If it is still not solved then I recommend you to get the another dht11 sensor. I am sure it will work!!
#include<dht.h>
dht DHT;
#define DHT11_PIN A0
void setup(){
Serial.begin(9600);
}
void loop()
{
DHT.read11(DHT11_PIN);
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(1000);
}

Writing to the serial monitor on the Sparkfun ESP8266 Thing

Below is my current code:
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "836addccd2ee4f05b96f0f3ad831249e"; // ***Type in your Blynk Token
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "_Fast&Furious";// ***your wifi name
char pass[] = "Mclaren2018";// ***and password
const int MOTION_PIN = 4; // Pin connected to motion detector
WidgetLCD lcd(V1);
void setup()
{
Serial.begin(115200);
Blynk.begin(auth, ssid, pass);
pinMode(MOTION_PIN, INPUT_PULLUP);
Serial.println("SETUP");
}
void loop()
{
Blynk.run();
int proximity = digitalRead(MOTION_PIN);
if (proximity == LOW) // If the sensor's output goes low, motion is detected
{
Blynk.virtualWrite(5,1023);
lcd.clear();
lcd.print(0,0,"Motion detected");
Serial.println("Motion detected!");
}
else
{
Blynk.virtualWrite(5,0);
lcd.clear();
lcd.print(0,0,"Motion NOT detected");
Serial.println("Motion NOT detected!");
}
}
I am currently trying to simily write some text to the serial console. But when I upload my code it will just write a string of k's to the console. What am I doing wrong to produce such a strange output?
This is a link to the tutorial I have been following: http://designinformaticslab.github.io/productdesign_tutorial/2017/01/24/motion_sensor.html
Any help would be much appreciated!
It all looks good to me, are you sure you have the baud rate set correctly on the serial monitor? I would write a new program real quick that ONLY does serial output and get that working (this simplifies the problem to solve, and makes it more obvious if it is something like serial port speed), then go back to your more complete program and it should work.

arduino programming: not enough memory message

I am new to arduino programming (Arduino Pro Mini 3.3v version), i have some code like below. I am connecting 9DOF, OLED screen and a BLE breakout to arduino pro mini.
I already went through some of the memory optimization tips, but i still have some issue. Even with the following code, i only have 9 bytes left for dynamic memory. If i enable BTLEserial.begin();, it will kill the memory. Please any suggestions will be appreciated.
#include <Wire.h>
#include <SPI.h>
#include <SparkFunLSM9DS1.h>
#include "Adafruit_BLE_UART.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
LSM9DS1 imu;
#define LSM9DS1_M 0x1E // Would be 0x1C if SDO_M is LOW
#define LSM9DS1_AG 0x6B // Would be 0x6A if SDO_AG is LOW
#define ADAFRUITBLE_REQ 10
#define ADAFRUITBLE_RDY 2
#define ADAFRUITBLE_RST 9
Adafruit_BLE_UART BTLEserial = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);
void setup(void) {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // initialize with the I2C addr 0x3D (for the 128x64)
display.display();
delay(2000);
display.clearDisplay();
display.drawPixel(10, 10, WHITE);
display.display();
delay(2000);
display.clearDisplay();
imu.settings.device.commInterface = IMU_MODE_I2C;
imu.settings.device.mAddress = LSM9DS1_M;
imu.settings.device.agAddress = LSM9DS1_AG;
if (!imu.begin())
{
while (1)
;
}
// BTLEserial.begin(); - if i uncomment this code, i will get a not enough memory error.
}
aci_evt_opcode_t laststatus = ACI_EVT_DISCONNECTED;
void loop() {
displayAllDOF();
}
void displayAllDOF(){
display.setTextSize(1);
display.setTextColor(WHITE);
imu.readGyro();
display.setCursor(0,0);
display.print("G:");
display.print(imu.calcGyro(imu.gx));
display.print(", ");
display.print(imu.calcGyro(imu.gy));
display.print(", ");
display.print(imu.calcGyro(imu.gz));
display.println(" ");
imu.readAccel();
display.print("A:");
display.print(imu.calcAccel(imu.ax));
display.print(", ");
display.print(imu.calcAccel(imu.ay));
display.print(", ");
display.print(imu.calcAccel(imu.az));
display.println(" ");
imu.readMag();
display.print("M:");
display.print(imu.calcMag(imu.mx));
display.print(", ");
display.print(imu.calcMag(imu.my));
display.print(", ");
display.print(imu.calcMag(imu.mz));
display.println(" ");
display.display();
display.clearDisplay();
}
To start, you'll need to figure out where your RAM is going - How much does each library take? Do you really need to run them all at the same time? You know that you can run the display library, and the IMU code in your current setup - Can you implement something that only enables the IMU code, pulls data, then disables it? And the same with the display and BTLE code? That way each library is only consuming RAM when it's needed, and frees it once it's operation is finished
Update 1
An example of what I mentioned above. I do not know if all the libraries implement the .end() function. They may have a similar method you can use.
// Simple data storage for the .gx and .gy values
typedef struct {
float x, y;
} GyroData_t;
GyroData_t getImuData() {
GyroData_t data;
// Create the IMU class, gather data from it, and then destroy it
LSM9DS1 *imu = new LSM9DS1();
imu->begin();
imu->readGyro();
data.x = imu.gx;
data.y = imu.gy;
imu->end();
// This will reclaim the RAM that was used by the IMU - We no longer need it
delete imu;
return data;
}
void displayAllDOF() {
// Gather the IMU data
GyroData_t data = getImuData();
// Create the display object, and print the data we received
Adafruit_SSD1306 *display = new Adafruit_SSD1306(OLED_RESET);
display->print(...);
....
display->end();
// Reclaim the display RAM used
delete display;
// Do any bluetooth operations now
doBluetoothStuff();
}
void doBluetoothStuff() {
Adafruit_BLE_UART *BTLEserial = new Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);
BTLESerial->begin();
...
BTLESerial->end();
delete BTLESerial;
}

XBee printout, void setup()

I'm having trouble with my XBee's printing out to the monitor a simple statement, within the void setup(), as shown in my program below.
It prints the GPS and various sensor data, but it skips the whole introductory sentences. Whenever I open the arduino serial monitor, with the board plugged into my computer, it works fine.
Any suggestions? I'm at a lost!
Thanks :)
// Temperature Sensor data, Air Quality, and GPS data update XBEE
//CSV format for user interpretation
//Last updated 11/5/14
//Amy Laguna
#include <Adafruit_GPS.h>
#include <math.h>
#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3);
SoftwareSerial mySerial(8, 7);
//Read temperature sensor on A1
int tempPin = 1;
//Read CO sensor on A0
int coPin = 0;
//Read Oxygen sensor on A2
int opin = 2;
//GPS setup
Adafruit_GPS GPS(&mySerial);
//Use to debug
//SET to 'fale' to turn off echoing the GPS data to Serial
//Set to 'true' to debug and listen to raw GPS data
#define GPSECHO false
// this keeps track of whether we're using the interrupt
boolean usingInterrupt = false;
void useInterrupt(boolean);
void setup()
{
// connect at 115200 so we can read the GPS fast enough and echo without dropping chars
XBee.begin(115200);
Serial.begin(115200);
delay(5000);
XBee.println("Vehicle GPS, Temperature, and Air Quality Data!");
delay(2000);
XBee.println("Note: Elevation in Pensacola Florida: ~ 31 m (102 ft)");
XBee.println();
XBee.println("\n Date: Time: Fix: Location: Speed (mph): Elevation: CO: O2: Temperature: ");
// 9600 NMEA is the default baud rate for Adafruit MTK GPS's
GPS.begin(9600);
// RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
useInterrupt(true);
delay(1500);
}
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
#ifdef UDR0
if (GPSECHO)
if (c) UDR0 = c;
#endif
}
void useInterrupt(boolean v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
uint32_t timer = millis();
void loop() // run over and over again
{
if (! usingInterrupt) {
// read data from the GPS in the 'main loop'
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO)
if (c) XBee.print(c);
}
// if a sentence is received, we can check the checksum, parse it...
if (GPS.newNMEAreceived()) {
if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
return; // we can fail to parse a sentence in which case we should just wait for another
}
// if millis() or timer wraps around, we'll just reset it
if (timer > millis()) timer = millis();
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 2000) {
timer = millis(); // reset the timer
PrintGPS(); //Print GPS readings
PrintAirQuality(); //Print CO and Temperature readings
}
}
void PrintGPS()
{
//Print Date, Time, Fix
XBee.print("\n");
XBee.print(GPS.month, DEC); XBee.print('/');
XBee.print(GPS.day, DEC); XBee.print("/20");
XBee.print(GPS.year, DEC);
XBee.print(" , ");
XBee.print(GPS.hour, DEC); XBee.print(':');
XBee.print(GPS.minute, DEC); XBee.print(':');
XBee.print(GPS.seconds, DEC);
}
Maybe try a longer delay before the XBee.println() statements. If the radio modules haven't associated yet, they won't be ready for you to start sending data through them.
Alternatively, wait until the first call to PrintGPS() and send it then:
void PrintGPS()
{
static int first_time = 1;
if (first_time) {
print_headers();
first_time = 0;
}
//Print Date, Time, Fix
...

Resources