Can't handle nRF24L01+ using ESP32 (esp-idf) - microcontroller

I'm trying to port nRF24L01 library to ESP32 (I'm using esp-idf). All I have to do is to replace functions that are using spi. That's how I do this:
static uint8_t *create_tx(uint8_t cmd, const uint8_t *buff, uint8_t length){
uint8_t *tx = (uint8_t*)heap_caps_malloc(64, MALLOC_CAP_DMA);
tx[0] = cmd;
if(buff != NULL)
memcpy(tx + 1, buff, length);
return tx;
}
uint8_t NRF24L01::Read(uint8_t cmd, uint8_t *pBuff, uint8_t length){
uint8_t *recv = (uint8_t*)heap_caps_malloc(64, MALLOC_CAP_DMA);
uint8_t *send = (uint8_t*)heap_caps_malloc(64, MALLOC_CAP_DMA);
memset(send, NRF24_CMD_NOP, 32);
send[0] = cmd;
CSPIBuilder builder;
spi_transaction_t t =
builder
.SetLength(length)
.SetTx(send)
.SetRx(recv)
.build();
spi_device_transmit(m_Spi, &t);
memcpy(pBuff, recv + 1, length);
uint8_t status = recv[0];
free(recv);
free(send);
return status;
}
uint8_t NRF24L01::Write(uint8_t cmd, uint8_t *pBuff, uint8_t length){
uint8_t *txBuff = create_tx(cmd, pBuff, length);
uint8_t *rxBuff = (uint8_t*)heap_caps_malloc(64, MALLOC_CAP_DMA);
memset(rxBuff, NRF24_CMD_NOP, length + 1);
spi_transaction_t t =
CSPIBuilder()
.SetLength(length)
.SetTx(txBuff)
.SetRx(rxBuff)
.build();
spi_device_transmit(m_Spi, &t);
free(txBuff);
uint8_t status = rxBuff[0];
free(rxBuff);
return status;
}
class CSPIBuilder{
public:
CSPIBuilder(){
memset(&t, 0, sizeof(t));
}
CSPIBuilder &UseTxData() { t.flags |= SPI_TRANS_USE_TXDATA; return *this; }
CSPIBuilder &UseRxData() { t.flags |= SPI_TRANS_USE_RXDATA; return *this; }
CSPIBuilder &UseTRxData() { t.flags |= SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; return *this; }
CSPIBuilder &SetLength(uint8_t length) { t.length = (length + 1) * 8; return *this; }
CSPIBuilder &SetRxLength(uint8_t rxLength) { t.rxlength = (rxLength + 1) * 8; return *this; }
CSPIBuilder &SetTx(uint8_t *txBuffer) { t.tx_buffer = txBuffer; return *this; }
CSPIBuilder &SetRx(uint8_t *rxBuffer) { t.rx_buffer = rxBuffer; return *this; }
spi_transaction_t build() {return t;}
private:
spi_transaction_t t;
};
But... There is nothing works. SPI wires are connected properly. I can read status via spi. I cannot confirm that it is correct value, but it's changing every time I distonnect GND from nRF24L01. So I think wiring is OK.
What's wrong with this code?

Well, as I understood there are big problems with native esp-idf spi api. The solution was to replace full spi transaction (more than 1 byte) by small transactions byte-by-byte (since spi api can send only first byte correctly (in my case atleast)). Also I have to control CS pin by myself and respect datasheet's timings
The result of these manipulations is my own library (haha)
Feel free to use it

Related

Goodix GT1151 touch driver library for Arduino not working

