ESP8266 random Soft WDT resets - firebase

I use WeMos D1 mini Pro with Bosche BME680 sensor shield. I get the data from sensor and put them frequently into Firebase database. Everything works, except that my device has random crashes.
The sensor library works in a way that for around 5 first minutes it is not showing IAQ data (it's returning iaq = 25 and accuracy = 0). ESP8266 crashes around 5th minute of working program - when IAQ data is available and some of correct reading had been made.
I think problem might be caused by bsec_iot_loop() which is working too long. I was trying to use yield() in random places of bsec_iot_loop(), but it didn't work. Program works perfectly, when I comment out Firebase set methods.
Huge part of my code is based on official Bosch documentation. Frankly speaking, it's extended copy-paste. Here's the docs: https://www.bosch-sensortec.com/bst/products/all_products/bsec
Here's the code:
/**********************************************************************************************************************/
/* header files */
/**********************************************************************************************************************/
#include "bsec_integration.h"
#include <Wire.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <FirebaseArduino.h>
#include <time.h>
#define DEVICE_NAME "device1"
#define SSID "ssid"
#define PWD "pass"
#define FIREBASE_HOST "host"
#define FIREBASE_AUTH "auth"
#define UPDATE_INTERVAL 20
int startupTime;
/**********************************************************************************************************************/
/* functions */
/**********************************************************************************************************************/
/*!
* #brief Write operation in either Wire or SPI
*
* param[in] dev_addr Wire or SPI device address
* param[in] reg_addr register address
* param[in] reg_data_ptr pointer to the data to be written
* param[in] data_len number of bytes to be written
*
* #return result of the bus communication function
*/
int8_t bus_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
Wire.beginTransmission((uint8_t) 0x77);
Wire.write(reg_addr); /* Set register address to start writing to */
/* Write the data */
for (int index = 0; index < data_len; index++) {
Wire.write(reg_data_ptr[index]);
}
return (int8_t)Wire.endTransmission();
}
/*!
* #brief Read operation in either Wire or SPI
*
* param[in] dev_addr Wire or SPI device address
* param[in] reg_addr register address
* param[out] reg_data_ptr pointer to the memory to be used to store the read data
* param[in] data_len number of bytes to be read
*
* #return result of the bus communication function
*/
int8_t bus_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
int8_t comResult = 0;
Wire.beginTransmission((uint8_t) 0x77);
Wire.write(reg_addr); /* Set register address to start reading from */
comResult = Wire.endTransmission();
delayMicroseconds(150); /* Precautionary response delay */
Wire.requestFrom((uint8_t) 0x77, (uint8_t)data_len); /* Request data */
int index = 0;
while (Wire.available()) /* The slave device may send less than requested (burst read) */
{
reg_data_ptr[index] = Wire.read();
index++;
}
return comResult;
}
/*!
* #brief System specific implementation of sleep function
*
* #param[in] t_ms time in milliseconds
*
* #return none
*/
void sleep(uint32_t t_ms)
{
delay(t_ms);
}
/*!
* #brief Capture the system time in microseconds
*
* #return system_current_time current system timestamp in microseconds
*/
int64_t get_timestamp_us()
{
return (int64_t) millis() * 1000;
}
/*!
* #brief Load previous library state from non-volatile memory
*
* #param[in,out] state_buffer buffer to hold the loaded state string
* #param[in] n_buffer size of the allocated state buffer
*
* #return number of bytes copied to state_buffer
*/
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
// ...
// Load a previous library state from non-volatile memory, if available.
//
// Return zero if loading was unsuccessful or no state was available,
// otherwise return length of loaded state string.
// ...
return 0;
}
/*!
* #brief Save library state to non-volatile memory
*
* #param[in] state_buffer buffer holding the state to be stored
* #param[in] length length of the state string to be stored
*
* #return none
*/
void state_save(const uint8_t *state_buffer, uint32_t length)
{
// ...
// Save the string some form of non-volatile memory, if possible.
// ...
}
/*!
* #brief Load library config from non-volatile memory
*
* #param[in,out] config_buffer buffer to hold the loaded state string
* #param[in] n_buffer size of the allocated state buffer
*
* #return number of bytes copied to config_buffer
*/
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
// ...
// Load a library config from non-volatile memory, if available.
//
// Return zero if loading was unsuccessful or no config was available,
// otherwise return length of loaded config string.
// ...
return 0;
}
void connectToWiFi() {
Serial.print("Connecting to ");
Serial.println(SSID);
WiFi.begin(SSID, PWD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void configureFirebase() {
Serial.print("Connecting to ");
Serial.println(FIREBASE_HOST);
Serial.println("");
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
delay(500);
}
void configureTime() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.println("\nWaiting for time");
while (!time(nullptr)) {
Serial.print(".");
delay(1000);
}
Serial.println("");
}
void configureSensor() {
return_values_init ret;
/* Init I2C and serial communication */
Wire.begin();
/* Call to the function which initializes the BSEC library
* Switch on low-power mode and provide no temperature offset */
ret = bsec_iot_init(BSEC_SAMPLE_RATE_LP, 5.0f, bus_write, bus_read, sleep, state_load, config_load);
if (ret.bme680_status)
{
/* Could not intialize BME680 */
Serial.println("Error while initializing BME680");
return;
}
else if (ret.bsec_status)
{
/* Could not intialize BSEC library */
Serial.println("Error while initializing BSEC library");
return;
}
Serial.println("Sensor success");
}
/*!
* #brief Handling of the ready outputs
*
* #param[in] timestamp time in nanoseconds
* #param[in] iaq IAQ signal
* #param[in] iaq_accuracy accuracy of IAQ signal
* #param[in] temperature temperature signal
* #param[in] humidity humidity signal
* #param[in] pressure pressure signal
* #param[in] raw_temperature raw temperature signal
* #param[in] raw_humidity raw humidity signal
* #param[in] gas raw gas sensor signal
* #param[in] bsec_status value returned by the bsec_do_steps() call
*
* #return none
*/
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status)
{
yield();
char startupTimeStr[32];
itoa(startupTime, startupTimeStr, 10);
//Get current time
time_t now = time(nullptr);
//Get last update time
int lastUpdate = Firebase.getInt("device1/lastUpdate");
if (Firebase.failed()) {
Serial.print("getting device1/lastUpdate failed:");
Serial.println(Firebase.error());
return;
}
if (lastUpdate + UPDATE_INTERVAL <= (int) now) {
//Set last update
Firebase.setInt("device1/lastUpdate", (int) now);
//Set the reading
char nowStr[32];
itoa(now, nowStr, 10);
String path = "device1/readings/" + String(nowStr);
// Firebase.setInt(path + "/iaq", iaq);
// Firebase.setFloat(path + "/temp", temperature);
// Firebase.setFloat(path + "/humid", humidity);
// Firebase.setFloat(path + "/press", pressure);
//Set uptime
int uptime = (int) now - startupTime;
//Firebase.setInt("device1/uptimes/" + String(startupTimeStr), uptime);
//Verbose data
Serial.print("Updated: ");
Serial.print((int) now);
Serial.print(" | Uptime: ");
Serial.print(uptime);
Serial.print(" | IAQ: ");
Serial.print(iaq);
Serial.print(" | Acc: ");
Serial.println(iaq_accuracy);
}
}
void setup()
{
Serial.begin(9600);
while (!Serial);
connectToWiFi();
configureFirebase();
configureTime();
configureSensor();
startupTime = (int) time(nullptr);
Serial.print("Startup time:");
Serial.println(startupTime);
/* Call to endless loop function which reads and processes data based on sensor settings */
/* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes */
bsec_iot_loop(sleep, get_timestamp_us, output_ready, state_save, 10000);
}
void loop()
{
}
And here's beginning of typical Serial monitor dump:
Soft WDT reset
ctx: cont
sp: 3fff0df0 end: 40101b51 offset: 01b0
>>>stack>>>
3fff0fa0: 3fff31f4 3fff70ec 3fff662c 3fff372c
3fff0fb0: 0002d5a7 3fff70ec 3fff662c 40208866
3fff0fc0: 3fff662c 00000000 3fff703c 40201952
3fff0fd0: 3fff703c 00001388 3fff3b04 3fff0680
3fff0fe0: 000001bb 3fff662c 3fff31f4 3fff0680
3fff0ff0: 000001bb 3fff662c 3fff31f4 402089fd
3fff1000: 3ffe9770 5561c923 3ffe9770 5561c923
3fff1010: 3fff367c 00000000 3fff3684 4020717c
3fff1020: 00000000 00000206 00000206 4020526c
3fff1030: fffffff4 00000000 3fff3684 40207980
3fff1040: 3ffe9584 00000046 3ffe96a9 40201ff3
3fff1050: 3fff36fc 3fff10c0 3fff367c 4020204c
3fff1060: 3fff3708 00000000 00000000 3ffe96a6
3fff1070: 3fff367c 3fff10a0 3ffe96a6 3ffe96a6
3fff1080: 3fff367c 3fff0680 3fff1188 402030fa
3fff1090: 3fff0660 3fff0680 3fff1188 40203b71
3fff10a0: 3fff3708 3e9b1316 3e9b1316 3d003b33
3fff10b0: 41ad99fb bf64685f 00000000 40212b8c
3fff10c0: 3fff39d0 af3cd700 0000d700 00000012
3fff10d0: 00000000 3fff11ac 3fff1198 3fff065c
3fff10e0: 3fff1128 3fff1128 402030e4 40202032
3fff10f0: 3fff112c 40590000 3ed1ca3e 3fff0660
3fff1100: 3fff11ac 3fff1120 3fff1188 40203e0a
3fff1110: 3fff1120 40fb6e3e 3fff2180 40219384
3fff1120: 3fff367c 3fff3944 3fff0680 41ad99fb

