#c #multithreading #arduino #arduino-uno
#c #многопоточность #arduino #arduino-uno
Вопрос:
Я пытаюсь использовать millis
, потому что мне нужно одновременно запускать несколько циклов. Один для получения значения GPS и температуры, затем отправляет каждое значение на номер телефона пользователя при получении запроса, а другой для отображения этих значений на OLED. Но мои функции, похоже, блокируют друг друга, даже несмотря на то, что я использую millis
. Это мой код:
инициализация
#include<TinyGPS .h>
#include <SoftwareSerial.h>
#include "DHT.h"
#include <Adafruit_ssd1306syp.h>
Adafruit_ssd1306syp display(A4,A5);
//dht
#define DHTPIN 5
#define DHTTYPE DHT11
//gps
const double Home_LAT = -6.1887;
const double Home_LNG = 106.7;
float glatitude;
float glongitude;
//sms
char ReceivedSms;
short DHT_OK=-1, GPS_OK=-1;
String DataSms;
//dht
int nilaiSuhu;
int nilaiKelembaban;
//millis
unsigned long counting_millis;
unsigned long millis_now;
const unsigned long delay_val = 2000;
const unsigned long delay_val2 = 1000;
TinyGPSPlus gps;
DHT dht(DHTPIN, DHTTYPE);
SoftwareSerial sim800l(2,3);
пустая настройка
void setup(){
sim800l.begin(9600);
Serial.begin(9600);
dht.begin();
Serial.println("Starting..");
delay(7000);
ReceiveMode();
display.initialize();
display.clear();
}
цикл void
void loop() {
millis_now = millis();
if(millis_now - counting_millis > delay_val)
{
oled_Display();
counting_millis = millis_now;
}
if(millis_now - counting_millis > delay_val2){
mergeFunction();
counting_millis = millis_now;
}
}
oled_Display () — это функция для отображения каждого значения на OLED
void oled_Display() {
display.clear();
display.setCursor(0,0);
float nilaiSuhu = dht.readTemperature();
float nilaiKelembaban = dht.readHumidity();
display.print("Suhu : ");
display.println(nilaiSuhu);
display.print("Kelembaban: ");
display.println(nilaiKelembaban);
display.print("Latitude : ");
display.println(gps.location.lat(), 5);
display.print("Longitude : ");
display.println(gps.location.lng(), 4);
display.print("Satellites: ");
display.println(gps.satellites.value());
display.print("Speed : ");
display.println(gps.speed.mph());
unsigned long Distance_To_Home = (unsigned long)TinyGPSPlus::distanceBetween(gps.location.lat(),gps.location.lng(),Home_LAT, Home_LNG);
display.print("KM asal :"); // Have TinyGPS Calculate distance to home and display it
display.print(Distance_To_Home);
display.update();
}
mergeFunction() — это функция для получения значения GPS и температуры и отправки его пользователю на основе запроса.
void mergeFunction(){
nilaiSuhu = dht.readTemperature();
nilaiKelembaban = dht.readHumidity();
String RSMS;
while(sim800l.available() > 0) {
ReceivedSms = sim800l.read();
Serial.print(ReceivedSms);
RSMS.concat(ReceivedSms);
DHT_OK = RSMS.indexOf("Suhu");
GPS_OK = RSMS.indexOf("All");
}
while(Serial.available() > 0){
if(gps.encode(Serial.read()))
readGPS();
}
if(DHT_OK!=-1){
Serial.println("DHT found");
Serial.print("Temperature = ");
Serial.print(nilaiSuhu);
Serial.print("*C Humidity = ");
Serial.print(nilaiKelembaban);
Serial.println(" %");
DataSms = "DHT11nSuhu = " String(nilaiSuhu,1) " C" " nKelembaban =" String(nilaiKelembaban,1) " %";
SendData();
ReceiveMode();
DHT_OK=-1;
}
if(gps.location.isValid())
{
glatitude = gps.location.lat();
glongitude = gps.location.lng();
String link = "http://www.google.com/maps/place/" String(glatitude,6) "," String(glongitude,6);
DataSms = "ALLnSuhu = " String(nilaiSuhu,1) " C" " nKelembaban =" String(nilaiKelembaban,1) " %" " nMaps = " link;
if(GPS_OK!=-1)
{
SendData();
ReceiveMode();
GPS_OK = -1;
}
}
else
{
if(GPS_OK!=-1)
{
DataSms = "ALLnSuhu = " String(nilaiSuhu,1) " C" " nKelembaban =" String(nilaiKelembaban,1) " %" " nMaps = " "Lokasi tidak ditemukan";
SendData();
ReceiveMode();
GPS_OK = -1;
}
}
}
Обе эти функции работают отлично, когда они не выполняются одновременно. Я не знаю, почему обе мои функции блокируют друг друга, когда они выполняются одновременно, даже если я использую millis
. Как правильно запустить многозадачность Arduino? Просветите меня.
Комментарии:
1. Конечно, функции могут выполняться параллельно, только если вы запустили их в отдельных потоках. Но вы не создали никаких потоков, и Arduino Uno также не может запускать несколько потоков (по крайней мере, не так просто, как вы ожидали). Вам нужно создать для них таймеры
2. кроме того, вам может повезти с Arduino
3. недостаточно ли использовать millis, как в моем цикле void?
4. нет. нет ничего, что заставляет 2 блока if работать параллельно. Сначала будет запущен первый, а затем второй
5. ваша ошибка заключается в том, что вы сбрасываете время начала каждый раз для всех. вам нужна отдельная
counting_millis
для каждой функции. и в функциях не должно быть цикла while. обработайте то, что доступно, и верните. продолжайте читать входные данные при следующем вызове функции.
Ответ №1:
Инструкции в C всегда выполняются последовательно сверху вниз. Как вы можете ожидать, что 2 функции будут выполняться параллельно, если они ничем не отличаются от других функций?
oled_Display();
mergeFunction(); // this will be run only after the previous statements have finished
Если это так, то setup()
or loop()
также выполняются параллельно. В обычной одиночной программе параллельно выполняются только потоки. Однако в крошечной встроенной системе, такой как Arduino, многопоточность не поддерживается, поскольку переключение контекста является очень дорогостоящим. Посмотрите, как я могу создать несколько запущенных потоков?
На компьютерах вы используете прерывания для ожидания событий вместо опроса, подобного тому, который тратит много циклов. Одно из прерываний для своевременного запуска называется таймером. В Arduino вы можете использовать arduino-timer. Просто установите 2 таймера для 2 процедур и дождитесь их запуска
Ответ №2:
Как упоминал @Juraj, вам нужна независимая counting_millis
переменная для каждой функции, которую вы пытаетесь вызвать.
Основной проблемой здесь является неправильное представление о том, что:
Мне нужно одновременно запускать несколько циклов
что невозможно с микроконтроллером AVR. С помощью функции можно выполнять многозадачность millis()
, однако это не означает, что оба процесса будут выполняться одновременно.
Если вы не перенесете свой код в RTOS, который выполняется на основе семафоров и очередей (но не параллельно), вы не сможете добиться такого распараллеливания. Сказав это, вам может потребоваться переосмыслить свою проблему с точки зрения приоритетов.
Я бы лично распечатал данные на OLED после обработки данных с GPS. Это полностью основано на недавнем опыте разработки сложной системы, которая запрашивает множество датчиков, пользовательских вводов и последовательной связи. Последнее, с чем вы хотите иметь дело, — это OLED, поскольку наиболее важной частью (я полагаю) является отображение соответствующих данных.
Я предлагаю обновлять экран каждые 40 (-иш) миллисекунд, что соответствует приблизительной минимальной частоте обновления 24 кадра в секунду, что человеческий глаз может воспринимать как плавное движение. Это не учитывает время, затрачиваемое библиотекой на отправку данных на OLED. Однако, в конце концов, вы не увидите никакого влияния на вашу производительность, если вы это сделаете.
Комментарии:
1. Я пытался использовать RTOS, но все равно не работает. Я думаю, что это также вызвано ограничением объема памяти моего MCU (Arduino uno), у него всего 2 КБ оперативной памяти, и вся программа потребляла более 50% места для хранения. Независимо от размера OLED-модуля, драйвер SSD1306 имеет встроенную память графического отображения данных объемом 1 КБ (GDDRAM) для экрана, который содержит отображаемый битовый шаблон. Я собираюсь предположить, что моему OLED не хватает памяти для буферизации всего дисплея.
2. Что я сделал, так это изменил свою библиотеку для запуска OLED и создал расписание с использованием библиотеки простого таймера. Теперь вся программа просто потребляла 47% дискового пространства. Да, он работает в течение нескольких минут, а затем зависает: D. Я думаю, мне нужно сменить свой MCU