Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Sashrika DasMithun Das
Published © LGPL

MCube box - a personalized message box [by Sashrika]

MCUBE can send pictures and messages to loved ones. This a DIY project using Wio Terminal and NeoPixel Ring

BeginnerWork in progress10 hours576
MCube box - a personalized message box [by Sashrika]

Things used in this project

Hardware components

Wio Terminal
Seeed Studio Wio Terminal
×1
Seeed Studio Wio Battery Chassis
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1

Software apps and online services

Arduino IDE
Arduino IDE
VS Code
Microsoft VS Code

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

ring cap

box lid

main box

chassis holder

Code

Image uploader

Python
from PIL import Image
import array
import os
from github import Github
from paho.mqtt import client as mqtt_client
import random
import time

g = Github("Your_github_personal_token")
repo = g.get_user().get_repo("your_public_repo")
broker_address="broker.hivemq.com"
client_id = f'python-mqtt-{random.randint(0, 1000)}'
port = 1883
topic = "your_topic"

fname =  input("Enter the filename to upload") 
generate_file  = "%s.bmp" %(time.strftime("%Y%m%d-%H%M%S"))

print(generate_file)

def rgb332(r, g, b):
    r = r >> 5
    g = g >> 5
    b = b >> 6
    c = r << 5 | g << 2 | b
    return [c]

def convert(filename):
    #converted_filename = "%s.bmp" %(filename)
    converted_filename = generate_file
    #size = [320,240]
    size = [288,216]
    Image.open(filename).resize(size=size).save(converted_filename)
    im = Image.open(converted_filename)
    width, height = im.size
    v = [rgb332(r, g, b) for (r, g, b) in im.getdata()]
    b = bytearray()
    b.append(width & 0xff)
    b.append(width >> 8)
    b.append(height & 0xff)
    b.append(height >> 8)
    for pair in v:
        for i in pair:
            b.append(i)
    f = open(converted_filename, "wb")
    f.write(b)
    f.close()
    print("done!")


def upload():
    with open(generate_file, 'rb') as file:
        content = file.read()
       
        r = repo.create_file("assets/%s" %(generate_file), "committing files", content,  branch="main")
        
        print(r)

published = False

def postMqtt(message):
    global published
    def on_connect(client, userdata, flags, rc):
        global published
        if rc == 0:
            print("Connected to MQTT Broker!")
            client.publish(topic, message)
            print("published")
            time.sleep(1)
            
            published = True

        else:
            print("Failed to connect, return code %d\n", rc)

    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(broker_address, port)
    client.loop_start()
    
      
    while not published:
        time.sleep(1)
        print(".")

def cleanRepo():
    contents = repo.get_contents("assets")
    print(contents)
    for file in contents:
        content = repo.get_contents(file.path)
        repo.delete_file(content.path, "remove", branch="main")

if __name__ == "__main__":
    convert(fname)
    upload()
    postMqtt(generate_file)
    
    

Wio Terminal Program

Arduino
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>  // Required for 16 MHz Adafruit Trinket
#endif


#define PIN        D0 // On Trinket or Gemma, suggest changing this to 1

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 12 // Popular NeoPixel ring size

// #include <rpcWiFi.h>
// #include <WiFiClientSecure.h>
// #include <HTTPClient.h>

#include <rpcWiFiClientSecure.h>
#include <PubSubClient.h>

const char *ssid = "WIFI_SSID";
const char *password = "WIFI_PASSWORD";

const char *ID = "Wio-Terminal-Client";  // Name of our device, must be unique
const char *TOPIC = "your_topic";  // Topic to subcribe to
const char *mqttserver = "broker.hivemq.com"; // Server URL
 
WiFiClientSecure client;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);



#include "TFT_eSPI.h"
#include "Seeed_FS.h"  //Including SD card library
#include "RawImage.h"  //Including image processing library

TFT_eSPI tft;
File myFile;

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
uint8_t brightness = 50;

void setup() {
  //Initialise SD card
  Serial.begin(115200);
  pinMode(WIO_KEY_A, INPUT_PULLUP);
  pinMode(WIO_5S_PRESS, INPUT_PULLUP);
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);
  pinMode(WIO_BUZZER, OUTPUT);

  pixels.begin();
  pixels.setBrightness(brightness);
  setRingColor(255, 208, 0);
  
  while (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
    Serial.println("Card Mount Failed");
    return;
  }
  Serial.println("SD card mounted");
  uint8_t cardType = SD.cardType();

  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }
  Serial.println("SD card attached");
  tft.begin();
  tft.setRotation(3);

  int dotCounter = 0;
     while (WiFi.status() != WL_CONNECTED)
     {
         if(dotCounter%10 == 0){
          tft.fillScreen(TFT_BLACK);
          tft.setCursor(100,110);
         }
         tft.print(".");
         dotCounter ++;
         WiFi.begin(ssid, password);
     }
     tft.fillScreen(TFT_BLACK); 
     tft.setCursor(100,110);
     tft.print("IP:");
     tft.print(WiFi.localIP());
     delay(1000);
  
  delay(1000);
  
  
  drawImage<uint8_t>("bg.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
  
  mqttClient.setServer(mqttserver, 1883);
  mqttClient.setCallback(callback);
  setRingColor(255, 255, 255);

}


String url = "web.bmp";

