Callback not working with ESP.deepsleep(time_interval_in_microseconds); - arduino

I need help i am doing one project which uses Pubsubclient with ep8266 12-e and wifimanager . i have a callback function which is not receving anything once i use deep sleep with pubsub client but it is working fine with normal delay function.
void callback(char* topic,byte* payload,unsigned int length){
led_2();
char buff[22];
Serial.print("Message arrived in topic: ");
Serial.println(topic);
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
buff[i]=(char)payload[i];
}
if(strcmp(topic, "ios/interval") == 0)
{
String test = String(buff);
Serial.println(test);
Serial.println("ok i am working");
}
}
and in my void loop() function i am publishing data which is working fine and then i am doing deepsleep.But data in callback is not coming while performing the void loop operations and even after deep sleep.

one more thing you have to connect REset to the D0 pin of nodeMCU only then this function will work. D0 is the hardware interrupt pin for NodeMCU, we have to wake the CPU up by pulling this pin to low. other than this you can also use void system_deep_sleep(uint32 time_in_us) function for deep sleep, These functions are ESP specific API for deep sleep

Related

ESP32 FreeRTOS how to avoid hardware conflict using MCP23017

I have an ESP32 using a MCP23017 IC using a rotary encoder working perfect with interrupts and a task. Then I added to a L293D connected to the MCP23017 and works fine.
The problem happens when I interact with the rotary encoder while the stepper motor is working. The ESP32 reboots and I'm pretty sure the reason is that both tasks want to access the MCP23017 hardware at the same time.
For the MCP23017 is using a Sempahore:
SemaphoreHandle_t rotaryISRSemaphore = nullptr;
// MCP23017
Adafruit_MCP23017 mcp;
....
void setup(){
....
rotaryISRSemaphore = xSemaphoreCreateBinary();
mcp.begin(1); // use default address 0
xTaskCreatePinnedToCore(&rotaryReaderTask, "rotary reader", 2048, NULL, 20, NULL, 1);
attachInterrupt(esp32IntPin, intCallBack, FALLING);
xTaskCreate(stepperMotor,"Stepper Motor",2048, NULL, 1, NULL);
}
// The int handler will just signal that the int has happened
// we will do the work from a task.
void IRAM_ATTR intCallBack() {
//Serial.println("intCallback");
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(rotaryISRSemaphore, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR ();
}
}
void rotaryReaderTask(void* pArgs) {
(void)pArgs;
Serial.println("Started rotary reader task.");
while(true) {
if(xSemaphoreTake(rotaryISRSemaphore, portMAX_DELAY) == pdPASS) {
handleInterrupt();
}
void handleInterrupt(){
//Read the entire state when the interrupt occurred
....
// Here some work to do with the MCP23017
}
void loop() {
}
int stepperValues[4][4] = {{HIGH,LOW,HIGH,LOW},{LOW,HIGH,HIGH,LOW},{LOW,HIGH,LOW,HIGH}, {HIGH,LOW,LOW,HIGH}};
void stepperMotor(void * params){
(void) params;
for(;;){
if(!stopStepper){
for ( int i = 0; i < 4; ++i ) {
// loop through columns of current row
mcp.digitalWrite(10, stepperValues[i][0]);
mcp.digitalWrite(11, stepperValues[i][1]);
mcp.digitalWrite(12, stepperValues[i][2]);
mcp.digitalWrite(13, stepperValues[i][3]);
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
Any clue on how to avoid ESP32 reboot issue?
When you have a shared resource that needs to be accessed by multiple tasks, you need to protect the resource using a mutex.
If you have full control over the code, the best option would be to add mutex to the I2C bus, as you may have other devices on the I2C bus that need to be accessed from multiple tasks.
If you cannot modify the I2C code and MCP23017 is the only device on the bus, you can write some wrapper functions/class that lock a mutex before accessing the MCP23017 and unlock it afterwards.
Please note, that you cannot use mutexes in ISR, so your code may need to be further refactored to only access the device from a task context.

Arduino interrupt returning unreliable values

I have a simple app to count water flux using a sensor that is equipped with a reed switch.
So the app should only count the number of times the switch closes.
My first code was:
const int sensorPin = 2;
volatile int counter = 0;
void setup() {
Serial.begin(115200);
pinMode(sensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(sensorPin), sensorISR, FALLING);
}
void loop() {
Serial.print("Counter: ");
Serial.println(counter);
}
void sensorISR() {
counter++;
}
And once a bottle of 20 liters was full the counter would show something like 120.
Then I changed the code as follows:
const int sensorPin = 2;
volatile int counter = 0;
void setup() {
Serial.begin(115200);
pinMode(sensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(sensorPin), sensorISR, FALLING);
}
void loop() { }
void sensorISR() {
counter++;
Serial.print("Counter: ");
Serial.println(counter);
}
And counter went down to 40 (using the same 20 liters bottle).
The count should be 20L but that is not my problem as it results from bouncing of the reed switch (I will address that latter).
As the project will have 3 sensors and 3 ISRoutines I wonder why putting the Serial.print() command into the main loop can result in such strange results.
Thanks
Paulo
Serial print statements rely on interrupts that are disabled during your ISR. So Serial.print statements don't belong in an ISR.
The reason your count went down is that now your ISR takes longer to execute and it covers up some of the bounce. There are innumerable tutorials on how to debounce something with an Arduino. You can surely find one.
The two easiest are to use a capacitor between the pin and ground for a hardware debounce or to just use millis or micros to note the time that an interrupt occurs and ignore any interrupts that occur within some small time of that.

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).

Making arduino only write when signaled (serial)

I'm trying to communicate between a Raspberry Pi and Arduino with USB serial, but I only want the Arduino to write when the RPI sends a signal.
My arduino code is as follows:
int sensorPin = A0;
int sensorValue = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
sensorValue = analogRead(sensorPin);
if (Serial.available() > 0) {
Serial.read();
Serial.println(sensorValue,DEC);
Serial.flush();
}
}
Once i do one call from the RPI of:
serial.write('hey')
The arduino writes repeatedly. I thought Serial.available would return 0 most of the time because the buffer is cleared by the read, but it seems like it never gets cleared. I thought flush() might do it but it doesn't really have any effect.
That's odd.. Serial.read() should remove the bytes from the buffer after reading them.
Note: Keep in mind that Serial.read() only reads one byte at a time, this could be your issue since you're sending 'hey' from the Raspberry Pi it'll take 3 iterations of the loop the completely empty the buffer.
If this is not the issue you could try the serialEvent() function wich is called each time something arrives trough serial.
Your code would be like this:
int sensorPin = A0;
int sensorValue = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
//Any other logic here
}
void serialEvent() {
sensorValue = analogRead(sensorPin);
Serial.read();
Serial.println(sensorValue,DEC);
}
By using the serialEvent() event, your loop looks cleaner. That's always nice.

Asynchronous UART transfer in microcontroller 8051

Hi i am trying to communicate my 89c52 with sim548c module. I am sending AT commands and then making the microcontroller store all replies in an array and go through a search function to see if proper reply was sent so it can move on to next AT command. This requires two way serial transfer. i have to first send serially the AT command, then enable reception and store all replies from the module in an array. I am using this program but i cant get the microcontroller to accept the incoming data and store it in an array. it transfers successfully but doesnt receive. Can you kindly identify what is the problem?
int check=0;
int out=0;
unsigned char info[20]={"00000000000000000000"};
unsigned char *s;
unsigned char a[3],b[3];
void transmit_data(unsigned char str)
{
SBUF=str;
while(TI==0);
TI=0;
}
void send_serial(unsigned char *s)
{
delay(50);
while(*s!=0x0)
{
SBUF=*s;
while(TI==0)
{
}
TI=0;
s++;
}
}
void receive_data() interrupt 4
{
if(RI)
{
info[check++]=SBUF;
RI=0;
}
if(TI)
TI=0;
}
void search(unsigned char b[])
{
int l=0;
for(l;l<18;l++)
{
if(info[l]==b[0] && info[l+1]==b[1] && info[l+2]==b[2])
{
out=1;
break;
}
}
}
void compare(unsigned char *s, unsigned char a[]) //for CIPSEND
{
while(1)
{
out=0;
check=0;
delay(50);
send_serial("AT+CIPSEND\r");
delay(100);
send_serial(s);
transmit_data(0x0D);
transmit_data(0x0A);
transmit_data(0x1A);
IE=0x90;
delay(200);
IE=0x88;
search(a);
if (out==1)
break;
}
}
i have seen this a couple of times and th mistake is that your serial receive works with an interrupt and it is not a voidable function smply remove the VOID that is attached to INTERRUPT 4 so tha ur code becomes
receive_data() interrupt 4
{
if(RI)
{
info[check++]=SBUF;
RI=0;
}
if(TI)
TI=0;
}
I would suggest making a smaller (as simple as you can) program that does nothing more than receive (by interrupt?) data and blink a LED or echo it back or in some other way indicate that you can reliably receive. Use that to talk to a terminal emulator or another known working interface. Cut out all possible middlemen and unknowns.
Check also UART error registers and configuration to make sure your clock/parity/data settings match in both ends. Start with a slow rate first.
Make one piece at a time work reliably, then put them together.

Resources