text:
Heltec WiFi LoRa 32 (V3) - Encode/decode packets to binary (make message shorter)
I'm using Heltec WiFi LoRa32 boards (ESP32-S3 + SX1262).
Could you please help me how to encode data e.g. from temperature and humidity sensor into binary or hexadecimal form - to make the LoRa packet length shorter? Similar to what LoRaWAN does.
For example, I have two values:
float temperature = 21.56;
float humidity = 76.21;

I use the official Heltec library for LoRa radio, I only transfer data via LoRa (not LoRaWAN), so it is necessary to make it work with the library function Radio.Send().
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) ); //send the package out

I found this example and this tutorial.
I'm confused in this, so I would appreciate a parto of code example compatible with Heltec library for encoding and decoding compatible with Heltec library functions.

21.56 can be represented by two bytes as 2156

If you want to send both temperature and humidity in a single packet,
the easiest way is to define the packet as a struct with two fields:
struct LoraData {
    float temperature;
    float humidity;
};

You can then send the raw bytes by typecasting the address of such a
struct to uint8_t*. This, however, is generally not recommended,
because it violates C++ type aliasing rules. You can circumvent this
issue by defining a union:
union LoraPacket {
    LoraData data;
    uint8_t bytes[sizeof(LoraData)];
};

This represents an area of memory that can be interpreted as containing
either the numeric data data, or an array of bytes. Then you can fill
the data field and send the raw bytes:
void send_dummy_data()
{
    LoraPacket packet = {
        .data = {
            .temperature = 12.56,
            .humidity = 76.21
        }
    };
    Radio.Send(packet.bytes, sizeof packet);
}

At the other end, you do the same thing backwards: you fill in the bytes
and read back the numeric data:
void rx_done(uint8_t *payload, uint16_t size, int16_t, int8_t)
{
    if (size != sizeof(LoraPacket)) {  // sanity check
        Serial.println("Bad packet size");
        return;
    }
    LoraPacket packet;
    memcpy(&packet.bytes, payload, size);
    Serial.print("temperature: ");
    Serial.println(packet.data.temperature);
    Serial.print("humidity: ");
    Serial.println(packet.data.humidity);
}

Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );

Noted that strlen(txpacket) is only applicable if the txpacket is a string. Since temperature is a floating point value, it should be sizeof(temperature)(see further explanation below).
I think there is a misunderstanding that this send a "shorter" data because you saw the uint8_t, but it is not the case. A single-precision floating point data is stored as a 4-byte data in according to IEEE754 format. What the RadioSend((uint8_t *) temperature, sizeof(temperature)) does is it simply cast the floating point value to a pointer of byte (i.e. uint8_t) that is point to the beginning of the temperature in memory so that it can send the data byte-by-byte, the sizeof(temperature) is equivalent to sizeof(float) so it is equal to 4. The data that going through the radio transmission is still 4-byte floating point value.
To send the temperature and humidity, you just need to send the temperature and humidity data as:
float temperature = 21.56;
float humidity = 76.21;

RadioSend((uint8_t *) temperature, sizeof(temperature));
RadioSend((uint8_t *) humidity, sizeof(humidity));

However, as point out by @jsotola, if you want, there is a way to reduce the data to be sent from 4-byte floating point to 2-byte int16_t by converting the float to int16_t by multiply the floating point value with 100. The converted data is only 2-byte long as an int16_t without losing the precision of the floating point (which has precision of 2 decimal points).
float temperature = 21.56;
float humidity = 76.21;

int16_t temperature_in_integer = (int16_t) temperature * 100; // integer 2156
int16_t humidity_in_integer = (int16_t) humidity * 100;  // integer 7621

// send 2-byte each
RadioSend((uint8_t *) temperature_in_integer, sizeof(temperature_in_integer));
RadioSend((uint8_t *) humidity_in_integer, sizeof(humidity_in_integer));

On the receiving end, the received data need to be divided by 100 to get back the actual temperature and humidity value.
If this is a little confuse to you, I would suggest you pick up a book of C or C++ and learn the foundation of  data types, data casting and pointer.

Glad to hear that, if this helps, consider to accept it as answer by clicking the "tick" sign.

But what about if I want to send the values in one packet - how to pack it? Especially when on some nodes I measure different kind of values?
And how to deal with the received data on the receiver's side?
Thanks

You don't need to purposely pack it, it send 4+4 bytes. Personally I don't know how internally the RadioSend and Lora handle the data, so I can' really answer that. For receiving, the int16_t data is quite straightforward. For float, you can read my answer to this question on how to read back the float from the received buffer.

I need to send all values in one packet and then on the receiver's side decode it and seperate the palyoad to individual values. Is this possible?

Come to think about it, You can create an array of float (or a struct of float) and send in 8-byte all together. array[0] be the temperature value and array[2] be the humidity value.

I also has an example on my blog which show pack temperature and humidity in a struct and send over transmission. You can take a look at that. If you have further question, you should raise in a separate question, the StackOverflow/StackExchange is better for one question at a time. Not keep adding questions when one has been answered.

Ok, and is it possible to somehow ensure that when measuring different variables to determine which variable is in the packet at a given position or do I just need to know?
For example, if I send temperature and humidity from one device and pressure, voltage and current from another?
Or just have a position for each variable in the packet, and in case it will not be measured leave for example zero?
Thanks

The CayenneLPP library works well for this. See the examples provided.


text:
Why are outputs (altitude and vertical velocity) still unstable (goes to negative infinity) after 2d Kalman filtering GY86 data?
I am using MS5611 in GY86 and ESP32 Dev kit to get the vertical velocity and altitude after running a 2d Kalman filter to it. (I modified the code from episode 19 of Carbon Aeronautics drone playlist). The results however are very bad, I couldn't reach those 2 values around 0, the integral summing is so bad the plot is a straight down line to infinity. When I lift the sensor up, the values are recorded but returns to 0 slowly, very slow. I tried tuning the Kalman filter by adjusting Q, R and Kalman gain, but it won't turn stable like in Carbon Aeronautic's video 1.
#include <Wire.h>
#include <MS5611.h>
float RateRoll, RatePitch, RateYaw;
float AngleRoll, AnglePitch;
float AccX, AccY, AccZ;
float AccZInertial;
float LoopTimer;
float AltitudeBarometer, AltitudeBarometerStartUp;
int RateCalibrationNumber;
#include <BasicLinearAlgebra.h>
using namespace BLA;
float AltitudeKalman, VelocityVerticalKalman;
BLA::Matrix<2,2> F; BLA::Matrix<2,1> G;
BLA::Matrix<2,2> P; BLA::Matrix<2,2> Q;
BLA::Matrix<2,1> S; BLA::Matrix<1,2> H;
BLA::Matrix<2,2> I; BLA::Matrix<1,1> Acc;
BLA::Matrix<2,1> K; BLA::Matrix<1,1> R;
BLA::Matrix<1,1> L; BLA::Matrix<1,1> M;
BLA::Matrix<1,1> inv_L = Inverse(L);
MS5611 ms5611;
void kalman_2d(void){
  Acc = {AccZInertial};
  S=F*S+G*Acc;
  P=F*P*~F+Q;
  L=H*P*~H+R;
  K=P*~H*inv_L;
  M={AltitudeBarometer};
  S=S+K*(M-H*S);
  AltitudeKalman=S(0,0);
  VelocityVerticalKalman=S(1,0);
  P=(I-K*H)*P;
}
double referencePressure;
void checkSettings()
{
  Serial.print("Oversampling: ");
  Serial.println(ms5611.getOversampling());
}
void baro(void){
  long realPressure = ms5611.readPressure();
  float AltitudeBarometer = ms5611.getAltitude(realPressure, referencePressure) * 100;
}
void gyro_signals(void) {
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);
  Wire.write(0x05);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);
  Wire.write(0x10);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x3B);
  Wire.endTransmission();
  Wire.requestFrom(0x68,6);
  int16_t AccXLSB = Wire.read() << 8 | Wire.read();
  int16_t AccYLSB = Wire.read() << 8 | Wire.read();
  int16_t AccZLSB = Wire.read() << 8 |  Wire.read();
  Wire.beginTransmission(0x68);
  Wire.write(0x1B);
  Wire.write(0x8);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x43);
  Wire.endTransmission();
  Wire.requestFrom(0x68,6);
  int16_t GyroX=Wire.read()<<8 | Wire.read();
  int16_t GyroY=Wire.read()<<8 | Wire.read();
  int16_t GyroZ=Wire.read()<<8 | Wire.read();
  RateRoll=(float)GyroX/65.5;
  RatePitch=(float)GyroY/65.5;
  RateYaw=(float)GyroZ/65.5;
  AccX=(float)AccXLSB/4096 - 0.1;
  AccY=(float)AccYLSB/4096 + 0.05;
  AccZ=(float)AccZLSB/4096 + 0.025;
  AngleRoll=atan(AccY/sqrt(AccX*AccX+AccZ*AccZ))*1/(3.142/180);
  AnglePitch=-atan(AccX/sqrt(AccY*AccY+AccZ*AccZ))*1/(3.142/180);
}
void setup()
{
  Serial.begin(57600);
  referencePressure = ms5611.readPressure();
  checkSettings();
  delay(2000);
  Wire.setClock(400000);
  Wire.begin();
  delay(250);
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  for (RateCalibrationNumber=0; RateCalibrationNumber<2000; RateCalibrationNumber ++) {
    baro();
    AltitudeBarometerStartUp+=AltitudeBarometer;
    delay(1);
  }
  AltitudeBarometerStartUp/=2000;
  F = {1, 0.004,
            0, 1};
  G = {0.5*0.004*0.004,
            0.004};
  H = {1, 0};
  I = {1, 0,
           0, 1};
  Q = G * ~G*10.0f*10.0f;
  R = {30*30};
  P = {0, 0,
           0, 0};
  S = {0,
           0};
           LoopTimer=micros();
}

void loop()
{
   gyro_signals();
  AccZInertial=-sin(AnglePitch*(3.142/180))*AccX+cos(AnglePitch*(3.142/180))*sin(AngleRoll*
  (3.142/180))* AccY+cos(AnglePitch*(3.142/180))*cos(AngleRoll*(3.142/180))*AccZ;
  AccZInertial=(AccZInertial-1)*9.81*100;
  baro();
  AltitudeBarometer-=AltitudeBarometerStartUp;
  kalman_2d();
  Serial.print("Altitude [m]:");
  Serial.print(AltitudeKalman/100);
  Serial.print(" Vertical velocity [m/s]:");
  Serial.println(VelocityVerticalKalman/100);
  while (micros() - LoopTimer < 4000);
  LoopTimer=micros();
}

