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;
}
}
Related
I am trying, as a test to Serial.write the int value: 5, to serial monitor, and if received, i want to print the the text "SUCCESS!" to the serial monitor.
But when writing Serial.write((int)5); All i get in the serial monitor is:
I have tried using Serial.println(5); which works fine, but then i am not able to read it.
My code:
enum read_states {
Modtag_Adresse,
Modtag_Bit_Position_I_Adresse,
Modtag_Bit_Position_Vaerdi
};
enum read_states state;
void setup() {
state = Modtag_Adresse;
Serial.begin(9600);
}
void loop() {
if(state == Modtag_Adresse) {
Serial.write((int)5);
delay(1000);
if(Serial.available() > 0) {
int serialReceived = Serial.read();
if(serialReceived >= 0) {
// Receive value 5
Serial.print("SUCCESS!!");
}
}
}
else if(state == Modtag_Bit_Position_I_Adresse) {
//
}
else if(state == Modtag_Bit_Position_Vaerdi) {
//
}
else {
// Failure.
}
}
Serial.write(5) sends the byte 5 to the computer. It appears as a square, because it's not an ASCII code of a letter or number or symbol.
Serial.print(5) sends the ASCII code for 5 (which is 53).
The reason you can't read what you wrote is because Serial.write sends data to the computer and Serial.read returns data received from the computer. If it read data from the Arduino program, it would be pointless because you don't need to use serial for that.
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?
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.
New to coding! I am trying to get text on my LED Matrix through serial data that is being sent in from processing.
My code works but the only problem is that though the serial data on processing is constant, the arduino code only reads the number once. This makes it so that the text will not scroll all the way through. How do I loop a serial read on arduino?
Here is the relevant portion of my code:
void loop()
{
if (Serial.available() > 0)
{
int matrixPinState = Serial.read();
// stage = Serial.read();
// analogWrite(matrixPin, stage);
if (matrixPinState == 1)
{
matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("Im kind"));
if (--x < -30)
{
x = matrix.width();
if (++pass >= 8) pass = 0;
matrix.setTextColor(colors[pass]);
}
matrix.show();
delay(30);
}
}
}
When a byte(or you call it character, a data that is 8 bit long) is fetched by uart block(the hardware), it is buffered to input buffer so that programmer can read and process it.
In your case, when you send a character, it is fetched and put to the buffer and when you read it there is no more byte available to read unless you send to new one.
In short, read the pin state once. You can do something like:
int matrixPinState = 0
void setup() {
// do all your setup settings first
while (Serial.available() < 0) {
// wait here for the input
delay(30);
}
// got your input, read it
matrixPinState = Serial.read();
}
void loop()
{
if (matrixPinState == 1)
{
matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("Im kind"));
if (--x < -30)
{
x = matrix.width();
if (++pass >= 8) pass = 0;
matrix.setTextColor(colors[pass]);
}
matrix.show();
delay(30);
}
}
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read(); // it reads from python voice recognition
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
It works with voice_command.py (which I wrote) on linux terminal. When code is like this, right after uploading this code to arduino, it works well until voice recognition understand a different word from "right" or "left". When voice command send to arduino another string different from "right" or "left", program still works without any error but after this point it starts not to respond "right" or "left" command anymore. To solve this I did this change. I put an 'else':
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read();
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
else { // I write this to make it work..
kontrol = '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
However, now it is not responding "right" and "left" command too. How can I solve this problem?
The Problem
The problem you are having is that your Serial.available() block is only reading one byte from the Serial buffer in each iteration of the loop. As a consequence, when your servo sends the word "right", the Serial buffer is "right". The first iteration through loop() gives "r" as the value for kontrolstr.
Without the else block, on the second loop, kontrolstr is set to ri, then rig, then righ, etc, and is only reset when left or right are found. This is also what causes the problem of left and right not being reached if another word has been recognized - kontrolstr would have been set to , e.g. "horse", this is not recognized, so then when it sends "right", you get "horseright", etc.
With the else block, on the first loop, kontrolstr is "r", so it hits the else block, and resets the string. On the second loop, kontrolstr is "i", it hits the else block and resets the string, etc, never reaching the relevant control block.
Possible solutions
The start of the solution is to read the entire Serial buffer before processing it, so replace the block starting with if(Serial.available() to:
while(Serial.available())
{
kontrol = Serial.read();
kontrolstr.concat(kontrol);
}
This will read the entire buffer in the first loop, so as long as all the data has been sent between iterations of the loop, your problem will be solved. However, it takes a non-zero amount of time to send data over a Serial port, so it's possible that your loop() iteration triggers in the middle of a send, in which case the Serial buffer might be something like "rig", which won't match "right" or "left", will be reset, then in the next loop you'll get "ht", and again it will be reset - the trigger will be missed.
If possible, I think the best solution would be to have your servo send the control words with a delimiter between them, e.g. \n. If your servo sends "right\nanother word\nleft\n", then you can wait for entire words to come in before processing them. You can do this by changing your loop() to:
void loop()
{
kontrolstr = ""; // Reset on each iteration of the loop
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
if (control == '\n') {
break;
}
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
myservo.write(pos);
delay(100);
}
Of course, this assumes that you're OK with allowing extra words to accumulate in the Serial buffer (seems fine since the buffer wasn't filling up even when you were reading only 1 character every 100ms). However, if it does happen that the Serial buffer is overflowing, then you can create a second string bufferstring and always append whatever is in the Serial buffer to that string, then on each iteration of the loop, pull out the oldest command, giving:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
String bufferstring = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
// Read whatever's in the Serial port into the buffer string
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
bufferstring.concat(kontrol);
}
// Split the string by the delimiter
int delimiter_loc = bufferstring.indexOf('\n');
if (delimiter_loc != -1) {
// Get the first delimiter_loc characters (doesn't include the delimiter)
kontrolstr = bufferstring.substring(0, delimiter_loc);
// Remove all the characters up to and including the delimiter_loc
bufferstring.remove(0, delimiter_loc + 1);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
// Reset on each iteration of the loop
kontrolstr = "";
myservo.write(pos);
delay(100);
}