Arduino array of structs in PROGMEM - c++

I am using a standalone ATmega328P with two piezo elements to generate some music.
I have defined some constants with the frequencies of the music notes.
Then I defined a struct which contains the note for the first and the second piezo and the length of the note.
Then I made more arrays of these structs to describe each songs.
The problem is that this way I run out of memory quickly. I tried to store the arrays of structs in the PROGMEM, to avoid this problem. I tried to use a small library called PROGMEM_readAnything, the memcpy_P() or the pgm_read_word() and pgm_read_byte() functions, but in all cases I get the same problem.
As I loop through the array of NOTES it skips some of the elements, while reads and plays the others correctly. It always skips the same elements, and not just random ones.
I even tried to change the microcontroller, thinking that certain parts of the chip may have been damaged by something, but uploading the same sketch I got the same results, so the microcontroller is probably intact.
Here is the code:
#include <Tone.h>
#include <avr/pgmspace.h>
// Define the notes frequency
#define G2 98
#define Gs2 104
#define Ab2 104
#define A2 110
#define As2 116
//... and so on with many other music notes ...
#define Fs7 2960
#define Gb7 2960
#define G7 3136
//Rest
#define R 0
typedef struct {
int n1;
int n2;
byte units;
} NOTES;
Tone buzzer1;
Tone buzzer2;
int myTempo = 100;
// Walkyrie
const NOTES walkyrie[] PROGMEM = {
{Fs3, Fs4, 2},
{B3, B4, 3},
{Fs3, Fs4, 1},
{B3, B4, 2},
{D4, D5, 6},
{B3, B4, 6},
{D4, D5, 3},
{B3, B4, 1},
{D4, D5, 2},
{Fs4, Fs5, 6},
{D4, D5, 6},
{Fs4, Fs5, 3},
{D4, D5, 1},
{Fs4, Fs5, 2},
{A4, A5, 6},
{A3, A4, 6},
{D4, D5, 3},
{A3, A4, 1},
{D4, D5, 2},
{Fs4, Fs5, 6},
{R, 0, 4},
{A3, A4, 2},
{D4, D5, 3},
{A3, A4, 1},
{D4, D5, 2},
{Fs4, Fs5, 6},
{D4, D5, 6},
{Fs4, Fs5, 3},
{D4, D5, 1},
{Fs4, Fs5, 2},
{A4, A5, 6},
{Fs4, Fs5, 6},
{A4, A5, 3},
{Fs4, Fs5, 1},
{A4, A5, 2},
{Cs5, Cs6, 6},
{Cs4, Cs5, 6},
{Fs4, Fs5, 3},
{Cs4, Cs5, 1},
{Fs4, Fs5, 2},
{As4, As5, 6}
};
void playSong()
{
// We store the frequency of the second piezo in this variable
int secondFreq = 0;
Serial.println(sizeof(walkyrie)/sizeof(walkyrie[0]));
// Walk through the array of music
for(int i = 0; i < sizeof(walkyrie)/sizeof(walkyrie[0]); i++)
{
int n1;
int n2;
byte units;
// Only play if it is not a rest
if (walkyrie[i].n1 > 0)
{
n1 = pgm_read_word(&(walkyrie[i].n1));
n2 = pgm_read_word(&(walkyrie[i].n2));
units = pgm_read_byte(&(walkyrie[i].units));
Serial.print("Row ");
Serial.print(i);
Serial.print(": Frequency 1: ");
Serial.print(n1);
Serial.print(" Frequency 2: ");
Serial.print(n2);
Serial.print(" Units: ");
Serial.println(units);
// Play the note of the first piezo
buzzer1.play(n1, (units*myTempo));
// If the frequency of the second piezo is 0, we play the same note
// as the first, else the note set for the second one
if (n2 == 0)
{
secondFreq = n1;
}
else {
secondFreq = n2;
}
buzzer2.play(secondFreq, (units*myTempo));
}
// Then we wait for the note to end plus a little, between two notes
delay((units*myTempo) + 10);
}
}
void setup() {
Serial.begin(9600);
buzzer1.begin(11);
buzzer2.begin(12);
}
void loop()
{
playSong();
}
I added some lines to see in serial monitor what happens. It reads the correct length...
The output of the serial monitor is the following:
41 (correct length)
Row 1: Freq1: 247 Freq2: 499 Units: 3 (row 0 - the first note is already missing)
Row 2: Freq1: 185 Freq2: 370 Units: 1
Row 3: Freq1: 247 Freq2: 499 Units: 2 (row 4 missing)
Row 5: Freq1: 247 Freq2: 499 Units: 6 (row 6-7 missing)
Row 8: Freq1: 294 Freq2: 587 Units: 2
Row 9: Freq1: 370 Freq2: 740 Units: 6
Row 10: Freq1: 294 Freq2: 587 Units: 6
Row 11: Freq1: 370 Freq2: 740 Units: 3
Row 12: Freq1: 294 Freq2: 587 Units: 1
Row 13: Freq1: 370 Freq2: 740 Units: 2
Row 14: Freq1: 440 Freq2: 880 Units: 6
Row 15: Freq1: 220 Freq2: 440 Units: 6 (row 16-17 missing)
Row 18: Freq1: 294 Freq2: 587 Units: 2
Row 19: Freq1: 370 Freq2: 740 Units: 6
Row 20: Freq1: 0 Freq2: 0 Units: 4
Row 21: Freq1: 220 Freq2: 440 Units: 2
Row 22: Freq1: 294 Freq2: 587 Units: 3
Row 23: Freq1: 220 Freq2: 440 Units: 1
Row 24: Freq1: 294 Freq2: 587 Units: 2
Row 25: Freq1: 370 Freq2: 740 Units: 6
Row 26: Freq1: 294 Freq2: 587 Units: 6
Row 27: Freq1: 370 Freq2: 740 Units: 3
Row 28: Freq1: 294 Freq2: 587 Units: 1
Row 29: Freq1: 370 Freq2: 740 Units: 2
Row 30: Freq1: 440 Freq2: 880 Units: 6
Row 31: Freq1: 370 Freq2: 740 Units: 6
Row 32: Freq1: 440 Freq2: 880 Units: 3
Row 33: Freq1: 370 Freq2: 740 Units: 1
Row 34: Freq1: 440 Freq2: 880 Units: 2
Row 35: Freq1: 554 Freq2: 1109 Units: 6
Row 36: Freq1: 277 Freq2: 554 Units: 6
Row 37: Freq1: 370 Freq2: 740 Units: 3
Row 38: Freq1: 277 Freq2: 554 Units: 1
Row 39: Freq1: 370 Freq2: 740 Units: 2
Row 40: Freq1: 466 Freq2: 932 Units: 6
Why does it happen? Or is there a better, more efficient way of solving this problem?