Any help would be appreciated. In his video he used the BMP280, while here I use MS5611, integrated in the GY86 sensor. If there is no way to get around this instability, will adding a PID controller stabilize this filter?
1    Carbon Aeronautics, Belgium. 19|Combine an accelerometer and barometer to measure altitude (2023). Accessed June. 30, 2024. [Online Video]. Available: https://www.youtube.com/watch?v=GZevJyabMdI

MS5611 sensor should be correctly initialized. Check the pressure and temperature readings separately before using them in the Kalman filter.

@tepalia  I tried that before with the initializing code. It took about 20 more seconds for the readings to start appearing, and it's still unstable. Without the initializing (the code above) there is no need for 20 seconds and the filter still unstable

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Not getting continuous motion data with Arduino Nano 33 BLE Sense Rev2 on Google's Tiny motion trainer
I'm using an Arduino Nano 33 BLE Sense Rev2 with the Tiny Motion Trainer by Google to train for certain movements. The original code was designed for the Arduino Nano 33 BLE Sense, which uses the LSM9DS1 IMU sensor. I have modified the code to use the BMI270_BMM150 sensor (updated the library in dataprovide.cpp from #include <Arduino_LSM9DS1.h> to #include <Arduino_BMI270_BMM150.h>).
However, after running the code and visualizing motion on the Tiny Motion Trainer, I observe a non-continuous graph compared to what is shown in YouTube tutorials. I've attached images for reference.
My data:

Expected data:

Link to images
I suspect this issue might be due to the difference in data output frequency between the LSM9DS1 (119 Hz) and the BMI270_BMM150 (99 Hz). However, I couldn't find anything in the code referencing this directly.
Could the frequency difference be causing this issue?  If not, what else could be causing the non-continuous graph as all the tutorial I have seen for this, the people are getting continuous graphs.

what do you mean by continuous motion data? ... how do you know that the arduino is not sending it?

@jsotola If you look at my graph there are points when the motion is 0 while I am moving the sensor continuously compared to one in tutorial where the graph is continuous.

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Using MAX30100 SpO2 value to turn on LED
max30100 i2c sensor gives heartrate and spo2 reading in serial and also can show values in lcd display. I want to show it's indication via LED.
If SpO2 value <94 , it will turn on Red led in digital pin 4
If SpO2 value >=94, it will urn on Green led in digital pin 5
Here pox.getSpO2() gives the SpO2 value.
How can I use this value to toggle between two digital pin. I understand it's easier to read analog pin value for toggling but I am searching for guidelines how I2C values can be use for this condition.
Thank you.
Here is the minimalistic code of SpO2
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#define REPORTING_PERIOD_MS     1000

PulseOximeter pox;
uint32_t tsLastReport = 0;

void setup()
{
    Serial.begin(9600);

    Serial.print("Initializing pulse oximeter..");

    if (!pox.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
}

void loop()
{

    pox.update();

    if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
        Serial.print("SpO2:");
        Serial.print(pox.getSpO2());
        Serial.println("%");

        tsLastReport = millis();
    }
}

You've done all the hard work already by setting the sensor up and managing to read from it.
I'd define a constant for the SpO2 threshold near the top, and convenient names for the pins:
const uint8_t SpO2_threshold = 94;  // percent
const uint8_t red_pin = 4;
const uint8_t green_pin = 5;

Then set the pins to output mode in the setup() as usual. (Maybe set them both HIGH (or LOW) too in there, to indicate that no reading has been done yet.)
In the loop, inside the reporting section, simply grab the value and toggle the pins:
...
uint8_t spo2 = pox.getSpO2();
// Print it out to serial if needed, then:
if (spo2 < SpO2_threshold) {
  digitalWrite(green_pin, LOW);
  digitalWrite(red_pin, HIGH);
} else {
  digitalWrite(red_pin, LOW);
  digitalWrite(green_pin, HIGH);
}

A more involved version that pulses one LED (short on time, variable off time) depending on the value read from the sensor. It uses the same technique as the sensor reading timing (the one used in the "blink without delay" example).
Sensor readings are clamped (any value at or below spo2_min will result in the fasted blink time, over spo2_max the slowest). Then that range is mapped to a range of led off times. Don't use the values in this example, I'm not a medical doctor :-)
// pin mapping
const uint8_t led_pin = LED_BUILTIN;

// sensor readings (%)
const int spo2_min = 85;
const int spo2_max = 100;

// led timings (ms)
const unsigned long led_off_min = 50;
const unsigned long led_off_max = 5000;

unsigned long led_off_time = led_off_max; // variable off time
const unsigned long led_on_time = 50;     // fixed, short on time

// sensor reading timing (ms)
const unsigned long sensor_interval = 1000;

// led state
uint8_t led_state = LOW;

// "timer" state
unsigned long last_reading = 0;
unsigned long last_led_toggle = 0;

void setup() {
  pinMode(led_pin, OUTPUT);
  digitalWrite(led_pin, led_state);

  Serial.begin(9600);

  Serial.print("Initializing pulse oximeter..");
  if (!pox.begin()) {
    Serial.println("FAILED");
    for (;;);
  } else {
    Serial.println("SUCCESS");
  }
}

void loop() {
  pox.update();
  unsigned long current_millis = millis();

  /* sensor reading "timer" */
  if (current_millis - last_reading >= sensor_interval)
  {
    last_reading = current_millis;

    int spo2 = pox.getSpO2();

    // clamp reading to expected range
    int clamped = constrain(spo2, spo2_min, spo2_max);
    // map to off time range
    led_off_time = map(clamped,
                       spo2_min, spo2_max,
                       led_off_min, led_off_max);

    char buff[128];
    sprintf_P(buff, PSTR("reading: %d (%%) -> interval: %lu (ms)"),
              spo2, led_off_time);
    Serial.println(buff);
  }

  /* led toggling "timers" */
  unsigned long led_elapsed = current_millis - last_led_toggle;

  if ((led_state == LOW) && (led_elapsed >= led_off_time))
  {
    last_led_toggle = current_millis;
    led_state = HIGH;
    digitalWrite(led_pin, led_state);
  }
  else if ((led_state == HIGH) && (led_elapsed >= led_on_time))
  {
    last_led_toggle = current_millis;
    led_state = LOW;
    digitalWrite(led_pin, led_state);
  }

}

Thanks a lot. I already tried earlier same as this way and it's working. You explanations made it more clearly understandable to me. Thank you.

Hello, how about using only single red led via giving pulse. If <94, led will blink rapidly. if >=94, led will blink slowly. I tried to add delay(); but it works when using around 10ms delay. but for 500ms delay, spo2 loops also gets delay and it only show 0%. Doesn't work for larger delay cycle. Any thoughts ?

delay is not your friend. Use the same technique as you did for the sensor readings, i.e. check the time elapsed since you last toggled the LED, and toggle it if that is longer than your threshold. Added an example. Probably won't be great if you're only receiving ints in a rather small range - you don't have much granularity.

Other note: if you need need more complicated than this, I'd recommend you find a timer library that you like, and look into state machines also. Lots of examples and docs on both these topics.

Hi, Can't thank you enough. It will take time to understand as I'm not familiar with these term some much. But I want to point out some which i couldn't understand
char buff[128];
sprintf_P(buff, PSTR("reading: %lu (%%) -> interval: %lu (ms)"),
spo2, led_off_time);
Serial.println(buff);
giving this output in serial monitor, when spo2 should show 0%
reading: 3276800 (%) -> interval: 1701969920 (ms)
So, I directly called Serial.print(pox.getSpO2());

Another thing, I wanted to show, when blink slow, "health normal" and when blink fast, :health critical" in serial monitor. First I tried to add in last if else if statement but serial monitor shows both lines.
So, I created another if else statement to show in serial monitor.
Any improvements and thoughts on this?

There was an error in the printf format string, fixed it, but can't reproduce what you are seeing. The only place where the speed changes is in the sensor update part. If you want to print something based on the reading value, it must go in there. If you want to repeat it, then you need to save it so that the last reading is available elsewhere in the loop.


text:
Arduino Hanging Not sure if software or hardware issue
I am trying to run a very simple program to glow LEDs in a certain fashion, I connected 9 LEDs to the GPIOs or digital pins, 2 To 10, via 220 Ohm resistors; when I am running the program it is hanging the Arduino after some time interval and LEDs stops glowing, not sure if it is a hardware issue or some bug in the software/code.
Here is the code:
int outGPIOsarr[9] = {2,3,4,5,6,7,8,9,10};

void setup() {

  for(int i=0; i<10; i++)
pinMode(outGPIOsarr[i], OUTPUT);
}

void loop() {

for(int i=0; i<10; i++)
 {
digitalWrite(outGPIOsarr[i],HIGH);
delay(10);
 }

for(int i=0; i<10; i++)
 {
digitalWrite(outGPIOsarr[i],LOW);
delay(10);
 }

}

Anything getting hot? Try using higher resistor values (470 or even 1k), you're drawing more than the allowed amps from a 328P (200mA IIRC)

@Mat , nothing is getting hotter , I checked it, one thing I observed is when I change the first argument of digitalwrite from outGPIOsarr[i] to respective pin number and change for loop from to run from 2 to 10 the code is running fine without a problem ! But I am not able to find good reasoning behind it !?

This is the UB!
The indexes in array in C++ counts from 0;
So, the last index in your array of 9 elements is 8.
All cycles must stop at 8. Yours are stops at 9.
Access beyond the existing element in the array is the Undefined Behavior - it hangs the MCU.

Oh well that was an embarrassing bug, thanks for pointing out >_<

and OP can't delete a question if it has an upvoted answer

But why this topic must be deleted?

because the question is not about the arduino, it is a general programming question, so it is off topic here ... any answers are also off topic here

I disagree. The reason is - OP doesn't know, what causes hanging - wrong firmware or wrong hardware.
So, to mark the question as off topic, you must literally answer the question.

https://arduino.meta.stackexchange.com/questions/2667/trivial-questions-must-be-off-topic

OP would like to remove the question because it is an "an embarrassing bug" and if not closed, with this count of votes it would be a "Hot network question" representing Arduino SE on the right side of every SE page.


text:
nRF24L01 programming issue
I'm new to nrf24l01 . I have a two nrf24l01 they both works very well ,I tested them with a simple LED blinking program. Now I write a program for both transmitter and receiver to just send message and get a response for the message. Both the transmitter and receiver programs are given bellow (program works but not as I'm expected)
Transmitter
#include<RF24.h>
#include <nRF24L01.h>
#include<SPI.h>

const byte CEpin=9;
const byte CSNpin=10;
RF24 radio(CEpin,CSNpin);
const byte address[][6]={"000001","000002"};
char ack[30],buf[]="hi....How are you?";
bool x;

void setup() {

  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(address[1]);
  radio.openReadingPipe(1,address[0]);
  radio.setPayloadSize(30);
  radio.setChannel(83);
  radio.setDataRate(RF24_1MBPS);
  radio.setPALevel(RF24_PA_HIGH);

}

