Related
Tried using the below code but it does not seem to work. The value of bm is null
BYTE imageData[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x5d, 0x52, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x50,
0x4c, 0x54, 0x45, 0x7a, 0xdf, 0xfd, 0xfd, 0xff, 0xfc, 0x39, 0x4d, 0x52, 0x19, 0x16, 0x15, 0xc3, 0x8d, 0x76, 0xc7,
0x36, 0x2c, 0xf5, 0x00, 0x00, 0x00, 0x40, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x95, 0xc9, 0xd1, 0x0d, 0xc0, 0x20,
0x0c, 0x03, 0xd1, 0x23, 0x5d, 0xa0, 0x49, 0x17, 0x20, 0x4c, 0xc0, 0x10, 0xec, 0x3f, 0x53, 0x8d, 0xc2, 0x02, 0x9c,
0xfc, 0xf1, 0x24, 0xe3, 0x31, 0x54, 0x3a, 0xd1, 0x51, 0x96, 0x74, 0x1c, 0xcd, 0x18, 0xed, 0x9b, 0x9a, 0x11, 0x85,
0x24, 0xea, 0xda, 0xe0, 0x99, 0x14, 0xd6, 0x3a, 0x68, 0x6f, 0x41, 0xdd, 0xe2, 0x07, 0xdb, 0xb5, 0x05, 0xca, 0xdb,
0xb2, 0x9a, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(imageData));
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, imageData, sizeof(imageData));
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Gdiplus::Bitmap* bm = Gdiplus::Bitmap::FromStream(pStream, true);
I can suggest 2 ways to convert PNG to BMP.
You can use third-party libraries. In case of opencv it will be something like that:
Mat image = imread("image.png", -1);
Mat image_bmp;
image.convertTo(image_bmp, CV_8UC3);
imwrite("image.bmp", image_bmp);
In VS you can use GDI+. The following code is runnable in my VS2019.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
INT GetEncoderClsid(const WCHAR* format, CLSID* pClsid); // helper function
INT main()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CLSID encoderClsid;
Status stat;
Image* image = new Image(L"image.png");
// Get the CLSID of the encoder.
GetEncoderClsid(L"image/bmp", &encoderClsid);
stat = image->Save(L"image.bmp", &encoderClsid, NULL);
if (stat == Ok)
printf("Image was saved successfully\n");
else
printf("Failure: stat = %d\n", stat);
delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
As Hardware, I'm using an Arduino Due and a GPS Receiver from u-Blox (https://www.sparkfun.com/products/15005) for my project. I basically want to retrieve a certain message (UBX-RXM-RAWX) via UART. As I will parse this message in post-processing, it would be fine to just read all binary data and store it directly onto an SD card. Unfortunately, not being very experienced in C++, I'm having troubles storing binary data into any file.
I see I'm missing some general knowledge there, and so I wanted to ask if you could help me out? My code is attached as well or can be found on github: https://github.com/dariopa/GPS-Logging-Station/blob/master/GPS%20Station/_UBX_GPS_StoreBinaryMessage_RAWX_DUE/_UBX_GPS_StoreBinaryMessage_RAWX_DUE.ino
Thanks for any help!
// RETRIEVE RAWX MESSAGE FOR RINEX GENERATION.
// Microcontroller: Arduino DUE
// GPS Receiver: NEO-M8P-2 (https://www.sparkfun.com/products/15005)
#include <SD.h>
File binaryFile;
const int CS = 10; // ChipSelect
const char UBLOX_INIT[] PROGMEM = {
// Disable NMEA
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x24, // GxGGA off
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B, // GxGLL off
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32, // GxGSA off
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39, // GxGSV off
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x40, // GxRMC off
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47, // GxVTG off
// Disable UBX
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0x02, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x46, // RXM-RAWX off
// Enable UBX
0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0x02, 0x15, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x27, 0x4B, // RXM-RAWX on
// Rate
0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xE8, 0x03, 0x01, 0x00, 0x01, 0x00, 0x01, 0x39, //(1Hz)
// 0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xD0, 0x07, 0x01, 0x00, 0x01, 0x00, 0xED, 0xBD, // (0.5Hz)
// 0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xB8, 0x0B, 0x01, 0x00, 0x01, 0x00, 0xD9, 0x41, // (0.33Hz)
};
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
delay(3000);
// send configuration data in UBX protocol
for (int i = 0; i < sizeof(UBLOX_INIT); i++) {
Serial1.write( pgm_read_byte(UBLOX_INIT + i) );
Serial.write( pgm_read_byte(UBLOX_INIT + i) );
delay(10); // simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
}
// SD CARD
// Initialize SD Card
pinMode(CS, OUTPUT);
if (!SD.begin(CS)) {
Serial.println("Initialization of SD card failed - Freeze!");
while (1) {}
}
else {
Serial.println("Initialization done.");
}
}
void loop() {
if (Serial1.available()) {
// read from port serial, send to port Serial:
char Coord[300] = {Serial1.read()};
Serial.write(Coord);
binaryFile = SD.open("Data.bin", FILE_WRITE);
if (binaryFile) {
binaryFile.println(Coord);
}
}
}
char Coord[300] = {Serial1.read()};
This initialized Coord[0] to the return of Serial1.read(). The rest of 299 member of Coord array are initialized with zero.
If Serial1.read() returns an integer int. If it's -1 than read was not succcessfull. Otherwise it's one valid character.
You want to read one character at a time and store it:
void setup() {
...
// is there a point in opening the binaryFile each loop!?
binaryFile = SD.open("Data.bin", FILE_WRITE);
if (!binaryFile) {
// handle errror
assert(0);
}
}
void loop() {
if (Serial1.available()) {
int ci = Serial1.read();
if (ci == -1) {
// handle errpr
return;
}
char c = ci;
Serial.write(c);
binaryFile.write(c);
}
}
I'm trying to use Ncrypt.lib to encrypt plain text with AES and then decrypt it.
I use Ncrypt.lib because I want to use a persistent symetric key.
My problem is that the decryption works partially. Indeed, I don't have my first 16 bytes decrypted correctly.
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <ncrypt.h>
#include <bcrypt.h>
void PrintBytes(
IN BYTE *pbPrintData,
IN DWORD cbDataLen) {
DWORD dwCount = 0;
for (dwCount = 0; dwCount < cbDataLen; dwCount++) {
printf("0x%02x, ", pbPrintData[dwCount]);
if (0 == (dwCount + 1) % 10) putchar('\n');
}
}
int main() {
BYTE plaintext[] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
static const int plainTextLen = 48;
printf("PlainText:\n");
PrintBytes(plaintext, plainTextLen);
printf("\n");
LPCWSTR keyName = L"NCryptTest";
SECURITY_STATUS status;
NCRYPT_PROV_HANDLE hProvider;
NCRYPT_KEY_HANDLE hKey;
// Open storage provider
status = NCryptOpenStorageProvider(&hProvider, NULL, 0);
// Get stored key
status = NCryptOpenKey(hProvider, &hKey, keyName, 0, 0);
if (status == NTE_BAD_KEYSET) {
// Create key if it doesn't exist
status = NCryptCreatePersistedKey(hProvider, &hKey, BCRYPT_AES_ALGORITHM, keyName, 0, 0);
status = NCryptFinalizeKey(hKey, 0);
}
// Set the chaining mode to cipher feedback
LPCWSTR chainMode = BCRYPT_CHAIN_MODE_CFB;
status = NCryptSetProperty(hKey, NCRYPT_CHAINING_MODE_PROPERTY,
(PBYTE)chainMode, wcslen(chainMode) * 2 + 2, 0);
// Random iv but here, it's fixed
//char* iv = "0123456789abcdef";
//status = NCryptSetProperty(hKey, BCRYPT_INITIALIZATION_VECTOR,
//(PBYTE)iv, 16, 0);
// Get size of the cipher text
DWORD cbCipherText = 0;
status = NCryptEncrypt(hKey, plaintext, plainTextLen, NULL, NULL, 0,
&cbCipherText, 0);
PBYTE pbCipherText = NULL;
pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
if (pbCipherText == NULL) {
printf("Error! memory allocation failed\n");
}
// Encrypt
DWORD outlen = -1;
status = NCryptEncrypt(hKey, plaintext, plainTextLen, NULL, pbCipherText,
cbCipherText, &outlen, 0);
printf("CipherText:\n");
PrintBytes(pbCipherText, cbCipherText);
printf("\n");
// Get size of the plain text
DWORD cbPlainText = 0;
status = NCryptDecrypt(hKey, pbCipherText, cbCipherText, NULL, NULL, 0,
&cbPlainText, 0);
PBYTE pbPlainText = NULL;
pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPlainText);
if (pbPlainText == NULL) {
printf("Error! memory allocation failed\n");
}
// Decrypt
outlen = -1;
status = NCryptDecrypt(hKey, pbCipherText, cbCipherText, NULL,
pbPlainText, cbPlainText, &outlen, 0);
printf("PlainText:\n");
PrintBytes(pbPlainText, cbPlainText);
printf("\n");
// Cleanup
NCryptFreeObject(hKey);
NCryptFreeObject(hProvider);
getchar();
return 0;
}
And the result is :
PlainText:
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
CipherText:
0xc5, 0xdc, 0x7e, 0xde, 0x83, 0x35, 0xbc, 0x34, 0x27, 0x4b,
0xf9, 0xde, 0x40, 0x36, 0xeb, 0x6d, 0xaf, 0x51, 0x8c, 0x48,
0x69, 0xa0, 0x16, 0xfb, 0x6d, 0x80, 0x44, 0xea, 0x5c, 0x74,
0x27, 0x38, 0xf1, 0x20, 0xa3, 0x87, 0x65, 0xc3, 0xcf, 0x62,
0x94, 0x84, 0xc9, 0xcd, 0x55, 0x4c, 0x7b, 0x48,
PlainText:
0x1d, 0x52, 0x88, 0x1b, 0x0c, 0x01, 0x13, 0xed, 0xe0, 0x39,
0x1e, 0x96, 0x67, 0x39, 0x72, 0x38, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
I suspect a initialisation vector problem but I don't know how to use it, simply with BCRYPT_INITIALIZATION_VECTOR ? or I must place the random iv in front of the plain text ?
Thanks for your help.
Answer in comment:
this is because every success call NCryptEncrypt or NCryptDecrypt change state of the hKey. so you can not use the same key. after you encrypt - you need again obtaining key for decrypt – RbMm
Thank you #RbMm !
As part of firmware, I want to save a graphic or graphics into the EEPROM of a MCU. The space is not much, 1K, however it can save some program space. And yes you can seperate the glyphs of the graphic to save space however it is not easy to manage and you need more code to display it right.
Most of the monochrome GUI graphics do not fill the screen entirely and contain allot of empty space or repeating pixels. The images are already compressed, each bit in a byte represent 8 pixels.
I show the graphics on a tiny display of 128x32 pixels. Display it and erase the parts that are not relevant, works perfectly fine and efficient.
So I came up with the idea to filter those repeats, to compress it a little. With success, a bitmap like this (see below), is 496 bytes and 'compressed' with my method less 401 bytes.
That doesn't sound much however is a 20% decrease in total size, really great when there is only 1K storage available.
Example of byte array:
PROGMEM const uint8_t TEP_DISPLAY [] = { /* 496 bytes */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x80, 0x90, 0x00, 0x3E, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x00, 0x47, 0x0F, 0xFE,
0x17, 0x01, 0xC0, 0x90, 0x00, 0x30, 0x00, 0x00, 0x03, 0x60, 0x01, 0x80, 0x01, 0x87, 0x10, 0x02,
0x30, 0x83, 0xE3, 0xFC, 0x00, 0x61, 0xE7, 0x39, 0xB6, 0x6F, 0x0F, 0x00, 0x03, 0x07, 0x36, 0xDA,
0x7F, 0xF0, 0x83, 0xFC, 0x7C, 0x7D, 0xB3, 0x6D, 0xB6, 0x61, 0x9B, 0x1F, 0x03, 0x87, 0x36, 0xDA,
0x30, 0x43, 0xE1, 0xF8, 0x00, 0x61, 0xB3, 0x6D, 0xA7, 0xCF, 0xB3, 0x00, 0x01, 0x80, 0x36, 0xDA,
0x13, 0x81, 0xC0, 0x60, 0x00, 0xC3, 0x66, 0x6D, 0xCC, 0x1B, 0x36, 0x00, 0x01, 0x07, 0x10, 0x02,
0x03, 0x00, 0x80, 0x60, 0x00, 0xFB, 0x66, 0x39, 0x8C, 0x0F, 0x1E, 0x00, 0x02, 0x07, 0x0F, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA2, 0xD5, 0x54,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02,
0x00, 0xC0, 0x22, 0x00, 0x08, 0x00, 0x02, 0x20, 0x00, 0x82, 0x48, 0x20, 0x00, 0x08, 0x00, 0x00,
0x40, 0xC0, 0x01, 0xE0, 0x00, 0x01, 0xC0, 0x1E, 0x00, 0x01, 0x50, 0x00, 0xFE, 0x00, 0x0C, 0x02,
0x00, 0xC0, 0x20, 0x10, 0x08, 0x07, 0xC2, 0x01, 0x00, 0x80, 0x00, 0x21, 0x01, 0x08, 0x0E, 0x00,
0x4F, 0xFC, 0x00, 0xFE, 0x00, 0x0F, 0x40, 0x3F, 0xF8, 0x03, 0xF8, 0x03, 0x01, 0x80, 0x0B, 0x02,
0x1C, 0xC2, 0x21, 0x11, 0x08, 0x1C, 0x42, 0x40, 0x04, 0x84, 0x04, 0x21, 0x11, 0x08, 0x69, 0x80,
0x59, 0xE2, 0x01, 0x11, 0x00, 0x18, 0x40, 0x55, 0x54, 0x05, 0x54, 0x03, 0x39, 0x80, 0x3B, 0x02,
0x12, 0xD2, 0x21, 0x11, 0x08, 0x10, 0x42, 0x40, 0x04, 0x84, 0x04, 0x21, 0x7D, 0x08, 0x1E, 0x00,
0x54, 0xCA, 0x01, 0x83, 0x00, 0x10, 0x40, 0x55, 0x54, 0x05, 0x54, 0x03, 0x11, 0x80, 0x3E, 0x02,
0x12, 0x12, 0x21, 0x01, 0x08, 0x11, 0xC2, 0x40, 0x04, 0x84, 0x04, 0x21, 0x11, 0x08, 0x6B, 0x00,
0x51, 0xE2, 0x01, 0x01, 0x00, 0x13, 0xC0, 0x47, 0xC4, 0x04, 0x44, 0x01, 0x11, 0x00, 0x09, 0x82,
0x10, 0x02, 0x21, 0x01, 0x08, 0x71, 0x82, 0x40, 0x04, 0x84, 0x04, 0x23, 0x01, 0x88, 0x0B, 0x00,
0x4F, 0xFC, 0x01, 0xFF, 0x00, 0xF0, 0x00, 0x3F, 0xF8, 0x05, 0x54, 0x01, 0x01, 0x00, 0x0E, 0x02,
0x0F, 0xFC, 0x20, 0xFE, 0x08, 0x60, 0x02, 0x1F, 0xF0, 0x84, 0x04, 0x20, 0xFE, 0x08, 0x0C, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
};
Still there is one problem and I think it is a small error in code and I cannot detect it (because spend a couple days on it to think about how to get it some smaller). Maybe there is somebody that can point me into the right direction to solve the problem.
The problem
The problem occurs when there are allot of similarties, more than 255 repetitions of the same, such as many 0xFF repeating lines or 0x00 repeating empty spaces. In my code I take some precautions to avoid a byte overflow however it fails (and now cannot figure out why). What I try to do is when there is an overflow, record it and start allover again with counting. I cannot figure out it is a problem of the read function or just the write function.
This is the layout of storage
At start address:
-----------------
<byte width>
<byte heigth>
<uint16 dataSize>
<data>
<if data=0xFF>
<0xFF>
<repeat count>
</if>
<if data=0x00>
<0x00>
<repeat count>
</if>
<else data>
</data>
Here is my code:
uint16_t TOLEDdisplay::writeToEeprom( uint16_t iAddress )
{
if( width == 0 || height == 0 || cacheSize == 0 )
{ return 0; }
uint8_t iZeros = 0;
uint8_t iFFs = 0;
bool bIsZero = false;
bool bIsFF = false;
bool bZeroOverflow = false;
bool bFFOverflow = false;
uint16_t iBits = 0;
uint8_t* pByteSize = (uint8_t*)&iBits;
uint8_t iZeroCount = 0; // empty stripes , same pixels in a row
uint8_t iFFCount = 0; // filled stripes, same pixels in a row
// Write screen bounds, when read it back with readFromEeprom,
// this bounds must match with the current screen bounds.
EEPROM.write( iAddress++, width );
EEPROM.write( iAddress++, height );
// Reserve two bytes for stream size
uint16_t iSizeAddress = iAddress++;
++iAddress;
// Write the cache content to the EEPROM
uint16_t i = 0;
while( i < cacheSize )
{
iBits = getCacheRawBits( i );
//iBits = displayCache[ i ];
bIsFF = ( iBits == 0xFF );
bIsZero = ( iBits == 0x00 );
if( bIsFF && !bFFOverflow )
{ ++iFFs; }
bFFOverflow = (iFFs == 0xFF);
if( bIsZero && !bZeroOverflow )
{ ++iZeros; }
bZeroOverflow = (iZeros == 0xFF);
if( (!bIsFF && !bIsZero) || bFFOverflow || bZeroOverflow )
{
if( (!bIsFF && iFFs > 0) || bFFOverflow )
{
// Read function knows if there is a 0xFF, amount of 0xFF
// will be follow.
EEPROM.write( iAddress++, 0xFF );
// Write the amount of FF's
EEPROM.write( iAddress++, iFFs );
iFFCount+=iFFs;
// If there is no byte 'overflow' iFFs = 0, otherwise it is 1
iFFs = (uint8_t)bIsFF;
}
if( (!bIsZero && iZeros > 0) || bZeroOverflow )
{
// Read function knows if there is a zero, amount of zeros
// will be follow.
EEPROM.write( iAddress++, 0 );
// Write the amount of zero's
EEPROM.write( iAddress++, iZeros );
iZeroCount+=iZeros;
// If there is no byte 'overflow' iZeros = 0, otherwise it is 1
iZeros = (uint8_t)bIsZero;
}
// Avoid confusion writing a FF or zero
if( !bIsFF && !bIsZero )
{ EEPROM.write( iAddress++, iBits ); }
}
++i;
}
// Calculate stream size
iBits=iAddress-iSizeAddress-1;
// Write size of stream
EEPROM.write( iSizeAddress , *pByteSize++ );
EEPROM.write( iSizeAddress+1, *pByteSize );
Serial.print( "Zeros found: " );
Serial.println( iZeroCount );
Serial.print( "FF found: " );
Serial.println( iFFCount );
Serial.print( "SIZE: " );
Serial.println( iBits );
// return bytes written
return iBits+2;
}
bool TOLEDdisplay::readFromEeprom( uint16_t iAddress )
{
uint8_t bits = 0;
uint16_t i = 0;
uint8_t* pI = (uint8_t*)&i;
uint8_t iZeros = 0;
uint8_t iFFs = 0;
uint8_t iWidth = EEPROM.read( iAddress++ );
uint8_t iHeight = EEPROM.read( iAddress++ );
// Read stream size, read two bytes
*pI = EEPROM.read( iAddress++ );
*pI++;
*pI = EEPROM.read( iAddress++ );
// Clear the screen
clear();
Serial.print( "Size: " );
Serial.println( i );
Serial.print( "Width: " );
Serial.println( iWidth );
Serial.print( "Height: " );
Serial.println( iHeight );
// If an error (no image on EEPROM address) or screen bounds
// doesn't match, skip to continue
if( i == 0 || iWidth != width || iHeight != height )
{
update( true );
return false;
}
uint16_t iCacheAddress = 0;
while( i-- )
{
do {
if( iFFs == 0 && iZeros == 0 )
{
bits = EEPROM.read( iAddress++ );
if( bits == 0xFF )
{
// read amount of FF's minus this one
iFFs = EEPROM.read( iAddress++ )-1;
Serial.print( "iFFs: ");
Serial.println( iFFs );
}
else if( bits == 0x00 )
{
// read amount of zeros minus this one
iZeros = EEPROM.read( iAddress++ )-1;
Serial.print( "iZeros: ");
Serial.println( iZeros );
}
}
else {
if( iFFs > 0 )
{
--iFFs;
bits = 0xFF;
}
else if( iZeros > 0 )
{
--iZeros;
bits = 0x00;
}
}
setCacheRawBits( iCacheAddress, bits );
++iCacheAddress;
}
while( iFFs == 0 && iZeros == 0 );
}
update( true );
return true;
}
Any ideas?
NOTE:
I don't want to use any expensive compression method, 96% of program space is already in use and my method seems to work fine but with some error and I need to know the error, no alternative compression method. It has some compression already, bits in a byte to represent 8 pixels and just want to slim it down a bit (proven however with error at byte overflow).
The first time thru the loop, bFFOverflow and bZeroOverflow are accessed without having been initialized.
The main problem, though, is that after you record your 255 0 or 0xFF bytes, you set the count to 1 if there are more. However, this should be zero, since you detect the overflow after you've counted the 255th copy of that byte.
So always set bFFOverflow and bZeroOverflow to 0 writing out the counts.
After some sleep I redone it with much better results and less code, I overcomplicated such thing too much.
I got impressive results with it and think about to refine it with an examination method to find the best 'compression' by selecting the bytes that repeats the most and record this into the EEPROM 'file'.
Anyway, this is my code, much better comparing to the first one, maybe it can help others. It is very lightweight solution to save some bytes.
For example a blank screen or full filled screen with a resolution of 128x32 pixels results in only 9 bytes, half by half only 17 bytes. The screen I showed in the question before 'compiles' to only 405 bytes, a saving of about 100 bytes.
Here is my renewed code:
uint8_t TOLEDdisplay::getCacheRawBits( uint16_t iAddress )
{
if( iAddress < cacheSize )
{ return displayCache[ iAddress ]; }
return 0x00;
}
bool TOLEDdisplay::setCacheRawBits( uint16_t iAddress, uint8_t iBitByte )
{
if( iAddress < cacheSize )
{
displayCache[ iAddress ]=iBitByte;
return true;
}
return false;
}
uint16_t TOLEDdisplay::writeToEeprom( uint16_t iAddress )
{
if( cacheSize == 0 || width == 0 || height == 0 )
{ return 0; }
uint8_t iBits; // Pixel * 8 = byte
uint8_t iFoundBits; // 'Type' of detected
uint16_t iByteSize = 0; // Total byte size of stream
uint8_t iCount = 0; // Count of repeats found
bool bSame; // Boolean to evaluate repeats
// Write screen bounds, when read it back with readFromEeprom,
// these bounds need to match with current screen bounds.
EEPROM.write( iAddress++, width );
EEPROM.write( iAddress++, height );
// Reserve two bytes for stream size
uint16_t iSizeAddress = iAddress;
iAddress+=2;
// Write the cache content to the EEPROM
uint16_t i = 0;
while( i < cacheSize )
{
// Get a byte with bits
iBits = getCacheRawBits( i );
++i;
// Find repeating lines or empty lines
if( iBits == 0xFF || iBits == 0x00 )
{
iFoundBits = iBits; // Set found bits to detect changes
bSame = true; // Set to true to able to start loop
iCount=1; // Count this found one
// Loop to find duplicates
while( bSame && ( iCount < 0xFF ) && ( i < cacheSize ))
{
iBits = getCacheRawBits( i ); // Get next byte with bits
bSame = (iBits == iFoundBits); // Determine is repeat, the same
iCount+=bSame; // Increment count when same is found
i+=bSame;
}
// Finally write result to EEPROM
EEPROM.write( iAddress++, iFoundBits ); // type
// Write the amount
EEPROM.write( iAddress++, iCount ); // count
// Goto main loop and find next if any
}
else {
// Write found value normally to EEPROM
EEPROM.write( iAddress++, iBits );
}
}
// Final EOF address is one pos back
--iAddress;
// Calculate stream size
iByteSize=iAddress-iSizeAddress;
uint8_t* pByteSize = (uint8_t*)&iByteSize;
// Write size of stream
EEPROM.write( iSizeAddress , *pByteSize++ );
EEPROM.write( iSizeAddress+1, *pByteSize );
// return bytes written including width and height bytes (+2 bytes)
return iByteSize+2;
}
bool TOLEDdisplay::readFromEeprom( uint16_t iAddress )
{
uint8_t iBits;
uint8_t iRepeats;
uint16_t i = 0;
uint8_t* pI = (uint8_t*)&i;
uint8_t iWidth = EEPROM.read( iAddress++ );
uint8_t iHeight = EEPROM.read( iAddress++ );
// Read stream size, read two bytes
*pI = EEPROM.read( iAddress++ );
*pI++;
*pI = EEPROM.read( iAddress++ );
// Clear the screen
clear();
// If an error (no image on EEPROM address) or screen bounds
// doesn't match, skip to continue
if( i == 0 || iWidth != width || iHeight != height )
{
update( true ); // Set screen to blank
return false;
}
uint16_t iCacheAddress = 0;
while( i-- )
{
// Get a byte with bits
iBits = EEPROM.read( iAddress++ );
// Explode repeats if detected
if( iBits == 0xFF || iBits == 0x00 )
{
// read amount of repeats
iRepeats = EEPROM.read( iAddress++ );
// Explode it into cache
while( iRepeats-- )
{ setCacheRawBits( iCacheAddress++, iBits ); }
}
else {
// Put value normally into cache
setCacheRawBits( iCacheAddress++, iBits );
}
}
// Done, update the screen
update( true );
// Return success
return true;
}
Maybe I have to add some EEPROM boundry checks but for now it works fine.
context->last_error = NULL;
const BYTE key[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// BLOBHEADER
0x00, 0x00, 0x00, 0x00,// Key size
0x23, 0x31, 0xb1, 0x24, 0x7b, 0x15, 0xdf, 0xb0,// AES KEY
0xc9, 0x92, 0xaa, 0xc4, 0x2e, 0x02, 0x9b, 0x07,
0xdf, 0x21, 0x12, 0x53, 0xba, 0x28, 0x77, 0xd2,
0x99, 0x74, 0x96, 0xa4, 0x54, 0x54, 0x0d, 0xf1 };
BLOBHEADER* hdr = (BLOBHEADER*)key;
hdr->aiKeyAlg = CALG_AES_256;
hdr->bType = PLAINTEXTKEYBLOB;
hdr->bVersion = CUR_BLOB_VERSION;//default value
hdr->reserved = NULL;//reserved, default value
LPDWORD key_size = (LPDWORD)&key[sizeof(BLOBHEADER)];
//*(DWORD *)(key + 0x14) = 0;
*key_size = AES_KEY_SIZE_;
if (!CryptImportKey(context->context, key, 32, 0, CRYPT_EXPORTABLE, &context->aes_hKey))
{
return context->last_error = GetLastError();
}
return context->last_error;
Works with Windows 7, this is not an exported key, but Fails on windows 8.1
If anyone has found a solution to this, it would help the entire internet.
dwDataLen param for CryptImportKey was set to 32, should be the total size of the byte array.
The issue is this works on windows 7 :\
sorry guys