It seems that you are trying to do a lot without much idea of how you are going to achieve it. Many times the advice has been given here (not least by me) that to eat an elephant you must do it in small bites.
So I would suggest that you break up this mega-project of yours into smaller chunks and make sure you can do each part before trying to replicate NASA headquarters using an Arduino UNO.
What you are trying to do is, in itself, not difficult, but the entire project is difficult for you, a beginner. So do it slowly and vielleicht wirst du Erfolg haben.
Oh, and watch all my videos on YouTube, they are designed for noobs who want to progress their projects, URL in the footer of this post! And subscribe too.
Du kannst in der Abteilung "Deutsch" auch auf Deutsch fragen
First figure out what can run in parallel. If you already wrote sketches for these parts, convert them into functions (setupXYZ(), handleXYZ()...), which can be added to and called in the setup() and loop() of a new "main" sketch. You also can put each function in its own *.cpp and *.h file, but I'd keep that option for later.
You also can insert the setup code parts directly into setup(), for better overview (duplicate pins...).
The rest depends on you specific code and requirements.
thanks for reply. Maybe I explained it not correct.
I wrote a procedure for reading temperature and it works well. Then I wrote a procedure to handle a dosing pump and it wokrs well. The same to display menues, and to read and react to input by touch screen.
Every single "program" works. Now I need to programm a "control structure" which runs in a kind of loop and starts from time to time each procedure and reacts to input by touch screen or if a temperature has changed.
This should run nearly simultaneously. So I thought a kind of State machine can be the right way.
Robin,
thanks,
I think your post is a good example. I'll replace "person in room == true" by a time based check like
if (wakeUpPumpNextTime >= lastWakeUpPumpTime + pumpInterval() {
if (pumpOn == false) {
pumpon();
}
if (readTempNextTime >= lastReadTempNextTime + TempInterval(){
if (temperatureRead == false) {
readTemp();
{
...
Guido_Ov:
this is exactly what I try to do. I converted all single sketches into functions and declare all variables, binding libs and stuff
In section setup(), I declare all needed stuff but in loop() section I have some issues to do things nearly simultaneously.
True parallel execution is impossible on a single-core controller. Don't expect something like threads or other woodoo, it's much simpler.
Instead call all your worker functions in loop(), and let the functions return as soon as possible. This way every function will execute thousands of times every second, what's sufficient for most tasks.
E.g. when you want to measure a temperature every second, check the current time and read a new value only if time has come to do so. Otherwise return immediately, so that the next task (function) can execute.
Guido_Ov:
but in loop() section I have some issues to do things nearly simultaneously.
I missed that earlier.
The "Planning" tutorial that I linked to earlier illustrates the same technique as in the demo Several Things at a Time which is the same concept as DrDiettrich mentioned.
Maybe a simple example might help. Assume a room with an air conditioner. During the day from 06:00 to 20:00 you want to keep the temperature between 18 and 22 degrees (C). During the night you don't care if it goes to minus 10 or to plus 35.
// statemachine states
#define ST_IDLE 0
#define ST_WARMUP 1
#define ST_COOLDOWN 2
// output pins
#define HEATER 4
#define COOLER 5
// variables
int currentState = ST_IDLE;
int currentTemperature;
void setup() {
// put your setup code here, to run once:
}
void loop() {
currentTemperature = readTemp();
switch (currentState)
{
case ST_IDLE:
doIdle();
break;
case ST_WARMUP:
doWarmup();
break;
case ST_COOLDOWN:
doCooldown();
break;
}
}
void doIdle()
{
// the current time is only used in this function
// so it is declared and set here
// you can also make it global and set it in loop() if you need the time elsewhere
// the 'int' type is just an exampe
int currentTime = readTime();
// check the time limits
if (currentTime < 06: 00 || currentTime > 20: 00)
{
// nothing to do; stay in current state
return;
}
// if too cold
if (currentTemperature < 18)
{
// switch to warm up
currentState = ST_WARMUP;
}
// if too hot
if (currentTemperature > 22)
{
// switch to cool down
currentState = ST_COOLDOWN;
}
}
void doWarmup()
{
// in case cooler is on, switch it off
digitalWrite(COOLER, LOW);
// switch heater on
digitalWrite(HEATER, HIGH);
if (currentTemperature > 20)
{
// switch heater off
digitalWrite(HEATER, LOW);
currentState = ST_IDLE;
}
}
void doCooldown()
{
// in case heater is on, switch it off
digitalWrite(HEATER, LOW);
// switch cooler on
digitalWrite(COOLER, HIGH);
if (currentTemperature < 20)
{
// switch cooler off
digitalWrite(COOLER, LOW);
currentState = ST_IDLE;
}
}
This is just an example to give you the idea. Each state has an associated function. Those functions can change the 'state of the program' if certain conditions are met.
I know that Arduino is not able to do real multitasking. It is just nearly... but this is ok for me.
I need to activate the pump from time to time (60s to maybe 30s). Read temperature every 2 seconds and to check for Touch input from time to time as well as update the TFT.
I think that is not really a problem.
My personal issue is to get an overview about all those "prevMillis", "Interval" and other variables and when to change what...
Let me say "Brain jogging". This is my training I've to do.
I'm very happy, my state machine works fine now. Touch screen, Menue, Pump and Temperatur are running and doing things...
Thanks to Robin2 your links are great and helped a lot.
Thanks to all others! I got a bunch of idaes how to solve issues. Thank you very much.
My next issue is, that, I try to read temperature from 6 IR-Sensors MLX90614 but in combination with state machine and it won't run.
This is the code I found somewhere here:
#include <i2cmaster.h>
void setup()
{
Serial.begin(115200);
// Setup I2CBus
i2c_init(); //Initialise then,
PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
delay(2000);
}
void loop()
{
float temperature1 = readDevice(0x5A<<1); // addresses from the scanner
float temperature2 = readDevice(0x5B<<1);
float temperature3 = readDevice(0x5C<<1);
float temperature4 = readDevice(0x5D<<1);
float temperature5 = readDevice(0x5E<<1);
float temperature6 = readDevice(0x5F<<1);
// Print Data To Screen...
Serial.println("Temperature Readings (*c):");
Serial.print("> FWL (90): ");
Serial.println(temperature1);
Serial.print("> FWM (91): ");
Serial.println(temperature2);
Serial.print("> FWR (92): ");
Serial.println(temperature3);
Serial.print("> RWL (93): ");
Serial.println(temperature4);
Serial.print("> RWM (94): ");
Serial.println(temperature5);
Serial.print("> RWR (95): ");
Serial.println(temperature6);
// delay(2000);
}
/////////////////////////////////////////////////////////
//
// helper functions
//
float readDevice(int address){
// Serial.println("Read Device");
int dev = address;
int data_low = 0;
int data_high = 0;
int pec = 0;
// Serial.print("Device Address :");
// Serial.println(dev);
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
//Process tempData
float objTemp = tempData - 273.15;
delay(500);
return objTemp;
}
So far this Project runs fine. But when I try to run this code in my state driven project, It won't work.
I tried this:
int mSensAddress[6] = {0x5A,0x5B,0x5C,0x5D,0x5E,0x5F}; // Addresses of IR Sensors for I2C bus
float mTemp[6]; // Array to store 6 temmp values
int mSensors = 6; // For Next counter
int dev; // temporarilly stores a single mSensAddress based on i
int data = 0;
int pec = 0;
unsigned long prevmTireTempMillis = 0;
unsigned long mTireTempBaseInterval = 1000;
byte mTempState = 0;
void Setup(){
Serial.begin(115200);
// Init I2C Bus
i2c_init(); // Initialize the I²C bus
PORTC = (1 << PORTC4) | (1 << PORTC5); // Enable pullups
delay(500);
}
void loop (){
getTemp();
}
void getTemp() {
Serial.println ("getTemp process");
Serial.print ("State :");
Serial.println (mTempState);
if (mTempState == 0) {
// if the Temperature process is stopped, we must wait for the interval to expire before turning it on
if (currentMillis - prevmTempMillis >= mTempBaseInterval) {
prevmTempMillis += mTempBaseInterval;
mTempState = ! mTempState;
}
if (mTempState == 1){
Serial.println ("TempState = 1");
// Do Temperature stuff
// Calling sensor
for (int i=0; i < mSensors;i++) {
int dev = mSensAddress[i];
int data_low = 0;
int data_high = 0;
int pec = 0;
Serial.print("Device Address :");
Serial.println(dev);
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
//Process tempData
mTemp[i] = tempData - 273.15;
delay(500);
}
}
mTempState = ! mTempState;
}
}
The Idea is to start a function called "getTemp();" if the state StateTemp = 1.
In this function I set some variables and the "For next loop" shall "walk" through the adress array "mSensAddress_", read the temperature of each sensor and stores those values in array "mTemp*"._ Then it is easy to find the right temperature to display. On serial Monitor I get: Device Address : 90 That means to me, thr code runs until this:
_```_
// Calling sensor
for (int i=0; i < mSensors;i++) {
int dev = mSensAddress[i];
int data_low = 0;
int data_high = 0;
int pec = 0;
Serial.print("Device Address :");
Serial.println(dev);
_*_ _*... and this sequence which should communicate via I2C won't work...*_ _*_
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
_```*_ I placed som Serial.println(data_low); but no information on serial monitor... Guido
Guido_Ov:
you are right. Sorry it was a bit late...
Maybe it's too early for me but I can't make sense of this. And you are still using the words "won't work" without any detailed explanation of what they mean.
Have you written a short program to read the temp sensor in the simplest possible way? If so post that program.
this code works fine. Every 500 millis the program displays a Block of 6 temperature values of my six sensores.
I renamed them with six unique addresses that the I²C can talk to each one.
#include <i2cmaster.h>
void setup()
{
Serial.begin(115200);
// Setup I2CBus
i2c_init(); //Initialise then,
PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
delay(2000);
}
void loop()
{
float temperature1 = readDevice(0x5A<<1); // addresses from the scanner
float temperature2 = readDevice(0x5B<<1);
float temperature3 = readDevice(0x5C<<1);
float temperature4 = readDevice(0x5D<<1);
float temperature5 = readDevice(0x5E<<1);
float temperature6 = readDevice(0x5F<<1);
// Print Data To Screen...
Serial.println("Temperature Readings (*c):");
Serial.print("> FWL (90): ");
Serial.println(temperature1);
Serial.print("> FWM (91): ");
Serial.println(temperature2);
Serial.print("> FWR (92): ");
Serial.println(temperature3);
Serial.print("> RWL (93): ");
Serial.println(temperature4);
Serial.print("> RWM (94): ");
Serial.println(temperature5);
Serial.print("> RWR (95): ");
Serial.println(temperature6);
// delay(2000);
}
/////////////////////////////////////////////////////////
//
// helper functions
//
float readDevice(int address){
// Serial.println("Read Device");
int dev = address;
int data_low = 0;
int data_high = 0;
int pec = 0;
// Serial.print("Device Address :");
// Serial.println(dev);
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
//Process tempData
float objTemp = tempData - 273.15;
delay(500);
return objTemp;
}
[\code]
Ok, now I'm trying to adapt this to my state driven program.
All the setup stuff of the code above I copied into setup section of my main program.
All this works until it runs into section
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
Here the programm stops.
I placed a Serial.print("i2c_start_wait_done"); after the command i2c_start_wait(dev + I2C_WRITE);
But I didn't get a print on serial monitor.
That means to me the command i2c_start_wait(dev + I2C_WRITE); was not processed...
This code as single project runs and read from 6 sensors.
#include <i2cmaster.h>
void setup()
{
Serial.begin(115200);
// Setup I2CBus
i2c_init(); //Initialise then,
PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
delay(2000);
}
void loop()
{
float temperature1 = readDevice(0x5A<<1); // addresses from the scanner
float temperature2 = readDevice(0x5B<<1);
float temperature3 = readDevice(0x5C<<1);
float temperature4 = readDevice(0x5D<<1);
float temperature5 = readDevice(0x5E<<1);
float temperature6 = readDevice(0x5F<<1);
// Print Data To Screen...
Serial.println("Temperature Readings (*c):");
Serial.print("> FWL (90): ");
Serial.println(temperature1);
Serial.print("> FWM (91): ");
Serial.println(temperature2);
Serial.print("> FWR (92): ");
Serial.println(temperature3);
Serial.print("> RWL (93): ");
Serial.println(temperature4);
Serial.print("> RWM (94): ");
Serial.println(temperature5);
Serial.print("> RWR (95): ");
Serial.println(temperature6);
// delay(2000);
}
/////////////////////////////////////////////////////////
//
// helper functions
//
float readDevice(int address){
// Serial.println("Read Device");
int dev = address;
int data_low = 0;
int data_high = 0;
int pec = 0;
// Serial.print("Device Address :");
// Serial.println(dev);
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
//Process tempData
float objTemp = tempData - 273.15;
delay(500);
return objTemp;
}
But if I try to adapt the code to my state driven project, it stops at the beginning of RAW READ.
The command i2c_start will not processed...
I programmed this. The idea is to use a For next loop six times an get adresses from array mSensAddress and store results (temperature) in array mTemp My idea is this: ``` *int mSensAddress[6] = {0x5A,0x5B,0x5C,0x5D,0x5E,0x5F}; // Addresses of IR Sensors for I2C bus
float mTemp[6]; // Array to store 6 temmp values
int mSensors = 6; // For Next counter
int dev; // temporarilly stores a single mSensAddress based on i
int data = 0;
int pec = 0;
unsigned long prevmTireTempMillis = 0;
unsigned long mTireTempBaseInterval = 1000;
byte mTempState = 0;
void Setup(){
Serial.begin(115200);
// Init I2C Bus
i2c_init(); // Initialize the I²C bus
PORTC = (1 << PORTC4) | (1 << PORTC5); // Enable pullups
if (mTempState == 0) {
// if the Temperature process is stopped, we must wait for the interval to expire before turning it on
if (currentMillis - prevmTempMillis >= mTempBaseInterval) {
prevmTempMillis += mTempBaseInterval;
mTempState = ! mTempState;
}
if (mTempState == 1){
Serial.println ("TempState = 1");
// Do Temperature stuff
// Calling sensor
for (int i=0; i < mSensors;i++) {
int dev = mSensAddress[i];
int data_low = 0;
int data_high = 0;
int pec = 0;
Serial.print("Device Address :");
Serial.println(dev);
// RAW READ
i2c_start_wait(dev + I2C_WRITE);
i2c_write(0x07);
i2c_rep_start(dev + I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
}
}
_```*_ Do you have an idea what I'm doing wrong? Do I need to slow down the For next loop? To give the i2c more time to communicate? Thanks in advance Guido