void loop() {
  delay(4);

  radio.stopListening();

  if(radio.write(buf,sizeof(buf)))
      Serial.println("MSG sent successfully");

  delay(6);

 radio.startListening();

 if(radio.available())
 {
  radio.read(ack,sizeof(ack));
  Serial.println(ack);
 }

 }

Receiver
#include<SPI.h>
#include<RF24.h>
#include<nRF24L01.h>

const byte cepin=9;
const byte cspin=10;
RF24 mynrf(cepin,cspin);
const byte address[][6]={"000001","000002"};
char buf[50];
char response[]={"I'm fine"};

void setup() {

Serial.begin(9600);
mynrf.begin();
mynrf.openReadingPipe(1,address[0]);
mynrf.openWritingPipe(address[1]);
mynrf.setPayloadSize(30);
mynrf.setChannel(83);

}

void loop() {

  delay(4);

  mynrf.startListening();

  if(mynrf.available())
  {
      mynrf.read(buf,sizeof(buf));
      Serial.println(buf);
  }

  delay(6);

  mynrf.stopListening();

  if(mynrf.write(response,sizeof(response)))
      Serial.println("response sent successfully\n\n\n");

}

I faced a lot of troubles
1.Without delay() [delay(4);delay(6);] it doesn't work
2.With setPayloadSize() and setChannel() it doesn't work
3.I can't set data rate at 250Kbps and 2Mbps which means setDataRate(RF24_1MBPS) only works
4.setPALevel(RF24_PA_HIGH) and setPALevel(RF24_PA_LOW) only works
5.It also doesn't give output as I'm expected. I attached a screenshot of my output bellow ( COM5 is Transmitter and COM4 is Receiver)
Transmitter and Receiver serial monitor output

These are the issues what I'm faced. Can any one explain it .
Please any one can suggest me a best tutorials and examples for nrf24lo1 .

Two things:

nRF24L01 is only half duplex. You can't send and receive at the same time. You must send only briefly and then spend some time listening for the response. With no delays there is no time to listen. It's like it's sat there with its fingers in its ears going "la la la la la" all the time.

You're spamming out a "response" all the time. The same situation occurs. You have two transmitters that rarely listen for anything. You should only send your response if you have received an original request.

I can't catch you clearly ......please explain briefly

i think nRF24L01 can support  full duplex  (two way communication)

Read your code and follow the flow of it logically. It's very clear.

It can do two way, but not "full duplex". Only "half duplex". It can transmit and it can receive, but it cannot do both at the same time.

@nskaran, as in like a "walkie-talkie", or marine VHF, or any number similar systems.  Hence all the human protocols carried out to keep people from talking at the same time or for a long time.


text:
compress data with 8-4-2-1-BCD-Code
Hello I would like to optimise a few values for transmission via radio (LoRa) beforehand. I have a sensor number (1-255), temperature (00.00-99.99) and humidity. (00.00-99.99) I would like to transmit this data compressed as a byte array. I have already calculated that I need 5 bytes via BCD for this.
How do I get my numbers mapped into the respective byte?
float temperature = 24.12;
float humidity = 60.12;
byte message[5];

void setup() {
  Serial.begin(9600);
  message[0] = 12; //sensor id 12
}

void loop() {
  float temperature1 = floor(temperature);
  float temperature2 = (temperature - temperature1) * 100;
  Serial.println(temperature1, 0);
  Serial.println(temperature2, 0);
  message[1] //?? temperature1
  message[2] //?? temperature2

  float humidity1 = floor(humidity);
  float humidity2 = (humidity - humidity1) * 100;
  message[3] //?? humidity1
  message[4] //?? humidity2
}

how many sensors do you have?

Why do you want to use BCD? Binary values are much simpler to handle for any processor (and have a wider range).

@thebusybee the benefit lies in the smaller package that has to be transmitted via Lora. There are strict limits here

I don't grasp what you mean by "smaller package". Binary values are as small as BCD, and have a wider range at the same time. Some might say that binary values are even smaller than BCD, but this is only true if you can use arbitrary bit numbers. For example, a value range of 0000 to 9999 needs 16 bits in BCD, but only 14 bits as binary value. -- However, you accepted an answer that uses binary values.

Why don't you use the following mapping:
1 byte: sensor number (0-255)
2 bytes: temperature (0-65535) by mapping the temperature to
(int) (65536.0 * (temperature / 100.0))

2 bytes: humidity (0-65535) by mapping the humidity to
(int) (65536.0 * (humidity / 100.0))

You will lose a little bit of accuracy for the temperature and humidity. If you know ranges in which the temperature/humidity will be, you can get a better accuracy.
message[0] = 12; // Sensor id 12
message[1] = highByte(temperature);
message[2] = lowByte(temperature);
message[3] = highByte(humidity);
message[4] = lowByte(humidity);

Note if you have multiple sensors and have to transmit them all, you don't need to send the sensor number, send in bytes 0..3 the temperature/humidity for sensor 0, in bytes 4..7 the temperature/humidity for sensor 1 etc.

like this?
int payload = int(65536.0 * (humidity / 100.0));
message[1] = highByte(payload);
message[2] = lowByte(payload);

@TomBaires Something like that, I updated my answer

This is my final solution with BCD-Code
int temperature1 = int(floor(temperature));
int temperature2 = int(round((temperature - temperature1) * 100));
uint8_t bcdTemperature1 = uint8_t((temperature1/10) <<4) | (temperature1 %10);
uint8_t bcdTemperature2 = uint8_t((temperature2/10) <<4) | (temperature2 %10);

message[0] = moduleUniqueidentifier;
message[1] = bcdTemperature1;
message[2] = bcdTemperature2;
message[3] = uint8_t(ceil(humidity));


text:
Uno: Infrared Transmitter Module Not Transmitting
I purchased https://www.jaycar.co.nz/medias/sys_master/images/images/9677365510174/XC4426-dataSheetMain.pdf, which seems identical to https://arduinomodules.info/ky-005-infrared-transmitter-sensor-module/.
I connected the module according to the diagram:

I then copied the following Sketch:
#include <IRremote.h>
IRsend irsend;
void setup()
{
    Serial.begin(9600); // Initialize serial interface
}
void loop()
{
    for (int i = 0; i < 10; i++) {
       irsend.sendSony(0xa90, 12); // code for Sony TV power command
       delay(5000); // wait 5 seconds
   }
}

When I compiled the above, I got the following warning:

warning: 'void IRsend::sendSony(long unsigned int, int)' is deprecated: This old function sends MSB first! Please use sendSony(aAddress, aCommand, aNumberOfRepeats).

So, I modified the code and added a 3 for the aNumberOfRepeats parameter. Thus, my final Sketch was this:
#include <IRremote.h>
IRsend irsend;
void setup()
{
    Serial.begin(9600); // Initialize serial interface
}
void loop()
{
    for (int i = 0; i < 10; i++) {
       irsend.sendSony(0xa90, 12, 3); // code for Sony TV power command
       delay(5000); // wait 5 seconds
   }
}

When I ran the Sketch on my Uno and viewed the module through my camera phone, I did not see the module blinking. My assumption is that this is not working for some reason. Any ideas why?
Also, I'm measuring about 4.5 volts between GND and the middle pin. That seems right. When I measure GND and the signal out pin, I get a steady 0.64 volts. Shouldn't that be toggling between 0 and some number?
UPDATE per @orithena's comment:
I've updated the code as per suggested, but the transmitter still doesn't blink when viewed through my camera phone:
#include <IRremote.h>

IRsend irsend;

#define IR_SEND_PIN 5

void setup()
{
    pinMode(IR_SEND_PIN , OUTPUT);
    Serial.begin(9600);
}

void loop()
{
    for (int i = 0; i < 10; i++) {
       irsend.sendSony(0xa90, 12, 255);
       delay(1000);
   }
}

Just a few notes about this new code:

I tested connecting S to pins 3 and 5; neither worked
255 is the highest number I could set for aNumberOfRepeats, otherwise I would get a compile warning: "warning: large integer implicitly truncated to unsigned type"

UPDATE per @jsotola's comment:

It may be that everything is blinking too fast for you to see or measure. Or, your pin 3 may be broken. Try explicitely initializing the pin: pinMode(3, OUTPUT); in setup. Try changing the signal pin: insert #define IR_SEND_PIN 5 at the very top of your sketch and connect S to pin 5. Try increasing the number of repeats to 1000, so that the LED blinks longer.

Please see my update.

Why are you using 0xa90 as Sony remote address, and 12 as command code? See the example code for Sony remote control. If you are not sure, run MinimalReceiver and point your remote to the arduino to decode the IR code, and then use it for your IR_send sketch.

On your updated code, if you explicitly #define IR_SEND_PIN 5;, then your pinMode() function should be pinMode(IR-SEND_PIN, OUTPUT). Don't just copy and paste the code, try to understand the code.

why did you connect the 5 V ? ... that module uses only two pins ... it needs only data and ground connections

@jsotola the diagram I followed connected all three pins.

@hcheung I don't care what signal I'm sending. I just want to see a signal to be sent. I'm not seeing anything through my camera phone. Also, I did use pinMode(IR_SEND_PIN, OUTPUT)  in one of my tests (when I connected to pin 5; it didn't work.

the crazy part is that you are wasting your time with arduino programming ... all you have to do, is to connect the module to a 5 V power supply and it should light up

please post clear, well lit pictures of the front and back of the IR emitter module ... I have a suspicion that a component is missing from it

@jsotola - I tried attaching the transmitter to GND and 5V only (no programming). I still don't see anything thought the camera phone. Would that suggest that the device is defective? Also - see photos in my post per your request.

the pictures are not very clear ... it looks like a resistor may be missing ... it is quite possible that the the board is incomplete

@jsotola - The operating voltage of the infrared transmitter module is 5VDC. No need for a resistor, right?

Hooking an IRED/LED directly to a voltage source is a time-honoured way of creating Dark Emitting Diode.

It really looks like R1 is missing. And yes, the operating voltage of the whole module is 5V -- the (typically) 1.5V forward voltage of the IR Diode is reached in a 5V circuit by using a 220Ohm resistor. Therefore, changing the resistor changes the operating voltage of the module. And: if it is missing, there is no electrical connection from the module's signal pads to the LED itself, so the LED cannot turn on.

I'll get another transmitter in case I now have a "Dark Emitting Diode", @timemage. Where would I connect R1, @orinthena?

Someone can tell you about IREDs/LEDs generally, but it's not really a good answer to the question. It would a better answer if someone could answer with some certainty about how your particular boards are made. The best resources I can find on the design of that board are very poor. If I had one, I would use a meter to determine the exact nature of the board before writing an answer. So far the only one here known to have this board is you. So the question seems mostly answerable (well) by you, so far anyway.

