text:
Arduino AES encryption, working example or tutorial
I am quite new to idea of encryption and am trying to use aes-256-ecb encryption.  I am using library called "Arduino cryptography". Unfortunately I could not find any tutorial that would explain how to encrypt a json string (and other things latter) using a  key.
I have a function that generates same key on both sides of Arduino and php script on a server side, but I could not find a working tutorial (except the case of obsolete buggy library with instruction like : "copy this text" and even that doesn't work.
Unfortunately libraries examples do not bother to show how to retrieve  encrypted string or explain what is what and instead just focus on checking how long encryption takes.
Does any of you know of some tutorial or an example from which I could engineer a solution?
library link:The library
in buioltin example there is a code of:
cipher->decryptBlock(buffer, test->ciphertext);

But I do not understand where is the output and where the input here
Second  question would be if I can use other modes like the mentioned GCM simply by changing the value in
static TestVector const testVectorAES256 = {
    .name        = "AES-256-ECB",
    .key         = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F},
    .plaintext   = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
                    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
    .ciphertext  = {0x8E, 0xA2, 0xB7, 0xCA, 0x51, 0x67, 0x45, 0xBF,
                    0xEA, 0xFC, 0x49, 0x90, 0x4B, 0x49, 0x60, 0x89}
};

or how?
After bits and strougles I have managed to get this working, but next step is how to make it take a string:
/*Skúška kódovania GCM-AES256#*/

#include <Crypto.h>
#include <AES.h>
#include <GCM.h>
//#include <pgmspace.h>

struct TestVector
{
    const char *name;
    uint8_t key[32];
    uint8_t plaintext[60];
    uint8_t ciphertext[60];
    uint8_t authdata[20];
    uint8_t iv[12];
    uint8_t tag[16];
    size_t authsize;
    size_t datasize;
    size_t tagsize;
    size_t ivsize;
};

static TestVector const testVectorGCM PROGMEM = {
    .name        = "AES-256 GCM",
    .key         = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
                    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08},
    .plaintext   = {0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
                    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
                    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
                    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
                    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
                    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
                    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
                    0xba, 0x63, 0x7b, 0x39},
    .ciphertext  = {0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
                    0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
                    0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
                    0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
                    0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
                    0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
                    0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
                    0xbc, 0xc9, 0xf6, 0x62},
    .authdata    = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xab, 0xad, 0xda, 0xd2},
    .iv          = {0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
                    0xde, 0xca, 0xf8, 0x88},
    .tag         = {0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68,
                    0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b},
    .authsize    = 20,
    .datasize    = 60,
    .tagsize     = 16,
    .ivsize      = 12
};

TestVector testVector;
byte buffer[128];

bool testCipher_N(AuthenticatedCipher *cipher, const struct TestVector *test, size_t inc)
{
    size_t posn, len;
    uint8_t tag[16];

    crypto_feed_watchdog();

    cipher->clear();
    if (!cipher->setKey(test->key, cipher->keySize())) {
        Serial.print("setKey ");
        return false;
    }
    if (!cipher->setIV(test->iv, test->ivsize)) {
        Serial.print("setIV ");
        return false;
    }

    memset(buffer, 0xBA, sizeof(buffer));

    for (posn = 0; posn < test->authsize; posn += inc) {
        len = test->authsize - posn;
        if (len > inc)
            len = inc;
        cipher->addAuthData(test->authdata + posn, len);
    }

    for (posn = 0; posn < test->datasize; posn += inc) {
        len = test->datasize - posn;
        if (len > inc)
            len = inc;
        cipher->encrypt(buffer + posn, test->plaintext + posn, len);
    }

    Serial.println("Vystup:\n");
    for(uint8_t i=0; i<sizeof(buffer); i++) printf("0x%X,",buffer[i]);

    return true;
}

void testCipher(AuthenticatedCipher *cipher, const struct TestVector *test)
{
    bool ok;

    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;

    Serial.print(test->name);
    Serial.print(" ... ");

    ok  = testCipher_N(cipher, test, test->datasize);
    /*ok &= testCipher_N(cipher, test, 1);
    ok &= testCipher_N(cipher, test, 2);
    ok &= testCipher_N(cipher, test, 5);
    ok &= testCipher_N(cipher, test, 8);
    ok &= testCipher_N(cipher, test, 13);
    ok &= testCipher_N(cipher, test, 16);*/

    if (ok)
        Serial.println("Passed");
    else
        Serial.println("Failed");
}

void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
{
    unsigned long start;
    unsigned long elapsed;
    int count;

    crypto_feed_watchdog();

    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;

    Serial.print(name);
    Serial.print(" SetKey ... ");

    start = micros();
    for (count = 0; count < 1000; ++count) {
        cipher->setKey(test->key, cipher->keySize());
        cipher->setIV(test->iv, test->ivsize);
    }
    elapsed = micros() - start;

    Serial.print(elapsed / 1000.0);
    Serial.print("us per operation, ");
    Serial.print((1000.0 * 1000000.0) / elapsed);
    Serial.println(" per second");
}

void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
{
    unsigned long start;
    unsigned long elapsed;
    int count;

    crypto_feed_watchdog();

    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;

    Serial.print(name);
    Serial.print(" Encrypt ... ");

    cipher->setKey(test->key, cipher->keySize());
    cipher->setIV(test->iv, test->ivsize);
    start = micros();
    for (count = 0; count < 500; ++count) {
        cipher->encrypt(buffer, buffer, 128);
    }
    elapsed = micros() - start;

    Serial.print(elapsed / (128.0 * 500.0));
    Serial.print("us per byte, ");
    Serial.print((128.0 * 500.0 * 1000000.0) / elapsed);
    Serial.println(" bytes per second");
}

void setup(){
    Serial.begin(115200);
    Serial.println("Začínam\n");
    GCM <AES256> *gcm=0;
    gcm= new GCM<AES256>();
    gcm->setKey(testVectorGCM.key, 32);
    gcm->setIV(testVectorGCM.iv,testVectorGCM.ivsize);
    crypto_feed_watchdog();
    //gcm->encrypt(buffer,testVectorGCM.plaintext,60);
    testCipher(gcm,&testVectorGCM);
    delete gcm;
    //gcm.encrypt(&vstupny,&vystup,49);
    crypto_feed_watchdog();
    Serial.println("Vystup: \n");
    for(uint8_t i=0; i<50;i++) Serial.print(buffer[i]);
}

void loop(){
    delay(1000);
    Serial.print(".");
}

I need to be able to get the text on PHP side

It is not recommended to use ECB mode. GCM is currently state of art.

install the library using your Arduino IDE, then look at the examples ... some of them show encrypt and decrypt functions ..... or look at the examples online https://github.com/OperatorFoundation/Crypto .... that link is found in the library.json file

your avatar is ironic, considering the textbook image used to show why ECB sucks.

@Kwasmich and dandavis I take your Point

@jsotola I have seen them, but I still do not understand them where the output comes and what is what

Kind of an answer. After the advise from @Kwasmich I have converted to aes-256-gcm.
Upgraded to UTF-8 thanks to Edgar Bonet

Based mostly on GCMTest example comming with Arduino Cryptograhy libraries:-----------------------------------------------
Shared between Encryption and decrition
First need to define parts which will not change:
#include <Crypto.h>
#include <AES.h>
#include <GCM.h>

struct TestVector
{
    const char *name;
    uint8_t key[32];
    uint8_t authdata[20];
    uint8_t iv[12];
    uint8_t tag[16];
    size_t authsize;
    size_t tagsize;
    size_t ivsize;
};

static TestVector const testVectorGCM PROGMEM = {   //Dont forget to change the Values here
    .name        = "AES-256 GCM",
    .key         = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
                    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08},
    .authdata    = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xab, 0xad, 0xda, 0xd2},
    .iv          = {0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
                    0xde, 0xca, 0xf8, 0x88},
    .tag         = {0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68,
                    0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b},
    .authsize    = 20,
    .tagsize     = 16,
    .ivsize      = 12
};

Now create a global buffer (easier to understand then pointers)
TestVector testVector;
byte buffer[128];

Encrytpion
{
    memcpy_P(&testVector, test, sizeof(TestVector));   //Load into memory
    test = &testVector;
    size_t posn, len;
    uint8_t tag[16];
    crypto_feed_watchdog();    //To protect from Watchdog reseting this function

It is important to clear memory of any ciphering before doing one:
    cipher->clear();

    cipher->setKey(test->key, cipher->keySize());    //Setting key
    cipher->setIV(test->iv, test->ivsize);     //vector
    for (posn = 0; posn < test->authsize; posn += datasize) {
        len = test->authsize - posn;
        if (len > datasize)
            len = datasize;
        cipher->addAuthData(test->authdata + posn, len);   //To make hack even more confusing
    }
    Serial.print("input numbers ");
    for(uint8_t i=0; i<60; i++) Serial.printf("%X",buffer[i]);
    Serial.println();

    for (posn = 0; posn < datasize; posn += datasize) {
        len = datasize - posn;
        if (len > datasize) len = datasize;
        crypto_feed_watchdog();
        cipher->encrypt((uint8_t*)buffer + posn, buffer + posn, len); //This is why we are here
        crypto_feed_watchdog();
    }
    Serial.println("Vystup:\n");
    for(uint8_t i=0; i<sizeof(buffer); i++) printf("0x%X,",buffer[i]);
}

