Fall detection using MPU6050 and sim900a - c++

I am trying to make fall detection using MPU6050, SIM900a, and Arduino Nano. after uploading the code to Arduino, the serial monitor show me this:
serial monitor output
this is the code that I have tried,
#include <SoftwareSerial.h> // Library for using software serial communication
//______________________sms part_____________________________________________
SoftwareSerial SIM900(7, 8); // rx, tx
char c = ' '; // variable to store the data from the sms module
//_______________________MPU part_______________________________________________________
#include <Wire.h>
const int MPU_addr=0x68; // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
float ax=0, ay=0, az=0, gx=0, gy=0, gz=0;
//int data[STORE_SIZE][5]; //array for saving past data
//byte currentIndex=0; //stores current data array index (0-255)
boolean fall = false; //stores if a fall has occurred
boolean trigger1=false; //stores if first trigger (lower threshold) has occurred
boolean trigger2=false; //stores if second trigger (upper threshold) has occurred
boolean trigger3=false; //stores if third trigger (orientation change) has occurred
byte trigger1count=0; //stores the counts past since trigger 1 was set true
byte trigger2count=0; //stores the counts past since trigger 2 was set true
byte trigger3count=0; //stores the counts past since trigger 3 was set true
int angleChange=0;
void setup(){
randomSeed(analogRead(0));
Serial.begin(9600);// baudrate for serial monitor
while (!Serial) {
; // wait for serial port to connect. Needed for Native USB only
}
SIM900.begin(9600); // baudrate for GSM shield
Serial.println(" Logging time completed!");
delay(1000); // wait for 5 seconds
Wire.begin();
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
}
void loop(){
//______________________________________MPU_________________________________________
mpu_read();
//2050, 77, 1947 are values for calibration of accelerometer
// values may be different for you
ax = (AcX-2050)/16384.00;
ay = (AcY-77)/16384.00;
az = (AcZ-1947)/16384.00;
//270, 351, 136 for gyroscope
gx = (GyX+270)/131.07;
gy = (GyY-351)/131.07;
gz = (GyZ+136)/131.07;
// calculating Amplitute vactor for 3 axis
float Raw_AM = pow(pow(ax,2)+pow(ay,2)+pow(az,2),0.5);
int AM = Raw_AM * 10; // as values are within 0 to 1, I multiplied
// it by for using if else conditions
Serial.println(AM);
//Serial.println(PM);
//delay(500);
if (trigger3==true){
trigger3count++;
//Serial.println(trigger3count);
if (trigger3count>=10){
angleChange = pow(pow(gx,2)+pow(gy,2)+pow(gz,2),0.5);
//delay(10);
Serial.println(angleChange);
if ((angleChange>=0) && (angleChange<=10)){ //if orientation changes remains between 0-10 degrees
fall=true; trigger3=false; trigger3count=0;
Serial.println(angleChange);
}
else{ //user regained normal orientation
trigger3=false; trigger3count=0;
Serial.println("TRIGGER 3 DEACTIVATED");
}
}
}
if (fall==true){ //in event of a fall detection
Serial.println("FALL DETECTED");
MakeCall;
delay(100000);
fall=false;
// exit(1);
}
if (trigger2count>=6){ //allow 0.5s for orientation change
trigger2=false; trigger2count=0;
Serial.println("TRIGGER 2 DECACTIVATED");
}
if (trigger1count>=6){ //allow 0.5s for AM to break upper threshold
trigger1=false; trigger1count=0;
Serial.println("TRIGGER 1 DECACTIVATED");
}
if (trigger2==true){
trigger2count++;
//angleChange=acos(((double)x*(double)bx+(double)y*(double)by+(double)z*(double)bz)/(double)AM/(double)BM);
angleChange = pow(pow(gx,2)+pow(gy,2)+pow(gz,2),0.5); Serial.println(angleChange);
if (angleChange>=30 && angleChange<=400){ //if orientation changes by between 80-100 degrees
trigger3=true; trigger2=false; trigger2count=0;
Serial.println(angleChange);
Serial.println("TRIGGER 3 ACTIVATED");
}
}
if (trigger1==true){
trigger1count++;
if (AM>=12){ //if AM breaks upper threshold (3g)
trigger2=true;
Serial.println("TRIGGER 2 ACTIVATED");
trigger1=false; trigger1count=0;
}
}
if (AM<=2 && trigger2==false){ //if AM breaks lower threshold (0.4g)
trigger1=true;
Serial.println("TRIGGER 1 ACTIVATED");
}
//It appears that delay is needed in order not to clog the port
delay(100);
}
//______________________________MPU raeding________________________
void mpu_read(){
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
}
//______________________________________GSM module calling function________________
void MakeCall()
{
SIM900.println("ATD+*********;"); // ATDxxxxxxxxxx; -- watch out here for semicolon at the end!!
Serial.println("Calling "); // print response over serial port
delay(20000); //wait
}
the configuration of MPU050
VCC --- 5V in arduino
GND ----GND
SCL ----A5
SDA----A4
INT ----- D2
The serial monitor should show some numbers first but instead of showing numbers trigger 1 activated and disactivate automatically and I didn't even touch it . No matter how much I throw the MPU6050, trigger 2 and 3 are not activated. all the three trigger must be activated to make the sim900a module make a call.
what could be the reason ? could be the code or the hardware connection.
here is a picture of the MPU position in case it could be the problem.
MPU6050 position
edit:
i have tried this code but all the values show zero values
#include <MPU6050.h>
/*
MPU6050 Triple Axis Gyroscope & Accelerometer. Simple Gyroscope Example.
Read more: http://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-zyroskop-i-akcelerometr-mpu6050.html
GIT: https://github.com/jarzebski/Arduino-MPU6050
Web: http://www.jarzebski.pl
(c) 2014 by Korneliusz Jarzebski
*/
#include <Wire.h>
MPU6050 mpu;
void setup()
{
Serial.begin(115200);
// Initialize MPU6050
Serial.println("Initialize MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
delay(500);
}
// If you want, you can set gyroscope offsets
// mpu.setGyroOffsetX(155);
// mpu.setGyroOffsetY(15);
// mpu.setGyroOffsetZ(15);
// Calibrate gyroscope. The calibration must be at rest.
// If you don't want calibrate, comment this line.
mpu.calibrateGyro();
// Set threshold sensivty. Default 3.
// If you don't want use threshold, comment this line or set 0.
mpu.setThreshold(3);
// Check settings
checkSettings();
}
void checkSettings()
{
Serial.println();
Serial.print(" * Sleep Mode: ");
Serial.println(mpu.getSleepEnabled() ? "Enabled" : "Disabled");
Serial.print(" * Clock Source: ");
switch(mpu.getClockSource())
{
case MPU6050_CLOCK_KEEP_RESET: Serial.println("Stops the clock and keeps the timing generator in reset"); break;
case MPU6050_CLOCK_EXTERNAL_19MHZ: Serial.println("PLL with external 19.2MHz reference"); break;
case MPU6050_CLOCK_EXTERNAL_32KHZ: Serial.println("PLL with external 32.768kHz reference"); break;
case MPU6050_CLOCK_PLL_ZGYRO: Serial.println("PLL with Z axis gyroscope reference"); break;
case MPU6050_CLOCK_PLL_YGYRO: Serial.println("PLL with Y axis gyroscope reference"); break;
case MPU6050_CLOCK_PLL_XGYRO: Serial.println("PLL with X axis gyroscope reference"); break;
case MPU6050_CLOCK_INTERNAL_8MHZ: Serial.println("Internal 8MHz oscillator"); break;
}
Serial.print(" * Gyroscope: ");
switch(mpu.getScale())
{
case MPU6050_SCALE_2000DPS: Serial.println("2000 dps"); break;
case MPU6050_SCALE_1000DPS: Serial.println("1000 dps"); break;
case MPU6050_SCALE_500DPS: Serial.println("500 dps"); break;
case MPU6050_SCALE_250DPS: Serial.println("250 dps"); break;
}
Serial.print(" * Gyroscope offsets: ");
Serial.print(mpu.getGyroOffsetX());
Serial.print(" / ");
Serial.print(mpu.getGyroOffsetY());
Serial.print(" / ");
Serial.println(mpu.getGyroOffsetZ());
Serial.println();
}
void loop()
{
Vector rawGyro = mpu.readRawGyro();
Vector normGyro = mpu.readNormalizeGyro();
Serial.print(" Xraw = ");
Serial.print(rawGyro.XAxis);
Serial.print(" Yraw = ");
Serial.print(rawGyro.YAxis);
Serial.print(" Zraw = ");
Serial.println(rawGyro.ZAxis);
Serial.print(" Xnorm = ");
Serial.print(normGyro.XAxis);
Serial.print(" Ynorm = ");
Serial.print(normGyro.YAxis);
Serial.print(" Znorm = ");
Serial.println(normGyro.ZAxis);
delay(10);
}
what could be the reason?

