//
// MAX7219 demo program
//
// Copyright (c) 2018 BitBank Software, Inc.
// Written by Larry Bank
// email: bitbank@pobox.com
// Project started 3/10/2018
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "max7219.h"
#include "helper.h"
#include "types.h"
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "max7219_c"
#define QOS 1
#define TIMEOUT 10000L
volatile MQTTClient_deliveryToken deliveredtoken;
struct scrolltext_t stWeather, stDisplay, stTime, stDate;
int drawString(struct scrolltext_t *st, char *cTemp) {
if(st->width+6 >= SCROLL_BUF_SIZE) {
printf("ERROR: no space for scrolltext\n");
return 0;
}
char buf[64];
snprintf(buf, sizeof(buf), "%s", cTemp);
int width = strlen(buf)*6;
if(width + st->width > SCROLL_BUF_SIZE) {
width = (SCROLL_BUF_SIZE - st->width)/6*6;
buf[width/6] = '\0';
printf("WARNING: scrolltext cropped\n");
}
maxScrollBitmap(st->bImg, SCROLL_BUF_SIZE/8, (st->width % 8));
maxDrawString(buf, (st->bImg) + st->width/8, SCROLL_BUF_SIZE/8, 1); // draw narrow digits
maxScrollBitmap(st->bImg, SCROLL_BUF_SIZE/8, -(st->width % 8));
printf("new scroll text, offset: %d:%d, width: %d, text: %s\n", st->width/8, st->width%8, width, buf);
st->width = st->width + width;
return width;
}
int drawGraph(struct scrolltext_t *st, struct weather_t data[], size_t field_offset, int count, float min, float max) {
int width = st->width;
for(int i=0; iwidth + sizeof(EASE_IN) < SCROLL_BUF_SIZE) {
memcpy(st->frameCount + st->width-sizeof(EASE_OUT)+2, EASE_OUT, sizeof(EASE_OUT)-1);
memcpy(st->frameCount + st->width+1, EASE_IN, sizeof(EASE_IN)-1);
st->frameCount[st->width+1] = 0xFF;
}
int col = st->width++;
float field = *((float*)(&data[i].valid+field_offset/sizeof(float)));
int val = fmax(fmin((field-min) / (max-min) * 8, 7), 0);
st->bImg[col/8 + (7-val)*SCROLL_BUF_SIZE/8] |= 0x80 >> (col%8);
}
}
return st->width - width;
}
void stReset(struct scrolltext_t *st) {
memset(st->bImg, 0, SCROLL_BUF_SIZE);
st->width = 0;
st->frame = 0;
memset(st->frameCount, 2, sizeof(st->frameCount));
memcpy(st->frameCount, EASE_IN, sizeof(EASE_IN)-1);
}
void delivered(void *context, MQTTClient_deliveryToken dt)
{
printf("Message with token value %d delivery confirmed\n", dt);
deliveredtoken = dt;
}
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
int i;
char* payloadptr;
printf("Message arrived\n");
printf(" topic: %s\n", topicName);
printf(" message: ");
umlauts(message->payload, message->payloadlen);
payloadptr = message->payload;
for(i=0; ipayloadlen; i++)
{
putchar(*payloadptr);
if(*payloadptr > 0x7F)
printf("[%X]", *payloadptr);
payloadptr++;
if (i >= 300) break;
}
putchar('\n');
if(strcmp(topicName, "Room/display") == 0) {
char displayMsg[128];
snprintf(displayMsg, sizeof(displayMsg), "%s", (char*)message->payload);
stReset(&stDisplay);
drawString(&stDisplay, displayMsg);
} else if(strcmp(topicName, "Timer/weather") == 0) {
struct json_object *parsed_json, *detail, *tempc, *humidity, *clouds, *windspeed;
parsed_json = json_tokener_parse(message->payload);
json_object_object_get_ex(parsed_json, "detail", &detail);
json_object_object_get_ex(parsed_json, "tempc", &tempc);
json_object_object_get_ex(parsed_json, "humidity", &humidity);
json_object_object_get_ex(parsed_json, "clouds", &clouds);
json_object_object_get_ex(parsed_json, "windspeed", &windspeed);
char weatherMsg[128];
snprintf(
weatherMsg, sizeof(weatherMsg),
"%s %.1f'C LF %d%% WLK %d%% %.1f m/s",
json_object_get_string(detail),
json_object_get_double(tempc),
json_object_get_int(humidity),
json_object_get_int(clouds),
json_object_get_double(windspeed)
);
} else if(strcmp(topicName, "Timer/forecast") == 0) {
time_t rawtime;
time(&rawtime);
struct json_object *parsed_json, *forecast, *tmp, *fmain, *weather, *clouds, *rain, *snow, *wind;
parsed_json = json_tokener_parse(message->payload);
int count = json_object_array_length(parsed_json);
if(count > 40)
count = 40;
struct weather_t data[40], maxData;
memset(data, 0, sizeof(data));
memset(&maxData, 0, sizeof(maxData));
int dataPoints = 0;
float temp_min = 100, rain_sum = 0, snow_sum = 0;
for(int i=0; i rawtime) {
data[i].valid = 1;
dataPoints++;
data[i].dt = timestamp;
maxData.dt = timestamp;
} else {
continue;
}
float val;
json_object_object_get_ex(forecast, "main", &fmain);
if(json_object_object_get_ex(fmain, "temp", &tmp)) {
val = json_object_get_double(tmp);
data[i].temp = val;
if(val > maxData.temp) maxData.temp = val;
if(val < temp_min) temp_min = val;
}
json_object_object_get_ex(forecast, "weather", &weather);
if(json_object_object_get_ex(json_object_array_get_idx(weather, 0), "description", &tmp)) {
//const char *desc = json_object_get_string(tmp);
}
json_object_object_get_ex(forecast, "clouds", &clouds);
if(json_object_object_get_ex(clouds, "all", &tmp)) {
val = json_object_get_double(tmp);
data[i].clouds = val;
if(val > maxData.clouds) maxData.clouds = val;
}
json_object_object_get_ex(forecast, "wind", &wind);
if(json_object_object_get_ex(wind, "speed", &tmp)) {
val = json_object_get_double(tmp);
data[i].wind = val;
if(val > maxData.wind) maxData.wind = val;
}
json_object_object_get_ex(forecast, "rain", &rain);
if(json_object_object_get_ex(rain, "3h", &tmp)) {
val = json_object_get_double(tmp);
data[i].rain = val;
if(val > maxData.rain) maxData.rain = val;
rain_sum += val;
}
json_object_object_get_ex(forecast, "snow", &snow);
if(json_object_object_get_ex(snow, "3h", &tmp)) {
val = json_object_get_double(tmp);
data[i].snow = val;
if(val > maxData.snow) maxData.snow = val;
snow_sum += val;
}
}
printf(
"dataPoints: %d, maxTemp: %.1f°C, maxClouds: %.1f%%, maxWind: %.1fm/s, rain: %.1fmm, snow: %.1fmm\n",
dataPoints,
maxData.temp,
maxData.clouds,
maxData.wind,
rain_sum,
snow_sum
);
char tmpStr[32];
struct tm *maxTm = localtime(&maxData.dt);
stReset(&stWeather);
sprintf(tmpStr, "Wetter bis %d.%d.: ", maxTm->tm_mday, maxTm->tm_mon+1);
drawString(&stWeather, tmpStr);
sprintf(tmpStr, "%.0f'C - %.0f'C ", temp_min, maxData.temp);
drawString(&stWeather, tmpStr);
drawGraph(&stWeather, data, offsetof(struct weather_t, temp), count, temp_min, maxData.temp);
if(maxData.wind > 3.0) {
sprintf(tmpStr, " Wind: %.1fm/s ", maxData.wind);
drawString(&stWeather, tmpStr);
drawGraph(&stWeather, data, offsetof(struct weather_t, wind), count, 0, maxData.wind);
}
if(maxData.clouds > 10.0) {
sprintf(tmpStr, " Wlk: %.1f%% ", maxData.clouds);
drawString(&stWeather, tmpStr);
drawGraph(&stWeather, data, offsetof(struct weather_t, clouds), count, 0, maxData.clouds);
}
if(rain_sum > 1.0) {
sprintf(tmpStr, " Regen: %.1fmm ", rain_sum);
drawString(&stWeather, tmpStr);
drawGraph(&stWeather, data, offsetof(struct weather_t, rain), count, 0, maxData.rain);
}
if(snow_sum > 1.0) {
sprintf(tmpStr, " Schnee: %.1fmm ", snow_sum);
drawString(&stWeather, tmpStr);
drawGraph(&stWeather, data, offsetof(struct weather_t, snow), count, 0, maxData.snow);
}
json_object_put(parsed_json);
}
MQTTClient_freeMessage(&message);
MQTTClient_free(topicName);
return 1;
}
void connlost(void *context, char *cause)
{
printf("\nConnection lost\n");
printf(" cause: %s\n", cause);
}
void drawTime(uint8_t *bImg, time_t *t) {
struct tm *now_tm = localtime(t);
char cTemp[6];
snprintf(
cTemp, sizeof(cTemp),
"%2d%c%02d",
now_tm->tm_hour,
now_tm->tm_sec%2 ? ':' : ' ',
now_tm->tm_min
);
memset(bImg, 0, 4*8);
maxDrawString(cTemp, bImg, 4, 1);
}
void drawDate(uint8_t *bImg, time_t *t) {
struct tm *now_tm = localtime(t);
char cTemp[10];
snprintf(
cTemp, sizeof(cTemp),
"%hhu.%hhu.",
now_tm->tm_mday,
now_tm->tm_mon+1
);
memset(bImg, 0, 4*8);
cTemp[6] = '\0';
maxDrawString(cTemp, bImg, 4, 1);
}
int drawScroll(uint8_t *bImg, struct scrolltext_t *st) {
for(int i=0; i<8; i++) {
if(st->offset < st->width && (st->bImg[i*SCROLL_BUF_SIZE/8 + st->offset/8] & (0x80 >> st->offset%8))) {
bImg[i*4+3] |= 0x01;
} else {
bImg[i*4+3] &= 0xFE;
}
}
return st->offset != 0;
}
int moveScroll(uint8_t *bImgCurrent, uint8_t *bImgTarget, struct scrolltext_t *st) {
int busy = 0;
int frames = (st->offset == 0) ? 0 : (st->frameCount)[st->offset-1];
if(st->width > 32 && ++(st->frame) >= frames) {
st->frame = 0;
maxScrollBitmap(bImgCurrent, 4, 1);
maxScrollBitmap(bImgTarget, 4, 1);
for(int i=0; i<8; i++) bImgCurrent[i*4+3] &= 0xFE;
busy = drawScroll(bImgTarget, st);
if(++(st->offset) >= st->width+32 || st->offset >= SCROLL_BUF_SIZE) {
st->offset = 0;
}
} else if(st->width > 32) {
st->frame += 1;
busy = 2;
} else {
st->offset = 0;
for(int i=0; i<8; i++) {
memcpy(&bImgTarget[i*4], &(st->bImg)[i*SCROLL_BUF_SIZE/8], 4);
}
}
return busy;
}
int main(int argc, char* argv[]) {
int rc;
time_t rawtime, oldTime = 0;
uint8_t bImgTarget[4*8], bImgCurrent[4*8];
uint8_t pixelPositions[4*8*8];
srand((unsigned) time(&rawtime));
// Initialize the library
// num controllers, BCD mode, SPI channel, GPIO pin number for CS
rc = maxInit(4, 0, 0, 22);
if (rc != 0)
{
printf("Problem initializing max7219\n");
return 0;
}
maxSetIntensity(0);
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(EXIT_FAILURE);
}
MQTTClient_subscribe(client, "Timer/#", QOS);
MQTTClient_subscribe(client, "Room/display", QOS);
uint32_t busy = 0;
while(1) {
if(!busy)
time(&rawtime);
if(rawtime - oldTime < 10) {
drawTime(bImgTarget, &rawtime);
} else if(rawtime - oldTime < 40) {
drawDate(bImgTarget, &rawtime);
} else if(rawtime - oldTime < 50) {
busy = moveScroll(bImgCurrent, bImgTarget, &stDisplay);
} else if(rawtime - oldTime < 60) {
busy = moveScroll(bImgCurrent, bImgTarget, &stWeather);
} else {
busy = 0;
oldTime = rawtime;
}
// find pixels to change
int pixelsToChange = 0;
for(int i=0; i<4*8; i++) {
if(bImgCurrent[i] != bImgTarget[i]) {
uint8_t diff = bImgCurrent[i] ^ bImgTarget[i];
// iterate bits
int p = 0;
while(diff) {
if(diff & 1) {
pixelPositions[pixelsToChange++] = i*8 + p;
}
p++;
diff >>= 1;
}
}
}
if(pixelsToChange) {
// shuffle list
// if(pixelsToChange > 1) {
// for(int i=0; i 1)
debugPrint(bImgCurrent, 4);
maxSendImage(bImgCurrent, 4, 1);
}
usleep(1000000 / 50);
}
// Quit library and free resources
maxShutdown();
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
} /* main() */