Decryption
void decrypt(AuthenticatedCipher *cipher, const struct TestVector *test, size_t datasize){
    bool ok;

    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;
    size_t posn, len;
    uint8_t tag[16];
    crypto_feed_watchdog();
    cipher->clear();
    cipher->setKey(test->key, cipher->keySize());
    cipher->setIV(test->iv, test->ivsize);
    for (posn = 0; posn < test->authsize; posn += datasize) {
        len = test->authsize - posn;
        if (len > datasize)
            len = datasize;
        cipher->addAuthData(test->authdata + posn, len);
    }

    for (posn = 0; posn < datasize; posn += datasize) {
        len = datasize - posn;
        if (len > datasize)
            len = datasize;
        cipher->decrypt((uint8_t*)buffer + posn, buffer + posn, len);
    }

    Serial.print("\nVystup: ");
    for(uint8_t i=0; i<60;i++) Serial.printf("%c",(char)buffer[i]);
    Serial.println();

}

-----------------------------------------------
full example:
/*Skúška kódovania GCM-AES256#*/

#include <Crypto.h>
#include <AES.h>
#include <GCM.h>

struct TestVector
{
    const char *name;
    uint8_t key[32];
    uint8_t authdata[20];
    uint8_t iv[12];
    uint8_t tag[16];
    size_t authsize;
    size_t tagsize;
    size_t ivsize;
};

static TestVector const testVectorGCM PROGMEM = {
    .name        = "AES-256 GCM",
    .key         = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
                    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08},
    .authdata    = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                    0xab, 0xad, 0xda, 0xd2},
    .iv          = {0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
                    0xde, 0xca, 0xf8, 0x88},
    .tag         = {0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68,
                    0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b},
    .authsize    = 20,
    .tagsize     = 16,
    .ivsize      = 12
};

TestVector testVector;
byte buffer[128];

void encrypt(AuthenticatedCipher *cipher, const struct TestVector *test, size_t datasize)
{
    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;
    size_t posn, len;
    uint8_t tag[16];
    crypto_feed_watchdog();

    cipher->clear();
    cipher->setKey(test->key, cipher->keySize());
    cipher->setIV(test->iv, test->ivsize);
    for (posn = 0; posn < test->authsize; posn += datasize) {
        len = test->authsize - posn;
        if (len > datasize)
            len = datasize;
        cipher->addAuthData(test->authdata + posn, len);
    }
    Serial.print("Cisla: ");
    for(uint8_t i=0; i<60; i++) Serial.printf("%X",buffer[i]);
    Serial.println();

    for (posn = 0; posn < datasize; posn += datasize) {
        len = datasize - posn;
        if (len > datasize) len = datasize;
        crypto_feed_watchdog();
        cipher->encrypt((uint8_t*)buffer + posn, buffer + posn, len);
        crypto_feed_watchdog();
    }
    Serial.println("Vystup:\n");
    //      Decrypt
    for(uint8_t i=0; i<sizeof(buffer); i++) printf("0x%X,",buffer[i]);
}

void decrypt(AuthenticatedCipher *cipher, const struct TestVector *test, size_t datasize){
    bool ok;

    memcpy_P(&testVector, test, sizeof(TestVector));
    test = &testVector;
    size_t posn, len;
    uint8_t tag[16];
    crypto_feed_watchdog();
    cipher->clear();
    cipher->setKey(test->key, cipher->keySize());
    cipher->setIV(test->iv, test->ivsize);
    for (posn = 0; posn < test->authsize; posn += datasize) {
        len = test->authsize - posn;
        if (len > datasize)
            len = datasize;
        cipher->addAuthData(test->authdata + posn, len);
    }

    for (posn = 0; posn < datasize; posn += datasize) {
        len = datasize - posn;
        if (len > datasize)
            len = datasize;
        cipher->decrypt((uint8_t*)buffer + posn, buffer + posn, len);
    }

    Serial.print("\nVystup: ");
    for(uint8_t i=0; i<60;i++) Serial.printf("%c",(char)buffer[i]);
    Serial.println();

}

void setup(){
    Serial.begin(115200);
    Serial.println("Začínam\n");
    GCM <AES256> *gcm=0;
    gcm= new GCM<AES256>();
    gcm->setKey(testVectorGCM.key, 32);
    gcm->setIV(testVectorGCM.iv,testVectorGCM.ivsize);
    crypto_feed_watchdog();
    memset(buffer, (int)'\0', sizeof(buffer));
    char vstup[30]="Tak to netuším";     //String containing Non-Asci characters
    for(uint8_t i=0; i<30; i++) buffer[i]=vstup[i];
    encrypt(gcm,&testVectorGCM,30);
    decrypt(gcm,&testVectorGCM,30);
    delete gcm;
}

void loop(){
    delay(1000);
    Serial.print(".");
}

simple key generator in PHP:
<?php
for($i=0;$i<31;$i++) printf("0x%X,",random_int(0,0xFF));
printf("0x%X\n",random_int(0,0xFF));
?>

Re “I could not figure-out how to convert utf-8 into numbers”: an UTF-8 text is a sequence of 8-bit numbers. There is nothing to convert.

I understand that, but the problem is that when one (char)letter get translated to multiple numbers (uint8_t), then converting it back require more then one conversion per number: needs to know how many numbers apply now and as such I could not convert numbers back to char letters

the important part is "and back" as while I could decrypt to the same int array I could not convert the int array into char array with utf-8 letters.

A char is not a letter (or a character): it's a byte. It synonym of int8_t and differs from uint8_t only by signedness. UTF-8 encodes each character as a sequence of 8-bit “code units”, thus an UTF-8 encoded text is just a byte array. You don't care about having a variable-width encoding unless you are converting to/from some form of wide characters. You can encrypt and decrypt the text with the same ease as ASCII text. If it happens to be stored as an array of char, you encrypt it with cipher->encrypt((uint8_t*)buffer...) and recover it with cipher->decrypt((uint8_t*)buffer...).

What are the authdata, iv and tag parameters for? And should I change them too?

I will start with tag witch is something like a check When Decrypting. (think of it same as a checksum), SO it is send with the data from server to client
IV and Auth data are handy way to to make same input generate different output even with the same key. the benefits it has against the brute force attack alone are enormous (there are many others) I generate authdata each time on both sides under same algoritm and send the IV from client. Un

at early stages you can keep auth as constant. (create your own),
same as in case of IV (but this was first one of the two I have generated)

@Tomas Can you show an example of how you decrypt this with php?

Mine decryption routine is too complicated, but I have been using php-aes-gcm composer plugin from spomky-labs, which does contain examples.
https://github.com/Spomky-Labs/php-aes-gcm

you will find a composer in your main hosting panel from any competent  webhosting company, if like me you are using less competent webhosting for developement (planethippo) and more competent for deployment, then you might have to either get a lamp and install composer there or just copy the composer directories from better managed server.
As a warning that when you are converting key, auth and tag from C++ approach to php approach, make sure the variable uses it always as string and that you use each number with two digits (without leading 0x).

Using your arduino sketch then feeding the params into openssl_decrypt (with hex2bin()), or that lib, does not produce any result. I'm sure you'd help the community if you'd make a tutorial/video about the whole cycle (encrypt/decrypt on the arduino then encrypt/decrypt on the web server) as there are absolutely zero meaningfull practical examples on the internet.

I am disgraphic myself, so major tutorial in written form even about how to butter a bread is out of mine league.
Next weak I have inspector inspection (mine inspecting is inspected), so Ill do a video tutorial in two weeks time.  I have wanted to start youtube channel for a while, but mine last attempt died when lorry drow (i know its driven, but it sounds like there should be a past perfect short for being driven) over mine phone just after filming... Sorry, cant do it sooner as I want Rob W to pass me...


text:
How to use this 3-pin slide-switch?
I purchased these slide switches recently because I want to use them in my project:

But I don't know what to connect up the three pins to and I can't find any information about them online. Nor can I find sample code. Can someone show me how to wire them up and provide sample code please? I just want to be able to check their state and save it as a boolean variable.

As stated by the seller, this is an SPDT switch.

Sample code? For how to read a switch? Really?

It has only two positions and three pins. You'll figure out how it works in less than a minute if you use a multimeter in the continuity test setting. You dou have one, do you? If you don't, I really recommend adding one to your shopping basket next time.

for use purely by sensing with an MCU, you can ignore or even cut off one of the outer pins, then it's just a button (logically), for which examples abound.

As others have said, this is a SPDT (Single Pole Double Throw) switch. The center pin is common. When the switch is slid one way, it connects the center pin to the outer pin on that side. When you slide the switch the other way, it connects the center pin to the opposite outer pin.
See the link about SPDT switches Edgar provided in his comment: http://www.learningaboutelectronics.com/Articles/What-is-a-single-pole-double-throw-switch-SPDT
That explains these switches pretty clearly.
Connect the center pin of the switch to a digital pin on your Arduino. Connect one of the outer pins to ground.
Set the digital pin on the Arduino to INPUT_PULLUP mode.
Then, when you read the digital state of the pin, if it is LOW, the switch is closed. (Slid to the side of the pin you attached to ground.)

Since it has two possible positions, and three pins, you have a connection between pin 1 and 2 when position 1 is selected, and between pin 1 and 3 when position 2 is selected.
As BSF comment below, the middle pin is common. This means it will be connected to the outside pins depending on where the switch is moved towards.
However, you always can verify this, in the following way:
To find out which is pin 1, pin 2 and pin 3, use a multimeter. Set it to continuity mode, or if you don't have that mode, to resistance mode, and put the red/black probes to two random pins. Check if the resistance is 0, in that case you know those two pins will be connected when position 1 is selected. If there is resistance, try other pins with the probes, until you know which are connected.
Than switch the position, and do it again.
Instead of a multimeter you also can 'hardwire'  it to e.g. 5V -> pin 1, pin 2 -> resistor of 220 ohm -> LED -> GND
And change pin 1 and pin 2 until you found which pins connect for which positions.
To use it for you Arduino, check for a normal simple switch example, but these are two in one, one for each position.