Related

How to control a linear actuator with an ESP32 and a BME280 sensor

I'm trying to program an EPS32 board to open and close a linear actuator depending of the temperature a BME 280 sensor gets. It is for an automated gardening project :)
My code is functional but I would like to perfect it.
The actuator is currently connected with two GPIO pin, one to open and he other to close.
GOALS:
Switch off relays when not used.
Replace the use of delay() with millis()
Currently, the actuator is opening when temperature is above 27c°. Relay is on for 7 seconds which let the time to the actuator to go full course. Then, the relay is switch off.
But if the temperature stay above 27, the "if" part of the loop is going on an on, meaning it turn on for 7 seconds, switch off for 7 seconds and so on... I would like to switch all relays off when not needed to save energy and extend components life.
I suppose there might be something to do with && or even the analogRead() function to get the state of the actuator and apply actions from that but it is too complicated for me.
To open the actuator, I decided to time the course of the actuator and let the relay on for that timing with a delay() function. There is probably a different approach to this ( with analogRead() I suppose) but I find it simple and working. I would like to replace delay() with millis() function in order not to block the rest of the script (I plan to add fans, water valves, a float switch sensor...).
May you show me the way ?
#include <Adafruit_BME280.h>
#include <WiFi.h>
const char* ssid = "#####"; // ESP32 and ESP8266 uses 2.4GHZ wifi only
const char* password = "#####";
//MQTT Setup Start
#include <PubSubClient.h>
#define mqtt_server "####.###.##.##"
WiFiClient espClient;
PubSubClient client(espClient);
#define mqttActuator "growShed/Acuator"
#define mqttTemp1 "growShed/temp1"
int actuatorUp = 15; //GPIO de l'actuator
int actuatorDown = 2; //GPIO de l'actuator
int actuatorUpread = (!digitalRead(actuatorUp)); //read state of gpio : on or off
int actuatorDownread = (!digitalRead(actuatorDown)); //read state of gpio : on or off
//MQTT Setup End
Adafruit_BME280 bme1; // I2C
//utiliser les virgules sur les capteurs suivants
float temp1, hum1, pres1;
//gestion des délais entre chaque captations de data
unsigned long millisNow1 = 0; //for delay purposes (laisser 0)
unsigned int sendDelay1 = 2000; //delay before sending sensor info via MQTT
//gestion des délais entre chaque captations de data
unsigned long millisNow3 = 0; //for delay purposes (laisser 0)
unsigned int sendDelay3 = 7000; //delay before sending sensor info via MQTT
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println();
// begin Wifi connect
Serial.println("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(2000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address (wifi RaspPi) : ");
Serial.println(WiFi.localIP());
//end Wifi connect
client.setServer(mqtt_server, 1883);
//BME
delay(5000);
unsigned status;
status = bme1.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
Serial.print("SensorID was: 0x"); Serial.println(bme1.sensorID(),16);
Serial.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
Serial.print(" ID of 0x56-0x58 represents a BMP 280,\n");
Serial.print(" ID of 0x60 represents a BME 280.\n");
Serial.print(" ID of 0x61 represents a BME 680.\n");
while (1);
}
//Acuator
pinMode(actuatorUp, OUTPUT);
digitalWrite(actuatorDown, HIGH); //Begin with actuator switched off
pinMode(actuatorDown, OUTPUT);
digitalWrite(actuatorUp, HIGH); //Begin with actuator switched off
//End Actuator
}
bool getValues() {
temp1 = round(bme1.readTemperature()); //rounded values
actuatorUpread = (digitalRead(actuatorUp)); //inverted value as the relay is inverted
Serial.print("BME 1 Temperature = ");
Serial.print(temp1);
Serial.println(" °C");
Serial.print("État de l'actuator haut = ");
Serial.println(actuatorUpread);
Serial.print("État de l'actuator bas = ");
Serial.print(actuatorDownread);
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
int counter = 0;
while (!client.connected()) {
if (counter==5){
ESP.restart();
}
counter+=1;
Serial.println("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("growTentController")) {
Serial.println("connected");
} else {
Serial.println("failed, rc=");
Serial.println(client.state());
Serial.println(" try again in 5 seconds");
// Wait 10 seconds before retrying
delay(5000);
}
}
}
void loop() {
// put your main code here, to run repeatedly:
if (!client.connected()){
reconnect();
}
if (millis() > millisNow1 + sendDelay1){
if (getValues()) {
client.publish(mqttTemp1, String(temp1).c_str(),true);
client.publish(mqttActuator, String(actuatorUpread).c_str(),true);
millisNow1 = millis();
}
}
//Acuator moving according to BME280 temperature sensor
temp1 = bme1.readTemperature();
if (millis() > millisNow3 + sendDelay3){
if ((temp1 > 27)){
digitalWrite(actuatorUp, LOW);
delay(7000); // approx the time it take to execute the full course of the acuator.
digitalWrite(actuatorUp, HIGH);
}
else {
digitalWrite(actuatorDown, LOW);
delay(7000); // approx the time it take to execute the full course of the acuator
digitalWrite(actuatorDown, HIGH);
}
millisNow3 = millis();
}
}
I think this is what you want to do. This uses a state variable to keep track of what state the actuator is in.
When the actuator is closed, the loop will check the temperature and, if the temperature goes above 27 degrees, it will start opening the actuator. Then the loop will simply monitor the time using millis() until the actuator is open - so you can put other code in the loop for other sensors.
The same happens if the actuator is open, it will close the actuator when the temperature is lower than 26 degrees (kept a degree difference here to stop the code constantly triggering if the temperature hovers around 27 degrees).
Currently this will read the temperature on every iteration of the loop whilst the actuator is either open or closed - so I've put a small delay at the end of the loop to restrict this.
Please note - I have not tested any of the changes in the code as I don't have the hardware to hand. But it compiles, and should give you an idea of how you can use a state model with millis() to handle this sort of thing without delaying the loop.
#include <Adafruit_BME280.h>
#include <WiFi.h>
const char* ssid = "#####"; // ESP32 and ESP8266 uses 2.4GHZ wifi only
const char* password = "#####";
//MQTT Setup Start
#include <PubSubClient.h>
#define mqtt_server "####.###.##.##"
WiFiClient espClient;
PubSubClient client(espClient);
#define mqttActuator "growShed/Acuator"
#define mqttTemp1 "growShed/temp1"
int actuatorUp = 15; //GPIO de l'actuator
int actuatorDown = 2; //GPIO de l'actuator
int actuatorUpread = (!digitalRead(actuatorUp)); //read state of gpio : on or off
int actuatorDownread = (!digitalRead(actuatorDown)); //read state of gpio : on or off
//MQTT Setup End
Adafruit_BME280 bme1; // I2C
//utiliser les virgules sur les capteurs suivants
float temp1, hum1, pres1;
//gestion des délais entre chaque captations de data
unsigned long millisNow1 = 0; //for delay purposes (laisser 0)
unsigned int sendDelay1 = 2000; //delay before sending sensor info via MQTT
//gestion des délais entre chaque captations de data
unsigned long millisNow3 = 0; //for delay purposes (laisser 0)
unsigned int sendDelay3 = 7000; //delay before sending sensor info via MQTT
// The states the actuator can be in.
enum ActuatorState
{
OPENING,
OPEN,
CLOSING,
CLOSED
};
ActuatorState actuatorState;
// When opening or closing the actuator - this is the time to stop.
unsigned long actuatorEndTime = 0 ;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println();
// begin Wifi connect
Serial.println("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(2000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address (wifi RaspPi) : ");
Serial.println(WiFi.localIP());
//end Wifi connect
client.setServer(mqtt_server, 1883);
//BME
delay(5000);
unsigned status;
status = bme1.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
Serial.print("SensorID was: 0x"); Serial.println(bme1.sensorID(),16);
Serial.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
Serial.print(" ID of 0x56-0x58 represents a BMP 280,\n");
Serial.print(" ID of 0x60 represents a BME 280.\n");
Serial.print(" ID of 0x61 represents a BME 680.\n");
while (1);
}
//Acuator
pinMode(actuatorUp, OUTPUT);
digitalWrite(actuatorDown, HIGH); //Begin with actuator switched off
pinMode(actuatorDown, OUTPUT);
digitalWrite(actuatorUp, HIGH); //Begin with actuator switched off
actuatorState = CLOSED ;
//End Actuator
}
bool getValues() {
temp1 = round(bme1.readTemperature()); //rounded values
actuatorUpread = (digitalRead(actuatorUp)); //inverted value as the relay is inverted
Serial.print("BME 1 Temperature = ");
Serial.print(temp1);
Serial.println(" °C");
Serial.print("État de l'actuator haut = ");
Serial.println(actuatorUpread);
Serial.print("État de l'actuator bas = ");
Serial.print(actuatorDownread);
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
int counter = 0;
while (!client.connected()) {
if (counter==5){
ESP.restart();
}
counter+=1;
Serial.println("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("growTentController")) {
Serial.println("connected");
} else {
Serial.println("failed, rc=");
Serial.println(client.state());
Serial.println(" try again in 5 seconds");
// Wait 10 seconds before retrying
delay(5000);
}
}
}
void loop() {
// put your main code here, to run repeatedly:
if (!client.connected()){
reconnect();
}
if (millis() > millisNow1 + sendDelay1){
if (getValues()) {
client.publish(mqttTemp1, String(temp1).c_str(),true);
client.publish(mqttActuator, String(actuatorUpread).c_str(),true);
millisNow1 = millis();
}
}
//Acuator moving according to BME280 temperature sensor
switch ( actuatorState ) {
case OPEN:
// Does it need closing (use value lower than 27 so we're not constantly opening and
// closing the actuator if the temperature is floating around 27)
temp1 = bme1.readTemperature();
if ( temp1 < 26 ) {
digitalWrite(actuatorDown, LOW);
actuatorState = CLOSING;
actuatorEndTime = millis() + 7000 ;
}
break;
case CLOSED:
// Does it need opening?
temp1 = bme1.readTemperature();
if ( temp1 > 27 ) {
digitalWrite(actuatorUp, LOW);
actuatorState = OPENING;
actuatorEndTime = millis() + 7000;
}
break ;
case OPENING:
// Is it fully open?
if ( millis() >= actuatorEndTime ) {
digitalWrite(actuatorUp, HIGH);
actuatorState = OPEN ;
}
break ;
case CLOSING:
// Is it fully closed?
if ( millis() >= actuatorEndTime ) {
digitalWrite(actuatorDown, HIGH);
actuatorState = CLOSED ;
}
break ;
}
// small delay to run the loop - and do the temperature check 5 times per second.
delay(200);
}