void loop() {


  if(digitalRead(WIO_5S_PRESS) == LOW){
    setRingColor(255,255,255);
  }

  if(digitalRead(WIO_5S_UP) == LOW){
    
    if(brightness < 250){
      brightness = brightness + 25;
      pixels.setBrightness(brightness);
      setRingColor(255, 255, 255);
      Serial.println(brightness);
    }
    
  }

  if(digitalRead(WIO_5S_DOWN) == LOW){
    
    if(brightness > 25){
      brightness = brightness - 25;
      pixels.setBrightness(brightness);
      setRingColor(255, 255, 255);
      Serial.println(brightness);
    }
    
  }

  if (digitalRead(WIO_KEY_A) == LOW) {
    Serial.println("A Key pressed");
    //buzz();
    //rainbow(10);    
    Serial.println("End of rainbow");
    downloadImage("bg.bmp");
   }

  if (!mqttClient.connected()) {
    reconnect();
  }
  mqttClient.loop();
    
}

void downloadImage(String filename){

  if(&client) {
        
        
        
        Serial.printf("RTL8720 Firmware Version: %s\n", rpc_system_version());
        char*  server = "raw.githubusercontent.com";  // Server URL

        if (!client.connect(server, 443))
            Serial.println("Connection failed!");
        else {
          Serial.println("Connected to server!");

          client.println("GET your_github_url_path/"+ filename +" HTTP/1.1");
          client.println("Host: "+ String(server));
          client.println("User-Agent: wio");
          
          client.println("Connection: close");
          client.println();

          

          long len = -1;

          unsigned long timeout = millis();
          while (client.available() == 0) {
            if (millis() - timeout > 5000) {
              Serial.println(">>> Client Timeout !");
              client.stop();
              return;
            }
          }


          while (client.connected()) {
            //Serial.print(".");
            String line = client.readStringUntil('\n');
            
            //Serial.println(line);
            if (line.startsWith("Content-Length:")) {
              len = line.substring(15).toInt();
            }
            if (line == "\r") {
              Serial.println("headers received");
              break;
            }
          }

          Serial.printf("Content-Length = %d \n", len);

          if(SD.exists("web.bmp")){
            SD.remove("web.bmp");
            Serial.println("removed old file");
          }
          File myFile = SD.open("web.bmp", FILE_WRITE); //Writing Mode

          if (myFile) {
              uint8_t buff[128];
              while (client.connected() && (len > 0 || len == -1)) {
              //while ((len > 0 || len == -1)) {

                size_t size = client.available();

                Serial.printf("size  %i\n", size);                

                if (size>0) {
                  int c = client.readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
                  //int c = client.readBytes(buff, ((size > 128) ? 128 : size));                  
                  myFile.write(buff, c);
                  if (len > 0) 	{
                    len -= c;
                  }
                }else { 
                  break; 
                }

                //delay(1);
               // Serial.printf("bytes left %i\n", len);
                
              }  

              myFile.flush();
              Serial.printf("Written file size = %d\n", myFile.size());            
              myFile.close();
              Serial.println("file closed.");
              tft.fillScreen(TFT_ORANGE);
              delay(1000);
              drawImage<uint8_t>("web.bmp", 16, 12);
          }else{
            Serial.println("error opening file");
          }

          client.stop();
          Serial.println("stopping client");
        }

    }else{
      Serial.println("Unable to create client");
    }
}
void buzz() {
        analogWrite(WIO_BUZZER, 128);
        delay(1000);
        analogWrite(WIO_BUZZER, 0);
        delay(1000);
    }

void blink(){
  pixels.clear();
  for(int i=0; i<NUMPIXELS; i++) { // For each pixel...

    // pixels.Color() takes RGB values, from 0,0,0 up to 255,255,255
    // Here we're using a moderately bright green color:
    pixels.setPixelColor(i, pixels.Color(0, 150, 0));

    pixels.show();   // Send the updated pixel colors to the hardware.

    delay(200); // Pause before next pass through loop
  }
  delay(1000);
  pixels.clear();
  pixels.show();
}

void setRingColor(uint8_t r, uint8_t g, uint8_t b){
  pixels.clear();
  for(int i=0; i<NUMPIXELS; i++) { // For each pixel...

    pixels.setPixelColor(i, pixels.Color(r, g, b));

    pixels.show();   // Send the updated pixel colors to the hardware.

    delay(200); // Pause before next pass through loop
  }
  
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  //url = String((char*)payload);
  char message[length]; 

  for (int i=0;i<length;i++) {
    //Serial.print((char)payload[i]);
    message[i] = (char)payload[i];

  }
  
  Serial.println(String(message));
  
  downloadImage(String(message));
  setRingColor(238, 0, 255);
  buzz();
}

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (mqttClient.connect(ID)) {
      Serial.println("connected to MQTT");
      // Once connected, publish an announcement...
      //mqttClient.publish(TOPIC, "{\"message\": \"Wio Terminal is connected!\"}");
      
      mqttClient.subscribe(TOPIC);
      
    }
    else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this outer loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    for(int i=0; i<pixels.numPixels(); i++) { // For each pixel in strip...
      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      int pixelHue = firstPixelHue + (i * 65536L / pixels.numPixels());
      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the single-argument hue variant. The result
      // is passed through strip.gamma32() to provide 'truer' colors
      // before assigning to each pixel:
      pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue)));
    }
    pixels.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }

  pixels.clear();
  pixels.show();
}

Credits

Sashrika Das

Sashrika Das

4 projects • 32 followers
I am a 9th grade student, passionate about technology.
Mithun Das

Mithun Das

34 projects • 180 followers
Hacker and Maker driven by passion. Ambassador at Edge Impulse and Balena. Follow me on Twitter @_mithundas

Comments