Pin 2, the one in the middle is always the common. There is no other possibility with a slider switch.

@SBF Ok ... my test would have the same conclusion, but good to know it's always the middle position which is common.

The key point of Michel's post is that you can figure out how a switch is wired in a few minutes with a multimeter. This is a very good skill to develop.

@DuncanC thanks for the compliment ... upvoted your answer as it confirmed something I thought should work (pullup + LOW active), assuming on MCU's with internal pulldown resistors HIGH active should work equally).

These switches are actually a lot more simple than you would think.  You have two positions, but you really only care about one of them, since if it's not in that position it must be in the other.
The central pin is the common, and when slid to one side it connects that central pin to the corresponding outside pin.
So you just use the central pin and one of the outside pins as if it were any other on-off switch or pushbutton.  Treat it exactly the same (pullup resistor, which may be internal to the AVR chip, and connect the IO pin to ground through the switch).
If it's "on" (reads LOW) then the switch is in one position. If it's "off" (reads HIGH) then it's in the other position.
The only times you'd want to use both outer pins is if you were wanting to direct a signal to one of two different places, or select between two different incoming signals, or if the switch were a "3 position on-off-on" switch where it can be in either outer position or neither position (centre off), in which case you treat it as two separate switches, but with a common central pin that connects both to ground.

So which pin should I connect to ground?
And which pin should I connect to Arduino's #2 to get this sketch to work? http://man.hubwiz.com/docset/Arduino.docset/Contents/Resources/Documents/www.arduino.cc/en/Tutorial/DigitalInputPullup.html

Connect any pin to ground. The pin you connect to #2 depends which pin you connected to ground. If you connected the central pin to ground then use one of the outside pins. If you connected one of the outside pins to ground then connect the central pin to #2. All the switch consists of is three metal posts and a sliding lump of metal that moves across the top of those posts.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
multiple button switch case how to add counter
I want to add counter in multiple-buttons-to-control-relays:
int relay1 = 23;
int relay2 = 25;
int relay3 = 27;
int relay4 = 29;
int relay5 = 31;
int relay6 = 33;
int relay7 = 35;
int relay8 = 37;
////////////int mode =  41;      // select mode AUTO/MANUAL if HIGH=AUTO else LOW=MANUAL
//////////int start = 43;      // select START/STOP auto mode
//////////int tool =  13;       // tool Sw
int sw1 = 2;
int sw2 = 4;
int sw3 = 6;
int sw4 = 8;

int sw5 = 3;  //additional
int sw6 = 5;
int sw7 = 7;
int sw8 = 9;
 ////////int modeState =  1;  //modelState for //start stop and tool
/////////int startState = 1; //start1State
/////////int toolState =  1;
int sw1Status, sw2Status, sw3Status, sw4Status,sw5Status, sw6Status, sw7Status, sw8Status;
int order = 1; // default is to set all relays off.
/// for counter//////////////////////////////////////////////////
int val = 0; // variable for reading the pin status
int counter = 0;
int currentState = 0;
int previousState = 0;
//// for counter ////////////////////////////////////////////////
void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);
  pinMode(relay7, OUTPUT);
  pinMode(relay8, OUTPUT);

  // input pull-up, so default state of buttons is HIGH.
  pinMode(sw1, INPUT_PULLUP);
  pinMode(sw2, INPUT_PULLUP);
  pinMode(sw3, INPUT_PULLUP);
  pinMode(sw4, INPUT_PULLUP);
  pinMode(sw5, INPUT_PULLUP);  ///additional
  pinMode(sw6, INPUT_PULLUP);
  pinMode(sw7, INPUT_PULLUP);
  pinMode(sw8, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  // read the buttons status
  sw1Status = digitalRead(sw1);
  sw2Status = digitalRead(sw2);
  sw3Status = digitalRead(sw3);
  sw4Status = digitalRead(sw4);
  sw5Status = digitalRead(sw5);
  sw6Status = digitalRead(sw6);
  sw7Status = digitalRead(sw7);
  sw8Status = digitalRead(sw8);
  ///// tool state example
  ////if (sw1Status == LOW && sw2Status == LOW && sw3Status == LOW && sw4Status == LOW && toolState == HIGH) { //

  if (sw1Status == LOW && sw2Status == LOW && sw3Status == LOW && sw4Status == LOW) { // if button 1 pressed
    order = 2; // go to case 2 start up state
    Serial.println("all switch off follow order 2");
  }
  if (sw1Status == HIGH && sw2Status == LOW && sw3Status == LOW && sw4Status == LOW) { // if button 2 pressed
    order = 3;// go to case 3
     Serial.println("order 3 sw 1 relay 1 on");
  }
  if (sw1Status == HIGH && sw2Status == LOW && sw3Status == LOW && sw4Status == HIGH) { // if button 3 pressed
     //// possible if (order != 5) { // if order is not equal 5 turn on relay 1 & 4
    order = 4;// go to case 4
    Serial.println(" order 4 sw 1,4 relay 1,7,8 on");

  }
  if (sw1Status == HIGH && sw2Status == LOW && sw3Status == HIGH && sw4Status == LOW) { // if button 3 pressed
    order = 5;// go to case 5
    Serial.println(" order 5 sw 2 3 on relay 1on now right down");
  }
  if (sw1Status == LOW && sw2Status == HIGH && sw3Status == HIGH && sw4Status == LOW) { // if button 3 pressed
    order = 6;// go to case 6
    Serial.println(" order 6 sw 3 and 2 on");
  }
  if (sw1Status == LOW && sw2Status == HIGH && sw3Status == LOW && sw4Status == HIGH) { // if button 2 pressed
    order = 7;// go to case
     Serial.println(" order 7 ");
  }
  if (sw1Status == LOW && sw2Status == HIGH && sw3Status == HIGH && sw4Status == LOW && sw7Status == LOW) { //
    order = 8;// go to case
    Serial.println("orde 8 left side ");
  }
  if (sw1Status == LOW && sw2Status == LOW && sw3Status == LOW && sw8Status == HIGH && sw8Status == LOW) { //
    order = 9;// go to case
    Serial.println(" spare order 9 sw 4 high");
  }

  if (sw1Status == LOW && sw2Status == LOW && sw3Status == LOW && sw4Status == LOW) { // button 4 toggle
    if (order != 10) { // if order is not equal
      Serial.println("  spare order 10 all off ");

      order = 10;
    }
    else { // else go to case 1, which will turns off all relays
      order = 1;
    }
  }

  switch (order) {
    case 1: // if order equals 1
    delay(200);
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println("default start up ");

      break;

    case 2: // if order equals 2
    delay(1000);
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println("move up case 2");

      break;

    case 3: // if order equals 3
      delay(1000);
      digitalWrite(relay1, HIGH);
      digitalWrite(relay2, LOW);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println("move right case 3");

      break;{

    case 4: // if order equals 4
      delay(1000);
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, LOW);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, LOW);
      digitalWrite(relay8, LOW);
      Serial.println("move down case 4");
      break;}

    case 5: // if order equals 5
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, LOW);
      digitalWrite(relay8, LOW);
      Serial.println("move down  case 5");
      break;
      case 6: // if order equals 6
      delay(1000);
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println("move up case 6");
      break;
          case 7: // if order equals
          delay(1000);
          val = digitalRead(sw2Status); // read input value
          if (val == HIGH)  // check if the input is HIGH (button released)
            currentState = 1;
            if(currentState != previousState)
            if(currentState == 1)
              counter = counter + 1;
              else
              case 10: // if order equals
            Serial.println(counter);

      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);

      Serial.println("move up rly 1");
      break;

    case 8: // if order equals
    delay(1000);
      digitalWrite(relay1, LOW);
      digitalWrite(relay2, HIGH);
      digitalWrite(relay3, LOW);
      digitalWrite(relay4, HIGH);
      digitalWrite(relay5, HIGH);
      digitalWrite(relay6, HIGH);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println("new left side");
      break;
      case 9: // if order
      delay(1000);
      digitalWrite(relay1, HIGH);
      digitalWrite(relay2, LOW);
      digitalWrite(relay3, HIGH);
      digitalWrite(relay4, LOW);
      digitalWrite(relay5, LOW);
      digitalWrite(relay6, LOW);
      digitalWrite(relay7, HIGH);
      digitalWrite(relay8, HIGH);
      Serial.println(" on right side");
      break;

    default: // if none of above cases is valid go to case 1
      order = 1;
      break;
  }
}

multiple button switch case for multiple relays

Align your code by selecting it and press ctrl-k  You added the counter already in your code. So I guess the counter does not count what you want, and we cannot guess. Describe what you expect counter to count, and what you see now.

I'm confused. Your buttons are set up using INPUT_PULLUP, so reading a low from a button should indicate that it's pressed. You have code like this: if (sw1Status == LOW && sw2Status == LOW && sw3Status == LOW && sw4Status == LOW) with a comment of "if button 1 pressed". I read that code to say "if switches 1, 2, 3, and 4 are all pressed...". Why do you interrogate the state of all your buttons to determine which case to execute in your switch statement? Is it valid for the user to press multiple buttons at the same time to get to different relay states?

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Parola library and DS3231 clock
EDIT2: Well, this is the "new" code and it just displays random dots on the matrices:
#include "RTClib.h"

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 16
#define NUM_ZONES 4

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins
// MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