Rotary encoder strange behaviour

I have problem with results (on serial monitor) of rotary encoder.
I am using Arduino UNO and RotaryEncoder library.
When I am running example code serial monitor show proper values when rotating with any speed.
I want to use encoder to change volume in Df-player.
Problem starts when I want to use this code together with more complicated one - Mp3 player.
It actually works only when I am rotating encoder very very slowly
#include <SPI.h>
#include <MFRC522.h>
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <RotaryEncoder.h>
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
#define PIN_IN1 2
#define PIN_IN2 3
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 30
const int playPauseButton = 4;
const int shuffleButton = 5;
boolean isPlaying = false;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
SoftwareSerial mySoftwareSerial(5, 6); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins:
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
// Last known rotary position.
int lastPos = -1;
//*****************************************************************************************//
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC, COMMENT OUT IF IT FAILS TO PLAY WHEN DISCONNECTED FROM PC
mySoftwareSerial.begin(9600);
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
while (! Serial);
encoder.setPosition(5 / ROTARYSTEPS); // start with the value of 5.
pinMode(playPauseButton, INPUT_PULLUP);
pinMode(shuffleButton, INPUT_PULLUP);
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
}
Serial.println(F("DFPlayer Mini online. Place card on reader to play a spesific song"));
//myDFPlayer.volume(15); //Set volume value. From 0 to 30
//volumeLevel = map(analogRead(volumePot), 0, 1023, 0, 30); //scale the pot value and volume level
myDFPlayer.volume(5);
//prevVolume = volumeLevel;
//----Set different EQ----
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
// myDFPlayer.EQ(DFPLAYER_EQ_POP);
// myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
// myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
// myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
// myDFPlayer.EQ(DFPLAYER_EQ_BASS);
}
//*****************************************************************************************//
void loop() {
encoder.tick();
// get the current physical position and calc the logical position
int newPos = encoder.getPosition() * ROTARYSTEPS;
if (newPos < ROTARYMIN) {
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN;
} else if (newPos > ROTARYMAX) {
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX;
} // if
if (lastPos != newPos) {
Serial.println(newPos);
myDFPlayer.volume(newPos);
lastPos = newPos;
} // if
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte block;
byte len;
MFRC522::StatusCode status;
if (digitalRead(playPauseButton) == LOW) {
if (isPlaying) {
myDFPlayer.pause();
isPlaying = false;
Serial.println("Paused..");
}
else {
isPlaying = true;
myDFPlayer.start();
Serial.println("Playing..");
}
delay(500);
}
if (digitalRead(shuffleButton) == LOW) {
myDFPlayer.randomAll();
Serial.println("Shuffle Play");
isPlaying = true;
delay(1000);
}
//-------------------------------------------
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( mfrc522.PICC_IsNewCardPresent()) {
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
//-------------------------------------------
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex
//-------------------------------------------
Serial.print(F("Number: "));
//---------------------------------------- GET NUMBER AND PLAY THE SONG
byte buffer2[18];
block = 1;
len = 18;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT NUMBER
String number = "";
for (uint8_t i = 0; i < 16; i++)
{
number += (char)buffer2[i];
}
number.trim();
Serial.print(number);
//PLAY SONG
myDFPlayer.play(number.toInt());
isPlaying = true;
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
}
Any ideas what is wrong?
You have a delay(1000) in your main loop, and since your RotaryEncoder object seems to need a tick() function, i am assuming that it is not interrupt driven. This means that it will check only once per second if it has moved to the next step.
If a rotary encoder is stepped twice, and the middle step is missed by the MCU, the latter has no way of knowing which way round the encoder has turned.
So in this case you can only turn it one step per second.
What you need is, either:
a free running main loop, which goes round at least 100 times per second. (less nice)
a rotary encoder driver that is interrupt driven. (very nice)
I don't know if such a library exists, because i tend not to use arduino libs, but it is a very good exercise to write your own using GPIO interrupts.

Unreliable SPI byte array transfer from Arduino to Raspberry Pi

I'm working on a project that collects data from an Arduino Pro Mini and sends it using SPI to a raspberry Pi for storage.
The Pro Mini will be reading analog input and calculating voltage (once I finish), and passing values to the Pi when prompted using an ISR.
I'm using C/C++ for both platforms to keep it uniform. The slave code is snipped together using Arduino IDE and the master code is based off a BCM2835 SPI library example for the Pi.
Arduino code is meant to calculate a float value and pre-process the float value into an array of 4 bytes/chars (I'm shooting for binary because I think it is the best way to go).
Once prompted by the Pi, each byte is sent and will be recompiled into a float.
Here is what I have now:
Slave
/*************************************************************
ARDUINO BREAKER READ/SPI PRE-PROC/TRANSMIT CASES
****************************************************************/
/***************************************************************
Global Variables
***************************************************************/
byte command = 0; //command from PI
byte bytes[4]; //
int sensorVoltage, sensorCurrent; //eventual live reading vars
float Voltage, Current, RealCurrent, RealVoltage, Power;
/***************************************************************
Set Up
-designate arudino as slave
-turn on interrupts
***************************************************************/
void setup (void)
{
//debugging with serial monitor
Serial.begin(9600);
// Set up arduino as slave
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
pinMode(SS, INPUT);
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
} // end of setup
/*************************************************************
Interrupt Service Routine
************************************************************/
// SPI interrupt routine
ISR (SPI_STC_vect)
{
delay(500); //for errors
// Create union of shared memory space
union
{
float f_var;
unsigned char bytes[4];
} u;
// Overwrite bytes of union with float variable
u.f_var = RealVoltage;
// Assign bytes to input array
memcpy(bytes, u.bytes, 4);
byte c = SPDR;
command = c;
switch (command)
{
// null command zeroes register
case 0:
SPDR = 0;
break;
// case a - d reserved for voltage
case 'a':
SPDR = bytes[3];
break;
// incoming byte, return byte result
case 'b':
SPDR = bytes[2];
break;
// incoming byte, return byte result
case 'c':
SPDR = bytes[1];
break;
// incoming byte, return byte result
case 'd':
SPDR = bytes[0];
break;
/** // case e -h reserved for current
case 'e':
SPDR = amps.b[0];
break;
// incoming byte, return byte result
case 'f':
SPDR = amps.b[1];
break;
// incoming byte, return byte result
case 'g':
SPDR = amps.b[2];
break;
// incoming byte, return byte result
case 'h':
SPDR = amps.b[3];
break;
// case i - l reserved for wattage
case 'i':
SPDR = watts.b[0];
break;
// incoming byte, return byte result
case 'j':
SPDR = watts.b[1];
break;
// incoming byte, return byte result
case 'k':
SPDR = watts.b[2];
break;
// incoming byte, return byte result
case 'l':
SPDR = watts.b[3];
break;**/
} // end of switch
} // end of interrupt service routine (ISR) SPI_STC_vect
/***************************************************************
Loop until slave is enabled by Pi.
****************************************************************/
void loop (void)
{
/*************************************************************
Read and Calculate
****************************************************************/
/**
sensorVoltage = analogRead(A2);
sensorCurrent = analogRead(A3);
Voltage = sensorVoltage*(5.0/1023.0);
Current = sensorCurrent*(5.0/1023.0);
RealCurrent = Current/0.204545;
RealVoltage = (Voltage/0.022005);
Power = RealVoltage*RealCurrent;
**/
RealVoltage = 1.234;
/*************************************************************
Loop Check for SS activation
****************************************************************/
// if SPI not active, clear current command, else preproc floats and pass to SPI
if (digitalRead (SS) == HIGH){
command = 0;
}
/*************************************************************
Debug with serial monitor
****************************************************************/
/*
Serial.print("Byte 3: ");
Serial.println(bytes[3],BIN);
delay(500);
Serial.print("Byte 2: ");
Serial.println(bytes[2],BIN);
delay(500);
Serial.print("Byte 1: ");
Serial.println(bytes[1],BIN);
delay(500);
Serial.print("Byte 0: ");
Serial.println(bytes[0],BIN);
delay(1000);
Serial.println();*/
}
Master
#include <bcm2835.h>
#include <stdio.h>
void setup()
{
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST); // The default
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default
}
char getByte(const char command){
char read_data = bcm2835_spi_transfer(command);
delay(100);
return read_data;
}
int main(int argc, char **argv)
{
//If you call this, it will not actually access the GPIO
//Use for testing
//bcm2835_set_debug(1);
if (!bcm2835_init())
return 1;
setup();
//Start communication
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);// Enable 0
//voltage 1-4
char read_data = getByte('a');
printf("byte is %02d\n", read_data);
read_data = getByte('b');
printf("byte is %02d\n", read_data);
read_data = getByte('c');
printf("byte is %02d\n", read_data);
read_data = getByte('d');
printf("byte is %02d\n", read_data);
/** voltage = volts.f;
printf("%.6f", voltage);
printf("\n");
**/
delay(1000);
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, HIGH);
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);// Disable 0
bcm2835_spi_end();
bcm2835_close();
return 0;
}
I'm using a fixed value to debug the code. Sometimes the output from SPI on the Pi is accurate, but otherwise it changes and outputs partially accurate and/or random bytes.
The issue I have been unable to work out is stability on the side of the Pi, so I am looking for help evaluating whether my code is causing inaccuracies or if it's my hardware.
At a guess I would say that this is a timing issue between the sender and receiver. Try looking at the bits of the data that you are receiving and see if they are shifted forward or backward. This will possibly indicate that the pi is waiting too long to begin receiving, or not waiting long enough for all of the data. I think the issues are probably around the delays:
delay(500); //for errors
on the Arduino, and
delay(1000);
on the receiver. Why are you using these? 500ms is a long time to keep the pi waiting for a response to the SPI data.
Just a note, there is also now an SPI kernel driver (spidev) for the pi - this is a much more industry standard approach, and is potentially a more robust method.
There's a good example of this on the raspberry pi github site: https://github.com/raspberrypi/linux/blob/rpi-4.4.y/Documentation/spi/spidev_test.c