In this line, you check the data, but you haven't done a 'pgm_read_word()' to actually get the data from the flash memory:
if(walkyrie[i].n1 > 0)
If, by accident, you get a non-zero value, then you correctly read the values from flash, but otherwise, you skip that row.
Further evidence:
Row 20: Frq1: 0 Frq2: 0 Units: 4
Here, n1 is zero, but that test should have skipped the row.
Also, the logic for a 'rest' is a little off. Right now, you don't read the units for the duration of the rest, so it's using the previous value (from a played note).
I think I'd get all three values first, and then check them.
I'd also encode the frequencies into a byte, and use a look-up table to convert the "key number" into a frequency (like MIDI key numbers). Your array of structs will be a little smaller that way. Maybe turn on the __packed__ (whatever) attribute also, to avoid the padding between the entries -- if saving flash space matters (then you could get more songs in there!)
Sounds fun! Good luck!

Related

How can I get N distinct spectrum-like colors in Qt with starting color RED and ending color BLUE

Initially, I thought it can be easily implemented using a single for-loop with pre-calculated step size.
// blue = (240, 255, 255)
// red = (0, 255, 255)
const int step = (240 - 0) / (N - 1);
QColor color;
for (int i = 0; i < N; ++i)
{
color.setHsv(i * step, 255, 255);
}
But as N grows larger, the ending color may not be what I expected.
For example (N == 82), ending color is hsv(162, 255, 255) instead of hsv(240, 255, 255).
My intention is to 1) generate N distinct color, 2) spectrum-like, 3) starts in RED and ends in BLUE.
Should I take S, V into consideration for my requirement as well?
Right now, you're choosing an integer step, and adding it to get from the smallest to the largest value. This has the advantage of spacing the hues evenly. But as you've observed, it has the disadvantage of getting the range wrong (possibly quite badly wrong) at times.
If you're willing to sacrifice a little in the way of the spacing of hues being perfectly even to get closer to filling the specified range, you can compute the step as a floating point number, do floating point multiplication, and round (or truncate) afterwards.
std::vector<int> interpolate_range(int lower, int upper, int step_count) {
double step = (upper - lower) / (step_count - 1.0);
std::vector<int> ret;
for (int i=0; i<step_count; i++) {
// step is a double, so `double * i` is done in floating
// point, then the result is truncated to be pushed into
// ret.
ret.push_back(step * i);
}
return ret;
}
Skipping the other color components, and just printing out these values, we get this:
0 2 5 8 11 14 17 20 23 26
29 32 35 38 41 44 47 50 53 56
59 62 65 68 71 74 77 80 82 85
88 91 94 97 100 103 106 109 112 115
118 121 124 127 130 133 136 139 142 145
148 151 154 157 160 162 165 168 171 174
177 180 183 186 189 192 195 198 201 204
207 210 213 216 219 222 225 228 231 234
237 240
As you can see, we usually get a separation of 3, but a few times (0 to 2, 80 to 82 and 160 to 162) only 2, allowing us to fill the entire range.