RTC_DS3231 rtc;
#define SPEED_TIME  25
#define PAUSE_TIME  1000

uint8_t lastUpdateMin = 0;
uint8_t lastUpdateHour = 0;

// Global variable
const char  *pc[NUM_ZONES] =
{
  "TIME",
  "ROOM", //0
  "SUBJECT", //1
  "GRADE",//2
  };

void setup(void)
{

  P.begin();
    if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

 if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
//     If the RTC have lost power it will set the RTC to the date & time this sketch was compiled in the following line
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
 }

 P.begin(4);
  P.setZone(0, 0, 3); // Modules 0-3 in zone 0
  P.setZone(1, 4, 7); // Modules 4-7 in zone 1
  P.setZone(2, 8, 11); // Modules 8-11 in zone 2
  P.setZone(3, 12, 15); // Modules 12-15 in zone 3
}

void loop(void)
{

 DateTime now = rtc.now();
 if(lastUpdateMin != now.minute()){  // change every minute
     lastUpdateMin = now.minute();
 char buffer[] = "hh:mm";  // hours and minutes
P.displayZoneText(0,now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);

    if(lastUpdateHour != now.hour()){     // change every hour
     lastUpdateHour = now.hour();
   P.displayZoneText(1, pc[1], PA_RIGHT, 0, 0, PA_PRINT, PA_NO_EFFECT);
   P.displayZoneText(2, pc[2], PA_LEFT, 0, 0, PA_PRINT, PA_NO_EFFECT);  // display
   P.displayZoneText(3, pc[3], PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
   }
  }
   P.displayAnimate();
}

If I do just this:
void loop(void)
{

 DateTime now = rtc.now();
 if(lastUpdateMin != now.minute()){  // change every minute
     lastUpdateMin = now.minute();
 char buffer[] = "hh:mm";  // hours and minutes
P.displayZoneText(0,now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
 }
     if(lastUpdateHour != now.hour()){     // change every hour
     lastUpdateHour = now.hour();

     }

   P.displayAnimate();
}

it displays the hour:minutes.
the problem is it doesn't display the time and the other text at the same time
EDIT: If I execute this code below it will display text on the first row, but my question will be how do I manage to display simultaneously the hour:minutes from the clock and some text of my choice?
void setup(void)
{

  P.begin();
    if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

 if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
//     If the RTC have lost power it will set the RTC to the date & time this sketch was compiled in the following line
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
 }

 P.begin(4);
  P.setZone(0, 0, 3); // Modules 0-3 in zone 0
  P.setZone(1, 4, 7); // Modules 4-7 in zone 1
  P.setZone(2, 8, 11); // Modules 8-11 in zone 2
  P.setZone(3, 12, 15); // Modules 12-15 in zone 3

for (uint8_t i=0; i<NUM_ZONES; i++)
    P.displayZoneText(0, pc[0], PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);

}

void loop(void)
{

 DateTime now = rtc.now();
 char buffer[] = "hh:mm";  // hours and minutes

// P.displayZoneText(0,now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);  // display
//if(now.hour()==12){
//  P.displayZoneText(0, pc[0], PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
//}
//else{
 // P.displayZoneText(1, pc[1], PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);  // display
//}
   P.displayAnimate();
}

I have multiple FC-16 modules based on MAX7219 led matrices and I want to use them and a real time clock, DS3231, to build a classroom timetable.
I want the led modules to operate on the concept of zones. I want to create a zone for each row of text (a zone is a contiguous block of modules forming a kind of sub-display) as I did here: P.begin(4); // We will have 4 zones
P.setZone(0, 0, 3); // Modules 0-3 in zone 0
P.setZone(1, 4, 7); // Modules 4-7 in zone 1
P.setZone(2, 8, 11); // Modules 8-11 in zone 2
P.setZone(3, 12, 15); // Modules 12-15 in zone 3
I want this in order to be able to change the text in a row without modifying other rows.
Then, I need to use the hour and minutes from the DS3231 clock module and to display it on the first row of the display and on the other 3 rows I need to display the number of the room, the subject and the class.
const char  *pc[NUM_ZONES] =
{
  "ROOM",
  "SUBJECT",
  "CLASS",};

And when the hour on the first row changes, the text from the other 3 rows should change simultaneously.It's a pretty simple concept but I can't seem to know how to manage this.
This is all the code I gathered up until now:
#include "RTClib.h"

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 16
#define NUM_ZONES 4

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins
// MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
RTC_DS3231 rtc;
#define SPEED_TIME  25
#define PAUSE_TIME  1000

// Global variables
uint8_t  curText;
const char  *pc[NUM_ZONES] =
{
  "ROOM",
  "SUBJECT",
  "CLASS",

};

void setup(void)
{

  P.begin();
    if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

 if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
//     If the RTC have lost power it will set the RTC to the date & time this sketch was compiled in the following line
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
 }

 P.begin(4);
  P.setZone(0, 0, 3); // Modules 0-3 in zone 0
  P.setZone(1, 4, 7); // Modules 4-7 in zone 1
  P.setZone(2, 8, 11); // Modules 8-11 in zone 2
  P.setZone(3, 12, 15); // Modules 12-15 in zone 3

}

void loop(void)
{

 DateTime now = rtc.now();
 char buffer[] = "hh:mm";  // hours and minutes

   for (uint8_t i=0; i<NUM_ZONES; i++)
    P.displayZoneText(i-1, now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);

P.displayAnimate();
}

