copy code from my personal git repo

This commit is contained in:
Fabrice Bellamy 2026-01-28 17:57:15 +01:00
parent 24d6c84170
commit f9f509c619
28 changed files with 787 additions and 1 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
secrets.h
tests/test-env.sh
build/**
test/**

100
README.md
View file

@ -1,2 +1,100 @@
# bibotron # The Bib O'Tron project
A proof of concept for replacing the raspberry pi that is managing the bib button by an ESP32 microcontroller.
## Features
- Read the status of the bib button using a digital input
- Display the state of the bib button using a trafic lights
- Implement a HTTP server to serve :
- A basic home page with some links
- A bib button status page, that is meant to be included in the future static version of the [bib web site](https://lebib.org/)
- CSS/images are not hosted locally (links to the [bib web site](https://www.lebib.org/))
- A web OTA page to update the firmware through the network
- A REST API to force the button state remotely
- Periodically pings a remote host and displays an error code on the RGB LED in case of failure (blinking orange color)
- Connects to the network through an RJ45 ethernet interface
- Implements Multicast DNS so that its IP address can be found through the bibotron.local name
- Can be powered by USB-C, using a 5V power supply connected to screw terminals or though the ethernet cable if the optional POE module is installed.
## Hardware
The microcontroller board used is a [Waveshare ESP32-S3-ETH](https://www.waveshare.com/wiki/ESP32-S3-ETH).
The RGB LED is a WS2812
## /!\ Warning /!\
This board, doesn't have any protection between the USB 5V line and the POE 5V line. you MUST remove the POE module and/or disconnect the ethernet cable before plugging-in a USB cable, or else the proverbial magic smoke will be released!!!
(potentially on your computer side)
## Pictures
check [the pictures](doc/pictures.md) in the doc directory
## License
[Creative Commons Zero v1.0 Universal](https://creativecommons.org/public-domain/cc0/)
## User guide
### Changing the bibutton state physically from Le Bib
Juste operate the switch that is next to the traffic lighs.
**ToDo :** add a picture of the switch
### Changing the bibutton state remotely
In case the person who last closed Le Bib has forgotten to switch the bibutton state to the closed position, or if the physical switch is broken, it is possible to call a remote API to force the button state.
This can be done from the command line with curl as below :
- To force the bibutton to the closed state :
```
curl -i -u "${bibutton_user}:${bibutton_password}" --request POST --data "{forceState:0}" --header "Content-Type: application/json" ${TARGET}
```
- To force the bibutton to the opened state :
```
curl -i -u "${bibutton_user}:${bibutton_password}" --request POST --data "{forceState:0}" --header "Content-Type: application/json" ${TARGET}
```
**ToDo :** The user and password to use need to be stored in the password manager used by Le Bib
While the bibutton state is being remotely forced to a diffent state than the physical switch state, the red and green traffic lights will be both illuminated at the same time.
As soon as the the physical switch is changed to the same state as the remotely forced state, or the state is remotly forced back to the same state as the physical switch, the "remotly forced" mode will be canceled, and only the red or green light will be on, according the physical switch state.
### Traffic lights
Depending on the bibutton state, the traffic lights will be illuminated as follow :
- Red color (upper light) when the bibutton is closed,
- Green color (bottom light) when button is opened, Solid )
- Orange color (middle light) when the network is down (the last ping to the reverse proxy failed)
- Both Green and Red color at the same time when the bibutton state has been remotely forced to a diffent state than the physical switch
### LED light strip
This is work in progress.
The light strip currently displays the count of requests received to read the bibutton state since the last boot of the microntroller, as a 64 bits binary number.
And there is an effect with sliding LED from the right side to the left side, each time a new request is processed.
It is also possible to change the LEDs through a REST API, that is only accessible from the local network. Check the scripts in the test directory to learn more.
But note that the request count is frequently displayed to the LED strip, which will overwrite the least significant bits of whatever has been set through the API. Also the slifing effect will overwrite everything each time a request is processed.
The LED strip behavior is not functionally usefull for the Bibutton feature. You can hack it any way you want.
## Developer guide
The project is developed using the Arduino IDE and the following libraries :
- FastLED, to drive the addressable RGB LEDs
- EthernetESP32, to drive the SPI ethernet chip (W5500) of the ESP32-S3-ETH
- ESPping, to periodically check network connectivity
- ElegantOTA light, to updade the firmware through the network
- ArduinoJson, to parse json in the rest api handlers
Additionally to these libraries, the esp32 boards package must be installed in Arduino IDE.
Then in the `Tools` menu, select :
- `Board` > `esp32` > `ESP32-S3-Box`
- `Partition scheme` > `16 MB Flash`
- `USB mode" > `Hardware CDC and JTAG`
- `Programmer` > `esptools`
- `Port` > the tty port of the board (if programming through USB)
Before building the souce code, you need to add in your working copy the secret.h file that contains the sensitive information that we not want to publish in the this git repository
**ToDo :** The secret.h needs to be stored in the password manager used by Le Bib
If your are building the source code for a different site than Le Bib, you can create your own secret.h based on secret.h.example
To update the firmware through Web OTA, first create the binary using the menu `Sketch` > `Export compiled Binary`.
Then :
- In your web browser, go to http://bibotron.local/update (or use the local IP address if the MDNS resolution of bibotron.local is not working : 10.13.12.40)
- **ToDo :** The user and password to use need to be stored in the password manager used by Le Bib
- In `OTA Mode` select firmware
- Click on `Select file` and select the file `bibotron.ino.bin` that should have been generated somewhere under the project build folder (do not used any of the other .bin files that have been generated).
- Wait for the upload to complete.
- The ESP32 should automatically restart after the upgrade. (The LEDs and traffic lights will turn off for a while during the reset)
- Check on www.lebib.org that the bibutton state is correctly displayed

455
bibotron.ino Normal file
View file

@ -0,0 +1,455 @@
// SPDX-License-Identifier: CC0-1.0
// Copyright © Fabrice Bellamy 2026
/*
* The Bib O'Tron project
* A proof of concept for replacing the raspberry pi that is managing the bib button by an ESP32 microcontroller.
*
* license : Creative Commons Zero v1.0 Universal
* original source code and documentation : https://git.interhacker.space/12b/bibotron
*/
// Include some libraries
#include <FastLED.h>
#include <Arduino.h>
//#include <WiFi.h>
#include <SPI.h>
#include <EthernetESP32.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <ESPping.h>
#include <ArduinoJson.h>
// You may need to change the below line in libraries/ElegantOTA/src/ElegantOTA.h for the ElegantOTA library to be built with the correct web server type. If you know a better way of doing this feel free to send a pull request ;-)
// #define ELEGANTOTA_USE_ASYNC_WEBSERVER O
#include <ElegantOTA.h>
// Include a non-versioned file that containes the web OTA update credentials (you have to create this file by copying the example one if you do not have it)
#include "secrets.h"
#if !( defined(ESP32) )
#error This code is designed for ESP32_S3 + W5500! Please check your Tools->Board setting. (see the file README.md for the settings to use)
#endif
// LED GPIOs
#define LEDS_PIN_0 21 // The integrated board LED
#define LEDS_PIN_1 2 // External RGB LED(s strip)
// Traffic lights GPIOs
#define LIGHT_RED 39
#define LIGHT_GREEN 40
#define LIGHT_ORANGE 38
#define SPARE_1 37
#define SPARE_2 42
#define SPARE_3 41
// Button GPIO
#define BIB_BUTTON 3 // Digital input for the bib button
// W5500 SPI/Ethernet chip GPIOs
#define W5500_CS 14 // Chip Select pin
#define W5500_RST 9 // Reset pin
#define W5500_INT 10 // Interrupt pin
#define W5500_MISO 12 // MISO pin
#define W5500_MOSI 11 // MOSI pin
#define W5500_SCK 13 // Clock pin
// Number of LEDs for FastLED arrays
#define NUM_LEDS_0 1 // The integrated board LED is only one LED
#define NUM_LEDS_1 64 // LED(s) connected to GPIO LEDS_PIN_1.
// Other settings
#define LEDS_BRIGHTNESS 50 // Min 0, Max 255
#define HEARTBEAT_PERIOD 20 // Cycle time for aniating the heartbeat LED (in ms)
// #define PING_PERIOD 60000 // Cycle time for checking network connectivity by ping of our reverse proxy
#define PING_PERIOD 1000 // Cycle time for checking network connectivity by ping of our reverse proxy
// SPI ethernet chip driver
W5500Driver driver(W5500_CS);
// HTTP Web server
WebServer server(8000);
// LEDs values arrays for FastLED
CRGB leds_0[NUM_LEDS_0];
CRGB leds_1[NUM_LEDS_1];
// Heartbeat related variables
int heartbeat_index = 0;
unsigned long last_heartbeat_time = 0;
unsigned long last_ping_time = 0;
bool is_network_ok = false;
int forced_state = -1;
int button_state = -1;
unsigned long long request_count = 0;
unsigned long long request_chen = 0;
bool clear_msb = false;
// Home web page handler
void handleRoot() {
Serial.println("serving page /");
String message = "Hello,<br>I am <b>Bib O'Tron</b>!<br>Master of buttons and lights.</p>";
message += "<hr><ul>";
message += "<li><a href=\"/bibutton\">Bib button status</a></li>";
message += "<li><a href=\"https://git.distrilab.fr/Charavane/arduino/src/branch/main/bibotron/bibotron\">Source code and documentation</a></li>";
message += "<li><a href=\"/update\">Update firmware via Web OTA</a></li>";
message += "</ul>";
server.send(200, "text/html", message);
}
// `/bibutton` page handler
void handleGetBibbutton() {
Serial.println("serving page /bibutton");
Serial.print(" FreeHeapSize : ");
Serial.println(xPortGetFreeHeapSize());
request_count++;
request_chen|=1;
bool isOpen = false;
if ((forced_state < 0)) {
isOpen = (button_state == HIGH);
} else {
isOpen = (forced_state == HIGH);
}
String message = "<html><head>";
message += "<meta charset=\"UTF-8\">";
message += "<meta http-equiv=\"refresh\" content=\"60\">";
message += "<link rel=\"stylesheet\" href=\"/css/bibstate.css\">";
message += "<link rel=\"stylesheet\" href=\"/css/style.css\">";
// message += "<link rel=\"icon\" type=\"image/png\" href=\"https://lebib.org/sites/default/files/favicon_02.png\"/>";
message += "</head><body>";
message += "<div id=\"bibstate\">";
if (isOpen) {
message += "<div id=\"bibopen\"></div>";
} else {
message += "<div id=\"bibclose\"></div>";
}
message += "<div class=\"bibstatus-status\">";
if (isOpen) {
message += "Le Bib est ouvert";
} else {
message += "Le Bib est Fermé";
}
message += "</div></div></body></html>";
server.send(200, "text/html", message);
Serial.print(" FreeHeapSize : ");
Serial.println(xPortGetFreeHeapSize());
}
// `/bibleds` POST request handler
void handleSetButton() {
if (!server.authenticate(button_user, button_password)) {
return server.requestAuthentication();
}
if (server.method() == HTTP_POST) {
Serial.println("received post request on /bibutton");
String postBody = server.arg("plain");
/* Disable printing the request body for prod
Serial.println(postBody);
*/
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, postBody);
if (error) {
Serial.print(F("Error parsing JSON "));
Serial.println(error.c_str());
String msg = error.c_str();
server.send(400, F("text/html"),
"Error while parsing json data! <br>" + msg);
} else {
JsonObject postObj = doc.as<JsonObject>();
forced_state = postObj["forceState"] | forced_state;
if ((forced_state != -1) && (forced_state != LOW) && (forced_state != HIGH)) {
forced_state = -1;
}
DynamicJsonDocument doc(512);
doc["forced_state"] = forced_state;
doc["button_state"] = button_state;
String buf;
serializeJson(doc, buf);
server.send(200, F("application/json"), buf);
Serial.print(F("done."));
}
}
}
// `/bibutton/leds` POST request handler
void handleSetLeds() {
if (!server.authenticate(leds_user, leds_password)) {
return server.requestAuthentication();
}
if (server.method() == HTTP_POST) {
Serial.println("received post request on /bibutton/leds");
String postBody = server.arg("plain");
/* Disable printing the request body for prod
Serial.println(postBody);
*/
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, postBody);
if (error) {
Serial.print(F("Error parsing JSON "));
Serial.println(error.c_str());
String msg = error.c_str();
server.send(400, F("text/html"),
"Error while parsing json data! <br>" + msg);
} else {
JsonObject postObj = doc.as<JsonObject>();
if (postObj.containsKey("index") && postObj.containsKey("count") && postObj.containsKey("values")) {
int index = postObj["index"] | -1;
int count = postObj["count"] | -1;
JsonArray values = postObj["values"];
if (index >= 0 && index < NUM_LEDS_1 && count > 0 && (index+count) <= NUM_LEDS_1) {
for (int i=0; i<count; i++) {
JsonArray val = values[i];
byte red = val[0];
byte green = val[1];
byte blue = val[2];
/* Disable printing these details for prod
Serial.print(F("Setting led #"));
Serial.print(index+i);
Serial.print(F(" to (r,g,b)=("));
Serial.print(red);
Serial.print(F(","));
Serial.print(green);
Serial.print(F(","));
Serial.print(blue);
Serial.println(F(")"));
*/
leds_1[i] = CRGB(red,green,blue);
}
}else {
DynamicJsonDocument doc(512);
doc["status"] = "Failed";
doc["message"] = F("Invalid index or count value!");
String buf;
serializeJson(doc, buf);
server.send(400, F("application/json"), buf);
Serial.print(F("done."));
}
DynamicJsonDocument doc(512);
doc["status"] = "OK";
String buf;
serializeJson(doc, buf);
server.send(201, F("application/json"), buf);
}else {
DynamicJsonDocument doc(512);
doc["status"] = "Failed";
doc["message"] = F("No data found, or incorrect!");
String buf;
serializeJson(doc, buf);
server.send(400, F("application/json"), buf);
Serial.print(F("done."));
}
}
}
}
// Error 404 handler
void handleNotFound() {
Serial.println("serving page 404");
String message = "Error 404\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void animateLedStrip(void) {
unsigned long long val = request_count ^ request_chen;
int i = 0;
while (i<NUM_LEDS_1 && val>0) {
unsigned bit = val & 1;
if (bit == 1) {
leds_1[NUM_LEDS_1-1-i] = CHSV((i+heartbeat_index)%255,255,255);
} else {
leds_1[NUM_LEDS_1-1-i] = CRGB(0,0,0);
}
val >>= 1;
i++;
}
if (i<NUM_LEDS_1) {
leds_1[NUM_LEDS_1-1-i] = CRGB(0,0,0);
}
if (clear_msb) {
clear_msb = false;
leds_1[0] = CRGB(0,0,0);
}
if (request_chen > 0) {
request_chen <<= 1;
clear_msb = (request_chen == 0);
}
}
// Setup the web OTA update library
void otaSetup(void) {
ElegantOTA.onStart([]() {
Serial.println("OTA update process started.");
});
// This is spitting out too many line to the serial line. Enable it only for debugging purpose.
// ElegantOTA.onProgress([](size_t current, size_t final) {
// Serial.printf("Progress: %u%%\n", (current * 100) / final);
// });
ElegantOTA.onEnd([](bool success) {
if (success) {
Serial.println("OTA update completed successfully.");
} else {
Serial.println("OTA update failed.");
}
});
ElegantOTA.begin(&server);
ElegantOTA.setAuth(ota_user, ota_password);
}
void webServerSetup() {
server.on("/", handleRoot);
server.on("/bibutton", HTTP_GET, handleGetBibbutton);
server.on("/bibutton", HTTP_POST, handleSetButton);
server.on("/bibleds", HTTP_POST, handleSetLeds);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
// Main setup function
void setup() {
// Init serial port
Serial.begin(500000);
Serial.setDebugOutput(true);
Serial.println("Serial port initialized.");
// Safety delay
delay(1000);
// Setup Addressable LEDs
Serial.println("Setting-up the LEDs...");
FastLED.addLeds<WS2812, LEDS_PIN_0, GRB>(leds_0, NUM_LEDS_0);
FastLED.addLeds<WS2812, LEDS_PIN_1, GRB>(leds_1, NUM_LEDS_1);
FastLED.setBrightness(LEDS_BRIGHTNESS);
FastLED.show();
// Setup the GPIO of the bib button as an input with an integrated weak pull-up resistor
Serial.println("Setting-up the button GPIO...");
pinMode(BIB_BUTTON, INPUT_PULLDOWN);
// Setup the GPIOs of the traffic lights as output
pinMode(LIGHT_RED, OUTPUT);
pinMode(LIGHT_GREEN, OUTPUT);
pinMode(LIGHT_ORANGE, OUTPUT);
Serial.println("Setting-up the ethernet interface...");
// Initialize SPI with specified pin configuration
SPI.begin(W5500_SCK, W5500_MISO, W5500_MOSI);
// Initialize Ethernet using DHCP to obtain an IP address
Ethernet.init(driver);
if (Ethernet.begin(mac[SELECTED_MAC]) == 0) {
Serial.println("DHCP failed, falling back to static IP...");
// Initialize with static IP
Ethernet.begin(mac[SELECTED_MAC], myIP, myDNS, myGW, mySN);
} else {
is_network_ok = true;
}
// Print the assigned IP address
Serial.print("IP Address: ");
Serial.println(Ethernet.localIP());
//Setup mdns so that other hosts can resolve our <hostname>.local DNS name
Serial.println("Setting-up the MDNS responder...");
if (!MDNS.begin(hostname)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("Setting-up the web server...");
webServerSetup();
Serial.println("Setting-up the web OTA library...");
otaSetup();
Serial.println("Setup completed :-)");
}
// Main loop function
void loop() {
// Get current time
unsigned long current_time = micros();
// Check if it is time to update the heartbeat LED
if ((current_time - last_heartbeat_time) >= 1000*HEARTBEAT_PERIOD) {
// Update last heartbeat time
last_heartbeat_time = current_time;
// Increment the hue we are displaying on the board LED, and cycle back to 0 when reaching 256
heartbeat_index = (heartbeat_index + 1) % 256;
// Update the LED color in the FastLED array
leds_0[0] = CHSV(heartbeat_index,255,255);
// Read the bibutton status and turn on/off the red and green lights accordingly
button_state = digitalRead(BIB_BUTTON);
if ((forced_state < 0) || forced_state == button_state) {
forced_state = -1;
if (button_state == HIGH) {
digitalWrite(LIGHT_GREEN, HIGH);
digitalWrite(LIGHT_RED, LOW);
} else {
digitalWrite(LIGHT_GREEN, LOW);
digitalWrite(LIGHT_RED, HIGH);
}
} else {
digitalWrite(LIGHT_GREEN, HIGH);
digitalWrite(LIGHT_RED, HIGH);
}
// Turn on/off the orange light according to network connectivity status
if (!is_network_ok) {
digitalWrite(LIGHT_ORANGE, HIGH);
} else {
digitalWrite(LIGHT_ORANGE, LOW);
}
// Do something on the LED strip values
animateLedStrip();
// Update the LEDs with the values we set in the arrays
FastLED.show();
}
// Check if it is time to check the network connectivity
if ((current_time - last_ping_time) >= 1000*PING_PERIOD) {
// ToDo : move that to an asynchronous task as the ping can take a while if the internet is not reachable. But that is not that important, because if the net is down, our main purpose is borked anyway.
is_network_ok = Ping.ping(myProxy, 1);
if (is_network_ok) {
Serial.print("ping time: ");
Serial.print(Ping.averageTime());
Serial.println(" ms");
} else {
Serial.println("Failed to ping remote host!");
}
last_ping_time = micros();
}
// Do nothing for 1 ms
delay(1);
// Let the web server and the web OTA update library do their things
server.handleClient();
ElegantOTA.loop();
}

Binary file not shown.

BIN
doc/ESP32-S3-ETH-pinout.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
doc/bibotron-home-page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
doc/bibotron_20251106.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

8
doc/pictures.md Normal file
View file

@ -0,0 +1,8 @@
# Pictures of the Bib O'Tron project
![top view of the prototype board](./bibotron-prototype-top.jpg "prototype board - top")
![bottom view of the prototype board](./bibotron-prototype-bottom.jpg "prototype board - bottom" )
![bib button status page in the closed state](./bibotron-button-closed.png "bib button status page - closed state")
![bib button status page in the opened state](./bibotron-button-opened.png "bib button status page - opened state")
![home page](./bibotron-home-page.png "home page")
![The bibotron in place @ le bib](./bibotron_20251106.jpg "in place @ le bib")

44
secrets.h.example Normal file
View file

@ -0,0 +1,44 @@
const char* hostname = "bibotron";
const char* ota_user = "adminuser";
const char* ota_password = "b1b0tr0n";
const char* button_user = "poweruser";
const char* button_password = "AllComputersAreBroken";
const char* leds_user = "user";
const char* leds_password = "yolo";
// MAC address and IP address
// You may want to set this to different values if you have several boards connected to your LAN using similar code.
// In the code we are only using the first MAC of the array. But the array is there if you want to change the code to randomly pick one each time the system boots.
#define NUMBER_OF_MAC 20
#define SELECTED_MAC 0
byte mac[][NUMBER_OF_MAC] =
{
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x00 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x01 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x03 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x04 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x05 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x06 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x07 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x08 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x09 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0A },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0B },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0C },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0D },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0E },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0F },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x10 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x11 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x12 },
{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x13 },
};
// Fallback static IPv4 configuration in case DHCP is not working
IPAddress myIP(10, 13, 12, 40);
IPAddress myGW(10, 13, 12, 254);
IPAddress mySN(255, 255, 255, 0);
IPAddress myDNS(10, 13, 12, 254);
// Server to ping to check the network connectivity
IPAddress myProxy(10, 13, 12, 254);

