Bluetooth afstandsbediening

Na wat onderzoek naar de verschillende Bluetooth modules en hun mogelijkheden, kon ik me richten op mijn volgende doel: Bluetooth gebruiken voor de afstandsbediening met het joystick shield die is uitgebreid met een lcd-display.

De communicatie vanaf het joystick shield is compatible met de ArduinoBlue software library in het te besturen model. Zo wordt de besturing met het joystick shield uitwisselbaar met de ArduinoBlue app zonder de Sketch in het wagentje te hoeven wisselen.

Video : toestemming voor cookies nodig
Instellingen
IMG_9992

Bestuurbaar fischertechnik wagentje

Als bestuurbaar model gebruik ik het wagentje uit het boek "fischertechnik-Roboter mit Arduino" van Dirk Fox en Thomas Püttmann. In de sectie 'Modellvarianten' op de begeleidende site bij dit boek staat een Sketch voor het autootje waarmee deze bestuurd kan worden met de ArduinoBlue app, die zowel voor iOS als Android apparaten beschikbaar is. De besturings-Sketch van het autootje, de 'Flitzer', maakt gebruik van de ArduinoBlue software library voor de Arduino.

Deze ArduinoBlue app heeft ook een 'virtuele joystick' op het smartphone-scherm waarmee bestuurd kan worden. Aangezien ik al vrij ver was met het bruikbaar maken van het joystick shield voor afstandsbesturing, leek het me een leuke uitdaging het joystick shield hiermee uitwisselbaar te maken. Op die manier kan het autootje dus óf met de ArduinoBlue app, óf met deze zelfgebouwde fysieke joystick-unit worden bestuurd.

 

Hiervoor dient dan communicatie tussen twee Bluetooth (BLE) modules tot stand te worden gebracht en het joystick shield moeten worden uitgerust met een module die de 'Central'-, of 'Controller'-rol heeft. Deze kan dan automatisch verbinden met de module met de 'Peripheral'-rol in het te besturen model. Dit stuk van het experiment beschrijf ik in de voorgaande 'knutselaerij'.

Dataverkeer in het voorbeeld op de genoemde website bij het robots-boek is echter slechts eenrichtingsverkeer. Het leek mij leuk om ook terug te kunnen communiceren naar het joystick shield, zodat er berichten of meetwaarden vanaf het bestuurbare model op een display op de besturing konden worden getoond.

Het joystick shield heeft standaard al een connector voor het aansluiten van een Nokia5110 display, maar uiteindelijk leek me, omdat ik er toch een kastje voor wilde bouwen, een 2-regelig LCD display dat over de I²C-bus kan worden aangestuurd praktischer.

IMG_9994

Sketch en aansluitschema voor het autootje

Voor de communicatie tussen de Arduino en de Bluetooth module, ligt het gebruik van SoftwareSerial in de Sketch voor de hand. Hiermee hoeft de seriële communicatie niet over de hardware serial op D0 en D1 plaats te vinden die ook gebruikt worden voor de usb waarmee de Arduino aan de computer word aangesloten. Dit heeft het grote voordeel dat de module niet tijdelijk hoeft te worden verwijderd als een nieuwe Sketch naar de Arduino wordt geüpload. Bovendien blijft tijdens ontwikkeling de seriële monitor van de Arduino IDE bruikbaar.

Maar helaas bleek dit niet mogelijk omdat de servo-library voor het aansturen van de stuurservo dezelfde hardware-timer van de Arduino gebruikt als SoftwareSerial voor zijn uitgaande signalen naar de module. Het resultaat is, vooral bij het zenden via de RxD ingang, storing op de servosturing met heftige bewegingen tot gevolg. Ik koos er daarom voor de module aan te sluiten op de reguliere hardware serial op D0 en D1 en de module even te verwijderen als de usb-bus van de Arduino moet worden gebruikt.

Hieronder het aansluitschema op het motorshield. Voor de RxD ingang van de module heb ik (voor de zekerheid) een z.g. level-shifter gemaakt met twee weerstanden.