I dont't know how to display the hour:minutes and my text simultaneously so that I can do the rest then:
{

 DateTime now = rtc.now();
 char buffer[] = "hh:mm";  // hours and minutes

   for (uint8_t i=0; i<NUM_ZONES; i++)
    P.displayZoneText(i-3, now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); //here I want to display the hour:minutes on the first row

P.displayAnimate();
}```

edited just now

I know how to display text in all 4 zones. I don't know how to display the real time from the clock module and some other text simultaneously

I know how to do that too

What is the Parola library? Where can your readers find this library? What kind of display does it work with? Briefly, what does it do for you? You should provide all of that information in your question.

Parola is mainly used to format text on led matrix displays. My FC16 led modules are based on MAX7219 led matrices. I can't really figure out how to display the time properly and a message simultaneously

you say that you can convert time to text, and you can display text in all zones, then what is actually the problem? ... i am just not able to understand with what you are having a problem ... please add some pictures that show the problem

Your first problem is the definition of the text array:
const char  *pc[NUM_ZONES] =
{ "TIME",      // This will be replaced by real time its only for logic of rows here
  "ROOM",
  "SUBJECT",
  "CLASS"
};

then we need a timer, as you only show minutes we only have to change time in that interval to prevent flicker and other unwanted effects and hour for changing texts lateron:
uint8_t lastUpdateMin = 0;
uint8_t lastUpdateHour = 0;

Then the loop looks like
void loop() {
   DateTime now = rtc.now();
   if(lastUpdateMin != now.minute()){  // change every minute
     lastUpdateMin = now.minute();
     char buffer[] = "hh:mm";  // hours and minutes
     P.displayZoneText(0, now.toString(buffer), PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); // 1st row
    }
  if(lastUpdateHour != now.hour()){     // change every hour
     lastUpdateHour = now.hour();
     P.displayZoneText(1, pc[1], PA_RIGHT, 0, 0, PA_PRINT, PA_NO_EFFECT);  // 2nd row
     P.displayZoneText(2, pc[2], PA_LEFT, 0, 0, PA_PRINT, PA_NO_EFFECT);   // 3rd row
     P.displayZoneText(3, pc[3], PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); // 4th row
    }
P.displayAnimate();
}

Things to do - define a two dimensional char table with all the texts needed for the different hours
#define NUM_LECTUREHOURS 7       // seven hour lectures
const char *pc[NUM_LECTUREHOURS][NUM_ZONES] = {
  { "TIME", "ROOM_0", "SUBJECT_0", "CLASS_0"},
  { "TIME", "ROOM_1", "SUBJECT_1", "CLASS_1"},
  { "TIME", "ROOM_2", "SUBJECT_2", "CLASS_2"},
  ...
  { "TIME", "ROOM_Last", "SUBJECT_Last", "CLASS_Last"}  // _Last in this example = 6
};

and display with a variable based on the hour so if your classes start at 9:00 you would use
uint8_t lectureHour= now.hour() - 9; // as arrays start with 0

and your different texts would be called like
  P.displayZoneText(1, pc[lectureHour][1], PA_RIGHT, 0, 0, PA_PRINT, PA_NO_EFFECT); // 2nd row

If your char array gets to large - depending on the texts - you will have to use PROGMEM which is an issue on its own. Hope this gets you started
EDIT
If you want to change every two hours you do
if(lastUpdateHour != now.hour() && lastUpdateHour +1 != now.hour() ){     // change every two hours
or shorter for a variable timespan (e.g. every three hours)
 if( now.hour() - lastUpdateHour > 3 ){     // change every three hours

I did everything you showed me until the char table and it isn't working. It's not displaying anything. And when I build the char table it says "conflicting declaration 'const char* pc [7][4['  "

Please start with the simple example and test, do not go for the full solution before you make the simple work. And edit your question and show the "new" code

just did that  .

I guess you just start your ESP so hour returned = 0 so either wait one hour (just kidding) or set the initial hour to 99 and use my original loop as in your program is an errorinbrackets!

god bless! and if I want to change the text every 2 hours?

See my edit in the answer

It works. Tell me, why the same code that worked a minute ago is not working at all the next moment when I upload? Eg. it worked just fine awhile ago and now it doesn't display anything at all or it displays just some random dots. Without doing hardware changes. Is this a hardware problem or a soft one? The same code, the same untouched hard connections. Sometime works, sometime won't.

Might be an issue with the used libraries, lookup issue tracker of the libs on github for similar problems/solutions. Link can be found in library manager of the IDE under the used lbrary


text:
LSM303 unable to compensate tilt
I am working on a tilt compensated compass with the LSM303DLHC. I manage to calibrate the magnetic and accelero part. I managed to calculate pitch and roll. But when it comes to calculate tilt compensation for the compass something goes wrong. My values on the serial monitor just don't make sense...If I calculate compass heading from calibrated magneto values it works using the Y axis as the magnetic vector. How would I be able to change that to the X-axis?
   LSM303DLHC including accelerometer to correct for position

*/
#include <Wire.h>

const int LSM303_ADDR = 0x19;
const int LSM303_ADDR_MAG = 0x1E;

//control register addresses
const int CTRL_REG1_ADDR = 0x20;
const int CTRL_REG2_ADDR = 0x21;
const int CTRL_REG3_ADDR = 0x22;
const int CTRL_REG4_ADDR = 0x23;
const int CTRL_REG5_ADDR = 0x24;
const int CTRL_REG6_ADDR = 0x25;

const int CRA_REG_M_ADDR = 0x00;
const int CRB_REG_M_ADDR = 0x01;
const int MR_REG_M_ADDR = 0x02;

//data register addresses LSB first for accelero, MSB first for magneto
const int Accelero_First_data_addr = 0x28;
const int Magneto_First_data_addr = 0x03;
const int Temp_out_data_addr = 0x31;//MSB first 12 bits

int CTRL_REG1_A_value = 0x47; //50Hz Low Power disable, all axes enabled
int CTRL_REG2_A_value = 0x00; //
int CTRL_REG3_A_value = 0x00; //no interrupt or watermark enabled
int CTRL_REG4_A_value = 0x00; // 2G full scale, high resolution, no SPI mode selected
int CTRL_REG5_A_value = 0x00; // nothing enabled
int CTRL_REG6_A_value = 0x00; // nothing enabled

int CRA_REG_M_value = 0x14; // Temp sens enabled, 15Hz data rate output
int CRB_REG_M_value = 0x20; // 2G is data 1100 - 980 G/LSB

int MR_REG_M_value = 0x00;

int16_t MagRaw_X_axis_value, MagRaw_Y_axis_value, MagRaw_Z_axis_value;
int16_t AccRaw_X_axis_value, AccRaw_Y_axis_value, AccRaw_Z_axis_value;

float pitch, roll;
float Xm_calibrated, Ym_calibrated, Zm_calibrated;
float Xm_norm, Ym_norm, Zm_norm, Xa_norm, Ya_norm, Za_norm;
float norm_a, norm_m;

void setup() {// put your setup code here, to run once:
  Serial.begin(9600);
  Wire.begin();
  setupLSM303();
  Serial.println("Setup complete..");
}

void loop() {// put your main code here, to run repeatedly:
  MagnetoDataRead();
  AcceleroDataRead();

}

void setupLSM303() {
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG1_ADDR);
  Wire.write(CTRL_REG1_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG2_ADDR);
  Wire.write(CTRL_REG2_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG3_ADDR);
  Wire.write(CTRL_REG3_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG4_ADDR);
  Wire.write(CTRL_REG4_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG5_ADDR);
  Wire.write(CTRL_REG5_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG6_ADDR);
  Wire.write(CTRL_REG6_A_value);
  Wire.endTransmission();
  //magneto register setup
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(CRA_REG_M_ADDR);
  Wire.write(CRA_REG_M_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(CRB_REG_M_ADDR);
  Wire.write(CRB_REG_M_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(MR_REG_M_ADDR);
  Wire.write(MR_REG_M_value);
  Wire.endTransmission();
}

void MagnetoDataRead() {
  Wire.beginTransmission(LSM303_ADDR_MAG );
  Wire.write(Magneto_First_data_addr);
  Wire.endTransmission();

  Wire.requestFrom(LSM303_ADDR_MAG , 6);
  if (Wire.available() <= 6) {
    MagRaw_X_axis_value = (Wire.read() << 8) | (Wire.read());
    MagRaw_Y_axis_value = (Wire.read() << 8) | (Wire.read());
    MagRaw_Z_axis_value = (Wire.read() << 8) | (Wire.read());

    float X_mag_cal = MagRaw_X_axis_value / 1100.0;
    float Y_mag_cal = MagRaw_Y_axis_value / 1100.0;
    float Z_mag_cal = MagRaw_Z_axis_value / 980.0;

    //input calibration factors
    float Xm_offset_corrected = MagRaw_X_axis_value - 0.041423;
    float Ym_offset_corrected = MagRaw_Y_axis_value + 0.022448;
    float Zm_offset_corrected = MagRaw_Z_axis_value + 0.078354;

    Xm_calibrated = 94.622065 * Xm_offset_corrected + 0.831029 * Ym_offset_corrected + 1.647934 * Zm_offset_corrected;
    Ym_calibrated = 0.831029 * Xm_offset_corrected + 93.462985 * Ym_offset_corrected + 3.416885 * Zm_offset_corrected;
    Zm_calibrated = 1.647934 * Xm_offset_corrected + 3.416885 * Ym_offset_corrected + 76.477691 * Zm_offset_corrected;

    //normalization formula
    norm_m = sqrt(sq(Xm_calibrated) + sq(Ym_calibrated) + sq(Zm_calibrated));
    Xm_norm = Xm_calibrated / norm_m;
    Ym_norm = Ym_calibrated / norm_m;
    Zm_norm = Zm_calibrated / norm_m;

    //float Heading = ((atan2(Ym_calibrated, Xm_calibrated)) * 180 ) / PI;
    //if (Heading < 0) {
    //  Heading = 360 + Heading;
    //}
    /*
        Serial.print(X_mag_cal, 10);
        Serial.print("\t");
        Serial.print(Y_mag_cal, 10);
        Serial.print("\t");
        Serial.print(Z_mag_cal, 10);
        Serial.println("\t");
    */

    delay(50);

  }
}

void AcceleroDataRead() {
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(Accelero_First_data_addr | 0x80);
  Wire.endTransmission();

  Wire.requestFrom(LSM303_ADDR, 6);
  if (Wire.available() <= 6) {
    AccRaw_X_axis_value = (Wire.read()) | (Wire.read() << 8);
    AccRaw_Y_axis_value = (Wire.read()) | (Wire.read() << 8);
    AccRaw_Z_axis_value = (Wire.read()) | (Wire.read() << 8);

    float X_Acc =  (AccRaw_X_axis_value >> 4);
    float Y_Acc =  (AccRaw_Y_axis_value >> 4);
    float Z_Acc =  (AccRaw_Z_axis_value >> 4);
//calibrate offset
    float X_acc_offset_corrected = X_Acc + 6.676443;
    float Y_acc_offset_corrected = Y_Acc - 6.622381;
    float Z_acc_offset_corrected = Z_Acc + 55.408804;
//calibrate
    float X_acc_calibrated = 0.977273 * X_acc_offset_corrected - 0.003458 * Y_acc_offset_corrected + 0.012639 * Z_acc_offset_corrected;
    float Y_acc_calibrated = -0.003458 * X_acc_offset_corrected + 0.979674 * Y_acc_offset_corrected - 0.001130 * Z_acc_offset_corrected;
    float Z_acc_calibrated = 0.012639 * X_acc_offset_corrected - 0.001130 * Y_acc_offset_corrected + 0.897083 * Z_acc_offset_corrected;
//normalize
    norm_a = sqrt(sq(X_acc_calibrated) + sq(Y_acc_calibrated) + sq(Z_acc_calibrated));
    Xa_norm = X_acc_calibrated / norm_a;
    Ya_norm = Y_acc_calibrated / norm_a;
    Za_norm = Z_acc_calibrated / norm_a;

//calculate pitch and roll
    pitch = asin(-Xa_norm) * 180 / PI;
    roll = (atan2(Ya_norm, Za_norm) * 180) / PI;
//calculate vectors fo tilt compensation
    float Xh = Xm_norm * cos(pitch * PI / 180) + Zm_norm * sin(pitch * PI / 180);
    float Yh = Xm_norm * sin(roll * PI / 180) * sin(pitch * PI / 180) + Ym_norm * cos(roll * PI / 180) - Zm_norm * sin(roll * PI / 180) * cos(pitch * PI / 180);
//calculate tilt compensated heading
    float Heading = ((atan2(Yh, Xh)) * 180.0 ) / PI;
    if (Heading < 0) {
      Heading = 360 + Heading;
    }

    Serial.print("Pitch \t");
    Serial.print(pitch);
    Serial.print("\t roll \t");
    Serial.print(roll);
    Serial.print("\t");
    Serial.println(Heading);

    delay(50);
  }
}

I have been stuck on this for a few days now. Any help is appreciated!

Are you developing on an STMicro evaluation kit?  If so you might be able to you STMicro MotionMC and MotionEC (warning, no source code!) libraries to implement a tilt compass.  Ug, my bad, this the the Arduino forum - of course you are not using an STMicro development kit!

Also, are you wanting to tilt your compass beyond normal?  As in more than, say, 30 degrees?  If you want to hold you compass at 90 degrees WRT the local surface of the Earth you will have to do more than just implement a tilt compass.

Hi! Thank you for commenting! I am developing on a NodeMcu with the Arduino IDE. My compass will not be tilted beyond these  kind of angles. Formulas used in my code are ectracted from the lsm303 app notes

Hmm, in another STMicro chip (lsm9ds1) one of the acc vectors is backwards WRT the mag.  I don't think that's a problem here w/the LSM303.  Could you be having problem converting to/from degrees/radians?  That can get confusing depending on where you are getting your trig function from.  Also, (just curious) are you keeping everything in Euler angles or are you using quaternions?

I have read somewhere that for the dlhc that the earths magnetic field is aligned along the y axis. Let me re-check the source though. Eventhough its just partially the problem I have.

Shouldn't be.  In section 1.2 figure 2 of the LSM303DLHC specifications shows the lms303 acc X+ & mag X+ (as well as the acc Y+ & mag Y+) in the same direction.  Note, not all STMicro chips are so aligned.  Careful not to use the wrong chip's example code!

Sorry I got it wrong indeed. It is aligned along the X-axis. I have to point the Y-axis down to earth for it to get valid readings. Which in my case means putting the PCB vertical. My formula for heading is from the application notes though...

That does not sound correct.   All the STMicro compass examples I have see have X and Y axis parallel with the surface of the earth and Z perpendicular to the surface of the earth. (Side note, don't bring permanent magnets close to the magnetometer.  I did and had to dig out an old degaussing tool to get the magnetometer working again.)

I have to go back to the beginning I guess in this case. I will plot raw sensor values one at a time to see what is going on. It all just does not make sense to me yet.

Now that I got the magnetometer and the accelerometer working it seems that if I tilt the compass my headings still fluctuate...eventhough I apply the formulas from the App note....

It seems I got it working! Thanks a lot for your help, I owe you a few beers. For anyone following and interested here is my code:
   LSM303DLHC including accelerometer to correct for position
  Vector calculation to be implemented
*/
#include <Wire.h>
#include <Math.h>

const int LSM303_ADDR = 0x19;
const int LSM303_ADDR_MAG = 0x1E;

//control register addresses
const int CTRL_REG1_ADDR = 0x20;
const int CTRL_REG2_ADDR = 0x21;
const int CTRL_REG3_ADDR = 0x22;
const int CTRL_REG4_ADDR = 0x23;
const int CTRL_REG5_ADDR = 0x24;
const int CTRL_REG6_ADDR = 0x25;

const int CRA_REG_M_ADDR = 0x00;
const int CRB_REG_M_ADDR = 0x01;
const int MR_REG_M_ADDR = 0x02;

//data register addresses LSB first for accelero, MSB first for magneto
const int Accelero_First_data_addr = 0x28;
const int Magneto_First_data_addr = 0x03;
const int Temp_out_data_addr = 0x31;//MSB first 12 bits

int CTRL_REG1_A_value = 0x47; //50Hz Low Power disable, all axes enabled
int CTRL_REG2_A_value = 0x00; //
int CTRL_REG3_A_value = 0x00; //no interrupt or watermark enabled
int CTRL_REG4_A_value = 0x00; // 2G full scale, high resolution, no SPI mode selected
int CTRL_REG5_A_value = 0x00; // nothing enabled
int CTRL_REG6_A_value = 0x00; // nothing enabled

int CRA_REG_M_value = 0x14; // Temp sens enabled, 15Hz data rate output
int CRB_REG_M_value = 0x20; // 2G is data 1100 - 980 G/LSB

int MR_REG_M_value = 0x00;

int16_t MagRaw_X_axis_value, MagRaw_Y_axis_value, MagRaw_Z_axis_value;
int16_t AccRaw_X_axis_value, AccRaw_Y_axis_value, AccRaw_Z_axis_value;

float pitch, roll;
float Xm_calibrated, Ym_calibrated, Zm_calibrated;
float Xm_norm, Ym_norm, Zm_norm, Xa_norm, Ya_norm, Za_norm;
float norm_a, norm_m;

void setup() {// put your setup code here, to run once:
  Serial.begin(9600);
  Wire.begin();
  setupLSM303();
  Serial.println("Setup complete..");
}

void loop() {// put your main code here, to run repeatedly:
  MagnetoDataRead();
  AcceleroDataRead();

}

void setupLSM303() {
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG1_ADDR);
  Wire.write(CTRL_REG1_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG2_ADDR);
  Wire.write(CTRL_REG2_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG3_ADDR);
  Wire.write(CTRL_REG3_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG4_ADDR);
  Wire.write(CTRL_REG4_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG5_ADDR);
  Wire.write(CTRL_REG5_A_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(CTRL_REG6_ADDR);
  Wire.write(CTRL_REG6_A_value);
  Wire.endTransmission();
  //magneto register setup
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(CRA_REG_M_ADDR);
  Wire.write(CRA_REG_M_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(CRB_REG_M_ADDR);
  Wire.write(CRB_REG_M_value);
  Wire.endTransmission();
  Wire.beginTransmission(LSM303_ADDR_MAG);
  Wire.write(MR_REG_M_ADDR);
  Wire.write(MR_REG_M_value);
  Wire.endTransmission();
}

void MagnetoDataRead() {
  Wire.beginTransmission(LSM303_ADDR_MAG );
  Wire.write(Magneto_First_data_addr);
  Wire.endTransmission();

  Wire.requestFrom(LSM303_ADDR_MAG , 6);
  if (Wire.available() <= 6) {
    MagRaw_X_axis_value = (Wire.read() << 8) | (Wire.read());
    MagRaw_Z_axis_value = (Wire.read() << 8) | (Wire.read());
    MagRaw_Y_axis_value = (Wire.read() << 8) | (Wire.read());

    float X_mag_cal = MagRaw_X_axis_value / 1100.0;
    float Y_mag_cal = MagRaw_Y_axis_value / 1100.0;
    float Z_mag_cal = MagRaw_Z_axis_value / 980.0;

    //input calibration factors
    float Xm_offset_corrected = X_mag_cal - 0.032874;
    float Ym_offset_corrected = Y_mag_cal + 0.047876;
    float Zm_offset_corrected = Z_mag_cal + 0.039649;

    Xm_calibrated = 92.387197 * Xm_offset_corrected - 0.307374 * Ym_offset_corrected + 1.151625 * Zm_offset_corrected;
    Ym_calibrated = -0.307374 * Xm_offset_corrected + 85.002110 * Ym_offset_corrected + 1.169755 * Zm_offset_corrected;
    Zm_calibrated = 1.151625 * Xm_offset_corrected + 1.169755 * Ym_offset_corrected + 80.276037 * Zm_offset_corrected;

    //normalization formula
    norm_m = sqrt(sq(Xm_calibrated) + sq(Ym_calibrated) + sq(Zm_calibrated));
    Xm_norm = Xm_calibrated / norm_m;
    Ym_norm = Ym_calibrated / norm_m;
    Zm_norm = Zm_calibrated / norm_m;

    /*
        Serial.print(X_mag_cal, 10);
        Serial.print("\t");
        Serial.print(Y_mag_cal, 10);
        Serial.print("\t");
        Serial.print(Z_mag_cal, 10);
        Serial.println("\t");
    */

    //float Heading = ((atan2(Ym_norm, Xm_norm)) * 180.0 ) / PI;
    //if (Heading < 0) {
    // Heading = 360 + Heading;
    //}
    //Serial.println(Heading);
    //delay(50);

  }
}

void AcceleroDataRead() {
  Wire.beginTransmission(LSM303_ADDR);
  Wire.write(Accelero_First_data_addr | 0x80);
  Wire.endTransmission();

  Wire.requestFrom(LSM303_ADDR, 6);
  if (Wire.available() <= 6) {
    AccRaw_X_axis_value = (Wire.read()) | (Wire.read() << 8);
    AccRaw_Y_axis_value = (Wire.read()) | (Wire.read() << 8);
    AccRaw_Z_axis_value = (Wire.read()) | (Wire.read() << 8);

    float X_Acc =  (AccRaw_X_axis_value >> 4);
    float Y_Acc =  (AccRaw_Y_axis_value >> 4);
    float Z_Acc =  (AccRaw_Z_axis_value >> 4);

    //calibrate offset
    float X_acc_offset_corrected = X_Acc + 6.676443;
    float Y_acc_offset_corrected = Y_Acc - 6.622381;
    float Z_acc_offset_corrected = Z_Acc + 55.408804;

    //calibrate
    float X_acc_calibrated = 0.977273 * X_acc_offset_corrected - 0.003458 * Y_acc_offset_corrected + 0.012639 * Z_acc_offset_corrected;
    float Y_acc_calibrated = -0.003458 * X_acc_offset_corrected + 0.979674 * Y_acc_offset_corrected - 0.001130 * Z_acc_offset_corrected;
    float Z_acc_calibrated = 0.012639 * X_acc_offset_corrected - 0.001130 * Y_acc_offset_corrected + 0.897083 * Z_acc_offset_corrected;
    //normalize
    norm_a = sqrt(sq(X_acc_calibrated) + sq(Y_acc_calibrated) + sq(Z_acc_calibrated));
    Xa_norm = X_acc_calibrated / norm_a;
    Ya_norm = Y_acc_calibrated / norm_a;
    Za_norm = Z_acc_calibrated / norm_a;

    //calculate pitch and roll
    /*
      pitch = asin(-Xa_norm) * 180 / PI;
      roll = asin(Ya_norm / cos(pitch * PI / 180)) * 180 / PI;
    */
    roll = atan2(Ya_norm, Za_norm);
    float  Gz2 = Ya_norm * sin(roll) + Za_norm * cos(roll);
    pitch = atan(-Xa_norm / Gz2);
    float By2 = Zm_norm * sin(roll) - Ym_norm * cos(roll);
    float Bz2 = Ym_norm * sin(roll) + Zm_norm * cos(roll);
    float Bx3 = Xm_norm * cos(pitch) + Bz2 * sin(pitch);
    float yaw = (atan2(By2, Bx3)*180)/PI;
    if (yaw < 0) {
      yaw = 360 + yaw;
    }

    float tilt = atan2(sqrt((sq(Xa_norm)+(sq(Ya_norm)))),Za_norm);

    //calculate vectors fo tilt compensation
    float Xh = Xm_norm * cos(pitch) + Zm_norm * sin(pitch);
    float Yh = Xm_norm * sin(roll ) * sin(pitch) + Ym_norm * cos(roll) - Zm_norm * sin(roll) * cos(pitch);

        // calculate tilt compensated heading
        float Heading = ((atan2(Yh, Xh)) * 180.0 ) / PI;
        if (Heading < 0) {
          Heading = 360 + Heading;
        }

    Serial.print("Pitch \t");
    Serial.print(pitch * 180 / PI);
    Serial.print("\t roll \t");
    Serial.print(roll * 180 / PI);
    Serial.print("\t tilt \t");
    Serial.print(tilt*180/PI);
    Serial.print("\t");
    Serial.println(Heading);
    delay(500);
  }
}

Found it...Magneto data is coming out XZY not XYZ...when I changed this it works! Sorry, RTFMA would have given me the answer straight away.

Glad to hear it.  But you posted the answer 10 hours ago and made a comment that it still does not work 8 hours ago.  Which is it?

Sorry for the confusion. The sensor data for magneto and accelero independently work. But when I have to combine the 2 for tilt compensation...it does not compensate...

Try changing the sign of your Z-normal value before using it in your Xh and Yh equations (about 15 lines before the end of your program).  If it works, I'll post an answer explaining why.

I have tried Za_norm = -Z_acc_calibrated/norm_a; but no luck. I also tried to change the sign in the Xh formula...also no luck...

Bummer. I'm also wondering about the pitch  calculation.  It is different than the STMicro paper I'm looking at. Are you using this stmicro 2010 documet? I can not even find that one on the STMicro web site.  Try this stmicro 2018 document.  It appears the Pitch calculation is different. You might want to print out your Pitch and Roll values to check.

Thanks for sending me that link! It is very helpful. Pitch and roll calculations were printed and the seemed to be correct. I will try to implement the calculations from the document, but for that I need some time. For the moment I can already tell you that those formula's also work. On to the next step!


text:
ATTiny84 fuses reading as 0
I was programming my ATTiny84 when it randomly stopped being able to be programmed.
I have an external 32768 Hz crystal connected to it, and I have been using my Arduino UNO with the ArduinoISP sketch to program it. The following command was working at least once before, but isn't anymore:
C:\Users\Anubhav\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude -CC:\Users\Anubhav\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf -v -pattiny84 -cstk500v1 -PCOM3 -b19200 -Uflash:w:C:\Users\Anubhav\AppData\Local\Temp\arduino_build_256583/watch.ino.hex:i -U lfuse:w:0xe6:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m

I tried with -B 5 as well but that didn't seem to help either. The problems seems to be that it's always reading the fuses as 0x000000. This is my output:
avrdude.exe: Version 6.3-20190619
             Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
             Copyright (c) 2007-2014 Joerg Wunsch

             System wide configuration file is "C:\Users\Anubhav\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf"

             Using Port                    : COM3
             Using Programmer              : stk500v1
             Overriding Baud Rate          : 19200
             AVR Part                      : ATtiny84
             Chip Erase delay              : 4500 us
             PAGEL                         : P00
             BS2                           : P00
             RESET disposition             : possible i/o
             RETRY pulse                   : SCK
             serial program mode           : yes
             parallel program mode         : yes
             Timeout                       : 200
             StabDelay                     : 100
             CmdexeDelay                   : 25
             SyncLoops                     : 32
             ByteDelay                     : 0
             PollIndex                     : 3
             PollValue                     : 0x53
             Memory Detail                 :

                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               eeprom        65     6     4    0 no        512    4      0  4000  4500 0xff 0xff
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               flash         65     6    32    0 yes      8192   64    128  4500  4500 0xff 0xff
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               efuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
                                      Block Poll               Page                       Polled
               Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
               ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
               calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00

             Programmer Type : STK500
             Description     : Atmel STK500 Version 1.x firmware
             Hardware Version: 2
             Firmware Version: 1.18
             Topcard         : Unknown
             Vtarget         : 0.0 V
             Varef           : 0.0 V
             Oscillator      : Off
             SCK period      : 0.1 us

avrdude.exe: PAGEL and BS2 signals not defined in the configuration file for part ATtiny84, using dummy values
avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.05s

avrdude.exe: Device signature = 0x000000 (retrying)

Reading | ################################################## | 100% 0.04s

avrdude.exe: Device signature = 0x000000 (retrying)

Reading | ################################################## | 100% 0.04s

avrdude.exe: Device signature = 0x000000
avrdude.exe: Yikes!  Invalid device signature.
             Double check connections and try again, or use -F to override
             this check.

avrdude.exe done.  Thank you.

How do I fix the Yikes!  Invalid device signature. error and why is it occurring?

you did not ask a question

Is the MCU running at 32kHz clock? Then ISP clock should be less than 1/4 of MCU clock frequency. So you should try to change the default #define SPI_CLOCK  (1000000/6) to value like 5000 in the ArduinoISP sketch.

Isn't the baudrate of 19200 too fast for a 32768 system clock?

@thebusybee that's serial speed between Computer and ArduinoISP (UNO). The ISP clock speed is hardcoded inside of ArduinoISP sketch (as far as I know)

Ah, thanks for clarification!

@KIIV that fixed it, thanks! If you submit as an answer I can mark it as accepted.

As noted in question, the 32kHz crystal is used as a clock source and also at least the first programming was working.
The ArduinoISP sketch has hardcoded ISP clock speed and ISP clock has to be at most 1/4 of MCU speed. So the ArduinoISP sketch SPI_CLOCK has to be set to the much lower value than default 166kHz, which is way too fast for 32kHz MCU clock. The safe value is about 5000 (5kHz)
#define SPI_CLOCK 5000   // (1000000/6)

There is also baud_rate settings for the avrdude, but it's just communication speed between computer and ArduinoISP programmer and changing it won't change SPI_CLOCK (which is ISP clock).


text:
Arduino Timer Precision
I am using the ATMega32u4 to generate multiple pulses at 2.5 kHz. I am using Timer 3, which is a 16-bit timer and using a prescaler of 1, so I have a 0.0625uS resolution. I want to generate pulses with pretty high accuracy and consistency. I have my previous post here, which gave me accuracy. But when I look at the pulse with my oscilloscope it has some jitter. Youtube Video of Jitter.
My final goal is to create a pulse on multiple different pins in this configuration.

What I am assuming is that the reason there is jitter is that there are other interrupts going on in the background, that could be shifting when the pulse stops and starts by a few microseconds. Looking through the datasheet, I found this, which seems to show interrupt priority.

It seems that my TIMER3 COMPA and COMPB are very low down the list of priorities, is there a way to change this priority list? Or is there something else that is causing the jitter?
I have posted my code below:
#define pin2 12
#define pin3 6
#define pin4 8

#define REFRESH 6400

int PULSEIndex;

class Pulse {
  public:
    int pulseLength;
    void Begin(byte GPIO_PIN) {
      pinMode(GPIO_PIN, OUTPUT);
    }
    void writeMicro(double pulse) {
      pulseLength = (pulse * 16);
    }
};

Pulse PULSE1;
Pulse PULSE2;
Pulse PULSE3;
Pulse PULSE4;

void setup() {
  PULSE1.Begin(pin1);
  PULSE2.Begin(pin2);
  PULSE3.Begin(pin3);
  PULSE4.Begin(pin4);

  // Reset Timer 3 Control Register to 0
  TCCR3A = 0;
  TCCR3B = 0;

  // Set CTC Mode
  TCCR3B &= ~(1 << WGM33); //0
  TCCR3B |= (1 << WGM32);  //1

  // Set prescaler to 1
  TCCR3B &= ~(1 << CS32); //0
  TCCR3B &= ~(1 << CS31); //0
  TCCR3B |= (1 << CS30);  //1

  //Reset Timer 3 and set compare value
  TCNT3 = 0;

  // Enable Timer 3 compare interrupt A and B
  TIMSK3 |= (1 << OCIE3B);
  TIMSK3 |= (1 << OCIE3A);

  //Enable global interrupts
  sei();

  //Set Refresh Rate
  OCR3A = REFRESH;
  OCR3B = 1000;
}

void loop() {
  PULSE1.writeMicro(40);
  PULSE2.writeMicro(40);
  PULSE3.writeMicro(40);
  PULSE4.writeMicro(40);
  delay(2);
}

ISR(TIMER3_COMPA_vect) {
  digitalWrite(pin1, HIGH);
  OCR3B = PULSE1.pulseLength;
}

ISR(TIMER3_COMPB_vect) {

  if (PULSEIndex == 1) {
    digitalWrite(pin1, LOW);
    digitalWrite(pin2, HIGH);
    OCR3B += PULSE2.pulseLength;
  }

  if (PULSEIndex == 2) {
    digitalWrite(pin2, LOW);
    digitalWrite(pin3, HIGH);
    OCR3B += PULSE3.pulseLength;
  }

  if (PULSEIndex == 3) {
    digitalWrite(pin3, LOW);
    digitalWrite(pin4, HIGH);
    OCR3B += PULSE4.pulseLength;
  }

  if (PULSEIndex == 4) {
    digitalWrite(pin4, LOW);
  }

  PULSEIndex++;
  if (PULSEIndex > 4) PULSEIndex = 1;
} ```

