OneWire bus functionality quits working when Photon WiFi is turned off

Hello,

I have a data logging system that logs water temperature and pH. In order to use the system in places where there is no WiFi or where I don't have access to the network, I have a switch that tells the Photon to turn the WiFi on or off on boot. It's a simple switch connected to D5 with INPUT_PULLUP and GND. I have done something identical to this in the past with no issue. However, when I turn of the WiFi off on the Photon, the OneWire communication stops.

I am using a DS18B20. I've tried using three different libraries (The built in DS18.cpp in the OneWire lib, DS18B20, and DS18B20Minimal) all with the same results. The sensor is hooked up correctly with a 4.7k resistor between D4 and 3.3V. The sensor works flawlessly when WiFi is on, but then reports NAN or -127, depending on the lib used once it's turned off. This leads me to believe that it's a firmware issue and not a hardware issue. The only post I could find related to this issue was here:

However, the OneWire issue mentioned there did not seem resolved. Am I going about taking the Photon offline the wrong way? Do I need to restart the OneWire bus when the WiFi is off? Any help is appreciated. Thanks!

Here's the code used, with some unrelated functions omitted for readability:

#include "SparkFun_Qwiic_OpenLog_Arduino_Library.h"
#include <SparkFunMicroOLED.h>
#include <OneWire.h>
#include <DS18B20.h>
//#include "DS18.h"
//#include <DS18B20Minimal.h>
#include "logo.h"

const int      MAXRETRY          = 4;
DS18B20  ds18b20(D4, true); //Sets Pin D2 for Water Temp Sensor and 
                            // this is the only sensor on bus

#define STATUS_SD_INIT_GOOD 0
#define STATUS_LAST_COMMAND_SUCCESS 1
#define STATUS_LAST_COMMAND_KNOWN 2
#define STATUS_FILE_OPEN 3
#define STATUS_IN_ROOT_DIRECTORY 4

#define internetInput 5//switch to turn WiFi on/off

OpenLog myLog; //Create instance
MicroOLED oled;

double tempf;
double tempc;

Timer timer(900000, logInfo);//15 min
Timer timer2(10000, printInfo);//10 second refresh rate on OLED

SYSTEM_MODE(SEMI_AUTOMATIC);//set to semi-auto to disable WiFi for demos

//---------------------------------------------------------------
void setup()
{
  pinMode(internetInput, INPUT_PULLUP);

  if(digitalRead(internetInput) == LOW)
  {
    WiFi.off();
    delay(2000);
    Particle.disconnect();
    RGB.control(true);
    RGB.color(0, 0, 0);
  }
  else
  {
    WiFi.on();
    delay(2000);
    Particle.connect();
    RGB.control(false);
  }
    
    Wire.begin(); //Initialize I2C
      
    myLog.begin(); //Open connection to OpenLog (no pun intended)
    myLog.syncFile();
    myLog.append("AquaGrove.txt");
    
    oled.begin();
    oled.clear(ALL); // Clear the display's internal memory
    oled.clear(PAGE);
    oled.drawBitmap(openponics);
    oled.display();
    delay(2000);
    
    checkStatus();
 
    Time.zone(-7);
    
    printInfo();//print and log once before starting timers
    logInfo();
    
    timer.start();
    timer2.start();
}
//---------------------------------------------------------------
void loop()
{
//Timers handle all function calls
}
//---------------------------------------------------------------
void getTemp()
{
  float _temp;
  int   i = 0;

  do {
    _temp = ds18b20.getTemperature();
  } while (!ds18b20.crcCheck() && MAXRETRY > i++);

  if (i < MAXRETRY) {
    tempc = _temp;
    tempf = ds18b20.convertToFahrenheit(_temp);
    //Serial.println(fahrenheit);
  }
  else {
    tempc = tempf = NAN;
    //Serial.println("Invalid reading");
  }
  //msLastSample = millis();
}
//---------------------------------------------------------------
void printInfo()
{
    get_ph1_data();
    
      oled.clear(PAGE);
      
      oled.setCursor(0,0);
      oled.print(tempf);
      oled.print("*F");
      
      oled.setCursor(0,15);
      oled.print(tempc);
      oled.print("*C");
      
      oled.setCursor(0,30);
      oled.print("pH:");
      oled.print(ph1_data);
      
  //print date
//   oled.setCursor(0,30);
//   if (Time.month() < 10)
//     oled.print('0'); // Print leading '0' 
//   oled.print(String(Time.month()) + "-"); // Print month
//   if (Time.day() < 10)
//     oled.print('0'); // Print leading '0' 
//   oled.print(String(Time.day()) + "-"); // Print day
//   oled.print(String(Time.year())); // Print year

  //Print current time 
//   oled.setCursor(0,40);
//   oled.print(String(Time.hour()) + ":"); // Print hour
//   if (Time.minute() < 10)
//     oled.print('0'); // Print leading '0' for minute
//   oled.print(String(Time.minute()) + ":"); // Print minute
//   if (Time.second() < 10)
//     oled.print('0'); // Print leading '0' for second
//   oled.print(String(Time.second())); // Print second

      oled.display();
}
//---------------------------------------------------------------
void logInfo()
{
      //print date
  //myLog.print("Date: ");
  if (Time.month() < 10)
    myLog.print('0'); // Print leading '0' 
  myLog.print(String(Time.month()) + "-"); // Print month
   delay(10);
  if (Time.day() < 10)
    myLog.print('0'); // Print leading '0' 
  myLog.print(String(Time.day()) + "-"); // Print day
   delay(10);
  myLog.print(String(Time.year())); // Print year
   delay(10);
    
    myLog.print(", ");
    myLog.print(String(Time.hour()) + ":"); // Print hour
    delay(10);
    if (Time.minute() < 10)
      myLog.print('0'); // Print leading '0' for minute
    myLog.print(String(Time.minute()) + ":"); // Print minute
    delay(10);
    if (Time.second() < 10)
      myLog.print('0'); // Print leading '0' for second
    myLog.print(String(Time.second())); // Print second
    delay(10);
    
    get_ph1_data();
    
    myLog.print(",  " + String(tempf, 2));// + "°F");
    delay(15);
    myLog.print(" ," + String(tempc, 2));// + "°C");
    delay(15);
    myLog.println(" ," + String(ph1_data, 2));
    delay(15);
    
    myLog.syncFile();
    myLog.append("AquaGrove.txt");
    
}