@ArduinoNewbie On the pads marked with R1... well, scratch that. I just devoted an hour finding more info on the KY-005 module. I found it to be one of the most underdocumented module I've ever seen. It seems to have a place for a resistor to solder in, but thats not documented anywhere except in some reverse engineering blog (also, if you solder one in, use the middle pin instead of S). Its sold everywhere listed with an operating voltage of 5V, but without R1, it'd be at most 1.5V. It's just an IR LED soldered to a PCB. Better unsolder the LED and use it directly (with 220Ohm resistor).

Although you might've run into the "Dark Emitting Diode" problem already, since I'm now pretty sure that you did put the full 5V onto the IR LED (that module is just bad: every shop tells you "it's for 5V! put it on your Arduino!" when the resistor is missing that should reduce the 5V to 1.5V). It may be that the LED is now shorting your Arduino pin to GND, so that pin may have taken some damage. Try another pin (don't forget to adapt the software to use another pin!) when you try a new IR LED.


text:
How to: Connecting 4-pin Joystick to Arduino board
I am completely new at Arduino. I have a joystick:

It is however quite different from all the other joystick-tutorials, as their joysticks have 5 pins, not just 4. So I am quite at loss here. How do I connect it?

I would imagine one pin goes to 5V, one to GND, and the other two connect to analog inputs. I would expect there to be markings on the board telling you which is which.

Wouldn't I need one to a key to read the data?

I didn't understand what you just asked.

Is the fifth for when you press down on the joystick, like a button? Try connecting a multimeter and see how the resistances change when you play with the joystick.

This joystick probably does not have a push button under it in the way that some others do.  If you need that functionality you can provide it with a distinct pushbutton placed to the side, or you can buy a different joystick.

Hah thanks, that's what I meant by my question, sorry for the lack of clarity @Majenko. All there is left for me to do is find the library/code to interpret the data from the joystick. Thanks for the help.

You read the analog value. That's really all there is to it. Analog values go from 0 to 1023. 512 (or thereabouts) is the middle of the joystick. It goes up and down from there. There's nothing to "interpret", and you certainly won't find a library for it.

Oh, well thanks then. How would it be if one moved it across the X/Y-axis? Would it just display a change?

This looks like it. There's a photo of the pinout and some basic details on it.

Is there nothing printed underneath? The one linked to by Mark Smith has the exact pin-outs under it. You hardly need a library for this. About four lines of code would do it, two of them being analogRead.

If the link that Mark Smith provided is correct, that is a “dual-axis mini xy joystick module PSP 2-Axis Analog Thumb Joystick Module Thumb Joystick for Arduino”, with (as other comments suggested) pins labeled on back of board as GND, X, Y, VCC.
To use it, attach GND to Arduino ground; connect each of X and Y to analog inputs, like A0 ... A7; connect VCC to (typically) 5 V.  Then read results on your analog input lines.
Note, it makes sense to test the device using a DVM beforehand.  Measure resistance from GND to VCC to find out overall resistance, so you can decide if 5 V is appropriate or if you should use a lower voltage.  (For example, if overall resistance is several thousand ohms, 5 V is ok; if only a few hundred, add one or two thousand ohms in series and take analog readings using a 1.1 V reference instead of 5 V reference.)  Also measure resistance from GND to X as you move the thumb control, and from GND to Y.

For sure there should be 4 pins, 'GND', 'VCC', 'X' and 'Y'. GND (-) is used when the joystick goes into a negative axis (depending x or y) on a Cartesian plane it transfers as negative into the Arduino board. The same thing happens when it goes to positive (VCC (+)) (depending x or y) on a Cartesian plane it transfers as positive onto the Arduino Board.

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/


text:
Can an interrupt function call a regular function?
If I want to create an interrupt and have it do something, can this 'something' be written in a regular function that I also use outside of the interrupt?

Yes you may.
Yet keep the function in question short, you don't want to spend too much time in interruption.
Another way is to set up a variable state in the interruption, and call the function in question in the normal cycle when the variable is in the desired state.

Yes, you can. You just have to make sure your function is reentrant.
If it accesses no memory beyond its parameters and (non static) local
variables, you should be fine. If it accesses global variables, hardware
registers, or static locals, it is unlikely to be, and only detailed
analysis will tell you for sure.
Beware also of the library functions you call: some of them are not
themselves reentrant and should therefore be avoided. See for example
the manual of strtok(), which explicitly addresses the
reentrancy problem.

In addition, if the function is only called by the one ISR, and it will always run to completion (won't be interrupted and run another time before the previous call finishes), it doesn't need to be re-entrant.

@JRobert: The OP stated that the function would also be called from non-interrupt context. That other call would need to be inside a critical section if one wanted to ensure the function is never called while another call has not finished.

True enough, making my comment irrelevant here.

If I want to create an interrupt and have it do something, can this 'something' be written in a regular function that I also use outside of the interrupt?

The right answer is yes and no.
The issue here is reentrancy : whether a function can be interrupted while it's being called by another. Not all functions are re entrant so the safer assumption is that they are.
Another issue with calling a function from within the isr is overhead.
The typical solutions are:

Make sure that the function called from the isr is re entrant.
The simplest way to assure that is to a. Limit your calling of that from the isr. Or b. Create two versions of the same function. And call them separately.

Btw, I routinely call functions from within isr. In my case through function pointers.

You left out not at the end of  “Not all functions are re entrant so the safer assumption is that they are.”

You can do that, but it will increase the complexity of your program.
Often when entering an interrupt service routine, the first thing you do is disabling interrupts - the make sure the callback completes reliably.
This is something you will have to consider if you want to use the same routine for a 'regular' scenario

You wrote: “the first thing you do is disabling interrupts”: on AVRs at least, you don't. The hardware does it right before executing the interrupt vector.

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/


text:
How can I call the variable **Servo::write** in Arduino for my 360 servo?
I'm trying to control a Servo with a push button and to help me watching if it is working I am using a LED but I'm not sure if I'm doing it well the Servo::write so I need you to tell me what am I doing wrong that my Servo::write function its not working but the LED does the function. Here is the code I am using:
#include <Servo.h>

Servo myservo;

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to
const int serPin = 9;

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
// initialize the button pin as a input:
pinMode(buttonPin, INPUT);
myservo.attach(serPin);
// initialize the LED as an output:
pinMode(ledPin, OUTPUT);
// initialize serial communication:
Serial.begin(9600);
}

void loop() {
// read the pushbutton input pin:
buttonState = digitalRead(buttonPin);

// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == HIGH) {
  // if the current state is HIGH then the button
  // wend from off to on:
  buttonPushCounter++;
  Serial.println("on");
  Serial.print("number of button pushes:  ");
  Serial.println(buttonPushCounter);
} else {
  // if the current state is LOW then the button
  // wend from on to off:
  Serial.println("off");
}
 // Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state,
//for next time through the loop
lastButtonState = buttonState;

Servo::write(myservo);

// turns on the LED every four button pushes by
// checking the modulo of the button push counter.
// the modulo function gives you the remainder of
// the division of two numbers:
if (buttonPushCounter % 2 == 0) {
 digitalWrite(ledPin, HIGH);
 myservo.write(serPin < 180);
} else {
 digitalWrite(ledPin, LOW);
 myservo.write(serPin > 0);
}

}

I think you should look at some servo examples in the IDE. You will then see that you have done the servo code completely wrong.

(in fact your code doesn't even compile).


text:
Arduino Leonardo clone voltage question
I'm planning my first Arduino project which involves a MPU-6050 accelerometer(3.3V) and a RGB led(5V). I've found this Arduino Clone but i'm not sure if it has 3.3V and 5V outputs that I can use. The pins do exist on the original Leonardo board but i'm not sure if the clone contains them as well.

That has a 5V pin (Vcc) but no 3.3V. Also the 5V pin can't provide more than a few hundred mA.
Better specifications are here: https://www.sparkfun.com/products/12640

I see, thanks a lot. If I find a 5V accelerometer would I be able to connect it + the led to the same 5V pin?

Yep. Note that a 5v accelerometer will probably be a 3.3v one with built in 3.3v regulator, so you need to take the same precautions as a 3.3v one.

What kind of procautions do you mean? Could you maybe give me term to google to send me down the right path?

Logic level shifting. Interfacing a 5v Arduino to a 3.3v device. If the accelerometer is I2C include that in your search too.


text:
SPI protocol with multiple devices
I need to write and read the measurements received by a sensor on an SD card. The card reading would be to display the data on a TFT screen. Specifically, I am using the 1.8-inch ST7735 screen. To use the card with Arduino I am using an SD card reader module (ANGEEK brand). Both devices work with SPI protocol.
To verify that this would be possible I make a test code. In it, two functions (measure_V and measure_I) simulate the voltage and current measurements. So what I do is call those two functions to get 10 measurements and save all of them to SD. Then I read them and display them on the screen. I show them both on the serial monitor and on the TFT screen. In order to map the values of V_data and I_data I use two functions that give me the maximum value in each measure. However, nothing appears either on the serial monitor or on the screen. The two devices share the MISO, MOSI, and SCK pins. Could someone help me with this? It is the first time that I use the SPI protocol with more than one device.
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SD.h>
#include <SPI.h>

#define TFT_CS 10
#define A0_pin 8
#define RST_pin 9

#define SD_CS 7                                              // CS pin of SD card connected to pin 7 of Arduino

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, A0_pin, RST_pin);

const uint8_t rotation=2;                                    // Origin of coordinates top left

float V_data[10], I_data[10];                                // Store voltage and current measurements

File V_points;
File I_points;

const uint16_t  Display_Color_Green = 0x07E0;                // Green color

int measure_V(int x){
  int voltage;
  voltage = x;
  return voltage;
}

int measure_I(int x){
  int current;
  current = pow(x, 2);
  return current;
}

int max_V(){
  int maxVal = 0;
  for (int i=0; i<sizeof(V_data)/sizeof(float); i++) {
    if (maxVal < V_data[i]) {
      maxVal= V_data[i];
    }
  }
  return maxVal;
}

int max_I(){
  int maxVal = 0;
  for (int i=0; i<sizeof(I_data)/sizeof(float); i++) {
    if (maxVal < I_data[i]) {
      maxVal= I_data[i];
    }
  }
  return maxVal;
}

void setup() {

  Serial.begin(9600);
  tft.initR(INITR_GREENTAB);                                                // Initialize a ST7735R chip, green tab
  tft.setSPISpeed(40000000);
  tft.setRotation(rotation);                                                // We set type of rotation
  tft.fillScreen(ST7735_BLACK);

  Serial.print("Initializing SD card...");

  if (!SD.begin(SD_CS))
  {
    Serial.println("SD initialization failed!");
    while (1) ;
  }
  Serial.println("SD initialization done.");
}