// 'Flitzer' with Bluetooth remote joystick shield
// ArduinoBlue compatible, uses Adafruit AdaFruit MotorShield
//
// Arnoud van Delden - March 2021
// Because PWM pins 9 and 10 use timer1, there is a conflict in using Software Serial
// This version uses the normal hardware RX/TX pins 0 and 1. Unplug the BT module during debug or upload!
// In my case: YELLOW wire to (output) TxD of BT module connects to pin 0 (RxD input) of the Arduino
// In my case: GREEN wire with level-shifter to (input) RxD of BT module connects to pin 1 (TxD output) of the Arduino
//

#include "Servo.h"
#include "Adafruit_MotorShield.h"
#include "ArduinoBlue.h"

//#define DEBUG
//#define BTBaud 38400 // Did some experiments on 38400 baud...
#define BTBaud 9600    // Configured/leaved my MT-19 BLE at 9600 baud...

// Various defines...
#define EncoderIRQPin 2
#define ServoPin 9
#define HornPin 12
#define minAngle 55
#define maxAngle 125
#define MaxSpeed 255
#define FlasherL 0
#define FlasherR 14
#define REARLIGHT 100  // Default read light brightness, with headroom for brake light (=255)

// Distance and velocity measurement...
//#define IPCM 14.475  // Theoretical nr impulses per cm with 50mm wheels...
#define IPCM 17.949    // My own value, derived from experiment and measure...
#define Interval 1000  // Sample/Measure interval in ms
volatile unsigned long tachoPulses;
unsigned long starttime;
const float f = 36000/IPCM; // Speed m/h = f x Impulses / Interval
unsigned long tachoSample;
float velocity, distance; // Speed & Distance
float velocityAvg; // Average speed
unsigned long tachoTimer;
char valueStr[15];
char displayStr[50];

ArduinoBlue remoteBLE(Serial); // BLE over RX/TX hardware serial!
Servo ServoMotor; // Steering servo...

Adafruit_MotorShield MShield = Adafruit_MotorShield(0x60);
Adafruit_DCMotor *Motor = MShield.getMotor(1); // Main motor
Adafruit_DCMotor *Light = MShield.getMotor(2); // Headlights...
Adafruit_DCMotor *RedRearLight = MShield.getMotor(3); // Rear and brake light...
Adafruit_DCMotor *ReverseLight = MShield.getMotor(4); // Reverse light..

int error = 0; 
byte controller_type = 0;
int motorspeed = 0;
int lastspeed = 0;
int steerAngle = 90; // Start with default steer angle...
unsigned long lasttime;
unsigned long brakeOffTimer;
boolean FlasherLeft = false, FlasherRight = false, FlasherOn = false, RumbleBrake = false,
        WarningLights = false, BrakeLights = false, autoParking = false, lightsOn = false;
boolean showSpeed = false;

// Vars for ArduinoBlue...
int throttleAB, steeringAB, buttonAB; // Sliders are not used (yet)...

void count() { // Interrupt routine of encoder motor...
  tachoPulses++;
}

