If you know the max number of possible entries in the list and array is the easiest way to do it.
And by default global variables (including arrays) are filled with all-zero values.
If you need any other initial values, you can do that static in the variable declaration section or in setup()
.
I made some progress but I think I am missing something here.
#include <PublishManager.h>
PublishManager<20,5,70> publishManager;
const unsigned long interval = 8000;
String testers[4] = {"TestTrackTag01", "TestTrackTag02", "TestTrackTag03", "TestTrackTag04"};
unsigned long testersInterval[4] = {0,0,0,0};
void setup() {
Serial1.begin(115200);
}
void loop() {
unsigned long currentMillis = millis();
if(Serial1.available()){
String s = Serial1.readStringUntil('\n'); // From Sparkfun RedBoard
//Serial.println(s.c_str());
for (int i = 0; i < 4; i++){
// if (strncmp(s, testers[i], 14) == 0 && currentMillis - testersInterval[i] >= interval){
// testersInterval[i] = currentMillis;
// publishWithTimeStamp("Lap", s);
// }
if (currentMillis - testersInterval[i] >= interval){
publishWithTimeStamp("Lap", s);
testersInterval[i] = currentMillis;
}
}
}
}
void publishWithTimeStamp(String eventName, String data){
char buffer[255];
sprintf(buffer, "{\"tag\": \"%s\", \"time\": %u}",data.c_str(), Time.now());
publishManager.publish(eventName, buffer);
}
As soon as load the firmware and hold on RFID tag up to the reader it will wait for about 8 seconds and then publish the tag. BUT only once and all other tags are ignored until I reset the photon. I donāt know why I am struggling with this so much. What I was hoping it would do is:
Did I find a tag that matches one in the list?
If yes:
Publish the tag read as a lap
Then make this one wait for about 8 seconds before I care about when I see it next.
So, I did something similar⦠using std::vector
. Iāve adapted for your application.
I think Iāve done a good job documenting the code with good variable names, but just ask if you have questions.
// This #include statement was automatically added by the Particle IDE.
#include <PublishManager.h>
#include <vector>
constexpr size_t MAX_MESSAGE_LENGTH = 32;
constexpr uint16_t CROSSING_HYSTERESIS_SECONDS = 3;
PublishManager<> publishManager;
struct Runner {
char runnerTag[32];
uint32_t ts;
};
std::vector<Runner> runners;
const char* knownRunners[] = {"TestTrackTag01", "TestTrackTag02", "TestTrackTag03", "TestTrackTag04"};
void setup() {
Serial1.begin(115200);
}
void loop() {
publishManager.process();
if (const char* crossingRunner = checkForNewRunner('\n')) {
uint32_t crossingTime = Time.now();
bool isKnownRunner = false;
for (auto& t : knownRunners) {
if (strcmp(crossingRunner, t) == 0) {
isKnownRunner = true;
}
}
if (isKnownRunner) {
Runner newRunner;
strcpy(newRunner.runnerTag, crossingRunner);
newRunner.ts = crossingTime;
for (size_t i = 0; i < runners.size(); i++) {
if (strcmp(runners[i].runnerTag, newRunner.runnerTag) == 0) {
runners.erase(runners.begin() + i);
}
}
runners.insert(runners.begin(), newRunner);
}
}
if (runners.size()) {
if (Time.now() - runners.back().ts > CROSSING_HYSTERESIS_SECONDS) {
char pubMessage[64];
sprintf(pubMessage, "{\"tag\":\"%s\",\"time\":%d}", runners.back().runnerTag, runners.back().ts);
publishManager.publish("Lap", pubMessage);
runners.pop_back();
}
}
}
const char* checkForNewRunner(const char endMarker) {
static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
static byte idx = 0;
if (Serial1.available()) {
incomingMessage[idx] = Serial1.read();
if (incomingMessage[idx] == endMarker) {
incomingMessage[idx] = '\0';
idx = 0;
return incomingMessage;
} else {
idx++;
if (idx > MAX_MESSAGE_LENGTH - 1) {
//stream.print(F("{\"error\":\"message too long\"}\n")); //you can send an error to sender here
idx = 0;
incomingMessage[idx] = '\0';
}
}
}
return nullptr;
}
This looks brilliant!
Not 100% sure what every bit means yet but I will google it to learn. Thanks a bunch for jumping in with great input! I will ask questions Iām sure.
@BulldogLowell I uploaded it for fun. I am not getting any tag reads to the console. I will put some serial prints in to see what is going on programmatically. Any good C++ books you would recommend?
Let me make some changes That will allow you to debug a little easier.
// This #include statement was automatically added by the Particle IDE.
#include <PublishManager.h>
#include <vector>
constexpr size_t MAX_MESSAGE_LENGTH = 32;
constexpr uint16_t CROSSING_HYSTERESIS_SECONDS = 3;
PublishManager<> publishManager;
const unsigned long interval = 8000;
struct Runner {
char runnerTag[32];
uint32_t ts;
};
std::vector<Runner> runners;
const char* knownRunners[] = {"TestTrackTag01", "TestTrackTag02", "TestTrackTag03", "TestTrackTag04"};
void setup() {
Serial.begin(9600); //<<<<< don't forget!
Serial1.begin(115200);
}
void loop() {
publishManager.process();
if (const char* crossingRunner = checkForNewRunner('\n')) {
Serial.printf("%s just crossed the line...", crossingRunner);
uint32_t crossingTime = Time.now();
bool isKnownRunner = false;
for (auto& t : knownRunners) {
if (strcmp(crossingRunner, t) == 0) {
isKnownRunner = true;
Serial.printf("%s is known to me", crossingRunner);
}
}
if (isKnownRunner) {
Runner newRunner;
strcpy(newRunner.runnerTag, crossingRunner);
newRunner.ts = crossingTime;
for (size_t i = 0; i < runners.size(); i++) {
if (strcmp(runners[i].runnerTag, newRunner.runnerTag) == 0) {
runners.erase(runners.begin() + i);
Serial.printf("%s was in the queue.", crossingRunner);
}
}
runners.insert(runners.begin(), newRunner);
Serial.printf("%s added to the queue.", crossingRunner);
}
}
if (runners.size()) {
if (Time.now() - runners.back().ts > CROSSING_HYSTERESIS_SECONDS) {
char pubMessage[64];
sprintf(pubMessage, "{\"tag\":\"%s\",\"time\":%d}", runners.back().runnerTag, runners.back().ts);
publishManager.publish("Lap", pubMessage);
Serial.printf("%s sent to publishManager with a time of: %d.", runners.back().runnerTag , runners.back().ts);
runners.pop_back();
}
}
}
const char* checkForNewRunner(const char endMarker) {
static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
static byte idx = 0;
if (Serial1.available()) {
incomingMessage[idx] = Serial1.read();
if (incomingMessage[idx] == endMarker) {
incomingMessage[idx] = '\0';
idx = 0;
return incomingMessage;
} else {
idx++;
if (idx > MAX_MESSAGE_LENGTH - 1) {
//stream.print(F("{\"error\":\"message too long\"}\n")); //you can send an error to sender here
idx = 0;
incomingMessage[idx] = '\0';
}
}
}
return nullptr;
}
try this as is and let me knowā¦
just crossed the line...TestTrackTag02.
Is what comes across in the serial monitor. So itās reading the tags. The publish does not occur.
I would also like to mention that I do appreciate your assistance. I found a good book at the store but itās like $80. So I read through it but couldnāt see how relevant it was next to your code.
For example, their use of Vector
std::vector<int> = myVec
There was no example to show how to use it for other cases. Like Runner
.
std::vector<this_is_the_kind_of_object_of_which_the_vector_stores> = this_is_the_name
and the other events...? Did they get reported?
No just "Crossed the line... TestTrackTag" n being which ever tag is read. But this is in Serial Only. So nothing is pub'd.
so now the next block then⦠we have to search the tag to make sure it is on the known list (i.e. error check).
bool isKnownRunner = false;
for (auto& t : knownRunners) {
if (strcmp(crossingRunner, t) == 0) {
isKnownRunner = true;
Serial.printf("%s is known to me", crossingRunner);
}
}
are you using my code EXACTLY as I just gave it to you?
ā¦by the way, this worked for me (published)
Exactly as provided. Literally did a copy and paste.
let's eliminate the effects of the check by changing this:
bool isKnownRunner = false;
to this:
bool isKnownRunner = true;
Just made the change as your described and now two tags have pubād to the cloud!
sent to publishManager with a time of: 1526350706.TestTrackTag02
Do you recon using printlnf would make more space for the Serial output?
OK, I think that there is a carriage return and a line feed coming in from your tag reader⦠yes?
Yes I believe \n
is at the end as I am using ptSerial.println();
at the end of the read on the arduino with the tag reader on it.
#include "SparkFun_UHF_RFID_Reader.h"
#include <SoftwareSerial.h>
SoftwareSerial rfSerial(2,3);
SoftwareSerial ptSerial(8,9);
RFID rfid;
void setup()
{
Serial.begin(115200);
ptSerial.begin(115200);
Serial.println("Initializing...");
ptSerial.println("Initializing...");
if (setupNano(38400) == false)
{
Serial.println("Check Wiring");
ptSerial.println("Check Wiring");
while (1);
}
rfid.setRegion(REGION_NORTHAMERICA);
rfid.setReadPower(500); // 5.00 dBm
Serial.println("Starting Scan...");
ptSerial.println("Starting Scan");
rfid.startReading();
}
void loop()
{
if (rfid.check() == true)
{
byte responseType = rfid.parseResponse();
if (responseType == RESPONSE_IS_KEEPALIVE)
{
Serial.println(F("Scanning"));
//ptSerial.println(F("Scanning"));
}
else if (responseType == RESPONSE_IS_TAGFOUND)
{
//If we have a full record we can pull out the fun bits
int rssi = rfid.getTagRSSI(); //Get the RSSI for this tag read
long freq = rfid.getTagFreq(); //Get the frequency this tag was detected at
long timeStamp = rfid.getTagTimestamp(); //Get the time this was read, (ms) since last keep-alive message
byte tagEPCBytes = rfid.getTagEPCBytes(); //Get the number of bytes of EPC from response
Serial.print(F(" rssi["));
Serial.print(rssi);
Serial.print(F("]"));
Serial.print(F(" freq["));
Serial.print(freq);
Serial.print(F("]"));
Serial.print(F(" time["));
Serial.print(timeStamp);
Serial.print(F("]"));
// ptSerial.print(F("RSSI: "));
// ptSerial.print(rssi);
// ptSerial.print(F(", "));
//
// ptSerial.print(F("Freq: "));
// ptSerial.print(freq);
// ptSerial.print(F(", "));
//Print EPC bytes, this is a subsection of bytes from the response/msg array
Serial.print(F("Tag: "));
for (byte x = 0 ; x < tagEPCBytes ; x++)
{
if (rfid.msg[31 + x] < 0x10) Serial.print(F("0"));//Pretty print
Serial.print((char) rfid.msg[31 + x]);
ptSerial.print((char) rfid.msg[31 + x]);
}
Serial.print(F(" "));
Serial.println();
ptSerial.println();
}
else if (responseType == ERROR_CORRUPT_RESPONSE)
{
Serial.println(F("Bad CRC"));
ptSerial.println(F("Bad CRC"));
}
else
{
Serial.println(F("Unknown Error"));
ptSerial.println(F("Unknown Error"));
}
}
}
boolean setupNano(long baudRate)
{
rfid.begin(rfSerial);
rfSerial.begin(baudRate);
while (!rfSerial);
while (rfSerial.available()) rfSerial.read();
rfid.getVersion();
if (rfid.msg[0] == ERROR_WRONG_OPCODE_RESPONSE)
{
rfid.stopReading();
delay(1500);
}
else
{
rfSerial.begin(115200);
rfid.setBaud(baudRate);
rfSerial.begin(baudRate);
}
rfid.getVersion();
if (rfid.msg[0] != ALL_GOOD) return (false);
rfid.setTagProtocol();
rfid.setAntennaPort();
return true;
}
The Arduino codeā¦
yes, but you were not error checking, as i did.
Can you then try one more thing? This is my original version but I ignore a carriage return when buffering the Serial1...
// This #include statement was automatically added by the Particle IDE.
#include <PublishManager.h>
#include <vector>
constexpr size_t MAX_MESSAGE_LENGTH = 32;
constexpr uint16_t CROSSING_HYSTERESIS_SECONDS = 3;
PublishManager<> publishManager;
const unsigned long interval = 8000;
struct Runner {
char runnerTag[32];
uint32_t ts;
};
std::vector<Runner> runners;
const char* knownRunners[] = {"TestTrackTag01", "TestTrackTag02", "TestTrackTag03", "TestTrackTag04"};
void setup() {
Serial1.begin(115200);
}
void loop() {
publishManager.process();
if (const char* crossingRunner = checkForNewRunner('\n')) {
Serial.printf("%s just crossed the line...", crossingRunner);
uint32_t crossingTime = Time.now();
bool isKnownRunner = false;
for (auto& t : knownRunners) {
if (strcmp(crossingRunner, t) == 0) {
isKnownRunner = true;
Serial.printf("%s is known to me", crossingRunner);
}
}
if (isKnownRunner) {
Runner newRunner;
strcpy(newRunner.runnerTag, crossingRunner);
newRunner.ts = crossingTime;
for (size_t i = 0; i < runners.size(); i++) {
if (strcmp(runners[i].runnerTag, newRunner.runnerTag) == 0) {
runners.erase(runners.begin() + i);
Serial.printf("%s was in the queue.", crossingRunner);
}
}
runners.insert(runners.begin(), newRunner);
Serial.printf("%s added to the queue.", crossingRunner);
}
}
if (runners.size()) {
if (Time.now() - runners.back().ts > CROSSING_HYSTERESIS_SECONDS) {
char pubMessage[64];
sprintf(pubMessage, "{\"tag\":\"%s\",\"time\":%d}", runners.back().runnerTag, runners.back().ts);
publishManager.publish("Lap", pubMessage);
Serial.printf("%s sent to publishManager with a time of: %d.", runners.back().runnerTag , runners.back().ts);
runners.pop_back();
}
}
}
const char* checkForNewRunner(const char endMarker) {
static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
static byte idx = 0;
if (Serial1.available()) {
if (Serial1.peek() == '\r') { // ditch the <CR>
Serial1.read();
return nullptr;
}
incomingMessage[idx] = Serial1.read();
if (incomingMessage[idx] == endMarker) {
incomingMessage[idx] = '\0';
idx = 0;
return incomingMessage;
} else {
idx++;
if (idx > MAX_MESSAGE_LENGTH - 1) {
//stream.print(F("{\"error\":\"message too long\"}\n")); //you can send an error to sender here
idx = 0;
incomingMessage[idx] = '\0';
}
}
}
return nullptr;
}