void loop() {

  int static n = 0;                                                              // Measurement number (initial measurement = 0)
  uint16_t pixelV, pixelI;

  // We perform and save on the memory card 10 measurements
  for(int i=0; i<=9; i++){
    V_points.print(measure_V(i));                                                // New voltage value
    I_points.print(measure_I(i));                                                // New current value
    }

  // We read the 10 measures of the card
  while (V_points.available() && I_points.available()){
    V_data[n] = V_points.parseFloat();                                           // Read voltage value from file
    I_data[n] = I_points.parseFloat();                                           // Read current value from file
    Serial.print("Voltage= ");
    Serial.print(V_data[n]);
    Serial.print(", ");
    Serial.print("Current= ");
    Serial.println(I_data[n]);
    pixelV = map(V_data[n], 0, max_V(), 0, 128);                                 // We map tension
    pixelI = map(I_data[n], 0, max_I(), 0, 160);                                 // We map current
    tft.drawPixel(pixelV, pixelI, Display_Color_Green);
    n++;
    }

}

Check the specific details of your sensors to check that they properly put the MISO pin into high-impedance when SS is pulled high.  See my answer to a similar question for a possible workaround.

it could be this problem: https://arduino.stackexchange.com/questions/36912/slave-spi-does-not-work/36915#36915

Thanks, I will take a look at what you have gone through and see if it fixes it.

You appear to be neither opening nor closing the files that you are attempting to write or read.
See this for an example of using an SD card: https://www.arduino.cc/en/Tutorial/LibraryExamples/ReadWrite


text:
Round to first significant digit
I want to round a decimal number (x), but I need to do it differently depending on how it is. If it has integer significant digits, we round to the nearest integer, and if not, we round to the first significant digit. So the following would happen with these example numbers: 11.871->12, 11.177->11, 37.233->37, 37.779->38, 0.578->0.6, 0.544->0.5, 0.0257->0.03, 0.0223->0.02... (The numbers could have more or less decimal places than in the examples).
To implement this, I have created a function (type) in which we check if the most significant digit is of type integer or decimal. This function returns this characteristic of the number in the form of a "string". So if the most significant digit is an integer we just use the "round()" function. However, I don't know how to round a number to the first significant digit (roundto_fsd() function). Does anyone know of any function capable of doing this or any method to do it?
float x;

char type(float number) {
  char type[8];
  if (int(number) != 0) {
    type = "integer";
  } else {
    type = "decimal";
  }
  return type;
}

float roundto_fsd(float number) {}

void setup() {
  Serial.begin(9600);
  if (!type(x).compareTo("integer")) {
    Serial.println(round(x));
  } else if (!type(x).compareTo("decimal")) {
    Serial.println(roundto_fsd(x));
  }
}

void loop() {}

First of all, let me clarify that there is nothing “decimal” in a
float: a float is a binary (not decimal) floating point number.
The Serial.println() function does a binary-to-decimal conversion for
the purpose of representing the number as human-readable text. If you
want a specific number of decimal digits after the radix point, you
should ask Serial.println() to provide them. This is done by passing
this number as a second parameter:
Serial.println(sqrt(2));    // prints "1.41" (2 decimals by default)
Serial.println(sqrt(2), 0); // prints "1"
Serial.println(sqrt(2), 5); // prints "1.41421"

Now the question is: how to compute the number of required digits after
the radix point? This depends on how big the float is. If it is
smaller than 1 but larger or equal to 0.1, you want just one digit. If
it is ten times smaller than that, you want to digits, and so on:
numbers      digits
───────────────────
[1, +∞)        0
[0.1, 1)       1
[0.01, 0.1)    2
[0.001, 0.01)  3

I suggest repeatedly multiplying your float by 10 until it gets ≥ 1,
and count the number of times you had to do so:
/* How many decimal places to print. */
int decimal_places(float x)
{
    int i = 0;
    while (x < 1) {
        x *= 10;
        i++;
    }
    return i;
}

You would use this function like this:
Serial.println(x, decimal_places(x));

Note that there is a small issue with this approach. If you try to print
0.97, decimal_places() will say you need one decimal, and
Serial.println() will print “1.0”. If you would rather have it print
“1”, then change the loop condition within decimal_places() to:
while (x < 0.95)

Caveat: All this assumes that the numbers you want to print are all
strictly larger than zero. Otherwise decimal_places() would have an
infinite loop.

Thank you! Is using the "while(x<0.95)" condition because Serial.print() rounds?

@Libegria: It's because Serial.print() rounds to nearest, meaning 0.97 would be rounded up to 1.0, but 0.93 would be rounded down to 0.9.


text:
ATTiny84 I2C with NeoPixels not working
I fetch RGB values via I2C - which works (with a splitting function).
Now I have a problem:

When I write values directly into strip.color() like strip.Color(255,0,0,0) the NeoPixels turn red when receiving the values.
When I use the values from the I2C connection it won't work anymore. But the values are correct if I have a look in the Serial Monitor.

Maybe the data type is wrong or something?
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN   4
#define LED_COUNT 4

static const int SLAVE_ADDRESS = 0x08;

uint32_t currentColor;
int currentBrightness = 50;
int flashState = 0;
String ledRed, ledGreen, ledBlue, ledWhite;

String temp;
String payload;

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);

void setup() {
  Serial.begin(9600);

  strip.begin();
  strip.show(); // Turn off all pixels asap
  strip.setBrightness(50); // Set brightness

  pinMode(3, OUTPUT);
  digitalWrite(3, HIGH);

  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(change);
}

// Set all pixels to color
void setPixelColors(uint32_t color) {
  for(int p = 0; p < LED_COUNT; p++) {
    strip.setPixelColor(p, color);
  }
  strip.show();

  delay(1000);
  digitalWrite(3, HIGH);
}

// Split the I2C message
String split(String s, char parser, int index) {
  String rs="";
  int parserIndex = index;
  int parserCnt=0;
  int rFromIndex=0, rToIndex=-1;
  while (index >= parserCnt) {
    rFromIndex = rToIndex+1;
    rToIndex = s.indexOf(parser,rFromIndex);
    if (index == parserCnt) {
      if (rToIndex == 0 || rToIndex == -1) return "";
      return s.substring(rFromIndex,rToIndex);
    } else parserCnt++;
  }
  return rs;
}

void loop() {
  // ...
}

void change(int howMany) {
  digitalWrite(3, LOW);
  temp = "";
  if (Wire.available()) {
    // Fetch the string
    while(Wire.available()) {
      char c = Wire.read();
      temp.concat(c);
    }

    payload = String(temp);
    flashState = split(payload, ',', 0).toInt();
    ledRed = split(payload, ',', 1);
    ledGreen = split(payload, ',', 2);
    ledBlue = split(payload, ',', 3);
    ledWhite = split(payload, ',', 4);

    byte bufRed[3];
    ledRed.toCharArray(bufRed, 3);

    byte bufGreen[3];
    ledGreen.toCharArray(bufGreen, 3);

    byte bufBlue[3];
    ledBlue.toCharArray(bufBlue, 3);

    byte bufWhite[3];
    ledWhite.toCharArray(bufWhite, 3);

    //setPixelColors(strip.Color(255, 0, 255, 0)); // WORKING

    currentColor = strip.Color(bufRed, bufGreen, bufBlue, bufWhite);
    setPixelColors(currentColor); // NOT WORKING
  }

  // Proof the I2C reading is finished
  delay(10);
}

I tried different data types, attached a status LED so I know it the I2C connection is working. When I test it with my UNO I get for R=255 G=0 B=255 W=0; like intended.
"Fun fact": On my UNO it works like a charm. It's just the ATTiny85 that won't accept that values(?)

New Code:
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN   4
#define LED_COUNT 20

static const int SLAVE_ADDRESS = 0x08;

int currentBrightness = 50;
int flashState = 0;
int ledRed, ledGreen, ledBlue, ledWhite;

volatile bool recvFlag = false;
volatile uint32_t currentColor = 0;
String temp;

String payload;

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);

void setup() {
  Serial.begin(9600);

  strip.begin();
  strip.show(); // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)

  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(change);
}

void setPixelColors(uint32_t color) {
  for (int p = 0; p < LED_COUNT; p++) {
    strip.setPixelColor(p, color);
    strip.show();
  }
}

String split(String s, char parser, int index) {
  String rs = "";
  int parserIndex = index;
  int parserCnt = 0;
  int rFromIndex = 0, rToIndex = -1;
  while (index >= parserCnt) {
    rFromIndex = rToIndex + 1;
    rToIndex = s.indexOf(parser, rFromIndex);
    if (index == parserCnt) {
      if (rToIndex == 0 || rToIndex == -1) return "";
      return s.substring(rFromIndex, rToIndex);
    } else parserCnt++;
  }
  return rs;
}

void loop() {
  if (recvFlag) {
    payload = String(temp);
    flashState = split(payload, ',', 0).toInt();
    ledRed = split(payload, ',', 1).toInt();
    ledGreen = split(payload, ',', 2).toInt();
    ledBlue = split(payload, ',', 3).toInt();
    ledWhite = split(payload, ',', 4).toInt();

    currentColor = strip.Color(ledRed, ledGreen, ledBlue, ledWhite);
    setPixelColors(currentColor);

    recvFlag = false;
  }
}

void change(int howMany) {
  temp = "";
  if (Wire.available()) {
    // Fetch the string
    while (Wire.available()) {
      char c = Wire.read();
      temp.concat(c);
    }

    // Proof the I2C reading is finished
    recvFlag = true;
  }
}

Honestly I don't know, why exactly this works on your Uno. You need to know, that the onReceive callback function is called from an ISR (Interrupt Service Routine). And inside an ISR you cannot do things, that rely on interrupts to work (like delay()), since only one interrupt can run at a time. I'm not sure, how the Neopixel library works in its core, but its probably not a good idea to run the show() method inside an ISR. Also generally you don't want to do long things inside an ISR, since it will block all the other interrupt powered functions of your microcontroller (for example I2C, Serial, time keeping via delay() or millis()...).
Instead the best practice is to only set a flag and then do these things in the main code (aka in loop()). So something like this:
volatile bool recv_flag = false;
volatile uint32_t current_color = 0;

void setup(){
    // Initiating everything like you already did in your code
    ...
}

void loop(){
    if(recv_flag){
        setPixelColors(currentColor);
        recv_flag = false;
    }
}
void change(int howMany) {
  digitalWrite(3, LOW);
  temp = "";
  if (Wire.available()) {
    // Doing all the data parsing like you already did
    ...
    // save current color in global variable
    currentColor = strip.Color(bufRed, bufGreen, bufBlue, bufWhite);
    // set flag
    recv_flag = true;
  }
}

Note that every variable, that will be written to inside an ISR should be declared as volatile. This prevents the compiler from optimizing it out, since it cannot know when the interrupts will happen. So it might think that this variable will never change and just replace it with a literal. The volatile keyword prevents that.