void driveCar() { // Remote controlled car...
  throttleAB = abs(remoteBLE.getThrottle());
  steeringAB = abs(remoteBLE.getSteering());
  if (steeringAB>99) steeringAB = 99;
  buttonAB = remoteBLE.getButton();
      
  // Servo steering...
  steerAngle = map(steeringAB, 0, 100, minAngle, maxAngle);
#ifdef DEBUG
  if (steerAngle != 90) {
    Serial.print(" steerAngle: ");
    Serial.println(steerAngle, DEC);
  }
#endif
  ServoMotor.write(steerAngle);

  // Process and set motor speed...
  motorspeed = throttleAB - 50;
  if (motorspeed>0) {
    // Forward...
    //motorspeed = abs(motorspeed);
    motorspeed = map(motorspeed, 0, 50, 0, 255);
    Motor->setSpeed(motorspeed);
    Motor->run(FORWARD);
    ReverseLight->setSpeed(0);
#ifdef DEBUG
    if (motorspeed>0) {
      Serial.print("Forward motorspeed: ");
      Serial.println(motorspeed, DEC);
    }
#endif

  } else {
    // Backward/reverse...
    motorspeed = map(motorspeed, 0, -50, 0, 255);
    Motor->run(BACKWARD);
    Motor->setSpeed(motorspeed);
    if (motorspeed>5)
      ReverseLight->setSpeed(255);
    else // Switch off reverse light if not moving...
      ReverseLight->setSpeed(0);
  #ifdef DEBUG
    if (motorspeed>0) {
      Serial.print("Reverse motorspeed: ");
      Serial.println(motorspeed, DEC);
    }
#endif   
  }
  if (motorspeed0) {
    delay(100); // Delay for button debouncing
    
    // Car horn...
    if (buttonAB==4) { // Button 4 was pressed...
      tone(HornPin, 200, 500);
      delay(1000);
      tone(HornPin, 200, 500);
      delay(500);
      noTone(HornPin);
#ifdef DEBUG
      Serial.println("Horn sounded");
#endif
    }
    
    // Toggle head and rear lights...
    if (buttonAB==1) { // Button 1 was pressed...
      if (lightsOn) {
        Light->setSpeed(0);
        RedRearLight->setSpeed(0);
        lightsOn=false;
      } else {
        Light->setSpeed(255);
        RedRearLight->setSpeed(REARLIGHT);
        lightsOn=true;
      }
#ifdef DEBUG
      Serial.println("Headlights toggle");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }

    // Toggle warning lights...
    if (buttonAB==2) { // Button 2 was pressed...
      if (WarningLights)
        WarningLights = false;
      else
        WarningLights = true;
#ifdef DEBUG
    Serial.println("WarningLights toggle");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }

    // Reset speed and distance...
    if (buttonAB==3) { // Button 3 was pressed...
      tachoPulses = tachoSample = 0;
      distance = velocity = velocityAvg = 0.0;
#ifdef DEBUG
      Serial.println("Reset speed and distance");
#endif
    }

    // showSpeed...
    if (buttonAB==5) { // Button 5 was pressed...
      if (showSpeed) { // End display mode...
        remoteBLE.sendMessage("VALUES");
      } else {
        remoteBLE.sendMessage("CLR");
        delay(100);
        remoteBLE.sendMessage("LCDPRINT0,0:Speed 0.00 m/h  ");
        delay(100);
        remoteBLE.sendMessage("LCDPRINT1,1:Dist 0.00 cm    ");
        delay(100);
      }
      //delay(100);
      showSpeed = !showSpeed;
#ifdef DEBUG
      Serial.println("Show velocity and speed");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }  
  }
}

void setup() {

  // Setup speed/distance tracking...
  pinMode(EncoderIRQPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(EncoderIRQPin), count, CHANGE);
  starttime = millis(); // Millisecs since program start...

  // Initialize car horn buzzer and signal we're alive...
  pinMode(HornPin, OUTPUT);
  tone(HornPin, 800, 250);
  delay(500);
  tone(HornPin, 800, 250);
  delay(250);
  noTone(HornPin);
      
  // Init BLE module over Hardware Serial...
  Serial.begin(BTBaud); // Start serial monitor
  
  MShield.begin();              // Motor Shield init...
  Wire.setClock(400000);        // Set I²C-Frequency to 400 kHz...
  ServoMotor.attach(ServoPin);  // Servo with default angle...
  ServoMotor.write(steerAngle); // Default middle...
      
  ReverseLight->setSpeed(0); 
  ReverseLight->run(FORWARD);
  RedRearLight->setSpeed(0); 
  RedRearLight->run(BACKWARD);
  Light->setSpeed(0); 
  Light->run(FORWARD);

  MShield.setPin(FlasherL, false);
  MShield.setPin(FlasherR, false);
    
  tachoPulses = 0;
  motorspeed = 0;
  Motor->run(FORWARD); // Default=Forward...
  Motor->setSpeed(motorspeed);
  
  delay(1000);
}

void Flasher() {
  if (FlasherOn) {
    if (FlasherLeft)  MShield.setPin(FlasherL, true);
    if (FlasherRight) MShield.setPin(FlasherR, true);
  }
  else {
    if (FlasherLeft)  MShield.setPin(FlasherL, false);
    if (FlasherRight) MShield.setPin(FlasherR, false);
  }
  FlasherOn = !FlasherOn;
}