Fist try to access pins directly. The digitalWrite is extremely slow, and at least for the first pulse it's no digitalWrite(pin4, LOW);, so there is few microseconds jitter. Just like that.
Maybe consider MCU with DMA

I'm not 100% sure, but I think the interrupt priorities are fixed in hardware.

What I am assuming is that the reason there is jitter is that
  there are other interrupts going on in the background

This seems like a reasonable assumption. Since you are not doing serial
communication, the Timer 0 interrupt is likely the only one triggering
in the background. You can disable it with:
TIMSK0 = 0;

but then all the Arduino timing functions will stop working. You will
have to #include <util/delay.h> and replace delay() with
_delay_ms(). Note that this avr-libc function only works with
compile-time constant delays. On the other hand, it is cycle-accurate
and accepts floating point delays.

is there a way to change this priority list?

There is not, and even if this was possible, it would not be that
useful. Interrupts in the Arduino are not interruptible: if a
low-priority interrupt is running when a high-priority one triggers, the
high-priority interrupt has to wait for the currently running one to
terminate. Priorities are only relevant when the processor is about to
serve an interrupt request but has to decide which one, among several
pending requests, it will serve first.
Now, a small remark about this:
void loop() {
  PULSE1.writeMicro(40);
  PULSE2.writeMicro(40);
  PULSE3.writeMicro(40);
  PULSE4.writeMicro(40);
  delay(2);
}