I'm trying to get my "2.9inch Touch e-Paper Module for Raspberry Pi Pico" working that I bought from Amazon (wiki here. My main problem is that I'm not able to read data over i2c from the touchscreen and I've tried using the wiki's library with no luck.
GoodixGT1151.h
#ifndef GOODIX_GT1151_H
#define GOODIX_GT1151_H
#include <Arduino.h>
#include <Wire.h>
// I2C address of the Goodix GT1151 touchpanel
const uint8_t GT1151_I2C_ADDRESS = 0x5D;
// Register addresses for the Goodix GT1151 touchpanel
const uint8_t GT1151_REG_CMD = 0x8040;
const uint8_t GT1151_REG_DATA = 0x8140;
// Command values for the Goodix GT1151 touchpanel
const uint8_t GT1151_CMD_RESET = 0x01;
const uint8_t GT1151_CMD_IDLE = 0x04;
const uint8_t GT1151_CMD_READ_COORDS = 0x08;
// Size of the touch data array for the Goodix GT1151 touchpanel
const uint8_t GT1151_TOUCH_DATA_SIZE = 18;
// Maximum number of touch points supported by the Goodix GT1151 touchpanel
const uint8_t GT1151_MAX_TOUCH_POINTS = 5;
class GoodixGT1151 {
public:
// Struct for storing touch point data
struct TouchPoint
{
int16_t x;
int16_t y;
bool touched;
};
// Constructor
GoodixGT1151();
// Initialize the touchpanel
void begin();
// Reset the touchpanel
void reset();
// Put the touchpanel into idle mode
void idle();
// Read the current touch coordinates from the touchpanel
void readCoords(TouchPoint* points);
private:
// Write a command to the touchpanel
void writeCommand(uint8_t cmd);
// Read touch data from the touchpanel
void readTouchData(uint8_t* data);
};
#endif
GoodixGT1151.cpp
#include "GoodixGT1151.h"
GoodixGT1151::GoodixGT1151() {}
void GoodixGT1151::begin() {
Wire.begin();
}
void GoodixGT1151::reset() {
writeCommand(GT1151_CMD_RESET);
}
void GoodixGT1151::idle() {
writeCommand(GT1151_CMD_IDLE);
}
void GoodixGT1151::readTouchData(uint8_t* data) {
writeCommand(GT1151_CMD_READ_COORDS);
Wire.requestFrom(GT1151_I2C_ADDRESS, GT1151_TOUCH_DATA_SIZE);
for (int i = 0; i < GT1151_TOUCH_DATA_SIZE; i++) {
data[i] = Wire.read();
}
}
void GoodixGT1151::readCoords(TouchPoint* points) {
uint8_t data[GT1151_TOUCH_DATA_SIZE];
readTouchData(data);
// read the touch data for each touch point
for (int i = 0; i < GT1151_MAX_TOUCH_POINTS; i++) {
// calculate the starting index of the touch data for this touch point
int start = i * 5;
// read the touch coordinates and touch status for this touch point
points[i].x = (data[start + 3] << 8) | data[start + 2];
points[i].y = (data[start + 5] << 8) | data[start + 4];
points[i].touched = (data[start + 1] & 0x01) == 0x01;
}
}
void GoodixGT1151::writeCommand(uint8_t cmd) {
Wire.beginTransmission(GT1151_I2C_ADDRESS);
Wire.write(GT1151_REG_CMD >> 8);
Wire.write(GT1151_REG_CMD & 0xFF);
Wire.write(cmd);
Wire.endTransmission();
}
None of Waveshare's examples were made for Arduino and when I tried to replace their code with the wire library I couldn't get it to work. So instead I am using the code above which I feel like should work and is promising but the serial monitor just prints out -1 for the values. Currently I am testing the code on my Tinypico nano esp32 and have checked my wiring with a multimeter to confirm that everything is correct.
I have two guesses about my problem:
The waveshare library included code for a touch reset pin (TRST), which I am not using with my current code and am not sure how to do.
Or the GT1151 touchscreen driver is just broken and I should order a new display

global variables not seen in SRAM arduino

I am trying to print all the sram contents and i expect to find the global variables declared in the code to be at Ox100. But it seems like they are not allocated at all. granted i am not using the variables anywhere in the code but i would still expect them to be allocated memory. Another thing to note is that even if i keep adding var in the code the Global variable usage seems to hold steady with message below. What am i missing? Some compiler optimization?
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
int var1 = 100;
int var2 = 100;
int var3 = 100;
int var4 = 100;
int var5 = 100;
int var6 = 100;
void setup() {
UCSR0B = (1 << TXEN0); //Enable the serial output
UBRR0L = 16; //Set the baud rate to 57600bps
memory_dump1(); //Execute to the memory_dump subroutine
}
void loop(){
int var2 = var1;
delay(5000);
memory_dump1();
}
void memory_dump1(){
uint16_t address;
uint8_t byte_at_address, new_line;
address = 0x0100;
int byteCount = 0;
while (address <= 0x08FF) {
byte_at_address = *(byte *)address;
Serial.print (byte_at_address,HEX);
Serial.print ('\t');
address++;
byteCount ++;
if ( byteCount % 8 == 0 ){
Serial.print ('\n');
}
}
}
The keyword volatile might help...
// volatile /* 9 byte, or 209 byte RAM if the // is removed */
int dummy_data[100];
void setup() { }
void loop() { }
Sample compiled for an Arduino Nano with IDE 1.8.9
int dummy_data[100] { 1,2,3,4,5,6 };
void dumpRAM (const char* name, void* adr, size_t size) {
unsigned int iadr = (unsigned int) adr;
Serial.print("0x");
if (iadr < 0x1000) Serial.write('0');
if (iadr < 0x100) Serial.write('0');
if (iadr < 0x10) Serial.write('0');
Serial.print (iadr, HEX);
Serial.print (": ");
Serial.println (name);
byte* bp = (byte*) adr;
for (size_t i = 0; i < size;) {
Serial.write (' '); if(*bp <0x10) Serial.write('0');
Serial.print( *bp++, HEX);
if ( (++i & 0x0F) == 0) // every 16 byte
Serial.println();
}
}
void setup() {
Serial.begin(9600);
dumpRAM("dummy_data", dummy_data, sizeof(dummy_data));
}
This helps to use your variable (thus avoids opitimizing it away), shows its content and address.