void loop() {
  // Do tacho stuff each interval, but only if showSpeed=true...
  if (showSpeed && (millis()-tachoTimer) > Interval) {
    tachoTimer = millis(); // Restart timer...
    distance = tachoPulses / IPCM;
    velocity = (tachoPulses-tachoSample) * f / Interval;  // Recalc to impulses m/h
    //velocityAvg = tachoPulses * f / (millis()-starttime);

    // Format and output velocity on the remote display...
    valueStr[0] = 0;
    dtostrf(velocity, 3, 2, valueStr);  // 3 is mininum width, 2 is precision
    sprintf(displayStr, "LCDPRINT0,6:%s m/h   |", valueStr);
    remoteBLE.sendMessage(displayStr);
    
    // Format and output distance on the remote display...
    valueStr[0] = 0;
    dtostrf(distance, 3, 2, valueStr);  // 3 is mininum width, 2 is precision
    sprintf(displayStr, "LCDPRINT1,6:%s cm    |", valueStr);
    remoteBLE.sendMessage(displayStr);
 
    tachoSample = tachoPulses; // Store current count...      
  }

  
  if (autoParking) { // auto parking...
    // To Do...
    autoParking = false; // End parking procedure...
  } else {
    // Normal loop...
    driveCar(); // Main...

    // Write distance and speed to remote display...

    if (WarningLights) { // Flashers on...
      FlasherRight = true;
      FlasherLeft = true;
      if ((millis()-lasttime) > 500) { // Flasher routine
        lasttime = millis();
        Flasher();
      }
    }
    else { // Flashers off...
      if (steerAngle < 89) { // Steering left
        FlasherLeft = true;
        FlasherRight = false;
        if ((millis()-lasttime) > 500) { // Flasher routine
          lasttime = millis();
          Flasher();
        }
      }
      else if (steerAngle > 91) { // Steering right
        FlasherRight = true;
        FlasherLeft = false;
        if ((millis()-lasttime) > 500) { // Flasher routine
          lasttime = millis();
          Flasher();
        }
      }
      else {
        FlasherRight = false;
        FlasherLeft = false;
        MShield.setPin(FlasherL, false); // Left flasher off
        MShield.setPin(FlasherR, false); // Right flasher off
        FlasherOn = false;
      }
    }

    // Brake light handling and auto-off...
    if (BrakeLights) {
      if (brakeOffTimer <= 0) brakeOffTimer = millis(); // Start timer...
      if ((millis()-brakeOffTimer) > 1000) { // Time-out brake lights...
        BrakeLights = false;
        brakeOffTimer = 0;
      } else {
        RedRearLight->setSpeed(255);         
      }
    } else {
      if (lightsOn)
        RedRearLight->setSpeed(REARLIGHT);
      else
        RedRearLight->setSpeed(0);
   }
 
  }
}
Motorshield_Flitzer_Bluetooth

Sketch en aansluitschema van het joystick shield

Het joystick shield heeft rechtsonder al een I²C-bus connector voor het LCD display, en rechtsboven een connector voor aansluiten van de Bluetooth module. De signaalniveaus en voedingsspanning zijn al 3.3 volt dus er hoeft geen extra level-shifter te worden gemaakt. De buitenste 2 pennen van de Bluetooth module vallen náast het voetje omdat deze alleen de vier noodzakelijke aansluitingen voert. Ik heb deze connectoren echter verwijderd omdat ik de noodzakelijke aansluitingen netjes onder de print wilde doen. Dat zitten ze ook niet in de weg bij de knoppen.

Een piezo-buzzer op de afstandsbediening leek me handig om b.v. een alarm van het model te kunnen doorgeven, ik sloot die aan op ingang A3. Omdat ik de aansluit-status van de module als een icoontje op het display wilde tonen, heb ik de STATE pin aangesloten op ingang A4.

De uitgaande stuursignalen vanaf het joystick shield zijn compatible met de ArduinoBlue library. Voor het display bouwde ik een eigen commandoset in waarmee het model hierop teksten kan laten verschijnen, laten knipperen of wissen.

// 'Flitzer' with Bluetooth remote joystick shield
// ArduinoBlue compatible, uses Adafruit AdaFruit MotorShield
//
// Arnoud van Delden - February-March 2021
//

#include "Servo.h"
#include "Adafruit_MotorShield.h"
#include "SoftwareSerial.h"
#include "ArduinoBlue.h"

