Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@

# Grammax
Parser.h
Parser_debug.txt
Parser_debug.txt

# Folders
ui
# terminal
terminal/node_modules
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# ZeroLamp

ZeroBone's ESP32-based smart lamp project.

## 📦 Dependencies / Arduino Libraries

**ZeroLamp** requires the following Arduino libraries, compatible with **ESP32-S3**.
All libraries can be installed via the **Arduino Library Manager**.

| Library | Purpose | Author | Install via Library Manager |
|-----------------|-----------------------------------------------|----------------------|-------------------------------------|
| FastLED | Control addressable RGB LEDs (WS2812B matrix) | Daniel Garcia et al. | Search `FastLED` in Library Manager |
| WiFi | Connect ESP32-S3 to Wi-Fi networks | Espressif Systems | Built-in for ESP32 in Arduino core |
| ESPmDNS | Device name discovery over network (mDNS) | Espressif Systems | Built-in for ESP32 in Arduino core |
| BluetoothSerial | Bluetooth communication (ESP32-S3) | Espressif Systems | Built-in for ESP32 in Arduino core |

**Notes:**

- `TOUCH_BUTTON_PIN` (TTP223) uses standard `digitalRead()`, no extra library required.
- Make sure your **Arduino IDE** is updated and ESP32-S3 board support is installed via **Boards Manager**.

## 🚀 Quick Start

1. Clone the repository
2. Open `zerolamp.ino` in Arduino IDE
3. Set `WLAN_SSID` and `WLAN_PASSWORD` in `wlan.h`
4. Upload to ESP32-S3 board
5. Press `TOUCH_BUTTON_PIN` to wake / sleep
65 changes: 65 additions & 0 deletions terminal/terminal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// siakinnik - terminal.js
const WebSocket = require('ws');
const readline = require('readline');

const SERVER_URL = 'ws://zerolamp.local:81';
let clientAuthorized = false;

// readline
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '> '
});

let ws;

const connect = () => {
ws = new WebSocket(SERVER_URL);
ws.on('open', () => {
console.log('Connected. Waiting for code request...');
});

ws.on('message', (data) => {
const msg = data.toString().trim();
console.log('\n<<', msg);

if (!clientAuthorized && msg.includes('Enter connection code:')) {
rl.question('Enter code: ', (code) => {
ws.send(code.trim());
});
} else if (!clientAuthorized && msg.includes('Authorized!')) {
clientAuthorized = true;
console.log('Authorization success!');
rl.prompt();
} else if (!clientAuthorized && msg.includes('Wrong code')) {
console.log('Wrong code, try again!');
ws.close();
setTimeout(connect, 1000);
}
});

ws.on('close', () => {
clientAuthorized = false;
console.log('\nConnection closed. Reconnecting in 3 seconds...');
setTimeout(connect, 3000);
});

ws.on('error', (err) => {
console.error('Connection error:', err.message);
setTimeout(connect, 3000);
});
};

rl.on('line', (line) => {
if (!clientAuthorized) {
console.log('Authorization first!');
rl.prompt();
return;
}
const command = line.trim().replace(/[\r\n]+$/, '').replace(/;$/, '');
ws.send(command);
rl.prompt();
});

connect();
29 changes: 29 additions & 0 deletions third_party/lib/LICENSES/LICENSE_ArduinoJWT.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2017, Chris Moorhouse
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
166 changes: 166 additions & 0 deletions zerolamp/JWT.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**

Copyright (c) 2016, Interior Automation Ltd.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be
used to endorse or promote products derived from this software without specific prior
written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

**/

#include <Arduino.h>
#include "JWT.h"
#include "base64.hpp"
#include "sha256.h"

// The standard JWT header already base64 encoded. Equates to {"alg": "HS256", "typ": "JWT"}
const PROGMEM char* jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";

JWT::JWT(String psk) {
_psk = psk;
}

JWT::JWT(char* psk) {
_psk = String(psk);
}

void JWT::setPSK(String psk) {
_psk = psk;
}
void JWT::setPSK(char* psk) {
_psk = String(psk);
}

int JWT::getJWTLength(String& payload) {
return getJWTLength((char*)payload.c_str());
}

int JWT::getJWTLength(char* payload) {
return strlen(jwtHeader) + encode_base64_length(strlen(payload)) + encode_base64_length(32) + 2;
}

int JWT::getJWTPayloadLength(String& jwt) {
return getJWTPayloadLength((char*)jwt.c_str());
}