How do I send multiple 32 byte strings to I2C master?

I have multiple variable length strings which do not exceed 32 bytes each which need to be sent via I2C between 2 Arduino Nanos. I use # to terminate each string. I can successfully send and receive the first string, but for the life of me I can't figure out how to send the succeeding strings. The samples I am testing with look like this:
String answer = "12345,123.4,50.6,12345#";
String answerV = "4.10,4.15,4.13,4.05,4.18,0#";
Master Code
#include <Wire.h>
int esc = 0;
int throttle = 80;
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("I2C Master ready!");
esc = throttle * 100 / 180;
}
void loop() {
delay(500);
Serial.println("Receive data");
Wire.requestFrom(9,32);
String response = "";
Serial.println(Wire.available());
while (Wire.available())
{
char b = Wire.read();
response += b;
}
String dataString = response;
int hashIndex = dataString.indexOf('#');
String SportData = dataString.substring(0, hashIndex);
Serial.println(SportData);
String SDdata = String (esc) + "," + String(SportData);
Serial.println(SDdata);
}
Slave Code
#include <Wire.h>
byte ANSWERSIZE= 22;
String answer = "12345,123.4,50.6,12345#";
String answerV = "4.10,4.15,4.13,4.05,4.18,0#";
void setup() {
Wire.begin(9);
Wire.onRequest(requestEvent); // data request from Master
Serial.begin(115200);
Serial.println("I2C Slave ready!");
ANSWERSIZE = answer.length();
}
void requestEvent() {
byte response[ANSWERSIZE];
for (byte i=0;i<ANSWERSIZE;i++)
{
response[i] = (byte)answer.charAt(i);
}
Wire.write(response,sizeof(response));
}
void loop() {
delay(50);
}
Can someone please show me how this can be done?
A simple idea is to keep track of the number of times requestEvent() is called, and use that to decide what to send back to the master.
Here is the code (n.b. I took the liberty to optimise it a bit):
Master:
#include <Wire.h>
/**
* globals
*/
int esc = 0;
int throttle = 80;
char i2c_buffer[33];
/**
* setup & loop
*/
void setup()
{
Serial.begin(115200);
Wire.begin();
esc = throttle * 100 / 180;
i2c_buffer[32] = '\0';
Serial.println("I2C Master ready!");
}
void loop()
{
delay(500);
Wire.requestFrom(9, 32);
for (byte i = 0; i < 32 && Wire.available(); ++i)
{
i2c_buffer[i] = Wire.read();
if (i2c_buffer[i] == '#') {
i2c_buffer[i] = '\0';
break;
}
}
Serial.print(esc);
Serial.print(',');
Serial.println(i2c_buffer);
}
Slave:
#include <Wire.h>
/**
* globals
*/
const byte DATA_SIZE = 2;
String data[DATA_SIZE] = {
"12345,123.4,50.6,12345#",
"4.10,4.15,4.13,4.05,4.18,0#"
};
/**
* setup & loop
*/
void setup()
{
Serial.begin(115200);
Wire.begin(9);
Wire.onRequest(requestEvent); // data request from Master
Serial.println("I2C Slave ready!");
}
void loop()
{
delay(50);
}
/**
* help functions
*/
void requestEvent()
{
static byte req_number = 0;
Wire.write(reinterpret_cast<const unsigned char*>(data[req_number].c_str()),
data[req_number].length());
req_number = (req_number + 1) % DATA_SIZE;
}
Note: I don't have two Arduino devices, so I could not test this code. If you spot some bugs, report back and I'll fix them.