//#define DEBUG
#define Baudrate 38400 // Serial monitor during DEBUG mode...

// Various defines...
#define EncoderIRQPin 2
#define ServoPin 9
#define HornPin 12
#define minAngle 55
#define maxAngle 125
#define MaxSpeed 255
#define FlasherL 0
#define FlasherR 14
#define REARLIGHT 100  // Default read light brightness, with headroom for brake light (=255)

// Distance and velocity measurement...
#define IPCM 14.606    // Nr impulses per cm with 50mm wheels...
#define Interval 1000  // Sample-/Measure interval in ms
volatile unsigned long tachoPulses;
unsigned long starttime;
const float f = 36000/IPCM; // Geschw. m/h = f x Impulse / Interval
unsigned long tachoSample;
float velocity, velocityAvg, distance;
unsigned long tachoTimer;
char valueStr[15];
char displayStr[50]; 

// Software Serial for BT-module. Keep D9 and D10 free on the otorshield for servos!
#define RX 4
#define TX 5
#define GND 6
#define Vcc 7
//#define BTBaud 38400 // Configured my MT-19 BLE to 38400 baud...
#define BTBaud 9600 // Configured my MT-19 BLE to 9600 baud...
SoftwareSerial BT(TX, RX);
ArduinoBlue remoteBLE(BT);

Servo ServoMotor; // Steering servo...

Adafruit_MotorShield MShield = Adafruit_MotorShield(0x60);
Adafruit_DCMotor *Motor = MShield.getMotor(1); // Main motor
Adafruit_DCMotor *Light = MShield.getMotor(2); // Headlights...
Adafruit_DCMotor *RedRearLight = MShield.getMotor(3); // Rear and brake light...
Adafruit_DCMotor *ReverseLight = MShield.getMotor(4); // Reverse light..

int error = 0; 
byte controller_type = 0;
int motorspeed = 0;
int lastspeed = 0;
int steerAngle = 90; // Start with default steer angle...
unsigned long lasttime;
unsigned long brakeOffTimer;
boolean FlasherLeft = false, FlasherRight = false, FlasherOn = false, RumbleBrake = false,
        WarningLights = false, BrakeLights = false, autoParking = false, lightsOn = false;
boolean showSpeed = true;

// Vars for ArduinoBlue...
int throttleAB, steeringAB, buttonAB; // Sliders are not used (yet)...

void count() { // Interrupt routine of encoder motor...
  tachoPulses++;
}