As mentioned above I'm even wondering, why your code works on the Uno. Though the main difference between the Uno and the Attiny84 is, that the Uno as a full fledged I2C hardware interface, while the Attiny84 only has an USI (Universal Serial interface). So the Uno has most of the I2C communication implemented in hardware, while the Attiny84 needs to run (probably interrupt based) code to do the communication.

I got it to work very shortly with 4 LEDs. Thought that was it. But then I tried with 20 LEDs - nothing happend again. I tried the example with "colorWipe" from the library. This works - so there is enough power and every LED is working.... And my API+Splitting has be working to because it worked for 2-3 tests before adding more LEDs. Any other ideas why THIS is happening now? :/

@dessi You can add the new code to your question. Then I will see, if I can help you with that. But please leave the current question text intact, so that others can learn from it

just updated the question with the new code

@dessi All in all your code looks good. You should declare temp as volatile, since you are writing to it inside the ISR. But I don't think this is the issue. Can you be sure, that the I2C communication works like intended? For example by lighting and LED, of the correct data was received? And can you check, if the code blocks at some point? If yes, where exactly does it block? Or does the code run, but it has no effect? That would be important information to debug further.

Maybe you get an issue with all the String use. It used dynamic memory allocation, which can make swiss cheese out of your memory (for reference Majenkos blog entry about this. Though that should only be a problem after some more I2C transactions.

Hey buddy, I just gave up... I'll do it now another way. I can transfer one single character without a problem so I'm pulling a value char of a custom matrix with me and have fixed LED values on the ATTiny85 :/ Project is a bit time critical so I have to fix that values :/ But thank you for your help anyway! Maybe someday some one needs the volatile stuff :)


text:
How to abort compilation based on a "wrong" value in a variable
I looking for some code that can generate a compile time error when a variable contains a wrong value.
I once started with this code in a c program:
#define FIFO_BUFFER_SIZE 8

#define FIFO_BUFFER_MASK ( FIFO_BUFFER_SIZE - 1 )

#if ( FIFO_BUFFER_SIZE & FIFO_BUFFER_MASK )
#error Fifo buffer size is not a power of 2
#endif

Rewrote that in C++ for a PC console application:
MovingAVG::MovingAVG( const unsigned long element_count ) {
    ptr = 0;
    siz = element_count;
    buf = new long[siz];
    msk = siz - 1;
    if( siz & msk ) {
        //std::cerr << "Buffer size is not a power of 2" << std::endl;
        //exit( -1 );
    }
}

Now I want to use something similar in an Arduino sketch.
Because there is not always a terminal connected to the serial port and exit() is useless in a microcontroller, I am looking for a way to abort compilation based on a value in a variable.
I don't give myself much chance to find anything but to be absolutely sure that there is really no way, I am dropping the question here.
I want to abort compiling (preferably with a message) when the parameter of a class-constructor contains an invalid value. Is that possible at all?
Actually I want to see a message before I start programming the device. I have posted a possible solution below, that will throw a message during linking, but that only works when I declare the class with a constant parameter:
class MovingAVG avg( 32 );

I'm not sure what to do with this, because it's probably better asked and answered on stackoverflow. It almost certainly has been. You are sort of looking for  static_assert. However, to make use of that you have actually have a constant expression (compile-time knowable calculation) and siz & msk isn't knowable at compile-time. So the better part trying to answer this would seem to be about transforming your rewrite into something that could be evaluated at compile time which is something you'd probably have make decisions about that we can't.

The second code throws the error at execution time, not at compilation time, thus not want to asked for. The first code snippet would do that. What exactly is the reason that you don't want to use it?

As suggested by timemage, I think static_assert() is the right
solution to your problem: it is the clearest and most semantically
correct. The problem is, you cannot plug static_assert() directly into
your code, as the expression you are testing is not a compile-time
constant.
I suggest you turn the argument of the constructor into a template
argument. This will guarantee it is a compile-time constant. As a bonus,
you will be able to use a statically-allocated buffer, which is nicer on
the RAM than dynamic allocation:
template <int siz> class MovingAVG {
    static const int msk = siz - 1;
    long buf[siz];
    int ptr = 0;
public:
    MovingAVG()
    {
        static_assert((siz & msk) == 0,
                "Element count should be a power of two.");
    }
};

This works:
MovingAVG<32> filter;

but this fails with “error: static assertion failed: Element count
should be a power of two.”:
MovingAVG<33> filter;

Yeah. If this is a design decision they can take, it's what I'd go with. Where we're not getting them to clarify, I'm voting this up.

Excellent coding. I like it!
Thank you.

This will do the trick, a very dirty trick but it will generate an error before I program/test the device.
That might save a few program cycles of the device, but more important it will not waste my time.
extern void ElementCountShouldBeAPowerOfTwo(void);

MovingAVG::MovingAVG( const unsigned long element_count ) {
    ptr = 0;
    siz = element_count;
    buf = new long[siz];
    msk = siz - 1;
    if( siz & msk ) {
        // This function will be removed by the optimiser
        // when the buffer size is a power of 2
        ElementCountShouldBeAPowerOfTwo();
    }
}

