123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- //
- // 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 <http://www.gnu.org/licenses/>.
- //
- //
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <unistd.h>
- #include <time.h>
- #include <math.h>
- #include <MQTTClient.h>
- #include<json-c/json.h>
- #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; i<count; i++) {
- if(data[i].valid) {
- if(i == 31 && st->width + 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; i<message->payloadlen; 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<count; i++) {
- forecast = json_object_array_get_idx(parsed_json, i);
- json_object_object_get_ex(forecast, "dt", &tmp);
- time_t timestamp = json_object_get_uint64(tmp);
- if(timestamp > 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<pixelsToChange-1; i++) {
- // int j = i + rand() / (RAND_MAX / (pixelsToChange - i) + 1);
- // uint8_t t = pixelPositions[j];
- // pixelPositions[j] = pixelPositions[i];
- // pixelPositions[i] = t;
- // }
- // }
- // animate change
- for(int i=0; i<(busy?2:1); i++) {
- int pos = pixelPositions[rand() % pixelsToChange];
- uint8_t bit = bImgTarget[pos/8] & (1 << (pos%8));
- if(bit)
- bImgCurrent[pos/8] |= bit;
- else
- bImgCurrent[pos/8] &= ~(1 << (pos%8));
- }
- }
- if(pixelsToChange || busy==1) {
- if(argc > 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() */
|