void driveCar() { // Remote controlled car...
  throttleAB = abs(remoteBLE.getThrottle());
  steeringAB = abs(remoteBLE.getSteering());
  if (steeringAB>99) steeringAB = 99;
  buttonAB = remoteBLE.getButton();
      
  // Servo steering...
  steerAngle = map(steeringAB, 0, 100, minAngle, maxAngle);
  
  #ifdef DEBUG
  if (steerAngle != 90) {
    Serial.print(" steerAngle: ");
    Serial.println(steerAngle, DEC);
  }
  #endif
  ServoMotor.write(steerAngle);

  // Process and set motor speed...
  motorspeed = throttleAB - 50;
  if (motorspeed>0) {
    // Forward...
    //motorspeed = abs(motorspeed);
    motorspeed = map(motorspeed, 0, 50, 0, 255);
    Motor->setSpeed(motorspeed);
    Motor->run(FORWARD);
    ReverseLight->setSpeed(0);
#ifdef DEBUG¨
    if (motorspeed>0) {
      Serial.print("Forward motorspeed: ");
      Serial.println(motorspeed, DEC);
    }
#endif

  } else {
    // Backward/reverse...
    motorspeed = map(motorspeed, 0, -50, 0, 255);
    Motor->run(BACKWARD);
    Motor->setSpeed(motorspeed);
    if (motorspeed>5)
      ReverseLight->setSpeed(255);
    else // Switch off reverse light if not moving...
      ReverseLight->setSpeed(0);
  #ifdef DEBUG
    if (motorspeed>0) {
      Serial.print("Reverse motorspeed: ");
      Serial.println(motorspeed, DEC);
    }
#endif   
  }
  if (motorspeed0) {
    delay(100); // Delay for button debouncing
    
    // Car horn...
    if (buttonAB==4) { // Button 4 was pressed...
      tone(HornPin, 200, 500);
      delay(1000);
      tone(HornPin, 200, 500);
      delay(500);
      noTone(HornPin);
#ifdef DEBUG
      Serial.println("Horn sounded");
#endif
    }
    
    // Toggle head and rear lights...
    if (buttonAB==1) { // Button 1 was pressed...
      if (lightsOn) {
        Light->setSpeed(0);
        RedRearLight->setSpeed(0);
        lightsOn=false;
      } else {
        Light->setSpeed(255);
        RedRearLight->setSpeed(REARLIGHT);
        lightsOn=true;
      }
#ifdef DEBUG
      Serial.println("Headlights toggle");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }
  
    // Toggle warning lights...
    if (buttonAB==2) { // Button 2 was pressed...
      if (WarningLights)
        WarningLights = false;
      else
        WarningLights = true;
#ifdef DEBUG
    Serial.println("WarningLights toggle");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }
  
    // Reset speed and distance...
    if (buttonAB==3) { // Button 3 was pressed...
      tachoPulses = tachoSample = 0;
      distance = velocity = velocityAvg = 0.0;
      Serial.println("Reset speed and distance");
#ifdef DEBUG
      Serial.println("Reset speed and distance");
#endif
    }

    // showSpeed...
    if (buttonAB==5) { // Button 5 was pressed...
      if (showSpeed) { // End display mode...
        remoteBLE.sendMessage("VALUES");
      } else {
        remoteBLE.sendMessage("CLR");
        delay(100);
        remoteBLE.sendMessage("LCDPRINT0,0:Speed        m/h");
        delay(100);
        remoteBLE.sendMessage("LCDPRINT1,1:Dist         cm");
        delay(100);
      }
      showSpeed = !showSpeed;
#ifdef DEBUG
      Serial.println("Show velocity and speed");
#endif
      // Toggle buttons/functions are one-shot, so wait until button is released
      while (remoteBLE.getButton()>0) delay(100);
    }  
  }

  //delay(50); // Grace...
}

void setup() {
#ifdef DEBUG
  Serial.begin(Baudrate); // Start serial monitor
#endif

  // Setup speed/distance tracking...
  pinMode(EncoderIRQPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(EncoderIRQPin), count, CHANGE);
  starttime = millis(); // Millisecs since program start...

  // Initialize car horn buzzer and signal we're alive...
  pinMode(HornPin, OUTPUT);
  tone(HornPin, 800, 250);
  delay(500);
  tone(HornPin, 800, 250);
  delay(250);
  noTone(HornPin);
      
  // Init BLE module over Software Serial...
  BT.begin(BTBaud);
  delay(1000);
  
  MShield.begin();              // Motor Shield init...
  Wire.setClock(400000);        // Set I²C-Frequency to 400 kHz...
  ServoMotor.attach(ServoPin);  // Servo with default angle...
  ServoMotor.write(steerAngle); // Default middle...
      
  ReverseLight->setSpeed(0); 
  ReverseLight->run(FORWARD);
  RedRearLight->setSpeed(0); 
  RedRearLight->run(BACKWARD);
  Light->setSpeed(0); 
  Light->run(FORWARD);

  MShield.setPin(FlasherL, false);
  MShield.setPin(FlasherR, false);
    
  tachoPulses = 0;
  motorspeed = 0;
  Motor->run(FORWARD); // Default=Forward...
  Motor->setSpeed(motorspeed);
  
  delay(1000);
}

void Flasher() {
  if (FlasherOn) {
    if (FlasherLeft)  MShield.setPin(FlasherL, true);
    if (FlasherRight) MShield.setPin(FlasherR, true);
  }
  else {
    if (FlasherLeft)  MShield.setPin(FlasherL, false);
    if (FlasherRight) MShield.setPin(FlasherR, false);
  }
  FlasherOn = !FlasherOn;
}

