0

I am finishing up a project for school, and I am at the last step for getting my device 100% working. For a little information on the project. I am developing a wireless speedometer that tracks rpm and converts it to MPH through a hall effects sensor. The modules I am using Heltec LoRa esp32 in addition to the LoRa library through Arduino. The issue I am having is that I am trying to use the data packets received and use the sensor values sent out to be used in an if statement to activate a vibration motor to indicate if they reached a certain top speed. My code is below, and any input on how to use the packets correctly would be much appreciated. Thank you. Receiver:

//lora stuff
#include <SPI.h>
#include <LoRa.h>
#include "SSD1306.h"

SSD1306 display(0x3c, 4, 15);

#define SS      18
#define RST     14
#define DI0     26
#define BAND    433E6

//neopixel
#include <Adafruit_NeoPixel.h>
#define LED_PIN 32
#define LED_COUNT 1

Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, 32, NEO_GRB + NEO_KHZ800);
const int buttonPin = 17;// button
const int neo = 32; //
const int neoCount = 1; //number of pixels
int counter = 0; //button state counter
int buttonState = 0;

//Vibration motor
int motorPin = 25;

//level of speed in mph to trigger motor
int beginner = 10;
int intermediate = 15;
int expert = 25;
int mphVal = 0;

String data;
unsigned int totalMPH;
void setup() {
  //lora
  pinMode(16, OUTPUT);
  digitalWrite(16, LOW);    // set GPIO16 low to reset OLED
  delay(50);
  digitalWrite(16, HIGH);
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);

  // button
  pinMode(buttonPin, INPUT);

  //motor
  pinMode(motorPin, OUTPUT);

  //neopixel
  strip.begin();
  strip.show();
  strip.setBrightness(50);
  Serial.begin(115200);

  //lora initial
  while (!Serial); //if just the the basic function, must connect to a computer
  delay(100);
  Serial.println("Speed Receiver");
  display.drawString(5, 5, "Speed Receiver");
  display.display();
  SPI.begin(5, 19, 27, 18);
  LoRa.setPins(SS, RST, DI0);

  if (!LoRa.begin(BAND)) {
    display.drawString(5, 25, "Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initial OK!");
  display.drawString(5, 25, "LoRa Initializing OK!");
  display.display();

}

void loop() {
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // received a packets
    //Serial.print("Received packet. ");
    display.clear();
    display.setFont(ArialMT_Plain_16);
    display.drawString(3, 0, "Received Speed ");
    display.display();
    // read packet
    while (LoRa.available()) {
      String data = LoRa.readString();
      Serial.print(data);
      display.drawString(20, 22, data);
      display.display();
      // if speed reaches x then motor will buzz
      if (data.equals("MPH: 12.5")) {
        digitalWrite(motorPin, HIGH);
        delay(10);
        digitalWrite(motorPin, LOW);
        delay(10);
      }

    }
    // print RSSI of packet
    //Serial.print(" with RSSI ");
    //Serial.println(LoRa.packetRssi());
    //    display.drawString(20, 45, "RSSI:  ");
    //    display.drawString(70, 45, (String)LoRa.packetRssi());
    //    display.display();


  }

  //taking the packet and turning it into an int from string
  //String data = data.toInt();
  //int data = totalMPH;
  //float totalMPH = data.toFloat();
  //   char data;
  //   float totalMPH;

  //totalMPH = atof(data);
  //Serial.println(totalMPH);

  //button
  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH) {
    counter++;

    delay(150);
  }

  //button states with three colors
  //beginner
  else if (counter == 0) {
    //Serial.println("pushed");
    strip.setPixelColor(0, 255, 0, 0);
    strip.show();
    //Serial.println(0);
    display.drawString(10, 45, "REC: 10 MPH");
    display.display();
    //vibration motor will trigger if the speed recieved is less then or equal to 12mph
//    digitalWrite(motorPin, HIGH);
//    delay(4000);
//    digitalWrite(motorPin, LOW);
//    delay(120000);

  }
  //intermediate
  else if (counter == 1) {
    strip.setPixelColor(0, 0, 255, 0);
    strip.show();
    Serial.println(1);
    display.drawString(10, 45, "REC: 15 MPH");
    display.display();
    //vibration motor will trigger if the speed recieved is less then or equal to 17mph
    //      digitalWrite(motorPin, HIGH);
    //      delay(4000);
    //      digitalWrite(motorPin, LOW);
    //      delay(240000);
    //digitalWrite(motorPin, LOW);

  }
  //expert
  else if (counter == 2) {
    strip.setPixelColor(0, 0, 0, 255);
    strip.show();
    Serial.print(2);
    display.drawString(10, 45, "REC: 20 MPH");
    display.display();
    //digitalWrite(motorPin, LOW);
    
  }
  //reset settings
  else {
    counter = 0;
  }
}