Arduino radio frequency receiver does not work with motor shield

Micro-controller : SainSmart Mega 2560
Motor Shield: Osepp Motor Shield V1.0
I am trying to implement Radio Frequency communication on my wheeled robot however when the motors are running the radio frequency code will not receive messages.
The motor shield uses pins 4,7,8,12
I have setup the radio frequency to occur on pins 22,23 ,5
I see this reference to
Why does VirtualWire conflicts with PWM signal in Arduino/ATmega328 pin D10?
but am not sure if this applies to my situation.
How do I get Radio Frequency receiver/transmitter to work while motor shield in use?
code demonstrating the situation:
#include <RH_ASK.h>
#include <SPI.h> // Not actually used but needed to compile
RH_ASK driver(2000, 22, 23, 5,true); // ESP8266: do not use pin 11
/// *************************
// MOTOR SETUP
/// *************************
// Arduino pins for the shift register
#define MOTORLATCH 12
#define MOTORCLK 4
#define MOTORENABLE 7
#define MOTORDATA 8
// 8-bit bus after the 74HC595 shift register
// (not Arduino pins)
// These are used to set the direction of the bridge driver.
#define MOTOR1_A 2
#define MOTOR1_B 3
#define MOTOR2_A 1
#define MOTOR2_B 4
#define MOTOR3_A 5
#define MOTOR3_B 7
#define MOTOR4_A 0
#define MOTOR4_B 6
// Arduino pins for the PWM signals.
#define MOTOR1_PWM 11
#define MOTOR2_PWM 3
#define MOTOR3_PWM 6
#define MOTOR4_PWM 5
#define SERVO1_PWM 10
#define SERVO2_PWM 9
// Codes for the motor function.
#define FORWARD 1
#define BACKWARD 2
#define BRAKE 3
#define RELEASE 4
void setup()
{
Serial.begin(9600); // Debugging only
if (!driver.init())
Serial.println("init failed");
//comment out code below to allow receiver to read radio frequency communication
//BEGIN
motor(1, FORWARD, 255);
motor(2, FORWARD, 255);
motor(4, FORWARD, 255);
motor(3, FORWARD, 255);
//END
}
void loop()
{
uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
uint8_t buflen = sizeof(buf);
if (driver.recv(buf, &buflen)) // Non-blocking
{
int i=0;
// Message with a good checksum received, dump it.
driver.printBuffer("Got:", buf, buflen);
buf[5]= '\0';
Serial.println((char*)buf);
}
}
void motor(int nMotor, int command, int speed)
{
int motorA, motorB;
if (nMotor >= 1 && nMotor <= 4)
{
switch (nMotor)
{
case 1:
motorA = MOTOR1_A;
motorB = MOTOR1_B;
break;
case 2:
motorA = MOTOR2_A;
motorB = MOTOR2_B;
break;
case 3:
motorA = MOTOR3_A;
motorB = MOTOR3_B;
break;
case 4:
motorA = MOTOR4_A;
motorB = MOTOR4_B;
break;
default:
break;
}
switch (command)
{
case FORWARD:
motor_output (motorA, HIGH, speed);
motor_output (motorB, LOW, -1); // -1: no PWM set
break;
case BACKWARD:
motor_output (motorA, LOW, speed);
motor_output (motorB, HIGH, -1); // -1: no PWM set
break;;
case RELEASE:
motor_output (motorA, LOW, 0); // 0: output floating.
motor_output (motorB, LOW, -1); // -1: no PWM set
break;
default:
break;
}
}
}
void motor_output (int output, int high_low, int speed)
{
int motorPWM;
switch (output)
{
case MOTOR1_A:
case MOTOR1_B:
motorPWM = MOTOR1_PWM;
break;
case MOTOR2_A:
case MOTOR2_B:
motorPWM = MOTOR2_PWM;
break;
case MOTOR3_A:
case MOTOR3_B:
motorPWM = MOTOR3_PWM;
break;
case MOTOR4_A:
case MOTOR4_B:
motorPWM = MOTOR4_PWM;
break;
default:
speed = -3333;
break;
}
if (speed != -3333)
{
shiftWrite(output, high_low);
if (speed >= 0 && speed <= 255)
{
analogWrite(motorPWM, speed);
}
}
}
void shiftWrite(int output, int high_low)
{
static int latch_copy;
static int shift_register_initialized = false;
if (!shift_register_initialized)
{
// Set pins for shift register to output
pinMode(MOTORLATCH, OUTPUT);
pinMode(MOTORENABLE, OUTPUT);
pinMode(MOTORDATA, OUTPUT);
pinMode(MOTORCLK, OUTPUT);
// Set pins for shift register to default value (low);
digitalWrite(MOTORDATA, LOW);
digitalWrite(MOTORLATCH, LOW);
digitalWrite(MOTORCLK, LOW);
// Enable the shift register, set Enable pin Low.
digitalWrite(MOTORENABLE, LOW);
// start with all outputs (of the shift register) low
latch_copy = 0;
shift_register_initialized = true;
}
bitWrite(latch_copy, output, high_low);
shiftOut(MOTORDATA, MOTORCLK, MSBFIRST, latch_copy);
delayMicroseconds(5); // For safety, not really needed.
digitalWrite(MOTORLATCH, HIGH);
delayMicroseconds(5); // For safety, not really needed.
digitalWrite(MOTORLATCH, LOW);
}
It looks like the reference you give could be the problem. To try that fix just find the RH_ASK.cpp file and uncomment line 16 like this
// RH_ASK on Arduino uses Timer 1 to generate interrupts 8 times per bit interval
// Define RH_ASK_ARDUINO_USE_TIMER2 if you want to use Timer 2 instead of Timer 1 on Arduino
// You may need this to work around other libraries that insist on using timer 1
#define RH_ASK_ARDUINO_USE_TIMER2
Your motor is using pin 5:
#define MOTOR4_PWM 5
Try using a different third pin for your radio (make sure to match your software and hardware to the same new pin). Glancing at the specsheet, one option would be pin 24. Your motor code reserves every pin from 0 through 12. So, try...
RH_ASK driver(2000, 22, 23, 24,true); // ESP8266: do not use pin 11
Or change your motor pins using similar logic.

