Problem: As soon as I write to the USB serial port after accessing my I2C device asynchronously (polling it in main loop rather than a blocking loop), I get a CPU exception
Background: I have an Arduino project running under Microchip studio (for debugging via the J-Link interface). I am communicating with a VCNL4010 proximity sensor via I2C. I have a working synchronous function that reads proximity as such:
Zult<uint16_t> ProximitySensor::readProximity(void) {
auto res = setIntMask(0x80);
if (res.isError())
return Zult<uint16_t>(res);
res = write8(VCNL4010_COMMAND, VCNL4010_MEASUREPROXIMITY);
if (res.isError())
return Zult<uint16_t>(res);
auto start = millis();
do {
auto cmd = read8(VCNL4010_COMMAND);
if (cmd.isError())
return Zult<uint16_t>(cmd);
if (cmd.getResult() & VCNL4010_PROXIMITYREADY) {
return read16(VCNL4010_PROXIMITYDATA);
}
delay(1);
} while (millis() - start < 1000);
return Zult<uint16_t>(0, "timeout in readProximity");
}
This code works fine, but blocks my main loop until the sensor reading is complete. I need to avoid blocking because I'm also controlling a motor. I wrote an async version that's broken into these functions:
Zult<bool> ProximitySensor::readProximityBegin() {
auto res = setIntMask(0x80);
if (res.isError())
return Zult<uint16_t>(res);
res = write8(VCNL4010_COMMAND, VCNL4010_MEASUREPROXIMITY);
if (res.isError())
return Zult<uint16_t>(res);
asyncStart = millis();
asyncReady = false;
asyncResult = NotReadyZult;
}
bool ProximitySensor::readProximityReady() {
return asyncReady;
}
void ProximitySensor::stopAsync() {
asyncReady = true;
asyncStart = 0;
}
void ProximitySensor::update() {
if (asyncStart != 0) {
auto cmd = read8(VCNL4010_COMMAND);
if (cmd.isError()) {
asyncResult = Zult<uint16_t>(cmd);
stopAsync();
return;
}
if (cmd.getResult() & VCNL4010_PROXIMITYREADY) {
asyncResult = read16(VCNL4010_PROXIMITYDATA);
stopAsync();
return;
}
if (millis() - asyncStart > 1000) {
asyncResult = Zult<uint16_t>(0, "timeout in readProximity");
stopAsync();
}
}
}
Zult<uint16_t> ProximitySensor::readProximityResult() {
return asyncResult;
}
In response to a serial command I invoke readProximityBegin and call update in my main loop until readProximityReady becomes true, at which time I read the value using readProximityResult. As mentioned, as soon as I write to the USB serial port after all this, I get a CPU exception (the error originates from the serial write in the RTL). I'm not sure which exception exactly because in cortex_handlers.c it looks like the arduino code for the SAMD21 routes all sorts of exceptions to this same function:
void Dummy_Handler(void)
{
#if defined DEBUG
__BKPT(3); // this is where my debugger stops
#endif
for (;;) { }
}
Here's an example of one of my I2C functions referenced by the above:
Zult<uint8_t> ProximitySensor::read8(uint8_t command) {
Wire.beginTransmission(_i2caddr);
Wire.write(command);
auto result = Wire.endTransmission();
if (result != 0) {
return Zult<uint8_t>((int)result, "Writing request to read8");
}
delayMicroseconds(170); // delay required
Wire.requestFrom(_i2caddr, (uint8_t)1);
auto start = millis();
while (!Wire.available()) {
if (millis() - start > 1000)
return Zult<uint16_t>(0, "Timeout waiting for read");
}
return Zult<uint8_t>(Wire.read());
}
Related
I am creating a library to manage our wifi network connection on many ESP32 devices.
Inside my library, i have a task that is looping with a 1 second pause started with an xTaskCreate inside my .begin() function. The problem is that i can't start the task if i haven't make it static in my .h file. So once the task is declared as static, i don't have access to the this-> pointer inside this task. Is there anyway i can send the this-> pointer inside my task like in parameters or something like that ?
Here is some code:
class EraWiFi{
public:
EraWiFi();
/*!
* #brief Initialize WiFi
* #return Returns true if successful
*/
bool begin(fs::FS &fs);
/*!
* #brief Start EraWiFi service
* #return Returns true if successful
*/
void downloadWiFi();
private:
static void wifiTask(void * parameter);
};
bool EraWiFi::begin(fs::FS &fs){
File file = fs.open("/WiFi.json", FILE_READ);
if(file){
DynamicJsonDocument wifi(2048);
DeserializationError error = deserializeJson(wifi, file);
file.close();
if (!error){
Serial.println("We have a VALID WiFi.json configurations file!");
Serial.println("Adding wifi networks:");
for (JsonArray arr : wifi.as<JsonArray>()){
Serial.print("SSID: ");
Serial.print(arr[0].as<char*>());
Serial.print(", KEY: ");
Serial.println(arr[1].as<char*>());
wifiMulti.addAP(arr[0].as<char*>(), arr[1].as<char*>());
}
}else{
Serial.println("We don't have a VALID WiFi.json configurations file!");
}
}else{
Serial.println("There is no WiFi.json configurations file!");
}
wifiMulti.addAP("Testing", "1234");
xTaskCreate(
this->wifiTask, // Function that should be called
"WiFiTask", // Name of the task (for debugging)
10000, // Stack size (bytes)
(void*)&this, // Parameter to pass
1, // Task priority
NULL // Task handle
);
return true;
}
void EraWiFi::downloadWiFi(){
Serial.println("Downloading WiFi.json from ErabliTEK Server.");
HTTPClient http;
// Send request
http.useHTTP10(true);
String url = "https://testing.testing.com/wifis/getWiFi/";
http.begin(url);
int httpResponseCode = http.GET();
if(httpResponseCode == HTTP_CODE_OK){
// Parse response
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, http.getStream());
// Disconnect
http.end();
if (!error){
File file = fs.open("/WiFi.json", FILE_WRITE);
if(file){
// Serialize JSON to file
if (serializeJson(doc, file) == 0) {
Serial.println("Error saving WiFi.json!");
Serial.println(http.getString());
}else{
Serial.println("Succesfully saved WiFi.json!");
}
}
// Close the file
file.close();
}else{
Serial.println("Error downloading WiFi.json");
}
}else{
Serial.println("Problem connecting to " + url + " with http code: " + String(httpResponseCode));
}
}
void EraWiFi::wifiTask(void * parameters){
bool wifiConnected = false;
for(;;){
uint8_t wifiStatus = wifiMulti.run();
if((wifiStatus != WL_CONNECTED) and wifiConnected) {
wifiConnected = false;
Serial.println("WiFi not connected!");
}else if((wifiStatus == WL_CONNECTED) and !wifiConnected){
wifiConnected = true;
Serial.println("WiFi Connected.");
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
Serial.print("KEY: ");
Serial.println(WiFi.psk());
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
EraWiFi::downloadWiFi();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
You're already doing that, you just don't realize it. Have a look at the FreeRTOS documentation on xTaskCreate() which explains how the fourth parameter pvParameters is a void pointer that gets passed on to the new task as its (one and only) input parameter.
The commented line here means you're taking the address of your EraWiFi object and passing it to the task:
xTaskCreate(
this->wifiTask,
"WiFiTask",
10000,
(void*)&this, //< Pointer gets forwarded to the task
1,
NULL
);
To use the pointer inside your task, you only need to cast it back from void*:
void EraWiFi::wifiTask(void * parameters){
EraWifi* p = static_cast<EraWifi*>(parameters); //< That's the same pointer
p->someMethod();
...
PS. Also note (before you poke at the same object from different threads, possibly causing hard-to-find bugs) that FreeRTOS provides excellent inter-thread communication facilities - the queues and task notifications often being the most useful ones. Check out their tutorial/book for an explanation.
I have a program on my computer that sends serial data to my Arduino that dictates the analog write on the Arduino. How can I check if no new serial data has been sent for x seconds and if there isn't do something. E.G:
if (newDataInLastSeconds(10) {
// Do Something...
}```
For this problem you can use a state machine that tracks the state of the serial link, and setup actions that trigger on state transitions. If you also want to setup actions that happen every loop() depending on the state of the link, that's even easier to set up (not shown).
You're likely already checking Serial.available() to see if there are any bytes received on the serial line, correct? All you need to do is create a very simple two-state state machine:
Determine the timeout you want
Keep track of what state the data timeout is in (bool serial_fresh -- you've either received data recently or not)
Keep a record of the last time you received any data
If the serial line is fresh, compare the current time to the previous time. If greater than your timeout, then set fresh to false (this is a state transition) and do the action you want to do
If the serial line is not fresh, and you just received data, then set fresh to true (another state transition) and do the action you want to do on that transition
// start in the unfresh state, we won't transition to fresh until
// we receive the first byte - if this doesn't match what you want change to fit
bool g_serial_fresh = false;
const unsigned long SERIAL_TIMEOUT_MS = 5000; // or however many milliseconds
unsigned long g_last_serial_rx_ms = 0;
void transitionToFresh() {
g_serial_fresh = true;
/* do what you want to do when the link comes back up - turn on ADC */
}
void transitionToStale() {
g_serial_fresh = false;
/* do what you want to do when the link times out - shutdown ADC */
}
bool serialTimedOut() {
return (millis() - g_last_serial_rx_ms) > SERIAL_TIMEOUT_MS;
}
void doStateMachine() {
bool received_data_this_loop = false;
if (Serial.available() > 0) { // assumes you read all available bytes later
g_last_serial_rx_ms = millis();
received_data_this_loop = true;
}
if (!g_serial_fresh && received_data_this_loop) {
transitionToFresh();
}
else if (g_serial_fresh && serialTimedOut()) {
transitionToStale();
}
}
void loop() {
doStateMachine();
/* do regular programmy things here */
}
You can make a function with a while loop that checks for new data during a specific amount of time by using the Serial.available function:
boolean newDataInInterval(long waitInterval){
long startTime = millis();
while (millis() <= (startTime+waitInterval)) {
if (Serial.available() > 0) {
return true;
}
}
return false;
}
Note that waitInterval should be given in milliseconds.
I am working on a project on Arduino in which Arduino have to count 1 sec and on every sec Arduino has to run a certain function (every sec I mean a complete sec not an instance of a sec).
if (((millis()) % 1000) == 0)
{
tym_countdown();
Serial.println("Refresh");
What is happening as you can see below my Arduino is counting instances rather only complete secs.
20:54:04.145 -> Refresh
20:54:04.145 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.165 -> Refresh
20:54:05.165 -> Refresh
20:54:05.165 -> Refresh
20:54:06.103 -> Refresh
20:54:06.103 -> Refresh
20:54:06.103 -> Refresh
20:54:06.150 -> Refresh
Code:
#define X 13
#define Y 12
#define Z 11
char txt[15];
char tym[6];
int index,tym_index;
int pin_status_array[2][3]={0,0,0,0,0,0};
void setup() {
// put your setup code here, to run once:
pinMode(X,OUTPUT);// setting the pin flow of control as output
pinMode(Y,OUTPUT);
pinMode(Z,OUTPUT);
Serial.begin(9600);
while(!Serial)
{
; //to wait for pc to connect
}
Serial.println("\nHome Automation");
initial_dash_print();
}
void loop() {
// put your main code here, to run repeatedly:
read_incomming_serial_data();
if(((millis())%1000)==0)
{
tym_countdown();
Serial.println("Refresh");
}
turn_on_n_off();
}
void initial_dash_print() //to print dashes
{ Serial.println("-----------------------------------------------");
Serial.println("give me some command"); //ask for command
}
void read_incomming_serial_data()// read incoming data from serial
{
if(Serial.available()>0)
{ delay(17); // delay for arduino to wait and read commands,1ms per char.
index=0;
tym_index=0;
while(Serial.available()>0) //if serial available
{ char inchar=Serial.read();
txt[index]=inchar; // add char to txt string
index++;// increment to where to write next
if(index>4)
{ tym[tym_index]=inchar;
tym_index++;
}
}
Serial.print(txt);
check_data_from_serial();
}
}
void check_data_from_serial() // to check data from serial
{
if(strncmp(txt,"ON X",4)==0)
{
pin_status_array[0][0]=1;
if(tym_index!=0)
{
tym_update(X);
}
}
else if(strncmp(txt,"OFF X",5)==0)
{
pin_status_array[0][0]=0;
}
if(strncmp(txt,"ON Y",4)==0)
{
pin_status_array[0][1]=1;
if(tym_index!=0)
{
tym_update(Y);
}
}
else if(strncmp(txt,"OFF Y",5)==0)
{
pin_status_array[0][1]=0;
}
if(strncmp(txt,"ON Z",4)==0)
{
pin_status_array[0][2]=1;
if(tym_index!=0)
{
tym_update(Z);
}
}
else if(strncmp(txt,"OFF Z",5)==0)
{
pin_status_array[0][2]=0;
}
else if(txt=="STATUS")
{
}
}
void tym_update(int pin) // update time row
{
int temp=pin-13; // pin to array column equivalent
pin_status_array[1][temp]=atoi(tym);
}
void tym_countdown() //to perform time countdown
{ for(int temp_index=0; temp_index<4; temp_index++)
{
if(pin_status_array[0][temp_index]==1 && pin_status_array[1][temp_index]>0) // '0' => txt '1' => tym
{
int temp=pin_status_array[1][temp_index];
temp--;
pin_status_array[1][temp_index]=temp;
if(temp==0)
{
pin_status_array[0][temp_index]=0; //turn off
}
}
}
}
void turn_on_n_off()
{
if(pin_status_array[0][0]==1)
{
digitalWrite(X,HIGH);
}
if(pin_status_array[0][0]==0)
{
digitalWrite(X,LOW);
}
if(pin_status_array[0][1]==1)
{
digitalWrite(Y,HIGH);
}
if(pin_status_array[0][1]==0)
{
digitalWrite(Y,LOW);
}
if(pin_status_array[0][2]==1)
{
digitalWrite(Z,HIGH);
}
if(pin_status_array[0][2]==0)
{
digitalWrite(Z,LOW);
}
}
If you need to trigger an action exactly every 1 second then you should try using an Interrupt Service Routine (ISR). Depending on what board you are using then this can be done using the Timer library. You'll need to keep the ISR short or you could defer the processing to the loop() function.
If you don't have such hard realtime requirements and only need to perform an action once per second then you can record the last time the action ran in loop(). Then check if at least 1000 ms have elapsed since the last iteration of loop().
e.g.
unsigned long lastTriggerTime = 0;
void loop() {
read_incomming_serial_data();
auto currentLoopTime = millis();
if(currentLoopTime - lastTriggerTime >= 1000)
{
lastTriggerTime = currentLoopTime;
tym_countdown();
You can "synchronize" blt's suggestion to exact 1000 ms intervals easily:
unsigned long lastTriggerTime = 0;
void loop() {
read_incomming_serial_data();
unsigned long currentLoopTime = millis();
if(currentLoopTime - lastTriggerTime >= 1000)
{
lastTriggerTime += 1000; // will increment in 1000's steps
tym_countdown();
...
Of course, this has nothing to do with real world seconds and has a limited accuracy of about 0.1%
Here is a link to openenergy monitor where I got the current sensor, you can find the type CT3100.
I'm working on an energy monitor using an Arduino Uno. I'm already able to get values (which are correct) of total power in the Arduino monitor. I would like to send that data to a database using an ESP8266 WiFi module.
The problem that occurs is when I add ESP module to Arduino, the values of total power I get in the monitor are not correct anymore.
void setup(){
wifi.setBootMarker(F("Version:0.9.2.4]\r\n\r\nready"));
softser.begin(9600); // Soft serial connection to ESP8266
Serial.begin(9600);;
emon1.current(1, 55.6); // Current: input pin, calibration.
char buffer[50];
Serial.print(F("Hard reset..."));
if(!wifi.hardReset()) {
Serial.println(F("no response from module."));
for(;;);
}
Serial.println(F("OK."));
Serial.print(F("Soft reset..."));
if(!wifi.softReset()) {
Serial.println(F("no response from module."));
for(;;);
}
Serial.println(F("OK."));
Serial.print(F("Checking firmware version..."));
wifi.println(F("AT+GMR"));
if(wifi.readLine(buffer, sizeof(buffer))) {
Serial.println(buffer);
wifi.find(); // Discard the 'OK' that follows
} else {
Serial.println(F("error"));
}
Serial.print(F("Connecting to WiFi..."));
if(wifi.connectToAP(F(ESP_SSID), F(ESP_PASS))) {
Serial.print(F("OK\nChecking IP addr..."));
wifi.println(F("AT+CIFSR"));
if(wifi.readLine(buffer, sizeof(buffer))) {
Serial.println(buffer);
wifi.find(); // Discard the 'OK' that follows
}
}
}
void loop(){
Serial.print(F("Connecting to host..."));
if(wifi.connectTCP(F(HOST), PORT)) {
Serial.print(F("OK\nRequesting page..."));
double Irms = emon1.calcIrms(1480); //extract Irms into Variable
double power = Irms*208;
String data;
data+="{\"IdClient\":";
data+=Client_id;
data+=",\"Nom\":\"";
data+=Nom;
data+="\",\"Irms\":";
data+=Irms;
data+=",\"Power\":";
data+=power;
data+="}";
softser.print(data);
String Post="POST / HTTP/1.1\r\nHost: 13.81.244.50\r\nContent-Type:
application/json\r\nContent-Length:";
Post+=String(data.length());
Post+="\r\n\r\n";
Post+=data;
Post+="\r\n\r\n";
String Send = "AT+CIPSEND=";
Send+=String(Post.length());
wifi.println(Send);
if (wifi.find(F(">"))){
wifi.print(Post);
}
//Test
if(wifi.find(F("OK"), true)) {
Serial.println(F("found!"));
} else {
Serial.println(F("not found."));
}
wifi.closeTCP();
} else { // TCP connect failed
Serial.println(F("D'oh!"));
}
delay(1000);
}
I am calculating throughput of BLE module using Arduino Mega. Module works on 3.3V so i have Logic Level Shifter between BLE and Arduino. BLE UART is set at 115200 and is sending data at the speed of 64Kbps(verified using CC2540 BLE packet sniffer). Packet send by BLE are in this format in hex:400102030405060708090A0B0C0D0E0F1011121323{40=#,23=#}. I am sending 100 number of packets.Here is the abstract of my code. Code works fine for lower bit rate 32Kbps but not for 64Kbs(BLE Connection interval to 10ms). It does not show any result in this bit rate.
void loop()
{
if(rxflag)
{
rxflag = false;
switch(rxState)
{
case get_first_header:
if(rxChar=='#')
{
startPoint=millis();
rxState=get_last_header;
}
break;
case get_last_header:
if(rxChar=='#')
{
packetNo++;
if(packetNo==100)
{
endPoint=millis();
totalTime= endPoint-startPoint;
Serial.print("Total Time of Packet=");
Serial.println(totalTime);
}
break;
}
}
}
void serialEvent1()
{
if (Serial1.available()>0)
{
rxChar = (char)Serial1.read();
rxflag = true;
}
}
When you 'purge' the serial buffer:
void serialEvent1()
{
if (Serial1.available()>0)
{
rxChar = (char)Serial1.read(); // was previous value of rxChar read ? mystery...
// this is a serious problem.
// 1 byte at a time? This can slow down
rxflag = true; // reception quite a bit, esp. if there
} // is xon/xoff.
}
When you have an event that's been set in an interrupt routine, you need to reset it before reading the data, unless you use the flag as a lock for interrupt data.
Here's what you should consider to dramatically improve throughput and reduce receive errors.
void serialEvent1()
{
rxflag = true;
}
//...
void lopp()
{
// ...
if (rxflag)
{
rxflag = false; // reset/read order is to avoid stalling.
while (Serial1.available())
{
// reading 4 or up 16 bytes at a time on a local buffer
// would help the tiny mega get this done job done faster.
//
// char buffer[8] buffer; // 2 dwords on the stack. don't overuse!
//
char c = (char)Serial.read();
// run state machine...
}
}
// ...
}