void loop() {

  // Do tacho stuff each interval, but only if showSpeed=true...
  if (showSpeed && (millis() - tachoTimer) > Interval) {
    tachoTimer = millis(); // Restart timer...
    distance = tachoPulses / IPCM;
    velocity = (tachoPulses-tachoSample) * f / Interval;  // Recalc to impulses m/h
    velocityAvg = tachoPulses * f / (millis()-starttime);
  
#ifdef DEBUG
    Serial.print("distance=");
    Serial.println(distance);
    dtostrf(distance, 3, 2, valueStr);  // 3 is mininum width, 2 is precision
    sprintf(displayStr, "distance %s cm", valueStr);
    Serial.println(displayStr);
    Serial.print("velocity=");
    Serial.println(velocity);
    dtostrf(velocity, 3, 2, valueStr);  // 3 is mininum width, 2 is precision
    sprintf(displayStr, "velocity %s m/h", valueStr);
    Serial.println(displayStr);        
    //Serial.print("velocityAvg=");
    //Serial.println(velocityAvg);
#endif
    
    tachoSample = tachoPulses; // Store current count...      
  }

  if (autoParking) { // auto parking...
    // To Do...
    autoParking = false; // End parking procedure...
  } else {
    // Normal loop...
    driveCar(); // Main...

    // Write distance and speed to remote display...

    if (WarningLights) { // Flashers on...
      FlasherRight = true;
      FlasherLeft = true;
      if ((millis()-lasttime) > 500) { // Flasher routine
        lasttime = millis();
        Flasher();
      }
    }
    else { // Flashers off...
      if (steerAngle < 89) { // Steering left
        FlasherLeft = true;
        FlasherRight = false;
        if ((millis()-lasttime) > 500) { // Flasher routine
          lasttime = millis();
          Flasher();
        }
      }
      else if (steerAngle > 91) { // Steering right
        FlasherRight = true;
        FlasherLeft = false;
        if ((millis()-lasttime) > 500) { // Flasher routine
          lasttime = millis();
          Flasher();
        }
      }
      else {
        FlasherRight = false;
        FlasherLeft = false;
        MShield.setPin(FlasherL, false); // Left flasher off
        MShield.setPin(FlasherR, false); // Right flasher off
        FlasherOn = false;
      }
    }

    // Brake light handling and auto-off...
    if (BrakeLights) {
      if (brakeOffTimer <= 0) brakeOffTimer = millis(); // Start timer...
      if ((millis()-brakeOffTimer) > 1000) { // Time-out brake lights...
        BrakeLights = false;
        brakeOffTimer = 0;
      } else {
        RedRearLight->setSpeed(255);         
      }
    } else {
      if (lightsOn)
        RedRearLight->setSpeed(REARLIGHT);
      else
        RedRearLight->setSpeed(0);
   }
 
  }
}
joystick_shield_lcd_wiring

Op meerdere manieren bestuurbaar!

Als het joystick shield met het LCD display wordt gebruikt, zijn de uitvoermogelijkheden van het model op het display van de besturing nuttig voor functionele meldingen of waarschuwingen. Het is echter wel zinvol om het doorsturen van continu geüpdate meldingen, zoals afstand en snelheid van het autootje, pas te starten na drukken van een specifieke knop op het joystick shield. De ArduinoBlue app meldt elk bericht namelijk in een apart pop-up venster en raakt hierdoor snel overbelast.

In de Sketch hierboven heb ik hiervoor op het joystick shield de kleine smd-knop 'F' bestemd. Door nogmaals te drukken keer je terug naar de weergave van de joystick- en button-waarden. Ook bleek het in de praktijk handig de afgelegde weg te kunnen resetten. Dit is mogelijk met knop 'C'.

De ArduinoBlue app op de smartphone koppelt met de fabrieksinstellingen direct met de Bluetooth module in het model. Doordat in de Sketch voor het bestuurbare model de ArduinoBlue functie-bibliotheek is gebruikt kan, afhankelijk van welke besturing aan het wagentje wordt gekoppeld, het met de ArduinoBlue app of deze zelfgemaakte unit worden bestuurd. Er kan echter natuurlijk maar één besturing tegelijk actief zijn.

Dit artikel over de PS2-controller is ook (in de Duitse taal) gepubliceerd in nummer 1 van 2021 van de ft:pedia; een zeer interessant online magazine over techniek in combinatie met het constructiemateriaal fischertechnik.