I am almost sure that this is happening because your code never reaches the loop() function, on ESP8266 Arduino library it resets the watchdog timer each interaction of loop function.
I think you can solve your problem by two ways, one is opening the function bsec_iot_loop() and put the calls inside the while(1) to loop() function, the other option is to call ESP.wdtFeed() inside the while(1) to reset watchdog Timer.
The link bellow has a good explanation about watchdog timer on ESP Arduino library.
https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/

Okay, so I solved the problem. As I suspected there was a problem with watchdog timer. Specifically, the code never reach loop() function, because while(1) inside bsec_iot_loop(). Solution is simple. I put code inside while(1) into loop(). The rest of the code bsec_iot_loop() was declaration of variables and I made them global.

Not releasing system resources is a common problem in the arduino library. Especially on systems with a single core CPU like esp8266 where no native threading is available to the system. The underlying RTOS needs processing time to maintain its TCP-Stack and other stuff. Calling yield() every now and then is not a reliable solution.
The arduino standard library for the esp8266 mostly consists of blocking functions, which often leads the programmer into WDT-Pitfalls.
I always recommend people to use async libraries for the esp8266. There are plenty of them available. And always return control back to the operating system as much as possible because even simple delay()-Calls are capable of triggering WDT-Reset.

Related

MAX17205(fuel gauge) Issue with COPY NV BLOCK [E904h]

from last 2 weeks I am trying to integrate an IC MAX17205 (Fuel Gauge) on arduino mega 2560. In that I am able to read and write registers but whenever I try to copy the content of Shadow RAM to Non Volatile Memory The CommStat.NVError gets High and Without that I cant modify PackCfg reg & get the voltage for 3S cell Configuration. Please Help me in this. I have provided link to the datasheet and code below.
MAX17205 Datasheet
max17205.ino
/**
This is an example demonstrating the use of the max1720x library
Print out the information to serial monitor at 115200 baud
*/
#include <Wire.h>
#include <max1720x.h>
max1720x gauge;
void setup()
{
Serial.begin(9600); // Initializes serial port
// Waits for serial port to connect. Needed for Leonardo only
while ( !Serial ) ;
gauge.reset(); // Resets MAX1720x
delay(200); // Waits for the initial measurements to be made
}
void loop()
{
// gauge.reset();
if (gauge.getAvgCurrent() != 0) {
Serial.println(gauge.getAvgCurrent());
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_COMMAND_ADDR);
Wire.write(0x04);
Wire.write(0xE9);
// delay(500);
Wire.endTransmission();
delay(400);
}
else{
Serial.print("Capacity: ");
Serial.print(gauge.getCapacity()); // Gets the battery's state of charge
Serial.print(" mAh, TTE: ");
Serial.print(gauge.getTTE()); // Gets the battery's state of charge
Serial.print(" s, TTF: ");
Serial.print(gauge.getTTF()); // Gets the battery's state of charge
Serial.print(" s, Current: ");
Serial.print(gauge.getCurrent()); // Gets the battery's state of charge
Serial.print(" mA, Temperature: ");
Serial.print(gauge.getAvgCurrent()); // Gets the battery's state of charge
Serial.print(" mA, Temperature: ");
Serial.print(gauge.getTemperature()); // Gets the battery's state of charge
Serial.print(" degC, SOC: ");
Serial.print(gauge.getSOC()); // Gets the battery's state of charge
Serial.print("%, VCELL: ");
Serial.print(gauge.getVoltage()); // Gets the battery voltage
Serial.println('mV');
delay(2000);
}
}
MAX17205.c
/**
* Name: max1720x
* Author: Luka Mustafa - Institute IRNAS Race { info#irnas.eu }
* Version: 1.0
* Description: A library for interfacing the MAXIM MAX17201/MAX17205
* Li+ fuel gauges.
* Source: https://github.com/pAIgn10/max1720x
* License: Copyright (c) 2017 Nick Lamprianidis
* This library is licensed under the GPL license
* http://www.opensource.org/licenses/mit-license.php
* Inspiration: The library is inspired by: https://github.com/pAIgn10/max1720x
* Filename: max1720x.cpp
* File description: Definitions and methods for the max1720x library
*/
#include "max1720x.h"
// Initializes variables and the Wire library
max1720x::max1720x() {
Wire.begin();
}
// Returns a measurement of the voltage of the connected LiIon Polymer battery
double max1720x::getVoltage()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_VBAT_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2,HIGH); //send stop
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double voltage = combined; //combine registers
return voltage*0.078125; // //calculate actual value and return in mV
}
double max1720x::getCurrent()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_CURENT_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2,HIGH); //send stop
int16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double current = (double)combined*0.0015625/0.01;
return current;//calculate actual value as 0.0015625 mV/Ohm
}
double max1720x::getAvgCurrent()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(0x61);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2,HIGH); //send stop
int16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double current = (double)combined*0.0015625/0.01;
return combined;//calculate actual value as 0.0015625 mV/Ohm
}
double max1720x::getTemperature()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_TEMP_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2,HIGH); //send stop
int16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double temperature = (double)combined/256;
return temperature;
}
// Returns the relative state of charge of the connected LiIon Polymer battery
// as a percentage of the full capacity w/ resolution 1/256%
double max1720x::getSOC()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_REPSOC_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2);
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double soc = combined; //combine registers
return soc/256; //calculate actual value and return in %
}
// RepCap or reported capacity is a filtered version of the AvCap register that prevents large jumps in the reported value caused by changes in the application such as abrupt changes in temperature or load current.
double max1720x::getCapacity()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_REPCAP_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2);
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double capacity = (double)combined*0.005/0.01;
return capacity;//calculate actual value as 0.005 mVh/Ohm
}
// The TTE register holds the estimated time to empty for the application under present temperature and load conditions
double max1720x::getTTE()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_TTE_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2);
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double capacity = (double)combined*5.625;
return capacity;//calculate actual value as value*5.625s
}
// The TTF register holds the estimated time to full for the application under present conditions.
double max1720x::getTTF()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_TTF_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2);
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
double capacity = (double)combined*5.625;
return capacity;//calculate actual value as value*5.625s
}
// Status Register (000h) The Status register maintains all flags related to alert thresholds and battery insertion or removal.
uint16_t max1720x::getStatus()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_STATUS_ADDR);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720X_ADDR, (int)2);
uint16_t combined = Wire.read()|(Wire.read()<<8); // LSB or-ed with MSB
return combined;
}
// Reset procedure
uint8_t max1720x::reset()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_COMMAND_ADDR);
Wire.write(0x0f);
Wire.write(0x00);
Wire.endTransmission();
delay(50);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_CONFIG2_ADDR);
Wire.write(0x01);
Wire.write(0x00);
delay(50);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_nPackcfg);
Wire.write(0x03);
Wire.write(0x0A);
Wire.endTransmission();
delay(50);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(0x61);
Wire.write(0x00);
Wire.write(0x00);
Wire.endTransmission();
delay(50);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_COMMAND_ADDR);
Wire.write(0xE0);
Wire.write(0x01);
Wire.endTransmission();
delay(400);
return;
}
MAX17205.h
/**
* Name: max1720x
* Author: Luka Mustafa - Institute IRNAS Race { info#irnas.eu }
* Version: 1.0
* Description: A library for interfacing the MAXIM MAX17201/MAX17205
* Li+ fuel gauges.
* Source: https://github.com/pAIgn10/LiFuelGauge
* License: Copyright (c) 2017 Nick Lamprianidis
* This library is licensed under the GPL license
* http://www.opensource.org/licenses/mit-license.php
* Inspiration: The library is inspired by: https://github.com/pAIgn10/LiFuelGauge
* Filename: max1720x.h
* File description: Definitions and methods for the max1720x library
*/
#ifndef max1720x
#define max1720x_h
#include <Arduino.h>
#include <Wire.h>
// MAX1720X register addresses
const int MAX1720X_ADDR = 0x36;
const int MAX1720X_STATUS_ADDR = 0x00; // Contains alert status and chip status
const int MAX1720X_VCELL_ADDR = 0x09; // Lowest cell voltage of a pack, or the cell voltage for a single cell
const int MAX1720X_REPSOC_ADDR = 0x06; // Reported state of charge
const int MAX1720X_REPCAP_ADDR = 0x05; // Reported remaining capacity
const int MAX1720X_TEMP_ADDR = 0x08; // Temperature
const int MAX1720X_CURENT_ADDR = 0x0A; // Battery current
const int MAX1720X_AVG_CURENT_ADDR = 0x29; // Battery current
const int MAX1720X_TTE_ADDR = 0x11; // Time to empty
const int MAX1720X_TTF_ADDR = 0x20; // Time to full
const int MAX1720X_CAPACITY_ADDR = 0x10; // Full capacity estimation
const int MAX1720X_VBAT_ADDR = 0xDA; // Battery pack voltage
const int MAX1720X_AVCELL_ADDR = 0x17; // Battery cycles
const int MAX1720X_COMMAND_ADDR = 0x60; // Command register
const int MAX1720X_CONFIG2_ADDR = 0xbb; // Command register
const int MAX1720X_nPackcfg = 0xBD;
const int MAX1720X_WriteSlave = 0x6C;
// Class for interfacing the MAX1720X Li+ fuel gauges
class max1720x
{
public:
max1720x();
double getVoltage();
double getSOC();
double getTemperature();
double getCurrent();
double getAvgCurrent();
double getCapacity();
double getTTE();
double getTTF();
uint8_t reset();
private:
uint16_t getStatus();
};
#endif // max1720x
In case it may help someone (i got here via google search):
I've been struggling with that procedure too, but got it working now.
This is my first I2C experience ever, so i think there's a lot of space for improving my code.
For me it's working like that:
const int MAX1720x_nV = 0x0b; // shifted 0x16
int a = 0; // to repeat NVCopy in case of failure
// Full Reset procedure
uint8_t max1720x::reset()
{
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_COMMAND_ADDR); // Hardware reset
Wire.write(0x0f);
Wire.write(0x00);
Wire.endTransmission(true);
delay(50);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_CONFIG2_ADDR); // Fuel Gauge reset
Wire.write(0x01);
Wire.write(0x00);
return Wire.endTransmission(true); // delay(at least 10) after this function
}
// copy the shadow RAM to the nonvolatile Block
void max1720x::copyNV()
{
Wire.beginTransmission(MAX1720x_nV);
Wire.write(0x8e); // nODSCTh
Wire.write(0x00); // 16 bit Value to write there
Wire.write(0x00); // continued
Wire.endTransmission(false);
Wire.beginTransmission(MAX1720x_nV);
Wire.write(0x8f); // nODSCCfg
Wire.write(0x00); // 16 bit Value to write there
Wire.write(0x00); // continued
Wire.endTransmission(false);
.
. // add more configuarations as desired
.
// start NVCopy procedure
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(0x61); // clear commstat register
Wire.write(0x00);
Wire.write(0x00);
Wire.endTransmission(true);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(0x60); // write E904h to 60h as described in datasheet
Wire.write(0x04);
Wire.write(0xe9);
Wire.endTransmission(true);
delay(1000); // can probably be shorter
Wire.beginTransmission(MAX1720X_ADDR); // read the CommStat.NVError
Wire.write(0x61);
Wire.endTransmission(false);
Wire.requestFrom(MAX1720x_nV, (int)2, true);
uint8_t combined = Wire.read()|(Wire.read()<<8);
Wire.endTransmission(true);
Serial.println(combined); // not neccessary, just for me
Serial.println(a);
if ((combined != 0) && (a == 1)) {
Serial.println(a);
Serial.println("failed more than once");
return;
}
if ((combined != 0) && (a == 0)) {
Serial.println("failed once");
a++;
max1720x::copyNV();
}
else if (combined == 0) { // if NVError is empty, finish the routine
Serial.println("CopyNV success!");
delay(500);
max1720x::reset(); // initiate a full reset
delay(150);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(0xba); // probably not neccessary
Wire.write(0x00); // something with hibernate mode?
Wire.write(0x00);
Wire.endTransmission(true);
Wire.beginTransmission(MAX1720X_ADDR);
Wire.write(MAX1720X_COMMAND_ADDR);
Wire.write(0x80); // probably not neccessary
Wire.write(0x00); // something with hibernate mode?
Wire.endTransmission(true);
max1720x::reset(); // initiate a full reset, probably not neccessary
delay(500);
return;
}
}