The compiler will not complain about the function "ElementCountShouldBeAPowerOfTwo" because it is marked as external. The linker will throw an error when the optimizer leaves the call in place.
In case the linker throws an error it will show:
undefined reference to `ElementCountShouldBeAPowerOfTwo()'

@chrisl
I've used both code in C and in C++ and I don't want to rewrite the C++ part.
You and @the_busybee were both right when you wrote that the error will not be shown at compile time.
I should have asked my question differently, I want to see a warning before I program the device :-)
I do not mark an answer because I expect this to fail in the future when the compiler is updated. For the moment, with Arduino 1.8.19, it does what I want.

It does not matter how you turn it, like any other method, this can work only if the value is known at compile time. And this is true only if the compiler sees all calls (in this case of the constructor) and their arguments are constant. So the usability is really very small. It would be good to add this to your question.

Because element_count is a method's parameter, it is by definition variable. Therefore, its value is generally not known at compile time.
Since the value of element_count is not known at compile time, you cannot get a compile time error for a wrong value.

Only if you use the method (here: a constructor) in the same translation unit as its definition, and if the arguments of all calls are known to the compiler (they are constants), the compiler will be able to evaluate siz & msk as a constant. Please note that some compilers need to receive optimization options for this.
Then you can use any approach that triggers a compiler diagnostic, for example like this one in C.


text:
FastLED web client, clicked on page to control on and off for ws2812
FastLED web client, when clicked on, all RGB on in red color. Clicked off, all RGB off/black. but not work.
Here is the full code, i don't know why it's loop always and web client stuck when i clicked on button on webpage
I can not turn on all LED, or turn off all LED.
#include <ESP8266WiFi.h>

#include <FastLED.h>
#define DATA_PIN 5
#define NUM_LEDS 300
CRGB leds[NUM_LEDS];
#define COLOR_ORDER GRB
#define BRIGHTNESS 254
#define FRAMES_PER_SECOND 30

// Enter your wifi network name and Wifi Password
const char* ssid = "";
const char* password = "";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// These variables store current output state of LED
String outputRedState = "off";

// Assign output variables to GPIO pins
const int redLED = 5;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);

  //FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
  LEDS.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);  // 初始化光带
  FastLED.setBrightness(BRIGHTNESS);                     // 设置光带亮度
  set_max_power_in_volts_and_milliamps(5, 2000);         // 光带电源管理（设置光带5伏特，500mA）

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {
  WiFiClient client = server.available();  // Listen for incoming clients

  if (client) {                     // If a new client connects,
    Serial.println("New Client.");  // print a message out in the serial port
    String currentLine = "";        // make a String to hold incoming data from the client
    currentTime = millis();
    previousTime = currentTime;
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
      currentTime = millis();
      if (client.available()) {  // if there's bytes to read from the client,
        char c = client.read();  // read a byte, then
        Serial.write(c);         // print it out the serial monitor
        header += c;
        if (c == '\n') {  // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // turns the GPIOs on and off
            if (header.indexOf("GET /2/on") >= 0) {
              Serial.println("RED LED is on");
              outputRedState = "on";

              fill_solid(leds, 300, CRGB::Red);
              FastLED.show();

            } else if (header.indexOf("GET /2/off") >= 0) {
              Serial.println("RED LED is off");
              outputRedState = "off";

              fill_solid(leds, 300, 0);
              FastLED.show();
            }

            // Display the HTML web page
            client.println("<!DOCTYPE html>");
            client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons
            client.println("");

            // Web Page Heading
            client.println("
My LED Control Server
");

            // Display current state, and ON/OFF buttons for GPIO 2 Red LED
            client.println("
Red LED is " + outputRedState + "
");
            // If the outputRedState is off, it displays the OFF button
            if (outputRedState == "off") {
              client.println("
<button class=\"button buttonOff\">OFF</button>
");
            } else {
              client.println("
<button class=\"button buttonRed\">ON</button>
");
            }

            client.println("");

            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else {  // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

but not work ... what does this mean?

all RGB on in red color ... what color are the supposed to be?

please clean up your code ... it is not indented properly, which makes errors very difficult to identify ... do yourself a favor, and spare yourself a world of hurt by learning how to format your code properly

look at the code at https://arduino.stackexchange.com/questions/91113/improve-sd-logger-0-02-of-data-isnt-logged-at-desired-rate .... that is what properly formatted code looks like

What i want to do is, access web link, http://x.x.x.x/2/on, LED strip of 300 NUM_LED on, and then if click button off, all LED strip off.
But i encounter device stuck when i clicked on, sometime LED not all switch on, and the web seems no response, then wait few seconds, try to click on again, sometime the LED can all on. But off LED totally not work.

people will not spend time looking at your code when it is presented unformatted ... it should be easy to read here, on this page ... people should not have to copy your code and paste it into an editor, format it and then read it

this may help ... https://www.martyncurrey.com/esp8266-and-the-arduino-ide-part-6-javascript-and-ajax .... look at the last sketch at AJAX With Acknowledgement   ................... here is another https://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/web-server-read-switch-using-AJAX/

just re-format. thanks. Sorry about my mistake

My expectation is all 300 Leds turn-on when i clicked button, but sometimes, only Led[0] on,  then i need to refresh "x.x.x.x/2/on" few times, then finally all Led on.


text:
Need help with debugging a heartrate/filter code (the heart rate calculation does not work)
The code below is a program that aims to apply the Pans-Thompskin algorithm to a signal attained from a ECG filter. The filter seems to work alright but it fails to blink the LED and collect a correct heart rate. The threshold might be the problem but I am struggling to see what is exactly correct.
    #include <bluefruit.h>

    int Threshold =160000;  // The measurement of the pulse.
    #define Threshold2 600 // The time of the pulse.
    #define INTERVAL 10 // Used for 25 millisecond timer.

    //intiation for heart rate
    unsigned long Timer;
    unsigned long Timer2;// Declares two timers.

    enum Heart { Wait, Idle, Update};
    enum Heart HeartState;// Used for states in the state machine below.

    float HBI;
    float SixHBI = 0.0;
    float HBIAverage = 0.0;
    float BPM = 0.0;
    int Count = 0;
    float filtered = 0;

    //dg filter initailization

    float xn1 = 0; //declare xn1 as zero
    float yn1 = 0;
    int k = 0;

    //moving average
    const int numReadings  = 30;
    int readings [numReadings];
    int readIndex  = 0;
    long total  = 0;
    long average;

    void HbiStd(float Value) {
      switch (HeartState) {
        case Wait:
          if (millis() - Timer2 > Threshold2) {
            HeartState = Idle;
            // If 600 milliseconds pass, go to Idle state.
          }
          break;
        case Idle:
          if (Value > Threshold) {
            HeartState = Update;
            // If Heartbeat is detected go into Update state.
          }
          break;

        case Update:
          HBI = millis() - Timer2; // Stores HBI time.
          Timer2 += HBI; // Increments Timer2.
          SixHBI += HBI; // Adds all HBI together.
          HBIAverage = SixHBI / 6000; // Calculates average HBI.
          BPM = (1 / HBIAverage) * (60); // Calculates BPM.
          Count++; // Increments what pass it is on.
          if (Count == 6) { // State machine goes through 6 times.

           // Serial.print(String(BPM) + String(" BPM\n"));
            // Prints the BPM.
            SixHBI = 0;
            Count = 0;
            // Resets the HBI and counter.
          }
          HeartState = Wait; // Go back to wait state.
          break;
      }
    }

    // put your setup code here, to run once:
    void setup() {
      // initialize digital pin LED_BUILTIN as an output.
      pinMode(LED_BUILTIN, OUTPUT);

      analogReadResolution(12);
      Serial.begin(115200);

      Timer = millis();      // Timer setup.
      Timer2 = millis(); // Initiate Timer2.
      HeartState = Wait; // State machine starts in wait state.
    }

    uint8_t INPUT_PIN = A1;

    void loop() {
      //varible for moving average filter

      if (millis() - Timer >= INTERVAL)
      { float raw = analogRead(INPUT_PIN);//read in data from input;
        filtered = Pan_thom_alg(raw);

        if (k % 3 == 0)
        {
          //int filtered = raw*raw;//square
          //   Serial.print(String(raw) + String(" raw\n"));
             Serial.print(String(filtered) + String(" filtered\n"));

          if (filtered > Threshold) {
            digitalWrite(LED_BUILTIN, HIGH);
            delay(50);
          }
          if (filtered < Threshold)  {
            digitalWrite(LED_BUILTIN, HIGH);
            //delay(10);
          }
        }
        k = k + 1;

        Timer += INTERVAL;
      }
    HbiStd(filtered); // State machine is called and always runs.
      //delay(10);

      //print after 3 entries are recorded for notch

    }

    float Pan_thom_alg(float raw) {
      float xn = raw;
      filtered = -0.00084 * yn1 + 0.999 * xn + -0.0008 * xn1;
      xn1 = xn;
      yn1 = filtered;

      //square data
      filtered = sq(filtered);

      // subtract the last reading:
      total = total - readings[readIndex];
      // read the sensor:
      readings[readIndex] = filtered;
      // add value to total:
      total = total + readings[readIndex];
      // handle index
      readIndex = readIndex + 1;
      if (readIndex >= numReadings) {
        readIndex = 0;
      }
      // calculate the average:
      average = total / numReadings;
      filtered = average;
      return filtered;
    }

This does not work on an 8 bit board such a s Uno: int Threshold =160000;  . What board are you using ?


text:
Problem cleaning string read from serial buffer
I am trying to move a stepper motor when a specific command is send to Arduino Mega 2560. This command is read character by character and stored in a string variable. Whenever a new command is sent, an act should be performed. However, it is not happening and I believe that the problem is in reading the sent command. I tried clearing the character and string variables, but to no avail. The sketch should run a loop that moves the motor clockwise or counterclockwise according to the command sent, which can be "crx*" for clockwise or "clx*" for anticlockwise, where "*" is the reading stop criterion of the characters.
Any suggestions what to do?
The script:
void(* resetFunc) (void) = 0;//Reset turn

// Stepper Motor X
const int stepPinX = 2; //X.STEP
const int dirPinX = 5; // X.DIR
const int pinEnable = 8;

// String read
char c;
String readString; //main captured String

// Unit steps
double av_len = 1.029; //medium length
double interval;
int cnt_steps = 0;
const int num_d = 200;//parameterized step
int fact = int(num_d / av_len);
int unit_len = fact * 1;//unit length (1mm)

void setup()
{
  // Sets pins as Outputs
  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
  pinMode(pinEnable, OUTPUT);

  digitalWrite(pinEnable, HIGH);//lock driver on cnc shield

  Serial.begin(115200);
  Serial.setTimeout(1);

  while (1) {

    while ( (Serial.available() == 0) )
    {

    }

    if (Serial.available())  {
      c = Serial.read();  //gets one byte from serial buffer
      //Serial.println(readString);
      if (c == '*') {
        Serial.println(readString);//test response
        if (readString == "crx") {
          cnt_steps = cnt_steps + 1;
          Serial.println(cnt_steps);
          digitalWrite(pinEnable, LOW);
          digitalWrite(dirPinX, HIGH);
          for (int x = 0; x < unit_len; x++) {
            digitalWrite(stepPinX, HIGH);
            delayMicroseconds(500);
            digitalWrite(stepPinX, LOW);
            delayMicroseconds(500);
          }

        }
        else if (readString == "clx") {
          cnt_steps += -1;
          Serial.println(cnt_steps);
          digitalWrite(pinEnable, LOW);
          digitalWrite(dirPinX, LOW);
          for (int x = 0; x < unit_len; x++) {
            digitalWrite(stepPinX, HIGH);
            delayMicroseconds(500);
            digitalWrite(stepPinX, LOW);
            delayMicroseconds(500);
          }
        }
         digitalWrite(pinEnable, HIGH);//lock driver on cnc shield
         c = (char)0;
         Serial.flush();
         readString = "";
         //resetFunc();
      }

      else {
        readString += c; //makes the string readString

      }
    }
  }
}

void loop() {

}

What are you sending to to your sketch? What does it print back on the serial port?

@EdgarBonet I send the commands "crx*" for clockwise rotation and "cry*" counterclockwise and "*" is the criterion for stopping character reading. The sketch should not print anything, but I asked it to print the commands sent to see if the reading was correct

This sketch will not understand "cry*": it expects either "crx*" or "clx*". 2. Re “The sketch should not print anything”: It should. It has some Serial.println() here and there. Let me ask again: what does it print to the serial port?

@EdgarBonet Ignore the "cry*". It was a typo. The correct one is "clx*". When receiving one of the aforementioned commands, the sketch must perform the loops contained within the conditions, as shown in the script. The Serial.println() are exclusively for making sure that the serial reading of the characters is being done. Can be commented. Depending on the conditions, the sketch acts on digital ports 2, 5 and 8 through the loops.

@EdgarBonet What happens is that the sketch executes the loops only after the first command is sent via serial. If I try a second time, nothing happens. It's as if the serial read doesn't match any of the "crx*" or "clx*" conditions.

How are you sending the commands to the sketch? Are you using the Arduino's serial monitor? If so, did you configure a line ending?

@EdgarBonet The problem is the line ending. Your guess was correct. Thanks a lot!

When sending text commands to an Arduino, it is common to define a
“line-oriented protocol”: each command is transmitted as a single line,
with the line ending signifying the end of the command. Common line
endings are carriage return (CR = '\r' = 0x0d = 13), line feed (LF =
'\n' = 0x0a = 10) and the sequence CR+LF.
The Arduino serial monitor is designed to support this convention by
automatically appending a line ending when you press “Send”. This line
ending can be configured to one of the following options:

No line ending
Newline (meaning “line feed”)
Carriage return
Both NL & CR (but CR is actually sent before LF)

In your case, you have chosen to use '*' as a command termination
character. For this to work, you have to choose “No line ending” and add
the “*” explicitly to the command. Alternatively, you may consider
changing your sketch so that it expects '\r' or '\n', which is a
more standard way of ending a command.

In addition to the good responses you have already received, I offer this.
For the Arduino, know the differences between String string and char arrays - there is much on this e.g., https://forum.arduino.cc/t/string-vs-string-vs-char-arrays-correct-approach/537468 . Many do not want to deal with the vagaries of String, but their use can be convenient.
Run this code:
String readstring = "xxx";

void setup() {
  Serial.begin(115200);
}

void loop() {
  if (Serial.available()) {
    readstring = Serial.readStringUntil('/n');
    Serial.print(readstring);
    Serial.println(" received");
    if (readstring.equals("crx")) {
      // move counter clockwise
      Serial.println("moving counter clockwise");
      // counterclockwise() <-- your function
    }
    else if (readstring.equals("clx") ) {
      // move clockwise
      Serial.println("moving clockwise");
      // clockwise() <-- your function
    }
    else {
      // bad command
      Serial.println("Bad command received - no action");
      // badcommand() <-- your function
    }
  }
}

If you add functions for the stepper, it may do what you want within a limited context and with using the Arduino serial monitor for input. Take a look at the output.

Note that the serial monitor is set to "no line ending". The terminating character '/n' is provide by your input in the serial monitor. It will do what you want, but again, many advise you stay away from String.


text:
Why is Arduino not visible in MacOS USB port list?
When I connected my Arduino Nano 33 IoT to my 2017 iMac (Ventura OS), the USB port did not show in the Arduino IDE port list, even after double-clicking the Nano reset button to put it in the right mode. Installing the special CH34X device driver on the Mac for the USB port did not work either.

This is a common problem as judged by online questions. The most common solution seems to be to install the special CH34X device driver on the Mac for the USB port, but I'm suspecting many people actually have the same problem I did.
Have you heard of a “charge only” USB cable? I was skeptical, but I discovered that four of my six USB-to-microUSB cables were charge-only! If you want to communicate data￼, for example connecting your computer to an Arduino microprocessor to program it, the charge-only cable will not work.
How to tell the difference? There are four wires in the data-capable cable. The outside two on the connector are for charging and the inside two are￼ for data. If yours is missing the inside connectors, then it’s charge-only. But if it has them, it still might be charge-only. Use a meter to do a continuity check to test if those middle connectors are connected. You’ll need to use a little fine wire to get inside the little micro-USB connector to make contact, and you’ll probably need somebody to help you by holding one end in contact.
As soon as I switched to a data-capable cable, the Nano USB port showed up in the IDE list! (After double-clicking the Nano reset button).
After I got it working, I uninstalled the special driver I had installed, and it continued to work.
Thanks ptillisch on Arduino Team!

This is a common issue with several Q+As already in the system. https://arduino.stackexchange.com/a/19856/37523

Yes, I looked at the others and they were either locked for further addition, or not quite appropriate.


text:
parseFloat() function
I have noticed that the parseFloat() function returns decimal numbers, but only up to 2 decimal places. Would it be possible for it to return the numbers with more decimal places? In case we need more precision

how did you get to that conclusion?

the parseFloat() function returns decimal numbers

No, it doesn't. It returns a binary (not decimal) floating point
number.

only up to 2 decimal places

The number returned by parseFloat() has 24 significant digits.
Binary digits, that is, also called “bits”. This is roughly equivalent
to 7 decimal digits.
That being said, beware of Serial.print(). This function converts
binary floating point numbers to decimal, and defaults to showing only
two digits after the radix point. You can ask for more:
Serial.println(x, 6);  // display x with 6 digits after the radix point.

If you want to gain some basic understanding of how floating point
numbers work, I recommend reading The Floating-Point Guide - What Every
Programmer Should Know About Floating-Point Arithmetic.

Thanks for the explanation, very educational!


text:
Arduino - Rural A-GPS Station
I'm trying to set up a GPS tracking solution in a rural area. The catch is the GPS trackers need to be lower cost (power and cash), because there will be a good amount of them.
So I'm trying to find a solution where I can benefit from A-GPS using a beacon of some sort to handle any cpu/server calculations while receiving GPS noise from the tracking devices (I think that's the idea). I've read up a bit on Mobile Station Assisted (MSA) GPS, but I'm not entirely sure what solutions there are to handle this.
Any help or advice is appreciated.
Example:
Arduino GPS tracker x 200 ---[RF comms/GPS noise]--->  Beacon/Tower  ---[Internet]---> GPS Server

I'm not sure I get what you want, but the A7 module of Ai-Thinker is a good GPRS module with GPS in it.

Thanks for the reply. I've already got GPS board solution (Simduino) but I'm just trying to find out how I can use Assisted GPS without having each tracker communicate with the GPS server. The solution I researched uses a single beacon to do all the grunt work for the Assisted server stuff and the trackers help it by sending low-power GPS noise.
I just don't know of any software solution to create this tracker->beacon->server communication

got it, sorry but i don't have any idea how to help you.


text:
Measuring ADC sample rate - Arduino DUE
I want to measure the conversion rate of an arduino DUE. I know I could just take it from the datasheet but I need to measure it because I am running the ADC in free running mode. Hints are welcome, I really don't know where to start.

What do you mean by "conversion rate"? Time to perform an acquisition? The easiest way is to save the millis value, perform 1000 acquisitions, then save again the millis value and make a subtraction. If the value is small, increase the number of acquisitions to have a reasonable time (1-10 s). Then make the division. For instance, 10000 acquisitions -> 7.92s means that each acquisition is 0.792ms, so 1262 samples/s

... Or make it toggle a pin each time the acquisition is done, then measure with an oscilloscope... Or tell us what you want to achieve, so hints can arrive...

Hi! Thanks for your answer! What I need is to measure the sample rate of the ADC because I need to do an Antialiasing Filter.
My expectative is to achieve a sample rate of 400kHz. If this value is fixed, it would be a lot more easy to do the processing that I want to do (FFT, Correlation)

If you plan on doing a real analysis on it a freerunning adc is not the way to go... You need precise timings for this. If you want to know what is the maximum speed you can achieve, go with the pin toggle or the multiple adc conversions techniques, but even if you manage to achieve a 400kHz sample rate then you have to actually process each sample and present results in less than 2.5 us...

No answer, but I'd be interested in this as well.

Measure how many times per second was main loop called:
bool secondPassed()
{
  static long lastTick = 0;
  long now = millis();

  if (now <= lastTick + 1000)
    return false;

  lastTick = now;
  return true;
}

int counter = 0;
void loop()
{
  analogRead(A0);
  counter++;
  if (secondPassed())
  {
    Serial.println(counter);
    counter = 0;
  }
}


text:
Measuring ADC sample rate - Arduino DUE
I want to measure the conversion rate of an arduino DUE. I know I could just take it from the datasheet but I need to measure it because I am running the ADC in free running mode.
Hints are welcome, I really don't know where to start.

If you have an oscilloscope or frequency counter, toggle a pin at each conversion and measure the frequency with your 'scope (and multiply by two).

Hi! Why I must multiply it by two?

Because toggling will give you one change- one 'edge'- per conversion and you need two edges to make a complete cycle, so the toggling acts as a divide-by-two flip-flop.

I think using an interrupt controlled measurement might provide the best results for you.
Note: you might get more complete feedback using the Arduino forum.
In general the process would look like this:

Set up an interrupt vector for the ADC peripheral (in this case support for ID37).
Setup ADC registers (res, clock etc)
Set up a timer to time the conversion.
Create Active flag and Timer variables for processing inbound timer values.
Start timer and ADC conversion.
Loop waiting for active flag to set (it gets set in the ISR), when detected do average or store timer values, then clear active flag.

This methodology times each individual conversion using a high speed timer, which could mean a lot of values to process. You could equally not time each conversion, but simply set up say a 1 second timer, and count the total number of conversions in 1 second for your particular ADC configuration.
Peripheral ISR

Waiting for Interrupt.
In peripheral ISR, read and store timer value, reset timer, set active flag
Return from ISR

To expand on Jack Creasey's answer, timestamping the samples as they are taken is the most direct way to do it. If you have a block of N samples, the average sample period is simply
$$\frac{Time_{last} - Time_{first}}{N - 1}$$
(Note that there are N-1 time intervals between N samples.)
If you want the sample rate, just invert the expression:
$$\frac{N - 1}{Time_{last} - Time_{first}}$$

However, you can also infer the sample rate from a known signal. For example, you can feed a tone signal into the ADC and count the number of samples between some well-defined feature of the waveform, such as rising zero crossings. If you count N samples, then the sample rate is N times the tone frequency. Averaging over multiple cycles of the tone will give you a more precise measurement.

Apparently MathJax is not enabled here.


text:
ESP32DevkitC-V4 interfacing ADS8698 (18 bit ADC)
I am currently working on an SPI interface to measure the voltage of an amplified sensor bridge signal. I am using an ESP32 module to control the device. Since I am logging the data to an SD card, I chose to connect the ADS8698  (Datasheet) via the default HSPI interface (see below). Unfortunately I am receiving zero readings only - no matter what commands I send. I tried to illustrate my setup as detailled as possible down below - if you think that I am missing something, do not hesitate to remind me ;).
My Question is: Do you have any ideas on things that I missed facing this issue? - Thanks in advance!
Related Topics
I know that there are related topics in Source A, Source B and Source C, from which however none does lead to a final answer. My research in the last couple of days showed, that several people face issues with this ADC, which lead me to start a new thread to reactive this topic and collect all referenced ressources without creating a duplicate.
Research
One possible reason for the communication problem could be, that I am not using DMA to interface via SPI which might lead to timing problems. I wasn't able to test any ESP32 spi dma library, since the only library I found (https://github.com/hideakitai/ESP32DMASPI) does not compile. Furthermore I found discussions, whether DMA is actually more precise with respect to timing or not, which did not lead to a verified result (the multi-core structure of the ESP32 might even be faster).
Hardware Setup & Wiring

The ADS8698 circuit is shown above, it is connected to the ESP32 as follows:
| ESP32DevKitCV4 | ADS8698IDBT|
| -------- | -------------- |
| 12 (HSPI_MISO)   | SDO|
| 13 (HSPI_MOSI)   | SDI|
| 14 (HSPI_SCK)   | SCK |
| 15 (HSPI_CS)   | CS |
Current Software State
#include <Arduino.h>
#include <SPI.h>

//ADC Settings
#define CHANNEL 0
#define SCLK_SPEED 18e6
#define BUFFER_SIZE 5
#define CS_PIN 15
#define RST_PIN 32
#define SPI_MODE SPI_MODE0

//objects
SPIClass * hspi = NULL;
uint8_t receivedBytes[3];

void setup() {
  Serial.begin(115200);                 //initialize serial communication

  pinMode(CS_PIN, OUTPUT);              //set pinmode
  pinMode(RST_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);           //no communication
  digitalWrite(RST_PIN, HIGH);          //normal operation

  hspi = new SPIClass(HSPI);
  hspi->begin();
  hspi->beginTransaction(SPISettings(SCLK_SPEED,MSBFIRST, SPI_MODE));

  digitalWrite(CS_PIN,LOW);
  hspi->transfer(0xC0);                 //manual read Ch0
  hspi->transfer(0x00);
  Serial.println(hspi->transfer(0x00));
  digitalWrite(CS_PIN,HIGH);
}

void loop() {
  digitalWrite(CS_PIN,LOW);
  hspi->transfer(0);                    // wait 16 clk cycles for conversion, 0x00 = continue Operation
  hspi->transfer(0);
  receivedBytes[0] = hspi->transfer(0); //read 3 bytes
  receivedBytes[1] = hspi->transfer(0);
  receivedBytes[2] = hspi->transfer(0);
  digitalWrite(CS_PIN,HIGH);

  for(int i=0;i<3;i++)
  {
    Serial.print(receivedBytes[i],BIN);
    Serial.print(":");
  }
  Serial.println();
  delay(1000);
}```