There is no point in rewriting those values over and over again. If you
plan to change the code in order to make those widths non-constant, then
you have to take care to:

make the pulseLength properties (or the whole Pulse objects)
volatile
within writeMicro(), disable interrupts while changing
pulseLength.


text:
Macbook Pro CH340 Nano 14201 error
I'm using a MacBook Pro Mojave 10.14.6 to code genuine Arduinos. Been working with multiple Uno R3s, no issue. Got a genuine Nano and get the following error: ser_open(): can't open device "/dev/cu.usbmodem14201": No such file or directory
The USB cable I'm using works fine with a cheap Chinese Uno clone, so there are data pins in the cable.
I followed instructions to delete the kext file, rebooted, installed CH341SER_MAC file and rebooted. Still getting the error message. I notice that others have asked this question; where are the answers? Thanks in advance.

What triggered this error message?

Also, you shouldn't need a CH34x driver. A genuine Arduino would have an FTDI chip, for which MacOS has the driver built-in.

The error message was triggered when I attempted to upload a simple sketch to make the LED blink on and off, to test the Nano. It is absolutely a genuine Arduino; that's the only thing I buy, in support of the Arduino ecosystem. Got it from Amazon, sealed OE box. I also bought 4 Unos at the same time, all of them work fine with the same sketch. My kid bought a cheap Chinese knock-off for school which doesn't always work perfectly (which is another reason why I buy genuine only), but has the same USB cable connector as the Nano. So the cable isn't the problem. Software is fine, cable is fine.

