First question. If I include a header of a class to the header file of a second class, do I have to include the header of the first class to the .cpp file of the second class?
At the .cpp file of the second class I include its header which includes the .h file of the first class. So isn't it correct or do I have to include the first's class header to the .cpp file of the second class also?
Second problem. I have two classes. The first has static variables and functions, so I can call it without making objects. At the first class I have a static object of the second class. Now at the second class I want to pass the returns of some functions of the first class as operands to functions of the first class. I get no errors at the second's class, but I get an error at the declaration of the static object I mention before at the first class. Why is this happening?
The code:
Header files:
/*
* NTPlib.h
*
* Created on: 13 Feb 2013
* Author : Platonas
*/
#ifndef NTP_H_
#define NTP_H_
#include "Arduino.h"
#include "SPI.h"
#include "IPAddress.h"
#include "EthernetUdp.h"
#include "Ethernet.h"
#include "DayNumber.h"
class NTP {
private:
public:
static EthernetUDP Udp;
static DayNumber DN;
static const int Gmt = 2;
static const unsigned int localPort = 8888;
static const int NTP_PACKET_SIZE = 48;
static byte packetBuffer[NTP_PACKET_SIZE ];
static unsigned long secsSince1900;
static unsigned long UnixTime;
static int utchour;
static int lcthour;
static int min;
static int sec;
static int year;
static int month;
static int date;
static int dayOfWeek;
static bool timeSet;
NTP();
static NTP getTime();
static bool testNtpServer();
static void startEthernetAndUdp();
static unsigned long sendNTPpacket(IPAddress& address);
static int getYear();
static int getMonth();
static int getDate();
static int getDayOfWeek();
static int getUTChour();
static int getLCThour();
static int getMin();
static int getSec();
static void serialPrinting();
virtual ~NTP();
};
#endif /* NTPLIB_H_ */
and
/*
DayNumber.h - Library for calculation of day's number on 1 and 4 years loop.
Created by Pavlidis Kyriakos, December 20, 2012.
Released into the public domain.
*/
#ifndef DayNumber_H_
#define DayNumber_H_
#include "NTP.h"
class DayNumber {
private:
int _day1YearLoop[];
int _day4YearLoop[];
public:
int Days1YearLoop;
int Days4YearLoop;
DayNumber();
void dayNumberCalc();
virtual ~DayNumber();
bool checkLeapYear(int setYear);
};
#endif
.cpp files:
/*
NTP.cpp - Library for NTP server.
Created by Pavlidis Kyriakos, Feb 13, 2013.
Released into the public domain.
*/
#include "NTP.h"
#include "DayNumber.h"
unsigned long NTP::UnixTime = 0;
unsigned long NTP::secsSince1900 = 0;
int NTP::utchour = 99;
int NTP::lcthour = 99;
int NTP::min = 99;
int NTP::sec = 99;
int NTP::year = 99;
int NTP::month = 99;
int NTP::date = 99;
int NTP::dayOfWeek = 99;
byte NTP::packetBuffer[NTP_PACKET_SIZE ];
bool NTP::timeSet = false;
DayNumber NTP::DN = DayNumber();
EthernetUDP NTP::Udp = EthernetUDP();
NTP::NTP() {
// TODO Auto-generated constructor stub
}
NTP NTP::getTime() {
if (testNtpServer()) {
timeSet = true;
// We've received a packet, read the data from it
Udp.read((unsigned char*)packetBuffer,NTP_PACKET_SIZE); // Read the packet into the buffer
// The timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// Combine the four bytes (two words) into a long integer.
// This is NTP time (seconds since Jan 1 1900):
secsSince1900 = highWord << 16 | lowWord;
// Now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// Subtract seventy years:
UnixTime = secsSince1900 - seventyYears;
sec = UnixTime % 60;
//Calc min
min = (UnixTime/60)%60;
//Calc hour
utchour = (UnixTime/3600)%24;
lcthour = utchour + Gmt;
//Day of the week
dayOfWeek = (((UnixTime/86400UL) + 3) % 7) + 1; //Setting first day Sunday = 1
//Calculating years
unsigned long UnixTimeToDays = UnixTime/86400UL;
//Serial.println(UnixTimeToDays);
unsigned long calcDaysInYears = 0;
int calcYear = 1970;
while((calcDaysInYears += (DN.checkLeapYear(calcYear)? 366:365)) <= UnixTimeToDays) {
calcYear++;
}
year = calcYear;
//Calculating days in this year
calcDaysInYears -= (DN.checkLeapYear(calcYear)? 366:365);
int daysPassedInYear = UnixTimeToDays - calcDaysInYears;
//Set DayNumber one year loop
DN.Days1YearLoop = daysPassedInYear + 1;
//calculating date and month
static const uint8_t monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int calcMonth;
int monthLength;
for (calcMonth = 0; calcMonth<12; calcMonth++) {
if (DN.checkLeapYear(year)) {
monthLength = (calcMonth == 1) ? 29: 28;
}
else {
monthLength = monthDays[calcMonth];
}
if ( daysPassedInYear > monthLength) {
daysPassedInYear -= monthLength;
}
else {
break;
}
}
month = ++calcMonth;
date = ++daysPassedInYear;
serialPrinting();
return NTP();
}
else {
//Error me tous ntp diavazoume wra apo DS1307
Serial.println("pame gia RTC");
return NTP();
}
}
unsigned long NTP::sendNTPpacket(IPAddress& address) {
// Set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// Eight bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// All NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer,NTP_PACKET_SIZE);
Udp.endPacket();
}
void NTP::startEthernetAndUdp() {
//Declaration of the mac address of ethernet shield
byte mac[] = {0x00,0xAA,0xBB,0xCC,0xDE,0x02};
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// No point in carrying on, so do nothing forevermore:
//for(;;)
// ;
//Prepei na diorthwthei na kanei bypass to DHCP kai na paei sto RTC an den exei internet
}
Udp.begin(localPort);
}
bool NTP::testNtpServer() {
//(193,93,167,241 ); //GR time server on athens ntp.asda.gr
//(129,215,160,240 ); //UK extntp0.inf.ed.ac.uk School of Informatics, University of Edinburgh, Scotland, UK
//(138,195,130,71 ); //FR ntp.via.ecp.fr VIA, Ecole Centrale Paris, France
//(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov NTP server
//(193,93,167,239); //GR time server on athens ChronosAsdaGr
//(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov NTP server
//(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov NTP server
byte serverslist[4][4] = {
193,93,167,241,
129,215,160,240,
138,195,130,71,
132,163,4,101
};
IPAddress ntpServers(serverslist[0]);
sendNTPpacket(ntpServers);
int x = 0;
delay(1000);
//Checking different NTP server if someone is down
while(!Udp.parsePacket() && x <= 3) {
//Have to check parsePacket return.
x++;
IPAddress ntpServers(serverslist[x]);
sendNTPpacket(ntpServers);
delay(1000);
}
switch (x) {
case 0:
Serial.println("1st NTPServer working");
return true;
break;
case 1:
Serial.println("2st NTPServer working");
return true;
break;
case 2:
Serial.println("3st NTPServer working");
return true;
break;
case 3:
Serial.println("4st NTPServer working");
return true;
break;
default:
Serial.println("All NTP Servers are Down");
return false;
}
}
int NTP::getYear() {
do {
getTime();
} while(timeSet == false);
return year;
}
int NTP::getMonth() {
do {
getTime();
}while(timeSet == false);
return month;
}
int NTP::getDate() {
do {
getTime();
} while(timeSet == false);
return date;
}
int NTP::getDayOfWeek() {
do {
getTime();
} while(timeSet == false);
return dayOfWeek;
}
int NTP::getUTChour() {
do {
getTime();
} while(timeSet == false);
return utchour;
}
int NTP::getLCThour() {
do {
getTime();
} while(timeSet == false);
return lcthour;
}
int NTP::getMin() {
do {
getTime();
} while(timeSet == false);
return min;
}
int NTP::getSec() {
do {
getTime();
} while(timeSet == false);
return sec;
}
void NTP::serialPrinting() {
//Serial.PRINTS
//print seconds since 1900
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// print Unix time:
Serial.print("Unix time = ");
Serial.println(UnixTime);
//print year
Serial.print("the year is :");
Serial.println(year);
//print month
Serial.print("Month is : ");
Serial.print(month);
//print date
Serial.print(" Date is: ");
Serial.println(date);
//print dayOfWeek
Serial.print("the day is : ");
Serial.println(dayOfWeek);
//printnumber of days that passed in this year (this day counts DayNumber object)
Serial.print("This day is the number:");
Serial.println(DN.Days1YearLoop);
//print Local Time Hour
Serial.print("The LTC time is ");
Serial.println(lcthour);
// Print the hour, minute and second:
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
Serial.print(utchour); // Print the hour (86400 equals secs per day)
Serial.print(':');
if ( min < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'.
Serial.print('0');
}
Serial.print(min); // Print the minute (3600 equals secs per minute)
Serial.print(':');
if ( sec < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'.
Serial.print('0');
}
Serial.println(sec); // Print the seconds
}
NTP::~NTP() {
// TODO Auto-generated destructor stub
}
and
/*
DayNumber.cpp - Library for Calculation of Day's Number on 1 and 4 years loop.
Created by Pavlidis Kyriakos, December 20, 2012.
Released into the public domain.
*/
#include "DayNumber.h"
DayNumber::DayNumber() {
}
void DayNumber::dayNumberCalc() {
int setYear = NTP::getYear();
int setMonth = NTP::getMonth();
int setDay = NTP::getDate();
//Days that passed from the begging of the year for the 1st Day each Month
int _day1YearLoop[] = {0,31,59,90,120,151,181,212,243,273,304,334};
//i = _day1YearLoop;
//Days that passed from the beginning of the second Year since the for the 1st Day of the running Year in 4 years loop.
int _day4YearLoop[] = {366,731,1096};
if (checkLeapYear(setYear)) {
if (setMonth>2) { //Diorthwsi gia ton mina flebari
Days1YearLoop = *(_day1YearLoop+(setMonth-1)) + setDay + 1;
Days4YearLoop = Days1YearLoop;
}
else {
Days1YearLoop = *(_day1YearLoop+(setMonth-1)) + setDay;
Days4YearLoop = Days1YearLoop;
}
}
else {
Days1YearLoop = *(_day1YearLoop + (setMonth-1)) + setDay;
switch (setYear%4) {
case 1:
Days4YearLoop = *(_day4YearLoop) + Days1YearLoop;
break;
case 2:
Days4YearLoop = *(_day4YearLoop+1) + Days1YearLoop;
break;
case 3:
Days4YearLoop = *(_day4YearLoop+2) + Days1YearLoop;
break;
Default:;
break;
}
}
}
DayNumber::~DayNumber() {
}
bool DayNumber::checkLeapYear(int setYear) {
if (setYear%4 == 0) {
return true;
}
else {
return false;
}
}
The error is at the first header of NTP.h that says
Description Resource Path Location Type
'DayNumber' does not name a type NTP.h /NTP/lib line 24 C/C++ Problem
It does not understand the declaration of the object.
It is always good practice to include ALL files that the current file depends on. Let's say you change your "DayNumber.h" such that it no longer needs "NTP.h", now your code won't compile.
Assuming your files are local, and your machine isn't extremely low on memory, it will only make a marginal difference. There was a question on a similar subject recently, and I measured the difference between including some header or or not, compiling 30 or so files (actually, the SAME file). And the margin of error between a "good" and "bad" run on my machine was quite a bit larger than the difference between including headers and not including it.
As for your second question, it is actually caused by including "NTP.h" which isn't needed in "DayNumber.h" - so you should just "not include that". But if you have a real situation where you have one class that needs to know of another class, you need to use a forward declaration, e.g. class NTP; - now, you can use NTP * or NTP & to pass/store pointers/references to the NTP class. You can still not use what is inside the NTP class until the NTP class has been fully defined.
Related
I'm trying to gather the data from DHT22 Humidity and Temperature sensor based on millis() function to avoid the buffer overload and due to how fast the sensor is itself. The point is, that the project has to manage the 72h constant routines, to control the air valve. Because of that 2 actions to be handled, delay is out of range. The problem is, that after setup() and filling in the buffer for average, the loop starts from 12 readings one after another, and eventually after these 12 readings it starts to perform 2 sec. interval readings.
#include <DHT.h>
#include <Adafruit_Sensor.h>
#include <CircularBuffer.h>
#include <time.h>
#define DHTPIN 2
#define DHTTYPE DHT22
using namespace std;
const int sample_rate = 4; // Defining how many samples are gathered to calculate the average
const int temper_low = -3; // Defining lower range of temperature
const int temper_high = 20;// Defining high range for temperature
float sum_temper, sum_humid, avg_temp, avg_humid = 0; // variables to calculate the Simple Moving Average
int pre_fill_count = 0; // iterator used for preFill buffer, goes up to 9 and then gets cleared
float test = -255.0;
int mins = 0; //Next 3 vars used for valve control timer
int hours = 0;
int days = 0;
DHT dht(DHTPIN, DHTTYPE); // Defining the dht object
CircularBuffer<float, sample_rate> temper_buffer; // Defining the CircularBuffer for FILO with Moving Average
CircularBuffer<float, sample_rate> humid_buffer; // Same as above
const unsigned long DHT_reads_interval = 2000; //interval used to perform reading action considering the speed of DHT sensor
unsigned long currentMillis = 0; //stores the value of millis() in each iteration of loop()
unsigned long previous_sens_readout = 0; //last time temperature was readed
//State machine for humidity readouts
enum state{
preFill,
measure,
fail
};
state sensorState = preFill;
bool measurmentFlag = false;
//Functions definitions
void calculateAverageTemperature(
const int& sample_rate,
float& sum_temper,
float& sum_humid,
float& avg_temp,
float& avg_humid
);
void getSensorReadings(
DHT &sensor,
CircularBuffer<float, sample_rate> &temper_buffer,
CircularBuffer<float, sample_rate> &humid_buffer
);
float readTemperature
(
state &sensorState,
DHT &dht
);
float readHumidity
(
state &sensorState,
DHT &dht
);
void setup() {
// Setup, runs once after uC boot
Serial.begin(9600);
Serial.println(F("DHTxx test!"));
dht.begin();
Serial.println("test value ");
Serial.println(test);
// Wait a few seconds between measurements.
currentMillis = millis();
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
///////////////////////////////////////////////////////////////////////////
//////////////////////////////INIT BUFFER FILL SECTION////////////////////
/////////////////////////////////////////////////////////////////////////
Serial.println("INITIALISING THE BUFFER, PLEASE WAIT... ");
while(sensorState == preFill && (pre_fill_count <=9)) {
//PUSHING READINGS TO THE BUFFER
//DUE TO BUFFER INIT ACTION, IT TAKES C.A. 20 SECS TO MOVE FROM preFill TO measure state!!!
if(currentMillis - previous_sens_readout >= DHT_reads_interval){
temper_buffer.push(readTemperature(sensorState, dht));
Serial.print(pre_fill_count);
Serial.print(" it Temperature measurement from temper buffer: ");
Serial.println(temper_buffer[pre_fill_count]);
humid_buffer.push(readHumidity(sensorState, dht));
Serial.print(pre_fill_count);
Serial.print(" it Humidity measurement from temper buffer: ");
Serial.println(humid_buffer[pre_fill_count]);
previous_sens_readout = previous_sens_readout + DHT_reads_interval;
pre_fill_count++;
}
currentMillis = millis();
}
sensorState = measure;
previous_sens_readout = 0;
currentMillis = millis();
Serial.print("Program time on the exit of preFill: ");
Serial.print(int(currentMillis/1000));
Serial.println(" seconds");
measurmentFlag = true;
}
void loop() {
///////////////////////////////////////////////////////////////////////////////////
////////////////////////////MAIN STATE MACHINE OF PROGRAM/////////////////////////
/////////////////////////////////////////////////////////////////////////////////
Serial.println("--------------------HELLO THERE------------------------");
currentMillis = millis();
//TOTO - IMPLEMENT ACTUAL USE OF THE calculateAverageTemperature function
if(sensorState == measure && measurmentFlag == true){
calculateAverageTemperature(
sample_rate,
sum_temper,
sum_humid,
avg_temp,
avg_humid
);
if(currentMillis - previous_sens_readout >= DHT_reads_interval){
getSensorReadings(dht,
temper_buffer,
humid_buffer
);
previous_sens_readout = previous_sens_readout + DHT_reads_interval;
}
if(sensorState == fail){
Serial.println("FAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP!");
delay(2000);
//TODO: RETRY MEASUREMENT AND RAISE LED INDICATOR OF FAIL!
}
}
}
void calculateAverageTemperature(
const int& sample_rate,
float& sum_temper,
float& sum_humid,
float& avg_temp,
float& avg_humid
){
//iter to gather the sum of 10 values
for (int k = 0; k < sample_rate; k++) {
Serial.println("-----------------HERE I AM!---------------------");
sum_temper = sum_temper + temper_buffer[k];
sum_humid = sum_humid + humid_buffer[k];
}
//calculate the sum of values
avg_temp = sum_temper / sample_rate;
avg_humid = sum_humid / sample_rate;
sum_temper = sum_humid = 0;
measurmentFlag = false;
}
void getSensorReadings(
DHT &sensor,
CircularBuffer<float, sample_rate> &temper_buffer,
CircularBuffer<float, sample_rate> &humid_buffer
){
//get next measurings
temper_buffer.push(readTemperature(sensorState, dht));
humid_buffer.push(readHumidity(sensorState, dht));
Serial.print(F("Avg Humidity: "));
Serial.print(avg_humid);
Serial.print(F("% Temperature: "));
Serial.print(avg_temp);
Serial.print(F("°C \n"));
measurmentFlag = true;
}
//GATHERING TEMPERATURE MEASUREMENT
float readTemperature
(
state &sensorState,
DHT &dht
){
float t = 0;
bool readed = false;
for (int r = 0; r < 4; r++) {
if ((!readed) && (!isnan(t = dht.readTemperature()))) {
Serial.print("Temperature readed on iteration: ");
Serial.println(r);
readed = true;
break;
}
else if ((r == 3) && (!readed)) {
Serial.println("Something went wrong with temperature");
sensorState = fail;
return -255;
}
}
return t;
}
//GATHERING HUMIDITY MEASUREMENT
float readHumidity
(
state &sensorState,
DHT &dht
){
float h = 0;
bool readed = false;
for (int o = 0; o < 4; o++) {
if (!readed && !isnan(h = dht.readHumidity())) {
Serial.print("Humidity readed on iteration: ");
Serial.println(o);
readed = true;
break;
}
else if ((o == 3) && (!readed)) {
//raise error, blink LED, Kill software
Serial.println("Something went wrong with Humidity");
sensorState = fail;
return -255;
}
}
return h;
}
I've tried many times using some extra flags inside code, nothing seem to really help.
DHT_reads_interval is defined as 2000. It should be expected that
(currentMillis - previous_sens_readout >= DHT_reads_interval)
is satisfied approximately every 2000 ms, which is 2 seconds.
Regarding the first 12 readings coming one after another, ask yourself what is currentMillis, and what is previous_sens_readout after setup completes?
In fact, currentMillis retains its value, but previous_sens_readout = 0; is spelled loud and clear. Do you see the consequences?
I am currently making a small discord bot that can play music to improve my skill. That's why i don't use any discord lib.
I want the music as smooth as possible, but when i played some piece of music, the music produced is very choppy.
here is my code:
concurrency::task<void> play(std::string id) {
auto shared_token = std::make_shared<concurrency::cancellation_token*>(&p_token);
auto shared_running = std::make_shared<bool*>(&running);
return concurrency::create_task([this, id, shared_token] {
audio* source = new audio(id); // create a s16le binary stream using FFMPEG
speak(); // sending speak packet
printf("creating opus encoder\n");
const unsigned short FRAME_MILLIS = 20;
const unsigned short FRAME_SIZE = 960;
const unsigned short SAMPLE_RATE = 48000;
const unsigned short CHANNELS = 2;
const unsigned int BITRATE = 64000;
#define MAX_PACKET_SIZE FRAME_SIZE * 5
int error;
OpusEncoder* encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_AUDIO, &error);
if (error < 0) {
throw "failed to create opus encoder: " + std::string(opus_strerror(error));
}
error = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
if (error < 0) {
throw "failed to set bitrate for opus encoder: " + std::string(opus_strerror(error));
}
if (sodium_init() == -1) {
throw "libsodium initialisation failed";
}
int num_opus_bytes;
unsigned char* pcm_data = new unsigned char[FRAME_SIZE * CHANNELS * 2];
opus_int16* in_data;
std::vector<unsigned char> opus_data(MAX_PACKET_SIZE);
class timer_event {
bool is_set = false;
public:
bool get_is_set() { return is_set; };
void set() { is_set = true; };
void unset() { is_set = false; };
};
timer_event* run_timer = new timer_event();
run_timer->set();
//this is the send loop
concurrency::create_task([run_timer, this, shared_token] {
while (run_timer->get_is_set()) {
speak();
int i = 0;
while (i < 15) {
utils::sleep(1000);
if (run_timer->get_is_set() == false) {
std::cout << "Stop sending speak packet due to turn off\n";
concurrency::cancel_current_task();
return;
}
if ((*shared_token)->is_canceled()) {
std::cout << "Stop sending speak packet due to cancel\n";
concurrency::cancel_current_task();
return;
}
}
}});
std::deque<std::string>* buffer = new std::deque<std::string>();
auto timer = concurrency::create_task([run_timer, this, buffer, FRAME_MILLIS, shared_token] {
while (run_timer->get_is_set() || buffer->size() > 0) {
utils::sleep(5 * FRAME_MILLIS); //std::this_thread::sleep_for
int loop = 0;
int sent = 0;
auto start = boost::chrono::high_resolution_clock::now();
while (buffer->size() > 0) {
if (udpclient.send(buffer->front()) != 0) { //send frame
//udpclient.send ~ winsock sendto
std::cout << "Stop sendding voice data due to udp error\n";
return;
}
buffer->pop_front();
if ((*shared_token)->is_canceled()) {
std::cout << "Stop sending voice data due to cancel\n";
concurrency::cancel_current_task();
}
sent++; //count sent frame
//calculate next time point we should (in theory) send next frame and store in *delay*
long long next_time = (long long)(sent+1) * (long long)(FRAME_MILLIS) * 1000 ;
auto now = boost::chrono::high_resolution_clock::now();
long long mcs_elapsed = (boost::chrono::duration_cast<boost::chrono::microseconds>(now - start)).count(); // elapsed time from start loop
long long delay = std::max((long long)0, (next_time - mcs_elapsed));
//wait for next time point
boost::asio::deadline_timer timer(context_io);
timer.expires_from_now(boost::posix_time::microseconds(delay));
timer.wait();
}
}
});
unsigned short _sequence = 0;
unsigned int _timestamp = 0;
while (1) {
if (buffer->size() >= 50) {
utils::sleep(FRAME_MILLIS);
}
if (source->read((char*)pcm_data, FRAME_SIZE * CHANNELS * 2) != true)
break;
if ((*shared_token)->is_canceled()) {
std::cout << "Stop encoding due to cancel\n";
break;
}
in_data = (opus_int16*)pcm_data;
num_opus_bytes = opus_encode(encoder, in_data, FRAME_SIZE, opus_data.data(), MAX_PACKET_SIZE);
if (num_opus_bytes <= 0) {
throw "failed to encode frame: " + std::string(opus_strerror(num_opus_bytes));
}
opus_data.resize(num_opus_bytes);
std::vector<unsigned char> packet(12 + opus_data.size() + crypto_secretbox_MACBYTES);
packet[0] = 0x80; //Type
packet[1] = 0x78; //Version
packet[2] = _sequence >> 8; //Sequence
packet[3] = (unsigned char)_sequence;
packet[4] = _timestamp >> 24; //Timestamp
packet[5] = _timestamp >> 16;
packet[6] = _timestamp >> 8;
packet[7] = _timestamp;
packet[8] = (unsigned char)(ssrc >> 24); //SSRC
packet[9] = (unsigned char)(ssrc >> 16);
packet[10] = (unsigned char)(ssrc >> 8);
packet[11] = (unsigned char)ssrc;
_sequence++;
_timestamp += SAMPLE_RATE / 1000 * FRAME_MILLIS; //48000Hz / 1000 * 20(ms)
unsigned char nonce[crypto_secretbox_NONCEBYTES];
memset(nonce, 0, crypto_secretbox_NONCEBYTES);
for (int i = 0; i < 12; i++) {
nonce[i] = packet[i];
}
crypto_secretbox_easy(packet.data() + 12, opus_data.data(), opus_data.size(), nonce, key.data());
packet.resize(12 + opus_data.size() + crypto_secretbox_MACBYTES);
std::string msg;
msg.resize(packet.size(), '\0');
for (unsigned int i = 0; i < packet.size(); i++) {
msg[i] = packet[i];
}
buffer->push_back(msg);
}
run_timer->unset();
timer.wait();
unspeak();
delete run_timer;
delete buffer;
opus_encoder_destroy(encoder);
delete[] pcm_data;
});
}
There are 3 possible causes:
I send packet late so server-end buffer run out, so the sound produced has some silence between each each 2 packets. Maybe the timer is not accurate so the sound is out of sync.
The encode process is wrong which causes lost data somehow.
Bad network (i have tested an open source bot written on java, it worked so i can assume that my network is good enough)
So i post this question, hope someone has experienced this situation show me what wrong and what should i do to correct it.
I figured out the problem myself. I want to post solution here for someone who need.
The problem is the timer is unstable so it's usually sleep more than it should, so it makes the music broken.
I changed it to an accurate sleep function which i found somewhere on the internet(i don't remember the source, sorry for that, if you know it please credit it bellow).
Function source code:
#include <math.h>
#include <chrono>
#include <window.h>
static void timerSleep(double seconds) {
using namespace std::chrono;
static HANDLE timer = CreateWaitableTimer(NULL, FALSE, NULL);
static double estimate = 5e-3;
static double mean = 5e-3;
static double m2 = 0;
static int64_t count = 1;
while (seconds - estimate > 1e-7) {
double toWait = seconds - estimate;
LARGE_INTEGER due;
due.QuadPart = -int64_t(toWait * 1e7);
auto start = high_resolution_clock::now();
SetWaitableTimerEx(timer, &due, 0, NULL, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
auto end = high_resolution_clock::now();
double observed = (end - start).count() / 1e9;
seconds -= observed;
++count;
double error = observed - toWait;
double delta = error - mean;
mean += delta / count;
m2 += delta * (error - mean);
double stddev = sqrt(m2 / (count - 1));
estimate = mean + stddev;
}
// spin lock
auto start = high_resolution_clock::now();
while ((high_resolution_clock::now() - start).count() / 1e9 < seconds);
}
Thank you for your support!
When switching between states, the lines get jumbled and the characters get mixed up. Nothing I've seen online helps and example code in the library works just fine. The main issue I think comes from when the LCD is wiped clean, but then I don't know where it should be wiped. I've moved it from loop() to the cases multiple times, and delays don't help.
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
#include "RTClib.h"
RTC_DS1307 rtc;
const int hourButton = 2; // Interrupt Pin 0 -- TOP
const int minuteButton = 3; // Interrupt Pin 1 -- 2nd
const int displayStateButton = 18; // Interrupt Pin 5 -- 3rd
const int alarmButton = 19; // Interrupt Pin 4 -- BOTTOM
int buttonState = LOW;
int redPin = 4;
int greenPin = 5; // RGB LED Pins
int bluePin = 6;
int alarmPin = 13; // Alarm Pin
enum DeviceDisplayState {CLOCK, ALARM, DATE, YEAR}; // All different states
DeviceDisplayState displayState = CLOCK; // Initially in Clock State
#ifdef DEBOUNCE
long lastDebounceTime = 0;
long debounceDelay = 60;
#endif
void setup() {
lcd.begin(16, 2);
Serial.begin(57600);
// Set the time:: //
const int hourInit = 1;
const int minuteInit = 2;
const int secondInit = 1;
const int dayInit = 3;
const int monthInit = 4;
const int yearInit = 2020;
rtc.adjust(DateTime(yearInit, monthInit, dayInit, hourInit , minuteInit, secondInit));
pinMode(hourButton, INPUT_PULLUP);
pinMode(minuteButton, INPUT_PULLUP);
pinMode(displayStateButton, INPUT_PULLUP);
attachInterrupt(0, increaseHour, FALLING);
attachInterrupt(1, increaseMinute, FALLING);
attachInterrupt(5, SwitchToNextDisplayState, FALLING);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(alarmPin, OUTPUT);
SwitchToClockState();
};
void RGB_color(int red_light_value, int green_light_value, int blue_light_value)
{
analogWrite(redPin, red_light_value);
analogWrite(greenPin, green_light_value);
analogWrite(bluePin, blue_light_value);
}
void increaseHour()
{
DateTime dt = rtc.now();
Serial.print("Previous Time: " + dt.hour());
if (dt.hour() < 23)
{
TimeSpan ts(3600);
dt = dt + ts;
}
else // do not roll over the day by upping the hour, go back to zero hours
{
TimeSpan ts(3600 * 23);
dt = dt - ts;
}
Serial.print("Changed Time: " + dt.hour());
Serial.println();
rtc.adjust(dt);
}
void increaseMinute()
{
DateTime dt = rtc.now();
if (dt.minute() < 59)
{
TimeSpan ts(60);
dt = dt + ts;
}
else // Don't roll over the minutes into the hours
{
TimeSpan ts(60 * 59);
dt = dt - ts;
}
rtc.adjust(dt);
}
void SwitchToClockState()
{
displayState = CLOCK;
RGB_color(255, 0, 0);
}
void SwitchToAlarmState()
{
displayState = ALARM;
RGB_color(255, 125, 0);
}
void SwitchToDateState()
{
displayState = DATE;
RGB_color(0, 255, 0);
}
void SwitchToYearState()
{
displayState = YEAR;
RGB_color(0, 0, 255);
}
void SwitchToNextDisplayState()
{
switch (displayState) {
case CLOCK:
SwitchToAlarmState();
Serial.print("Switching to Alarm State...");
Serial.println();
lcd.clear();
break;
case ALARM:
SwitchToDateState();
Serial.print("Switching to Date State...");
Serial.println();
lcd.clear();
break;
case DATE:
SwitchToYearState();
Serial.print("Switching to Year State...");
Serial.println();
lcd.clear();
break;
case YEAR:
SwitchToClockState();
Serial.print("Switching to Clock State...");
Serial.println();
lcd.clear();
break;
default:
// assert()
digitalWrite(redPin, LOW);
digitalWrite(greenPin, LOW);
digitalWrite(bluePin, LOW);
break;
}
}
String WithLeadingZeros(int number)
{
if (number < 10)
{
return "0" + String(number);
}
else
{
return String(number);
}
}
void loop() {
DateTime now = rtc.now();
int yearInt = now.year();
int monthInt = now.month();
int dayInt = now.day();
int hourInt = now.hour();
int minuteInt = now.minute();
int secondInt = now.second();
switch (displayState) {
case CLOCK:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Time> " + WithLeadingZeros(now.hour()) + ":" + WithLeadingZeros(now.minute()) + ":" + WithLeadingZeros(now.second()));
break;
case ALARM:
lcd.print("Robot Slave");
case DATE:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Date> " + WithLeadingZeros(now.month()) + " - " + WithLeadingZeros(now.day()));
break;
//case YEAR:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Year> " + String(now.year()));
break;
}
}
You're creating nonsense instructions for your LCD if you execute commands in an ISR while already executing instructions in your normal program.
Let's say the serial command to write letter A is "WRITEA" and the command for clearing the display is "CLEAR".
Now while sending the letter A to your display you push the button, your display will receive something like "WRCLEARTEB" which it cannot make sense of. Or maybe it receives "WRITECLEARA" and it will write C instead of A.
Please note that this is just to give you an idea what is going on. Of course the data sent to the display is different.
But you're creating a mess by interleaving commands.
Update your display in a loop and use ISRs only to update variables that are then used in the next frame. Clocks with second precision are usually updated once per second.
I'm having trouble identifying the cause of a recurrent issue with some arduino code. The code below reads two temperature sensors, sends the result to a PID library, and uses the output to control some relays on a fridge (adding accurate temperature control to a fridge, basically).
The code freezes or the Arduino resets periodically. This happens periodically, but the period changes - it freezes every minimum 30 minutes, maximum about 30 hours.
I suspect that there's an overflow or that I'm writing beyond the range of an array, but I can't find the issue. It's very unlikely that there's a power issue - the arduino is on a 10A 12v supply with a dedicated 5v regulator, so I doubt it.
I'm fairly new to all this and would be very grateful for any pointers or advice - even some tips on how to troubleshoot this unpredictable error would be very appreciated!
Here's the code:
Setup and main loop, also checks an analog input for the set temperature:
// Call libraries for display, sensor, I2C, and memory. Library setup included as well.
#include <avr/pgmspace.h>
char buffer[20];
#include <Time.h>
#include <TimeLib.h>
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,20,4);
#include <DallasTemperature.h>
#include <PID_v1.h>
// Special Characters for the display and display animations
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif
#define ONE_WIRE_BUS 5 //DS18S20 Signal pin on digital 2
uint8_t heart[8] = { 0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};
uint8_t deg[8] = { 0x1c,0x14,0x1c,0x0,0x3,0x4,0x4,0x3};
uint8_t Pv[8] = { 0x1c,0x14,0x1c,0x10,0x10,0x5,0x5,0x2};
uint8_t Sv[8] = { 0xc,0x10,0x8,0x4,0x18,0x5,0x5,0x2};
// end special chars
//************* Begin Variables Setup ***************//
//Sensors (ds18s20 needs additional chatter)
byte addr1[8]= {0x28, 0x3F, 0xB5, 0x3C, 0x05, 0x00, 0x00, 0x25};
byte addr2[8]= {0x28, 0xC7, 0xCD, 0x4C, 0x05, 0x00, 0x00, 0x0D};
byte data1[12];
byte data2[12];
byte MSB = 0;
byte LSB = 0;
float tempRead = 0;
float TemperatureSum = 0;
OneWire ds(ONE_WIRE_BUS);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//controller outputs
int ControlCpin = 6; // control to fridge
int ControlTpin = 8; // control to temperature/heater (get aquarium heater)
int ControlLpin = 7; // control to light
int ControlApin = 9; // control to airflow
//operational vars (the button)
//int buttonPushCounter = 0; // counter for the number of button presses DEPRACATED
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
boolean buttonstate = false; // calculastate of the button (includes timer delay. use this in menus)
int buttontime = 0; // press length measure
int buttontimeon = 0; // necessary for press length measure
//operational vars (sensors and timing)
unsigned int sensorInterval = 20000; // time between readings
unsigned long int sensorTime = 0; // current time
unsigned long int sensorTime2 = 0; // time of last sensor reading
// fans, lights, and timers
unsigned long int fanONmillis = 0;
unsigned long int fanOFFmillis = 0;
byte fanON = 0;
byte fanOFF = 0;
boolean fanstate = false;
unsigned long int Time = 0;
unsigned long int TimeAdjust = 0;
unsigned long int LightON = 0;
unsigned long int LightOFF = 0;
unsigned int Hours = 0;
unsigned int Minutes = 0;
unsigned int Days = 0;
byte daysAdj = 0; //not implemented yet
float tempDiff = 0;
//key var storage
float PvH = 0;
double PvT = 0;
float SvH = 0;
double SvT = 12;
float SvTdisplay = 5.5;
float SvTdisplayOld = 5.5;
float Temp1; //Current readings
float Temp2; //Current readings
float Temp3; //Current readings
// Fridge limits
unsigned int safetyRest = 5; // off this long every hour (minimum) to let the compressor rest in minutes
int minCool = 10; // minimum cooling period in minutes
int coolStart = 0;
byte coolON = 0; // PID attached to this
// Heat limits
byte heatON = 0; // PID attached to this
//cool
double Kp = 0.5;
double Ki = 0.5;
double Kd = 0.5;
double Output;
PID coolPID(&PvT, &Output, &SvT ,Kp,Ki,Kd, REVERSE);
unsigned coolWindowSize = 600; // minutes*10
unsigned long coolWindowStartTime;
unsigned long coolOffElapsed = 0;
long unsigned PIDpos = 0;
unsigned long Outputx = 0;
unsigned long PIDposx = 0;
unsigned long safetyRestx = 0;
// ensure setpoint, input, and outpit are defined
//************* End Variables Setup ***************//
void setup(){
//Sensor start
sensors.begin();
//Pin declarations
pinMode(ControlTpin, OUTPUT); //set outputs
pinMode(ControlLpin, OUTPUT);
pinMode(ControlApin, OUTPUT);
pinMode(ControlCpin, OUTPUT);
digitalWrite(ControlTpin, HIGH); // write outputs HIGH (off in this case) FIRST to prevent startup jitters.
digitalWrite(ControlLpin, HIGH);
digitalWrite(ControlApin, HIGH);
digitalWrite(ControlCpin, HIGH);
//LCD and special chars
Serial.begin(9600);
lcd.begin();
lcd.backlight();
lcd.createChar(0, heart);
lcd.createChar(1, deg);
lcd.createChar(2, Pv);
lcd.createChar(3, Sv);
lcd.clear();
LoadScreen();
HomeSetup();
//PID setup
coolPID.SetOutputLimits(0, coolWindowSize);
coolPID.SetMode(AUTOMATIC);
coolOffElapsed = millis();
}
void loop(){
//if interval has passed, check the sensors, update the triggers, and update the screen
if (millis() - sensorTime2 > sensorInterval){
sensorTime2 = millis();
SensorCheck();
Triggers();
HomeSetup ();
}
SvTdisplay = (float)analogRead(A0);
SvTdisplay = SvTdisplay/40+5;
if(abs(SvTdisplay-SvTdisplayOld) > 0.2){
SvTdisplayOld = SvTdisplay;
lcd.setCursor(2,0);
lcd.print(SvTdisplayOld,1); //svt
lcd.printByte(1);
lcd.print(" ");
SvT = analogRead(A0)/4+50;
}
PIDpos = ((millis()/60000) % (coolWindowSize/10));
}
The following codes a loading screen and updates the screen with current values:
void LoadScreen (){
lcd.clear();
lcd.home();
lcd.setCursor(0,0);
lcd.print(" LaggerLogger ");
lcd.printByte(0);
lcd.setCursor(0,1);
lcd.print(" V2.0 Beepboop!");
delay(3000);
lcd.clear();
}
//write the home screen to the LCD with current data
void HomeSetup(){
lcd.setCursor(0,0);
lcd.printByte(3);
lcd.print(" ");
lcd.print(SvTdisplayOld,1); //svt
lcd.printByte(1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.printByte(2);
lcd.print(" ");
lcd.print(PvT/10,1); //pvt
lcd.printByte(1);
lcd.print(" ");
lcd.setCursor(8,1);
lcd.print(day()-1);
lcd.print("/");
lcd.print(hour());
lcd.print(":");
lcd.print(minute());
lcd.print(" ");
lcd.setCursor(8,0);
lcd.print(Output/10,1);
lcd.print("m/h ");
}
The following checks output values and 'triggers' the relay if its appropriate to do so:
void Triggers () {
coolPID.Compute();
// Check PID
if ((Output/10) > (coolWindowSize/10-PIDpos) && PIDpos > safetyRest ) { //
coolON = 1;
coolStart = millis();
}
else if ((millis() - coolStart) > (minCool * 60000)){
coolON = 0;
}
else {}
// Write to temp relay pins
if (coolON == 1) {
digitalWrite(ControlCpin, LOW);
}
else {
digitalWrite(ControlCpin, HIGH);
}
// Control fans
if (coolON == 1 || heatON == 1 || tempDiff > 1) {
fanOFFmillis = millis();
fanONmillis = millis();
fanstate = true;
digitalWrite(ControlApin, LOW);
}
else {
fanstate = false;
digitalWrite(ControlApin, HIGH);
}
}
The following checks the temperature sensors and does some clock calculations:
void SensorCheck(){
Temp1 = getTemp1();
Temp2 = getTemp2();
//average readings and note the difference
if(Temp1 > 0 && Temp2 >0) {
PvT = (Temp1 + Temp2) / .2;
tempDiff = abs(Temp1 - Temp2);
}
//... unless there's only one thermometer...
else if (Temp1 > 0){
PvT = Temp1*10;
tempDiff = 0;
}
else {
PvT = 999;
tempDiff = 0;
}
//clock update
Time = millis() + TimeAdjust;
Hours = hour();
Minutes = minute();
Days = day();
}
float getTemp1(){
sensors.requestTemperatures();
float z = sensors.getTempCByIndex(0);
return z;
}
float getTemp2(){
sensors.requestTemperatures();
float z = sensors.getTempCByIndex(1);
return z;
}
The problem was that an integer (declared as int coolStart) was later updated to hold the value of millis().
Millis() can be much larger than the 16 bits available to ints - creating an overflow.
Changing the variable declaration to 'unsigned long coolStart = 0;' appears to have solved the problem.
Thanks to everyone for the troubleshooting advice.
I see a lot of discussion on this error, but I can't seem to find a format that works. I am getting the following error:
In member function 'void radarClass::ping()':
error: no matching function for call to 'NewPing::ping_timer(<unresolved overloaded function type>)'
note: candidates are: void NewPing::ping_timer(void (*)())
My full code is attached below, the error is at line 198:
sonar->ping_timer(echoCheck);
I have also unsuccessfully tried:
sonar->ping_timer((void* ) &radarClass::echoCheck));
sonar->ping_timer(&radarClass::echoCheck);
Full code here:
#ifndef RADAR_CLASS
#define RADAR_CLASS
#include <NewPing.h>
#include <Servo.h>
#include <PString.h>
/*
**
** MOVING AVERAGE CLASS
** Special volatile impelentation, fixed Int type
**
*/
template <int N> class volatileMovingAverage
{
public:
volatileMovingAverage(int def = 0) : sum(0), p(0)
{
for (int i = 0; i < N; i++) {
samples[i] = def;
sum += samples[i];
}
}
int add(int new_sample)
{
sum = sum - samples[p] + new_sample;
samples[p++] = new_sample;
if (p >= N)
p = 0;
return sum / N;
}
int inline getAverage(void)
{
return sum / N;
}
private:
volatile int samples[N];
volatile int sum;
volatile int p;
}; // Class volatileMoving Average
/* *************************************************************** */
// RadarClass
//
// USAGE:
// ----------------------
// NewPing mySonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
// Servo myServo;
// radarClass myRadar(&myServo, &mySonar);
// myRadar.attachServo(pinNumber);
// myRadar.setServoSpeed(200); // 0.2 seconds/60 degrees of rotation
// myRadar.run(); etc.
#define RADAR_DEBUG_MODE YES // for testing only, comment out otherwise
#define NO_ANGLES 9 // number of angles, i.e. positions at which to take measurements
class radarClass
{
private:
int maxAngle; // 9 values, positions 0 - 8
int angles[NO_ANGLES]; // values: {0, 22, 44, 67, 90, 112, 135, 157, 180}
volatileMovingAverage<4> sonarAverage[NO_ANGLES]; // array of moving average distances (one per angle)
volatile uint8_t pos; // current position within angle array
unsigned long int servoArrivalTime; // time (millis) when servo will complete its rotation and arrive at next position
int direction; // direction traversing the angle array (+1 or -1)
Servo *radarServo; // pointer to the servo
NewPing *sonar; // point to the NewPing sensor
int maxDist; // max distance (used by NewPing sensor)
int servoSpeed; // servo peed in milli-seconds to move 60 degrees (e.g. 0.15 seconds to move 60 degrees of rotation = 0.15 x 1000 = 150)
volatile bool waitingForPing; // flag to mark when ping result received
bool pingStarted; // flag to keep track of ping loop status
volatile int pingValue; // used to pass ping result between the two routines/ISRs
void nextPosition(void);
void ping(void);
int getMinDistance(int start_pos, int end_pos);
void commonInit(void);
void echoCheck(void);
public:
radarClass(Servo *svr, NewPing *snr);
radarClass(NewPing *snr, Servo *svr);
int getMinDistanceLeft(void);
int getMinDistanceRight(void);
int getMinDistasnceFront(void);
void run(void);
void attachServo(int pin);
void setServoSpeed(int);
void setMaxDistance(int d) {maxDist = d;};
#ifdef RADAR_DEBUG_MODE
void printAllValues(void);
void printInt(int val, int len);
#endif
//int getCurrentAngle(void) {return angles[pos];}
//int getDistance(int p) {return sonarAverage[p].getAverage();}
}; // radarClass
/* ********************************************************** */
// constructor
radarClass::radarClass(Servo *svr, NewPing *snr)
{
// save the passed pointers
radarServo = svr;
sonar = snr;
// run common initialization routine
commonInit();
} // radarClass::radarClass constructor
/* ********************************************************** */
// constructor
radarClass::radarClass(NewPing *snr, Servo *svr)
{
// save the passed pointers
radarServo = svr;
sonar = snr;
// run common initialization routine
commonInit();
} // radarClass::radarClass constructor
/* ********************************************************** */
// constructor common initialization routine
void radarClass::commonInit(void)
{
// initialize variables
maxAngle = NO_ANGLES - 1;
pos = 0;
direction = 1;
servoSpeed = 100; // default to 0.10 s/60 degrees (x 1000 per millisecond) for TowerPro SG-90
maxDist = 100; // max distance usable by NewPing IN INCHES
pingStarted = false;
waitingForPing = false;
// initialize the angle array...not elegant but it works
// angles[] = {0, 22, 44, 67, 90, 112, 135, 157, 180};
angles[0] = 0;
angles[1] = 22;
angles[2] = 44;
angles[3] = 67;
angles[4] = 90;
angles[5] = 112;
angles[6] = 135;
angles[7] = 157;
angles[8] = 180;
// move servo to initial position
radarServo->write(angles[0]);
// set arrival time (estimated)
servoArrivalTime = millis() + (abs(angles[0] - angles[maxAngle]) * servoSpeed / 60 / 2);
}
/* ********************************************************** */
// main external program loop...call continually, moves servo & reads values
void radarClass::run(void)
{
unsigned long int t = millis();
// has the servo arrived at target angle yet?
if (t < servoArrivalTime)
return;
// read distance at current location, wait for result, and advance to next location
ping();
}
/* ********************************************************** */
// get ping distance at current servo position
void radarClass::ping(void)
{
if (!pingStarted) // send new ping, echoCheck will be called by interrupt Timer2 until result received
{
pingStarted = true;
waitingForPing = true;
sonar->ping_timer(echoCheck);
return;
}
else // pingStarted loop is true, already called ping timer...
{
if (!waitingForPing) // have we received the result back?
{
if (pingValue == 0) // NewPing returns 0 for undetactable ranges...
pingValue = maxDist;
sonarAverage[pos].add( pingValue );
pingStarted = false; // toggle flag to mark that we are ot of hte loop
nextPosition(); // advance to the next position
}
}
}
/* ********************************************************** */
// function called by NewPing ISR to check for ping result;
// Timer2 interrupt calls this function every 24uS where you can check the ping status.
void radarClass::echoCheck(void)
{
// Don't do anything here!
if (sonar->check_timer()) // Check to see if the ping was received.
{
pingValue = (sonar->ping_result / US_ROUNDTRIP_IN); // Ping returned, uS result in ping_result, convert to inches with US_ROUNDTRIP_IN.
waitingForPing = false; // toggle flag so we know result is complete
}
// Don't do anything here!
}
/* ********************************************************** */
// move servo to the next position
void radarClass::nextPosition(void)
{
// if at either end, change direction
if (pos == 0)
direction = 1;
else if (pos == maxAngle)
direction = -1;
// advance to next position
pos += direction;
radarServo->write(angles[pos]);
// calculate arrival time at next position
servoArrivalTime = millis() + ( (abs(angles[pos] - angles[pos + direction]) * servoSpeed ) / 60);
}
/* ********************************************************** */
// set the servo speed, in milliseconds per 60 degrees
void radarClass::setServoSpeed(int spd)
{
servoSpeed = spd;
}
/* ********************************************************** */
// Attach servo to specified pin
void radarClass::attachServo(int pin)
{
radarServo->attach(pin);
}
/* ********************************************************** */
// return the longest distance for given array entries (inclusive)
int radarClass::getMinDistance(int start_pos, int end_pos)
{
// to make sure underlying data isn't changed by ISR routine, briefly turn off interrupts
//noInterrupts();
// calculate the min value
int smallest = sonarAverage[start_pos].getAverage();
for (int t = start_pos + 1; t <= end_pos; t++)
{
int curr = sonarAverage[t].getAverage();
if (curr < smallest)
smallest = curr;
}
// turn interrupts back on
//interrupts();
// return the calculated value
return (smallest);
}
/* ********************************************************** */
// return closest distance to left (positions 0, 1 & 2)
int radarClass::getMinDistanceLeft(void)
{
return getMinDistance(0, 2);
}
/* ********************************************************** */
// return closest distance to right (positions 6, 7 & 8)
int radarClass::getMinDistanceRight(void)
{
return getMinDistance(6, 8);
}
/* ********************************************************** */
// return closest distance to front (positions 3, 4 & 5)
int radarClass::getMinDistasnceFront(void)
{
return getMinDistance(3, 5);
}
// code for debugging; prints all values in neat format
#ifdef RADAR_DEBUG_MODE
void radarClass::printAllValues(void)
{
static bool firstEntry = true;
if (firstEntry) // only print the header row once
{
Serial.println(" ");
Serial.println(F("Angle 1 Angle 2 Angle 3 Angle 4 Angle 5 Angle 6 Angle 7 Angle 8 Angle 9"));
for (int i = 0; i <= maxAngle; i++) // print the angles
{
printInt(angles[i], 7);
Serial.print(" ");
}
Serial.println("");
Serial.println(F("------- ------- ------- ------- ------- ------- ------- ------- -------"));
firstEntry = false;
}
// print the distances
for (int i = 0; i <= maxAngle; i++)
{
printInt(sonarAverage[i].getAverage(), 7);
Serial.print(" ");
}
Serial.println("");
} // void printAllValues(void)
/* ********************************************************** */
// Print integer, padded for alignment
void radarClass::printInt(int val, int len)
{
int strLen;
char buffer[30];
PString str(buffer, sizeof(buffer));
// Find the length of the printed value
str.begin();
str.print(val);
strLen = str.length();
str.begin();
// Pad string with leading spaces
for (int t = len - strLen; t > 0; t--)
{
str += " ";
}
// then print the string
str.print(val);
//str += " ";
// print result
Serial.print(str);
} // void printInt(int val, int len)
#endif // RADAR_DEBUG_MODE
#endif // RADAR_CLASS
/* ****************************************************************************************** */
/*
** TEST CODE HERE
********************************
*/
//#include <TimerOneThree.h>
#include <TimerOne.h>
#define RADAR_INT_TIME 100000 // 100,000 microseconds = 10x/second = 1 sweep of 9 angles per sceond
Servo myServo;
NewPing mySonar(10, 11, 250);
radarClass myRadar(&myServo, &mySonar);
// function to be called by the interrupt timer; will run the Radar code
void intRunRadar(void)
{
myRadar.run();
} // void intRunRadar()
void setupRadarInterrupt(void)
{
// Timer1.initialize(RADAR_INT_TIME); // set how often to run the interrupt (in microseconds)
// Timer1.attachInterrupt(intRunRadar);
} // void setupRadarInterrupt(void)
void setup()
{
Serial.begin(9600);
myRadar.attachServo(3);
myRadar.setServoSpeed(150);
delay(500);
//setupRadarInterrupt();
}
void loop()
{
unsigned long int t1, t2;
t1 = millis();
Serial.println("Starting test");
while (1)
{
myRadar.run();
t2 = millis();
if (t2 >= t1 + 1000)
{
t1 = t2;
myRadar.printAllValues();
}
} // while
} // loop()
I have apparently reached the limit of my knowledge here.
Add constructors to the sketch:
#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); //