100 errors with digitalPinMap[] using VS code and platformio in Sd2PinMap

I just started a new Platformio project in VS Code and included SD library.
On build 100 errors appeared and I really don't know how to fix them as googling them didn't bring any fruit.
I'm using Arduino Uno Rev 2 WiFi
Atmega328P
a value of type "DDRDClass *" cannot be used to initialize an entity of type "volatile uint8_t *"
and
'DDRD' was not declared in this scope are some of the errors involved.
this is my main.cpp file if it can help
#include <Arduino.h>
#include <SPI.h>
#include <SD.h>
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial)
{
}
Serial.print('Initializing SD card...');
if (!SD.begin(4))
{
Serial.println('initialization failed!');
while (1)
;
}
Serial.println('initialization done');
}
void loop()
{
// put your main code here, to run repeatedly:
}
Here is the code that returns the errors
#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// 168 and 328 Arduinos
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRB, &PINB, &PORTB, 0}, // B0 8
{&DDRB, &PINB, &PORTB, 1}, // B1 9
{&DDRB, &PINB, &PORTB, 2}, // B2 10
{&DDRB, &PINB, &PORTB, 3}, // B3 11
{&DDRB, &PINB, &PORTB, 4}, // B4 12
{&DDRB, &PINB, &PORTB, 5}, // B5 13
{&DDRC, &PINC, &PORTC, 0}, // C0 14
{&DDRC, &PINC, &PORTC, 1}, // C1 15
{&DDRC, &PINC, &PORTC, 2}, // C2 16
{&DDRC, &PINC, &PORTC, 3}, // C3 17
{&DDRC, &PINC, &PORTC, 4}, // C4 18
{&DDRC, &PINC, &PORTC, 5} // C5 19
};

Bytes in serial code to an array on Arduino