Based on the page 48-49 of the datasheet, your implementation does not follow the timing and signalling of the READ operation.

Thank you vor your reply!
Could you please specify in which regards I misunderstood the read signaling? I assume you refer to table 8 and figure 92 on page 49 of the datasheet. Since I have put the ADC in MANUAL_READ_CH0 mode in the setup, an address / cmd of 0x00 should be fine in terms of continued operation. I thought that the manual read command would lead to an SDO signal output according to table 14 on page 54. The latter starts after the 16th falling clk cycle whereas the read command outputs data following the first byte transferred. Maybe that lead to confusion.

Table 14 describes the detail of the received data. But the table 8 and figure 92 shows how the command should be sent and when the data will arriving. The timing diagram shows that data start to coming in on 3rd byte, and DS line won't be drop until data has been received. Your code send the command 0xC000, then drop the CS line. What it should be according the time timing diagram should be digitalWrite(CS_PIN,LOW); hspi->transfer(0xC0); hspi->transfer(0x00); receivedBytes[0] = hopi->trnasfer(0); ... digitalWrite(CS_PIN,HIGH); .

Read the first program of 8.5.2.1, "The program register is a 16-bit read or write register. There must be a minimum of 24 SCLKs after the CS falling edge for any read or write operation to the program registers".

Thank you for your explanation. You are right, I did not consider the accurate timing for the command register write sequence. I updated my code and added a line in void setup to read back the value written to the register - which is obviously zero. Unfortunately I am still getting exclusively zero readings in my main loop. Did I miss something else you mentioned?