Executing multiple functions/commands on Arduino

I am using an RFduino and an iOS application to control some RGB LEDs.
This is how I'm sending a string command to the module:
- (IBAction)fadeButtonPressed:(id)sender {
[rfduino send:[#"fade" dataUsingEncoding:NSUTF8StringEncoding]];
}
These command(s) are coming back just fine on the RFduino side:
void RFduinoBLE_onReceive(char *data, int len) {
if (strncmp(data, "fade", 4) == 0) {
// begin fading chosen LED colour
}
}
Is there a better way of executing multiple functions on Arduino? It seems to me that there should be a better way of doing what I'm trying to do.
Originally for example I was getting an issue where the "fade" string was coming back as "fadek" so I used strncmp(data, "fade", 4) instead of strcmp(data, "fade") and this fixed the issue.
I guess I'd like a way of cleaning up my code and perhaps make it easier to introduce new bits of functionality depending on which strings are coming back.
The functions I would like to be able to do would be controlling of the RGB colours and then Fading or Blinking that particular chosen colour.
What if I wanted to introduce faster blinking? Rather than setting another command integer and adding another condition is there a cleaner approach?
The selection of the colours is set by selection of a color wheel within my iOS application. This is working fine. The problem is that the Blinking and Fading does not blink/fade the selected colour (command 0).
Here is my entire sketch so far:
#include <RFduinoBLE.h>
// Pin 2 on the RGB LED.
int rgb2_pin = 2; // red
int rgb3_pin = 3; // green
int rgb4_pin = 4; // blue
int brightness = 0;
int fadeAmount = 5;
// Command properties.
int command = 0;
void setup() {
// debug output at 9600 baud
Serial.begin(9600);
// Setup the LEDs for output.
pinMode(rgb2_pin, OUTPUT);
pinMode(rgb3_pin, OUTPUT);
pinMode(rgb4_pin, OUTPUT);
// This is the data we want to appear in the advertisement
// (the deviceName length plus the advertisement length must be <= 18 bytes.
RFduinoBLE.advertisementData = "rgb";
// Start the BLE stack.
RFduinoBLE.begin();
}
void loop() {
if (command == 1) { // Fade in/out chosen colour.
analogWrite(rgb2_pin, brightness);
analogWrite(rgb3_pin, brightness);
analogWrite(rgb4_pin, brightness);
// Change the brightness for next time through the loop:
brightness = brightness + fadeAmount;
// Reverse the direction of the fading at the ends of the fade:
if (brightness == 0 || brightness == 255) {
fadeAmount = -fadeAmount ;
}
// Wait for 30 milliseconds to see the dimming effect
delay(30);
} else if (command == 2) { // Blink
digitalWrite(rgb2_pin, HIGH);
digitalWrite(rgb3_pin, HIGH);
digitalWrite(rgb4_pin, HIGH);
delay(200);
digitalWrite(rgb2_pin, LOW);
digitalWrite(rgb3_pin, LOW);
digitalWrite(rgb4_pin, LOW);
delay(200);
}
}
void RFduinoBLE_onConnect() {}
void RFduinoBLE_onDisconnect() {}
void RFduinoBLE_onReceive(char *data, int len) {
Serial.println(data);
// Each transmission should contain an RGB triple.
if (strncmp(data, "fade", 4) == 0) {
command = 1;
} else if (strncmp(data, "blink", 5) == 0) {
command = 2;
} else { // Change colour.
// Reset other functions.
command = 0;
if (len >= 3) {
// Get the RGB values.
uint8_t red = data[0];
uint8_t green = data[1];
uint8_t blue = data[2];
// Set PWM for each LED.
analogWrite(rgb2_pin, red);
analogWrite(rgb3_pin, green);
analogWrite(rgb4_pin, blue);
}
}
Serial.println(command);
}
My approach to these sort of communications is to define a protocol that includes start and stop characters (say 0x01 and 0x03) and then build a state machine that processes each incoming byte.
The reason for this is it helps correct for out-of-sequence bytes and communication errors. You can ignore data until you get a 0x01 and the command doesn't end until you get a 0x03. If you get a 0x03 before you expect it then you can discard the invalid packet.
One issue you have with your current approach and this technique is that you are sending 8 bit data for the RGB command - this can conflict with your start/end bytes. It won't have much impact to encode your data as 2 digit hex, so you can have a protocol which looks something like
0x01 - Start of packet
1 byte command b=Blink, f=Fade, c=set color
6 bytes arguments. For command c this would be three pairs of hex characters for rgb. For b & f it could be 2 characters of blink/fade rate with the other 4 bytes being 0000 for placeholder
0x03 - End of packet
Then you can build a state machine -
Waiting for 0x01. Once you get it move to state 2
Waiting for a valid command byte. If you get a valid one move to state 3.
If you get 0x01 move back to state 2. If you get any other byte
move to state 1
Waiting for 6 hex digits. If you get 0x01
stay in state 2. If you get anything other than 0-9 a-f move
back to state 1
Waiting for 0x03. If you get it then process
complete command and return to state 1. If you get 0x01 move back
to state 2. If you get anything else move to state 1
This won't compile as I don't have an Arduino in front of me, but you would use something like this
int state; // Initialise this to 1
char command;
string hexstring;
void RFduinoBLE_onReceive(char *data, int len) {
for (int i=0;i<len;i++) {
stateMachine(data[i]);
}
}
stateMachine(char data) {
switch (state) {
case 1:
if (data == 1) {
state=2;
}
break;
case 2:
if (data=='b' || data== 'f' || data == 'c') { // If we received a valid command
command=data; // store it
hexstring=""; // prepare to receive a hex string
state=3;
} else if (data != 1) { //Stay in state 2 if we received another 0x01
state =1;
}
break;
case 3:
if ((data >='a' && data <='z') || (data >='0' && data <='9')) {
hexstring=hexstring+data; // if we received a valid hex byte, add it to the end of the string
if (length(hexstring) == 6) { // If we have received 6 characters (24 bits) move to state 4
state=4;
}
} else if (data == 1) { // If we received another 0x01 back to state 2
state =2;
} else {
state=1; // Anything else is invalid - back to look for 0x01
}
break;
case 4:
if (data == 3) // 0x03=valid terminator
{
processCommand(command,hexstring); // We have a valid command message - process it
state=1;
} else if (data==1) { // 0x01= start of new message, back to state 2
state=2;
} else {
state=1; // anything else, back to look for 0x01
}
break;
}
}