1
tests/button_empty.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1 @@
{forceState:0}

View file

@ -0,0 +1 @@
{forceState:1}

1
tests/leds_3_values.json Normal file
View file

@ -0,0 +1 @@
{index:12,count:3,values:[[255,0,0],[0,255,0],[0,0,255]]}

70
tests/leds_64_values.json Normal file
View file

@ -0,0 +1,70 @@
{
index:0,
count:64,
values:[
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0],
[255,0,0],
[0,255,0],
[0,0,255],
[128,128,0],
[128,0,128],
[0,128,128],
[84,84,84],
[0,0,0]
]
}

70
tests/leds_all-off.json Normal file
View file

@ -0,0 +1,70 @@
{
index:0,
count:64,
values:[
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0]
]
}

View file

@ -0,0 +1 @@
{index:-12,count:3,values:[[255,0,0],[0,255,0],[0,0,255]]}

View file

@ -0,0 +1 @@
{index:1234567,count:3,values:[[255,0,0],[0,255,0],[0,0,255]]}

View file

@ -0,0 +1 @@
{index:12345678901234567890,count:3,values:[[255,0,0],[0,255,0],[0,0,255]]}

View file

@ -0,0 +1 @@
{index:"All Computers Are Broken",count:3,values:[[255,0,0],[0,255,0],[0,0,255]]}