Translating Arduino to ChibiOS

I'm trying to implement "one wire" for ChibiOS, running on atmega1280 (Arduino board). I am mostly copy/pasting from the arduino implementation (which works fine):
uint8_t OneWire::reset(void)
{
IO_REG_TYPE mask = bitmask;
volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
uint8_t r;
uint8_t retries = 125;
noInterrupts();
DIRECT_MODE_INPUT(reg, mask);
interrupts();
// wait until the wire is high... just in case
do {
if (--retries == 0) return 0;
delayMicroseconds(2);
} while ( !DIRECT_READ(reg, mask));
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
interrupts();
delayMicroseconds(480);
noInterrupts();
DIRECT_MODE_INPUT(reg, mask); // allow it to float
delayMicroseconds(70);
r = !DIRECT_READ(reg, mask);
interrupts();
delayMicroseconds(410);
return r;
}
What I wrote for the ChibiOS implementation of the "reset" function is the following:
uint8_t OneWire::reset(void)
{
palSetPadMode((ioportid_t)IOPORT2, 3, PAL_MODE_INPUT);
// Wait until the wire is high.
uint8_t retries = 125;
do {
if (--retries == 0) {
return 0;
}
chThdSleepMicroseconds(2);
} while (palReadPad(IOPORT2, 3) == PAL_LOW);
palSetPadMode((ioportid_t)IOPORT2, 3, PAL_MODE_OUTPUT_PUSHPULL);
palWritePad(IOPORT2, 3, PAL_LOW); // drive output low
chThdSleepMicroseconds(480);
palSetPadMode((ioportid_t)IOPORT2, 3, PAL_MODE_INPUT);
chThdSleepMicroseconds(70);
uint8_t result = (palReadPad(IOPORT2, 3) == PAL_LOW);
chThdSleepMicroseconds(410);
return result;
}
What am I doing wrong?

Arduino : Check byte array for chars one at a time

I communicate with Arduino via Serial using a program that sends a series of bytes.
In order for the Arduino to realize it is receiving a message rather than junk, I have tagged the start of my byte array with the chars 'S' 'T' 'A' 'R' 'T'. After this will eventually follow a series of bytes that will be assigned to internal variables (not yet implemented).
The Arduino must read each byte sequentially and compare it to the byte array and if all are present in the correct order it will continue with the next part of the program, otherwise it will should discard current byte and wait for more bytes to arrive.
I am trying to implement it in the most efficient and readable way rather than using a series of nested if statements.
So far I have got:
byte inByte = 0;
byte handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
}
else break;
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This however doesn't seem to get past the second byte and I'm not sure why.
Worked it out :
Here is the answer:
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
while(!Serial.available()) {delay(1);}
}
else {break;}
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This may not be the most efficient way perhaps, but I can't see a problem with it currently.
Better answer : This allows the rest of the loop to iterate while waiting for the message to finish and if the full handshake message isn't received the counter will reset.
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
int messageIndex = 0;
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
inByte = Serial.read();
Serial.println(messageIndex);
if (inByte == handShake[messageIndex])
{
messageIndex++;
if (messageIndex == sizeof(handShake)) {messageIndex = 0; setArduino();}
}
else {messageIndex=0;}
}
// Other code while waiting for message to finish
Serial.println("tick");
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
You could try to calculate your message. CRC is old and good solution. I use it and it works perfect for me. I am not sure what kind of device are you communicating with.
//define
const uint32_t Polynomial = 0xEDB88320;
const uint16_t NumBytes = 256;
uint8_t data[NumBytes];
/// compute CRC32
uint32_t crc32_bitwise(const void* data, uint16_t length, uint32_t previousCrc32 = 0)
{
uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF
uint8_t* current = (uint8_t*) data;
while (length--)
{
crc ^= *current++;
for (uint8_t j = 0; j < 8; j++)
{
uint8_t lowestBit = crc & 1;
crc >>= 1;
if (lowestBit)
crc ^= Polynomial;
}
}
return ~crc; // same as crc ^ 0xFFFFFFFF
}
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
when you need to calculate CRC
uint32_t crc = crc32_bitwise(data_bytes, sizeof(data_bytes));
data_bytes is byte array.
Then you can get all settings or message in byte data[x] and calculate CRC. Then you can add CRC to the message and send message byte data[x+sizeof(CRC)]
P.S. Use byte instead of int. For ex. for(byte x =0; x<sizeof(handShake); x++)

Resources