int JWT::getJWTPayloadLength(char* jwt) {
char jwtCopy[strlen(jwt)];
memcpy((char*)jwtCopy, jwt, strlen(jwt));
// Get all three jwt parts
const char* sep = ".";
char* token;
token = strtok(jwtCopy, sep);
token = strtok(NULL, sep);
if(token == NULL) {
return -1;
} else {
return decode_base64_length((unsigned char*)token) + 1;
}
}

String JWT::encodeJWT(String& payload) {
char jwt[getJWTLength(payload)];
encodeJWT((char*)payload.c_str(), (char*)jwt);
return String(jwt);
}

void JWT::encodeJWT(char* payload, char* jwt) {
unsigned char* ptr = (unsigned char*)jwt;
// Build the initial part of the jwt (header.payload)
memcpy(ptr, jwtHeader, strlen(jwtHeader));
ptr += strlen(jwtHeader);
*ptr++ = '.';
encode_base64((unsigned char*)payload, strlen(payload), ptr);
ptr += encode_base64_length(strlen(payload));
// Get rid of any padding (trailing '=' added when base64 encoding)
while(*(ptr - 1) == '=') {
ptr--;
}
*(ptr) = 0;
// Build the signature
Sha256.initHmac((const unsigned char*)_psk.c_str(), _psk.length());
Sha256.print(jwt);
// Add the signature to the jwt
*ptr++ = '.';
encode_base64(Sha256.resultHmac(), 32, ptr);
ptr += encode_base64_length(32);
// Get rid of any padding and replace / and +
while(*(ptr - 1) == '=') {
ptr--;
}
*(ptr) = 0;
}

bool JWT::decodeJWT(String& jwt, String& payload) {
int payloadLength = getJWTPayloadLength(jwt);
if(payloadLength > 0) {
char jsonPayload[payloadLength];
if(decodeJWT((char*)jwt.c_str(), (char*)jsonPayload, payloadLength)) {
payload = String(jsonPayload);
return true;
}
}
return false;
}

bool JWT::decodeJWT(char* jwt, char* payload, int payloadLength) {
// Get all three jwt parts
const char* sep = ".";
char* encodedHeader = strtok(jwt, sep);
char* encodedPayload = strtok(NULL, sep);
char* encodedSignature = strtok(NULL, sep);

// Check all three jwt parts exist
if(encodedHeader == NULL || encodedPayload == NULL || encodedSignature == NULL)
{
payload = NULL;
return false;
}

// Build the signature
Sha256.initHmac((const unsigned char*)_psk.c_str(), _psk.length());
Sha256.print(encodedHeader);
Sha256.print(".");
Sha256.print(encodedPayload);

// Encode the signature as base64
unsigned char base64Signature[encode_base64_length(32)];
encode_base64(Sha256.resultHmac(), 32, base64Signature);
unsigned char* ptr = &base64Signature[0] + encode_base64_length(32);
// Get rid of any padding and replace / and +
while(*(ptr - 1) == '=') {
ptr--;
}
*(ptr) = 0;

// Do the signatures match?
if(strcmp((char*)encodedSignature, (char*)base64Signature) == 0) {
// Decode the payload
decode_base64((unsigned char*)encodedPayload, (unsigned char*)payload);
payload[payloadLength - 1] = 0;
return true;
} else {
payload = NULL;
return false;
}
}
64 changes: 64 additions & 0 deletions zerolamp/JWT.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**

Copyright (c) 2016, Interior Automation Ltd.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be
used to endorse or promote products derived from this software without specific prior
written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

**/

#ifndef ARDUINO_JWT_H
#define ARDUINO_JWT_H

#include <Arduino.h>



class JWT {
private:
String _psk;

public:
JWT(String psk);
JWT(char* psk);

// Set a new psk for encoding and decoding JWTs
void setPSK(String psk);
void setPSK(char* psk);

// Get the calculated length of a JWT
int getJWTLength(String& payload);
int getJWTLength(char* payload);
// Get the length of the decoded payload from a JWT
int getJWTPayloadLength(String& jwt);
int getJWTPayloadLength(char* jwt);
// Create a JSON Web Token
String encodeJWT(String& payload);
void encodeJWT(char* payload, char* jwt);
// Decode a JWT and retreive the payload
bool decodeJWT(String& jwt, String& payload);
bool decodeJWT(char* jwt, char* payload, int payloadLength);
};

#endif
Loading