Handling multiple events on single serial port in Arduino - arduino

I am working on an Arduino project where I am using only an integrated module for GPS,GSM and Bluetooth.
I am getting the response of all these devices on single serial port where bluetooth and SMS event can occur at any time but when they occur it disturbs the current data on serial port data and does not detect any new bluetooth event or any new sms event.
void loop(){
while((digitalRead(6)==LOW)&&(q==0));
{
delay(1000);
Serial.println("Reading SMS");
while ( Serial3.available() > 0) Serial3.read(); // Clean the input buffer
memset(response,'\0',250);
memset(sms,'\0',11);
memset(number,'\0',14);
readsms();
q++;
}
if((digitalRead(6)==HIGH))
{
q=0;
perform the normal operation
}
while(digitalRead(7)==HIGH);
if(digitalRead(7)==HIGH)
{
blepair(); //function to pair a new device with bluetooth
delay(1000);
}
if(digitalRead(7)==LOW)
{
blestatus(); //monitoring any new event via bluetooth
delay(1000);
}
All the data I am getting on same serial port.
What are the options available for overcome this problem.
I would really appreciate your early responses.

Related

esp32 BLE client application - connect to device name

I've hacked apart the ESP32 BLE arduino sketches to do what I want. The server side is easy. Please see code below:
if (con == 0){
digitalWrite(LED, LOW);
}
if (con == 1){
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
con = 1;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
con = 0;
}
This works exactly how I want. It simply sits idle doing nothing, when a device connects to the BLE server then it will flash an LED.
No problems there, even though I suspect my code isn't 'that pretty.
What i'm having trouble doing however is creating an ESP32 client to connect to the BLE device.
The client has the name set as
BLEDevice::init("BOX_A1");
The example code seems to want UID for both the service and characteristic. Is there any way to just connect to the short advertised name? No data is being shared, it's just simply acting as a beacon to identify a box when connected to.
Thanks
Andrew
You can't connect to a device using the advertised name, not directly at least.
If you want to use the advertised name you have to scan for all BLE devices around you and select the one matching your name. The BLE scan example shows you how this is done. The code scans for a scanTime of 5 seconds, waits 2 seconds and starts scanning again:
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
The example just prints the amount of found devices, you want to search through them and look for the correct name. The BLEScan returns an object of type BLEScanResults. You can access the found devices using getDevice with an index. Something like this might work to print the names of all found devices:
BLEAdvertisedDevice device;
for (int i = 0; i < foundDevices.getCount(); ++i) {
device = foundDevices.getDevice(i);
Serial.println(device.getName().c_str());
}
Now you can compare the names and work with the correct device.
To my understanding,
You want the client to connect to the server with given advertised name.
After connection is success, server turns on led.
You do have notification service running on server, but your client isn't interested.
Below is the client code which only connects to server with name "BOX_A1".
First Set our callback function that checks if device name matches when scanner discovers the devices:
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice)
{
if (advertisedDevice.getName() == "BOX_A1")
{
advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
foundDevice = new BLEAdvertisedDevice(advertisedDevice);
deviceFound = true;
Serial.print("Device found: ");
Serial.println(advertisedDevice.toString().c_str());
}
}
};
Use the BLEAdvertisedDevice object i.e. foundDevice object to connect to this server.
BLEClient* connectToServer(BLEAdvertisedDevice* device) {
BLEClient* pClient = BLEDevice::createClient();
if (pClient->connect(device)){ // Connect to the remote BLE Server.
Serial.println(" - Connected to server");
return pClient;
}
else{
Serial.println("Failed to Connect to device");
return NULL;
}
}
Use following line to call this connect function, it return the client object which can be used to disconnect from server.
if(deviceFound==true){
BLEClient* myDevice = connectToServer(device);
if(myDevice!=NULL){
Serial.print("Connected to the BLE Server: ");
Serial.println(device->getName().c_str());//print name of server
//DO SOME STUFF
//disconnect the device
myDevice->disconnect();
Serial.println("Device disconnected.");
}
}
This was the client side.
At server side to set the connection status flag use the following:
//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
Serial.println("Client Connected");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
Serial.println("Client Disconnected");
deviceConnected = false;
}
};

Can I sniff a BLE communication between my multimeter and my smartphone?