Without SYSTEM_THREAD(ENABLED) (and not using SYSTEM_MODE(MANUAL)) a loss of connection will consume a considerable amount of time trying to reconnect potentially throwing the OneWire timing off.

When your application detects a prolonged loss of connection it should actively disconnect from WiFi and only sporadically check whether a reconnect makes sense at all before trying to reconnect (e.g. via WiFi.scan()).

Although it wouldn't impact your issue at hand, but yes :wink:

  1. you only check your internetInput pin in setup() so any connection loss after that won't be handled by it
  2. with SYSTEM_MODE(SEMI_AUTOMATIC) you will not need to disconnect as the device starts up with the WiFi module disabled anyway
  3. a Particle.disconnect() after WiFi.off() will do nothing at all as the lack of WiFi implicitly already broke the cloud connection
  4. even with the correct order first Particle.disconnect() and then WiFi.off() you wouldn't need a 2 second delay, but if your goal is to switch the module anyway a single WiFi.off() will suffice and do all the intermediate stuff implicitly
  5. you also don't need a delay(2000) between WiFi.on() and Particle.connect()

If you should happen to opt for SYSTEM_THREAD(ENABLED) you may want to consider using waitFor() / waitUntil() instead of delay().

Some extra hints:

  • have a look at Time.format() to streamline your date time logging - this will reduce your 21 lines of code into a single line.
  • do you really need a 10ms delay after each individual byte sent to your logger?
  • try to avoid String and rather go with C strings (aka char arrays)
  • have a look at sprintf() / snprintf() to create your data strings in one go

Hi @joel_e_b -

Welcome to the Particle community forum!!

I found ‘almost similar’ behaviour using this library, but in my case it ONLY reflects -127 when there is no sensor connected at all or no power to the sensor. As soon as the sensor is connected 9or has power restored), the appropriate sensor reading is displayed. You can look at my code here.

I am not sure whether this -127 is normal behaviour but if indeed the case, it might actually work to your advantage and be used to flag a non-responsive or bad sensor, so not all bad I guess :slight_smile:

You can follow the pointers @ScruffR gave you, I might actually look at a couple of them myself :smile:

Best of luck.
Regards, Friedl.

1 Like

Thanks for the Tips, ScruffR. I haven’t taken the time to optimize the library examples I cobbled to together to write this code yet since it’s not fully functional. Once I get it working, then I can focus on cleaning up the data logging portion.

I have added the following to above my setup function:

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);

And, I have now taken the switch out the equation and am just using the on-board Mode button to turn WiFi on or off using some code I found last night.

