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.
Related
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
Raspberry Pi is sending commands and reading data from Arduino via I2C.
Everything is done this way:
Raspberry send a command to Arduino and read a response.
Arduino sends command (on or off) to dali power supply and gets a response. It takes about 40 ms.
On Raspberry Pi, it's not getting updated response.
How to wait until loop() is done reading data? And then send to the Raspberry Pi? I can not wait because some commands should be repeated at less than 100 ms.
Here is my code:
#include <Wire.h>
#include "dali.h"
volatile bool state = false;
volatile uint8_t response = 0;
volatile bool newCommand = false;
void setup()
{
dali.Init(A0, 7);
dali.busTest();
dali.transmit(BROADCAST, RECALL_MIN_LEVEL);
Wire.onReceive(ReceiveEvent);
Wire.onRequest(RequestEvent);
Wire.begin(10);
}
void loop()
{
if (newCommand)
{
Serial.println(state);
response = dali.GetData(state);
Serial.println(response);
newCommand = false;
}
}
void ReceiveEvent(int howMany)
{
byte req;
while (Wire.available())
{
req = Wire.read();
}
if (req == 97)
{
state = !state;
newCommand = true;
}
}
void RequestEvent()
{
Wire.write(response);
}
You did not add Serial.begin() into the setup and then tried to call Serial.print() in the loop, this may be freezing your Arduino.
For a simple project, I would like to use an ATTiny85 connected to an HC-06 Bluetooth module, so it can talk to my Android phone.
I wrote code for my Arduino Uno and it worked as expected. When I changed the code to use on my ATTiny85 I got an error saying that 'Serial' was not declared in this scope and assumed that the ATTiny does not support Hardware Serial.
I need to read a String when received and sleep the MCU when not receiving. I went to use SoftwareSerial and was not able to get a String, just the first char.
I approached it in some way, like defining a char string[10]; as global and string[i] = mySerial.read(); i++; inside the loop, but it keeps not working. Whether it is the sleep, or my work to read data, I couldn't make it work.
Can someone provide a way to put a ATTiny85 to sleep, wake up to receive a String through Serial and sleep until the next data through Serial, please?
To sleep I'm using
void sleep() {
GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts
PCMSK |= _BV(PCINT3); // Use PB3 as interrupt pin
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable(); // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
sei(); // Enable interrupts
sleep_cpu(); // sleep
// woke up
cli(); // Disable interrupts
PCMSK &= ~_BV(PCINT3); // Turn off PB3 as interrupt pin
sleep_disable(); // Clear Sleep Enable bit
sei(); // Enable interrupts
}
ISR(PCINT3_vect) {
}
And my loop is something like
char inputString[10];
int i = 0;
void loop() {
sleep();
if (serial.available() > 0) {
char inputChar = serial.read();
if (inputChar == '2') { //Char to break
//Do something and reset i
} else {
inputString[i] = inputChar;
}
i++;
}
}
Thanks to all.
if (serial.available() > 0) {
That's a one-time thing. You should put this in a while loop.
while (serial.available() > 0) {
char inputChar = serial.read();
if (inputChar == '2') { //Char to break
//Do something and reset i
} else {
inputString[i] = inputChar;
}
i++;
}
Wouldn't hurt to check i after incrementing, too.
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).
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.