Start ESP32 when start is published in mqtt - c

This is my first time working with Arduino, an esp32 and MQTT. I made a motion sensor that prints to an LCD when it senses movement and publishes a message to mqtt, but it loops on forever. I am trying to make it so it will only start when start is published through mqtt and stops when stop is published. However, I am having some trouble figuring it out. Here is my current code (the main part excluding the MQTT set up), and I've been told putting it in callback may help but I get an error saying "a function-definition is not allowed here before '{' token" referring to void loop. Any suggestions are appreciated.
void callback(char *topic, byte *payload, unsigned int length) {
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
for (int i = 0; i < length; i++) {
Serial.print((char) payload[i]);
}
Serial.println();
Serial.println("-----------------------");
}
void loop() {
client.loop();
int motion = digitalRead(sensorPin);
if (motion == HIGH)
{
lcd.setCursor(0, 0);
lcd.print("!!!!!MOTION!!!!!");
client.publish(topic, "MOTION");
delay(100);
}
else
{
lcd.setCursor(0, 0);
lcd.print(" no motion ");
client.publish(topic, "NO MOTION");
delay(500);
}
}

first: why would you publish unlimited times on mqtt when motion is detected?
you detect the motion (digitalRead), publish it (i.e. with retain=true), and once published, set the flag i.e. published=true. Store the previous state of the motion and don't publish until it changed (either from motion to no-motion or vice versa).
Your code is publishing for ever when motion is detected (client.publish(topic, "MOTION");) or when motion is cleared (client.publish(topic, "NO MOTION");)
if I get you correctly: you want to publish motion state ONLY when the message on MQTT came: "start checking the motion", right?
if so, in callback you change the global variable i.e. "start_checking_motion = true" when message arrives (before the command on MQTT you set it to false)
Then in loop, first what you do is to check if start_checking_motion == true - if so, you perform the check of the motion and you publish - but again: only when motion changed - see the first paragraph of my post

Related

Arduino Socket.io shows disconnected when delay() is used

I am new to Arduino and facing an issue. I am implementing socket in Arduino for ESP8266 . It works as expected when I am not using delay() or not using someFunction(). As soon as i use delay or do some processing than i am getting connection disconnected one server socket.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
SocketIOclient socketIO;
#define USE_SERIAL Serial1
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
switch(type) {
case sIOtype_DISCONNECT:
USE_SERIAL.printf("[IOc] Disconnected!\n");
break;
case sIOtype_CONNECT:
USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
break;
case sIOtype_EVENT:
USE_SERIAL.printf("[IOc] get event: %s\n", payload);
break;
case sIOtype_ACK:
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_ERROR:
USE_SERIAL.printf("[IOc] get error: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_EVENT:
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_ACK:
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
hexdump(payload, length);
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
// disable AP
if(WiFi.getMode() & WIFI_AP) {
WiFi.softAPdisconnect(true);
}
WiFiMulti.addAP("SSID", "passpasspass");
//WiFi.disconnect();
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
String ip = WiFi.localIP().toString();
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
// server address, port and URL
socketIO.begin("10.11.100.100", 8880);
// event handler
socketIO.onEvent(socketIOEvent);
}
unsigned long messageTimestamp = 0;
void loop() {
socketIO.loop();
uint64_t now = millis();
if(now - messageTimestamp > 2000) {
messageTimestamp = now;
// creat JSON message for Socket.IO (event)
DynamicJsonDocument doc(1024);
JsonArray array = doc.to<JsonArray>();
// add evnet name
// Hint: socket.on('event_name', ....
array.add("event_name");
// add payload (parameters) for the event
JsonObject param1 = array.createNestedObject();
param1["now"] = now;
// JSON to String (serializion)
String output;
serializeJson(doc, output);
// Send event
socketIO.sendEVENT(output);
// Print JSON for debugging
USE_SERIAL.println(output);
}
//NEED TO DO SOME PROCESSIONG
delay(2000);
someFuntion();
}
void someFunction(){
//some processing that consume the time
}
You are getting timeout on server side.
When you are making delay(10000) esp8266 is just hanging on some loop (as a delay implementation) - any other code is not executed due this time. So when the server is not getting any data it disconnects client.
The solution (easy way) is to make a new connection every time you want to send data. In pseudocode it will look like this:
void loop(){
connectToServer();
sendData();
Disconnect();
delay(1000);
SomeFunction();
}
Then it should work.
The more complicated method is to familiarize yourself with the concept of Events and callbacks and get rid of delay() function. Or try to use esp_rtos_sdk witch is asynchronous tasks concepts RTOS framework.
Have fun!
Edited:
After refreshed the code I'm able to tell more about it, so I'm editing the previous post.
The quickest way to make it work(partly) is to remove the delay(2000) statement - because this If() statement is handling sending messages in 2 seconds interval. As I see you may wan't to put this void someFunction() to this if() statement too. This will make "some processing" in 2 seconds interval.
socketIO.loop() is handling maintaining connection with server and sending event's to server so it has to be call as quick as plausible. In case with delay you are getting socket timeout and disconnection.
void loop() function must be shortest as plausible to maintain the connection. Probably it is 500ms or so - regarding to you experiments. So any delay's is not plausible. If you need more processing try to familiarize with event processing. The easiest way is to declare flags as Boolean and timers counting time intervals. When timer is lunched it's sets the flag as true and you may process function.
Websockets on ESP(or other embedded microprocessors) are hard for beginners because they are time dependent. You need to have short processing loop or some of asynchronous methods used.
I hope, that I help.

Sound Sensor RGB LED coding

So we connected a sound sensor to our board to light up our LED light when sound is heard, it kinda works but there some hiccups.
We tried messing with the code for a while but no matter what we do the senor will only react to loud even when we put in a threshold. If you see in the picture, it it only displaying "loud" noise to the display and cant seem be able to go to the other condition we set in our threshold. We configure the sensor with our screw driver but nothing seem to work. Our code is below & before we continue on, we wanted to know if there a problem with it that can fix out the issue,thanks you
ALSO the sound sensor is a "ko9A01"
PS: we use "energia" to code this.
#include <msp430.h>
#include <Wire.h>
int soundsensor = 2;
int led = 3;
void setup()
{
Serial.begin(9600);
Serial.println("Begin Test");
pinMode(soundsensor,OUTPUT);
pinMode(led,OUTPUT);
}
void loop()
{
int sensorValue = digitalRead(soundsensor);
Serial.println(sensorValue);
delay(250);
if (sensorValue == 1)
{
Serial.print("LOUD");
digitalWrite(led,HIGH);
}
else
{
Serial.print("QUIET");
digitalWrite(led,LOW);
}
}
EDIT: NOW With the help of Brydon we change the output to input and change it to this we change it to this and now we get this new error voi
void setup()
{
Serial.begin(9600);
Serial.println("Begin Testing");
pinMode(soundsensor,INPUT);
}
and it only show
"begin test":
0 and wont move from there
You have the sound sensor configured as an OUTPUT in the setup.
I assume you want it to be an input? That would be the case if you're reading values from it.
I can't tell what sensor you have - but with more information on the sensor, we can read the documentation and help you configure the inputs appropriately (ie a threshold)

Problems with Arduino remotely controlling an outlet

This is a project that will be turned in, in 8 hours! We are stuck with this problem and thought of this place as a kind of last resort.
I'm trying to turn on/off a remotely controlled outlet with an Arduino.
We get inconsistencies when doing so. Sometimes when it goes into the if statement that ”should be ON” but instead turns the power off of the outlet and vice versa.
Code:
// on off remote control
int off = 12;
int on = 13;
void setup() {
pinMode(off, OUTPUT); // sets the digital pin as output
pinMode(on, OUTPUT);
}
void loop() {
// ..first we getting response from server if remote control should be on/off,
// working fine so not really relevant to problem.
// then we determine if outlet should be ON or OFF:
response.toCharArray(responseCharArray,100);
if(strstr( responseCharArray, "active") && strstr( responseCharArray, "1")) {
// This should turn ON the outlet.
digitalWrite(on, HIGH);
delay(250);
digitalWrite(on, LOW);
Serial.println("should be ON");
}
else if(strstr( responseCharArray, "active") && strstr( responseCharArray, "0")) {
// This should turn OFF the outlet.
digitalWrite(off, HIGH);
delay(250);
digitalWrite(off, LOW);
Serial.println("should be OFF");
}
}
Picture of the wiring:
Question:
What could be missing here? Since it randomly turns it on/off while entering same if statement.
There is too little information here.
Notice that your code will trigger (turn on) if responseCharArray contains something like e.g. "retroactively from 1941", or turn off for something like "active for 37 seconds".
In other words, that string-matching is not very precise, but it's hard to know what it should be since I don't know anything about the format of the response.
Perhaps it should at least be
if(strstr(responseCharArray, "active=1") != NULL)
or something, to at least lock the 1 to the active part.
In the logging that you do, print out the value of responseCharArray too. This will let you analyze whether or no the decision-making made sense.
Also, as always, triple-check your wiring and watch for e.g. back-feeding.

Touch sensor not working

I am attempting to create a very simple program in RobotC. In this program the robot will move forward until the touch sensor is hit.
#pragma config(Sensor, S2, touchSensor, sensorTouch)
void setMotors(int a, int b){
motor[motorA] = a;
motor[motorB] = b;
}
task main(){
wait1Msec(100);//Wait for sensor to init
setMotors(50, 50);
while(sensorValue(touchSensor) == 0){
//Do Nothing
}
setMotors(0, 0);
}
This code should make the robot move forward until the touch sensor is triggered.
Whenever I try and do anything with the touch sensor it does not work. When I output the value to the debug log it shows 180 when pressed and 1024 when released. I have verified that it is working normally by viewing the value on the brick itself.
Robot C Version: 4.0
Apparently, your touch sensor is stuck in SensorRaw mode. It is unclear - from the documentation I could find - how this could be fixed in code, but a work-around would be to explicitly put the sensor into raw mode (in case the situation changes in the future), and then compute the boolean value with a function like this:
bool sensorIsOn(short sensorRawValue)
{
bool isOn = false;
if(sensorRawValue > 512)
{
isOn = true;
}
return isOn;
}

how to stop a loop arduino

I have this loop, how would I end the loop?
void loop() {
// read the pushbutton input pin:
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300){
analogWrite(speakerOut, 200);
}
if(a <= 49){
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499){
analogWrite(speakerOut, NULL);
}
This isn't published on Arduino.cc but you can in fact exit from the loop routine with a simple exit(0);
This will compile on pretty much any board you have in your board list. I'm using IDE 1.0.6. I've tested it with Uno, Mega, Micro Pro and even the Adafruit Trinket
void loop() {
// All of your code here
/* Note you should clean up any of your I/O here as on exit,
all 'ON'outputs remain HIGH */
// Exit the loop
exit(0); //The 0 is required to prevent compile error.
}
I use this in projects where I wire in a button to the reset pin. Basically your loop runs until exit(0); and then just persists in the last state. I've made some robots for my kids, and each time the press a button (reset) the code starts from the start of the loop() function.
Arduino specifically provides absolutely no way to exit their loop function, as exhibited by the code that actually runs it:
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
Besides, on a microcontroller there isn't anything to exit to in the first place.
The closest you can do is to just halt the processor. That will stop processing until it's reset.
Matti Virkkunen said it right, there's no "decent" way of stopping the loop. Nonetheless, by looking at your code and making several assumptions, I imagine you're trying to output a signal with a given frequency, but you want to be able to stop it.
If that's the case, there are several solutions:
If you want to generate the signal with the input of a button you could do the following
int speakerOut = A0;
int buttonPin = 13;
void setup() {
pinMode(speakerOut, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
int a = 0;
void loop() {
if(digitalRead(buttonPin) == LOW) {
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300) {
analogWrite(speakerOut, 200);
}
if(a <= 49) {
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499) {
analogWrite(speakerOut, NULL);
}
}
}
In this case we're using a button pin as an INPUT_PULLUP. You can read the Arduino reference for more information about this topic, but in a nutshell this configuration sets an internal pullup resistor, this way you can just have your button connected to ground, with no need of external resistors.
Note: This will invert the levels of the button, LOW will be pressed and HIGH will be released.
The other option would be using one of the built-ins hardware timers to get a function called periodically with interruptions. I won't go in depth be here's a great description of what it is and how to use it.
The three options that come to mind:
1st) End void loop() with while(1)... or equally as good... while(true)
void loop(){
//the code you want to run once here,
//e.g., If (blah == blah)...etc.
while(1) //last line of main loop
}
This option runs your code once and then kicks the Ard into
an endless "invisible" loop. Perhaps not the nicest way to
go, but as far as outside appearances, it gets the job done.
The Ard will continue to draw current while it spins itself in
an endless circle... perhaps one could set up a sort of timer
function that puts the Ard to sleep after so many seconds,
minutes, etc., of looping... just a thought... there are certainly
various sleep libraries out there... see
e.g., Monk, Programming Arduino: Next Steps, pgs., 85-100
for further discussion of such.
2nd) Create a "stop main loop" function with a conditional control
structure that makes its initial test fail on a second pass.
This often requires declaring a global variable and having the
"stop main loop" function toggle the value of the variable
upon termination. E.g.,
boolean stop_it = false; //global variable
void setup(){
Serial.begin(9600);
//blah...
}
boolean stop_main_loop(){ //fancy stop main loop function
if(stop_it == false){ //which it will be the first time through
Serial.println("This should print once.");
//then do some more blah....you can locate all the
// code you want to run once here....eventually end by
//toggling the "stop_it" variable ...
}
stop_it = true; //...like this
return stop_it; //then send this newly updated "stop_it" value
// outside the function
}
void loop{
stop_it = stop_main_loop(); //and finally catch that updated
//value and store it in the global stop_it
//variable, effectively
//halting the loop ...
}
Granted, this might not be especially pretty, but it also works.
It kicks the Ard into another endless "invisible" loop, but this
time it's a case of repeatedly checking the if(stop_it == false) condition in stop_main_loop()
which of course fails to pass every time after the first time through.
3rd) One could once again use a global variable but use a simple if (test == blah){} structure instead of a fancy "stop main loop" function.
boolean start = true; //global variable
void setup(){
Serial.begin(9600);
}
void loop(){
if(start == true){ //which it will be the first time through
Serial.println("This should print once.");
//the code you want to run once here,
//e.g., more If (blah == blah)...etc.
}
start = false; //toggle value of global "start" variable
//Next time around, the if test is sure to fail.
}
There are certainly other ways to "stop" that pesky endless main loop
but these three as well as those already mentioned should get you started.
This will turn off interrupts and put the CPU into (permanent until reset/power toggled) sleep:
cli();
sleep_enable();
sleep_cpu();
See also http://arduino.land/FAQ/content/7/47/en/how-to-stop-an-arduino-sketch.html, for more details.
just use this line to exit function:
return;

Resources