I've read with a logic analyzer a TX of a controller. I know that it works at 1200 Baud and I have identified the frames according to this photo:
Frames
I have identified in the frame:
1byte - Always 54
2byte - Sequential, ++ per frame
3byte - Always 0
4, 5 and 6byte - Data
7byte - Always 0
8, 9, 10 and 11- Data
12, 13, 14 and 15- They vary (I understand that 15 is a checksum)
I cannot identify the checksum (I suspect Checksum8 Xor due to its similarity to another controller).
I try to take each byte with Arduino to a position of an Array, knowing that the first byte is constant (54) and the frame is always the same length.
Could it be that as the Arduino loop being faster than Serial it duplicates the data in all positions of each Array?
Looking for information I have read that when I make a Serial.print it works correctly (each byte is not repeated, but when I write an array with while (Serial.available ()) it fails.
I leave some frames obtained through arduino with:
#include <Arduino.h>
void setup() {
Serial.begin(9600);
Serial1.begin(1200);
}
void loop() {
if (Serial1.available()) {
int test= Serial1.read();
Serial.println(test);
}
}
54 154 0 84 84 84 0 84 84 84 84 201 133 84 224
54 155 0 89 89 89 0 89 89 89 89 206 138 89 233
54 156 0 2 2 2 0 2 2 2 2 119 51 2 238
54 157 0 7 7 7 0 7 7 7 7 124 56 7 239
54 158 0 0 0 0 0 0 0 0 0 117 49 0 236
54 159 0 5 5 5 0 5 5 5 5 122 54 5 229
Any help is welcome
Thank you very much

the function to get size of hypercube array

this is what I want.
Given M, B, N, I want the function that returns S = (M/B/K)^(1/N)
M: memoryInByte (float)
B: byteForOne (int)
N: dimension (int)
K: the number of arrays ( int)
S: size of each dimension of array (long long)
//Input data is..
float memories[] = {1e6f, 1e9f, 2e9f, 4e9f, 8e9f, 16e9f, 32e9f };
int dimArrays[] = {1, 2, 3};
int byteForOnes[] = {4, 8};
int numArrays[] = {1, 3, 5};
At first, I tried to use pow(), but failed.. maybe because of... converting float into long long????
OP doesn't specify how his implementation failed, but considering the usual suspect, integer division (1 / N == 0), my "fixed" proposal is:
long long int size_of_each_dimension_of_array( double mem, int bs, int n, int k ) {
return round(pow( mem / (bs * k), 1.0 / n ));
}
Which, with the given input values, produces:
Size (in bytes) = 4
Number of arrays = 1
Memory 1D 2D 3D
1e+06 250000 500 63
1e+09 250000000 15811 630
2e+09 500000000 22361 794
4e+09 1000000000 31623 1000
8e+09 2000000000 44721 1260
1.6e+10 4000000000 63246 1587
3.2e+10 8000000000 89443 2000
Size (in bytes) = 8
Number of arrays = 1
Memory 1D 2D 3D
1e+06 125000 354 50
1e+09 125000000 11180 500
2e+09 250000000 15811 630
4e+09 500000000 22361 794
8e+09 1000000000 31623 1000
1.6e+10 2000000000 44721 1260
3.2e+10 4000000000 63246 1587
Size (in bytes) = 4
Number of arrays = 3
Memory 1D 2D 3D
1e+06 83333 289 44
1e+09 83333333 9129 437
2e+09 166666667 12910 550
4e+09 333333333 18257 693
8e+09 666666667 25820 874
1.6e+10 1333333333 36515 1101
3.2e+10 2666666667 51640 1387
Size (in bytes) = 8
Number of arrays = 3
Memory 1D 2D 3D
1e+06 41667 204 35
1e+09 41666667 6455 347
2e+09 83333333 9129 437
4e+09 166666667 12910 550
8e+09 333333333 18257 693
1.6e+10 666666667 25820 874
3.2e+10 1333333333 36515 1101
Size (in bytes) = 4
Number of arrays = 5
Memory 1D 2D 3D
1e+06 50000 224 37
1e+09 50000000 7071 368
2e+09 100000000 10000 464
4e+09 200000000 14142 585
8e+09 400000000 20000 737
1.6e+10 800000000 28284 928
3.2e+10 1600000000 40000 1170
Size (in bytes) = 8
Number of arrays = 5
Memory 1D 2D 3D
1e+06 25000 158 29
1e+09 25000000 5000 292
2e+09 50000000 7071 368
4e+09 100000000 10000 464
8e+09 200000000 14142 585
1.6e+10 400000000 20000 737
3.2e+10 800000000 28284 928

What is supposed to happen with the transpose flag for glUniformMatrix with non-square matrices?

I encountered what seemed to me as strange behavior using glUniformMatrix4x3fv. Specifically when I give TRUE as for the transpose flag entire rows of my matrices are missing in my shader variable (and those that are there are therefor out of order).
For example. Say I have in my GLSL shader:
mat4x3 T[m];
Then in my C++ OpenGL call I want to send a matrix whose entries are (stored in row-major order):
T =
1 2 3
4 5 6
7 8 9
10 11 12
101 102 103
104 105 106
107 108 109
110 111 112
101 102 103
204 205 206
207 208 209
210 211 212
...
And I call
glUniformMatrix4x3fv(location,m,false,T);
Then I see in my shader that the each matrix comes out correctly as:
T[0] ->
1 4 7 10
2 5 8 11
3 6 9 12
T[1] ->
101 104 107 110
102 105 108 111
103 106 109 112
...
BUT, if I store my matrix on the C++ side as (again row-major order):
T =
1 4 7 10
2 5 8 11
3 6 9 12
101 104 107 110
102 105 108 111
103 106 109 112
201 204 207 210
202 205 208 211
203 206 209 212
...
And try to use the transpose flag as TRUE with:
glUniformMatrix4x3fv(location,m,true,T);
Then in my shader the matrices show up incorrectly as:
T[0] ->
1 4 7 10
2 5 8 11
3 6 9 12
T[1] ->
102 105 108 111
103 106 109 112
201 204 207 210
T[2] ->
203 206 209 212
301 304 307 310
302 305 308 311
...
Every 4th row of my data is missing.
Is there a sensible reason for this? I find nothing in the spec (s2.1 p82).
GL_VERSION: 2.1 NVIDIA-1.6.36
GL_SHADING_LANGUAGE_VERSION: 1.20
Then in my C++ OpenGL call I want to send a matrix whose entries are (stored in row-major order):
That's not row-major order. That's column-major order.
Given the following 4x3 matrix:
1 4 7 10
2 5 8 11
3 6 9 12
This is what a C++ array of this data in column-major order would look like:
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
That's what your data is. Feel free to insert space wherever you want; it's entirely irrelevant.
This is what the same data in row-major order looks like:
{1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12}
As to the specific issue you encountered with transposing your data for 4x3 matrices, it may simply be a driver bug.