void loop()
{  
      // Button press needs to be 1 second long
    if (HAL_Core_Mode_Button_Pressed(1000)) {
        if (!Particle.connected()) {
            WiFi.on();
            Particle.connect();
            RGB.control(false);
        }
        else {
            Particle.disconnect();
            WiFi.off();
            RGB.control(true);
            RGB.color(0, 0, 0);
        }
        // delay 2.1 more seconds before resetting debounce time
        // in case user is trying to enter Listening Mode
        delay(2100);
        HAL_Core_Mode_Button_Reset(10);
    }
    //Particle.process();
}

The mode button works in turning WiFi on and off, but all of this still results in the OneWire bus not functioning when WiFi is off.

I don’t understand. I’ve done this exact thing with way worse code on a Photon before without having any issues. Did something fundamental about how WiFi is turned off change in a firmware upgrade at some point?

Did you try monitoring the connection and implement this suggestion?

I have not tried that yet. I’ve been operating under the assumption that I shouldn’t have to monitor the connection if I’m telling the Photon when to connect and disconnect. I’m not trying to make it network outage proof but rather just trying to get it to behave like an Arduino and not run all the Particle processes in the background when I’m not near any network. Do I really need to monitor the network connection if I’m brute forcing the WiFi on and off? Shouldn’t it just run setup() and loo() and not be attempting to connect to the cloud if I’ve disconnected and turned WiFi off?

You may not be telling it to disconnect but the WiFi might go away and hence your word may stand against natures facts :wink:

1 Like

lol, good point. Ok, I will add some network monitoring functionality and report back any updates. Thanks!

Thanks for the share, friedl_1977. It is normal to see -127 when the DS18B20 is not hooked up correctly or not powered. Depending on the library it normally reports -127 or NAN as an error code so that the false reading is not interpreted as a temperature value. It is very useful in a logging scenario in that you can tell the logger not to record those “bad” values if something goes wrong while collecting data.

1 Like

Ok, I've added the code found in this post:

Just added an additional check for the status of the switch.

  Particle.connect();
  if (!waitFor(Particle.connected, msRetryTime) || digitalRead(D5) == LOW)
  {
    WiFi.off();                // no luck, no need for WiFi
    RGB.control(true);
    RGB.color(0, 0, 0);
  }

It seems to have done the trick. The temp sensor is working whether or not the Photon is connected to wifi. This addition has raised a few other questions though.

  1. Without the 500ms delay in the loop, the one-wire communication fails again, no matter the wifi connection status. Why is that delay so crucial in this instance? I thought delays in the loop were generally bad practice?
  2. When re-flashing the firmware, the behavior is noticeably different with this code installed. The RGB LED solidly blinks magenta rather than the rapid blinks, and it takes a little bit longer than usual. Is this normal behavior while listening for the connection semi-automatically?
  3. Do Time functions, like Time.now(), work even if the Photon is not connected to WiFi or the cloud? It seems to be polling the time accurately when wifi is off and it's disconnected from the cloud. I always assumed the Photon received its time from the router to which it was connected. If there's no internet connection to retrieve the time from, will my time functions stop working or is that information stored internally on the Photon somewhere?

Thanks for your help! The time.format did help save a ton of cycles.

HI @joel_e_b

Just my 5 cents worth;

Have you had a look at the docs about enabling System_Thread? Or maybe this post by @rickkas7:

Particle threads tutorial

I worked with Threads once and reverted back to normal mode very quickly, haha. It seems do work well where ‘multiple threads’ are required but it does come at a price. I tried using it in a voltage/current sensor device and even though it had the desired effect, it also come with some unwanted side effects :wink: In short, for me it was not as simple as adding SYSTEM_THREAD_ENABLE, I had to rework code quite drastically and could not manage to resolve the unwanted behaviour, so reverted back :laughing:

Having said this, my C++ is shaky at best, but I have heard about some more seasoned programmers finding’s it not so easy as it seems “the way things work” changes drastically. I will leave you in the capable hands of Scruff and the rest of the forum, SYSTEM_THRRAD_ENABLE is over my head.

Hope you resolve this.
Regards Friedl.

With regards to your questoins

  1. delay() is bad in loop() but you can adopt non-blocking strategies (e.g. FSM or “soft delay”) to slow down some aspects of your code. Some sensors have a limited sampling speed and cannot be read faster than that, so you may not be able to read them on every iteration of loop().

  2. that is probably due to the fact that you are now using SYSTEM_THREAD(ENABLED) which will cause the firmware download to interleave with your still running code concurrently.

  3. the Photon has an internal RTC (Real Time Clock) that only needs to be (re)synced from time to time via the cloud connection (on each reconnect or via Particle.syncTime()). Between resyncs the RTC is keeping the time running on its own time base.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.