#include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Button2.h" #include "melodies.h" #include #include #include #include // src: https://github.com/knolleary/pubsubclient #include #include #include #include // TODO: define topics used to publish/subscribe #define TOPIC_BR "brightness" #define TOPIC_OLED "oled" #define TOPIC_LED "led" #define TOPIC_BUZZ "buzzer" #define TOPIC_BTN "button" #define HASH_SIZE 32 // TODO: is this needed? #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define MAX_WAIT_FOR_TIMER 3 #define SCREEN_ADDRESS 0x3C // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 #define OLED_RESET 16 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); const char *ssid = "Segfault_2.4"; const char *password = "i3mlpgdOPLVwXeC"; const char *server = "192.168.1.153"; // TODO: server IP const int port = 1883; const char *mqtt_name = "user"; // TODO: username const char *mqtt_pass = "test"; // TODO: password char id[65]; /* * by default, the max msg size is 256 bytes including header * configurable with MQTT_MAX_PACKET_SIZE or PubSubClient::setBufferSize(size) */ char msg[256]; WiFiClient wclient; PubSubClient client(wclient); /* * structures */ enum { EMPTY, FULL }; struct mailbox_s { int state; int val; bool periodic; }; struct Led_s { int timer; // numéro du timer pour cette tâche utilisé par WaitFor unsigned long period; // periode de clignotement int pin; // numéro de la broche sur laquelle est la LED int etat; // etat interne de la led }; struct Oled_s { int timer; unsigned long period; int i; }; struct Lum_s { int timer; // numéro de timer utilisé par WaitFor unsigned long period; // periode d'affichage uint val; }; struct Buz_s { int timer; unsigned long period; int n; int pause; int size; int *melody; int duration; int tempo; bool play; }; /* * structure initialisation */ struct Led_s Led1; struct Oled_s Oled1; struct Lum_s Lum1; struct Buz_s Buz1; Button2 button; struct mailbox_s mb_led_state = {.state = EMPTY}; // -------------------------------------------------------------------------------------------------------------------- // Multi-tâches cooperatives : solution basique mais efficace :-) // -------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------- // unsigned int waitFor(timer, period) // Timer pour taches périodiques // configuration : // - MAX_WAIT_FOR_TIMER : nombre maximum de timers utilisés // arguments : // - timer : numéro de timer entre 0 et MAX_WAIT_FOR_TIMER-1 // - period : période souhaitée // retour : // - nombre de périodes écoulées depuis le dernier appel // -------------------------------------------------------------------------------------------------------------------- unsigned int waitFor(int timer, unsigned long period) { static unsigned long waitForTimer[MAX_WAIT_FOR_TIMER]; // il y a autant de timers que de tâches périodiques unsigned long newTime = micros() / period; // numéro de la période modulo 2^32 int delta = newTime - waitForTimer[timer]; // delta entre la période courante et celle enregistrée if (delta < 0) delta = 1 + newTime; // en cas de dépassement du nombre de périodes possibles sur 2^32 if (delta) waitForTimer[timer] = newTime; // enregistrement du nouveau numéro de période return delta; } /* * setup functions */ void setup_Led(struct Led_s *ctx, int timer, unsigned long period, byte pin) { ctx->timer = timer; ctx->period = period; ctx->pin = pin; ctx->etat = 1; pinMode(pin, OUTPUT); digitalWrite(pin, ctx->etat); } void setup_Oled() { Wire.begin(4, 15); Serial.begin(115200); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.cp437(true); // Use full 256 char 'Code Page 437' font display.clearDisplay(); display.setTextSize(1); display.setCursor(1, 1); display.write("Hello, World!"); display.display(); } void setup_Lum(struct Lum_s *ctx, int timer, unsigned long period) { ctx->timer = timer; ctx->period = period; pinMode(36, INPUT); } void setup_Buz(struct Buz_s *ctx, int timer) { ctx->timer = timer; ctx->play = false; pinMode(17, OUTPUT); ledcAttachPin(17, 0); } void setup_Btn() { button.begin(23); // button.setChangedHandler(changed); // button.setPressedHandler(pressed); // button.setReleasedHandler(released); // setTapHandler() is called by any type of click, longpress or shortpress button.setTapHandler(tap); } void setup_wifi() { Serial.print("[WiFi] Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); } Serial.println(); Serial.print("IP @:"); Serial.println(WiFi.localIP()); /* hash mac address and store it is ID */ byte id_hash[32]; String mac = WiFi.macAddress(); Serial.println(mac); mbedtls_md_context_t ctx; mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; mbedtls_md_init(&ctx); mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0); mbedtls_md_starts(&ctx); mbedtls_md_update(&ctx, (const unsigned char *)mac.c_str(), mac.length()); mbedtls_md_finish(&ctx, id_hash); mbedtls_md_free(&ctx); for (int i = 0; i < sizeof(id_hash); i++) { char tmp[3]; sprintf(tmp, "%02x", (int)id_hash[i]); strcat(id, tmp); } id[65] = NULL; } void choose_song(int n) { switch (n) { case 1: Buz1.melody = hp; Buz1.size = (sizeof(hp) / sizeof(hp[0]) / 2) * 2; Buz1.tempo = tempo_hp; break; case 2: Buz1.melody = zelda; Buz1.size = (sizeof(zelda) / sizeof(zelda[0]) / 2) * 2; Buz1.tempo = tempo_zelda; break; case 3: Buz1.melody = sw; Buz1.size = (sizeof(sw) / sizeof(sw[0]) / 2) * 2; Buz1.tempo = tempo_sw; break; case 4: Buz1.melody = gf; Buz1.size = (sizeof(gf) / sizeof(gf[0]) / 2) * 2; Buz1.tempo = tempo_gf; break; case 5: Buz1.melody = rick; Buz1.size = (sizeof(rick) / sizeof(rick[0]) / 2) * 2; Buz1.tempo = tempo_rick; break; default: Buz1.play = false; Buz1.melody = NULL; Buz1.n = 0; return; } Buz1.n = 0; Buz1.play = true; } void write_Oled(const char *str) { display.clearDisplay(); display.setTextSize(1); display.setCursor(1, 1); display.write(str); display.display(); } void callback(const char *topic, byte *payload, unsigned int length) { Serial.print("callback "); Serial.println(topic); payload[length] = '\0'; Serial.println((char *)payload); DynamicJsonDocument doc(256); DeserializationError error = deserializeJson(doc, payload); if (error) return; String device_id = doc["device_id"]; if (strcmp(device_id.c_str(), id) != 0) return; int i; if (strcmp(topic, TOPIC_OLED) == 0) { String m = doc["text"]; Serial.println(m); write_Oled(m.c_str()); } else if (strcmp(topic, TOPIC_LED) == 0) { mb_led_state.val = doc["period"]; mb_led_state.state = FULL; mb_led_state.periodic = mb_led_state.val != 0; } else if (strcmp(topic, TOPIC_BUZZ) == 0) { int value = doc["song"]; choose_song(value); } } void setup_mqtt() { client.setServer(server, port); client.connect(id, mqtt_name, mqtt_pass); if (!client.subscribe(TOPIC_BUZZ)) Serial.println("fail"); if (!client.subscribe(TOPIC_OLED)) Serial.println("fail"); if (!client.subscribe(TOPIC_LED)) Serial.println("fail"); client.setCallback(callback); } /* * Handlers */ void tap(Button2 &btn) { DynamicJsonDocument doc(256); doc["device_id"] = id; serializeJson(doc, msg); client.publish(TOPIC_BTN, msg); Serial.println("tap"); } void loop_Led(struct Led_s *ctx, struct mailbox_s *mb_s) { if (mb_s->state != EMPTY) { mb_s->state = EMPTY; if (mb_s->val == 0) { digitalWrite(ctx->pin, ctx->etat); ctx->etat = 1 - ctx->etat; return; } } if (mb_s->periodic) { int period = mb_s->val < 2 ? ctx->period : ctx->period * (mb_s->val / 2); if (!waitFor(ctx->timer, period)) return; digitalWrite(ctx->pin, ctx->etat); ctx->etat = 1 - ctx->etat; } } void loop_Lum(struct Lum_s *ctx) { if (!(waitFor(ctx->timer, ctx->period))) return; // sort s'il y a moins d'une période écoulée int perc; DynamicJsonDocument doc(256); ctx->val = analogRead(36); perc = map(ctx->val, 0, 4095, 100, 0); itoa(perc, msg, 10); doc["device_id"] = id; doc["val"] = perc; serializeJson(doc, msg); client.publish(TOPIC_BR, msg); // Serial.println("published"); } void loop_Buz(struct Buz_s *ctx) { if (!ctx->play) { ledcDetachPin(17); ctx->n = 0; return; } int wholenote = (60000 * 4) / ctx->tempo; int divider = ctx->melody[ctx->n + 1]; if (divider > 0) ctx->duration = wholenote / divider; else if (divider < 0) ctx->duration = (wholenote / abs(divider)) * 1.5; ctx->period = ctx->duration; if (!(waitFor(ctx->timer, ctx->period))) return; tone(17, ctx->melody[ctx->n], ctx->duration * 0.9); ctx->n = (ctx->n + 2) % ctx->size; noTone(17); } void setup() { Serial.begin(115200); delay(10); Serial.println(); setup_wifi(); setup_mqtt(); setup_Lum(&Lum1, 1, 2000000); setup_Buz(&Buz1, 0); setup_Led(&Led1, 2, 100000, LED_BUILTIN); // Led est exécutée toutes les 100ms setup_Btn(); setup_Oled(); } void reconnect() { while (!client.connected()) { Serial.println("Reconnecting"); setup_mqtt(); } } void loop() { reconnect(); loop_Led(&Led1, &mb_led_state); loop_Buz(&Buz1); loop_Lum(&Lum1); client.loop(); button.loop(); }