I used the RTC, from an Arduino MKR 1300 with integrated RTC, as an alarm that will trigger a "boolean"(it's an integer) that will tell the loop to run a certain method every minute and then send some data every 5 minutes. It's on an active loop but the method to send data ONLY WORKS if it's inside the loop (no idea why). The problem is the RTC apparently is subtracting 1 second at every 8 hours or so after a few days the timing might come off and instead of sending data every xx:10:xx-xx:15:xx it might send data xx:09:xx-xx:14:xx.
Here's the code:
#include <EmonLib.h>
#include <RTCZero.h>
#include <MKRWAN.h>
EnergyMonitor emon1;
EnergyMonitor emon2;
EnergyMonitor emon3;
RTCZero rtc;
LoRaModem modem;
String appEui = "1234567891011121";
String appKey = "ffffffffffffffffffffffffffffffff";
/* INITIAL_TIME */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 0;
const byte day = 17;
const byte month = 12;
const byte year = 18;
byte second_alarm = 0;
byte minute_alarm = 0;
byte hour_alarm = 0;
byte INTERVAL = 60;
int SEND_LOOP = 5;
int totalKW;
int counter= 0;
int alarm_Triggered = 0;
void setup()
{
Serial.begin(115200);
if (!modem.begin(EU868)) {
Serial.println("Failed to start module");
while (1) {}
};
Serial.print("Your module version is: ");
Serial.println(modem.version());
Serial.print("Your device EUI is: ");
Serial.println(modem.deviceEUI());
Serial.println("Connecting");
int connected = modem.joinOTAA(appEui, appKey);
if (!connected) {
Serial.println("Something went wrong; are you indoor? Move near a window and retry");
while (1) {}
}
Serial.println("Connected");
// Set poll interval to 60 secs.
modem.minPollInterval(60);
analogReadResolution(9);
emon1.current(1, 53);
emon2.current(2, 53);
emon3.current(3, 53);
counter= 0;
rtc.begin(); // initialize RTC
rtc.setAlarmTime(hour_alarm, minute_alarm, second_alarm);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.attachInterrupt(triggerAlarm);
// Set the time
rtc.setHours(hours);
rtc.setMinutes(minutes);
rtc.setSeconds(seconds);
// Set the date
rtc.setDay(day);
rtc.setMonth(month);
rtc.setYear(year);
}
void loop() {
if (alarm_Triggered == 1) {
dataMonitor();
alarm_Triggered = 0;
}
}
void dataMonitor() {
int totalWatt = 0;
unsigned long delay_send = 0;
int sending = 0;
double Irms1 = emon1.calcIrms(600);
if (Irms1 < 0.3) Irms1 = 0;
double Watt1 = Irms1 * 230;
double Irms2 = emon2.calcIrms(600);
if (Irms2 < 0.3) Irms2 = 0;
double Watt2 = Irms2 * 230;
double Irms3 = emon3.calcIrms(600);
if (Irms3 < 0.3) Irms3 = 0;
double Watt3 = Irms3 * 230;
totalWatt = Watt1 + Watt2 + Watt3;
totalKW = totalKW + totalWatt / 1000;
Serial.println(counter);
sendDataChecker(Irms1, Irms2, Irms3);
setAlarm();
counter= counter+ 1;
}
void sendDataChecker(double Irms1, double Irms2, double Irms3) {
if (counter== SEND_LOOP) {
double IrmsTotal = Irms1 + Irms2 + Irms3;
String msg = "{\"id\":\"avac_aud2\",\"kW\":" + String(totalKW) + ", \"current\":" + String(IrmsTotal) + "}";
int err;
Serial.println("Ready to Send");
modem.beginPacket();
modem.print(msg);
err = modem.endPacket(true);
Serial.println("Sent1");
if (err > 0) {
//message sent correctly
Serial.println("Sent");
counter= 0;
totalKW = 0;
} else {
Serial.println("ERR");
counter= 0;
}
}
}
void setAlarm() {
second_alarm += INTERVAL;
if (second_alarm >= 60) {
minute_alarm++;
second_alarm = 0;
}
if (minute_alarm >= 60) {
hour_alarm++;
minute_alarm = 0;
}
if (hour_alarm >= 24) {
hour_alarm = 0;
}
rtc.setAlarmTime(hour_alarm, minute_alarm, second_alarm);
}
void triggerAlarm() {
alarm_Triggered = 1;
}
Related
I have implemented recording and playing back audio from a microphone in C++. The next step is to process the audio data for speech recognition. For this I want to write them to large buffers so that there are no word breaks. To do this, I implemented copying to large buffers using the memcpy function. Unfortunately, it doesn't work because only part of words can be recognized. What is my mistake and can this buffer manipulation be done in a more convenient way?
My code:
#include <stdio.h>
#include <Windows.h>
#include <mmsystem.h>
#include <iostream>
#include <fstream>
using namespace std;
#pragma comment(lib, "winmm.lib")
#define Samples 16000
#define NUM_FRAMES Samples*2
#define Channels 1
const int NUM_BUF = 4;
int main()
{
HWAVEIN inStream;
HWAVEOUT outStream;
WAVEFORMATEX waveFormat;
WAVEHDR buffer[NUM_BUF];
waveFormat.cbSize = 0;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = Channels;
waveFormat.nSamplesPerSec = Samples;
waveFormat.wBitsPerSample = 16;
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nBlockAlign * waveFormat.nSamplesPerSec;
HANDLE event = CreateEventA(NULL, TRUE, FALSE, "waveout event");
MMRESULT res = MMSYSERR_NOERROR;
res = waveInOpen(&inStream, WAVE_MAPPER, &waveFormat, (unsigned long)event, 0, CALLBACK_EVENT);
if (res != MMSYSERR_NOERROR) {
printf("error in waveInOpen\n");
return -1;
}
res = waveOutOpen(&outStream, WAVE_MAPPER, &waveFormat, (unsigned long)event, 0, CALLBACK_EVENT);
if (res != MMSYSERR_NOERROR) {
printf("error in waveOutOpen\n");
return -2;
}
short int *_pBuf;
size_t bpbuff = 16000*2;
_pBuf = new short int [bpbuff * NUM_BUF];
for ( int i = 0; i < NUM_BUF; i++ )
{
buffer[i].lpData = (LPSTR)&_pBuf [i * bpbuff];
buffer[i].dwBufferLength = bpbuff*sizeof(*_pBuf);
buffer[i].dwFlags = 0L;
buffer[i].dwLoops = 0L;
waveInPrepareHeader(inStream, & buffer[i], sizeof(WAVEHDR));
}
ResetEvent(event);
for (int index = 0; index < NUM_BUF; index++) // queue all buffers for input
waveInAddBuffer(inStream, &buffer[index], sizeof(WAVEHDR));
waveInStart(inStream);
int len_buff = buffer[0].dwBufferLength*6 + 1;
int limit_buff = buffer[0].dwBufferLength*5 + 1;
int size = buffer[0].dwBufferLength;
int rl = 0;
int flagg = 0;
char * buff1 = new char[len_buff];
char * buff2 = new char[len_buff];
int flag_buf = 0;
int flag1 = 0, flag2 = 0;
int i = 0;
int inIndex = 0, outIndex = 0; // the next input and output to watch
while (true) {
if (buffer[inIndex].dwFlags & WHDR_DONE & flagg!=1)
{
flagg = 1;
waveInAddBuffer(inStream, &buffer[inIndex], sizeof(WAVEHDR));
inIndex = (inIndex + 1) % NUM_BUF;
}
if (buffer[outIndex].dwFlags & WHDR_DONE & flagg!=0) {
flagg = 0;
if (flag_buf == 0)
{
if (rl<limit_buff)
{
cout << rl << endl;
if (flag1 == 0)
{
//strcpy(buff1, buffer[outIndex].lpData);
memcpy(buff1, buffer[outIndex].lpData, size);
flag1 = 1;
rl = size + 1;
}
else
{
//strcat(buff1, buffer[outIndex].lpData);
memcpy(buff1 + rl, buffer[outIndex].lpData, size);
rl = rl + size;
}
}
else
{
//recognize buff1
flag_buf = 1;
flag1 = 0;
rl = 0;
}
}
else
{
if (rl<limit_buff)
{
if (flag2 == 0)
{
memcpy(buff2, buffer[outIndex].lpData, size);
flag2 = 1;
rl = size + 1;
}
else
{
memcpy(buff2 + rl, buffer[outIndex].lpData, size);
rl = rl + size;
}
}
else
{
//recognize buff2
flag_buf = 0;
flag2 = 0;
rl = 0;
}
}
waveOutWrite(outStream, &buffer[outIndex], sizeof(WAVEHDR));
outIndex = (outIndex + 1) % NUM_BUF;
printf("N_buff_%i %i\n",outIndex , i);
i++;
}
}
for (int index = 0; index < 4; index++)
waveInUnprepareHeader(inStream, &buffer[inIndex], sizeof(WAVEHDR));
free(buffer);
}
I am trying to make brown noise in C++, and to play the sound of it. You can hear the brown noise, but I constantly hear clicking in the background and I don't know why.
Here is my code:
#include <xaudio2.h>
#include <iostream>
#include <random>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
XAUDIO2_BUFFER buffer;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
BYTE pDataBuffer[2*l];
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
w[0] = w[l - 1] + dis(gen)*ampl;
for (int t = 1; t < l; t++) {
tt = (float)(t + frame*l); //total time
w[t] = w[t - 1] + dis(gen)*ampl;
if (w[t] > ampl) {
cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16*(intw/pow16[k]));
intw -= bytw[k]*(pow16[k]/16);
bytw[k] += (BYTE)(intw/(pow16[k]/16));
intw -= (intw/(pow16[k]/16))*pow16[k]/16;
}
pDataBuffer[2*t] = bytw[0];
pDataBuffer[2*t + 1] = bytw[1];
}
cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) {}
}
buffer.AudioBytes = 2*l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
cout << "start";
while (loop()) {}
return 0;
}
The line before the for-loop in loop() is to make sure that the first element nicely attaches itself to the last element of the previous iteration.
To make sure that w doesn't go over ampl or under -ampl, I have added a couple lines that make them bounce back, and I make it output "upper edge" or "lower edge" respectively so that you know when this is happening. As you notice, the clicking also happens when the w is not near the edges.
As a test to make sure it isn't because of XAudio2 being implemented wrongly, you can comment the first line in loop() that defines the first element of w; make the for-loop (in the next line) start from 0; comment the lines that create the brown noise; and uncomment one of the two lines after that: the first line to hear a sine wave sound, the second line to hear a square wave sound (both with a frequency of about 44100/1000 = 44.1 Hz, which you can change around by changing how p is initialized in init()). You will (hopefully) hear a clean sine/square wave sound.
So what is going wrong?
You have two issues in your code:
You only have a single buffer therefore its near impossible to submit a new buffer for playing quickly enough after the buffer stops playing for there to not be a gap between buffers. You are also modifying the buffer data whilst it is being played which will corrupt the output. You should use multiple buffers. With enough buffers this would also allow you to add some short sleeps to your while loop which is checking BuffersQueued to reduce the CPU usage.
You never set pDataBuffer[0] or pDataBuffer[1] so they will always be 0.
This code works:
#include <xaudio2.h>
#include <iostream>
#include <random>
#include <array>
#include <thread>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
const size_t bufferCount = 64;
std::array<XAUDIO2_BUFFER, bufferCount> buffers;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
std::array<std::array<BYTE,2 * l>, bufferCount> pDataBuffers;
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
float prevW = w[l - 1];
auto& pDataBuffer = pDataBuffers[frame & (bufferCount-1)];
auto& buffer = buffers[frame & (bufferCount - 1)];
for (int t = 0; t < l; t++) {
tt = (float)(t + frame * l); //total time
w[t] = prevW + dis(gen) * ampl;
if (w[t] > ampl) {
//cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
//cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
prevW = w[t];
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16 * (intw / pow16[k]));
intw -= bytw[k] * (pow16[k] / 16);
bytw[k] += (BYTE)(intw / (pow16[k] / 16));
intw -= (intw / (pow16[k] / 16)) * pow16[k] / 16;
}
pDataBuffer[2 * t] = bytw[0];
pDataBuffer[2 * t + 1] = bytw[1];
}
//cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(1); }
}
buffer.AudioBytes = 2 * l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer.data();
buffer.Flags = 0;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = { 0 };
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
while (loop()) {}
return 0;
}
I haven't tried to follow all of your logic but it seems over complicated and could definitely be simplified.
The massive use of global variables is also not a great way to write a program. You should move variables inside the functions where possible, otherwise either pass them to the function as arguments or use a class to hold the state.
I'm currently working on a project that is mostly run on a Raspberry pi, but has tasks complete by microcontroller. Currently, my RPi is sending a string of integers separated by commas to a microcontroller (ESP 32 Thing plus), the microcontroller is then converting that into an intergern array and using those values to set the level of a PWM output. Simultaneously, the microcontroller is reading an analogue input and storing those values into another int array, this data needs to be sent back to the RPi. I've been trying for ages to convert this into a string to be sent back, which works for the most part using sprintf() and strcat(), but it only works when I have just the number values. It would be good to have commas separate the numbers, but as soon as I do that, I get a really weird out put and the numbers start changing from their actual value to "1792" or 44" and it cycles between them or if I us a float array then I get 6 variables correct and then the rest are changes to "512".
Its a lot to send but possibly better to see the whole thing.
#include <stdio.h>
// variables to hold, parse and manipulate data
const byte numChars = 64;
const byte stringLength = 255;
char receivedChars[1000];
char tempChars[1000];
char messageFromPC[stringLength] = {0};
int MyArray[] = {};
int LaserMeasurmentArray[200] = {};
char LaserMeasurmentString[] = "";
int BrakeArray [60] = {10,200,600,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,500,0,0,0};
int AccelleratingArray [60] = {10,200,600,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,500,0,0,0};
int TimeStamps[200] = {};
char TimeStampString = '0';
int i = 0;
int SizeOfArray = 0;
int MyArraySize = 0;
int Counter = 0;
int CounterLaser = 0;
//Clutch/Brake Setup
int ClutchPin = 33;
int BrakePin = 27;
int ButtonPin = 32;
int ButtonState = LOW;
int Delay = 10;
int PWM = 1023;
const int LaserPin = A0;
//Millis variables
unsigned long PreviousMillisAccel = 0;
unsigned long PreviousMillisLaser = 0;
unsigned long CurrentMillis = 0;
const int PeriodWrite = 8;
const int PeriodRead = 5;
//Action States
boolean newData = false;
boolean SendData = false;
boolean AccellerationInProgress = false;
boolean BrakingInProgress = false;
//============
void setup() {
Serial.begin(115200);
//ledcSetup(ledChannel, freq, resolution);
ledcSetup(1, 5000, 10);
ledcSetup(2, 5000, 10);
//ledcAttachPin(LED, ledChannel);
ledcAttachPin(ClutchPin, 1);
ledcAttachPin(BrakePin, 2);
pinMode(LaserPin, INPUT);
pinMode(ButtonPin, INPUT_PULLUP);
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
//Serial.print(receivedChars);
//Serial.println();
parseData();
//showParsedData();
//ButtonPress();
ButtonState = HIGH;
while (ButtonState == HIGH) {
ButtonState = digitalRead(ButtonPin);
}//while
Serial.println();
Serial.print("Accellerating");
Serial.println();
PWMSignal();
Serial.println();
Serial.print("Deccellerating");
Serial.println();
PWMSignal2();
newData = false;
ButtonState = HIGH;
while (ButtonState == HIGH) {
ButtonState = digitalRead(ButtonPin);
}//While
SendReply();
}
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= stringLength) {
ndx = stringLength - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//============
void parseData() { // split the data into its parts
i = 0;
String serialResponse = tempChars;
char buf[sizeof(tempChars)];
serialResponse.toCharArray(buf, sizeof(buf));
char *p = buf;
char *str;
while ((str = strtok_r(p, ",", &p)) != NULL && i < 1180 ){ // delimiter is the comma
MyArray[i] = atoi(str);
//Serial.println(str);
MyArraySize = i;
i++;
}//While
}//parseData
//============
void showParsedData() {
for(int i = 0; i < MyArraySize+1; i++){
Serial.print(MyArray[i]);
Serial.print(",");
}//For
Serial.println();
}
void PWMSignal(){
int Counter = 0;
CounterLaser = 0;
CurrentMillis = millis();
AccellerationInProgress = true;
while(AccellerationInProgress == true) {
CurrentMillis = millis();
if (Counter == 60){
AccellerationInProgress = false;
}//if
else if (CurrentMillis - PreviousMillisAccel >= PeriodWrite) {
ledcWrite(1, AccelleratingArray[Counter]);
//Serial.println(AccelleratingArray[Counter]);
Counter = Counter + 1;
PreviousMillisAccel = CurrentMillis;
}//else if
if (CurrentMillis - PreviousMillisLaser >= PeriodRead){
LaserMeasurmentArray[CounterLaser] = analogRead(LaserPin);
Serial.println(LaserMeasurmentArray[CounterLaser]);
TimeStamps [CounterLaser] = CurrentMillis;
PreviousMillisLaser = CurrentMillis;
CounterLaser = CounterLaser + 1;
}//if
}//While Accel
ledcWrite(1, 0);
}//PWMSignal
void PWMSignal2() {
int Counter = 0;
BrakingInProgress = true;
while(BrakingInProgress == true) {
CurrentMillis = millis();
if (Counter == 60){
BrakingInProgress = false;
}//if
else if (CurrentMillis - PreviousMillisAccel >= PeriodWrite) {
ledcWrite(2, BrakeArray[Counter]);
//Serial.println(BrakeArray[Counter]);
Counter = Counter + 1;
PreviousMillisAccel = CurrentMillis;
}//else if
if (CurrentMillis - PreviousMillisLaser >= PeriodRead){
LaserMeasurmentArray[CounterLaser] = analogRead(LaserPin);
Serial.println(LaserMeasurmentArray[CounterLaser]);
TimeStamps [CounterLaser] = CurrentMillis;
PreviousMillisLaser = CurrentMillis;
CounterLaser = CounterLaser + 1;
}//if
}//While Accel
ledcWrite(1, 0);
ledcWrite(2, 0);
}//PWMSignal2
void SendReply() {
//Serial.println("Laser Data");
//Serial.println(sizeof(LaserMeasurmentArray));
for (int i = 0; i<=200; i++){
Serial.println(LaserMeasurmentArray[i]);
}
for (int i = 0; i <= 200; i++){
int buff = LaserMeasurmentArray[i];
char str [7];
sprintf(str,"%u" ,buff);
strcat(LaserMeasurmentString,str);
strcat(LaserMeasurmentString, ",");
}
Serial.print(LaserMeasurmentString);
}//sendReply
If someone could point out what I am doing wrong or have misunderstood, that would be great, thank you.
I have a code that works on Maple Mini but I have to change hardware to Nucleo F030r8 because it has more ADC's and it totally sucks.
In the modbus_update() function there is a check of the size of inputBuffer and the following code should run only if the value of this variable is bigger than 0.
if (inputBuffer > 0 && micros() - microsFlag >= T3_5) {
...
}
But it runs even if value of inputBuffer is exactly 0. The strange thing is that this code (with different serial ports opening method) runs perfectly on Maple Mini. Does anyone have any idea what can be be the problem?
Here is the whole code:
#define BAUDRATE 19200
#define RE_PIN PC12
#define TE_PIN PF4
#define LED_PIN LED_BUILTIN
#define SLAVE_ID 1
#define BUFFER_SIZE 256 // max frame size
#define READ_INTERNAL_REGISTERS 4 // function code
#define MIN_REGISTER_ADDRESS 30001
#define MAX_REGISTER_ADDRESS 30020
#define MAX_SENSORS_PER_ROW 10
#define SENSORS_PER_ROW 7
#define MAX_ROWS 2
#define ROWS 1
#define DEBUG true
#define INVALID_VALUE 0x7FFF
const byte INPUTS[] = {PA0, PA1, PA4, PB0, PC1, PC0};
unsigned char frame[BUFFER_SIZE];
unsigned char functionCode;
unsigned int T1;
unsigned int T1_5; // inter character time out
unsigned int T3_5; // frame delay
unsigned long millisFlag = 0;
unsigned long microsFlag = 0;
unsigned char inputBuffer = 0;
int dlyCounter = 0;
int16_t sensorVals[MAX_SENSORS_PER_ROW * MAX_ROWS];
/*
HardwareSerial *modbus = &Serial;
HardwareSerial Serial1(PA10, PA9);
HardwareSerial *debug = &Serial1;
*/
HardwareSerial debug(PA10, PA9);
void setup() {
pinMode(LED_PIN, OUTPUT);
for (int i = 0; i < SENSORS_PER_ROW; i++) {
pinMode(INPUTS[i], INPUT_PULLUP);
}
debug.begin(BAUDRATE, SERIAL_8E1);
modbus_configure(BAUDRATE, 0); // baud rate, low latency
microsFlag = micros();
}
void loop() {
readSensorVals(100);
modbus_update();
}
unsigned int modbus_update() {
unsigned char overflow = 0;
while (Serial.available()) {
if (overflow) {
Serial.read(); // empty the input buffer
} else {
if (inputBuffer == BUFFER_SIZE) {
overflow = 1;
} else {
frame[inputBuffer] = Serial.read();
inputBuffer++;
}
}
microsFlag = micros();
}
// If an overflow occurred return to the main sketch
// without responding to the request i.e. force a timeout
if (overflow) {
debug.println("Error: input buffer overflow");
return 0;
}
// if inter frame delay occurred, check the incoming package
if (inputBuffer > 0 && micros() - microsFlag >= T3_5) {
debug.println("\nIncoming frame:");
for (int i = 0; i < inputBuffer; i++) {
debug.print(frame[i], HEX);
debug.print(" ");
}
debug.println();
// check CRC
unsigned int crc = ((frame[inputBuffer - 2] << 8) | frame[inputBuffer - 1]); // combine the crc Low & High bytes
if (calculateCRC(frame, inputBuffer - 2) != crc) {
debug.println("Error: checksum failed");
inputBuffer = 0;
return 0;
}
debug.println("CRC OK");
// check ID
unsigned char id = frame[0];
if (id > 242) {
debug.println("Error: Invalid ID");
inputBuffer = 0;
return 0;
}
// check if it's a broadcast message
if (id == 0) {
debug.println("Broadcast message");
inputBuffer = 0;
return 0;
}
if (id != SLAVE_ID) {
debug.println("Not my ID");
inputBuffer = 0;
return 0;
}
debug.println("ID OK");
// check function code
functionCode = frame[1];
if (functionCode != READ_INTERNAL_REGISTERS) {
debug.println("Exception: illegal function");
exceptionResponse(1);
inputBuffer = 0;
return 0;
}
debug.println("Function code OK");
// check frame size (function 4 frame MUST be 8 bytes long)
if (inputBuffer != 8) {
// some workaround here:
//if (inputBuffer != 8 || !(inputBuffer == 9 && frame[inputBuffer] == 0)) {
debug.println("Error: inaccurate frame length");
inputBuffer = 0;
return 0;
}
debug.println("Frame size OK");
// check data address range
unsigned int noOfRegisters = ((frame[4] << 8) | frame[5]); // combine the number of register bytes
if (noOfRegisters > 125) {
debug.println("Exception: illegal data address");
exceptionResponse(2);
inputBuffer = 0;
return 0;
}
unsigned int firstRegAddress = ((frame[2] << 8) | frame[3]); // combine the starting address bytes
debug.print("First address: ");
debug.println(firstRegAddress);
unsigned int lastRegAddress = firstRegAddress + noOfRegisters - 1;
debug.print("Last address: ");
debug.println(lastRegAddress);
unsigned char noOfBytes = noOfRegisters * 2;
unsigned char responseFrameSize = 5 + noOfBytes; // ID, functionCode, noOfBytes, (dataLo + dataHi) * number of registers, crcLo, crcHi
unsigned char responseFrame[responseFrameSize];
responseFrame[0] = SLAVE_ID;
responseFrame[1] = 0x04;
responseFrame[2] = noOfBytes;
unsigned char address = 3; // PDU starts at the 4th byte
for (int index = (int)(firstRegAddress - MIN_REGISTER_ADDRESS); index <= (int)(lastRegAddress - MIN_REGISTER_ADDRESS); index++) {
int16_t temp = (index >= 0 && index < MAX_ROWS * MAX_SENSORS_PER_ROW) ? sensorVals[index] : INVALID_VALUE;
responseFrame[address] = temp >> 8; // split the register into 2 bytes
address++;
responseFrame[address] = temp & 0xFF;
address++;
}
unsigned int crc16 = calculateCRC(responseFrame, responseFrameSize - 2);
responseFrame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
responseFrame[responseFrameSize - 1] = crc16 & 0xFF;
debug.println("Frame to send:");
for (int i = 0; i < responseFrameSize; i++) {
debug.print(responseFrame[i], HEX);
debug.print(" ");
}
debug.println();
sendPacket(responseFrame, responseFrameSize);
inputBuffer = 0;
while (Serial.available()) { // empty input buffer
Serial.read();
}
}
}
void modbus_configure(long baud, unsigned char _lowLatency) {
Serial.begin(baud, SERIAL_8E1);
pinMode(TE_PIN, OUTPUT);
pinMode(RE_PIN, OUTPUT);
rxEnable(); // pin 0 & pin 1 are reserved for RX/TX. To disable set TE and RE pin < 2
if (baud == 1000000 && _lowLatency) {
T1 = 1;
T1_5 = 1;
T3_5 = 10;
} else if (baud >= 115200 && _lowLatency) {
T1 = 50;
T1_5 = 75;
T3_5 = 175;
} else if (baud > 19200) {
T1 = 500;
T1_5 = 750;
T3_5 = 1750;
} else {
T1 = 10000000 / baud;
T1_5 = 15000000 / baud; // 1T * 1.5 = T1.5
T3_5 = 35000000 / baud; // 1T * 3.5 = T3.5
}
}
void exceptionResponse(unsigned char exception) {
unsigned char responseFrameSize = 5;
unsigned char responseFrame[responseFrameSize];
responseFrame[0] = SLAVE_ID;
responseFrame[1] = (functionCode | 0x80); // set the MSB bit high, informs the master of an exception
responseFrame[2] = exception;
unsigned int crc16 = calculateCRC(responseFrame, 3); // ID, functionCode + 0x80, exception code == 3 bytes
responseFrame[3] = crc16 >> 8;
responseFrame[4] = crc16 & 0xFF;
sendPacket(responseFrame, responseFrameSize); // exception response is always 5 bytes (ID, functionCode + 0x80, exception code, 2 bytes crc)
}
unsigned int calculateCRC(unsigned char f[], byte bufferSize) {
unsigned int temp, temp2, flag;
temp = 0xFFFF;
for (unsigned char i = 0; i < bufferSize; i++) {
temp = temp ^ f[i];
for (unsigned char j = 1; j <= 8; j++) {
flag = temp & 0x0001;
temp >>= 1;
if (flag)
temp ^= 0xA001;
}
}
// Reverse byte order.
temp2 = temp >> 8;
temp = (temp << 8) | temp2;
temp &= 0xFFFF;
return temp; // the returned value is already swapped - crcLo byte is first & crcHi byte is last
}
void rxEnable() {
if (TE_PIN > 1 && RE_PIN > 1) {
digitalWrite(TE_PIN, LOW);
digitalWrite(RE_PIN, LOW);
digitalWrite(LED_PIN, LOW);
}
}
void txEnable() {
if (TE_PIN > 1 && RE_PIN > 1) {
digitalWrite(TE_PIN, HIGH);
digitalWrite(RE_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
}
}
void sendPacket(unsigned char f[], unsigned char bufferSize) {
txEnable();
delayMicroseconds(T3_5);
for (unsigned char i = 0; i < bufferSize; i++) {
Serial.write(f[i]);
}
Serial.flush();
delayMicroseconds(T3_5); // allow frame delay to indicate end of transmission
rxEnable();
}
// #param dly delay between sensor readings in milliseconds
void readSensorVals(int dly) {
if (millis() != millisFlag) {
dlyCounter++;
millisFlag = millis();
}
if (dlyCounter >= dly) { // read sensor values
for (int i = 0; i < MAX_ROWS; i++) {
for (int j = 0; j < MAX_SENSORS_PER_ROW; j++) {
int actualIndex = i * MAX_SENSORS_PER_ROW + j;
if (i < ROWS && j < SENSORS_PER_ROW) {
sensorVals[actualIndex] = random(20, 30);
//sensorVals[actualIndex] = (4096 - analogRead(INPUTS[j])) / 20 - 133;
} else {
sensorVals[actualIndex] = INVALID_VALUE;
}
}
}
dlyCounter = 0;
}
}
The program's supposed to constantly listen for 433 MHz messages coming from soil moisture sensors in plant pots and decide whether or not to activate a pump based on those readings.
Additionally it's supposed to check wired water sensors while it's listening.
All 433 MHz Received Messages should be stored within the array sensor_data[i].
At startup the positions 1to NUM_Sensors(in this case 3) is filled with the constant int NO_DATA (500).
The Problem is, that for some reason I get corrupted Number in the array number 3:
Serial Print:
Wired Flower Pots Checked
All Sensors or Timeout reached
Array_Print: 500
Array_Print: 500
Array_Print: 30001
In this case the 30001 appears in the array with no specific reason (I guess).. there's no received message.
Here's the code changed to the minimum so that the error occurs:
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();
//Receiver Setup for wireless soil moisture readings
unsigned long last_ground_check = 0;
const int NUM_SENSORS = 3;
const uint32_t SENSOR_TIMEOUT = 30000;
int sensor_data[NUM_SENSORS];
uint32_t last_message_time = 0;
uint32_t elapsed = 0;
float total_value = 0;
float real_value = 0;
int count = 0;
const int NO_DATA = 500;
boolean received = false;
//###################//
void setup()
{
Serial.begin(9600);
mySwitch.enableReceive(INT1); // Interrupt 2 = Pin 2
delay(1500);
Serial.println("<><><><><><><><><><><><>");
Serial.println(" Start ");
Serial.println("<><><><><><><><><><><><>");
for (int i = 0; i <= NUM_SENSORS; i++) {
sensor_data[i] = NO_DATA;
}
} // Setup END
void loop()
{
if (received == false) {
if (millis() - last_ground_check > 10000) {
Serial.println("Checking Wired Flower Pot");
}
Serial.println("Wired Flower Pots Checked");
last_ground_check = millis();
}
if (mySwitch.available()) { // Start whenever a 433 MHz Message is received
received = true;
double value = mySwitch.getReceivedValue();
delay(1000);
int sensor_id = 1;
int sensor_value = 2;
if (sensor_value >= 0 && sensor_value <= 100) {
sensor_data[sensor_id] = sensor_value;
last_message_time = millis();
mySwitch.resetAvailable();
}
}
byte sensors_reported = 0;
for (int i = 0; i <= NUM_SENSORS; i++) {
if (NO_DATA != sensor_data[i]) {
sensors_reported += 1; // CODE Gets here because of corrupted Array Value although no message was received
}
}
if (sensors_reported != 0) {
uint32_t elapsed = millis() - last_message_time;
if (NUM_SENSORS == sensors_reported || elapsed > SENSOR_TIMEOUT) {
Serial.println("All Sensors or Timeout reached");
for (int i = 1; i <= NUM_SENSORS; i++) {
Serial.print("Array_Print: ");
Serial.println(sensor_data[i]);
}
for (int i = 1; i <= NUM_SENSORS; i++) {
if (sensor_data[i] < NO_DATA) {
count++;
total_value += sensor_data[i];
}
}
real_value = total_value / count;
Serial.print("Soil Moisture: ");
Serial.println(real_value);
if (real_value <= 20) {
//Set Pump ON
}
for (int i = 1; i <= NUM_SENSORS; i++) {
sensor_data[i] = NO_DATA;
}
total_value = 0;
real_value = 0;
sensors_reported = 0;
count = 0;
received = false;
Serial.println("RESET #### RESET ####");
delay(5000);
}
}
} //LOOP
You first allocate an array of size NUM_SENSORS=3, and then you continue to use it as if it was of size 4.
Your array has 3 elements sensor_data[0], [1] and [2]. Your loop condition i <= NUM_SENSORS results in accessing sensor_data[3] which is just some memory after your last array element. Even if you set sensor_data[3] in your setup, if the same memory is referenced by some other variable, the NO_DATA will be overwritten.
Index your loops over the data array from i = 0 to i < NUM_SENSORS.