The title is quite self explanatory, just like the other question I have posted at Exception 28 thrown on ESP8266-01 when connected to Adafruit MQTT and Telegram
Somebody might say this is the same question, but it's actually a follow-up of it, and I deem this as worthy of a standalone question, since it might help other people as well and it is not really covered anywhere else on the internet. I am also going to post the entirety of my code (just like I did on the previous question), although StackOverflow recommends posting the bare minimum to replicate the error, since I feel like it is needed in its entirety to fully replicate it. (Private data is replaced by the word PRIVATE)
#include "UniversalTelegramBot.h"
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#define BOTtoken "PRIVATE"
#define fanPin 2
#define myChatId "PRIVATE"
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883
#define AIO_USERNAME "PRIVATE"
#define AIO_KEY "PRIVATE"
char ssid[] = "PRIVATE";
char password[] = "PRIVATE";
bool fanState;
unsigned long lastTimeBotRan = 0;
unsigned long checkTime = 1000;
int numNewMessages;
unsigned long timerStartPoint = 0;
bool timerStart;
String chat_id;
String text;
int messagesNumber;
String timerString;
String Request;
const unsigned long rst = 300000;
boolean MQTT_connect();
WiFiClientSecure telegramClient;
WiFiClient MQTTClient;
UniversalTelegramBot bot(BOTtoken, telegramClient);
Adafruit_MQTT_Client mqtt(&MQTTClient, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Subscribe PRIVATE = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/PRIVATE");
void checkUpdates(int numNewMessages) {
for (int i = 0; i < numNewMessages; i++) {
chat_id = String(bot.messages[i].chat_id);
text = bot.messages[i].text;
String from_name = bot.messages[i].from_name;
if (chat_id != myChatId) {
bot.sendMessage(chat_id, "Unauthorized user, please refrain from texting this bot again.", "");
continue;
}
if (text == "/start") {
bot.sendMessage(chat_id, "Welcome " + from_name + "!\n"
"Control your fan remotely!\n\n"
"/fanon: switch the fan ON\n"
"/fanoff: switch the fan OFF\n"
"/state: get the current state of the fan"
"/timer15: run fan for 15 minutes"
"/timer30: run fan for 30 minutes"
"/timer60: run fan for 1 hour"
"/timer: run fan for the amount of time you specify", "Markdown");
}
if (text == "/fanon") {
digitalWrite(fanPin, HIGH);
Serial.println("Fan on");
fanState = true;
bot.sendMessage(chat_id, "Your fan is ON", "");
}
if (text == "/fanoff") {
fanState = false;
timerStart = false;
digitalWrite(fanPin, LOW);
Serial.println("Fan off");
bot.sendMessage(chat_id, "Your fan is OFF", "");
}
if (text == "/state") {
if (digitalRead(2) == HIGH) {
bot.sendMessage(chat_id, "Your fan is ON", "");
} else {
bot.sendMessage(chat_id, "Your fan is OFF", "");
}
}
if (text == "/timer15") {
timerStartPoint = millis();
digitalWrite(fanPin, HIGH);
timerStart = true;
Serial.print("(/timer15) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(chat_id, "Your fan will run for 15 minutes", "");
launchTimer(15);
}
if (text == "/timer30") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(/timer30) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(chat_id, "Your fan will run for 30 minutes", "");
launchTimer(30);
}
if (text == "/timer60") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(/timer60) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(chat_id, "Your fan will run for 1 hour", "");
launchTimer(60);
}
if (text == "/timer") {
messagesNumber = bot.last_message_received + 1;
bot.sendMessage(chat_id, "How long do you want the fan to run for? (in minutes)", "");
Serial.println(messagesNumber);
while (messagesNumber == (bot.last_message_received + 1)) {
checkUpdates(bot.getUpdates(bot.last_message_received + 1));
timerString = bot.messages[i].text;
yield();
}
if (messagesNumber < (bot.last_message_received + 1)) {
unsigned long timer = timerString.toInt();
Serial.println(timer);
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(/timer) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(chat_id, "Your fan will run for " + timerString + " minutes", "");
launchTimer(timer);
}
}
text = "";
}
}
void launchTimer(unsigned long timeInMinutes) {
unsigned long timeInMillis = timeInMinutes * 60 * 1000;
while (timerStart) {
checkUpdates(bot.getUpdates(bot.last_message_received + 1));
if (MQTT_connect()) {
Adafruit_MQTT_Subscribe *subscription_name;
while ((subscription_name = mqtt.readSubscription(4000))) {
if (subscription_name == &PRIVATE) {
Request = ((char *)PRIVATE.lastread);
if (Request == "fanon") {
digitalWrite(fanPin, HIGH);
Serial.println("(Control panel) Fan on");
fanState = true;
bot.sendMessage(myChatId, "Fan turned on through Control Panel", "");
}
if (Request == "fanoff") {
fanState = false;
timerStart = false;
Serial.println("(Control panel) Fan off");
digitalWrite(fanPin, LOW);
bot.sendMessage(myChatId, "Fan turned off through Control Panel", "");
}
if (Request == "timer15") {
timerStartPoint = millis();
digitalWrite(fanPin, HIGH);
timerStart = true;
Serial.print("(CP /timer15) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 15 minutes through Control Panel", "");
launchTimer(15);
}
if (Request == "timer30") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(CP /timer30) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 30 minutes through Control Panel", "");
launchTimer(30);
}
if (Request == "timer60") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(CP /timer60) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 1 hour through Control Panel", "");
launchTimer(60);
}
}
}
}
if (millis() - timerStartPoint > timeInMillis) {
digitalWrite(fanPin, LOW);
timerStart = false;
Serial.print("Timer run out at ");
Serial.println(millis());
bot.sendMessage(myChatId, "Fan turned off because timer ran out", "");
}
yield();
}
}
boolean MQTT_connect() {
int8_t ret;
if (mqtt.connected()) {
return true;
} uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) {
mqtt.disconnect();
delay(2000);
retries--;
if (retries == 0) {
return false;
}
} return true;
}
void setup() {
Serial.begin(115200);
telegramClient.setInsecure();
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
Serial.print("Connecting Wifi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
pinMode(fanPin, OUTPUT);
delay(10);
digitalWrite(fanPin, LOW);
Request = "";
mqtt.subscribe(&PRIVATE);
if (MQTT_connect()) {
Serial.println("mqtt connected");
}
else {
Serial.println("mqtt connection failed");
}
}
void loop() {
if (millis() - lastTimeBotRan > checkTime) {
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages) {
checkUpdates(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
delay(1000);
if (MQTT_connect()) {
Adafruit_MQTT_Subscribe *subscription_name;
while ((subscription_name = mqtt.readSubscription(4000))) {
if (subscription_name == &PRIVATE) {
Request = ((char *)PRIVATE.lastread);
if (Request == "fanon") {
digitalWrite(fanPin, HIGH);
Serial.println("(Control panel) Fan on");
fanState = true;
bot.sendMessage(myChatId, "Fan turned on through Control Panel", "");
}
if (Request == "fanoff") {
fanState = false;
timerStart = false;
Serial.println("(Control panel) Fan off");
digitalWrite(fanPin, LOW);
bot.sendMessage(myChatId, "Fan turned off through Control Panel", "");
}
if (Request == "timer15") {
timerStartPoint = millis();
digitalWrite(fanPin, HIGH);
timerStart = true;
Serial.print("(CP /timer15) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 15 minutes through Control Panel", "");
launchTimer(15);
}
if (Request == "timer30") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(CP /timer30) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 30 minutes through Control Panel", "");
launchTimer(30);
}
if (Request == "timer60") {
digitalWrite(fanPin, HIGH);
timerStart = true;
timerStartPoint = millis();
Serial.print("(CP /timer60) Fan on at ");
Serial.println(timerStartPoint);
bot.sendMessage(myChatId, "Fan turned on for 1 hour through Control Panel", "");
launchTimer(60);
}
}
}
}
if (!mqtt.ping()) {
mqtt.disconnect();
}
}
Now, here is the issue: I would like to instantiate both WiFiClient (to connect to Adafruit MQTT) and WiFiClientSecure (to connect to my Telegram bot) without ending up with a crash, followed by an Exception 28 stacktrace (of which I have already posted a "normal" and decoded version in the other question at the link). I know for a fact, after hours of research, that I cannot instantiate two instances of WiFiClientSecure because it would exceed the available heap on an ESP01, and I also know that WiFiClient alone, or WiFiClientSecure alone, never happen to throw said exception.
Nothing that I have found says that WiFiClient and WiFiClientSecure cannot be used together, but maybe I am missing something. Therefore my question is: is it possible to use both WiFiClient and WiFiClientSecure, for two different purposes (respectively, MQTT and Telegram), in the same code?
I look forward to reading any piece of advice, since I am currently at a loss for ideas. Thanks in advance to everybody who will help.
EDIT: for anybody who is interested, here are my latest findings:
Overclocking the CPU of the ESP01 from 80 MHz to 160 MHz helps mitigate the crashes, in fact it only crashed once over the course of 8 hours. Going from a crash every 30 minutes to 8 hours sure is a step forward;
I found out (thanks to romkey for the advice), that it WiFiClient and WiFiClientSecure are NOT incompatible, the problem was caused by the free heap. Freeing up the heap, by removing Strings and turning them to char arrays, got the program not to crash over the course of 13 whole hours. I might test this further and leave it on for even more than 24 hours, to see if it is really solved. In case, I will update.
UPDATE: found the problem and changed the title. The problem lies entirely in heap use of both the libraries. Both the libraries use an enormous amount of heap space and after a while it runs out and causes exception 28, because, as far as I know, the ESP tries to read from the wrong part of its memory, causing it to crash. I will attach the Serial output of the heap throughout just 3 minutes:
41552
19448
20008
20120
20312
20200
20120
20120
20120
20312
20120
20120
20120
20312
20312
20312
20312
20312
20312
20504
20312
20312
19640
As you can see, the first instance has the heap show more than 40k, which is normal, as 51k-ish is the maximum. Then it jumps down to 20000, and later runs out, even though the ESP reclaims its own memory after a couple of minutes.
I also do use both WiFiClient and WiFiClientSecure in my ESP8266 multisensors checking and publishing project.
And while my WifiClient is up all the time (for MQTT and ThingSpeak), secured connection (needed for publishing to GoogleSheets more rarely - every 30 minutes) I decided to put into the function.
The whole project code is too large (there's 5570 lines), so here's a part of it with comments introducing heap usage:
// heap size before call of the function: 37112
int SendGSCRPT() {
showHeap(); // 35312
WiFiClientSecure GoogleClient;
showHeap(); // 27512
GoogleClient.setInsecure();
if (!GoogleClient.connect("script.google.com", 443)) {
GoogleClient.stop();
return 0;
}
showHeap(); // 9840 !!!
char sGog[200];
snprintf(sGog, sizeof(sGog), "GET %s HTTP/1.1\r\nHost: script.google.com\r\nUser-Agent: PRIVATE\r\nConnection: close\r\n\r\n", "PRIVATE");
GoogleClient.print(sGog);
Serial.println("request sent!");
int res = 1;
GoogleClient.stop();
showHeap(); // 30104
return res;
}
// heap size after function releases all the memory taken: 37112 again !!!
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 days ago.
Improve this question
I am programming my Arduino Uno with an ultrasonic sensor to sense the distance from it and print that on the serial plotter / monitor, at the same time 4 LEDS are blinking. I am not having any problems with them doing their thing at the same time, but I want the ultrasonic sensor to sense more than one time after the LEDS blink once. Here is the code:
const int red1 = 4;
const int red2 = 5;
const int yellow1 = 6;
const int yellow2 = 7;
const int echo = 10;
const int trig = 9;
const int buzzer = 11;
int maxCmAway = 50;
float duration_us, distance_cm;
void setup() {
Serial.begin (9600);
pinMode(red1, OUTPUT);
pinMode(red2, OUTPUT);
pinMode(yellow1, OUTPUT);
pinMode(yellow2, OUTPUT);
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
}
void loop() {
digitalWrite(yellow1, LOW);
digitalWrite(yellow2, LOW);
digitalWrite(red1, HIGH);
digitalWrite(red2, HIGH);
for(int i=0; i<6; i++) {
if (checkDist() > maxCmAway) {
blip();
} else {
noTone(buzzer);
delay(250);
}
}
digitalWrite(red1, LOW);
digitalWrite(red2, LOW);
digitalWrite(yellow1, HIGH);
digitalWrite(yellow2, HIGH);
for(int i=0; i<6; i++) {
if (checkDist() > maxCmAway) {
blip();
} else {
noTone(buzzer);
delay(250);
}
}
int checkDist() {
// generate 10-microsecond pulse to TRIG pin
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
// measure duration of pulse from ECHO pin
duration_us = pulseIn(echo, HIGH);
// calculate the distance
distance_cm = 0.017 * duration_us;
// print the value to Serial Monitor
if (distance_cm < maxCmAway) {
Serial.print("distance: ");
Serial.print(distance_cm);
Serial.println(" cm");
Serial.print("real: ");
Serial.print(distance_cm);
Serial.println(" cma");
} else {
Serial.println("distance: 0 cm");
Serial.print("real: ");
Serial.print(distance_cm);
Serial.println(" cma");
}
return distance_cm;
}
int blip() {
tone(buzzer, 800);
delay(50);
tone(buzzer, 600);
delay(100);
tone(buzzer, 800);
delay(100);
noTone(buzzer);
return 1;
}
The code that is messing up is after all the digitalWrites in the loop() function in the for loop where it says if (checkDist() > maxCmAway). The error is " 'checkDist' was not declared in this scope " I know what this error means, but I don't know any other way to fix it besides copying and pasting the if statement 5 times.
When I look this up on Google, it just shows a bunch of things about the setup() and loop() functions.
And the other thing I tried is putting the word 'global' before the function I am calling, but 'global' does not look like it is a keyword.
Thanks!
You are missing a closing bracket for void loop()
If you look at the other error messages above "Compilation error", you should see one that says error: expected '}' at end of input. If not, you can go to preferences and enable verbose out for compilation.
I'm thinking of building a DIY gokart-offroad type of car from scrap vehicle parts. And as electronics, I'm planning on using my Arduino Uno for controlling basically everything. So I just made a gear display which lets me know which gear I'm currently on. And a simple parking sensor.
If I'm too close to an object the parking sensor buzzer will blink. And I can't blink with delay() functions because if I try to shift up when it's blinking, the input won't register because delay is a blocking function. So I tried to use millis instead but seems like it also doesn't register.
The concept is easy. I will have two buttons on each side of my shiftstick. And when I shift up the button gets pressed therefore the 7 segment display adds one. And another one for downshifting.
I have not added the reverse gear yet, but still when the parking sensor function is working, I still can't push the buttons for a good amount of time.
I hope you guys could understand what I'm trying to say. I just started coding in Arduino so I don't have enough experience. I'm leaving the .ino file as a code block. I tried to explain everything in the code. Hopefully someone knows the solution for this. Thanks in advance...
// Setting up the pins for 7-Segment-Display.
const int E = 13;
const int D = 12;
const int C = 11;
const int DP = 10;
const int G = 9;
const int F = 8;
const int A = 7;
const int B = 6;
// Setting up the counter for gear shifting.
int gearCount = 0;
// Setting up the button states and button pin for state-change detectors.
int buttonAddPin = 5;
int buttonAddState = 0;
int previousButtonAddState = 0;
// And this is for downshifting.
int buttonSubstractPin = 4;
int buttonSubstractState = 0;
int previousButtonSubstractState = 0;
// Adding buzzer for parking sensor.
const int buzzerPin = 3;
int buzzerTone = 0;
// Adding parking sensor.
const int trigPin = 2;
const int echoPin = 1;
long duration;
int distance;
// Adding non-delay blinker with millis for buzzer to blink when sensor detects an object.
unsigned long previousTime = 0;
/*---------------------------------- FUNCTIONS FOR SHIFTING -------------------------------------------------*/
// "zero" function is the series of commands for displayıng 0 on the 7-Segment corresponding to neutral gear.
void zero()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "one" function is the series of commands for displaying 1 on the 7-Segment-Display corresponding to 1st gear.
void one()
{
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "two" function is the series of commands for displaying 2 on the 7-Segment-Display corresponding to 2nd gear.
void two()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// "three" function is the series of commands for displaying 3 on the 7-Segment-Display corresponding to 3rd gear.
void three()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// And now the main function that we are going to call in "void loop" to determine which gear we are on.
void gearDisplay()
{
buttonAddState = digitalRead(buttonAddPin); // And now the button state change detector for shifting up is ready. As you can see when I push the button
// program adds 1 to gearCount variable. So now gearCount is 1. Therefore the 7-Segment-Display will show
if(buttonAddState != previousButtonAddState){ // 1 on it. We will do this a bit later.
if(buttonAddState == HIGH){
gearCount++;
}
delay(50);
}
previousButtonAddState = buttonAddState;
// Now for downshifting.
buttonSubstractState = digitalRead(buttonSubstractPin);
if(buttonSubstractState != previousButtonSubstractState) {
if(buttonSubstractState == HIGH){
gearCount--;
}
delay(50);
}
previousButtonSubstractState = buttonSubstractState;
// Now we will call one of the 7-Segment-Display number functions according to the gearCount variable.
if(gearCount == 0){
zero();
}
else if(gearCount == 1){
one();
}
else if(gearCount == 2){
two();
}
else if(gearCount == 3){
three();
}
else if(gearCount > 3){ // Just making sure if we press buttons accidentally more than enough, it sets gearCount back to closest gear available.
gearCount--;
}
else if(gearCount < 0){ // Same with this one.
gearCount++;
}
}
// That was al for the gear panel. Now the hard part. The parking sensor.
/*---------------------------------------------- SHIFTING DONE ----------------------------------------------------*/
/*--------------------------------------------- PARKING SENSOR ------------------------------------------------------*/
void parkingSensor()
{
digitalWrite(trigPin, LOW); // Resetting trigPin
delayMicroseconds(2); // Delaying to prevent any issiues. ( I saw this online, most people do it like this so I also did.)
digitalWrite(trigPin, HIGH); // Creating a short soundwave.
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH); // Detecting the time that passes for the soundwave to reach back to the receiver.
distance = duration*0.034/2; // Simple math for calculating the distance between the obstacle and sensor.
unsigned long currentTime = millis(); // Store the current time for blinking the buzzers.
if((distance > 0) && (distance < 31)){ // If the distance is in between 0-31 buzz non-stop.
buzzerTone = 1000;
tone(buzzerPin, buzzerTone);
}
else if((distance > 30) && (distance < 76)){ // If the distance is between 30-76 buzz every 100 miliseconds.
if(currentTime - previousTime >= 100){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if((distance > 75) && (distance < 101)){ // If the distance is between 75-101 buzz every 300 miliseconds.
if(currentTime - previousTime >= 300){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if(distance > 100){ // If the distance is more than 100 don't buzz.
buzzerTone = 0;
tone(buzzerPin, buzzerTone);
}
}
/*------------------------------- PARKING SENSOR DONE -----------------------------------*/
// And this was, I guess, all I had to do but unfortunately it's not working...
void setup() {
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
pinMode(DP, OUTPUT);
pinMode(buttonAddPin, INPUT);
pinMode(buttonSubstractPin, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop() {
gearDisplay();
parkingSensor();
}
EDIT: Turns out that pulseIn()'s default duration is 1 second. And it's also a blocking function which blocks the buttons from registering inputs on every loop for 1 second. Changing that to smaller values, for example 10ms in my case, was enough and solved the issue. All credit goes to #CherryDT who solved this. Thanks a lot.
btw this is how the code should look like for 10ms:
pulseIn(echoPin, HIGH, 10000)
// Note that the duration in pulseIn() is in microsconds.
EDIT: Turns out that I misunderstood the question. Anyway, leaving this answer here because it answers "how to blink without blocking" which may be useful too.
The answer for the main problem would be: You are using pulseIn which also blocks until the pulse is received. The default timeout for pulseIn is one second, so if there is no object in view of your parking sensor, you are blocking for 1s every time! Since you are probably only interested in reasonably small distances anyway, you could just set a timeout of, say, 10ms (10000us):
pulseIn(echoPin, HIGH, 10000)
If this doesn't fit your requirements or is still too much of a delay though, consider writing your own code instead which measures the time without blocking, processing the button presses in the meantime. See this answer where someone had the exact same issue and solved it this way.
Basically, you could do something like this:
unsigned long pulseTimer = 0;
int lastEchoState = HIGH; // Initially HIGH so that the actual LOW triggers a "change"
void loop () {
int currentEchoState = digitalRead(echoPin);
// To set a timeout of the pulse, simulate a pulse if already waiting for too long
if (lastEchoState == LOW && micros() - pulseTimer > 1000000ul) {
currentEchoState = HIGH;
}
if (currentEchoState != lastEchoState) {
lastEchoState = currentEchoState;
if (currentEchoState == LOW) {
// Echo pulse over (or it's the initial loop iteration)
// Start counting time until echo
// We do this before sending the trigger because pulseIn would
// normally measure until the pulse is *over*, but for simplicity
// we measure until the pulse *starts*, so this implicitly shifts
// the result by our pulse duration, as trigger and echo pulse are
// usually of the same length
pulseTimer = micros();
// Send new trigger pulse
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
} else {
// Pulse came in, measure time
unsigned long pulseDelayTime = micros() - pulseTimer;
// **** DO SOMETHING WITH pulseDelayTime HERE! ****
}
}
// **** DO YOUR REGULAR BUTTON PROCESSING HERE! ****
}
You would do the blinking by setting the LED output to:
(millis() / 500) % 2
...to blink it, on every loop iteration*.
This would basically be LOW for 500ms and HIGH for 500ms, because dividing millis() by 500 (which won't have any fractional part because we are in integer world here) would give a number that increments every 500ms, and % 2 takes it modulo 2, i.e. it'd be 1 for odd and 0 for even numbers. And 1 and 0 are the same as HIGH and LOW for Arduino.
Example:
+----------+--------------+------------------+
| millis() | millis()/500 | (millis()/500)%2 |
+----------+--------------+------------------+
| ... | | |
| 1499 | 2 | 0 (LOW) |
| 1500 | 3 | 1 (HIGH) |
| 1501 | 3 | 1 (HIGH) |
| ... | | |
| 1999 | 3 | 1 (HIGH) |
| 2000 | 4 | 0 (LOW) |
| 2001 | 4 | 0 (LOW) |
| ... | | |
| 2499 | 4 | 0 (LOW) |
| 2500 | 5 | 1 (HIGH) |
| 2501 | 5 | 1 (HIGH) |
| ... | | |
+----------+--------------+------------------+
Cool project, by the way.
*: If you wanted to squeeze a bit more performance out of this, you could save the last pin state in a separate variable and do digitalWrite only when it changed. That's because digitalWrite is pretty slow actually, so you could avoid calling it every time unless an actual change is needed.
I have a simple RPM Simulator (opensource) that generates values through the tone() function.
I need to send the RPM wirelessly through an nrf24l01 to a second Arduino that uses the RPM as a shiftlight, both come from chippernut.
The tone() function only sends to the pin, trying to read the values did not work.
How can i get the value of x (RPM) after it leaves the tone function?
I have tried reading it through analogRead, digitalRead, BitRead, tried printing the value of x which stays constant, to no avail, and it updates very slowly if it reads the output pin.
This is the code:
float RPM_MIN = 2500;
float RPM_MAX = 8000;
int Accel_Rate = 20;
float pulses_per_rev = 2.0; // make sure to keep the decimal
boolean accel = false;
float x;
long previousMillis = 0;
unsigned long currentMillis=0;
//float RPM_PRINT; //my addition to get a value to print
void setup() {
Serial.begin(57600);
pinMode(5, OUTPUT);
RPM_MIN = (RPM_MIN / 60);
RPM_MAX = (RPM_MAX / 60);
x = RPM_MIN;
}
void loop() {
while (accel==false){
currentMillis = millis();
if(currentMillis - previousMillis > Accel_Rate) {
previousMillis = currentMillis;
x++;
tone(5,x*pulses_per_rev);
if (x>RPM_MAX){accel=true;}
}
}
while (accel==true){
currentMillis = millis();
if(currentMillis - previousMillis > Accel_Rate) {
previousMillis = currentMillis;
x--;
tone(5,x*pulses_per_rev);
if (x<RPM_MIN){accel=false;}
}
}
//RPM_PRINT = x*pulses_per_rev;
//RPM_PRINT = digitalRead(5);
//RPM_PRINT = analogRead(5);
//Serial.println(RPM_PRINT);
}
Expected Result is a Value between 2000-8000 that is constantly changing
actual result is 1/0 or 81,33 or 4,1 or 900-980 updating once every few seconds.
How can I solve?
When reading a JSON string from the serial port on an ESP8266 it cuts off the beginning of the data.
I have tried reading data from the Serial port and printing each character, however it is cutting off part of the begging of the data.
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
}
void loop() {
int curSize = 30;
char* buffer = new char[curSize];
std::fill_n(buffer, curSize, 0);
int pos = 0;
Serial.print("Sending: ");
while(Serial.available() == false) delay(500);
while (Serial.available()) {
char c = Serial.read();
Serial.print(c);
if(pos == curSize-1){
char* newBuffer = increaseBuffer(buffer, curSize, curSize + 30);
curSize += 30;
delete[] buffer;
buffer = newBuffer;
}
if(c == '\n'){
buffer[pos] = 0;
pos = 0;
break;
}
buffer[pos++] = c;
}
if(buffer[0] != 0) {
sendBuffer(buffer);
}
delete[] buffer;
}
char* increaseBuffer(char* orig, int oldSize, int newSize){
char* data = new char[newSize];
std::fill_n(data, newSize, 0);
for(int i = 0; i < newSize; i++){
if(i < oldSize) data[i] = orig[i];
else data[i] = '\0';
}
return data;
}
JSON data used (and expected output)
{"type":0,"ver":"0.0.1","T":[28,29,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
examples of what is actually output
Example 1: 9,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
Example 2: 29,29,29,29,29,29,29,29,29],"H":[59.1608,59.1608,60,59.1608,60,60,60,59.1608,59.1608,59.1608],"DP":[20.36254,20.36254,20.59363,20.36254,20.59363,20.59363,20.59363,20.36254,20.36254],"HI":[30.90588,30.90588,31.0335,30.90588,31.0335,31.0335,31.0335,30.90588,30.90588]}
Try making the delay 1 instead of 500 in the blocking loop that's waiting for data to start coming in. I'm going to guess what happens is that on one iteration of that loop Serial.available() is false and during the delay you start to get data coming in that ends up getting written over by the time your delay ends to check again.
What I'm picturing is the following. If you were to expand out that delay(500) to be delay(1) called 500 times.
while(Serial.available() == false){
delay(1);
delay(1);
// ...
delay(1); // first character comes in
delay(1);
delay(1); // second character comes in
// ...
delay(1); // n character comes in
}
Then after the delay is over you start actually collecting the characters that are coming in.
For a project I´m following this tutorial on how to track the location and output the GPS data using an Arduino with this SIM908 shield. The Arduino correctly sends the GPS data to the database. However, the coordinates are all exactly the same and it seems that they have been rounded off.
For example:
Latitude: 52.216667
Longitude: 5.483333
This isn't because of the PHP script, all it does is put the data it receives in the database. My guess is that it has something to do with the conversion function convert2Degrees.
This is the code we´re running on our Arduino:
int8_t answer;
int onModulePin= 2;
char data[100];
int data_size;
char aux_str[100];
char aux;
int x = 0;
char N_S,W_E;
char url[] = "informatica-corlaer.nl";
char frame[200];
char pin[]="0000";
char apn[]="mmm.nl";
char user_name[]="";
char password[]="";
char latitude[15];
char longitude[15];
char altitude[10];
char date[16];
char time[7];
char satellites[3];
char speedOTG[10];
char course[10];
void setup(){
pinMode(onModulePin, OUTPUT);
Serial.begin(115200);
Serial.println("Starting...");
power_on();
delay(3000);
//sets the PIN code
snprintf(aux_str, sizeof(aux_str), "AT+CPIN=%s", pin);
sendATcommand(aux_str, "OK", 2000);
delay(3000);
// starts the GPS and waits for signal
while ( start_GPS() == 0);
while (sendATcommand("AT+CREG?", "+CREG: 0,1", 2000) == 0);
// sets APN , user name and password
sendATcommand("AT+SAPBR=3,1,\"Contype\",\"GPRS\"", "OK", 2000);
snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"APN\",\"%s\"", apn);
sendATcommand(aux_str, "OK", 2000);
snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"USER\",\"%s\"", user_name);
sendATcommand(aux_str, "OK", 2000);
snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"PWD\",\"%s\"", password);
sendATcommand(aux_str, "OK", 2000);
// gets the GPRS bearer
while (sendATcommand("AT+SAPBR=1,1", "OK", 20000) == 0)
{
delay(5000);
}
}
void loop(){
// gets GPS data
get_GPS();
// sends GPS data to the script
send_HTTP();
delay(5000);
}
void power_on(){
uint8_t answer=0;
// checks if the module is started
answer = sendATcommand("AT", "OK", 2000);
if (answer == 0)
{
// power on pulse
digitalWrite(onModulePin,HIGH);
delay(3000);
digitalWrite(onModulePin,LOW);
// waits for an answer from the module
while(answer == 0){
// Send AT every two seconds and wait for the answer
answer = sendATcommand("AT", "OK", 2000);
}
}
}
int8_t start_GPS(){
unsigned long previous;
previous = millis();
// starts the GPS
sendATcommand("AT+CGPSPWR=1", "OK", 2000);
sendATcommand("AT+CGPSRST=0", "OK", 2000);
// waits for fix GPS
while(( (sendATcommand("AT+CGPSSTATUS?", "2D Fix", 5000) ||
sendATcommand("AT+CGPSSTATUS?", "3D Fix", 5000)) == 0 ) &&
((millis() - previous) < 90000));
if ((millis() - previous) < 90000)
{
return 1;
}
else
{
return 0;
}
}
int8_t get_GPS(){
int8_t counter, answer;
long previous;
// First get the NMEA string
// Clean the input buffer
while( Serial.available() > 0) Serial.read();
// request Basic string
sendATcommand("AT+CGPSINF=0", "AT+CGPSINF=0\r\n\r\n", 2000);
counter = 0;
answer = 0;
memset(frame, '\0', 100); // Initialize the string
previous = millis();
// this loop waits for the NMEA string
do{
if(Serial.available() != 0){
frame[counter] = Serial.read();
counter++;
// check if the desired answer is in the response of the module
if (strstr(frame, "OK") != NULL)
{
answer = 1;
}
}
// Waits for the asnwer with time out
}
while((answer == 0) && ((millis() - previous) < 2000));
frame[counter-3] = '\0';
// Parses the string
strtok(frame, ",");
strcpy(longitude,strtok(NULL, ",")); // Gets longitude
strcpy(latitude,strtok(NULL, ",")); // Gets latitude
strcpy(altitude,strtok(NULL, ".")); // Gets altitude
strtok(NULL, ",");
strcpy(date,strtok(NULL, ".")); // Gets date
strtok(NULL, ",");
strtok(NULL, ",");
strcpy(satellites,strtok(NULL, ",")); // Gets satellites
strcpy(speedOTG,strtok(NULL, ",")); // Gets speed over ground. Unit is knots.
strcpy(course,strtok(NULL, "\r")); // Gets course
convert2Degrees(latitude);
convert2Degrees(longitude);
return answer;
}
/* convert2Degrees ( input ) - performs the conversion from input
* parameters in DD°MM.mmm’ notation to DD.dddddd° notation.
*
* Sign '+' is set for positive latitudes/longitudes (North, East)
* Sign '-' is set for negative latitudes/longitudes (South, West)
*
*/
int8_t convert2Degrees(char* input){
float deg;
float minutes;
boolean neg = false;
//auxiliar variable
char aux[10];
if (input[0] == '-')
{
neg = true;
strcpy(aux, strtok(input+1, "."));
}
else
{
strcpy(aux, strtok(input, "."));
}
// convert string to integer and add it to final float variable
deg = atof(aux);
strcpy(aux, strtok(NULL, '\0'));
minutes=atof(aux);
minutes/=1000000;
if (deg < 100)
{
minutes += deg;
deg = 0;
}
else
{
minutes += int(deg) % 100;
deg = int(deg) / 100;
}
// add minutes to degrees
deg=deg+minutes/60;
if (neg == true)
{
deg*=-1.0;
}
neg = false;
if( deg < 0 ){
neg = true;
deg*=-1;
}
float numberFloat=deg;
int intPart[10];
int digit;
long newNumber=(long)numberFloat;
int size=0;
while(1){
size=size+1;
digit=newNumber%10;
newNumber=newNumber/10;
intPart[size-1]=digit;
if (newNumber==0){
break;
}
}
int index=0;
if( neg ){
index++;
input[0]='-';
}
for (int i=size-1; i >= 0; i--)
{
input[index]=intPart[i]+'0';
index++;
}
input[index]='.';
index++;
numberFloat=(numberFloat-(int)numberFloat);
for (int i=1; i<=10 ; i++)
{
numberFloat=numberFloat*10;
digit= (long)numberFloat;
numberFloat=numberFloat-digit;
input[index]=char(digit)+48;
index++;
}
input[index]='\0';
}
void send_HTTP(){
uint8_t answer=0;
// Initializes HTTP service
answer = sendATcommand("AT+HTTPINIT", "OK", 10000);
if (answer == 1)
{
// Sets CID parameter
answer = sendATcommand("AT+HTTPPARA=\"CID\",1", "OK", 5000);
if (answer == 1)
{
// Sets url
sprintf(aux_str, "AT+HTTPPARA=\"URL\",\"http://%s/vehicleLocationTransmitter.php?", url);
Serial.print(aux_str);
sprintf(frame, "vehicleID=1&latitude=%s&longitude=%s&altitude=%s&time=%s&satellites=%s",
latitude, longitude, altitude, date, satellites);
Serial.print(frame);
answer = sendATcommand("\"", "OK", 5000);
if (answer == 1)
{
// Starts GET action
answer = sendATcommand("AT+HTTPACTION=0", "+HTTPACTION:0,200", 30000);
if (answer == 1)
{
Serial.println(F("Done!"));
}
else
{
Serial.println(F("Error getting url"));
}
}
else
{
Serial.println(F("Error setting the url"));
}
}
else
{
Serial.println(F("Error setting the CID"));
}
}
else
{
Serial.println(F("Error initializating"));
}
sendATcommand("AT+HTTPTERM", "OK", 5000);
}
int8_t sendATcommand(char* ATcommand, char* expected_answer1, unsigned int timeout){
uint8_t x=0, answer=0;
char response[100];
unsigned long previous;
memset(response, '\0', 100); // Initialize the string
delay(100);
while( Serial.available() > 0) Serial.read(); // Clean the input buffer
Serial.println(ATcommand); // Send the AT command
x = 0;
previous = millis();
// this loop waits for the answer
do{
if(Serial.available() != 0){
response[x] = Serial.read();
x++;
// check if the desired answer is in the response of the module
if (strstr(response, expected_answer1) != NULL)
{
answer = 1;
}
}
// Waits for the asnwer with time out
}
while((answer == 0) && ((millis() - previous) < timeout));
return answer;
}
Write a test case for the function in doubt (convert2Degrees())
from the comment: "parameters in DD°MM.mmm’ notation to DD.dddddd°":
If you input: 52°27.123
Then the expected Output should be: 52.45205
Calculation: 52°27.123 = 52 + 27.123/60.0 =
= 52.45205
Further you should post here the value which is input to convert2Degrees()
Your use of strtok is incorrect, and the convert2degrees has major problems.
Here's something derived from NeoGPS that doesn't use the expensive (on AVRs) divide or modulo arithmetic:
//.................................................
// A special divide-by-3 function that avoids division.
// From http://www.hackersdelight.org/divcMore.pdf
static uint32_t divu3( uint32_t n )
{
uint32_t q = (n >> 2) + (n >> 4); // q = n*0.0101 (approx).
q = q + (q >> 4); // q = n*0.01010101.
q = q + (q >> 8);
q = q + (q >> 16);
uint32_t r = n - q*3; // 0 <= r <= 15.
return q + (11*r >> 5); // Returning q + r/3.
}
//------------------------------------------------------------
// Parse the NMEA "DDDMM.mmmm" format for lat and lon.
//
// returns degrees * 10^7
uint32_t parseDDMMmmmm( char *input )
{
uint8_t digits = 0;
uint8_t sixth_digit = 0;
char chr;
// Find the decimal point
while (isdigit( input[digits] ))
digits++;
// All but the last two are degrees.
uint32_t val = 0;
while (digits > 2) {
chr = *input++;
val = val*10 + (chr - '0');
digits--;
}
// convert from degrees to minutes
val *= 60;
// Add in the last 2 minutes digits
uint8_t minutes = 0;
while (digits > 0) {
chr = *input++;
minutes = minutes*10 + (chr - '0');
digits--;
}
val += minutes;
// Decimal point?
chr = *input++;
if (chr == '.') {
chr = *input++;
// Parse up to 6 digits of the fractional minutes.
while ((digits++ < 5) && isdigit( chr )) {
val = val*10 + (chr - '0');
chr = *input++;
}
if ((digits == 6) && isdigit(chr)) {
sixth_digit = chr - '0';
digits++;
}
// Scale the value up to minutes x 1000000.
while (digits < 4) {
val *= 10;
digits++;
}
}
// convert from minutes x 1000000 to degrees x 10000000.
val += divu3( val*2 + 1 ); // same as 10 * (val+30)/60 without truncation
if (digits >= 6) {
if (sixth_digit >= 9)
val += 2;
else if (sixth_digit >= 4)
val += 1;
}
return val;
} // parseDDMMmmmm
...and a floating-point version:
double parseDDMMmmmm_f( char *input )
{
uint8_t digits = 0;
char chr;
// Find the decimal point
while (isdigit( input[digits] ))
digits++;
// All but the last two are degrees.
double val = 0.0;
while (digits > 2) {
chr = *input++;
val = val*10 + (chr - '0');
digits--;
}
// convert from degrees to minutes
val *= 60;
// Add in the last 2 minutes digits
uint8_t minutes = 0;
while (digits > 0) {
chr = *input++;
minutes = minutes*10 + (chr - '0');
digits--;
}
val += minutes;
// Decimal point?
chr = *input++;
if (chr == '.') {
chr = *input++;
// Parse the fractional "mmmm" minutes.
while (isdigit( chr ) && (digits++ <= 4)) {
val = val*10 + (chr - '0');
chr = *input++;
}
// Scale the value up to minutes x 1000000.
while (digits < 4) {
val *= 10;
digits++;
}
}
// convert from minutes x 1000000 to degrees.
val *= 10.0/1000000.0 * 1.0/60.0;
return val;
} // parseDDMMmmmm_f
If you use these functions, you also need to pop the '-' off the front of your AT string, and negate the returned values:
bool south = (latitude[0] == '-');
if (south)
latitude++;
float lat = parseDDMMmmmm_f( latitude );
if (south)
lat = -lat;
convert2Degrees(longitude);
bool west = (longitude[0] == '-');
if (west)
longitude++;
float lon = parseDDMMmmmm_f( longitude );
if (west)
lon = -lon;[/code]