Arduino not registering SPI transfer from STM32F407

EDIT/SOLVED: The issue ended up being my Serial.print statements in the Arduino code which were slowing down to execution of the actual communication code. Basically, the Arduino was receiving bytes after the STM board had finished sending them, so it was only registering 'd'/100.
I'm currently enrolled in an online course and facing an issue with one of the exercises. The exercise seems pretty straightforward, but I can't seem to get everything to work. The exercise is to send a string from a master MCU STM32F407 to an Arduino Uno board, which is then supposed to print the message and it's length.
The Arduino board seemed to register the SS, but would not print anything, print an incomplete/incorrect message (e.g. "hello vorl"), or print gibberish. After some debugging Idiscovered I had incorrectly set my CPOL and CPHA registers, however, now it won't print anything close to "Hello world." After more debugging, it seems like the Arduino is getting stuck in it's SPI_SlaveReceive function. The SPI code for the STM is designed to first send the length of the message and then send the actual message. From what I can tell, the Arduino doesn't seem to be registering the correct length for the first transmission, which is messing up the loop for the SPI_SlaveReceive calls that are supposed to grab the actual message.
I am new to embedded and have been trying to learn it mostly on my own. I feel like there is probably something basic that I am missing, but don't know about. I also feel as if the code is poorly designed, but I am trying to stick to the course (the STM code is "written" following guidelines and the arduino code was just provided). I will post the code below, but would even just appreciate ideas about what may be going wrong/how I can debug. Below everything I will also post some error logging.
This is also my first stackoverflow post, so please let me know if there is anything I need to add (or remove). Thanks!
Update:
I think it might be something with the clock speed. I tried running through the code again in a debugger and found that I was successfully able to send the data if I put a break point on the code that modifies the Data Register of the SPI. It seems like the SPI might be sending data more quickly than the Arduino can process. If I step through the code that sends the length of the message "Hello world" then I can see that the Arduino successfully received 11. If I continue to step through the pSPIx->DR = *pTxBuffer; instruction of the second call to the SPI_SendData, I can successfully send the message. However, if after sending the length, I try to step through the entirety of the second call to SPI_SendData in one step. The arduino doesn't register the entire string. I tried modifying the SPI pre-scalar to be greater values and this seemed to help. i.e. The arduino was able to output more valid characters (resulting in "hellod"). This confuses me however as the APB1 that controls SPI2 has a maximum freq of 42MHz. The pre-scalar value that I found gave me the best results was 7, which means the PCLK is divided by 256 resulting in, if I'm thinking about this correctly, a frequency of 164 MHz at most. Based on the specs of the Ardunio, it should have no problem processing this communication. I'm thinking I might have a problem is how often the bits are sent? Maybe my SPI_SendData function isn't working correctly?
Other details:
4 wires connecting the boards: GND, CLK (STM: PB13 <-> UNO: 13), MOSI (STM: PB15 <-> A: 11), and NSS (STM: PB12 <-> A:10)
I don't have a logic analyzer, so I haven't been able to see what's actually being sent (which sucks)
I realize it may be hard to get the full context without posting all of the code, but I figured that would be too much, so tried to just post the relevant parts.
Logging:
16:01:42.461 -> Slave Initialized
16:01:42.494 -> Slave waiting for ss to go low
16:01:45.500 -> slave selected
16:01:45.500 -> Waiting to receive length
16:01:45.534 -> Length: 68
16:01:47.520 -> Received:
16:01:47.554 -> Received: d
16:01:48.968 -> Received:
16:01:48.968 -> Received: d
16:01:49.858 -> Received:
16:01:49.858 -> Received: d
16:01:50.499 -> Received:
16:01:50.499 -> Received: d
16:01:51.137 -> Received:
16:01:51.137 -> Received: d
16:01:51.707 -> Received:
16:01:51.741 -> Received: d
16:01:52.244 -> Received:
16:01:52.277 -> Received: d
16:01:52.782 -> Received:
16:01:52.782 -> Received: d
16:01:53.360 -> Received:
16:01:53.393 -> Received: d
16:01:53.866 -> Received:
16:01:53.899 -> Received: d
16:01:54.441 -> Received:
16:01:54.441 -> Received: d
16:01:54.946 -> Received:
16:01:54.980 -> Received: d
16:01:55.484 -> Received:
16:01:55.518 -> Received: d
16:01:55.994 -> Received:
16:01:56.027 -> Received: d
16:01:56.599 -> Received:
16:01:56.599 -> Received: d
16:01:57.105 -> Received:
16:01:57.138 -> Received: `
16:01:57.609 -> Received:
16:01:57.644 -> Received: d
16:01:58.085 -> Received:
16:01:58.118 -> Received: d
16:01:58.622 -> Received:
16:01:58.622 -> Received: d
16:01:59.128 -> Received:
16:01:59.161 -> Received: d
16:01:59.676 -> Received:
16:01:59.676 -> Received: d
16:02:00.146 -> Received:
16:02:00.180 -> Received: d
16:02:00.654 -> Received:
16:02:00.654 -> Received: d
16:02:01.127 -> Received:
16:02:01.127 -> Received: d
16:02:01.634 -> Received:
16:02:01.668 -> Received: d
16:02:02.105 -> Received:
16:02:02.139 -> Received: d
16:02:02.582 -> Received:
16:02:02.582 -> Received: d
16:02:03.052 -> Received:
16:02:03.052 -> Received: d
16:02:03.729 -> Received:
16:02:03.729 -> Received: d
16:02:04.236 -> Received:
16:02:04.236 -> Received: d
16:02:04.774 -> Received:
16:02:04.774 -> Received: d
16:02:05.336 -> Received:
16:02:05.336 -> Received: d
16:02:05.947 -> Received:
16:02:05.980 -> Received: d
16:02:06.485 -> Received:
16:02:06.485 -> Received: d
16:02:06.518 -> done receiving word
16:02:06.518 -> Rcvd:
16:02:06.553 -> ddddddddddddddd`dddddddddddddddddd
16:02:06.620 -> Length:68
16:02:06.620 -> Slave deselected
16:02:06.654 -> Slave waiting for ss to go low
16:02:07.025 -> slave selected
16:02:07.058 -> Waiting to receive length
16:02:07.092 -> Length: 100
Arduino code:
/* SPI Slave Demo
*
* SPI pin numbers:
* SCK 13 // Serial Clock.
* MISO 12 // Master In Slave Out.
* MOSI 11 // Master Out Slave In.
* SS 10 // Slave Select . Arduino SPI pins respond only if SS pulled low by the master
*
*/
#include <SPI.h>
#include<stdint.h>
#define SPI_SCK 13
#define SPI_MISO 12
#define SPI_MOSI 11
#define SPI_SS 10
char dataBuff[500];
//Initialize SPI slave.
void SPI_SlaveInit(void)
{
#if 0
// Initialize SPI pins.
pinMode(SPI_SCK, INPUT);
pinMode(SPI_MOSI, INPUT);
pinMode(SPI_MISO, OUTPUT);
pinMode(SPI_SS, INPUT);
// Enable SPI as slave.
SPCR = (1 << SPE);
#endif
// Initialize SPI pins.
pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SS, INPUT);
//make SPI as slave
// Enable SPI as slave.
SPCR = (1 << SPE);
}
//This function returns SPDR Contents
uint8_t SPI_SlaveReceive(void)
{
/* Wait for reception complete */
while(!(SPSR & (1<<SPIF)));
/* Return Data Register */
return SPDR;
}
//sends one byte of data
void SPI_SlaveTransmit(char data)
{
/* Start transmission */
SPDR = data;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
}
// The setup() function runs right after reset.
void setup()
{
// Initialize serial communication
Serial.begin(9600);
// Initialize SPI Slave.
SPI_SlaveInit();
Serial.println("Slave Initialized");
}
uint16_t dataLen = 0;
uint32_t i = 0;
// The loop function runs continuously after setup().
void loop()
{
Serial.println("Slave waiting for ss to go low");
while(digitalRead(SS) );
Serial.println("slave selected");
// Serial.println("start");
//1. read the length
// dataLen = (uint16_t)( SPI_SlaveReceive() | (SPI_SlaveReceive() << 8) );
//Serial.println(String(dataLen,HEX));
i = 0;
Serial. println("Waiting to receive length");
dataLen = SPI_SlaveReceive();
for(i = 0 ; i < dataLen ; i++ )
{
dataBuff[i] = SPI_SlaveReceive();
Serial.print("Received: ");
Serial.println(dataBuff[i]);
}
Serial.println("done receiving word");
// Serial.println(String(i,HEX));
dataBuff[i] = '\0';
Serial.println("Rcvd:");
Serial.println(dataBuff);
Serial.print("Length:");
Serial.println(dataLen);
while(!digitalRead(SS));
Serial.println("Slave deselected");
}
Main STM code:
#include <string.h>
#include "stm32f407xx.h"
/*
* PB15 --> SPI2_MOSI
* PB14 --> SPI2_MISO
* PB13 --> SPI2_SCLK
* PB12 --> SPI2_NSS
* ALT Function mode: 5
*/
void SPI_GPIO_Setup(void);
void GPIO_ButtonInit(void);
void SPI_Setup(void);
void delay(int time){
for(uint32_t i=0; i<time; i++);
}
int main(void) {
char userData[] = "Hello world";
//initizalize button
GPIO_ButtonInit();
//This function is used to initialize the gpio pins to behave as spi pins
SPI_GPIO_Setup();
//This function is used to initialize the SPI peripheral
SPI_Setup();
//this makes NSS signal internally high and avoids MODF error
//NOTE: This should be taken care of in my SPI_Init function
// SPI_SSIConfig(SPI2, ENABLE);
//SSOE to 1 does NSS output enable
//I don't want to enable this in this way, but it is more simple due to the code design
//i.e. (using shifts instead of masks for registers)
SPI_SSOEConfig(SPI2, ENABLE);
while(1){
//wait for button to be pressed
while(! GPIO_ReadFromInputPin(GPIOA, GPIO_PIN_0));
delay(250000); //used for button debouncing
//enable the SPI2 peripheral --- according to generated code, this seems to be done in the transmit function
// I a SET_BIT macro in the transmit function if SPI isn't enabled, make sure to test this
SPI_PeripheralControl(SPI2, ENABLE);
//first send the length of data (Arduino expects this as 1 byte)
uint8_t dataLen = strlen(userData);
SPI_SendData(SPI2, &dataLen, 1);
//send data
SPI_SendData(SPI2, (uint8_t*)userData, dataLen);
//confirm the SPI is not busy
while(SPI_GetFlagStatus(SPI2, SPI_BSY_FLAG));
//disable the SPI2 peripheral --- according to Generated code, this seems to be done in abort functions
SPI_PeripheralControl(SPI2, DISABLE);
}
return 0;
}
void SPI_GPIO_Setup(void) {
GPIO_Handle_t SPIpins;
SPIpins.pGPIOx = GPIOB;
SPIpins.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_ALTFN;
SPIpins.GPIO_PinConfig.GPIO_PinAltFunMode = 5;
SPIpins.GPIO_PinConfig.GPIO_PinOPType = GPIO_OPTYPE_PP;
SPIpins.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PU;
SPIpins.GPIO_PinConfig.GPIO_PinSpeed = GPIO_SPEED_FAST;
//MOSI
SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_15;
GPIO_Init(&SPIpins);
//MISO
// SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_14;
// GPIO_Init(&SPIpins);
//SCLK
SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_13;
GPIO_Init(&SPIpins);
//NSS
SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_12;
GPIO_Init(&SPIpins);
}
void GPIO_ButtonInit(void){
GPIO_Handle_t GpioBtn;
// configure button
//Set up PA0 to be an input for the button press
GpioBtn.pGPIOx = GPIOA;
GpioBtn.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_0;
GpioBtn.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_IN;
GpioBtn.GPIO_PinConfig.GPIO_PinSpeed = GPIO_SPEED_FAST;
GpioBtn.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_NO_PUPD;
GPIO_Init(&GpioBtn);
}
void SPI_Setup(void){
SPI_Handle_t hSPI;
hSPI.SPIx = SPI2;
hSPI.SPIConfig.SPI_BusConfig = SPI_BUS_CONFIG_FD;
hSPI.SPIConfig.SPI_DeviceMode = SPI_DEVICE_MODE_MASTER;
hSPI.SPIConfig.SPI_SclkSpeed = SPI_SCLK_SPEED_DIV8; //generate SCLK of 2 MHz
hSPI.SPIConfig.SPI_DFF = SPI_DFF_8BITS;
hSPI.SPIConfig.SPI_CPOL = SPI_CPOL_LOW;
hSPI.SPIConfig.SPI_CPHA = SPI_CPHA_LOW;
hSPI.SPIConfig.SPI_SSM = SPI_SSM_DI; //Hardware slave management disabled for NSS pin
SPI_Init(&hSPI);
}
SPI Driver code:
/*
* stm32f407xx_spi_driver.c
*
* Created on: Oct 17, 2019
* Author: pigmo
*/
#include "stm32f407xx_spi_driver.h"
/**************************************************************************
* APIs supported by this driver
* For more information about these APIs check the function description
**************************************************************************/
/*
* Peripheral clock setup
*/
/******************************************************
* #fn - SPI_PeriClockControl
*
* #brief - Enables or disables peripheral clock for the given SPI port
*
* #param[in] - base address of SPI peripheral
* #param[in] - ENABLE or DISABLE macros
*
* #return - none
*
* #Note - SPI4 is reserved and won't be affected by this function
*/
void SPI_PeriClockControl(SPI_RegDef_t* pSPIx, uint8_t enOrDi){
if(enOrDi == ENABLE){
if(pSPIx == SPI1){
SPI1_PCLK_EN();
} else if (pSPIx == SPI2){
SPI2_PCLK_EN();
} else if (pSPIx == SPI3){
SPI3_PCLK_EN();
}
} else {
if(pSPIx == SPI1){
SPI1_PCLK_DI();
} else if (pSPIx == SPI2){
SPI2_PCLK_DI();
} else if (pSPIx == SPI3){
SPI3_PCLK_DI();
}
}
}
/*
* Init and De-init
*/
/******************************************************
* #fn - SPI_Init
*
* #brief - Initializes a given GPIO peripheral with given configurations
*
* #param[in] - Struct holding base address and desired configurations of SPI peripheral
*
* #return - none
*
* #Note - I don't like the way this is done, but I'm doing it to be consistent with the course
* Instead, I think the SPIConfig should hold masks, not enum type values
*/
void SPI_Init(SPI_Handle_t *pSPIHandle){
//enable the spi clock
SPI_PeriClockControl(pSPIHandle->SPIx, ENABLE);
//disable the SPI (concept taken from STM generated code)
SPI_PeripheralControl(pSPIHandle->SPIx, DISABLE);
//first configure the SPI CR1 register
uint32_t tempReg = 0;
//1. configure the device mode
if(pSPIHandle->SPIConfig.SPI_DeviceMode == SPI_DEVICE_MODE_MASTER){
// if the device is master mode, then the SSI must also be 1 to avoid an error (unless Multi master?)
// this is only true if SSM is enabled, can we keep this functionality even is SSM is disabled?
// based on stm generated code, the SSI stays enabled for SSM enabled and disabled
tempReg |= pSPIHandle->SPIConfig.SPI_DeviceMode << SPI_CR1_MSTR;
tempReg |= (1 << SPI_CR1_SSI); // this should probably be done in another way
} else {
tempReg |= pSPIHandle->SPIConfig.SPI_DeviceMode << SPI_CR1_MSTR;
}
//2. configure the bus config
if(pSPIHandle->SPIConfig.SPI_BusConfig == SPI_BUS_CONFIG_FD){
//bdi should be cleared
tempReg &= ~(1 << SPI_CR1_BIDIMODE);
} else if(pSPIHandle->SPIConfig.SPI_BusConfig == SPI_BUS_CONFIG_HD){
//bdi should be set
tempReg |= ~(1 << SPI_CR1_BIDIMODE);
} else {
//bdi should be cleared
tempReg &= ~(1 << SPI_CR1_BIDIMODE);
//RXONLY should be set
tempReg |= (1 << SPI_CR1_RXONLY);
}
//3. configure the clock speed
tempReg |= (pSPIHandle->SPIConfig.SPI_SclkSpeed << SPI_CR1_BR);
//4. configure the Data frame format
tempReg |= (pSPIHandle->SPIConfig.SPI_DFF << SPI_CR1_DFF);
//5. configure the clock polarity
tempReg |= (pSPIHandle->SPIConfig.SPI_CPOL << SPI_CR1_CPOL);
//6. configure the clock phase
tempReg |= (pSPIHandle->SPIConfig.SPI_CPHA << SPI_CR1_CPHA);
//7. determine hardware or software slave management
tempReg |= (pSPIHandle->SPIConfig.SPI_SSM << SPI_CR1_SSM);
pSPIHandle->SPIx->CR1 = tempReg;
}
/******************************************************
* #fn - SPI_DeInit
*
* #brief - Deinitializes a given SPI periphal and resets register
*
* #param[in] - base address of SPI peripheral
*
* #return - none
*
* #Note - none
*/
void SPI_DeInit(SPI_RegDef_t* pSPIx){
if(pSPIx == SPI1){
SPI1_REG_RESET();
} else if (pSPIx == SPI2){
SPI2_REG_RESET();
} else if (pSPIx == SPI3){
SPI3_REG_RESET();
}
}
/*
* Data send and receive
*/
uint8_t SPI_GetFlagStatus(SPI_RegDef_t* pSPIx, uint32_t flag){
if(pSPIx->SR & flag){
return SET;
} else {
return RESET;
}
}
/******************************************************
* #fn - SPI_SendData
*
* #brief - sends data of length len to transmit buffer
*
* #param[in] - base address of SPI peripheral
* #param[in] - address of Tx buffer
* #param[in] - length of data to send
*
* #return - none
*
* #Note - This is a blocking call
*/
//actual code seems much more complex, check that out
void SPI_SendData(SPI_RegDef_t* pSPIx, uint8_t *pTxBuffer, uint32_t len){
//if SPi is not enabled, then enable it
if((pSPIx->CR1 & (1 << SPI_CR1_SPE)) != (1 << SPI_CR1_SPE)){
SET_BIT(pSPIx->CR1, (1 << SPI_CR1_SPE)); // this is repetitive, should just have a mask,
}
while(len > 0) {
//1. Wait until Tx is empty
while(SPI_GetFlagStatus(pSPIx, SPI_TXE_FLAG) == RESET);
//2. Check DFF in CR1
if (pSPIx->CR1 & (1 << SPI_CR1_DFF) ) {
//16 bit dff
//3. load data into DR
pSPIx->DR = *((uint16_t*)pTxBuffer);
len--;
len--;
(uint16_t*)pTxBuffer++;
} else {
//8 bit dff
//3. load data into DR
pSPIx->DR = *pTxBuffer;
len--;
pTxBuffer++;
}
}
}
/******************************************************
* #fn - SPI_ReceiveData
*
* #brief - Receives data of length len to Receive buffer
*
* #param[in] - base address of SPI peripheral
* #param[in] - address of Rx buffer
* #param[in] - length of data to Receive
*
* #return - none
*
* #Note - none
*/
void SPI_ReceiveData(SPI_RegDef_t* pSPIx, uint8_t *pRxBuffer, uint32_t len){
}
/*
* IRQ configuration and handling
*/
/******************************************************
* #fn - SPI_IRQITConfig
*
* #brief - enables or disables a SPI peripherals IRQ functionality
*
* #param[in] - IRQ number of SPI peripheral
* #param[in] - ENABLE or DISABLE macros
*
* #return - none
*
* #Note - none
*/
void SPI_IRQInterruptConfig(uint8_t IRQNumber, uint8_t enOrDi){
}
/******************************************************
* #fn - SPI_IRQPriorityConfig
*
* #brief - sets the priority for a given IRQ number
*
* #param[in] - IRQ priority of SPI peripheral
* #param[in] - IRQ number of SPI peripheral
*
* #return - none
*
* #Note - modifies the NVIC peripheral
*/
void SPI_IRQPriorityConfig(uint8_t IRQPriority, uint8_t IRQNumber){
}
/******************************************************
* #fn - SPI_IRQHandling
*
* #brief - deals with a interrupt triggered by a SPI pin
*
* #param[in] - Struct holding base address and desired configurations of SPI peripheral
*
* #return - none
*
* #Note - none
*/
void SPI_IRQHandling(SPI_Handle_t *pHandle){
}
/******************************************************
* #fn - SPI_PeripheralControl
*
* #brief - Enables or Disables SPI
*
* #param[in] - Pointer to a SPI register
*
* #return - none
*
* #Note - I'm implementing this in the transmit function
*/
void SPI_PeripheralControl(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
if(enOrDi == ENABLE){
pSPIx->CR1 |= (1 << SPI_CR1_SPE);
} else {
pSPIx->CR1 &= ~(1 << SPI_CR1_SPE);
}
}
/******************************************************
* #fn - SPI_SSIConfig
*
* #brief - Sets or Resets SSI bit in SPI CR1 reg
*
* #param[in] - Pointer to a SPI register
*
* #return - none
*
* #Note - This code is used in the course I'm following,
* but I think it is redundant and bad practice. It will not be used.
* Instead, I implemented a check in the SPI init function to enable SSI w/ master
*/
void SPI_SSIConfig(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
if(enOrDi == ENABLE){
pSPIx->CR1 |= (1 << SPI_CR1_SSI);
} else {
pSPIx->CR1 &= ~(1 << SPI_CR1_SSI);
}
}
/******************************************************
* #fn - SPI_SSOEConfig
*
* #brief - Sets or Resets SSOE bit in SPI CR2 reg
*
* #param[in] - Pointer to a SPI register
*
* #return - none
*
* #Note - This code is used in the course I'm following,
* but I think it is redundant and bad practice. I will use it bec of consistency
* Otherwise, in the STM generated code, this bit is determine by a NSS value in the init struct
* See: WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode));
*/
void SPI_SSOEConfig(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
if(enOrDi == ENABLE){
pSPIx->CR2 |= (1 << SPI_CR2_SSOE);
} else {
pSPIx->CR2 &= ~(1 << SPI_CR2_SSOE);
}
}