View file

@ -0,0 +1 @@
{index:12,count:32,values:[[255,0,0],[0,255,0],[0,0,255]]}

View file

@ -0,0 +1 @@
{index:12,count:3,values:[[-1,0,0],[0,600,0],[0,0,70000]]}

10
tests/send_one_request.sh Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
source "test-env.sh"
REQUEST=${1:-"./leds_64_values.json"}
TARGET=${test_base_url}${2:-/bibleds}
echo "##########################################"
echo "# sending post request to ${TARGET} with :"
echo "# ${REQUEST}"
echo "##########################################"
curl -i -u "${leds_user}:${leds_password}" --request POST --data "@${REQUEST}" --header "Content-Type: application/json" ${TARGET}
echo

12
tests/send_test_requests.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
source "test-env.sh"
TARGET=${test_base_url}/bibleds
for f in leds*.json; do
echo "##########################################"
echo "# sending post request to ${TARGET} with :"
echo "# ${f}"
echo "##########################################"
time curl -u "${leds_user}:${leds_password}" --request POST --data "@${f}" --header "Content-Type: application/json" ${TARGET}
echo
echo
done

View file

@ -0,0 +1,5 @@
export test_button_user="poweruser"
export test_button_password="AllComputersAreBroken"
export test_leds_user="user"
export test_leds_password="yolo"
export test_base_url="http://10.13.12.40:8000"