I've programmed a callback function that handles MQTT messages (payload). I want to switch of pins not based on an incoming payload. The problem I have now is that the first statement is executed nicely when sending 11. However the second statement is not working. Strangely when I send two messages one containing 1x and the other x1 I can again toggle both pins. It's a very strange problem!
Could anyone help me with this, thanks very much. I'm using the PubSubClient library.
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
//Set GPIO0 to HIGH or LOW on first character received in message
if (payload[0] == '1') {
digitalWrite(GPIO0, HIGH); // Turn the relay on
client.publish(getOutTopic().c_str(), "GPIO0 set to HIGH");
} else if (payload[0] == '0') {
digitalWrite(GPIO0, LOW); // Turn the relay off
client.publish(getOutTopic().c_str(), "GPIO0 set to LOW");
}
//Set GPIO2 to HIGH or LOW on first character received in message
if (payload[1] == '1') {
digitalWrite(GPIO2, HIGH); // turn LED off. With High it is inactive on the ESP-01)
client.publish(getOutTopic().c_str(), "GPIO2 set to HIGH");
} else if (payload[1] == '0') {
digitalWrite(GPIO2, LOW); // Turn the LED on by making the voltage LOW
client.publish(getOutTopic().c_str(), "GPIO2 set to LOW");
}
}
The client.publish destroys the payload buffer. So I had to catch the payloads into new variables to overcome the problem.
void callback(char* topic, byte* payload, unsigned int length) {
char p0 = (char)payload[0];
char p1 = (char)payload[1];
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
//Set GPIO0 to HIGH or LOW on first character received in message
if (p0 == '1') {
digitalWrite(GPIO0, HIGH); // Turn the relay on
client.publish(getOutTopic().c_str(), "GPIO0 set to HIGH");
} else if (p0 == '0') {
digitalWrite(GPIO0, LOW); // Turn the relay off
client.publish(getOutTopic().c_str(), "GPIO0 set to LOW");
}
//Set GPIO2 to HIGH or LOW on first character received in message
if (p1 == '1') {
digitalWrite(GPIO2, HIGH); // turn LED off. With High it is inactive on the ESP-01)
client.publish(getOutTopic().c_str(), "GPIO2 set to HIGH");
} else if (p1 == '0') {
digitalWrite(GPIO2, LOW); // Turn the LED on by making the voltage LOW
client.publish(getOutTopic().c_str(), "GPIO2 set to LOW");
}
}
Related
I am new to this shift register. So i created a online simulation of the shift register that hook up to 8 LEDs, the ol' 8bit translate to 8 LED experiment. My design is that when i entered a character like "a" into the serial monitor, it will show in the result in code like 100000, and it should shown in the LEDs too(the sixth LED should light up).
char character;
byte input = 0b01;
void setup() {
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
byte input = 0b01;
if (Serial.available() > 0) {
character = Serial.read();
if (character == 'a') {
Serial.print("character received: ");
Serial.println(character);
for (int i = 0; i < 5; i++) {
updateShiftRegister(input);
input = input << 1;
delay(100);
}
Serial.print("Input: ");
Serial.print(input, BIN);
Serial.println();
} else {
Serial.print("Please retry.\n");
}
}
}
void updateShiftRegister(byte input) {
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, input);
digitalWrite(latchPin, HIGH);
}
Problem is when i run the thing, the result is alright but the shift register in the simulation is wack. Every LEDs turn on,
I found the shiftout function is main cause of this, any idea why it is causing every LED to light up?
I'm super new to programming so apologies if this is a badly phrased question or just confusing code! I have some Arduino code that automates things inside my campervan. The arduino reads data from temperature and battery sensors, and also controls the heating system. I had everything working, but the code was really inefficient and buggy, and I'd frequently get gibberish back when reading from the sensors. The internet suggested I use start and end markers to identify the useful information and discard the rest. Some copy and pasting and editing, I've ended up with the code below. However while the start and end markers are filtering out the information, I can't seem to make it call functions which actually do the controlling of things (e.g., move a servo, switch the heater on etc.).
// Van control system.
// Heater and sensor control code
// 20.03.2021
// This code controls the heater and reads from temp and power sensors.
#include <Servo.h> //Controls the heater air flow
#include <DHT.h> //temp sensor
#define DHT_SENSOR_TYPE DHT_TYPE_11
#include <Adafruit_INA260.h> //this breakout board monitors battery level and power consumption
Adafruit_INA260 ina260 = Adafruit_INA260();
Servo myservo; // servo to turn on heater
int pos = 90;// variable to store the servo position
#define DHTPIN 3 // temp sensor pin
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
// Set up pins, the controller uses a common ground, so only one
// ground connection is needed. Pin A and B are used as pairs to
// generate a quadrature signal that mimics a rotary encoder (which controllers the heater)
int pinA = 9; //Pin A of encoder output
int pinB = 10; // Pin B of encoder output
int offPin = 11; // Acts as momentary on button
int onPin = 12; // Act as momentary off button
const byte numChars = 32; //serial input buffer
char receivedChars[numChars];
boolean newData = false;
void setup() {
Serial.begin(9600);
dht.begin(); //initalise temp sensor
myservo.attach(5); //attaches servo to pin.
pinMode(pinA, OUTPUT);
pinMode(pinB, OUTPUT);
pinMode(onPin, OUTPUT);
pinMode(offPin, OUTPUT);
digitalWrite(pinA, LOW);
digitalWrite(pinB, LOW);
digitalWrite(onPin, LOW);
digitalWrite(offPin, LOW);
ina260.begin(); //initalise power sensor
Serial.println("<ATMega is ready>"); //confirms that microcontroller is ready
}
//End of set up process.
void loop() {
recvWithStartEndMarkers(); //for maximum efficiency, loop should be as short as possible.
showNewData();
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; //terminate string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void showNewData() {
if (newData == true) {
Serial.println(receivedChars); //this works and I get the character back in the serial monitor.
if (receivedChars == "H") {
upHeat(); //this doesn't work, if the character is H I want to call this function
}
else if (receivedChars == "C") {
downHeat();
}
else if (receivedChars == "O") {
heatOn();
}
else if (receivedChars == "F") {
heatOff();
}
else if (receivedChars == "a") {
servoPosOne();
}
else if (receivedChars == "s") {
servoPosTwo();
}
else if (receivedChars == "d") {
servoPosThree();
}
newData = false;
}
}
void upHeat() { //this generates a quadrature to simulate a rotary encoder.
digitalWrite(pinB, HIGH);
delay(200);
digitalWrite(pinA, HIGH);
delay(200);
digitalWrite(pinB, LOW);
delay(200);
digitalWrite(pinA, LOW);
delay(200);
digitalWrite(pinB, HIGH);
delay(200);
digitalWrite(pinA, HIGH);
delay(200);
digitalWrite(pinB, LOW);
delay(200);
digitalWrite(pinA, LOW);
}
//I haven't included the rest of the functions because, if one works, the rest will.
Any help would be amazing. I feel like it could be something really simple that I'm missing. Equally it could all be completely terrible!
For my project I am trying to messure the distance between my sensor and an object to trigger an alarm. If the alarm goes off, a message will be sent to my Telegram account with a library (UniversalTelegramBot). For messuring th distance, I'm using a loop to the distance between the sensor and the object, but to check if I get any messages through I also need to use a loop. I want to make it so that with the comment "/off" the distance get messured again, but when you type something else it waits till the comment "/off" gets used again. While the alarm is on and it's checking for messages, I want to use "/start" as comment to see which other comments to use and how to use it, without going to the messure loop again.
My main problem is that when the alarm goes off and I type anything, it doesn't matter what, the loop starts messuring again, but I only want it to go back to messuring when I type "/off" in telegram.
My code:
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
UniversalTelegramBot bot(TELEGRAM_BOT_TOKEN, client);
int delayBetweenChecks = 250;
unsigned long lastTimeChecked;
void handleNewMessages(int numNewMessages) {
for (int i = 0; i < numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
String from_name = bot.messages[i].from_name;
if (from_name == "")
from_name = "Gast";
Serial.print("De volgende knop is ingedrukt: ");
Serial.println(text);
if (text == F("/off")) {
digitalWrite(LED_PIN, LOW);
alarmStatus = 0;
noTone(buzzer);
}
if (text == "/start") {
String welcome = "Welkom bij jouw alarmsysteem, " + from_name + ".\n";
welcome += "/off : om het alarm uit te zetten\n";
bot.sendMessage(chat_id, welcome, "Markdown");
}
}
}
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(5);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);
// Convert the time into a distance
cm = (duration / 2) / 29.1; // Divide by 29.1 or multiply by 0.0343
inches = (duration / 2) / 74; // Divide by 74 or multiply by 0.0135
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
while (cm <= 9) {
Serial.println("ALARM!");
digitalWrite(LED_PIN, HIGH);
tone(buzzer, 250);
alarmStatus = 1;
if (alarmStatus = 1) {
if (millis() > lastTimeChecked + delayBetweenChecks) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
handleNewMess ages(numNewMessages);
if (numNewMessages) {
Serial.println("got response");
handleNewMessages(numNewMessages);
}
lastTimeChecked = millis();
}
}
break;
}
delay(250);
}
If I understand it correctly, you could keep two separate state variables. One for turning the alarm on and off when you leave/return home and one for when triggering the alarm.
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
const int delayBetweenChecks = 250;
bool alarmStatus = false; // off by default
bool alarmTriggered = false; // off by default
UniversalTelegramBot bot(TELEGRAM_BOT_TOKEN, client);
double measure_distance_in_cm() {
digitalWrite(trigPin, LOW);
delayMicroseconds(5);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
unsigned long duration = pulseIn(echoPin, HIGH);
// Convert the time into a distance
return static_cast<double>(duration) / (29.1 * 2.);
}
void trigger_on() {
digitalWrite(LED_PIN, HIGH);
alarmTriggered = true;
}
void trigger_off() {
digitalWrite(LED_PIN, LOW);
alarmTriggered = false;
}
void handleNewMessages() {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
for (int i = 0; i < numNewMessages; i++) {
String text = bot.messages[i].text;
Serial.println(text);
if (text == "/start") { // when you're leaving home
alarmStatus = true;
trigger_off();
}
else if (text == "/stop") { // when you're returning home
alarmStatus = false;
trigger_off();
}
else if (text == "/off") { // turn off a triggered alarm
trigger_off();
}
else if (test == "/on") { // force triggering of the alarm
trigger_on();
}
}
}
void loop() {
static unsigned long lastTimeChecked = millis();
handleNewMessages();
if(alarmStatus) { // you only need to do this if the alarm is on
if(alarmTriggered == false) { // only measure if the alarm isn't already triggered
double cm = measure_distance_in_cm();
if(cm <= 9.) {
Serial.println("ALARM!");
trigger_on();
}
}
// If it's triggered: Sound the alarm in 200ms bursts until /off or /stop is received
if(alarmTriggered) tone(buzzer, delayBetweenChecks-50);
}
// try to keep the polling at a steady pace
lastTimeChecked += delayBetweenChecks;
unsigned long now = millis();
if(lastTimeChecked > now) delay(lastTimeChecked - now);
}
Disclaimer: I don't have an Arduino so I can't test it myself.
I'm trying to blink a LED according to press of toggle button. If I press the first toggle switch the first time, LED blinks at 5 Hz, when I press the toggle button for the second time, LED blink at 6 Hz and when I press the third time, LED turns off.
I tried using the program below, but It's not working as I wanted.
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 7; // the number of the pushbutton pin
const int ledPin = 6; // the number of the LED pin
// variables will change:
int buttonState = 0;
// variable for reading the pushbutton status
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop() {
int x=0;
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
Serial.print(x);
// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState == HIGH && x==0) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
Serial.print(x);
} else {
// turn LED off:
x = x+1;
}
if (buttonState == HIGH && x==1) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(2000);
digitalWrite(ledPin, LOW);
delay(2000);
Serial.print(x);
} else {
// turn LED off:
digitalWrite(ledPin, LOW);
x = x+1;
}
if (buttonState == HIGH && x==2) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(3000);
digitalWrite(ledPin, LOW);
delay(3000);
Serial.print(x);
} else {
// turn LED off:
digitalWrite(ledPin, LOW);
x = x+1;
}
if (buttonState == HIGH && x==3) {
// turn LED off:
digitalWrite(ledPin, LOW);
x = 0;
}
}
When I use this code it works for first case that is LED blinks at 1000 ms delay, but if I toggle switch it again works for first condition. How can I make it to execute second condition i.e. to blink at delay of 2000 ms?
Firstly this is your circuit. I tried this circuit and code and worked for me. I used interrupt for checking button state. And millis calculation is simple.
Frequency = 1 / Period
Period = Ton + Toff
6Hz = 1000 millis / T => T = 166 millis
166 = Ton + Toff (for %50 duty cycle Ton=Toff) => Ton = Toff = 83 millis
enter image description here
const int ledPin = 13;
const int buttonPin = 2;
int state = -1;
bool willLightOn = false;
unsigned long currentDelay = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), changeState, FALLING);
}
void loop() {
if(state % 3 == 0) { //6Hz
currentDelay = 83;
willLightOn = true;
} else if (state % 3 == 1) { //5Hz
currentDelay = 100;
willLightOn = true;
} else if (state % 3 == 2) { //LED off
currentDelay = 0;
willLightOn = false;
digitalWrite(ledPin, LOW);
}
currentMillis = millis();
if (currentMillis - previousMillis >= currentDelay && willLightOn) {
previousMillis = currentMillis;
digitalWrite(ledPin, !digitalRead(ledPin));
}
}
void changeState() {
state++;
}
Right now your logic checks 3 times for the value of x in a single loop.
Below code toggles light whenever x is greater than zero. x's value is changed when button is pressed.
But there is a big problem here: If button is pressed when there's something else going on in the processor or it is sleeping (like the long delays you want to use), it may be ignored. So you better study interrupts and implement this behavior using them.
if (x > 0)
{
digitalWrite(ledPin, HIGH);
delay(1000 * x);
digitalWrite(ledPin, LOW);
}
if (buttonState == HIGH)
{
x++;
if (x > 3)
x = 0;
}
You should create a global state of the application. This state is where you remember if you are blinking at 50hz/60hz/off. Then you can use a switch to do the right thing.
Then you check if the button is pressed and change the application state.
See my example below:
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 7; // the number of the pushbutton pin
const int ledPin = 6; // the number of the LED pin
// variables will change:
int applicationState = 0;
bool lightOn = true;
int currentDelay = 1000;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
// variable for reading the pushbutton status
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
void loop() {
if (digitalRead(buttonPin) == HIGH) {
applicationState++;
if(applicationState >= 3) {
applicationState = 0;
}
delay(100);
}
switch(applicationState){
case 0:
currentDelay = 1000;
lightOn = true;
break;
case 1:
currentDelay = 2000;
lightOn = true;
break;
case 2:
digitalWrite(ledPin, LOW);
lightOn = false;
break;
}
currentMillis = millis();
if (currentMillis - previousMillis >= currentDelay && lightOn) {
previousMillis = currentMillis;
digitalWrite(ledPin, !digitalRead(ledPin));
}
}
I hope you understand what I try to say and demo with the example code.
Your code can not work:
You do need to check if the button state changes, detect when there is a edge. And make sure you detect a single edge only once.
You must repeat the blinking it in a loop till the button is pressed, then you can change the frequency.
You must check the button while you sleep, otherwise your program do not recognize when you press the button.
To make it work, you must change the complete program.
#define BLINK_SLEEP_TIME <some value> // insert value for 16.6666ms
//return 1 after a positive edge
bool button_read(void)
{
static bool lastState=1; //set this to 1, so that a pressed button at startup does not trigger a instant reaction
bool state = digitalRead(buttonPin);
if(state != lastState)
{
state=lastState;
return state;
}
return 0;
}
//Blink the LED with a given period, till button is pressed
//Times are in x*16.666ms or x/60Hz
//At least one time should be more than 0
void blink(uint8_t ontime, uint8_t offtime)
{
while(1)
{
for(uint8_t i=0;i<ontime;i++)
{
led_setOn();
delay(BLINK_SLEEP_TIME);
if(button_read())
{
return;
}
}
for(uint8_t i=0;i<offtime;i++)
{
led_setOff();
delay(BLINK_SLEEP_TIME);
if(button_read())
{
return;
}
}
}
}
const uint8_t time_table[][]=
{
{0,50},//LED is off
{6,6}, //LED blinks with 5Hz, 60Hz/2/6=5Hz
{5,5}, //LED blinks with 6Hz, 60Hz/2/5=6Hz
}
void endless(void)
{
uint8_t i=0;
for(;;)
{
i++;
if(i>2)
{
i=0;
}
blink(time_table[i][0],time_table[i][1]);
}
}
A better approach would be to use a hardware PWM-Module and change the values after a edge on the button.
#include <SoftwareSerial.h>
SoftwareSerial SIM900(7, 8);
String outMessage1 = "Hello Arduino";
String outMessage2 = "Arduino2";
volatile int NbTopsFan; //measuring the rising edges of the signal
int Calc;
int hallsensor = 2; //The pin location of the sensor
int condition_1 = LOW;
int condition_2 = LOW;
int gsm_condition_1 = HIGH;
int gsm_condition_2 = HIGH;
String destinationNumber = "+6014681xxxx";
void rpm () //This is the function that the interupt calls
{
NbTopsFan++; //This function measures the rising and falling edge of the hall effect sensors signal
}
// The setup() method runs once, when the sketch starts
void setup() //
{
Serial.begin(9600); //This is the setup function where the serial port is initialised,
SIM900.begin(19200);
SIM900power();
delay(20000);
pinMode(hallsensor, INPUT); //initializes digital pin 2 as an input
pinMode(13, OUTPUT);
attachInterrupt(0, rpm, RISING); //and the interrupt is attached
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void SIM900power()
{
digitalWrite(9,HIGH);
delay(1000);
digitalWrite(9,LOW);
delay(5000);
}
void sendSMS1()
{
gsm_condition_2 = HIGH;
SIM900.print("AT+CMGF=1\r"); //AT command to send SMS message
delay(100);
SIM900.println("AT + CMGS = \"" + destinationNumber +"\""); //recipient's mobile number, in international format
delay(100);
SIM900.println(outMessage1); //message to send
delay(100);
SIM900.println((char)26); //End AT command with a^Z, ASCII code 26 or ctrl+z
delay(100);
}
void sendSMS2()
{
gsm_condition_1 = HIGH;
SIM900.print("AT+CMGF=1\r"); //AT command to send SMS message
delay(100);
SIM900.println("AT + CMGS = \"" + destinationNumber +"\""); //recipient's mobile number, in international format
delay(100);
SIM900.println(outMessage2); //message to send
delay(100);
SIM900.println((char)26); //End AT command with a^Z, ASCII code 26 or ctrl+z
delay(100);
}
void gsm_check()
{
if (condition_1 == HIGH && gsm_condition_1 == HIGH) {
condition_1 = LOW;
gsm_condition_1 = LOW;
sendSMS1();
}
else if ( condition_2 == HIGH && gsm_condition_2 == HIGH) {
condition_2 = LOW;
gsm_condition_2 = LOW;
sendSMS2();
}
}
void water_rate()
{
NbTopsFan = 0; //Set NbTops to 0 ready for calculations
sei(); //Enables interrupts
delay (1000); //Wait 1 second
cli(); //Disable interrupts
Calc = (NbTopsFan * 60 / 7.5); //(Pulse frequency x 60) / 7.5Q, = flow rate in L/hour
Serial.print (Calc, DEC); //Prints the number calculated above
Serial.print (" L/hour\r\n"); //Prints "L/hour" and returns a new line
}
void loop ()
{
water_rate();
if ( Calc > 100 ) {
digitalWrite(13, HIGH);
condition_1 = HIGH;
}
else {
digitalWrite(13, LOW);
condition_2 = HIGH;
}
gsm_check();
}
I cannot use water flow rate sensor and GSM(SIM900) together. Once I use both then GSM will not work at all. I have tried a lot of methods to solve it but still not succeeded. Hope you guys can give me some help.
I was having the same problem. The solution is to stop the time 2 interrupt from running to send the message.
The edit of your code:
void sendSMS2()
{
cli();
gsm_condition_1 = HIGH;
SIM900.print("AT+CMGF=1\r"); //AT command to send SMS message
delay(100);
SIM900.println("AT + CMGS = \"" + destinationNumber +"\"");
delay(100);
SIM900.println(outMessage2); //message to send
delay(100);
SIM900.println((char)26); //End AT command with a^Z, ASCII code 26 or ctrl+z
delay(100);
sei();
}
The problem is that the sensor can only read using the interrupt, but your sim900 will not function when the interrupt is active. So you'll have to temporarily disable the interrupt and then carry on. Also, SIM900.print("AT+CMGF=1\r"); should be in void loop.
The last problem is that your sim 900 will not send integer. So you'll have to send it as a string. Please follow this example for that: http://microembarcado.blogspot.com.au/p/wr-bridge-send-sms-with-temperature.html
I sat for about 4 hours to figure it out.
Hope this makes things a little simplier for you.