Multitech mDot ARMmBed platform i2c communication with Arduino

I am not able to retrieve data from i2c bus from an Arduino to a mDot LoRa node. Based on the ARMBed i2c library tried to get data from the Arduino (which is the slave) using this code:
/**
* Copyright (c) 2017, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include "lorawan/LoRaWANInterface.h"
#include "lorawan/system/lorawan_data_structures.h"
#include "events/EventQueue.h"
// Application helpers
#include "DummySensor.h"
#include "trace_helper.h"
#include "lora_radio_helper.h"
using namespace events;
// documentacion de i2c
// https://os.mbed.com/docs/mbed-os/v5.12/apis/i2c.html
#include "mbed.h"
mbed::I2C i2c(I2C_SDA, I2C_SCL);
const int addr7bit = 0x29; // 7-bit I2C address (del ARDUINO)
const int addr8bit = addr7bit << 1; // 8-bit I2C address (shift a izquierda para el MDOT)
// Max payload size can be LORAMAC_PHY_MAXPAYLOAD.
// This example only communicates with much shorter messages (<30 bytes).
// If longer messages are used, these buffers must be changed accordingly.
uint8_t tx_buffer[30];
uint8_t rx_buffer[30];
/*
* Sets up an application dependent transmission timer in ms. Used only when Duty Cycling is off for testing
*/
#define TX_TIMER 10000
/**
* Maximum number of events for the event queue.
* 10 is the safe number for the stack events, however, if application
* also uses the queue for whatever purposes, this number should be increased.
*/
#define MAX_NUMBER_OF_EVENTS 10
/**
* Maximum number of retries for CONFIRMED messages before giving up
*/
#define CONFIRMED_MSG_RETRY_COUNTER 3
/**
* Dummy pin for dummy sensor
*/
#define PC_9 0
/**
* Dummy sensor class object
*/
DS1820 ds1820(PC_9);
/**
* This event queue is the global event queue for both the
* application and stack. To conserve memory, the stack is designed to run
* in the same thread as the application and the application is responsible for
* providing an event queue to the stack that will be used for ISR deferment as
* well as application information event queuing.
*/
static EventQueue ev_queue(MAX_NUMBER_OF_EVENTS *EVENTS_EVENT_SIZE);
/**
* Event handler.
*
* This will be passed to the LoRaWAN stack to queue events for the
* application which in turn drive the application.
*/
static void lora_event_handler(lorawan_event_t event);
/**
* Constructing Mbed LoRaWANInterface and passing it the radio object from lora_radio_helper.
*/
static LoRaWANInterface lorawan(radio);
/**
* Application specific callbacks
*/
static lorawan_app_callbacks_t callbacks;
/**
* Entry point for application
*/
int main(void)
{
// setup tracing
setup_trace();
// stores the status of a call to LoRaWAN protocol
lorawan_status_t retcode;
// Initialize LoRaWAN stack
if (lorawan.initialize(&ev_queue) != LORAWAN_STATUS_OK) {
printf("\r\n LoRa initialization failed! \r\n");
return -1;
}
printf("\r\n Mbed LoRaWANStack initialized \r\n");
// prepare application callbacks
callbacks.events = mbed::callback(lora_event_handler);
lorawan.add_app_callbacks(&callbacks);
// Set number of retries in case of CONFIRMED messages
if (lorawan.set_confirmed_msg_retries(CONFIRMED_MSG_RETRY_COUNTER)
!= LORAWAN_STATUS_OK) {
printf("\r\n set_confirmed_msg_retries failed! \r\n\r\n");
return -1;
}
printf("\r\n CONFIRMED message retries : %d \r\n",
CONFIRMED_MSG_RETRY_COUNTER);
// Enable adaptive data rate
if (lorawan.enable_adaptive_datarate() != LORAWAN_STATUS_OK) {
printf("\r\n enable_adaptive_datarate failed! \r\n");
return -1;
}
printf("\r\n Adaptive data rate (ADR) - Enabled \r\n");
retcode = lorawan.connect();
if (retcode == LORAWAN_STATUS_OK ||
retcode == LORAWAN_STATUS_CONNECT_IN_PROGRESS) {
} else {
printf("\r\n Connection error, code = %d \r\n", retcode);
return -1;
}
printf("\r\n Connection - In Progress ...\r\n");
// make your event queue dispatching events forever
ev_queue.dispatch_forever();
return 0;
}
/**
* Sends a message to the Network Server
*/
static void send_message()
{
uint16_t packet_len;
int16_t retcode;
float sensor_value;
// esta sería la variable que guardará el "paquete" leido del ARDUINO
char cmd[28];
// inicializamos en 0 cada valor del arreglo
for (size_t i = 0; i < 28; i++)
{
cmd[i] = 0x00;
}
if (ds1820.begin()) {
ds1820.startConversion();
sensor_value = ds1820.read();
printf("\r\n Esto es console log = %3.1f \r\n", sensor_value);
ds1820.startConversion();
packet_len = sprintf((char *) tx_buffer, "Esto es lo que se manda: %3.1f", sensor_value); //28 bytes
} else {
// printf("\r\n No sensor found \r\n");
// return;
// basicamente tengo que mandar cualquier cosa para que el maestro me conteste con los datos
// de los sensores.
i2c.write(addr8bit, cmd, 28);
printf("%d\n", addr7bit);
printf("%d\n", addr8bit);
wait(5); // 5 segundos
// y ahora leo los 28 bytes recibidos, el paquete enviado desde arduino deberia ser de 28 bytes
i2c.read(addr8bit, cmd, 28);
printf("%s\r\n", cmd);
packet_len = sprintf((char *) tx_buffer, "%s", cmd);
}
retcode = lorawan.send(MBED_CONF_LORA_APP_PORT, tx_buffer, packet_len, MSG_UNCONFIRMED_FLAG);
if (retcode < 0) {
retcode == LORAWAN_STATUS_WOULD_BLOCK ? printf("send - WOULD BLOCK\r\n")
: printf("\r\n send() - Error code %d \r\n", retcode);
if (retcode == LORAWAN_STATUS_WOULD_BLOCK) {
//retry in 3 seconds
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
ev_queue.call_in(3000, send_message);
}
}
return;
}
printf("\r\n %d bytes scheduled for transmission \r\n", retcode);
memset(tx_buffer, 0, sizeof(tx_buffer));
}
/**
* Receive a message from the Network Server
*/
static void receive_message()
{
uint8_t port;
int flags;
int16_t retcode = lorawan.receive(rx_buffer, sizeof(rx_buffer), port, flags);
if (retcode < 0) {
printf("\r\n receive() - Error code %d \r\n", retcode);
return;
}
printf(" RX Data on port %u (%d bytes): ", port, retcode);
for (uint8_t i = 0; i < retcode; i++) {
printf("%02x ", rx_buffer[i]);
}
printf("\r\n");
memset(rx_buffer, 0, sizeof(rx_buffer));
}
/**
* Event handler
*/
static void lora_event_handler(lorawan_event_t event)
{
switch (event) {
case CONNECTED:
printf("\r\n Connection - Successful \r\n");
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
send_message();
} else {
ev_queue.call_every(TX_TIMER, send_message);
}
break;
case DISCONNECTED:
ev_queue.break_dispatch();
printf("\r\n Disconnected Successfully \r\n");
break;
case TX_DONE:
printf("\r\n Message Sent to Network Server \r\n");
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
send_message();
}
break;
case TX_TIMEOUT:
case TX_ERROR:
case TX_CRYPTO_ERROR:
case TX_SCHEDULING_ERROR:
printf("\r\n Transmission Error - EventCode = %d \r\n", event);
// try again
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
send_message();
}
break;
case RX_DONE:
printf("\r\n Received message from Network Server \r\n");
receive_message();
break;
case RX_TIMEOUT:
case RX_ERROR:
printf("\r\n Error in reception - Code = %d \r\n", event);
break;
case JOIN_FAILURE:
printf("\r\n OTAA Failed - Check Keys \r\n");
break;
case UPLINK_REQUIRED:
printf("\r\n Uplink required by NS \r\n");
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
send_message();
}
break;
default:
MBED_ASSERT("Unknown Event");
}
}
// EOF
This is my Arduino sketch:
#include <DHT.h>
#include <Wire.h>
// Sensor temperatura, humedad DHT11 (test).
#define DHTPIN 10
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// Comunicacion i2c con nodo lora.
#define SLAVE_ADDRESS 0x29
// Variables globales de sensores
float h;
float t;
void setup() {
Serial.begin(9600);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
dht.begin();
}
void loop() {
delay(5000);
h = dht.readHumidity();
t = dht.readTemperature();
// Comprobamos si ha habido algún error en la lectura
if (isnan(h) || isnan(t)) {
Serial.println("Error obteniendo los datos del sensor DHT11");
return;
}
Serial.print("Humedad: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperatura: ");
Serial.print(t);
Serial.println(" *C ");
}
// Total de bytes del arreglo de datos a transmitir: 28 bytes.
void receiveEvent(int howMany) {
Serial.println("Recibido paquete desde MDOT LoRa Node");
Wire.beginTransmission(SLAVE_ADDRESS);
Wire.write("/Humedad:");
char hum[3];
String(h).toCharArray(hum, 3);
Wire.write(hum);
Wire.write("/Temperatura:");
char temp[3];
String(t).toCharArray(temp, 3);
Wire.write(temp);
Wire.endTransmission();
}
And I leave the repo with all the code I'm currently using: https://github.com/cipiasentini/lorai2c
Here's a picture of my physical setup (I tried it with and without the 10k pullup resistors)