I am currently trying to manipulate a multimeter (Zoyi ZT-5BQ) using an Arduino board, but I don't know the protocol that uses my multimeter to change the reading mode (in example, from ohmeter to termometer).
I have tried to pair my AT-09 module to the manufacturer's app (Bluetooth DMM) and see what does it sends when I try to change the reading mode from the smartphone, but AT-09 is not detected by the phone, and I guess it's due to the MAC address of my module.
Is there any way I can sniff between the communication of my smarthphone and multimeter?
Thanks in advance!
The manufacturers' app can't find the AT-09 most likely because the app searches for devices that advertise a specific service that the module does not offer.
Start your research by installing a generic BLE scanner app such as nRF Connect. Connect to your multimeter and look at the services and characteristics it discovers. Try reading and/or writing from/to them, sometimes this is enough to figure out the easier protocolls.
nRF Connect also offers to debug the connection if nRF Connetc is open in the background and you connect to your device using the manufacturers' app. This can already give some insights to the messages sent and received.
The last resort would be to use a real BLE sniffer. There are multiple options, I personally have great experience using the one from Nordic Semiconductor. You would need a bit of hardware and can use Wireshark with an extension to see everything. The cheapest option for the hardware would be the nRF52840-Dongle.
Finally, I found a solution thanks to Michael Kotzjan.
I searched about sniffing and native ways to do it from my phone, and I found the "enable Bluetooth HCI Snoop solution". It was not trivial, at least not for a MIUI based smartphone because I had to enable that option in developer's options and find a way to see those logs, in my case I had to reboot my Redmi Note 8 Pro and then I went to "Xiaomi Services and Feedback" app, where I enabled the logs of the phone's bluetooth, then I started a communication between manufacturer's app (Bluetooth DMM) and my multimeter (Zoyi ZT-5BQ), after that I had to find the folder where logs are stored, in my case: debuglogger->connyslog->bthci->CsLog_2022...
I downloaded the folder to my PC and finally using Wireshark I was able to see the commands that I sent from the destination (Redmi Note...) to my source (Shenzen__88...).
Commands shown in Wireshark
This way I was able to note the commands down, and I made this list:
List of commands
Using NRF Connect app, I was able to test the commands, updating the 0xFFF4 characteristic of the multimeter through BLE technology.
Finally, I made this code in Arduino to connect my ESP32 to the multimeter and test the commands, and I can read and write the 0xFFF4 characteristic under 0xFFF0 service.
/**
A BLE client example that is rich in capabilities.
There is a lot new capabilities implemented.
author unknown
updated by chegewara
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
//String serverUUID = "FC:58:FA:88:56:59";
// The remote service we wish to connect to.
static BLEUUID serviceUUID("fff0");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("fff4");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
//BLEAddress address(serverUUID.c_str());
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
Serial.println((char*)pData);
}
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if (pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
}
if (pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
/**
Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
} // End of setup.
// This is the Arduino main loop function.
void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothing more we will do.");
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
String newValue = "Time since boot: " + String(millis() / 1000);
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
// Set the characteristic's value to be the array of bytes that is actually a string.
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
byte Command[2][10] = {
{0xea, 0xec, 0x70, 0xe3, 0xa2, 0xc1, 0x32, 0x71, 0x64, 0x9b}, // Celsius
{0xea, 0xec, 0x70, 0xe2, 0xa2, 0xc1, 0x32, 0x71, 0x64, 0x98} // Fahr
};
Serial.println("Celsius");
pRemoteCharacteristic->writeValue(Command[0], sizeof(colors[0]));
delay(2000);
Serial.println("Fahr");
pRemoteCharacteristic->writeValue(Command[1], sizeof(colors[1]));
delay(2000);
} else if (doScan) {
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop

Trouble accessing SIM card with MRK GSM 1400

I received yesterday my brand new Arduino MKR GSM 1400 and started playing around with today.
However, I'm having trouble stablishing the connection with the SIM card. I'm using the "ReceiveSMS" example from the MKRGSM library (code below), but the execution is getting stuck at the gsmAccess.begin(PINNUMBER) command.
Using AT commands and the debug mode, I got the following description for the error message:
AT+CPIN?
+CME ERROR: SIM not inserted
I have tried connecting with 3 different SIM cards, all tested beforehand with a smartphone and confirmed to be functional. I have tried removing the PIN, but still got the same error.
Does anybody have any idea on what could be going wrong?
Thanks in advance
Code:
// include the GSM library
#include <MKRGSM.h>
#include "arduino_secrets.h"
// Please enter your sensitive data in the Secret tab or arduino_secrets.h
// PIN Number
const char PINNUMBER[] = SECRET_PINNUMBER;
// initialize the library instances
GSM gsmAccess;
GSM_SMS sms;
// Array to hold the number a SMS is retreived from
char senderNumber[20];
void setup() {
// initialize serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("SMS Messages Receiver");
// connection state
bool connected = false;
// Start GSM connection
while (!connected) {
if (gsmAccess.begin(PINNUMBER) == GSM_READY) {
connected = true;
} else {
Serial.println("Not connected");
delay(1000);
}
}
Serial.println("GSM initialized");
Serial.println("Waiting for messages");
}

How do I repeatedly send an AT command to a Bluetooth module arduino

I've been working on a project and want an Arduino uno with an HM-19 Bluetooth module to repeatedly send an AT command to the module then print the modules result in the serial monitor. I'm having trouble and it is only returning the number 53 and not the response 'OK'. Thanks for any insight you're able to give.
//import Software Serial to communicate over comms port
#include <SoftwareSerial.h>
// 0 and 1 are the tx and rx pins ans should be where the hm-19 is
// may be 0,1 instead
SoftwareSerial HM19(1,0);
void setup() {
// begin serial monitor and wait for something(?, found online)
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
//tell user it's started
Serial.println("Started");
//I believe this begins the monitor for the HM-19
HM19.begin(9600);
delay(1000);
}
void loop() {
// give user status report
delay(1000);
Serial.println("Sending an AT command...");
// need to find if this is how you send an AT command (currenty send 'AT' as a test)
HM19.write("AT\r");
delay(30);
//wait for the HM19 to respond then print it out
int HMresponse = 0;
if (HM19.available()){}
HMresponse = HM19.read();
Serial.println(HMresponse);
}

How to make Arduino run a script

i'm a software developer but i'm new to Arduino, and to the electronics world.
I would like to build a simple project that combined both, and really appreciate any help where to start.
final project should be Arduino with a button and a LED, when tapping the button I want run a script on my mac, then if the script finished successfully i want to turn on the LED
I already saw some tutorial about how to use buttons and LEDs so
the main thing i'm interested here is how communicate from the Arduino to the mac and vice versa. and especially how to make it run a script on my mac.
You should look into the Serial class and the examples (via File > Examples > Commmunication)
You will need to write a bit of code on the Arduino side to send data via Serial when the button is pressed (so you can trigger your script) and receive data (when the script is done) to control the LED.
Here is a rough example on the Arduino side:
const int btnPin = 12;//button pin
const int ledPin = 13;
int lastButtonState;
void setup(){
//setup pins
pinMode(btnPin,INPUT_PULLUP);
pinMode(ledPin,OUTPUT);
//setup communication
Serial.begin(9600);
}
void loop() {
int currentButtonState = digitalRead(btnPin);//read button state
if(lastButtonState != currentButtonState && currentButtonState == LOW){//if the state of the pin changed
Serial.write(currentButtonState);//send the data
lastButtonState = currentButtonState;//update the last button state
//turn on LED
digitalWrite(ledPin,HIGH);
}
}
void serialEvent(){//if any data was sent
if(Serial.available() > 0){//and there's at least 1 byte to look at
int data = Serial.read();//read the data
//do something with it if you want
//turn off the LED
digitalWrite(ledPin,LOW);
}
}
Be aware you may have different pin numbers in your setup and depending on how the button wired, you will either look for a LOW or HIGH value in the in the condition checking the currentButtonState.
In terms of the communication there are a few key elements:
Serial.begin(9600);
Starts Serial communication with baud rate 9600. You will need to match this baud rate on the other end to ensure correct communication.
Serial.write();
Will send data to the port.
serialEvent()
is predefined in Arduino and gets called when new Serial data arrives.
Note that this gets called automatically on a Arduino Uno (and other simpler boards), however this doesn't get called on other boards:
serialEvent() doesn’t work on the Leonardo, Micro, or Yún.
serialEvent() and serialEvent1() don’t work on the Arduino SAMD Boards
serialEvent(), serialEvent1()``serialEvent2(), and serialEvent3() don’t work on the Arduino Due.
On the script side, you haven't mentioned the language, but the principle is the same: you need to know the port name and baud rate to establish communication from your mac.
(You can manually call serialEvent() in loop() as a workaround. For more details see the Arduino Reference)
The port is what you used to upload the Arduino code (something like /dev/tty.usbmodem####) and in this particular case the baud rate is 9600. You should be able to test using Serial Monitor in the Arduino IDE.
Your script will be something along there lines (pseudo code)
open serial connection ( port name, baudrate = 9600 )
poll serial connection
if there is data
run the script you need
script finished executing, therefore send data back via serial connection
Be sure to checkout the Interfacing with Software to find guide on the scripting language of your choice
Update
Here's a quick example using Processing to interface with the arduino using it's Serial library. So on the Arduino side, here's a minimal sketch:
const int btnPin = 12;//button pin
const int ledPin = 13;
boolean wasPressed;
char cmd[] = "/Applications/TextEdit.app\n";
void setup(){
//setup pins
pinMode(btnPin,INPUT_PULLUP);
pinMode(ledPin,OUTPUT);
//setup communication
Serial.begin(9600);
}
void loop() {
int currentButtonState = digitalRead(btnPin);//read button state
if(!wasPressed && currentButtonState == LOW){//if the state of the pin changed
Serial.write(cmd);//send the data
wasPressed = true;//update the last button state
//turn on LED
digitalWrite(ledPin,HIGH);
}
if(currentButtonState == HIGH) wasPressed = false;
}
void serialEvent(){//if any data was sent
if(Serial.available() > 0){//and there's at least 1 byte to look at
int data = Serial.read();//read the data
//do something with it if you want
//turn off the LED
digitalWrite(ledPin,LOW);
}
}
and on the Processing side:
import processing.serial.*;
void setup(){
try{
Serial arduino = new Serial(this,"/dev/tty.usbmodemfa141",9600);
arduino.bufferUntil('\n');//buffer until a new line is encountered
}catch(Exception e){
System.err.println("Error opening serial connection! (check cables and port/baud settings!");
e.printStackTrace();
}
}
void draw(){}
void serialEvent(Serial s){
String[] command = s.readString().trim().split(",");//trim spaces, split to String[] for args
println(command);//see what we got
open(command);//run the command
s.write("A");//send a message back to flag that the command is finished (turn LED off)
}
Hope this is a helpful proof of concept. Feel free to use any other language with a serial library available instead of Processing. The syntax may differ just a tad (probably more on the running of the process/command), but the concept is the same.
Update The example above can be simplified a touch by not having the command on the Arduino side:
there's less data sent on Serial from Arduino to Processing reducing communication time and the odds of communication interference
the app runs on a computer hence changing the command is simpler to do on software side as opposed to having to change the Arduino code and re-upload each time.
Arduino:
const int btnPin = 12;//button pin
const int ledPin = 13;
boolean wasPressed;
void setup(){
//setup pins
pinMode(btnPin,INPUT_PULLUP);
pinMode(ledPin,OUTPUT);
//setup communication
Serial.begin(9600);
}
void loop() {
int currentButtonState = digitalRead(btnPin);//read button state
if(!wasPressed && currentButtonState == LOW){//if the state of the pin changed
Serial.write('R');//send the data
wasPressed = true;//update the last button state
//turn on LED
digitalWrite(ledPin,HIGH);
}
if(currentButtonState == HIGH) wasPressed = false;
}
void serialEvent(){//if any data was sent
if(Serial.available() > 0){//and there's at least 1 byte to look at
int data = Serial.read();//read the data
//do something with it if you want
//turn off the LED
digitalWrite(ledPin,LOW);
}
}
Processing:
import processing.serial.*;
String[] command = {"/Applications/TextEdit.app", "myText.txt"};
void setup() {
try {
Serial arduino = new Serial(this, "/dev/tty.usbmodemfa141", 9600);
}
catch(Exception e) {
System.err.println("Error opening serial connection! (check cables and port/baud settings!");
e.printStackTrace();
}
}
void draw() {
}
void serialEvent(Serial s) {
if (s.read() == 'R') {
launch(command);//run the command
s.write("A");//send a message back to flag that the command is finished (turn LED off)
}
}
(btw, 'R' is an arbitrary single character, can be something else as long it's the same char on both Serial send and receive sides)
Also, bare in mind launch() returns Proccess which can be useful to get more information from the software launched.
Dunno if it will help you as much as it did me but this question shows a simple example of how to use the script with a simple button in an android app
Run script with android app
Hope it helps
I found a workaround in Linux. It's a little messy but it works. I use Coolterm to capture the serial output from the arduino to a text file and I wrote a small python script that reads the file (or simply, in my case, executes the command I want if the file is not empty).

Resources