What was the actual action that triggered the message, e.g. connecting the Nano to the Mac? Selecting the port in the IDE's 'Tools' menu? Clicking 'Upload'? You're using the physically same cable for either Nano and one nano works but the other doesn't? Have you tried a cable swap anyway (since your success with the Nano clone was intermittent)?

What was the actual action that triggered the message, e.g. connecting the Nano to the Mac? "The error message was triggered when I attempted to upload a simple sketch to make the LED blink on and off"    You're using the physically same cable for either Nano and one nano works but the other doesn't? "No, the Chinese knock off is a Uno clone that uses an USB-C connector, same as the Nano.", Have you tried a cable swap anyway (since your success with the Nano clone was intermittent)? "Not a nano clone, it's an Uno, and I don't have a second cable that is reliably data and charging capability."

The Chinese Uno clone that doesn't always work perfectly relates to its inability to keep time to within 0.5%, it restarts whenever it is reconnected to the computer, and it sometimes hangs/crashes when running a sketch where a genuine Uno will function perfectly. Hence, I only buy genuine. I'd have to buy another cable, since I don't have devices that use USB-B and-C cables.

Sorry, the connector used is a USB Mini-B by both the Chinese Uno clone and the Nano -- my bad that I didn't have the nomenclature straight. In any case, I'll have to buy another cable, since I don't have anything that uses the Mini-B.

Timekeeping isn't exact with any of the boards, though .05% - just over 7 minutes/day - doesn't sound like too high an expectation.
Restarting when you connect it to your computer is by design and doesn't indicate a fault. Opening the IDE's terminal or connecting another terminal program to a connected and running Arduino will - and is supposed to - reset it. Here is a forum discussion on how to disable the auto-reset function (Uno-specific) if you want to. Note that it involves modifying the board.
It's quite possible that this one board has a fault, though it still could be the cable. A couple of swaps should confirm it one way or the other.
I'm still not clear on where the error message,

ser_open(): can't open device "/dev/cu.usbmodem14201"

came from, but I'm guess it's from AVRdude, the uploader application on your Mac, and still I'm wondering if the correct port is selected in the IDE's Tools | Port menu? For some reason, a USB device called "cu.usbmodem14201" is expected but not found. Since neither the Mac nor the IDE inherently knows this name - a USB device announces its name when you connect it - could this selection be left over from having previously used the genuine Arduino? Another reason to check the Ports menu. Also note that the Ports menu doesn't update its list spontaneously - only when you open it (and maybe only when you open the top-level, Tools, menu; I don't remember exactly).
Update:

The regular Arduino at 0.5% is pretty crude, I'd pay more for a more
  accurate crystal, but I was able to develop a sketch that adjusts for
  the Arduino's inaccuracy.

You could try putting the Arduino in an "oven" - and I emphasize the quotes. This is not a heating chamber per-se but a temperature controlled chamber. If you're getting good results by adjusting the count, adding the oven should get you even better results, especially as the seasons change and if that makes enough difference where you are.

I'll be damned, I went to Tools/Ports and selected "dev/cu.usbserial-AK069QY4 and uploaded a sketch successfully.  Regarding the timekeeping, 0.05% doesn't cut it for my application, I'm looking for better than 1 second in 24 hours, which is 1/86,400, or 0.0012%. The regular Arduino at 0.5% is pretty crude, I'd pay more for a more accurate crystal, but I was able to develop a sketch that adjusts for the Arduino's inaccuracy. Anyhow, thanks for pointing me in the right way. :)

You're welcome. If the answer solved your problem, please select it so. It will keep it from falling into the barrel of presumed unanswered, but abandoned, questions that like the phoenix, keep rising from their ashes (auto-reposted).


text:
What are the best GPIO pins to use for my project?
I'm working on a home automation project to control my Sommer Base+ garage door opener through Home Assistant.
The project consists of a WeMos D1 Mini with two reed switches (to detect door open/closed) and a 5V relay to control the door opener.
When the door is open the 'top / open' switch is triggered, giving a status of open. Likewise when the door is closed, the 'bottom / closed' switch is triggered, giving a status of closed. When neither switch is triggered (i.e. when the door is in motion), the status is either opening or closing, depending on the previous status.

This works fine unless the WeMos is powered up while either reed switch is closed (so if the door is open or closed).
I know this is because D3 and D8 GPIO pins for the open/close sensors and those can put the device into boot mode (I think that's what it's called).
What I want to know is which pins are the best to use for this project.
Is it possible for me to just change to pins ?? and ?? without needing to update my code?
Is it possible that the WeMos D1 Mini isn't a good fit for what I want?

GPIO 4+5 are basically the only universal inputs on the 8266; no side-effects or implementation complications.

How are you hooking it into the Sommer? I have the same opener and would love to do this.

I use a combination of the conex and output oc modules to connect to the wemos that then integrate with my Home Assistant instance.

Output oc: https://www.amazon.co.uk/dp/B07XZKDFX9/ref=cm_sw_r_cp_apa_fabt1_XAkVFb6XEGZKG?_encoding=UTF8&psc=1

Conex module: https://www.amazon.co.uk/dp/B07BQYSHDV/ref=cm_sw_r_cp_apa_fabt1_bEkVFbN1XB28M

And here is my updated project: https://github.com/jampez77/garage_door_sensor

NodeMCU and "Wemos D1 R2 and Mini" esp8266 (esp-12) pins and io overview

Serial
RX io3 RX0
TX io1 TX0

boot config pins with pullup or pulldown on board
D3 io0 PULLUP (LOW for boot to flashing mode)
D4 io2 TX1 PULLUP (Serial1 TX. no RX for Serial1)
D8 io15 PULLDOWN (SS pin if esp8266 is SPI slave); TX if Serial.swap()

untroubled GPIOs with optional function for I2C or SPI bus:
D1 io5 default pin for I2C SCL
D2 io4 default pin for I2C SCA
D5 io14 SPI CLK
D6 io12 SPI MISO
D7 io13 SPI MOSI; RX if Serial.swap()

RTC pin for timed deep sleep wake-up (if connected to reset pin)
D0 io16 - optional internal pulldown, internal pullup not available

not useable: on NodeMcu Sx pins io 6 to io 11 connect QIO SPI flash memory
(in a very special setup the SPI bus can be used with other SPI device)
(in other very special setup the QIO pins can be used as gpio)
note: most digital IO start as INPUT_PULLUP