Arduino CC3000 Wifi Shield can not run a 'buildtest' in the cc3000 library

To check if my cc3000 Wifi Shield works, I download the Adafruit CC3000 Library and run the example in the library, which is 'buildtest'.
Here are the link of Adafruit and library.
https://learn.adafruit.com/adafruit-shield-compatibility/cc3000-wifi-shield
https://github.com/adafruit/Adafruit_CC3000_Library
But when I run 'buildtest', the only things I can see in Serial monitor are corrupt(or garbled) messages.
Here is the code of 'buildtest' in the library.
/***************************************************
This is an example for the Adafruit CC3000 Wifi Breakout & Shield
Designed specifically to work with the Adafruit WiFi products:
----> https://www.adafruit.com/products/1469
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Kevin Townsend & Limor Fried for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
/*
This example does a full test of core connectivity:
* Initialization
* SSID Scan
* AP connection
* DHCP printout
* DNS lookup
* Ping
* Disconnect
It's a good idea to run this sketch when first setting up the
module.
*/
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ 3 // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
SPI_CLOCK_DIVIDER); // you can change this clock speed but DI
#define WLAN_SSID "myNetwork" // cannot be longer than 32 characters!
#define WLAN_PASS "myPassword"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY WLAN_SEC_WPA2
/**************************************************************************/
/*!
#brief Sets up the HW and the CC3000 module (called automatically
on startup)
*/
/**************************************************************************/
void setup(void)
{
Serial.begin(115200);
Serial.println(F("Hello, CC3000!\n"));
displayDriverMode();
Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);
/* Initialise the module */
Serial.println(F("\nInitialising the CC3000 ..."));
if (!cc3000.begin())
{
Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
while(1);
}
/* Optional: Update the Mac Address to a known value */
/*
uint8_t macAddress[6] = { 0x08, 0x00, 0x28, 0x01, 0x79, 0xB7 };
if (!cc3000.setMacAddress(macAddress))
{
Serial.println(F("Failed trying to update the MAC address"));
while(1);
}
*/
uint16_t firmware = checkFirmwareVersion();
if (firmware < 0x113) {
Serial.println(F("Wrong firmware version!"));
for(;;);
}
displayMACAddress();
/* Optional: Get the SSID list (not available in 'tiny' mode) */
#ifndef CC3000_TINY_DRIVER
listSSIDResults();
#endif
/* Delete any old connection data on the module */
Serial.println(F("\nDeleting old connection profiles"));
if (!cc3000.deleteProfiles()) {
Serial.println(F("Failed!"));
while(1);
}
/* Optional: Set a static IP address instead of using DHCP.
Note that the setStaticIPAddress function will save its state
in the CC3000's internal non-volatile memory and the details
will be used the next time the CC3000 connects to a network.
This means you only need to call the function once and the
CC3000 will remember the connection details. To switch back
to using DHCP, call the setDHCP() function (again only needs
to be called once).
*/
/*
uint32_t ipAddress = cc3000.IP2U32(192, 168, 1, 19);
uint32_t netMask = cc3000.IP2U32(255, 255, 255, 0);
uint32_t defaultGateway = cc3000.IP2U32(192, 168, 1, 1);
uint32_t dns = cc3000.IP2U32(8, 8, 4, 4);
if (!cc3000.setStaticIPAddress(ipAddress, netMask, defaultGateway, dns)) {
Serial.println(F("Failed to set static IP!"));
while(1);
}
*/
/* Optional: Revert back from static IP addres to use DHCP.
See note for setStaticIPAddress above, this only needs to be
called once and will be remembered afterwards by the CC3000.
*/
/*
if (!cc3000.setDHCP()) {
Serial.println(F("Failed to set DHCP!"));
while(1);
}
*/
/* Attempt to connect to an access point */
char *ssid = WLAN_SSID; /* Max 32 chars */
Serial.print(F("\nAttempting to connect to ")); Serial.println(ssid);
/* NOTE: Secure connections are not available in 'Tiny' mode!
By default connectToAP will retry indefinitely, however you can pass an
optional maximum number of retries (greater than zero) as the fourth parameter.
ALSO NOTE: By default connectToAP will retry forever until it can connect to
the access point. This means if the access point doesn't exist the call
will _never_ return! You can however put in an optional maximum retry count
by passing a 4th parameter to the connectToAP function below. This should
be a number of retries to make before giving up, for example 5 would retry
5 times and then fail if a connection couldn't be made.
*/
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.println(F("Connected!"));
/* Wait for DHCP to complete */
Serial.println(F("Request DHCP"));
while (!cc3000.checkDHCP())
{
delay(100); // ToDo: Insert a DHCP timeout!
}
/* Display the IP address DNS, Gateway, etc. */
while (! displayConnectionDetails()) {
delay(1000);
}
#ifndef CC3000_TINY_DRIVER
/* Try looking up www.adafruit.com */
uint32_t ip = 0;
Serial.print(F("www.adafruit.com -> "));
while (ip == 0) {
if (! cc3000.getHostByName("www.adafruit.com", &ip)) {
Serial.println(F("Couldn't resolve!"));
}
delay(500);
}
cc3000.printIPdotsRev(ip);
/* Do a quick ping test on adafruit.com */
Serial.print(F("\n\rPinging ")); cc3000.printIPdotsRev(ip); Serial.print("...");
uint8_t replies = cc3000.ping(ip, 5);
Serial.print(replies); Serial.println(F(" replies"));
if (replies)
Serial.println(F("Ping successful!"));
#endif
/* You need to make sure to clean up after yourself or the CC3000 can freak out */
/* the next time you try to connect ... */
Serial.println(F("\n\nClosing the connection"));
cc3000.disconnect();
}
void loop(void)
{
delay(1000);
}
/**************************************************************************/
/*!
#brief Displays the driver mode (tiny of normal), and the buffer
size if tiny mode is not being used
#note The buffer size and driver mode are defined in cc3000_common.h
*/
/**************************************************************************/
void displayDriverMode(void)
{
#ifdef CC3000_TINY_DRIVER
Serial.println(F("CC3000 is configure in 'Tiny' mode"));
#else
Serial.print(F("RX Buffer : "));
Serial.print(CC3000_RX_BUFFER_SIZE);
Serial.println(F(" bytes"));
Serial.print(F("TX Buffer : "));
Serial.print(CC3000_TX_BUFFER_SIZE);
Serial.println(F(" bytes"));
#endif
}
/**************************************************************************/
/*!
#brief Tries to read the CC3000's internal firmware patch ID
*/
/**************************************************************************/
uint16_t checkFirmwareVersion(void)
{
uint8_t major, minor;
uint16_t version;
#ifndef CC3000_TINY_DRIVER
if(!cc3000.getFirmwareVersion(&major, &minor))
{
Serial.println(F("Unable to retrieve the firmware version!\r\n"));
version = 0;
}
else
{
Serial.print(F("Firmware V. : "));
Serial.print(major); Serial.print(F(".")); Serial.println(minor);
version = major; version <<= 8; version |= minor;
}
#endif
return version;
}
/**************************************************************************/
/*!
#brief Tries to read the 6-byte MAC address of the CC3000 module
*/
/**************************************************************************/
void displayMACAddress(void)
{
uint8_t macAddress[6];
if(!cc3000.getMacAddress(macAddress))
{
Serial.println(F("Unable to retrieve MAC Address!\r\n"));
}
else
{
Serial.print(F("MAC Address : "));
cc3000.printHex((byte*)&macAddress, 6);
}
}
/**************************************************************************/
/*!
#brief Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false;
}
else
{
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
/**************************************************************************/
/*!
#brief Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/
void listSSIDResults(void)
{
uint32_t index;
uint8_t valid, rssi, sec;
char ssidname[33];
if (!cc3000.startSSIDscan(&index)) {
Serial.println(F("SSID scan failed!"));
return;
}
Serial.print(F("Networks found: ")); Serial.println(index);
Serial.println(F("================================================"));
while (index) {
index--;
valid = cc3000.getNextSSID(&rssi, &sec, ssidname);
Serial.print(F("SSID Name : ")); Serial.print(ssidname);
Serial.println();
Serial.print(F("RSSI : "));
Serial.println(rssi);
Serial.print(F("Security Mode: "));
Serial.println(sec);
Serial.println();
}
Serial.println(F("================================================"));
cc3000.stopSSIDscan();
}
Sorry, I know this is very long code, but I can't understand why I get the corrupt message.
I don't think this problem is related to the setting of WIFI(#define WLAN SSID or #define WLAN PASS).
Because... if you look at the 'void setup' function, you can see
Serial.println(F("Hello, CC3000!\n"));
so I should see this message(Hello CC3000) no matter what.
Even if I set the my WLAN SSID and WLAN PASS, same corrupt message appears.
I can not check whether my cc3000 work well because of this corrupt message.
How can I solve this problem??
It usually means that you are using the wrong baudrate. In your program listing you have this line
“Serial.begin(115200);”
This sets the default baudrate.
In the Arduino software-> Tools->Serial Monitor-> bottom right corner you will see the baudrate window. This needs to match the baudrate in your program otherwise you get rubbish displayed.

Arduino - managing two SPI devices at once

I'm trying to control two SPI devices at once using identical MISO, MOSI, and CLOCK pins but different SS pins.
One is the Wifly shield from SparkFun that uses a SPI-to-UART chip, the other is a MAX31855.
They work independently, but not together..
The SPI-to-UART code that I'm using is listed below. The only changes I've made is in the header file; I set select() and deselect() to public.
#include "SpiUart.h"
// See section 8.10 of the datasheet for definitions
// of bits in the Enhanced Features Register (EFR)
#define EFR_ENABLE_CTS 1 << 7
#define EFR_ENABLE_RTS 1 << 6
#define EFR_ENABLE_ENHANCED_FUNCTIONS 1 << 4
// See section 8.4 of the datasheet for definitions
// of bits in the Line Control Register (LCR)
#define LCR_ENABLE_DIVISOR_LATCH 1 << 7
// The original crystal frequency used on the board (~12 MHz) didn't
// give a good range of baud rates so around July 2010 the crystal
// was replaced with a better frequency (~14 MHz).
#ifndef USE_14_MHZ_CRYSTAL
#define USE_14_MHZ_CRYSTAL true // true (14 MHz) , false (12 MHz)
#endif
#if USE_14_MHZ_CRYSTAL
#define XTAL_FREQUENCY 14745600UL // On-board crystal (New mid-2010 Version)
#else
#define XTAL_FREQUENCY 12288000UL // On-board crystal (Original Version)
#endif
// See datasheet section 7.8 for configuring the
// "Programmable baud rate generator"
#define PRESCALER 1 // Default prescaler after reset
#define BAUD_RATE_DIVISOR(baud) ((XTAL_FREQUENCY/PRESCALER)/(baud*16UL))
// TODO: Handle configuration better
// SC16IS750 register values
struct SPI_UART_cfg {
char DataFormat;
char Flow;
};
struct SPI_UART_cfg SPI_Uart_config = {
0x03,
// We need to enable flow control or we overflow buffers and
// lose data when used with the WiFly. Note that flow control
// needs to be enabled on the WiFly for this to work but it's
// possible to do that with flow control enabled here but not there.
// TODO: Make this able to be configured externally?
EFR_ENABLE_CTS | EFR_ENABLE_RTS | EFR_ENABLE_ENHANCED_FUNCTIONS
};
void SpiUartDevice::begin(unsigned long baudrate) {
/*
* Initialize SPI and UART communications
*
* Uses BAUD_RATE_DEFAULT as baudrate if none is given
*/
SPI.begin();
initUart(baudrate);
}
void SpiUartDevice::deselect() {
/*
* Deslects the SPI device
*/
digitalWrite(SS, HIGH);
}
void SpiUartDevice::select() {
/*
* Selects the SPI device
*/
digitalWrite(SS, LOW);
}
void SpiUartDevice::initUart(unsigned long baudrate) {
/*
* Initialise the UART.
*
* If initialisation fails this method does not return.
*/
// Initialise and test SC16IS750
configureUart(baudrate);
if(!uartConnected()){
while(1) {
// Lock up if we fail to initialise SPI UART bridge.
};
}
// The SPI UART bridge is now successfully initialised.
}
void SpiUartDevice::setBaudRate(unsigned long baudrate) {
unsigned long divisor = BAUD_RATE_DIVISOR(baudrate);
writeRegister(LCR, LCR_ENABLE_DIVISOR_LATCH); // "Program baudrate"
writeRegister(DLL, lowByte(divisor));
writeRegister(DLM, highByte(divisor));
}
void SpiUartDevice::configureUart(unsigned long baudrate) {
/*
* Configure the settings of the UART.
*/
// TODO: Improve with use of constants and calculations.
setBaudRate(baudrate);
writeRegister(LCR, 0xBF); // Access EFR register
writeRegister(EFR, SPI_Uart_config.Flow); // Enable enhanced registers
writeRegister(LCR, SPI_Uart_config.DataFormat); // 8 data bit, 1 stop bit, no parity
writeRegister(FCR, 0x06); // Reset TXFIFO, reset RXFIFO, non FIFO mode
writeRegister(FCR, 0x01); // Enable FIFO mode
}
boolean SpiUartDevice::uartConnected() {
/*
* Check that UART is connected and operational.
*/
// Perform read/write test to check if the UART is working
const char TEST_CHARACTER = 'H';
writeRegister(SPR, TEST_CHARACTER);
return (readRegister(SPR) == TEST_CHARACTER);
}
void SpiUartDevice::writeRegister(byte registerAddress, byte data) {
/*
* Write <data> byte to the SC16IS750 register <registerAddress>
*/
select();
SPI.transfer(registerAddress);
SPI.transfer(data);
deselect();
}
byte SpiUartDevice::readRegister(byte registerAddress) {
/*
* Read byte from SC16IS750 register at <registerAddress>.
*/
// Used in SPI read operations to flush slave's shift register
const byte SPI_DUMMY_BYTE = 0xFF;
char result;
select();
SPI.transfer(SPI_READ_MODE_FLAG | registerAddress);
result = SPI.transfer(SPI_DUMMY_BYTE);
deselect();
return result;
}
int SpiUartDevice::available() {
/*
* Get the number of bytes (characters) available for reading.
*
* This is data that's already arrived and stored in the receive
* buffer (which holds 64 bytes).
*/
// This alternative just checks if there's data but doesn't
// return how many characters are in the buffer:
// readRegister(LSR) & 0x01
return readRegister(RXLVL);
}
int SpiUartDevice::read() {
/*
* Read byte from UART.
*
* Returns byte read or or -1 if no data available.
*
* Acts in the same manner as 'Serial.read()'.
*/
if (!available()) {
return -1;
}
return readRegister(RHR);
}
size_t SpiUartDevice::write(byte value) {
/*
* Write byte to UART.
*/
while (readRegister(TXLVL) == 0) {
// Wait for space in TX buffer
};
writeRegister(THR, value);
}
size_t SpiUartDevice::write(const char *str, size_t size) {
/*
* Write string to UART.
*/
while (size--)
write(*str++);
while (readRegister(TXLVL) < 64) {
// Wait for empty TX buffer (slow).
// (But apparently still not slow enough to ensure delivery.)
};
}
void SpiUartDevice::flush() {
/*
* Flush characters from SC16IS750 receive buffer.
*/
// Note: This may not be the most appropriate flush approach.
// It might be better to just flush the UART's buffer
// rather than the buffer of the connected device
// which is essentially what this does.
while(available() > 0) {
read();
}
}
void SpiUartDevice::ioSetDirection(unsigned char bits) {
writeRegister(IODIR, bits);
}
void SpiUartDevice::ioSetState(unsigned char bits) {
writeRegister(IOSTATE, bits);
}
I attempted to use it like this:
SpiSerial.deselect(); //Deselect Wi-Fi
delay(100); //Wait, just for the heck of it.
currentTemp = thermocouple.readFarenheit(); //Read from max31855... readFarenheit selects and unselects its own SS pin.
SpiSerial.select(); //Reselect Wi-Fi
But it's still unable to function. What further things should I attempt in order to get this to function?
TWO issues:
You don't mention anything about the MAX31855 library or how it is used. It looks like you are using Adafruit_MAX31855. Did you set the second argument of the constructors instance to "SS" or the value of the pin for chip select tied to the MAX31855? Their respective chip selects can not share the same pin.
The Adafruit_MAX31855 bit bangs out an emulated SPI over GPIO. This is not the same as the Wi-Fi's true hard SPI. Once spi.begin(); from SpiUartDevice::begin() then the GPIO pins shared with the hard SPI are no longer available as GPIO. As a result, the Adafruit_MAX31855 bit bangs do nothing. There are several options for the latter problem.
Use a different MAX31855 library that does not bit bang it. But I don't quickly see one.
Fix the Adafruit_MAX31855 to hard SPI.
Use different pins for the MAX31855 other than the Hard SPI pins.
Use the SPI.end() function to shut down the SPI and return GPIO back to normal IO use, and then restart SPI.begin() after bit banging out the Adafruit_MAX31855 function calls.
Such as the following:
SpiSerial.deselect(); //Deselect Wi-Fi
SPI.end(); //Restore GPIO mode of pins
delay(100); //Wait, just for the heck of it
currentTemp = thermocouple.readFarenheit();
SPI.begin(); //Restore SPI mode of pins
SpiSerial.select(); //Reselect Wi-Fi

Resources