Transmitter:

//lora
#include <SPI.h>
#include <LoRa.h>
#include "SSD1306.h"
#include <Arduino.h>
#include <Bounce2.h>

Bounce hall = Bounce();
SSD1306  display(0x3c, 4, 15);

#define SS      18
#define RST     14
#define DI0     26
#define BAND    433E6  //915E6 

int counter = 0;
int rpmTimer = 0;
int rpmInterval = 1000;

void setup() {
  //board and lcd
  pinMode(25, OUTPUT); //Send success, LED will bright 1 second
  pinMode(16, OUTPUT);
  digitalWrite(16, LOW);    // set GPIO16 low to reset OLED
  delay(50);
  digitalWrite(16, HIGH);

  // sensor and led
  hall.attach(37, INPUT);
  hall.interval(1);
  //pinMode(led, OUTPUT);
  //pinMode(hallSens, INPUT);
  Serial.begin(115200);

  //lcd
  while (!Serial); //If just the the basic function, must connect to a computer
  // Initialising the UI will init the display too.
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(5, 5, "LoRa Sender");
  display.display();
  //lora
  SPI.begin(5, 19, 27, 18);
  LoRa.setPins(SS, RST, DI0);
  Serial.println("LoRa Sender");
  if (!LoRa.begin(BAND)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initial OK!");
  display.drawString(5, 20, "LoRa Initializing OK!");
  display.display();
  delay(10);
}


void loop() {
 
  hall.update();

  if(hall.rose()){
    counter++;
  }
  if(millis() - rpmTimer > rpmInterval){
    float rpm = counter * 60;
    float rph = rpm * 60;
    float dia = 0.00004349;
    float cir = dia * 3.14;
    float totalMPH = rph * cir;

    Serial.println(totalMPH);

    rpmTimer = millis();
    counter = 0;

  //sending packet
  LoRa.beginPacket();
  LoRa.print("MPH: " + String(totalMPH));
  LoRa.endPacket();

  //LoRa.sleep(); //puts lora receiver to sleep probably dont need
  digitalWrite(25, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(10);                       // wait for a second
  digitalWrite(25, LOW);    // turn the LED off by making the voltage LOW
  delay(100);                       // wait for a second
  //delay(300);

  
  
  }
  
}
Tarick Welling
  • 3,119
  • 3
  • 19
  • 44
  • you could send "S12.5" (S=Speed) and in the receiver use substr() to get the first char and the rest of the message into stof(). The first char of the message determines what the rest of the message means. The sender and receiver both know that "S" means "MPH" and "12.5" is what "S" is referring to. This is essentially wire-based API design, where the transmitter determines the API and the receiver just parses the string. It makes for very compact messages over LoRa. – codebrane Mar 23 '21 at 12:38
  • Why send string? why not just sent numeric with [Lora.write()](https://github.com/sandeepmistry/arduino-LoRa/blob/master/src/LoRa.h#L51-L52)? – hcheung Mar 23 '21 at 13:07
  • if the sender manages more than one sensor the receiver would need to know which measurement Lora.write() referred to – codebrane Mar 23 '21 at 14:41
  • @codebrane OP's question is not about how to design a wire-based communication protocol, I agreed with you that it make more sense to compare data with a numeric value than a string, which raise the question on why you'd want to convert a numeric value to a string, send it over, parse it and convert it back to a numeric value? In fact for the argument of having a "very compact message over LoRa", one could using `(int) 12.5*10` by converting a float to an integer with one decimal point precision, this will make it very compact by sending just two bytes than a string of 5 bytes with "S12.5". – hcheung Mar 24 '21 at 00:54
  • There are many way to design a serial API, I could think of using a struct than a string. How to design a serial API is not relevant in this case, and it is also often opinionated, and therefore off-topic. – hcheung Mar 24 '21 at 00:57

1 Answers1

0

In terms of wire-based API design:

// packet is the packet from the transmitter
String packet = "S12.5"
String measurement = packet.substring(0, 1); // "S"
if (measurement == "S") {
  // at this point we know that the rest of the message is the MPH
  String data = packet.substring(1, strlen(packet)-1); // "12.5"
  float mph = data.toFloat();
  if (mph >= TOP_SPEED) {
    doSomething();
  }
  else {
    doSomethingElse();
  }
}

you could use charAt(0) to get the measurement instead of substring() if you know there are less than 25 sensors on the network.

each measurement would have its own char at the start of message and the data that related to that measurement would immediately follow. The sender and receiver are therefore interacting like a client/server in an API exchange. There is an implicit contract between them such that, when the transmitter says "S12.5", the receiver knows that means 12.5MPH. It's your system so you can design the communication packets any way you like.

codebrane
  • 4,290
  • 2
  • 18
  • 27